From 684571ca3464cbdc11edb17a1490bf1268f699c4 Mon Sep 17 00:00:00 2001 From: Skalii Date: Mon, 5 Apr 2021 19:20:50 +0300 Subject: [PATCH 01/86] add srtmf map download option p1 --- OsmAnd/res/layout/settings_group_title.xml | 30 +++++ OsmAnd/res/values/strings.xml | 4 + .../resources/IncrementalChangesManager.java | 105 +++++++++--------- .../backend/backup/FileSettingsItem.java | 5 +- .../fragments/ExportItemsBottomSheet.java | 3 +- 5 files changed, 90 insertions(+), 57 deletions(-) diff --git a/OsmAnd/res/layout/settings_group_title.xml b/OsmAnd/res/layout/settings_group_title.xml index 7d4fb811cd..43fedb8440 100644 --- a/OsmAnd/res/layout/settings_group_title.xml +++ b/OsmAnd/res/layout/settings_group_title.xml @@ -60,6 +60,36 @@ + + + + + Please select the needed format. You will need to re-download the file to change the format. + OsmAnd provides contour lines data in meters and feet. You will need to re-download the file to change the format. + Contour lines unit format + feets Update all maps added to %1$s? • OsmAnd Live updates moved to \"Downloads > Updates\"\n\n diff --git a/OsmAnd/src/net/osmand/plus/resources/IncrementalChangesManager.java b/OsmAnd/src/net/osmand/plus/resources/IncrementalChangesManager.java index 2f5ba258f0..efcb487fd4 100644 --- a/OsmAnd/src/net/osmand/plus/resources/IncrementalChangesManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/IncrementalChangesManager.java @@ -1,7 +1,5 @@ package net.osmand.plus.resources; -import android.view.LayoutInflater; - import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.binary.BinaryMapIndexReader; @@ -33,12 +31,11 @@ public class IncrementalChangesManager { private static final org.apache.commons.logging.Log log = PlatformUtil.getLog(IncrementalChangesManager.class); private ResourceManager resourceManager; private final Map regions = new ConcurrentHashMap(); - - + public IncrementalChangesManager(ResourceManager resourceManager) { this.resourceManager = resourceManager; } - + public List collectChangesFiles(File dir, String ext, List files) { if (dir.exists() && dir.canRead()) { File[] lf = dir.listFiles(); @@ -47,8 +44,9 @@ public class IncrementalChangesManager { } Set existingFiles = new HashSet(); for (File f : files) { - if(!f.getName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT) && - !f.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { + if (!f.getName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT) && + !f.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) && + !f.getName().endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)) { existingFiles.add(Algorithms.getFileNameWithoutExtension(f)); } } @@ -71,7 +69,7 @@ public class IncrementalChangesManager { public synchronized void indexMainMap(File f, long dateCreated) { String nm = Algorithms.getFileNameWithoutExtension(f).toLowerCase(); RegionUpdateFiles regionUpdateFiles = regions.get(nm); - if(regionUpdateFiles == null) { + if (regionUpdateFiles == null) { regionUpdateFiles = new RegionUpdateFiles(nm); regions.put(nm, regionUpdateFiles); } @@ -98,7 +96,7 @@ public class IncrementalChangesManager { RegionUpdate monthRu = regionUpdateFiles.monthUpdates.get(month); while (it.hasNext()) { RegionUpdate ru = it.next(); - if(ru == null) { + if (ru == null) { continue; } if (ru.obfCreated <= dateCreated || @@ -114,58 +112,58 @@ public class IncrementalChangesManager { } } } - + public synchronized boolean index(File f, long dateCreated, BinaryMapIndexReader mapReader) { String index = Algorithms.getFileNameWithoutExtension(f).toLowerCase(); - if(index.length() <= 9 || index.charAt(index.length() - 9) != '_'){ + if (index.length() <= 9 || index.charAt(index.length() - 9) != '_') { return false; } String nm = index.substring(0, index.length() - 9); String date = index.substring(index.length() - 9 + 1); RegionUpdateFiles regionUpdateFiles = regions.get(nm); - if(regionUpdateFiles == null) { + if (regionUpdateFiles == null) { regionUpdateFiles = new RegionUpdateFiles(nm); regions.put(nm, regionUpdateFiles); } return regionUpdateFiles.addUpdate(date, f, dateCreated); } - + protected static String formatSize(long vl) { return (vl * 1000 / (1 << 20l)) / 1000.0f + ""; } - + public static long calculateSize(List list) { long l = 0; - for(IncrementalUpdate iu : list) { + for (IncrementalUpdate iu : list) { l += iu.containerSize; } return l; - } - + } + protected class RegionUpdate { protected File file; protected String date; - protected long obfCreated; + protected long obfCreated; } - + protected class RegionUpdateFiles { protected String nm; protected File mainFile; protected long mainFileInit; TreeMap> dayUpdates = new TreeMap>(); TreeMap monthUpdates = new TreeMap(); - + public RegionUpdateFiles(String nm) { this.nm = nm; } - + public boolean addUpdate(String date, File file, long dateCreated) { String monthYear = date.substring(0, 5); RegionUpdate ru = new RegionUpdate(); ru.date = date; ru.file = file; ru.obfCreated = dateCreated; - if(date.endsWith("00")) { + if (date.endsWith("00")) { monthUpdates.put(monthYear, ru); } else { List list = dayUpdates.get(monthYear); @@ -179,22 +177,21 @@ public class IncrementalChangesManager { } } - + public class IncrementalUpdateList { - public TreeMap updateByMonth = + public TreeMap updateByMonth = new TreeMap(); public String errorMessage; public RegionUpdateFiles updateFiles; - - + public boolean isPreferrableLimitForDayUpdates(String monthYearPart, List dayUpdates) { List lst = updateFiles.dayUpdates.get(monthYearPart); - if(lst == null || lst.size() < 10) { + if (lst == null || lst.size() < 10) { return true; } return false; } - + public List getItemsForUpdate() { Iterator it = updateByMonth.values().iterator(); List ll = new ArrayList(); @@ -208,7 +205,7 @@ public class IncrementalChangesManager { } else { // it causes problem when person doesn't restart application for 10 days so updates stop working // && isPreferrableLimitForDayUpdates(n.monthYearPart, n.getDayUpdates()) - if (n.isDayUpdateApplicable() ) { + if (n.isDayUpdateApplicable()) { ll.addAll(n.getDayUpdates()); } else if (n.isMonthUpdateApplicable()) { ll.addAll(n.getMonthUpdate()); @@ -234,51 +231,51 @@ public class IncrementalChangesManager { } } } - + protected static class IncrementalUpdateGroupByMonth { - public final String monthYearPart ; + public final String monthYearPart; public List dayUpdates = new ArrayList(); public IncrementalUpdate monthUpdate; - + public long calculateSizeMonthUpdates() { return calculateSize(getMonthUpdate()); } - + public long calculateSizeDayUpdates() { return calculateSize(getDayUpdates()); } - + public boolean isMonthUpdateApplicable() { return monthUpdate != null; } - + public boolean isDayUpdateApplicable() { boolean inLimits = dayUpdates.size() > 0 && dayUpdates.size() < 4; - if(!inLimits) { + if (!inLimits) { return false; } return true; } - + public List getMonthUpdate() { List ll = new ArrayList(); - if(monthUpdate == null) { + if (monthUpdate == null) { return ll; } ll.add(monthUpdate); - for(IncrementalUpdate iu : dayUpdates) { - if(iu.timestamp > monthUpdate.timestamp) { + for (IncrementalUpdate iu : dayUpdates) { + if (iu.timestamp > monthUpdate.timestamp) { ll.add(iu); } } return ll; } - + public List getDayUpdates() { return dayUpdates; } - - public IncrementalUpdateGroupByMonth(String monthYearPart ) { + + public IncrementalUpdateGroupByMonth(String monthYearPart) { this.monthYearPart = monthYearPart; } } @@ -300,7 +297,7 @@ public class IncrementalChangesManager { return "Update " + fileName + " " + sizeText + " MB " + date + ", timestamp: " + timestamp; } } - + private List getIncrementalUpdates(String file, long timestamp) throws IOException, XmlPullParserException { String url = URL + "?aosmc=true×tamp=" + timestamp + "&file=" + URLEncoder.encode(file); @@ -314,7 +311,7 @@ public class IncrementalChangesManager { int elements = 0; while (parser.next() != XmlPullParser.END_DOCUMENT) { if (parser.getEventType() == XmlPullParser.START_TAG) { - elements ++; + elements++; if (parser.getName().equals("update")) { IncrementalUpdate dt = new IncrementalUpdate(); dt.date = parser.getAttributeValue("", "updateDate"); @@ -332,21 +329,19 @@ public class IncrementalChangesManager { conn.disconnect(); return lst; } - - public IncrementalUpdateList getUpdatesByMonth(String fileName) { IncrementalUpdateList iul = new IncrementalUpdateList(); RegionUpdateFiles ruf = regions.get(fileName.toLowerCase()); iul.updateFiles = ruf; - if(ruf == null) { + if (ruf == null) { iul.errorMessage = resourceManager.getContext().getString(R.string.no_updates_available); return iul; } long timestamp = getTimestamp(ruf); try { List lst = getIncrementalUpdates(fileName, timestamp); - for(IncrementalUpdate iu : lst) { + for (IncrementalUpdate iu : lst) { iul.addUpdate(iu); } } catch (Exception e) { @@ -357,9 +352,9 @@ public class IncrementalChangesManager { return iul; } - public long getUpdatesSize(String fileName){ + public long getUpdatesSize(String fileName) { RegionUpdateFiles ruf = regions.get(fileName.toLowerCase()); - if(ruf == null) { + if (ruf == null) { return 0; } long size = 0; @@ -374,9 +369,9 @@ public class IncrementalChangesManager { return size; } - public void deleteUpdates(String fileName){ + public void deleteUpdates(String fileName) { RegionUpdateFiles ruf = regions.get(fileName.toLowerCase()); - if(ruf == null) { + if (ruf == null) { return; } for (List regionUpdates : ruf.dayUpdates.values()) { @@ -399,7 +394,7 @@ public class IncrementalChangesManager { public long getTimestamp(String fileName) { RegionUpdateFiles ruf = regions.get(fileName.toLowerCase()); - if(ruf == null) { + if (ruf == null) { return System.currentTimeMillis(); } return getTimestamp(ruf); @@ -407,7 +402,7 @@ public class IncrementalChangesManager { public long getMapTimestamp(String fileName) { RegionUpdateFiles ruf = regions.get(fileName.toLowerCase()); - if(ruf == null) { + if (ruf == null) { return System.currentTimeMillis(); } return ruf.mainFileInit; diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java index 88c72beddc..e549c74406 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java @@ -92,7 +92,8 @@ public class FileSettingsItem extends StreamSettingsItem { case OTHER: break; case SRTM_MAP: - if (name.endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { + if (name.endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + || name.endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)) { return subtype; } break; @@ -257,6 +258,8 @@ public class FileSettingsItem extends StreamSettingsItem { prefix = oldPath.substring(0, oldPath.lastIndexOf(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)); } else if (oldPath.endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { prefix = oldPath.substring(0, oldPath.lastIndexOf(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)); + } else if (oldPath.endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)) { + prefix = oldPath.substring(0, oldPath.lastIndexOf(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)); } else if (oldPath.endsWith(IndexConstants.BINARY_ROAD_MAP_INDEX_EXT)) { prefix = oldPath.substring(0, oldPath.lastIndexOf(IndexConstants.BINARY_ROAD_MAP_INDEX_EXT)); } else { diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java index dfe3771416..b78a4bd0aa 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java @@ -530,7 +530,8 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { return getString(R.string.download_roads_only_item); } else if (file.getName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) { return getString(R.string.download_wikipedia_maps); - } else if (file.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { + } else if (file.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + || file.getName().endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)) { return getString(R.string.download_srtm_maps); } else if (file.getName().endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { return getString(R.string.download_regular_maps); From 1813a99da1c9ce0d0d5fccfa364df53c3ae9f746 Mon Sep 17 00:00:00 2001 From: Skalii Date: Mon, 5 Apr 2021 19:21:17 +0300 Subject: [PATCH 02/86] add srtmf map download option p2 --- OsmAnd/res/layout/settings_group_title.xml | 6 +- .../plus/activities/LocalIndexHelper.java | 10 +- .../base/SelectMultipleItemsBottomSheet.java | 394 ++++++++++++------ .../bottomsheetmenu/BaseBottomSheetItem.java | 4 + .../plus/download/DownloadActivityType.java | 64 +-- .../download/DownloadOsmandIndexesHelper.java | 98 ++--- .../plus/download/DownloadResources.java | 27 +- .../plus/download/MultipleIndexItem.java | 26 +- .../download/MultipleIndexesUiHelper.java | 215 ++++++++-- .../plus/download/ui/ItemViewHolder.java | 93 +++-- .../download/ui/LocalIndexesFragment.java | 28 +- .../plus/resources/ResourceManager.java | 202 +++++---- .../datastorage/DataStorageHelper.java | 10 +- 13 files changed, 788 insertions(+), 389 deletions(-) diff --git a/OsmAnd/res/layout/settings_group_title.xml b/OsmAnd/res/layout/settings_group_title.xml index 43fedb8440..4ac7fbfe05 100644 --- a/OsmAnd/res/layout/settings_group_title.xml +++ b/OsmAnd/res/layout/settings_group_title.xml @@ -97,12 +97,12 @@ android:background="?attr/selectableItemBackground" android:gravity="center_vertical" android:minHeight="@dimen/bottom_sheet_list_item_height" + android:paddingStart="@dimen/content_padding" android:paddingLeft="@dimen/content_padding" android:paddingTop="@dimen/content_padding_small" + android:paddingEnd="@dimen/content_padding" android:paddingRight="@dimen/content_padding" - android:paddingBottom="@dimen/content_padding_small" - android:paddingStart="@dimen/content_padding" - android:paddingEnd="@dimen/content_padding"> + android:paddingBottom="@dimen/content_padding_small"> getLocalIndexInfos(String downloadName) { List list = new ArrayList<>(); LocalIndexInfo info = getLocalIndexInfo(LocalIndexType.MAP_DATA, downloadName, false, false); @@ -313,7 +311,7 @@ public class LocalIndexHelper { } } } - + private void loadTravelData(File mapPath, List result, AbstractLoadLocalIndexTask loadTask) { if (mapPath.canRead()) { for (File mapFile : listFilesSorted(mapPath)) { @@ -334,7 +332,8 @@ public class LocalIndexHelper { for (File mapFile : listFilesSorted(mapPath)) { if (mapFile.isFile() && mapFile.getName().endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { LocalIndexType lt = LocalIndexType.MAP_DATA; - if (mapFile.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { + if (mapFile.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + || mapFile.getName().endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)) { lt = LocalIndexType.SRTM_DATA; } else if (mapFile.getName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) { lt = LocalIndexType.WIKI_DATA; @@ -403,7 +402,7 @@ public class LocalIndexHelper { if (fileName.endsWith(IndexConstants.SQLITE_EXT)) { return fileName.substring(0, fileName.length() - IndexConstants.SQLITE_EXT.length()); } - if (localIndexInfo.getType() == TRAVEL_DATA && + if (localIndexInfo.getType() == TRAVEL_DATA && fileName.endsWith(IndexConstants.BINARY_WIKIVOYAGE_MAP_INDEX_EXT)) { return fileName.substring(0, fileName.length() - IndexConstants.BINARY_WIKIVOYAGE_MAP_INDEX_EXT.length()); } @@ -430,5 +429,4 @@ public class LocalIndexHelper { return fileName; } } - } diff --git a/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java index 60b507c972..d09c4a3d6c 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java @@ -5,6 +5,7 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; @@ -21,8 +22,14 @@ import net.osmand.plus.UiUtilities; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton.Builder; +import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.SimpleDividerItem; +import net.osmand.plus.download.MultipleIndexesUiHelper.SelectedItemsListener; +import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.widgets.MultiStateToggleButton; +import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener; +import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; import net.osmand.util.Algorithms; import net.osmand.view.ThreeStateCheckbox; @@ -35,6 +42,8 @@ import static net.osmand.view.ThreeStateCheckbox.State.UNCHECKED; public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragment { + public static final String TAG = SelectMultipleItemsBottomSheet.class.getSimpleName(); + private OsmandApplication app; private UiUtilities uiUtilities; @@ -45,15 +54,57 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen private TextView selectedSize; private ThreeStateCheckbox checkBox; + private int sizeAboveList = 0; private int activeColorRes; private int secondaryColorRes; + private String addDescriptionText; + private String leftRadioButtonText; + private String rightRadioButtonText; + private boolean customOptionsVisible; + private boolean leftButtonSelected; private final List allItems = new ArrayList<>(); private final List selectedItems = new ArrayList<>(); private SelectionUpdateListener selectionUpdateListener; private OnApplySelectionListener onApplySelectionListener; + private OnRadioButtonSelectListener onRadioButtonSelectListener; + private SelectedItemsListener selectedItemsListener; - public static final String TAG = SelectMultipleItemsBottomSheet.class.getSimpleName(); + public static SelectMultipleItemsBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull List items, + @Nullable List selected, + boolean usedOnMap) { + SelectMultipleItemsBottomSheet fragment = new SelectMultipleItemsBottomSheet(); + fragment.setUsedOnMap(usedOnMap); + fragment.setItems(items); + fragment.setSelectedItems(selected); + FragmentManager fm = activity.getSupportFragmentManager(); + fragment.show(fm, TAG); + return fragment; + } + + public static SelectMultipleItemsBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull List items, + @Nullable List selected, + boolean usedOnMap, + String addDescription, + boolean customOptionsVisible, + boolean leftButtonSelected, + String leftRadioButtonText, + String rightRadioButtonText) { + SelectMultipleItemsBottomSheet fragment = new SelectMultipleItemsBottomSheet(); + fragment.setUsedOnMap(usedOnMap); + fragment.setItems(items); + fragment.setSelectedItems(selected); + fragment.setAddDescriptionText(addDescription); + fragment.setCustomOptionsVisible(customOptionsVisible); + fragment.setLeftButtonSelected(leftButtonSelected); + fragment.setLeftRadioButtonText(leftRadioButtonText); + fragment.setRightRadioButtonText(rightRadioButtonText); + FragmentManager fm = activity.getSupportFragmentManager(); + fragment.show(fm, TAG); + return fragment; + } @Nullable @Override @@ -72,9 +123,18 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen items.add(createTitleItem()); items.add(new SimpleDividerItem(app)); + sizeAboveList = items.size(); createListItems(); } + @Override + public void onPause() { + super.onPause(); + if (requireActivity().isChangingConfigurations()) { + dismiss(); + } + } + private BaseBottomSheetItem createTitleItem() { LayoutInflater themedInflater = UiUtilities.getInflater(requireContext(), nightMode); View view = themedInflater.inflate(R.layout.settings_group_title, null); @@ -85,66 +145,111 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen selectedSize = view.findViewById(R.id.selected_size); title = view.findViewById(R.id.title); View selectAllButton = view.findViewById(R.id.select_all_button); - selectAllButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - checkBox.performClick(); - boolean checked = checkBox.getState() == CHECKED; - if (checked) { - selectedItems.addAll(allItems); - } else { - selectedItems.clear(); + TextView addDescription = view.findViewById(R.id.additional_description); + LinearLayout customRadioButtons = view.findViewById(R.id.custom_radio_buttons); + + if (!isMultipleItem()) { + AndroidUiHelper.setVisibility(View.GONE, description, selectedSize, selectAllButton); + } else { + selectAllButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + checkBox.performClick(); + boolean checked = checkBox.getState() == CHECKED; + if (checked) { + selectedItems.addAll(allItems); + } else { + selectedItems.clear(); + } + onSelectedItemsChanged(); + updateItems(checked); } - onSelectedItemsChanged(); - updateItems(checked); - } - }); + }); + } + + if (!Algorithms.isEmpty(addDescriptionText)) { + addDescription.setText(addDescriptionText); + AndroidUiHelper.setVisibility(View.VISIBLE, addDescription); + } + + if (customOptionsVisible) { + AndroidUiHelper.setVisibility(View.VISIBLE, customRadioButtons); + RadioItem leftRadioButton = new RadioItem(leftRadioButtonText); + RadioItem rightRadioButton = new RadioItem(rightRadioButtonText); + MultiStateToggleButton toggleButtons = + new MultiStateToggleButton(app, customRadioButtons, nightMode); + toggleButtons.setItems(leftRadioButton, rightRadioButton); + toggleButtons.updateView(true); + leftRadioButton.setOnClickListener(new OnRadioItemClickListener() { + @Override + public boolean onRadioItemClick(RadioItem radioItem, View view) { + onRadioButtonSelectListener.onSelect(leftButtonSelected = true); + updateSelectedSizeView(); + updateSelectAllButton(); + updateApplyButtonEnable(); + return true; + } + }); + rightRadioButton.setOnClickListener(new OnRadioItemClickListener() { + @Override + public boolean onRadioItemClick(RadioItem radioItem, View view) { + onRadioButtonSelectListener.onSelect(leftButtonSelected = false); + updateSelectedSizeView(); + updateSelectAllButton(); + updateApplyButtonEnable(); + return true; + } + }); + toggleButtons.setSelectedItem(leftButtonSelected ? leftRadioButton : rightRadioButton); + } + return new SimpleBottomSheetItem.Builder().setCustomView(view).create(); } private void createListItems() { - for (final SelectableItem item : allItems) { - boolean checked = selectedItems.contains(item); - final BottomSheetItemWithCompoundButton[] uiItem = new BottomSheetItemWithCompoundButton[1]; - final Builder builder = (BottomSheetItemWithCompoundButton.Builder) new Builder(); - builder.setChecked(checked) - .setButtonTintList(AndroidUtils.createCheckedColorStateList(app, secondaryColorRes, activeColorRes)) - .setLayoutId(R.layout.bottom_sheet_item_with_descr_and_checkbox_56dp) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - boolean checked = !uiItem[0].isChecked(); - uiItem[0].setChecked(checked); - SelectableItem tag = (SelectableItem) uiItem[0].getTag(); - if (checked) { - selectedItems.add(tag); - } else { - selectedItems.remove(tag); + if (isMultipleItem()) { + for (int i = 0; i < allItems.size(); i++) { + final SelectableItem item = allItems.get(i); + boolean checked = selectedItems.contains(item); + final int finalI = i; + items.add(new Builder() + .setChecked(checked) + .setButtonTintList(AndroidUtils.createCheckedColorStateList(app, secondaryColorRes, activeColorRes)) + .setDescription(item.description) + .setIcon(uiUtilities.getIcon(item.iconId, activeColorRes)) + .setTitle(item.title) + .setLayoutId(R.layout.bottom_sheet_item_with_descr_and_checkbox_56dp) + .setTag(item) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + BottomSheetItemWithCompoundButton item = (BottomSheetItemWithCompoundButton) items.get(finalI + sizeAboveList); + boolean checked = item.isChecked(); + item.setChecked(!checked); + SelectableItem tag = (SelectableItem) item.getTag(); + if (!checked) { + selectedItems.add(tag); + } else { + selectedItems.remove(tag); + } + onSelectedItemsChanged(); } - onSelectedItemsChanged(); - } - }) - .setTag(item); - setupListItem(builder, item); - uiItem[0] = builder.create(); - items.add(uiItem[0]); + }) + .create()); + } + } else if (allItems.size() == 1) { + final SelectableItem item = allItems.get(0); + items.add(new Builder() + .setDescription(item.description) + .setDescriptionColorId(AndroidUtils.getSecondaryTextColorId(nightMode)) + .setIcon(uiUtilities.getIcon(item.iconId, activeColorRes)) + .setTitle(item.title) + .setLayoutId(R.layout.bottom_sheet_item_with_descr_56dp) + .setTag(item) + .create()); } } - @Override - protected void setupRightButton() { - super.setupRightButton(); - applyButtonTitle = rightButton.findViewById(R.id.button_text); - } - - @Override - protected void onRightBottomButtonClick() { - if (onApplySelectionListener != null) { - onApplySelectionListener.onSelectionApplied(selectedItems); - } - dismiss(); - } - private void onSelectedItemsChanged() { updateSelectAllButton(); updateSelectedSizeView(); @@ -154,48 +259,32 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen } } - @Override - protected int getRightBottomButtonTextId() { - return R.string.shared_string_apply; - } - - @Override - protected boolean useVerticalButtons() { - return true; - } - - private void setupListItem(Builder builder, SelectableItem item) { - builder.setTitle(item.title); - builder.setDescription(item.description); - builder.setIcon(uiUtilities.getIcon(item.iconId, activeColorRes)); - } - private void updateSelectAllButton() { - String checkBoxTitle; - if (Algorithms.isEmpty(selectedItems)) { - checkBox.setState(UNCHECKED); - checkBoxTitle = getString(R.string.shared_string_select_all); - } else { - checkBox.setState(selectedItems.containsAll(allItems) ? CHECKED : MISC); - checkBoxTitle = getString(R.string.shared_string_deselect_all); + if (isMultipleItem()) { + String checkBoxTitle; + if (Algorithms.isEmpty(selectedItems)) { + checkBox.setState(UNCHECKED); + checkBoxTitle = getString(R.string.shared_string_select_all); + } else { + checkBox.setState(selectedItems.containsAll(allItems) ? CHECKED : MISC); + checkBoxTitle = getString(R.string.shared_string_deselect_all); + } + int checkBoxColor = checkBox.getState() == UNCHECKED ? secondaryColorRes : activeColorRes; + CompoundButtonCompat.setButtonTintList(checkBox, ColorStateList.valueOf(ContextCompat.getColor(app, checkBoxColor))); + this.checkBoxTitle.setText(checkBoxTitle); } - int checkBoxColor = checkBox.getState() == UNCHECKED ? secondaryColorRes : activeColorRes; - CompoundButtonCompat.setButtonTintList(checkBox, ColorStateList.valueOf(ContextCompat.getColor(app, checkBoxColor))); - this.checkBoxTitle.setText(checkBoxTitle); } private void updateSelectedSizeView() { - String selected = String.valueOf(selectedItems.size()); - String all = String.valueOf(allItems.size()); - selectedSize.setText(getString(R.string.ltr_or_rtl_combine_via_slash, selected, all)); + if (isMultipleItem()) { + String selected = String.valueOf(selectedItems.size()); + String all = String.valueOf(allItems.size()); + selectedSize.setText(getString(R.string.ltr_or_rtl_combine_via_slash, selected, all)); + } } private void updateApplyButtonEnable() { - if (Algorithms.isEmpty(selectedItems)) { - rightButton.setEnabled(false); - } else { - rightButton.setEnabled(true); - } + rightButton.setEnabled(!Algorithms.isEmpty(selectedItems)); } private void updateItems(boolean checked) { @@ -206,6 +295,59 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen } } + public void setItems(List allItems) { + this.allItems.clear(); + if (!Algorithms.isEmpty(allItems)) { + this.allItems.addAll(allItems); + } + } + + private void setSelectedItems(List selected) { + this.selectedItems.clear(); + if (!Algorithms.isEmpty(selected)) { + /*List prevDownloadItems = new ArrayList<>(this.selectedItems); + for (SelectableItem prevDownloadItem : selected) { + Object object = prevDownloadItem.getObject(); + if (object instanceof IndexItem && ((IndexItem) object).isDownloaded()) { + prevDownloadItems.add(prevDownloadItem); + } + } + selected.removeAll(prevDownloadItems);*/ + this.selectedItems.addAll(selected); + } + } + + public void recreateList(List allItems) { + setItems(allItems); + if (selectedItemsListener != null) { + setSelectedItems(selectedItemsListener.createSelectedItems(this.allItems, leftButtonSelected)); + } + if (items.size() > sizeAboveList) { + for (int i = 0; i < this.allItems.size(); i++) { + SelectableItem item = this.allItems.get(i); + BottomSheetItemWithDescription button = (BottomSheetItemWithDescription) items.get(i + sizeAboveList); + button.setDescription(item.description); + button.setTitle(item.title); + button.setTag(item); + if (isMultipleItem()) { + ((BottomSheetItemWithCompoundButton) button).setChecked(selectedItems.contains(item)); + } + } + } + } + + public boolean isMultipleItem() { + return allItems.size() > 1; + } + + public List getSelectedItems() { + return selectedItems; + } + + public void setConfirmButtonTitle(@NonNull String confirmButtonTitle) { + applyButtonTitle.setText(confirmButtonTitle); + } + public void setTitle(@NonNull String title) { this.title.setText(title); } @@ -214,45 +356,24 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen this.description.setText(description); } - public void setConfirmButtonTitle(@NonNull String confirmButtonTitle) { - applyButtonTitle.setText(confirmButtonTitle); + public void setAddDescriptionText(String addDescriptionText) { + this.addDescriptionText = addDescriptionText; } - private void setItems(List allItems) { - if (!Algorithms.isEmpty(allItems)) { - this.allItems.addAll(allItems); - } + public void setLeftRadioButtonText(String leftRadioButtonText) { + this.leftRadioButtonText = leftRadioButtonText; } - private void setSelectedItems(List selected) { - if (!Algorithms.isEmpty(selected)) { - this.selectedItems.addAll(selected); - } + public void setRightRadioButtonText(String rightRadioButtonText) { + this.rightRadioButtonText = rightRadioButtonText; } - public List getSelectedItems() { - return selectedItems; + public void setCustomOptionsVisible(boolean customOptionsVisible) { + this.customOptionsVisible = customOptionsVisible; } - @Override - public void onPause() { - super.onPause(); - if (requireActivity().isChangingConfigurations()) { - dismiss(); - } - } - - public static SelectMultipleItemsBottomSheet showInstance(@NonNull AppCompatActivity activity, - @NonNull List items, - @Nullable List selected, - boolean usedOnMap) { - SelectMultipleItemsBottomSheet fragment = new SelectMultipleItemsBottomSheet(); - fragment.setUsedOnMap(usedOnMap); - fragment.setItems(items); - fragment.setSelectedItems(selected); - FragmentManager fm = activity.getSupportFragmentManager(); - fragment.show(fm, TAG); - return fragment; + public void setLeftButtonSelected(boolean leftButtonSelected) { + this.leftButtonSelected = leftButtonSelected; } public void setSelectionUpdateListener(SelectionUpdateListener selectionUpdateListener) { @@ -263,6 +384,14 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen this.onApplySelectionListener = onApplySelectionListener; } + public void setOnRadioButtonSelectListener(OnRadioButtonSelectListener onRadioButtonSelectListener) { + this.onRadioButtonSelectListener = onRadioButtonSelectListener; + } + + public void setSelectedItemsListener(SelectedItemsListener selectedItemsListener) { + this.selectedItemsListener = selectedItemsListener; + } + public interface SelectionUpdateListener { void onSelectionUpdate(); } @@ -271,6 +400,10 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen void onSelectionApplied(List selectedItems); } + public interface OnRadioButtonSelectListener { + void onSelect(boolean leftButton); + } + public static class SelectableItem { private String title; private String description; @@ -281,6 +414,10 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen this.title = title; } + public String getDescription() { + return description; + } + public void setDescription(String description) { this.description = description; } @@ -298,4 +435,27 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen } } + @Override + protected void setupRightButton() { + super.setupRightButton(); + applyButtonTitle = rightButton.findViewById(R.id.button_text); + } + + @Override + protected void onRightBottomButtonClick() { + if (onApplySelectionListener != null) { + onApplySelectionListener.onSelectionApplied(selectedItems); + } + dismiss(); + } + + @Override + protected int getRightBottomButtonTextId() { + return R.string.shared_string_apply; + } + + @Override + protected boolean useVerticalButtons() { + return true; + } } diff --git a/OsmAnd/src/net/osmand/plus/base/bottomsheetmenu/BaseBottomSheetItem.java b/OsmAnd/src/net/osmand/plus/base/bottomsheetmenu/BaseBottomSheetItem.java index 1914756d21..f77fc8503f 100644 --- a/OsmAnd/src/net/osmand/plus/base/bottomsheetmenu/BaseBottomSheetItem.java +++ b/OsmAnd/src/net/osmand/plus/base/bottomsheetmenu/BaseBottomSheetItem.java @@ -32,6 +32,10 @@ public class BaseBottomSheetItem { return tag; } + public void setTag(Object tag) { + this.tag = tag; + } + public BaseBottomSheetItem(View view, @LayoutRes int layoutId, Object tag, diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java index 071a856454..5c84250d07 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java @@ -10,6 +10,7 @@ import net.osmand.map.OsmandRegions; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.Version; +import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.util.Algorithms; @@ -27,11 +28,13 @@ import java.util.Locale; import java.util.Map; import static net.osmand.IndexConstants.BINARY_MAP_INDEX_EXT; +import static net.osmand.plus.activities.LocalIndexHelper.LocalIndexType.SRTM_DATA; +import static net.osmand.plus.download.MultipleIndexesUiHelper.getSRTMExt; public class DownloadActivityType { private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd.MM.yyyy", Locale.US); private static Map byTag = new HashMap<>(); - + public static final DownloadActivityType NORMAL_FILE = new DownloadActivityType(R.string.download_regular_maps, "map", 10); public static final DownloadActivityType VOICE_FILE = @@ -83,7 +86,7 @@ public class DownloadActivityType { iconResource = R.drawable.ic_map; } - public int getStringResource(){ + public int getStringResource() { return stringResource; } @@ -101,7 +104,7 @@ public class DownloadActivityType { public static boolean isCountedInDownloads(IndexItem es) { DownloadActivityType tp = es.getType(); - if(tp == NORMAL_FILE || tp == ROADS_FILE){ + if (tp == NORMAL_FILE || tp == ROADS_FILE) { if (!es.extra) { return true; } @@ -120,17 +123,17 @@ public class DownloadActivityType { public static Collection values() { return byTag.values(); } - + protected static String addVersionToExt(String ext, int version) { return "_" + version + ext; } - + public boolean isAccepted(String fileName) { - if(NORMAL_FILE == this) { - return fileName.endsWith(addVersionToExt(IndexConstants.BINARY_MAP_INDEX_EXT_ZIP, IndexConstants.BINARY_MAP_VERSION)) + if (NORMAL_FILE == this) { + return fileName.endsWith(addVersionToExt(IndexConstants.BINARY_MAP_INDEX_EXT_ZIP, IndexConstants.BINARY_MAP_VERSION)) || fileName.endsWith(IndexConstants.EXTRA_ZIP_EXT) || fileName.endsWith(IndexConstants.SQLITE_EXT); - } else if(ROADS_FILE == this) { + } else if (ROADS_FILE == this) { return fileName.endsWith(addVersionToExt(IndexConstants.BINARY_ROAD_MAP_INDEX_EXT_ZIP, IndexConstants.BINARY_MAP_VERSION)); } else if (VOICE_FILE == this) { return fileName.endsWith(addVersionToExt(IndexConstants.VOICE_INDEX_EXT_ZIP, IndexConstants.VOICE_VERSION)); @@ -145,8 +148,9 @@ public class DownloadActivityType { return fileName.endsWith(addVersionToExt(IndexConstants.BINARY_TRAVEL_GUIDE_MAP_INDEX_EXT_ZIP, IndexConstants.BINARY_MAP_VERSION)); } else if (SRTM_COUNTRY_FILE == this) { - return fileName.endsWith(addVersionToExt(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT_ZIP, - IndexConstants.BINARY_MAP_VERSION)); + boolean srtm = fileName.endsWith(addVersionToExt(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT_ZIP, IndexConstants.BINARY_MAP_VERSION)); + boolean srtmf = fileName.endsWith(addVersionToExt(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT_ZIP, IndexConstants.BINARY_MAP_VERSION)); + return srtm || srtmf; } else if (HILLSHADE_FILE == this) { return fileName.endsWith(IndexConstants.SQLITE_EXT); } else if (SLOPE_FILE == this) { @@ -160,7 +164,7 @@ public class DownloadActivityType { } return false; } - + public File getDownloadFolder(OsmandApplication ctx, IndexItem indexItem) { if (NORMAL_FILE == this) { if (indexItem.fileName.endsWith(IndexConstants.SQLITE_EXT)) { @@ -196,17 +200,17 @@ public class DownloadActivityType { } public boolean isZipStream(OsmandApplication ctx, IndexItem indexItem) { - return HILLSHADE_FILE != this && SLOPE_FILE != this && SQLITE_FILE != this && WIKIVOYAGE_FILE != this && GPX_FILE != this; + return HILLSHADE_FILE != this && SLOPE_FILE != this && SQLITE_FILE != this && WIKIVOYAGE_FILE != this && GPX_FILE != this; } public boolean isZipFolder(OsmandApplication ctx, IndexItem indexItem) { return this == VOICE_FILE; } - + public boolean preventMediaIndexing(OsmandApplication ctx, IndexItem indexItem) { return this == VOICE_FILE && indexItem.fileName.endsWith(IndexConstants.VOICE_INDEX_EXT_ZIP); } - + public String getUnzipExtension(OsmandApplication ctx, IndexItem indexItem) { if (NORMAL_FILE == this) { if (indexItem.fileName.endsWith(IndexConstants.BINARY_MAP_INDEX_EXT_ZIP)) { @@ -217,7 +221,7 @@ public class DownloadActivityType { return IndexConstants.EXTRA_EXT; } else if (indexItem.fileName.endsWith(IndexConstants.SQLITE_EXT)) { return IndexConstants.SQLITE_EXT; - } else if (indexItem.fileName.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)){ + } else if (indexItem.fileName.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)) { return ""; } } else if (ROADS_FILE == this) { @@ -227,7 +231,7 @@ public class DownloadActivityType { } else if (FONT_FILE == this) { return IndexConstants.FONT_INDEX_EXT; } else if (SRTM_COUNTRY_FILE == this) { - return IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; + return getSRTMExt(indexItem); } else if (WIKIPEDIA_FILE == this) { return IndexConstants.BINARY_WIKI_MAP_INDEX_EXT; } else if (WIKIVOYAGE_FILE == this) { @@ -249,9 +253,9 @@ public class DownloadActivityType { } throw new UnsupportedOperationException(); } - + public String getUrlSuffix(OsmandApplication ctx) { - if (this== ROADS_FILE) { + if (this == ROADS_FILE) { return "&road=yes"; } else if (this == LIVE_UPDATES_FILE) { return "&aosmc=yes"; @@ -280,7 +284,7 @@ public class DownloadActivityType { public String getBaseUrl(OsmandApplication ctx, String fileName) { String url = "https://" + IndexConstants.INDEX_DOWNLOAD_DOMAIN + "/download?event=2&" + Version.getVersionAsURLParam(ctx) + "&file=" + encode(fileName); - if(this == LIVE_UPDATES_FILE && fileName.length() > 16) { + if (this == LIVE_UPDATES_FILE && fileName.length() > 16) { // DATE_AND_EXT_STR_LEN = "_18_06_02.obf.gz".length() String region = fileName.substring(0, fileName.length() - 16).toLowerCase(); url += "®ion=" + encode(region); @@ -343,7 +347,7 @@ public class DownloadActivityType { } return ""; } - + public String getVisibleName(DownloadItem downloadItem, Context ctx, OsmandRegions osmandRegions, boolean includingParent) { if (this == VOICE_FILE) { String fileName = downloadItem.getFileName(); @@ -383,7 +387,7 @@ public class DownloadActivityType { return osmandRegions.getLocaleName(basename, includingParent); } - + public String getTargetFileName(IndexItem item) { String fileName = item.fileName; // if(fileName.endsWith(IndexConstants.VOICE_INDEX_EXT_ZIP) || @@ -423,7 +427,7 @@ public class DownloadActivityType { } String baseNameWithoutVersion = fileName.substring(0, l); if (this == SRTM_COUNTRY_FILE) { - return baseNameWithoutVersion + IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; + return baseNameWithoutVersion + getSRTMExt(item); } if (this == WIKIPEDIA_FILE) { return baseNameWithoutVersion + IndexConstants.BINARY_WIKI_MAP_INDEX_EXT; @@ -487,7 +491,7 @@ public class DownloadActivityType { return fileName.substring(0, l); } if (this == LIVE_UPDATES_FILE) { - if(fileName.indexOf('.') > 0){ + if (fileName.indexOf('.') > 0) { return fileName.substring(0, fileName.indexOf('.')); } return fileName; @@ -495,10 +499,20 @@ public class DownloadActivityType { int ls = fileName.lastIndexOf('_'); if (ls >= 0) { return fileName.substring(0, ls); - } else if(fileName.indexOf('.') > 0){ + } else if (fileName.indexOf('.') > 0) { return fileName.substring(0, fileName.indexOf('.')); } return fileName; } -} \ No newline at end of file + public static boolean isSRTMItem(Object item) { + if (item instanceof IndexItem) { + return ((IndexItem) item).getType() == SRTM_COUNTRY_FILE; + } else if (item instanceof DownloadItem) { + return ((DownloadItem) item).getType() == SRTM_COUNTRY_FILE; + } else if (item instanceof LocalIndexInfo) { + return ((LocalIndexInfo) item).getType() == SRTM_DATA; + } + return false; + } +} diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java b/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java index d77245cecb..8af9f9eb6b 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java @@ -1,5 +1,26 @@ package net.osmand.plus.download; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.res.AssetManager; +import android.provider.Settings.Secure; + +import net.osmand.AndroidUtils; +import net.osmand.IndexConstants; +import net.osmand.PlatformUtil; +import net.osmand.osm.io.NetworkUtils; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.util.Algorithms; + +import org.apache.commons.logging.Log; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; + import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -11,29 +32,9 @@ import java.util.Comparator; import java.util.List; import java.util.zip.GZIPInputStream; -import net.osmand.AndroidUtils; -import net.osmand.IndexConstants; -import net.osmand.PlatformUtil; -import net.osmand.osm.io.NetworkUtils; -import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; - -import org.apache.commons.logging.Log; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; -import org.xmlpull.v1.XmlPullParserFactory; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.content.res.AssetManager; -import android.provider.Settings.Secure; - public class DownloadOsmandIndexesHelper { private final static Log log = PlatformUtil.getLog(DownloadOsmandIndexesHelper.class); - + public static class IndexFileList implements Serializable { private static final long serialVersionUID = 1L; @@ -41,29 +42,29 @@ public class DownloadOsmandIndexesHelper { IndexItem basemap; ArrayList indexFiles = new ArrayList(); private String mapversion; - - private Comparator comparator = new Comparator(){ + + private Comparator comparator = new Comparator() { @Override public int compare(IndexItem o1, IndexItem o2) { String object1 = o1.getFileName(); String object2 = o2.getFileName(); - if(object1.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)){ - if(object2.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)){ + if (object1.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)) { + if (object2.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)) { return object1.compareTo(object2); } else { return -1; } - } else if(object2.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)){ + } else if (object2.endsWith(IndexConstants.ANYVOICE_INDEX_EXT_ZIP)) { return 1; } return object1.compareTo(object2); } }; - + public void setDownloadedFromInternet(boolean downloadedFromInternet) { this.downloadedFromInternet = downloadedFromInternet; } - + public boolean isDownloadedFromInternet() { return downloadedFromInternet; } @@ -75,12 +76,12 @@ public class DownloadOsmandIndexesHelper { @SuppressLint("DefaultLocale") public void add(IndexItem indexItem) { indexFiles.add(indexItem); - if(indexItem.getFileName().toLowerCase().startsWith("world_basemap")) { + if (indexItem.getFileName().toLowerCase().startsWith("world_basemap")) { basemap = indexItem; } } - - public void sort(){ + + public void sort() { Collections.sort(indexFiles, comparator); } @@ -91,7 +92,7 @@ public class DownloadOsmandIndexesHelper { public List getIndexFiles() { return indexFiles; } - + public IndexItem getBasemap() { return basemap; } @@ -106,7 +107,7 @@ public class DownloadOsmandIndexesHelper { return false; } - } + } public static IndexFileList getIndexesList(OsmandApplication app) { PackageManager pm = app.getPackageManager(); @@ -141,16 +142,16 @@ public class DownloadOsmandIndexesHelper { } private static void listVoiceAssets(IndexFileList result, AssetManager amanager, PackageManager pm, - OsmandSettings settings) { + OsmandSettings settings) { try { - File voicePath = settings.getContext().getAppPath(IndexConstants.VOICE_INDEX_DIR); + File voicePath = settings.getContext().getAppPath(IndexConstants.VOICE_INDEX_DIR); // list = amanager.list("voice"); String date = ""; long dateModified = System.currentTimeMillis(); try { OsmandApplication app = settings.getContext(); ApplicationInfo appInfo = pm.getApplicationInfo(app.getPackageName(), 0); - dateModified = new File(appInfo.sourceDir).lastModified(); + dateModified = new File(appInfo.sourceDir).lastModified(); date = AndroidUtils.formatDate((Context) settings.getContext(), dateModified); } catch (NameNotFoundException e) { log.error(e); @@ -177,17 +178,17 @@ public class DownloadOsmandIndexesHelper { log.error("Error while loading tts files from assets", e); //$NON-NLS-1$ } } - - private static IndexFileList downloadIndexesListFromInternet(OsmandApplication ctx){ + + private static IndexFileList downloadIndexesListFromInternet(OsmandApplication ctx) { try { IndexFileList result = new IndexFileList(); log.debug("Start loading list of index files"); //$NON-NLS-1$ try { String strUrl = ctx.getAppCustomization().getIndexesUrl(); long nd = ctx.getAppInitializer().getFirstInstalledDays(); - if(nd > 0) { - strUrl += "&nd=" + nd; + if (nd > 0) { + strUrl += "&nd=" + nd; } strUrl += "&ns=" + ctx.getAppInitializer().getNumberOfStarts(); try { @@ -202,12 +203,15 @@ public class DownloadOsmandIndexesHelper { GZIPInputStream gzin = new GZIPInputStream(in); parser.setInput(gzin, "UTF-8"); //$NON-NLS-1$ int next; - while((next = parser.next()) != XmlPullParser.END_DOCUMENT) { + while ((next = parser.next()) != XmlPullParser.END_DOCUMENT) { if (next == XmlPullParser.START_TAG) { DownloadActivityType tp = DownloadActivityType.getIndexType(parser.getAttributeValue(null, "type")); if (tp != null) { + if (tp == DownloadActivityType.SRTM_COUNTRY_FILE) { + log.debug("strUrl = " + strUrl); + } IndexItem it = tp.parseIndexItem(ctx, parser); - if(it != null) { + if (it != null) { result.add(it); } } else if ("osmand_regions".equals(parser.getName())) { @@ -226,7 +230,7 @@ public class DownloadOsmandIndexesHelper { log.error("Error while loading indexes from repository", e); //$NON-NLS-1$ return null; } - + if (result.isAcceptable()) { return result; } else { @@ -239,19 +243,19 @@ public class DownloadOsmandIndexesHelper { } public static class AssetIndexItem extends IndexItem { - + private final String assetName; private final String destFile; private final long dateModified; public AssetIndexItem(String fileName, String description, String date, - long dateModified, String size, long sizeL, String assetName, String destFile, DownloadActivityType type) { + long dateModified, String size, long sizeL, String assetName, String destFile, DownloadActivityType type) { super(fileName, description, dateModified, size, sizeL, sizeL, type); this.dateModified = dateModified; this.assetName = assetName; this.destFile = destFile; } - + public long getDateModified() { return dateModified; } @@ -261,7 +265,7 @@ public class DownloadOsmandIndexesHelper { return new DownloadEntry(assetName, destFile, dateModified); } - public String getDestFile(){ + public String getDestFile() { return destFile; } } diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java index 40f4a7488a..7293d46659 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java @@ -264,7 +264,7 @@ public class DownloadResources extends DownloadResourceGroup { } private Map listWithAlternatives(final java.text.DateFormat dateFormat, File file, - final String ext, final Map files) { + final String ext, final Map files) { if (file.isDirectory()) { file.list(new FilenameFilter() { @Override @@ -518,7 +518,7 @@ public class DownloadResources extends DownloadResourceGroup { @Nullable private List collectIndexesOfType(@NonNull List regions, - @NonNull DownloadActivityType type) { + @NonNull DownloadActivityType type) { List collectedIndexes = new ArrayList<>(); for (WorldRegion region : regions) { List regionIndexes = getIndexItems(region); @@ -528,7 +528,6 @@ public class DownloadResources extends DownloadResourceGroup { if (index.getType() == type) { found = true; collectedIndexes.add(index); - break; } } } @@ -646,10 +645,10 @@ public class DownloadResources extends DownloadResourceGroup { } public static List findIndexItemsAt(OsmandApplication app, - List names, - DownloadActivityType type, - boolean includeDownloaded, - int limit) { + List names, + DownloadActivityType type, + boolean includeDownloaded, + int limit) { List res = new ArrayList<>(); OsmandRegions regions = app.getRegions(); DownloadIndexesThread downloadThread = app.getDownloadThread(); @@ -666,9 +665,9 @@ public class DownloadResources extends DownloadResourceGroup { } private static boolean isIndexItemDownloaded(DownloadIndexesThread downloadThread, - DownloadActivityType type, - WorldRegion downloadRegion, - List res) { + DownloadActivityType type, + WorldRegion downloadRegion, + List res) { List otherIndexItems = new ArrayList<>(downloadThread.getIndexes().getIndexItems(downloadRegion)); for (IndexItem indexItem : otherIndexItems) { @@ -681,7 +680,7 @@ public class DownloadResources extends DownloadResourceGroup { } private boolean doesListContainIndexWithType(List indexItems, - DownloadActivityType type) { + DownloadActivityType type) { if (indexItems != null) { for (IndexItem indexItem : indexItems) { if (indexItem.getType() == type) { @@ -693,9 +692,9 @@ public class DownloadResources extends DownloadResourceGroup { } private static boolean addIndexItem(DownloadIndexesThread downloadThread, - DownloadActivityType type, - WorldRegion downloadRegion, - List res) { + DownloadActivityType type, + WorldRegion downloadRegion, + List res) { List otherIndexItems = new ArrayList<>(downloadThread.getIndexes().getIndexItems(downloadRegion)); for (IndexItem indexItem : otherIndexItems) { diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java b/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java index 5f5ff9c297..309f5210b9 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java @@ -1,5 +1,7 @@ package net.osmand.plus.download; +import android.content.Context; + import androidx.annotation.NonNull; import net.osmand.map.WorldRegion; @@ -9,13 +11,16 @@ import java.io.File; import java.util.ArrayList; import java.util.List; +import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; +import static net.osmand.plus.download.MultipleIndexesUiHelper.isBaseSRTMItem; + public class MultipleIndexItem extends DownloadItem { private final List items; public MultipleIndexItem(@NonNull WorldRegion region, - @NonNull List items, - @NonNull DownloadActivityType type) { + @NonNull List items, + @NonNull DownloadActivityType type) { super(type); this.items = items; } @@ -110,6 +115,22 @@ public class MultipleIndexItem extends DownloadItem { return totalSizeMb; } + @NonNull + public String getSizeDescription(Context ctx, boolean baseSRTM) { + double totalSizeMb = 0.0d; + if (this.type == SRTM_COUNTRY_FILE) { + for (IndexItem item : items) { + if (item.hasActualDataToDownload()) { + if (baseSRTM && isBaseSRTMItem(item) || !baseSRTM && !isBaseSRTMItem(item)) { + totalSizeMb += item.getSizeToDownloadInMb(); + } + } + } + return getFormattedMb(ctx, totalSizeMb); + } + return getFormattedMb(ctx, getSizeToDownloadInMb()); + } + @Override public double getArchiveSizeMB() { double result = 0.0d; @@ -118,5 +139,4 @@ public class MultipleIndexItem extends DownloadItem { } return result; } - } diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java b/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java index f84da0a74a..575507d7d8 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java @@ -1,28 +1,52 @@ package net.osmand.plus.download; +import android.content.Context; + import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; +import net.osmand.IndexConstants; import net.osmand.map.OsmandRegions; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; +import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.base.SelectMultipleItemsBottomSheet; import net.osmand.plus.base.SelectMultipleItemsBottomSheet.OnApplySelectionListener; +import net.osmand.plus.base.SelectMultipleItemsBottomSheet.OnRadioButtonSelectListener; import net.osmand.plus.base.SelectMultipleItemsBottomSheet.SelectableItem; import net.osmand.plus.base.SelectMultipleItemsBottomSheet.SelectionUpdateListener; +import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.util.Algorithms; import java.text.DateFormat; import java.util.ArrayList; import java.util.List; +import static net.osmand.IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; +import static net.osmand.IndexConstants.BINARY_SRTM_MAP_INDEX_EXT_ZIP; +import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; + public class MultipleIndexesUiHelper { - public static void showDialog(@NonNull MultipleIndexItem multipleIndexItem, - @NonNull AppCompatActivity activity, - @NonNull final OsmandApplication app, - @NonNull DateFormat dateFormat, - boolean showRemoteDate, - @NonNull final SelectItemsToDownloadListener listener) { + public static void showDialog(@NonNull DownloadItem item, + @NonNull AppCompatActivity activity, + @NonNull final OsmandApplication app, + @NonNull DateFormat dateFormat, + boolean showRemoteDate, + @NonNull final SelectItemsToDownloadListener listener) { + if (item.getType() == SRTM_COUNTRY_FILE) { + showSRTMDialog(item, activity, app, dateFormat, showRemoteDate, listener); + } else if (item instanceof MultipleIndexItem) { + showBaseDialog((MultipleIndexItem) item, activity, app, dateFormat, showRemoteDate, listener); + } + } + + public static void showBaseDialog(@NonNull MultipleIndexItem multipleIndexItem, + @NonNull AppCompatActivity activity, + @NonNull final OsmandApplication app, + @NonNull DateFormat dateFormat, + boolean showRemoteDate, + @NonNull final SelectItemsToDownloadListener listener) { List indexesToDownload = getIndexesToDownload(multipleIndexItem); List allItems = new ArrayList<>(); List selectedItems = new ArrayList<>(); @@ -30,12 +54,10 @@ public class MultipleIndexesUiHelper { for (IndexItem indexItem : multipleIndexItem.getAllIndexes()) { SelectableItem selectableItem = new SelectableItem(); selectableItem.setTitle(indexItem.getVisibleName(app, osmandRegions, false)); - String size = indexItem.getSizeDescription(app); String date = indexItem.getDate(dateFormat, showRemoteDate); String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); selectableItem.setDescription(description); - selectableItem.setIconId(indexItem.getType().getIconResource()); selectableItem.setObject(indexItem); allItems.add(selectableItem); @@ -51,22 +73,102 @@ public class MultipleIndexesUiHelper { dialog.setSelectionUpdateListener(new SelectionUpdateListener() { @Override public void onSelectionUpdate() { - dialog.setTitle(app.getString(R.string.welmode_download_maps)); - String total = app.getString(R.string.shared_string_total); - double sizeToDownload = getDownloadSizeInMb(dialog.getSelectedItems()); - String size = DownloadItem.getFormattedMb(app, sizeToDownload); - String description = - app.getString(R.string.ltr_or_rtl_combine_via_colon, total, size); - dialog.setDescription(description); - String btnTitle = app.getString(R.string.shared_string_download); - if (sizeToDownload > 0) { - btnTitle = app.getString(R.string.ltr_or_rtl_combine_via_dash, btnTitle, size); - } - dialog.setConfirmButtonTitle(btnTitle); + updateSize(app, dialog); } }); + dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); + } - dialog.setOnApplySelectionListener(new OnApplySelectionListener() { + public static void showSRTMDialog(@NonNull final DownloadItem downloadItem, + @NonNull AppCompatActivity activity, + @NonNull final OsmandApplication app, + @NonNull final DateFormat dateFormat, + final boolean showRemoteDate, + @NonNull final SelectItemsToDownloadListener listener) { + List selectedItems = new ArrayList<>(); + final List leftItems = new ArrayList<>(); + final List rightItems = new ArrayList<>(); + List indexesToDownload = getIndexesToDownload(downloadItem); + boolean baseSRTM = isBaseSRTMMetricSystem(app); + + List allIndexes = new ArrayList<>(); + if (downloadItem instanceof MultipleIndexItem) { + allIndexes.addAll(((MultipleIndexItem) downloadItem).getAllIndexes()); + } else { + for (IndexItem indexItem : downloadItem.getRelatedGroup().getIndividualResources()) { + if (indexItem.getType() == SRTM_COUNTRY_FILE) { + allIndexes.add(indexItem); + } + } + } + + for (IndexItem indexItem : allIndexes) { + boolean baseItem = isBaseSRTMItem(indexItem); + SelectableItem selectableItem = new SelectableItem(); + selectableItem.setTitle(indexItem.getVisibleName(app, app.getRegions(), false)); + String size = indexItem.getSizeDescription(app); + size += " (" + getSRTMAbbrev(app, baseItem) + ")"; + String date = indexItem.getDate(dateFormat, showRemoteDate); + String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); + selectableItem.setDescription(description); + selectableItem.setIconId(indexItem.getType().getIconResource()); + selectableItem.setObject(indexItem); + + if (baseItem) { + leftItems.add(selectableItem); + } else { + rightItems.add(selectableItem); + } + + if (indexesToDownload.contains(indexItem) + && (baseSRTM && baseItem || !baseSRTM && !baseItem)) { + selectedItems.add(selectableItem); + } + } + + String addDescription = app.getString(isListDialog(app, leftItems, rightItems) + ? R.string.srtm_download_list_help_message : R.string.srtm_download_single_help_message); + final SelectMultipleItemsBottomSheet dialog = SelectMultipleItemsBottomSheet.showInstance( + activity, baseSRTM ? leftItems : rightItems, selectedItems, true, + addDescription, true, baseSRTM, + Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_meters)), + Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_feets))); + + dialog.setSelectionUpdateListener(new SelectionUpdateListener() { + @Override + public void onSelectionUpdate() { + updateSize(app, dialog); + } + }); + dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); + dialog.setOnRadioButtonSelectListener(new OnRadioButtonSelectListener() { + @Override + public void onSelect(boolean leftButton) { + dialog.recreateList(leftButton ? leftItems : rightItems); + updateSize(app, dialog); + } + }); + dialog.setSelectedItemsListener(new SelectedItemsListener() { + @Override + public List createSelectedItems(List currentAllItems, boolean baseSRTM) { + List indexesToDownload = getIndexesToDownload(downloadItem); + List selectedItems = new ArrayList<>(); + + for (SelectableItem currentItem : currentAllItems) { + IndexItem indexItem = (IndexItem) currentItem.getObject(); + boolean baseItem = isBaseSRTMItem(indexItem); + if (indexesToDownload.contains(indexItem) + && (baseSRTM && baseItem || !baseSRTM && !baseItem)) { + selectedItems.add(currentItem); + } + } + return selectedItems; + } + }); + } + + private static OnApplySelectionListener getOnApplySelectionListener(final SelectItemsToDownloadListener listener) { + return new OnApplySelectionListener() { @Override public void onSelectionApplied(List selectedItems) { List indexItems = new ArrayList<>(); @@ -78,16 +180,70 @@ public class MultipleIndexesUiHelper { } listener.onItemsToDownloadSelected(indexItems); } - }); + }; } - private static List getIndexesToDownload(MultipleIndexItem multipleIndexItem) { - if (multipleIndexItem.hasActualDataToDownload()) { - // download left regions - return multipleIndexItem.getIndexesToDownload(); + private static void updateSize(OsmandApplication app, SelectMultipleItemsBottomSheet dialog) { + boolean isListDialog = dialog.isMultipleItem(); + dialog.setTitle(app.getString(isListDialog ? R.string.welmode_download_maps : R.string.srtm_unit_format)); + double sizeToDownload = getDownloadSizeInMb(dialog.getSelectedItems()); + String size = DownloadItem.getFormattedMb(app, sizeToDownload); + if (isListDialog) { + String total = app.getString(R.string.shared_string_total); + String description = app.getString(R.string.ltr_or_rtl_combine_via_colon, total, size); + dialog.setDescription(description); + } + String btnTitle = app.getString(R.string.shared_string_download); + if (sizeToDownload > 0) { + btnTitle = app.getString(R.string.ltr_or_rtl_combine_via_dash, btnTitle, size); + } + dialog.setConfirmButtonTitle(btnTitle); + } + + private static boolean isListDialog(OsmandApplication app, + List leftItems, List rightItems) { + return (isBaseSRTMMetricSystem(app) ? leftItems : rightItems).size() > 1; + } + + public static String getSRTMAbbrev(Context context, boolean base) { + return context.getString(base ? R.string.m : R.string.foot); + } + + public static String getSRTMExt(IndexItem indexItem) { + return isBaseSRTMItem(indexItem) + ? IndexConstants.BINARY_SRTM_MAP_INDEX_EXT : IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT; + } + + public static boolean isBaseSRTMItem(Object item) { + if (item instanceof IndexItem) { + return ((IndexItem) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT_ZIP); + } else if (item instanceof LocalIndexInfo) { + return ((LocalIndexInfo) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT); + } + return false; + } + + public static boolean isBaseSRTMMetricSystem(OsmandApplication app) { + return app.getSettings().METRIC_SYSTEM.get() != MetricsConstants.MILES_AND_FEET; + } + + private static List getIndexesToDownload(DownloadItem downloadItem) { + if (downloadItem instanceof MultipleIndexItem) { + if (downloadItem.hasActualDataToDownload()) { + // download left regions + return ((MultipleIndexItem) downloadItem).getIndexesToDownload(); + } else { + // download all regions again + return ((MultipleIndexItem) downloadItem).getAllIndexes(); + } } else { - // download all regions again - return multipleIndexItem.getAllIndexes(); + List indexesToDownload = new ArrayList<>(); + for (IndexItem indexItem : downloadItem.getRelatedGroup().getIndividualResources()) { + if (indexItem.getType() == SRTM_COUNTRY_FILE && indexItem.hasActualDataToDownload()) { + indexesToDownload.add(indexItem); + } + } + return indexesToDownload; } } @@ -110,4 +266,7 @@ public class MultipleIndexesUiHelper { void onItemsToDownloadSelected(List items); } + public interface SelectedItemsListener { + List createSelectedItems(List currentAllItems, boolean base); + } } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index a24e2c5b2a..767f116dda 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -51,8 +51,15 @@ import net.osmand.util.Algorithms; import java.io.File; import java.text.DateFormat; +import java.util.ArrayList; import java.util.List; +import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; +import static net.osmand.plus.download.DownloadActivityType.isSRTMItem; +import static net.osmand.plus.download.MultipleIndexesUiHelper.getSRTMAbbrev; +import static net.osmand.plus.download.MultipleIndexesUiHelper.isBaseSRTMItem; +import static net.osmand.plus.download.MultipleIndexesUiHelper.isBaseSRTMMetricSystem; + public class ItemViewHolder { protected final TextView nameTextView; @@ -160,20 +167,20 @@ public class ItemViewHolder { boolean disabled = checkDisabledAndClickAction(downloadItem); /// name and left item String name; - if(showTypeInName) { + if (showTypeInName) { name = downloadItem.getType().getString(context); } else { name = downloadItem.getVisibleName(context, context.getMyApplication().getRegions(), showParentRegionName); } String text = (!Algorithms.isEmpty(cityName) && !cityName.equals(name) ? cityName + "\n" : "") + name; nameTextView.setText(text); - if(!disabled) { + if (!disabled) { nameTextView.setTextColor(textColorPrimary); } else { nameTextView.setTextColor(textColorSecondary); } int color = textColorSecondary; - if(downloadItem.isDownloaded() && !isDownloading) { + if (downloadItem.isDownloaded() && !isDownloading) { int colorId = downloadItem.isOutdated() ? R.color.color_distance : R.color.color_ok; color = context.getResources().getColor(colorId); } @@ -189,6 +196,8 @@ public class ItemViewHolder { } descrTextView.setTextColor(textColorSecondary); if (!isDownloading) { + boolean srtmItem = isSRTMItem(downloadItem); + boolean baseMetricSystem = isBaseSRTMMetricSystem(context.getMyApplication()); progressBar.setVisibility(View.GONE); descrTextView.setVisibility(View.VISIBLE); if (downloadItem instanceof CustomIndexItem && (((CustomIndexItem) downloadItem).getSubName(context) != null)) { @@ -207,31 +216,55 @@ public class ItemViewHolder { MultipleIndexItem item = (MultipleIndexItem) downloadItem; String allRegionsHeader = context.getString(R.string.shared_strings_all_regions); String regionsHeader = context.getString(R.string.regions); - String allRegionsCount = String.valueOf(item.getAllIndexes().size()); - String leftToDownloadCount = String.valueOf(item.getIndexesToDownload().size()); + String allRegionsCountStr; + String leftToDownloadCountStr; + if (isSRTMItem(item)) { + List items = new ArrayList<>(); + for (IndexItem indexItem : item.getAllIndexes()) { + boolean baseItem = isBaseSRTMItem(indexItem); + if (baseMetricSystem && baseItem || !baseMetricSystem && !baseItem) { + items.add(indexItem); + } + } + allRegionsCountStr = String.valueOf(items.size()); + items.clear(); + for (IndexItem indexItem : item.getIndexesToDownload()) { + boolean baseItem = isBaseSRTMItem(indexItem); + if (!indexItem.isDownloaded() + && (baseMetricSystem && baseItem || !baseMetricSystem && !baseItem)) { + items.add(indexItem); + } + } + leftToDownloadCountStr = String.valueOf(items.size()); + } else { + allRegionsCountStr = String.valueOf(item.getAllIndexes().size()); + leftToDownloadCountStr = String.valueOf(item.getIndexesToDownload().size()); + } String header; String count; if (item.hasActualDataToDownload()) { if (!item.isDownloaded()) { header = allRegionsHeader; - count = leftToDownloadCount; + count = leftToDownloadCountStr; } else { header = regionsHeader; count = String.format( context.getString(R.string.ltr_or_rtl_combine_via_slash), - leftToDownloadCount, - allRegionsCount); + leftToDownloadCountStr, + allRegionsCountStr); } } else { header = allRegionsHeader; - count = allRegionsCount; + count = allRegionsCountStr; + } + String fullDescription = context.getString(R.string.ltr_or_rtl_combine_via_colon, header, count); + if (srtmItem) { + fullDescription += " (" + getSRTMAbbrev(context, baseMetricSystem) + ")"; } - String fullDescription = - context.getString(R.string.ltr_or_rtl_combine_via_colon, header, count); if (item.hasActualDataToDownload()) { fullDescription = context.getString( - R.string.ltr_or_rtl_combine_via_bold_point, fullDescription, - item.getSizeDescription(context)); + R.string.ltr_or_rtl_combine_via_bold_point, fullDescription, srtmItem + ? item.getSizeDescription(context, baseMetricSystem) : item.getSizeDescription(context)); } descrTextView.setText(fullDescription); } else { @@ -239,6 +272,9 @@ public class ItemViewHolder { String pattern = context.getString(R.string.ltr_or_rtl_combine_via_bold_point); String type = item.getType().getString(context); String size = item.getSizeDescription(context); + if (srtmItem) { + size += " (" + getSRTMAbbrev(context, isBaseSRTMItem(item)) + ")"; + } String date = item.getDate(dateFormat, showRemoteDate); String fullDescription = String.format(pattern, size, date); if (showTypeInDesc) { @@ -254,14 +290,14 @@ public class ItemViewHolder { if (showProgressInDesc) { double mb = downloadItem.getArchiveSizeMB(); - String v ; + String v; if (progress != -1) { v = context.getString(R.string.value_downloaded_of_max, mb * progress / 100, mb); } else { v = context.getString(R.string.file_size_in_mb, mb); } String fullDescription = v; - if(showTypeInDesc && downloadItem.getType() == DownloadActivityType.ROADS_FILE) { + if (showTypeInDesc && downloadItem.getType() == DownloadActivityType.ROADS_FILE) { fullDescription = context.getString(R.string.ltr_or_rtl_combine_via_bold_point, downloadItem.getType().getString(context), fullDescription); } @@ -302,7 +338,7 @@ public class ItemViewHolder { if (isDownloading) { rightImageButton.setImageDrawable(getContentIcon(context, R.drawable.ic_action_remove_dark)); rightImageButton.setContentDescription(context.getString(R.string.shared_string_cancel)); - } else if(!item.hasActualDataToDownload()) { + } else if (!item.hasActualDataToDownload()) { rightImageButton.setImageDrawable(getContentIcon(context, R.drawable.ic_overflow_menu_white)); rightImageButton.setContentDescription(context.getString(R.string.shared_string_more)); } else { @@ -318,7 +354,7 @@ public class ItemViewHolder { private int getDownloadActionIconId(@NonNull DownloadItem item) { return item instanceof MultipleIndexItem ? R.drawable.ic_action_multi_download : - R.drawable.ic_action_import; + R.drawable.ic_action_gsave_dark; } @SuppressLint("DefaultLocale") @@ -389,13 +425,13 @@ public class ItemViewHolder { return new View.OnClickListener() { @Override public void onClick(View v) { - if(isDownloading) { - if(silentCancelDownload) { + if (isDownloading) { + if (silentCancelDownload) { context.getDownloadThread().cancelDownload(item); } else { context.makeSureUserCancelDownload(item); } - } else if(!item.hasActualDataToDownload()){ + } else if (!item.hasActualDataToDownload()) { showContextMenu(v, item, item.getRelatedGroup()); } else { download(item, item.getRelatedGroup()); @@ -406,8 +442,8 @@ public class ItemViewHolder { } protected void showContextMenu(View v, - final DownloadItem downloadItem, - final DownloadResourceGroup parentOptional) { + final DownloadItem downloadItem, + final DownloadResourceGroup parentOptional) { OsmandApplication app = context.getMyApplication(); PopupMenu optionsMenu = new PopupMenu(context, v); MenuItem item; @@ -455,10 +491,11 @@ public class ItemViewHolder { } } } - if(!handled) { + if (!handled) { startDownload(item); } } + private void confirmDownload(final DownloadItem item) { AlertDialog.Builder builder = new AlertDialog.Builder(context); builder.setTitle(R.string.are_you_sure); @@ -476,15 +513,15 @@ public class ItemViewHolder { } private void startDownload(DownloadItem item) { - if (item instanceof MultipleIndexItem) { - selectIndexesToDownload((MultipleIndexItem) item); + if (item instanceof MultipleIndexItem || item.getType() == SRTM_COUNTRY_FILE) { + selectIndexesToDownload(item); } else if (item instanceof IndexItem) { IndexItem indexItem = (IndexItem) item; context.startDownload(indexItem); } } - private void selectIndexesToDownload(MultipleIndexItem item) { + private void selectIndexesToDownload(DownloadItem item) { OsmandApplication app = context.getMyApplication(); MultipleIndexesUiHelper.showDialog(item, context, app, dateFormat, showRemoteDate, new SelectItemsToDownloadListener() { @@ -498,7 +535,7 @@ public class ItemViewHolder { } private void confirmRemove(@NonNull final DownloadItem downloadItem, - @NonNull final List downloadedFiles) { + @NonNull final List downloadedFiles) { OsmandApplication app = context.getMyApplication(); AlertDialog.Builder confirm = new AlertDialog.Builder(context); @@ -526,7 +563,7 @@ public class ItemViewHolder { } private void remove(@NonNull LocalIndexType type, - @NonNull List filesToDelete) { + @NonNull List filesToDelete) { OsmandApplication app = context.getMyApplication(); LocalIndexOperationTask removeTask = new LocalIndexOperationTask( context, diff --git a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java index 3b259ab241..f67f4de704 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java @@ -74,6 +74,10 @@ import java.util.List; import java.util.Map; import java.util.Set; +import static net.osmand.plus.download.DownloadActivityType.isSRTMItem; +import static net.osmand.plus.download.MultipleIndexesUiHelper.getSRTMAbbrev; +import static net.osmand.plus.download.MultipleIndexesUiHelper.isBaseSRTMItem; + public class LocalIndexesFragment extends OsmandExpandableListFragment implements DownloadEvents, OnMapSourceUpdateListener, RenameCallback { private LoadLocalIndexTask asyncLoader; @@ -351,10 +355,10 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement getMyApplication().getResourceManager().closeFile(info.getFileName()); File tShm = new File(f.getParentFile(), f.getName() + "-shm"); File tWal = new File(f.getParentFile(), f.getName() + "-wal"); - if(tShm.exists()) { + if (tShm.exists()) { Algorithms.removeAllFiles(tShm); } - if(tWal.exists()) { + if (tWal.exists()) { Algorithms.removeAllFiles(tWal); } } @@ -370,8 +374,8 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement getMyApplication().getResourceManager().closeFile(info.getFileName()); } } else if (operation == CLEAR_TILES_OPERATION) { - ITileSource src = (ITileSource) info.getAttachedObject(); - if(src != null) { + ITileSource src = (ITileSource) info.getAttachedObject(); + if (src != null) { src.deleteTiles(info.getPathToData()); } } @@ -419,10 +423,10 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement @Override protected void onPostExecute(String result) { a.setProgressBarIndeterminateVisibility(false); - if(result != null && result.length() > 0) { + if (result != null && result.length() > 0) { Toast.makeText(a, result, Toast.LENGTH_LONG).show(); } - + if (operation == RESTORE_OPERATION || operation == BACKUP_OPERATION || operation == CLEAR_TILES_OPERATION) { a.reloadLocalIndexes(); } else { @@ -878,8 +882,8 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement private String getNameToDisplay(LocalIndexInfo child) { return child.getType() == LocalIndexType.VOICE_DATA ? FileNameTranslationHelper.getVoiceName(ctx, child.getFileName()) : FileNameTranslationHelper.getFileName(ctx, - ctx.getMyApplication().getResourceManager().getOsmandRegions(), - child.getFileName()); + ctx.getMyApplication().getResourceManager().getOsmandRegions(), + child.getFileName()); } @Override @@ -963,7 +967,8 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement return ctx.getString(R.string.download_roads_only_item); } else if (child.isBackupedData() && child.getFileName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) { return ctx.getString(R.string.download_wikipedia_maps); - } else if (child.isBackupedData() && child.getFileName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT)) { + } else if (child.isBackupedData() && (child.getFileName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + || child.getFileName().endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT))) { return ctx.getString(R.string.download_srtm_maps); } return ""; @@ -1029,6 +1034,10 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement builder.append(AndroidUtils.formatSize(ctx, child.getSize() * 1024l)); } + if (isSRTMItem(child)) { + builder.append(" (").append(getSRTMAbbrev(ctx, isBaseSRTMItem(child))).append(")"); + } + if (!Algorithms.isEmpty(child.getDescription())) { if (builder.length() > 0) { builder.append(" • "); @@ -1150,5 +1159,4 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement private DownloadActivity getDownloadActivity() { return (DownloadActivity) getActivity(); } - } diff --git a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java index e48e37878a..33bddc97a6 100644 --- a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java @@ -78,11 +78,11 @@ import java.util.concurrent.ConcurrentHashMap; import static net.osmand.IndexConstants.VOICE_INDEX_DIR; /** - * Resource manager is responsible to work with all resources + * 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. + * can't be loaded fully into memory & clear them on request. */ public class ResourceManager { @@ -91,10 +91,10 @@ public class ResourceManager { public static final String DEFAULT_WIKIVOYAGE_TRAVEL_OBF = "Default_wikivoyage.travel.obf"; private static final Log log = PlatformUtil.getLog(ResourceManager.class); - + protected static ResourceManager manager = null; - protected File dirWithTiles ; + protected File dirWithTiles; private List tilesCacheList = new ArrayList<>(); private BitmapTilesCache bitmapTilesCache; @@ -115,21 +115,22 @@ public class ResourceManager { STREET_LOOKUP, TRANSPORT, ADDRESS, - QUICK_SEARCH, + QUICK_SEARCH, ROUTING, TRANSPORT_ROUTING } - + public static class BinaryMapReaderResource { private BinaryMapIndexReader initialReader; private File filename; private List readers = new ArrayList<>(BinaryMapReaderResourceType.values().length); private boolean useForRouting; private boolean useForPublicTransport; + public BinaryMapReaderResource(File f, BinaryMapIndexReader initialReader) { this.filename = f; this.initialReader = initialReader; - while(readers.size() < BinaryMapReaderResourceType.values().length) { + while (readers.size() < BinaryMapReaderResourceType.values().length) { readers.add(null); } } @@ -173,7 +174,7 @@ public class ResourceManager { } initialReader = null; } - + public boolean isClosed() { return initialReader == null; } @@ -189,7 +190,7 @@ public class ResourceManager { public void setUseForRouting(boolean useForRouting) { this.useForRouting = useForRouting; } - + public boolean isUseForRouting() { return useForRouting; } @@ -202,34 +203,32 @@ public class ResourceManager { this.useForPublicTransport = useForPublicTransport; } } - + protected final Map fileReaders = new ConcurrentHashMap(); - - + private final Map addressMap = new ConcurrentHashMap(); - protected final Map amenityRepositories = new ConcurrentHashMap(); -// protected final Map routingMapFiles = new ConcurrentHashMap(); + protected final Map amenityRepositories = new ConcurrentHashMap(); + // protected final Map routingMapFiles = new ConcurrentHashMap(); protected final Map transportRepositories = new ConcurrentHashMap(); protected final Map travelRepositories = new ConcurrentHashMap(); protected final Map indexFileNames = new ConcurrentHashMap(); protected final Map basemapFileNames = new ConcurrentHashMap(); - - + 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 boolean depthContours; - + public ResourceManager(OsmandApplication context) { - + this.context = context; this.renderer = new MapRenderRepositories(context); @@ -271,7 +270,7 @@ public class ResourceManager { public MapTileDownloader getMapTileDownloader() { return tileDownloader; } - + public HandlerThread getRenderingBufferImageThread() { return renderingBufferImageThread; } @@ -293,17 +292,17 @@ public class ResourceManager { // ".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 ) { + } catch (Exception e) { } for (TilesCache tilesCache : tilesCacheList) { tilesCache.setDirWithTiles(dirWithTiles); } } - + public java.text.DateFormat getDateFormat() { return DateFormat.getDateFormat(context); } - + public OsmandApplication getContext() { return context; } @@ -323,7 +322,7 @@ public class ResourceManager { return null; } - public synchronized void tileDownloaded(DownloadRequest request){ + public synchronized void tileDownloaded(DownloadRequest request) { if (request instanceof TileLoadDownloadRequest) { TileLoadDownloadRequest req = ((TileLoadDownloadRequest) request); TilesCache cache = getTilesCache(req.tileSource); @@ -332,13 +331,13 @@ public class ResourceManager { } } } - + public synchronized boolean tileExistOnFileSystem(String file, ITileSource map, int x, int y, int zoom) { TilesCache cache = getTilesCache(map); return cache != null && cache.tileExistOnFileSystem(file, map, x, y, zoom); } - - public void clearTileForMap(String file, ITileSource map, int x, int y, int zoom){ + + public void clearTileForMap(String file, ITileSource map, int x, int y, int zoom) { TilesCache cache = getTilesCache(map); if (cache != null) { cache.getTileForMap(file, map, x, y, zoom, true, false, true, true); @@ -376,7 +375,7 @@ public class ResourceManager { ////////////////////////////////////////////// Working with indexes //////////////////////////////////////////////// - public List reloadIndexesOnStart(AppInitializer progress, List warnings){ + public List reloadIndexesOnStart(AppInitializer progress, List warnings) { close(); // check we have some assets to copy to sdcard warnings.addAll(checkAssets(progress, false)); @@ -429,7 +428,7 @@ public class ResourceManager { return warnings; } - public List indexFontFiles(IProgress progress){ + public List indexFontFiles(IProgress progress) { File file = context.getAppPath(IndexConstants.FONT_INDEX_DIR); file.mkdirs(); List warnings = new ArrayList(); @@ -473,10 +472,10 @@ public class ResourceManager { log.error("Error while loading tts files from assets", e); } } - + public List checkAssets(IProgress progress, boolean forceUpdate) { String fv = Version.getFullVersion(context); - if(context.getAppInitializer().isAppVersionChanged()) { + if (context.getAppInitializer().isAppVersionChanged()) { copyMissingJSAssets(); } if (!fv.equalsIgnoreCase(context.getSettings().PREVIOUS_INSTALLED_VERSION.get()) || forceUpdate) { @@ -509,7 +508,7 @@ public class ResourceManager { } return Collections.emptyList(); } - + private void copyRegionsBoundaries() { try { File file = context.getAppPath("regions.ocbf"); @@ -522,7 +521,7 @@ public class ResourceManager { log.error(e.getMessage(), e); } } - + private void copyPoiTypes() { try { File file = context.getAppPath(IndexConstants.SETTINGS_DIR + "poi_types.xml"); @@ -540,6 +539,7 @@ public class ResourceManager { private final static String ASSET_COPY_MODE__overwriteOnlyIfExists = "overwriteOnlyIfExists"; private final static String ASSET_COPY_MODE__alwaysOverwriteOrCopy = "alwaysOverwriteOrCopy"; private final static String ASSET_COPY_MODE__copyOnlyIfDoesNotExist = "copyOnlyIfDoesNotExist"; + private void unpackBundledAssets(AssetManager assetManager, File appDataDir, IProgress progress, boolean isFirstInstall) throws IOException, XmlPullParserException { List assetEntries = DownloadOsmandIndexesHelper.getBundledAssets(assetManager); for (AssetEntry asset : assetEntries) { @@ -581,7 +581,7 @@ public class ResourceManager { } public static void copyAssets(AssetManager assetManager, String assetName, File file) throws IOException { - if(file.exists()){ + if (file.exists()) { Algorithms.removeAllFiles(file); } file.getParentFile().mkdirs(); @@ -593,9 +593,9 @@ public class ResourceManager { } private List collectFiles(File dir, String ext, List files) { - if(dir.exists() && dir.canRead()) { + if (dir.exists() && dir.canRead()) { File[] lf = dir.listFiles(); - if(lf == null || lf.length == 0) { + if (lf == null || lf.length == 0) { return files; } for (File f : lf) { @@ -606,12 +606,10 @@ public class ResourceManager { } return files; } - - - + private void renameRoadsFiles(ArrayList files, File roadsPath) { Iterator it = files.iterator(); - while(it.hasNext()) { + 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, @@ -632,7 +630,7 @@ public class ResourceManager { 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); @@ -645,7 +643,7 @@ public class ResourceManager { if (OsmandPlugin.getEnabledPlugin(SRTMPlugin.class) != null || InAppPurchaseHelper.isContourLinesPurchased(context)) { collectFiles(context.getAppPath(IndexConstants.SRTM_INDEX_DIR), IndexConstants.BINARY_MAP_INDEX_EXT, files); } - + changesManager.collectChangesFiles(context.getAppPath(IndexConstants.LIVE_INDEX_DIR), IndexConstants.BINARY_MAP_INDEX_EXT, files); Collections.sort(files, Algorithms.getFileVersionComparator()); @@ -703,7 +701,8 @@ public class ResourceManager { log.error(String.format("File %s could not be read", f.getName()), e); } boolean wikiMap = (f.getName().contains("_wiki") || f.getName().contains(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)); - boolean srtmMap = f.getName().contains(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT); + boolean srtmMap = f.getName().contains(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + || f.getName().contains(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT); if (mapReader == null || (!Version.isPaidVersion(context) && wikiMap && !f.getName().equals(DEFAULT_WIKIVOYAGE_TRAVEL_OBF))) { warnings.add(MessageFormat.format(context.getString(R.string.version_index_is_not_supported), f.getName())); //$NON-NLS-1$ } else { @@ -714,9 +713,9 @@ public class ResourceManager { if (dateCreated == 0) { dateCreated = f.lastModified(); } - if(f.getParentFile().getName().equals(liveDir.getName())) { + if (f.getParentFile().getName().equals(liveDir.getName())) { boolean toUse = changesManager.index(f, dateCreated, mapReader); - if(!toUse) { + if (!toUse) { try { mapReader.close(); } catch (IOException e) { @@ -724,7 +723,7 @@ public class ResourceManager { } continue; } - } else if(!wikiMap && !srtmMap) { + } else if (!wikiMap && !srtmMap) { changesManager.indexMainMap(f, dateCreated); } indexFileNames.put(f.getName(), dateFormat.format(dateCreated)); //$NON-NLS-1$ @@ -733,7 +732,7 @@ public class ResourceManager { } renderer.initializeNewResource(progress, f, mapReader); BinaryMapReaderResource resource = new BinaryMapReaderResource(f, mapReader); - if (collectTravelFiles(resource)){ + if (collectTravelFiles(resource)) { //travel files are indexed continue; } @@ -746,7 +745,7 @@ public class ResourceManager { transportRepositories.put(f.getName(), resource); } // disable osmc for routing temporarily due to some bugs - if (mapReader.containsRouteData() && (!f.getParentFile().equals(liveDir) || + if (mapReader.containsRouteData() && (!f.getParentFile().equals(liveDir) || context.getSettings().USE_OSM_LIVE_FOR_ROUTING.get())) { resource.setUseForRouting(true); } @@ -789,7 +788,7 @@ public class ResourceManager { } } Iterator>> it = toAddPoiTypes.entrySet().iterator(); - while(it.hasNext()) { + while (it.hasNext()) { Entry> next = it.next(); PoiCategory category = next.getKey(); category.addExtraPoiTypes(next.getValue()); @@ -839,7 +838,7 @@ public class ResourceManager { } private boolean collectTravelFiles(BinaryMapReaderResource resource) { - if (resource.getFileName().contains(IndexConstants.BINARY_TRAVEL_GUIDE_MAP_INDEX_EXT)){ + if (resource.getFileName().contains(IndexConstants.BINARY_TRAVEL_GUIDE_MAP_INDEX_EXT)) { travelRepositories.put(resource.getFileName(), resource); return true; } @@ -855,7 +854,7 @@ public class ResourceManager { } } } - + ////////////////////////////////////////////// Working with amenities //////////////////////////////////////////////// public List getAmenityRepositories() { @@ -872,7 +871,7 @@ public class ResourceManager { } public List searchAmenities(SearchPoiTypeFilter filter, - double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom, final ResultMatcher matcher) { + double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom, final ResultMatcher matcher) { final List amenities = new ArrayList(); searchAmenitiesInProgress = true; try { @@ -901,8 +900,8 @@ public class ResourceManager { return amenities; } - public List searchAmenitiesOnThePath(List locations, double radius, SearchPoiTypeFilter filter, - ResultMatcher matcher) { + public List searchAmenitiesOnThePath(List locations, double radius, SearchPoiTypeFilter filter, + ResultMatcher matcher) { searchAmenitiesInProgress = true; final List amenities = new ArrayList(); try { @@ -921,9 +920,9 @@ public class ResourceManager { if (!filter.isEmpty()) { for (AmenityIndexRepository index : getAmenityRepositories()) { if (index.checkContainsInt( - MapUtils.get31TileNumberY(topLatitude), - MapUtils.get31TileNumberX(leftLongitude), - MapUtils.get31TileNumberY(bottomLatitude), + MapUtils.get31TileNumberY(topLatitude), + MapUtils.get31TileNumberX(leftLongitude), + MapUtils.get31TileNumberY(bottomLatitude), MapUtils.get31TileNumberX(rightLongitude))) { repos.add(index); } @@ -931,7 +930,7 @@ public class ResourceManager { if (!repos.isEmpty()) { for (AmenityIndexRepository r : repos) { List res = r.searchAmenitiesOnThePath(locations, radius, filter, matcher); - if(res != null) { + if (res != null) { amenities.addAll(res); } } @@ -943,12 +942,11 @@ public class ResourceManager { } return amenities; } - - - public boolean containsAmenityRepositoryToSearch(boolean searchByName){ + + public boolean containsAmenityRepositoryToSearch(boolean searchByName) { for (AmenityIndexRepository index : getAmenityRepositories()) { - if(searchByName){ - if(index instanceof AmenityIndexRepositoryBinary){ + if (searchByName) { + if (index instanceof AmenityIndexRepositoryBinary) { return true; } } else { @@ -957,10 +955,10 @@ public class ResourceManager { } return false; } - + public List searchAmenitiesByName(String searchQuery, - double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, - double lat, double lon, ResultMatcher matcher) { + double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, + double lat, double lon, ResultMatcher matcher) { List amenities = new ArrayList(); List list = new ArrayList(); int left = MapUtils.get31TileNumberX(leftLongitude); @@ -973,16 +971,16 @@ public class ResourceManager { } if (index instanceof AmenityIndexRepositoryBinary) { if (index.checkContainsInt(top, left, bottom, right)) { - if(index.checkContains(lat, lon)){ + if (index.checkContains(lat, lon)) { list.add(0, (AmenityIndexRepositoryBinary) index); } else { list.add((AmenityIndexRepositoryBinary) index); } - + } } } - + // Not using boundares results in very slow initial search if user has many maps installed // int left = 0; // int top = 0; @@ -1000,7 +998,7 @@ public class ResourceManager { return amenities; } - + public Map> searchAmenityCategoriesByName(String searchQuery, double lat, double lon) { Map> map = new LinkedHashMap>(); for (AmenityIndexRepository index : getAmenityRepositories()) { @@ -1016,17 +1014,17 @@ public class ResourceManager { public AmenityIndexRepositoryBinary getAmenityRepositoryByFileName(String filename) { return (AmenityIndexRepositoryBinary) amenityRepositories.get(filename); } - + ////////////////////////////////////////////// Working with address /////////////////////////////////////////// - - public RegionAddressRepository getRegionRepository(String name){ + + public RegionAddressRepository getRegionRepository(String name) { return addressMap.get(name); } - - public Collection getAddressRepositories(){ + + public Collection getAddressRepositories() { return addressMap.values(); } - + public Collection getFileReaders() { List fileNames = new ArrayList<>(fileReaders.keySet()); Collections.sort(fileNames, Algorithms.getStringVersionComparator()); @@ -1039,8 +1037,7 @@ public class ResourceManager { } return res; } - - + ////////////////////////////////////////////// Working with transport //////////////////////////////////////////////// private List getTransportRepositories(double topLat, double leftLon, double bottomLat, double rightLon) { @@ -1076,7 +1073,7 @@ public class ResourceManager { public List getRoutesForStop(TransportStop stop) { List rts = stop.getRoutes(); - if(rts != null) { + if (rts != null) { return rts; } return Collections.emptyList(); @@ -1086,26 +1083,26 @@ public class ResourceManager { public boolean updateRenderedMapNeeded(RotatedTileBox rotatedTileBox, DrawSettings drawSettings) { return renderer.updateMapIsNeeded(rotatedTileBox, drawSettings); } - - public void updateRendererMap(RotatedTileBox rotatedTileBox, OnMapLoadedListener mapLoadedListener){ + + public void updateRendererMap(RotatedTileBox rotatedTileBox, OnMapLoadedListener mapLoadedListener) { renderer.interruptLoadingMap(); asyncLoadingThread.requestToLoadMap(new MapLoadRequest(rotatedTileBox, mapLoadedListener)); } - - public void interruptRendering(){ + + public void interruptRendering() { renderer.interruptLoadingMap(); } - + public boolean isSearchAmenitiesInProgress() { return searchAmenitiesInProgress; } - + public MapRenderRepositories getRenderer() { return renderer; } - + ////////////////////////////////////////////// Closing methods //////////////////////////////////////////////// - + public void closeFile(String fileName) { amenityRepositories.remove(fileName); addressMap.remove(fileName); @@ -1114,12 +1111,12 @@ public class ResourceManager { travelRepositories.remove(fileName); renderer.closeConnection(fileName); BinaryMapReaderResource resource = fileReaders.remove(fileName); - if(resource != null) { + if (resource != null) { resource.close(); } - } + } - public synchronized void close(){ + public synchronized void close() { for (TilesCache tc : tilesCacheList) { tc.close(); } @@ -1130,7 +1127,7 @@ public class ResourceManager { travelRepositories.clear(); addressMap.clear(); amenityRepositories.clear(); - for(BinaryMapReaderResource res : fileReaders.values()) { + for (BinaryMapReaderResource res : fileReaders.values()) { res.close(); } fileReaders.clear(); @@ -1154,7 +1151,7 @@ public class ResourceManager { Collection fileReaders = getFileReaders(); List readers = new ArrayList<>(fileReaders.size()); for (BinaryMapReaderResource r : fileReaders) { - if (r.isUseForPublicTransport()) { + if (r.isUseForPublicTransport()) { BinaryMapIndexReader reader = r.getReader(BinaryMapReaderResourceType.TRANSPORT_ROUTING); if (reader != null) { readers.add(reader); @@ -1182,9 +1179,8 @@ public class ResourceManager { public Map getIndexFileNames() { return new LinkedHashMap(indexFileNames); } - - - public boolean containsBasemap(){ + + public boolean containsBasemap() { return !basemapFileNames.isEmpty(); } @@ -1218,13 +1214,13 @@ public class ResourceManager { } return map; } - + public synchronized void reloadTilesFromFS() { for (TilesCache tc : tilesCacheList) { tc.tilesOnFS.clear(); } } - + /// On low memory method /// public void onLowMemory() { log.info("On low memory"); @@ -1233,10 +1229,10 @@ public class ResourceManager { r.clearCache(); } renderer.clearCache(); - + System.gc(); } - + public GeoidAltitudeCorrection getGeoidAltitudeCorrection() { return geoidAltitudeCorrection; } @@ -1251,7 +1247,7 @@ public class ResourceManager { tc.clearTiles(); } } - + public IncrementalChangesManager getChangesManager() { return changesManager; } diff --git a/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java b/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java index 67fd73897e..bd424210f9 100644 --- a/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java +++ b/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java @@ -200,7 +200,7 @@ public class DataStorageHelper { terrainMemory = MemoryItem.builder() .setKey(TERRAIN_MEMORY) - .setExtensions(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + .setExtensions(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT, IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT) .setDirectories( createDirectory(SRTM_INDEX_DIR, true, EXTENSIONS, true), createDirectory(TILES_INDEX_DIR, false, PREFIX, false)) @@ -302,9 +302,9 @@ public class DataStorageHelper { } public DirectoryItem createDirectory(@NonNull String relativePath, - boolean processInternalDirectories, - CheckingType checkingType, - boolean addUnmatchedToOtherMemory) { + boolean processInternalDirectories, + CheckingType checkingType, + boolean addUnmatchedToOtherMemory) { String path = app.getAppPath(relativePath).getAbsolutePath(); return new DirectoryItem(path, processInternalDirectories, checkingType, addUnmatchedToOtherMemory); } @@ -323,7 +323,7 @@ public class DataStorageHelper { public interface UpdateMemoryInfoUIAdapter { void onMemoryInfoUpdate(); - + void onFinishUpdating(String tag); } From b14f62822bc31763285469eef2fb3dc706fb1cf2 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Fri, 9 Apr 2021 19:48:17 +0300 Subject: [PATCH 03/86] add srtm map download option, refactoring p1 --- .../plus/activities/LocalIndexHelper.java | 9 +- .../base/SelectMultipleItemsBottomSheet.java | 119 +++++++++--------- .../download/DownloadOsmandIndexesHelper.java | 7 +- .../plus/download/DownloadResources.java | 26 ++-- .../plus/download/MultipleIndexItem.java | 5 +- .../download/MultipleIndexesUiHelper.java | 32 ++--- .../plus/download/ui/ItemViewHolder.java | 20 +-- .../download/ui/LocalIndexesFragment.java | 8 +- .../plus/resources/ResourceManager.java | 10 +- .../datastorage/DataStorageHelper.java | 6 +- 10 files changed, 121 insertions(+), 121 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java b/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java index 87bfa5f698..3ffced9201 100644 --- a/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java +++ b/OsmAnd/src/net/osmand/plus/activities/LocalIndexHelper.java @@ -331,15 +331,16 @@ public class LocalIndexHelper { if (mapPath.canRead()) { for (File mapFile : listFilesSorted(mapPath)) { if (mapFile.isFile() && mapFile.getName().endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { + String fileName = mapFile.getName(); LocalIndexType lt = LocalIndexType.MAP_DATA; - if (mapFile.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) - || mapFile.getName().endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)) { + if (fileName.endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + || fileName.endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)) { lt = LocalIndexType.SRTM_DATA; - } else if (mapFile.getName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) { + } else if (fileName.endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) { lt = LocalIndexType.WIKI_DATA; } LocalIndexInfo info = new LocalIndexInfo(lt, mapFile, backup, app); - if (loadedMaps.containsKey(mapFile.getName()) && !backup) { + if (loadedMaps.containsKey(fileName) && !backup) { info.setLoaded(true); } updateDescription(info); diff --git a/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java index d09c4a3d6c..7629f80169 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java @@ -70,42 +70,6 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen private OnRadioButtonSelectListener onRadioButtonSelectListener; private SelectedItemsListener selectedItemsListener; - public static SelectMultipleItemsBottomSheet showInstance(@NonNull AppCompatActivity activity, - @NonNull List items, - @Nullable List selected, - boolean usedOnMap) { - SelectMultipleItemsBottomSheet fragment = new SelectMultipleItemsBottomSheet(); - fragment.setUsedOnMap(usedOnMap); - fragment.setItems(items); - fragment.setSelectedItems(selected); - FragmentManager fm = activity.getSupportFragmentManager(); - fragment.show(fm, TAG); - return fragment; - } - - public static SelectMultipleItemsBottomSheet showInstance(@NonNull AppCompatActivity activity, - @NonNull List items, - @Nullable List selected, - boolean usedOnMap, - String addDescription, - boolean customOptionsVisible, - boolean leftButtonSelected, - String leftRadioButtonText, - String rightRadioButtonText) { - SelectMultipleItemsBottomSheet fragment = new SelectMultipleItemsBottomSheet(); - fragment.setUsedOnMap(usedOnMap); - fragment.setItems(items); - fragment.setSelectedItems(selected); - fragment.setAddDescriptionText(addDescription); - fragment.setCustomOptionsVisible(customOptionsVisible); - fragment.setLeftButtonSelected(leftButtonSelected); - fragment.setLeftRadioButtonText(leftRadioButtonText); - fragment.setRightRadioButtonText(rightRadioButtonText); - FragmentManager fm = activity.getSupportFragmentManager(); - fragment.show(fm, TAG); - return fragment; - } - @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { @@ -392,6 +356,66 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen this.selectedItemsListener = selectedItemsListener; } + @Override + protected void setupRightButton() { + super.setupRightButton(); + applyButtonTitle = rightButton.findViewById(R.id.button_text); + } + + @Override + protected void onRightBottomButtonClick() { + if (onApplySelectionListener != null) { + onApplySelectionListener.onSelectionApplied(selectedItems); + } + dismiss(); + } + + @Override + protected int getRightBottomButtonTextId() { + return R.string.shared_string_apply; + } + + @Override + protected boolean useVerticalButtons() { + return true; + } + + public static SelectMultipleItemsBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull List items, + @Nullable List selected, + boolean usedOnMap) { + SelectMultipleItemsBottomSheet fragment = new SelectMultipleItemsBottomSheet(); + fragment.setUsedOnMap(usedOnMap); + fragment.setItems(items); + fragment.setSelectedItems(selected); + FragmentManager fm = activity.getSupportFragmentManager(); + fragment.show(fm, TAG); + return fragment; + } + + public static SelectMultipleItemsBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull List items, + @Nullable List selected, + boolean usedOnMap, + String addDescription, + boolean customOptionsVisible, + boolean leftButtonSelected, + String leftRadioButtonText, + String rightRadioButtonText) { + SelectMultipleItemsBottomSheet fragment = new SelectMultipleItemsBottomSheet(); + fragment.setUsedOnMap(usedOnMap); + fragment.setItems(items); + fragment.setSelectedItems(selected); + fragment.setAddDescriptionText(addDescription); + fragment.setCustomOptionsVisible(customOptionsVisible); + fragment.setLeftButtonSelected(leftButtonSelected); + fragment.setLeftRadioButtonText(leftRadioButtonText); + fragment.setRightRadioButtonText(rightRadioButtonText); + FragmentManager fm = activity.getSupportFragmentManager(); + fragment.show(fm, TAG); + return fragment; + } + public interface SelectionUpdateListener { void onSelectionUpdate(); } @@ -435,27 +459,4 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen } } - @Override - protected void setupRightButton() { - super.setupRightButton(); - applyButtonTitle = rightButton.findViewById(R.id.button_text); - } - - @Override - protected void onRightBottomButtonClick() { - if (onApplySelectionListener != null) { - onApplySelectionListener.onSelectionApplied(selectedItems); - } - dismiss(); - } - - @Override - protected int getRightBottomButtonTextId() { - return R.string.shared_string_apply; - } - - @Override - protected boolean useVerticalButtons() { - return true; - } } diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java b/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java index 8af9f9eb6b..a67de8b8d7 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadOsmandIndexesHelper.java @@ -142,7 +142,7 @@ public class DownloadOsmandIndexesHelper { } private static void listVoiceAssets(IndexFileList result, AssetManager amanager, PackageManager pm, - OsmandSettings settings) { + OsmandSettings settings) { try { File voicePath = settings.getContext().getAppPath(IndexConstants.VOICE_INDEX_DIR); // list = amanager.list("voice"); @@ -207,9 +207,6 @@ public class DownloadOsmandIndexesHelper { if (next == XmlPullParser.START_TAG) { DownloadActivityType tp = DownloadActivityType.getIndexType(parser.getAttributeValue(null, "type")); if (tp != null) { - if (tp == DownloadActivityType.SRTM_COUNTRY_FILE) { - log.debug("strUrl = " + strUrl); - } IndexItem it = tp.parseIndexItem(ctx, parser); if (it != null) { result.add(it); @@ -249,7 +246,7 @@ public class DownloadOsmandIndexesHelper { private final long dateModified; public AssetIndexItem(String fileName, String description, String date, - long dateModified, String size, long sizeL, String assetName, String destFile, DownloadActivityType type) { + long dateModified, String size, long sizeL, String assetName, String destFile, DownloadActivityType type) { super(fileName, description, dateModified, size, sizeL, sizeL, type); this.dateModified = dateModified; this.assetName = assetName; diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java index 7293d46659..fc4ddc2a01 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java @@ -264,7 +264,7 @@ public class DownloadResources extends DownloadResourceGroup { } private Map listWithAlternatives(final java.text.DateFormat dateFormat, File file, - final String ext, final Map files) { + final String ext, final Map files) { if (file.isDirectory()) { file.list(new FilenameFilter() { @Override @@ -518,7 +518,7 @@ public class DownloadResources extends DownloadResourceGroup { @Nullable private List collectIndexesOfType(@NonNull List regions, - @NonNull DownloadActivityType type) { + @NonNull DownloadActivityType type) { List collectedIndexes = new ArrayList<>(); for (WorldRegion region : regions) { List regionIndexes = getIndexItems(region); @@ -645,10 +645,10 @@ public class DownloadResources extends DownloadResourceGroup { } public static List findIndexItemsAt(OsmandApplication app, - List names, - DownloadActivityType type, - boolean includeDownloaded, - int limit) { + List names, + DownloadActivityType type, + boolean includeDownloaded, + int limit) { List res = new ArrayList<>(); OsmandRegions regions = app.getRegions(); DownloadIndexesThread downloadThread = app.getDownloadThread(); @@ -665,9 +665,9 @@ public class DownloadResources extends DownloadResourceGroup { } private static boolean isIndexItemDownloaded(DownloadIndexesThread downloadThread, - DownloadActivityType type, - WorldRegion downloadRegion, - List res) { + DownloadActivityType type, + WorldRegion downloadRegion, + List res) { List otherIndexItems = new ArrayList<>(downloadThread.getIndexes().getIndexItems(downloadRegion)); for (IndexItem indexItem : otherIndexItems) { @@ -680,7 +680,7 @@ public class DownloadResources extends DownloadResourceGroup { } private boolean doesListContainIndexWithType(List indexItems, - DownloadActivityType type) { + DownloadActivityType type) { if (indexItems != null) { for (IndexItem indexItem : indexItems) { if (indexItem.getType() == type) { @@ -692,9 +692,9 @@ public class DownloadResources extends DownloadResourceGroup { } private static boolean addIndexItem(DownloadIndexesThread downloadThread, - DownloadActivityType type, - WorldRegion downloadRegion, - List res) { + DownloadActivityType type, + WorldRegion downloadRegion, + List res) { List otherIndexItems = new ArrayList<>(downloadThread.getIndexes().getIndexItems(downloadRegion)); for (IndexItem indexItem : otherIndexItems) { diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java b/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java index 309f5210b9..a6a0a46686 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java @@ -19,8 +19,8 @@ public class MultipleIndexItem extends DownloadItem { private final List items; public MultipleIndexItem(@NonNull WorldRegion region, - @NonNull List items, - @NonNull DownloadActivityType type) { + @NonNull List items, + @NonNull DownloadActivityType type) { super(type); this.items = items; } @@ -139,4 +139,5 @@ public class MultipleIndexItem extends DownloadItem { } return result; } + } diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java b/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java index 575507d7d8..509609bd94 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java @@ -29,11 +29,11 @@ import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; public class MultipleIndexesUiHelper { public static void showDialog(@NonNull DownloadItem item, - @NonNull AppCompatActivity activity, - @NonNull final OsmandApplication app, - @NonNull DateFormat dateFormat, - boolean showRemoteDate, - @NonNull final SelectItemsToDownloadListener listener) { + @NonNull AppCompatActivity activity, + @NonNull final OsmandApplication app, + @NonNull DateFormat dateFormat, + boolean showRemoteDate, + @NonNull final SelectItemsToDownloadListener listener) { if (item.getType() == SRTM_COUNTRY_FILE) { showSRTMDialog(item, activity, app, dateFormat, showRemoteDate, listener); } else if (item instanceof MultipleIndexItem) { @@ -42,11 +42,11 @@ public class MultipleIndexesUiHelper { } public static void showBaseDialog(@NonNull MultipleIndexItem multipleIndexItem, - @NonNull AppCompatActivity activity, - @NonNull final OsmandApplication app, - @NonNull DateFormat dateFormat, - boolean showRemoteDate, - @NonNull final SelectItemsToDownloadListener listener) { + @NonNull AppCompatActivity activity, + @NonNull final OsmandApplication app, + @NonNull DateFormat dateFormat, + boolean showRemoteDate, + @NonNull final SelectItemsToDownloadListener listener) { List indexesToDownload = getIndexesToDownload(multipleIndexItem); List allItems = new ArrayList<>(); List selectedItems = new ArrayList<>(); @@ -80,11 +80,11 @@ public class MultipleIndexesUiHelper { } public static void showSRTMDialog(@NonNull final DownloadItem downloadItem, - @NonNull AppCompatActivity activity, - @NonNull final OsmandApplication app, - @NonNull final DateFormat dateFormat, - final boolean showRemoteDate, - @NonNull final SelectItemsToDownloadListener listener) { + @NonNull AppCompatActivity activity, + @NonNull final OsmandApplication app, + @NonNull final DateFormat dateFormat, + final boolean showRemoteDate, + @NonNull final SelectItemsToDownloadListener listener) { List selectedItems = new ArrayList<>(); final List leftItems = new ArrayList<>(); final List rightItems = new ArrayList<>(); @@ -201,7 +201,7 @@ public class MultipleIndexesUiHelper { } private static boolean isListDialog(OsmandApplication app, - List leftItems, List rightItems) { + List leftItems, List rightItems) { return (isBaseSRTMMetricSystem(app) ? leftItems : rightItems).size() > 1; } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index 767f116dda..07aab7de88 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -216,8 +216,8 @@ public class ItemViewHolder { MultipleIndexItem item = (MultipleIndexItem) downloadItem; String allRegionsHeader = context.getString(R.string.shared_strings_all_regions); String regionsHeader = context.getString(R.string.regions); - String allRegionsCountStr; - String leftToDownloadCountStr; + String allRegionsCount; + String leftToDownloadCount; if (isSRTMItem(item)) { List items = new ArrayList<>(); for (IndexItem indexItem : item.getAllIndexes()) { @@ -226,7 +226,7 @@ public class ItemViewHolder { items.add(indexItem); } } - allRegionsCountStr = String.valueOf(items.size()); + allRegionsCount = String.valueOf(items.size()); items.clear(); for (IndexItem indexItem : item.getIndexesToDownload()) { boolean baseItem = isBaseSRTMItem(indexItem); @@ -235,27 +235,27 @@ public class ItemViewHolder { items.add(indexItem); } } - leftToDownloadCountStr = String.valueOf(items.size()); + leftToDownloadCount = String.valueOf(items.size()); } else { - allRegionsCountStr = String.valueOf(item.getAllIndexes().size()); - leftToDownloadCountStr = String.valueOf(item.getIndexesToDownload().size()); + allRegionsCount = String.valueOf(item.getAllIndexes().size()); + leftToDownloadCount = String.valueOf(item.getIndexesToDownload().size()); } String header; String count; if (item.hasActualDataToDownload()) { if (!item.isDownloaded()) { header = allRegionsHeader; - count = leftToDownloadCountStr; + count = leftToDownloadCount; } else { header = regionsHeader; count = String.format( context.getString(R.string.ltr_or_rtl_combine_via_slash), - leftToDownloadCountStr, - allRegionsCountStr); + leftToDownloadCount, + allRegionsCount); } } else { header = allRegionsHeader; - count = allRegionsCountStr; + count = allRegionsCount; } String fullDescription = context.getString(R.string.ltr_or_rtl_combine_via_colon, header, count); if (srtmItem) { diff --git a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java index f67f4de704..91b1aabc76 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java @@ -511,7 +511,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement ItemClickListener listener = new ContextMenuAdapter.ItemClickListener() { @Override public boolean onContextMenuClick(ArrayAdapter adapter, - int itemId, int pos, boolean isChecked, int[] viewCoordinates) { + int itemId, int pos, boolean isChecked, int[] viewCoordinates) { localOptionsMenu(itemId); return true; } @@ -612,7 +612,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement } private void openSelectionMode(final int actionResId, final int actionIconId, - final DialogInterface.OnClickListener listener) { + final DialogInterface.OnClickListener listener) { final int colorResId = getMyApplication().getSettings().isLightContent() ? R.color.active_buttons_and_links_text_light : R.color.active_buttons_and_links_text_dark; String value = getString(actionResId); if (value.endsWith("...")) { @@ -713,7 +713,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement } public void openSelectionMode(int stringRes, int darkIcon, DialogInterface.OnClickListener listener, - EnumSet filter) { + EnumSet filter) { if (filter != null) { listAdapter.filterCategories(filter); } @@ -864,7 +864,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement @Override public View getChildView(final int groupPosition, final int childPosition, - boolean isLastChild, View convertView, ViewGroup parent) { + boolean isLastChild, View convertView, ViewGroup parent) { LocalIndexInfoViewHolder viewHolder; if (convertView == null) { LayoutInflater inflater = LayoutInflater.from(ctx); diff --git a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java index 33bddc97a6..9271a3b1c2 100644 --- a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java @@ -871,7 +871,7 @@ public class ResourceManager { } public List searchAmenities(SearchPoiTypeFilter filter, - double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom, final ResultMatcher matcher) { + double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, int zoom, final ResultMatcher matcher) { final List amenities = new ArrayList(); searchAmenitiesInProgress = true; try { @@ -901,7 +901,7 @@ public class ResourceManager { } public List searchAmenitiesOnThePath(List locations, double radius, SearchPoiTypeFilter filter, - ResultMatcher matcher) { + ResultMatcher matcher) { searchAmenitiesInProgress = true; final List amenities = new ArrayList(); try { @@ -957,8 +957,8 @@ public class ResourceManager { } public List searchAmenitiesByName(String searchQuery, - double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, - double lat, double lon, ResultMatcher matcher) { + double topLatitude, double leftLongitude, double bottomLatitude, double rightLongitude, + double lat, double lon, ResultMatcher matcher) { List amenities = new ArrayList(); List list = new ArrayList(); int left = MapUtils.get31TileNumberX(leftLongitude); @@ -1056,7 +1056,7 @@ public class ResourceManager { public List searchTransportSync(double topLat, double leftLon, double bottomLat, double rightLon, - ResultMatcher matcher) throws IOException { + ResultMatcher matcher) throws IOException { TransportStopsRouteReader readers = new TransportStopsRouteReader(getTransportRepositories(topLat, leftLon, bottomLat, rightLon)); List stops = new ArrayList<>(); diff --git a/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java b/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java index bd424210f9..763f8ff996 100644 --- a/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java +++ b/OsmAnd/src/net/osmand/plus/settings/datastorage/DataStorageHelper.java @@ -302,9 +302,9 @@ public class DataStorageHelper { } public DirectoryItem createDirectory(@NonNull String relativePath, - boolean processInternalDirectories, - CheckingType checkingType, - boolean addUnmatchedToOtherMemory) { + boolean processInternalDirectories, + CheckingType checkingType, + boolean addUnmatchedToOtherMemory) { String path = app.getAppPath(relativePath).getAbsolutePath(); return new DirectoryItem(path, processInternalDirectories, checkingType, addUnmatchedToOtherMemory); } From 9368b7383a5647e285748c63a9db65bdd8d04b5f Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Mon, 12 Apr 2021 20:14:27 +0300 Subject: [PATCH 04/86] Refactoring SRTMf download ui, part 1 --- .../plus/base/SelectModeBottomSheet.java | 96 ++++ .../base/SelectMultipleItemsBottomSheet.java | 445 ++++-------------- .../SelectMultipleWithModeBottomSheet.java | 51 ++ .../plus/base/SelectionBottomSheet.java | 201 ++++++++ .../plus/download/DownloadActivityType.java | 2 +- .../plus/download/MultipleIndexItem.java | 2 +- ...Helper.java => SelectIndexesUiHelper.java} | 222 ++++++--- .../plus/download/ui/ItemViewHolder.java | 12 +- .../download/ui/LocalIndexesFragment.java | 4 +- 9 files changed, 600 insertions(+), 435 deletions(-) create mode 100644 OsmAnd/src/net/osmand/plus/base/SelectModeBottomSheet.java create mode 100644 OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java create mode 100644 OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java rename OsmAnd/src/net/osmand/plus/download/{MultipleIndexesUiHelper.java => SelectIndexesUiHelper.java} (52%) diff --git a/OsmAnd/src/net/osmand/plus/base/SelectModeBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectModeBottomSheet.java new file mode 100644 index 0000000000..f1ecf92743 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/base/SelectModeBottomSheet.java @@ -0,0 +1,96 @@ +package net.osmand.plus.base; + +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.FragmentManager; + +import net.osmand.AndroidUtils; +import net.osmand.plus.R; +import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; +import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; + +import java.util.Collections; +import java.util.List; + +public class SelectModeBottomSheet extends SelectionBottomSheet { + + public static final String TAG = SelectModeBottomSheet.class.getSimpleName(); + + private BottomSheetItemWithDescription previewUi; + + private List modes; + private SelectableItem previewItem; + + @Override + protected void initHeaderUi() { + radioGroup.setItems(modes); + + AndroidUiHelper.setVisibility(View.VISIBLE, secondaryDescription, toggleContainer); + + AndroidUiHelper.setVisibility(View.GONE, checkBox, checkBoxTitle, + primaryDescription, selectedSize, selectAllButton); + } + + @Override + protected void createSelectionUi() { + previewUi = (BottomSheetItemWithDescription) new BottomSheetItemWithDescription.Builder() + .setDescription(previewItem.getDescription()) + .setDescriptionColorId(AndroidUtils.getSecondaryTextColorId(nightMode)) + .setTitle(previewItem.getTitle()) + .setIcon(uiUtilities.getIcon(previewItem.getIconId(), activeColorRes)) + .setTag(previewItem) + .setLayoutId(R.layout.bottom_sheet_item_with_descr_56dp) + .create(); + items.add(previewUi); + } + + private void updatePreviewUi() { + previewUi.setTitle(previewItem.getTitle()); + previewUi.setIcon(uiUtilities.getIcon(previewItem.getIconId(), activeColorRes)); + previewUi.setDescription(previewItem.getDescription()); + previewUi.setTag(previewItem); + } + + private void setModes(@NonNull List modes) { + this.modes = modes; + } + + public void setSelectedMode(@NonNull RadioItem mode) { + radioGroup.setSelectedItem(mode); + } + + public void setPreviewItem(@NonNull SelectableItem preview) { + this.previewItem = preview; + if (previewUi != null) { + updatePreviewUi(); + } + } + + @Override + public void setDescription(@NonNull String description) { + secondaryDescription.setText(description); + } + + @NonNull + @Override + public List getSelection() { + return Collections.singletonList(previewItem); + } + + public static SelectModeBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull SelectableItem previewItem, + @NonNull List radioItems, + boolean usedOnMap) { + SelectModeBottomSheet fragment = new SelectModeBottomSheet(); + fragment.setUsedOnMap(usedOnMap); + fragment.setModes(radioItems); + fragment.setPreviewItem(previewItem); + FragmentManager fm = activity.getSupportFragmentManager(); + fragment.show(fm, TAG); + return fragment; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java index 7629f80169..ab8ae1f3e9 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java @@ -1,12 +1,7 @@ package net.osmand.plus.base; import android.content.res.ColorStateList; -import android.os.Bundle; -import android.view.LayoutInflater; import android.view.View; -import android.view.ViewGroup; -import android.widget.LinearLayout; -import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -16,22 +11,11 @@ import androidx.core.widget.CompoundButtonCompat; import androidx.fragment.app.FragmentManager; import net.osmand.AndroidUtils; -import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; -import net.osmand.plus.UiUtilities; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton; import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton.Builder; -import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; -import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; -import net.osmand.plus.base.bottomsheetmenu.simpleitems.SimpleDividerItem; -import net.osmand.plus.download.MultipleIndexesUiHelper.SelectedItemsListener; -import net.osmand.plus.helpers.AndroidUiHelper; -import net.osmand.plus.widgets.MultiStateToggleButton; -import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener; -import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; import net.osmand.util.Algorithms; -import net.osmand.view.ThreeStateCheckbox; import java.util.ArrayList; import java.util.List; @@ -40,180 +24,68 @@ import static net.osmand.view.ThreeStateCheckbox.State.CHECKED; import static net.osmand.view.ThreeStateCheckbox.State.MISC; import static net.osmand.view.ThreeStateCheckbox.State.UNCHECKED; -public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragment { +public class SelectMultipleItemsBottomSheet extends SelectionBottomSheet { public static final String TAG = SelectMultipleItemsBottomSheet.class.getSimpleName(); - private OsmandApplication app; - private UiUtilities uiUtilities; - - private TextView title; - private TextView description; - private TextView applyButtonTitle; - private TextView checkBoxTitle; - private TextView selectedSize; - private ThreeStateCheckbox checkBox; - - private int sizeAboveList = 0; - private int activeColorRes; - private int secondaryColorRes; - private String addDescriptionText; - private String leftRadioButtonText; - private String rightRadioButtonText; - private boolean customOptionsVisible; - private boolean leftButtonSelected; - private final List allItems = new ArrayList<>(); private final List selectedItems = new ArrayList<>(); private SelectionUpdateListener selectionUpdateListener; - private OnApplySelectionListener onApplySelectionListener; - private OnRadioButtonSelectListener onRadioButtonSelectListener; - private SelectedItemsListener selectedItemsListener; - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { - View mainView = super.onCreateView(inflater, parent, savedInstanceState); - onSelectedItemsChanged(); - return mainView; - } @Override - public void createMenuItems(Bundle savedInstanceState) { - app = requiredMyApplication(); - uiUtilities = app.getUIUtilities(); - activeColorRes = nightMode ? R.color.icon_color_active_dark : R.color.icon_color_active_light; - secondaryColorRes = nightMode ? R.color.icon_color_secondary_dark : R.color.icon_color_secondary_light; - - items.add(createTitleItem()); - items.add(new SimpleDividerItem(app)); - sizeAboveList = items.size(); - createListItems(); - } - - @Override - public void onPause() { - super.onPause(); - if (requireActivity().isChangingConfigurations()) { - dismiss(); - } - } - - private BaseBottomSheetItem createTitleItem() { - LayoutInflater themedInflater = UiUtilities.getInflater(requireContext(), nightMode); - View view = themedInflater.inflate(R.layout.settings_group_title, null); - - checkBox = view.findViewById(R.id.check_box); - checkBoxTitle = view.findViewById(R.id.check_box_title); - description = view.findViewById(R.id.description); - selectedSize = view.findViewById(R.id.selected_size); - title = view.findViewById(R.id.title); - View selectAllButton = view.findViewById(R.id.select_all_button); - TextView addDescription = view.findViewById(R.id.additional_description); - LinearLayout customRadioButtons = view.findViewById(R.id.custom_radio_buttons); - - if (!isMultipleItem()) { - AndroidUiHelper.setVisibility(View.GONE, description, selectedSize, selectAllButton); - } else { - selectAllButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - checkBox.performClick(); - boolean checked = checkBox.getState() == CHECKED; - if (checked) { - selectedItems.addAll(allItems); - } else { - selectedItems.clear(); - } - onSelectedItemsChanged(); - updateItems(checked); + protected void initHeaderUi() { + selectAllButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + checkBox.performClick(); + boolean checked = checkBox.getState() == CHECKED; + if (checked) { + selectedItems.addAll(allItems); + } else { + selectedItems.clear(); } - }); - } - - if (!Algorithms.isEmpty(addDescriptionText)) { - addDescription.setText(addDescriptionText); - AndroidUiHelper.setVisibility(View.VISIBLE, addDescription); - } - - if (customOptionsVisible) { - AndroidUiHelper.setVisibility(View.VISIBLE, customRadioButtons); - RadioItem leftRadioButton = new RadioItem(leftRadioButtonText); - RadioItem rightRadioButton = new RadioItem(rightRadioButtonText); - MultiStateToggleButton toggleButtons = - new MultiStateToggleButton(app, customRadioButtons, nightMode); - toggleButtons.setItems(leftRadioButton, rightRadioButton); - toggleButtons.updateView(true); - leftRadioButton.setOnClickListener(new OnRadioItemClickListener() { - @Override - public boolean onRadioItemClick(RadioItem radioItem, View view) { - onRadioButtonSelectListener.onSelect(leftButtonSelected = true); - updateSelectedSizeView(); - updateSelectAllButton(); - updateApplyButtonEnable(); - return true; - } - }); - rightRadioButton.setOnClickListener(new OnRadioItemClickListener() { - @Override - public boolean onRadioItemClick(RadioItem radioItem, View view) { - onRadioButtonSelectListener.onSelect(leftButtonSelected = false); - updateSelectedSizeView(); - updateSelectAllButton(); - updateApplyButtonEnable(); - return true; - } - }); - toggleButtons.setSelectedItem(leftButtonSelected ? leftRadioButton : rightRadioButton); - } - - return new SimpleBottomSheetItem.Builder().setCustomView(view).create(); - } - - private void createListItems() { - if (isMultipleItem()) { - for (int i = 0; i < allItems.size(); i++) { - final SelectableItem item = allItems.get(i); - boolean checked = selectedItems.contains(item); - final int finalI = i; - items.add(new Builder() - .setChecked(checked) - .setButtonTintList(AndroidUtils.createCheckedColorStateList(app, secondaryColorRes, activeColorRes)) - .setDescription(item.description) - .setIcon(uiUtilities.getIcon(item.iconId, activeColorRes)) - .setTitle(item.title) - .setLayoutId(R.layout.bottom_sheet_item_with_descr_and_checkbox_56dp) - .setTag(item) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - BottomSheetItemWithCompoundButton item = (BottomSheetItemWithCompoundButton) items.get(finalI + sizeAboveList); - boolean checked = item.isChecked(); - item.setChecked(!checked); - SelectableItem tag = (SelectableItem) item.getTag(); - if (!checked) { - selectedItems.add(tag); - } else { - selectedItems.remove(tag); - } - onSelectedItemsChanged(); - } - }) - .create()); + onSelectedItemsChanged(); + updateItems(checked); } - } else if (allItems.size() == 1) { - final SelectableItem item = allItems.get(0); - items.add(new Builder() - .setDescription(item.description) - .setDescriptionColorId(AndroidUtils.getSecondaryTextColorId(nightMode)) - .setIcon(uiUtilities.getIcon(item.iconId, activeColorRes)) - .setTitle(item.title) - .setLayoutId(R.layout.bottom_sheet_item_with_descr_56dp) - .setTag(item) - .create()); + }); + } + + @Override + protected void createSelectionUi() { + for (final SelectableItem item : allItems) { + boolean checked = selectedItems.contains(item); + final BottomSheetItemWithCompoundButton[] uiItem = new BottomSheetItemWithCompoundButton[1]; + final Builder builder = (BottomSheetItemWithCompoundButton.Builder) new Builder(); + builder.setChecked(checked) + .setButtonTintList(AndroidUtils.createCheckedColorStateList(app, secondaryColorRes, activeColorRes)) + .setLayoutId(R.layout.bottom_sheet_item_with_descr_and_checkbox_56dp) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean checked = !uiItem[0].isChecked(); + uiItem[0].setChecked(checked); + SelectableItem tag = (SelectableItem) uiItem[0].getTag(); + if (checked) { + selectedItems.add(tag); + } else { + selectedItems.remove(tag); + } + onSelectedItemsChanged(); + } + }) + .setTag(item); + setupListItem(builder, item); + uiItem[0] = builder.create(); + items.add(uiItem[0]); } } + @Override + protected void notifyUiInitialized() { + onSelectedItemsChanged(); + super.notifyUiInitialized(); + } + private void onSelectedItemsChanged() { updateSelectAllButton(); updateSelectedSizeView(); @@ -223,32 +95,38 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen } } + private void setupListItem(Builder builder, SelectableItem item) { + builder.setTitle(item.getTitle()); + builder.setDescription(item.getDescription()); + builder.setIcon(uiUtilities.getIcon(item.getIconId(), activeColorRes)); + } + private void updateSelectAllButton() { - if (isMultipleItem()) { - String checkBoxTitle; - if (Algorithms.isEmpty(selectedItems)) { - checkBox.setState(UNCHECKED); - checkBoxTitle = getString(R.string.shared_string_select_all); - } else { - checkBox.setState(selectedItems.containsAll(allItems) ? CHECKED : MISC); - checkBoxTitle = getString(R.string.shared_string_deselect_all); - } - int checkBoxColor = checkBox.getState() == UNCHECKED ? secondaryColorRes : activeColorRes; - CompoundButtonCompat.setButtonTintList(checkBox, ColorStateList.valueOf(ContextCompat.getColor(app, checkBoxColor))); - this.checkBoxTitle.setText(checkBoxTitle); + String checkBoxTitle; + if (Algorithms.isEmpty(selectedItems)) { + checkBox.setState(UNCHECKED); + checkBoxTitle = getString(R.string.shared_string_select_all); + } else { + checkBox.setState(selectedItems.containsAll(allItems) ? CHECKED : MISC); + checkBoxTitle = getString(R.string.shared_string_deselect_all); } + int checkBoxColor = checkBox.getState() == UNCHECKED ? secondaryColorRes : activeColorRes; + CompoundButtonCompat.setButtonTintList(checkBox, ColorStateList.valueOf(ContextCompat.getColor(app, checkBoxColor))); + this.checkBoxTitle.setText(checkBoxTitle); } private void updateSelectedSizeView() { - if (isMultipleItem()) { - String selected = String.valueOf(selectedItems.size()); - String all = String.valueOf(allItems.size()); - selectedSize.setText(getString(R.string.ltr_or_rtl_combine_via_slash, selected, all)); - } + String selected = String.valueOf(selectedItems.size()); + String all = String.valueOf(allItems.size()); + selectedSize.setText(getString(R.string.ltr_or_rtl_combine_via_slash, selected, all)); } private void updateApplyButtonEnable() { - rightButton.setEnabled(!Algorithms.isEmpty(selectedItems)); + if (Algorithms.isEmpty(selectedItems)) { + rightButton.setEnabled(false); + } else { + rightButton.setEnabled(true); + } } private void updateItems(boolean checked) { @@ -259,127 +137,30 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen } } - public void setItems(List allItems) { - this.allItems.clear(); + protected void setItems(List allItems) { if (!Algorithms.isEmpty(allItems)) { + this.allItems.clear(); this.allItems.addAll(allItems); } } - private void setSelectedItems(List selected) { - this.selectedItems.clear(); + protected void setSelectedItems(List selected) { if (!Algorithms.isEmpty(selected)) { - /*List prevDownloadItems = new ArrayList<>(this.selectedItems); - for (SelectableItem prevDownloadItem : selected) { - Object object = prevDownloadItem.getObject(); - if (object instanceof IndexItem && ((IndexItem) object).isDownloaded()) { - prevDownloadItems.add(prevDownloadItem); - } - } - selected.removeAll(prevDownloadItems);*/ - this.selectedItems.addAll(selected); + selectedItems.clear(); + selectedItems.addAll(selected); } } - public void recreateList(List allItems) { - setItems(allItems); - if (selectedItemsListener != null) { - setSelectedItems(selectedItemsListener.createSelectedItems(this.allItems, leftButtonSelected)); - } - if (items.size() > sizeAboveList) { - for (int i = 0; i < this.allItems.size(); i++) { - SelectableItem item = this.allItems.get(i); - BottomSheetItemWithDescription button = (BottomSheetItemWithDescription) items.get(i + sizeAboveList); - button.setDescription(item.description); - button.setTitle(item.title); - button.setTag(item); - if (isMultipleItem()) { - ((BottomSheetItemWithCompoundButton) button).setChecked(selectedItems.contains(item)); - } - } - } - } - - public boolean isMultipleItem() { - return allItems.size() > 1; - } - - public List getSelectedItems() { + @NonNull + @Override + public List getSelection() { return selectedItems; } - public void setConfirmButtonTitle(@NonNull String confirmButtonTitle) { - applyButtonTitle.setText(confirmButtonTitle); - } - - public void setTitle(@NonNull String title) { - this.title.setText(title); - } - - public void setDescription(@NonNull String description) { - this.description.setText(description); - } - - public void setAddDescriptionText(String addDescriptionText) { - this.addDescriptionText = addDescriptionText; - } - - public void setLeftRadioButtonText(String leftRadioButtonText) { - this.leftRadioButtonText = leftRadioButtonText; - } - - public void setRightRadioButtonText(String rightRadioButtonText) { - this.rightRadioButtonText = rightRadioButtonText; - } - - public void setCustomOptionsVisible(boolean customOptionsVisible) { - this.customOptionsVisible = customOptionsVisible; - } - - public void setLeftButtonSelected(boolean leftButtonSelected) { - this.leftButtonSelected = leftButtonSelected; - } - public void setSelectionUpdateListener(SelectionUpdateListener selectionUpdateListener) { this.selectionUpdateListener = selectionUpdateListener; } - public void setOnApplySelectionListener(OnApplySelectionListener onApplySelectionListener) { - this.onApplySelectionListener = onApplySelectionListener; - } - - public void setOnRadioButtonSelectListener(OnRadioButtonSelectListener onRadioButtonSelectListener) { - this.onRadioButtonSelectListener = onRadioButtonSelectListener; - } - - public void setSelectedItemsListener(SelectedItemsListener selectedItemsListener) { - this.selectedItemsListener = selectedItemsListener; - } - - @Override - protected void setupRightButton() { - super.setupRightButton(); - applyButtonTitle = rightButton.findViewById(R.id.button_text); - } - - @Override - protected void onRightBottomButtonClick() { - if (onApplySelectionListener != null) { - onApplySelectionListener.onSelectionApplied(selectedItems); - } - dismiss(); - } - - @Override - protected int getRightBottomButtonTextId() { - return R.string.shared_string_apply; - } - - @Override - protected boolean useVerticalButtons() { - return true; - } - public static SelectMultipleItemsBottomSheet showInstance(@NonNull AppCompatActivity activity, @NonNull List items, @Nullable List selected, @@ -393,70 +174,8 @@ public class SelectMultipleItemsBottomSheet extends MenuBottomSheetDialogFragmen return fragment; } - public static SelectMultipleItemsBottomSheet showInstance(@NonNull AppCompatActivity activity, - @NonNull List items, - @Nullable List selected, - boolean usedOnMap, - String addDescription, - boolean customOptionsVisible, - boolean leftButtonSelected, - String leftRadioButtonText, - String rightRadioButtonText) { - SelectMultipleItemsBottomSheet fragment = new SelectMultipleItemsBottomSheet(); - fragment.setUsedOnMap(usedOnMap); - fragment.setItems(items); - fragment.setSelectedItems(selected); - fragment.setAddDescriptionText(addDescription); - fragment.setCustomOptionsVisible(customOptionsVisible); - fragment.setLeftButtonSelected(leftButtonSelected); - fragment.setLeftRadioButtonText(leftRadioButtonText); - fragment.setRightRadioButtonText(rightRadioButtonText); - FragmentManager fm = activity.getSupportFragmentManager(); - fragment.show(fm, TAG); - return fragment; - } - public interface SelectionUpdateListener { void onSelectionUpdate(); } - public interface OnApplySelectionListener { - void onSelectionApplied(List selectedItems); - } - - public interface OnRadioButtonSelectListener { - void onSelect(boolean leftButton); - } - - public static class SelectableItem { - private String title; - private String description; - private int iconId; - private Object object; - - public void setTitle(String title) { - this.title = title; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - - public void setIconId(int iconId) { - this.iconId = iconId; - } - - public void setObject(Object object) { - this.object = object; - } - - public Object getObject() { - return object; - } - } - -} +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java new file mode 100644 index 0000000000..5aee912d0d --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java @@ -0,0 +1,51 @@ +package net.osmand.plus.base; + +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.FragmentManager; + +import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; + +import java.util.List; + +public class SelectMultipleWithModeBottomSheet extends SelectMultipleItemsBottomSheet { + + private List modes; + + @Override + protected void initHeaderUi() { + super.initHeaderUi(); + radioGroup.setItems(modes); + + AndroidUiHelper.setVisibility(View.VISIBLE, secondaryDescription, toggleContainer, + checkBox, checkBoxTitle, primaryDescription, selectedSize, selectAllButton); + } + + private void setModes(@NonNull List modes) { + this.modes = modes; + } + + public void setSelectedMode(@NonNull RadioItem mode) { + radioGroup.setSelectedItem(mode); + } + + public static SelectMultipleWithModeBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull List items, + @Nullable List selected, + @NonNull List modes, + boolean usedOnMap) { + SelectMultipleWithModeBottomSheet fragment = new SelectMultipleWithModeBottomSheet(); + fragment.setUsedOnMap(usedOnMap); + fragment.setItems(items); + fragment.setSelectedItems(selected); + fragment.setModes(modes); + FragmentManager fm = activity.getSupportFragmentManager(); + fragment.show(fm, TAG); + return fragment; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java new file mode 100644 index 0000000000..978f649083 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java @@ -0,0 +1,201 @@ +package net.osmand.plus.base; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; +import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; +import net.osmand.plus.base.bottomsheetmenu.simpleitems.SimpleDividerItem; +import net.osmand.plus.widgets.MultiStateToggleButton; +import net.osmand.view.ThreeStateCheckbox; + +import java.util.List; + +public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment { + + protected OsmandApplication app; + protected UiUtilities uiUtilities; + + protected TextView title; + protected TextView primaryDescription; + protected TextView secondaryDescription; + protected TextView selectedSize; + protected LinearLayout toggleContainer; + protected MultiStateToggleButton radioGroup; + protected View selectAllButton; + protected TextView checkBoxTitle; + protected ThreeStateCheckbox checkBox; + protected LinearLayout selectionListView; + protected TextView applyButtonTitle; + + protected int activeColorRes; + protected int secondaryColorRes; + + private OnUiInitializedListener uiInitializedListener; + private OnApplySelectionListener applySelectionListener; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { + View mainView = super.onCreateView(inflater, parent, savedInstanceState); + notifyUiInitialized(); + return mainView; + } + + @Override + public void createMenuItems(Bundle savedInstanceState) { + app = requiredMyApplication(); + uiUtilities = app.getUIUtilities(); + activeColorRes = nightMode ? R.color.icon_color_active_dark : R.color.icon_color_active_light; + secondaryColorRes = nightMode ? R.color.icon_color_secondary_dark : R.color.icon_color_secondary_light; + + items.add(createHeaderUi()); + items.add(new SimpleDividerItem(app)); + createSelectionUi(); + } + + @Override + public void onPause() { + super.onPause(); + if (requireActivity().isChangingConfigurations()) { + dismiss(); + } + } + + private BaseBottomSheetItem createHeaderUi() { + LayoutInflater themedInflater = UiUtilities.getInflater(requireContext(), nightMode); + View view = themedInflater.inflate(R.layout.settings_group_title, null); + + title = view.findViewById(R.id.title); + primaryDescription = view.findViewById(R.id.description); + secondaryDescription = view.findViewById(R.id.additional_description); + selectedSize = view.findViewById(R.id.selected_size); + toggleContainer = view.findViewById(R.id.custom_radio_buttons); + radioGroup = new MultiStateToggleButton(app, toggleContainer, nightMode); + selectAllButton = view.findViewById(R.id.select_all_button); + checkBoxTitle = view.findViewById(R.id.check_box_title); + checkBox = view.findViewById(R.id.check_box); + + initHeaderUi(); + return new SimpleBottomSheetItem.Builder().setCustomView(view).create(); + } + + protected abstract void createSelectionUi(); + + protected abstract void initHeaderUi(); + + @Override + protected void setupRightButton() { + super.setupRightButton(); + applyButtonTitle = rightButton.findViewById(R.id.button_text); + } + + public void setTitle(@NonNull String title) { + this.title.setText(title); + } + + public void setDescription(@NonNull String description) { + this.primaryDescription.setText(description); + } + + public void setSecondaryDescription(@NonNull String description) { + this.secondaryDescription.setText(description); + } + + public void setApplyButtonTitle(@NonNull String title) { + applyButtonTitle.setText(title); + } + + @Override + protected void onRightBottomButtonClick() { + if (applySelectionListener != null) { + applySelectionListener.onSelectionApplied(getSelection()); + } + dismiss(); + } + + @NonNull + public abstract List getSelection(); + + @Override + protected int getRightBottomButtonTextId() { + return R.string.shared_string_apply; + } + + public void setUiInitializedListener(OnUiInitializedListener uiInitializedListener) { + this.uiInitializedListener = uiInitializedListener; + } + + public void setOnApplySelectionListener(OnApplySelectionListener onApplySelectionListener) { + this.applySelectionListener = onApplySelectionListener; + } + + protected void notifyUiInitialized() { + if (uiInitializedListener != null) { + uiInitializedListener.onUiInitialized(); + } + } + + @Override + protected boolean useVerticalButtons() { + return true; + } + + public interface OnUiInitializedListener { + void onUiInitialized(); + } + + public interface OnApplySelectionListener { + void onSelectionApplied(List selectedItems); + } + + public static class SelectableItem { + private String title; + private String description; + private int iconId; + private Object object; + + public String getTitle() { + return title; + } + + public String getDescription() { + return description; + } + + public int getIconId() { + return iconId; + } + + public Object getObject() { + return object; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setDescription(String description) { + this.description = description; + } + + public void setIconId(int iconId) { + this.iconId = iconId; + } + + public void setObject(Object object) { + this.object = object; + } + } + +} diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java index 5c84250d07..715d2f02f5 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java @@ -29,7 +29,7 @@ import java.util.Map; import static net.osmand.IndexConstants.BINARY_MAP_INDEX_EXT; import static net.osmand.plus.activities.LocalIndexHelper.LocalIndexType.SRTM_DATA; -import static net.osmand.plus.download.MultipleIndexesUiHelper.getSRTMExt; +import static net.osmand.plus.download.SelectIndexesUiHelper.getSRTMExt; public class DownloadActivityType { private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd.MM.yyyy", Locale.US); diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java b/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java index a6a0a46686..caf47d71f5 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java @@ -12,7 +12,7 @@ import java.util.ArrayList; import java.util.List; import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; -import static net.osmand.plus.download.MultipleIndexesUiHelper.isBaseSRTMItem; +import static net.osmand.plus.download.SelectIndexesUiHelper.isBaseSRTMItem; public class MultipleIndexItem extends DownloadItem { diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java similarity index 52% rename from OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java rename to OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java index 509609bd94..ff0401215d 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleIndexesUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java @@ -1,6 +1,7 @@ package net.osmand.plus.download; import android.content.Context; +import android.view.View; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; @@ -11,22 +12,28 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.base.SelectMultipleItemsBottomSheet; -import net.osmand.plus.base.SelectMultipleItemsBottomSheet.OnApplySelectionListener; -import net.osmand.plus.base.SelectMultipleItemsBottomSheet.OnRadioButtonSelectListener; -import net.osmand.plus.base.SelectMultipleItemsBottomSheet.SelectableItem; import net.osmand.plus.base.SelectMultipleItemsBottomSheet.SelectionUpdateListener; +import net.osmand.plus.base.SelectModeBottomSheet; +import net.osmand.plus.base.SelectMultipleWithModeBottomSheet; +import net.osmand.plus.base.SelectionBottomSheet; +import net.osmand.plus.base.SelectionBottomSheet.OnApplySelectionListener; +import net.osmand.plus.base.SelectionBottomSheet.OnUiInitializedListener; +import net.osmand.plus.base.SelectionBottomSheet.SelectableItem; import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener; +import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; import net.osmand.util.Algorithms; import java.text.DateFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static net.osmand.IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; import static net.osmand.IndexConstants.BINARY_SRTM_MAP_INDEX_EXT_ZIP; import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; -public class MultipleIndexesUiHelper { +public class SelectIndexesUiHelper { public static void showDialog(@NonNull DownloadItem item, @NonNull AppCompatActivity activity, @@ -35,7 +42,11 @@ public class MultipleIndexesUiHelper { boolean showRemoteDate, @NonNull final SelectItemsToDownloadListener listener) { if (item.getType() == SRTM_COUNTRY_FILE) { - showSRTMDialog(item, activity, app, dateFormat, showRemoteDate, listener); + if (item instanceof MultipleIndexItem) { + showMultipleSrtmDialog(item, activity, app, dateFormat, showRemoteDate, listener); + } else { + showSingleSrtmDialog(item, activity, app, dateFormat, showRemoteDate, listener); + } } else if (item instanceof MultipleIndexItem) { showBaseDialog((MultipleIndexItem) item, activity, app, dateFormat, showRemoteDate, listener); } @@ -70,24 +81,116 @@ public class MultipleIndexesUiHelper { final SelectMultipleItemsBottomSheet dialog = SelectMultipleItemsBottomSheet.showInstance(activity, allItems, selectedItems, true); + dialog.setUiInitializedListener(new OnUiInitializedListener() { + @Override + public void onUiInitialized() { + dialog.setTitle(app.getString(R.string.welmode_download_maps)); + } + }); + dialog.setSelectionUpdateListener(new SelectionUpdateListener() { @Override public void onSelectionUpdate() { - updateSize(app, dialog); + updateSize(app, dialog, true); } }); dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); } - public static void showSRTMDialog(@NonNull final DownloadItem downloadItem, - @NonNull AppCompatActivity activity, - @NonNull final OsmandApplication app, - @NonNull final DateFormat dateFormat, - final boolean showRemoteDate, - @NonNull final SelectItemsToDownloadListener listener) { + public static void showSingleSrtmDialog(@NonNull final DownloadItem downloadItem, + @NonNull AppCompatActivity activity, + @NonNull final OsmandApplication app, + @NonNull final DateFormat dateFormat, + final boolean showRemoteDate, + @NonNull final SelectItemsToDownloadListener listener) { + List allIndexes = getIndexesToDownload(downloadItem); + boolean baseSRTM = isBaseSRTMMetricSystem(app); + + List radioItems = new ArrayList<>(); + final RadioItem meters = new RadioItem(Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_meters))); + RadioItem feet = new RadioItem(Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_feets))); + radioItems.add(meters); + radioItems.add(feet); + SelectableItem meterItem = null; + SelectableItem feetItem = null; + for (IndexItem indexItem : allIndexes) { + boolean baseItem = isBaseSRTMItem(indexItem); + SelectableItem selectableItem = new SelectableItem(); + selectableItem.setTitle(indexItem.getVisibleName(app, app.getRegions(), false)); + String size = indexItem.getSizeDescription(app); + size += " (" + getSRTMAbbrev(app, baseItem) + ")"; + String date = indexItem.getDate(dateFormat, showRemoteDate); + String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); + selectableItem.setDescription(description); + selectableItem.setIconId(indexItem.getType().getIconResource()); + selectableItem.setObject(indexItem); + if (baseItem) { + meterItem = selectableItem; + } else { + feetItem = selectableItem; + } + } + + final SelectableItem initMeter = meterItem; + final SelectableItem initFeet = feetItem; + + final SelectModeBottomSheet dialog = SelectModeBottomSheet.showInstance(activity, + baseSRTM ? meterItem : feetItem, radioItems, true); + + meters.setOnClickListener(new OnRadioItemClickListener() { + @Override + public boolean onRadioItemClick(RadioItem radioItem, View view) { + dialog.setPreviewItem(initMeter); + updateSize(app, dialog, false); + return true; + } + }); + + feet.setOnClickListener(new OnRadioItemClickListener() { + @Override + public boolean onRadioItemClick(RadioItem radioItem, View view) { + dialog.setPreviewItem(initFeet); + + double sizeToDownload = getDownloadSizeInMb(Collections.singletonList(initFeet)); + String size = DownloadItem.getFormattedMb(app, sizeToDownload); + String btnTitle = app.getString(R.string.shared_string_download); + if (sizeToDownload > 0) { + btnTitle = app.getString(R.string.ltr_or_rtl_combine_via_dash, btnTitle, size); + } + dialog.setApplyButtonTitle(btnTitle); + return true; + } + }); + + final RadioItem initRadio = baseSRTM ? meters : feet; + dialog.setUiInitializedListener(new OnUiInitializedListener() { + @Override + public void onUiInitialized() { + dialog.setTitle(app.getString(R.string.srtm_unit_format)); + dialog.setDescription(app.getString(R.string.srtm_download_single_help_message)); + double sizeToDownload = getDownloadSizeInMb(Collections.singletonList(initFeet)); + String size = DownloadItem.getFormattedMb(app, sizeToDownload); + String btnTitle = app.getString(R.string.shared_string_download); + if (sizeToDownload > 0) { + btnTitle = app.getString(R.string.ltr_or_rtl_combine_via_dash, btnTitle, size); + } + dialog.setApplyButtonTitle(btnTitle); + dialog.setSelectedMode(initRadio); + } + }); + + dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); + } + + public static void showMultipleSrtmDialog(@NonNull final DownloadItem downloadItem, + @NonNull AppCompatActivity activity, + @NonNull final OsmandApplication app, + @NonNull final DateFormat dateFormat, + final boolean showRemoteDate, + @NonNull final SelectItemsToDownloadListener listener) { List selectedItems = new ArrayList<>(); - final List leftItems = new ArrayList<>(); - final List rightItems = new ArrayList<>(); + final List meterItems = new ArrayList<>(); + final List feetItems = new ArrayList<>(); List indexesToDownload = getIndexesToDownload(downloadItem); boolean baseSRTM = isBaseSRTMMetricSystem(app); @@ -115,9 +218,9 @@ public class MultipleIndexesUiHelper { selectableItem.setObject(indexItem); if (baseItem) { - leftItems.add(selectableItem); + meterItems.add(selectableItem); } else { - rightItems.add(selectableItem); + feetItems.add(selectableItem); } if (indexesToDownload.contains(indexItem) @@ -126,45 +229,48 @@ public class MultipleIndexesUiHelper { } } - String addDescription = app.getString(isListDialog(app, leftItems, rightItems) - ? R.string.srtm_download_list_help_message : R.string.srtm_download_single_help_message); - final SelectMultipleItemsBottomSheet dialog = SelectMultipleItemsBottomSheet.showInstance( - activity, baseSRTM ? leftItems : rightItems, selectedItems, true, - addDescription, true, baseSRTM, - Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_meters)), - Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_feets))); + List radioItems = new ArrayList<>(); + RadioItem meters = new RadioItem(Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_meters))); + RadioItem feet = new RadioItem(Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_feets))); + final RadioItem selectedMode = isBaseSRTMItem(downloadItem) ? meters : feet; + radioItems.add(meters); + radioItems.add(feet); + + final SelectMultipleWithModeBottomSheet dialog = SelectMultipleWithModeBottomSheet.showInstance( + activity, baseSRTM ? meterItems : feetItems, selectedItems, radioItems, true); + + meters.setOnClickListener(new OnRadioItemClickListener() { + @Override + public boolean onRadioItemClick(RadioItem radioItem, View view) { +// dialog.recreateList(meterItems); + return true; + } + }); + + feet.setOnClickListener(new OnRadioItemClickListener() { + @Override + public boolean onRadioItemClick(RadioItem radioItem, View view) { +// dialog.recreateList(feetItems); + return true; + } + }); + + dialog.setUiInitializedListener(new OnUiInitializedListener() { + @Override + public void onUiInitialized() { + dialog.setTitle(app.getString(R.string.welmode_download_maps)); + dialog.setSelectedMode(selectedMode); + dialog.setSecondaryDescription(app.getString(R.string.srtm_download_list_help_message)); + } + }); dialog.setSelectionUpdateListener(new SelectionUpdateListener() { @Override public void onSelectionUpdate() { - updateSize(app, dialog); + updateSize(app, dialog, true); } }); dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); - dialog.setOnRadioButtonSelectListener(new OnRadioButtonSelectListener() { - @Override - public void onSelect(boolean leftButton) { - dialog.recreateList(leftButton ? leftItems : rightItems); - updateSize(app, dialog); - } - }); - dialog.setSelectedItemsListener(new SelectedItemsListener() { - @Override - public List createSelectedItems(List currentAllItems, boolean baseSRTM) { - List indexesToDownload = getIndexesToDownload(downloadItem); - List selectedItems = new ArrayList<>(); - - for (SelectableItem currentItem : currentAllItems) { - IndexItem indexItem = (IndexItem) currentItem.getObject(); - boolean baseItem = isBaseSRTMItem(indexItem); - if (indexesToDownload.contains(indexItem) - && (baseSRTM && baseItem || !baseSRTM && !baseItem)) { - selectedItems.add(currentItem); - } - } - return selectedItems; - } - }); } private static OnApplySelectionListener getOnApplySelectionListener(final SelectItemsToDownloadListener listener) { @@ -183,12 +289,12 @@ public class MultipleIndexesUiHelper { }; } - private static void updateSize(OsmandApplication app, SelectMultipleItemsBottomSheet dialog) { - boolean isListDialog = dialog.isMultipleItem(); - dialog.setTitle(app.getString(isListDialog ? R.string.welmode_download_maps : R.string.srtm_unit_format)); - double sizeToDownload = getDownloadSizeInMb(dialog.getSelectedItems()); + private static void updateSize(OsmandApplication app, + SelectionBottomSheet dialog, + boolean updateDescription) { + double sizeToDownload = getDownloadSizeInMb(dialog.getSelection()); String size = DownloadItem.getFormattedMb(app, sizeToDownload); - if (isListDialog) { + if (updateDescription) { String total = app.getString(R.string.shared_string_total); String description = app.getString(R.string.ltr_or_rtl_combine_via_colon, total, size); dialog.setDescription(description); @@ -197,12 +303,7 @@ public class MultipleIndexesUiHelper { if (sizeToDownload > 0) { btnTitle = app.getString(R.string.ltr_or_rtl_combine_via_dash, btnTitle, size); } - dialog.setConfirmButtonTitle(btnTitle); - } - - private static boolean isListDialog(OsmandApplication app, - List leftItems, List rightItems) { - return (isBaseSRTMMetricSystem(app) ? leftItems : rightItems).size() > 1; + dialog.setApplyButtonTitle(btnTitle); } public static String getSRTMAbbrev(Context context, boolean base) { @@ -266,7 +367,4 @@ public class MultipleIndexesUiHelper { void onItemsToDownloadSelected(List items); } - public interface SelectedItemsListener { - List createSelectedItems(List currentAllItems, boolean base); - } } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index 07aab7de88..498b122547 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -41,8 +41,8 @@ import net.osmand.plus.download.DownloadActivityType; import net.osmand.plus.download.DownloadResourceGroup; import net.osmand.plus.download.DownloadResources; import net.osmand.plus.download.IndexItem; -import net.osmand.plus.download.MultipleIndexesUiHelper; -import net.osmand.plus.download.MultipleIndexesUiHelper.SelectItemsToDownloadListener; +import net.osmand.plus.download.SelectIndexesUiHelper; +import net.osmand.plus.download.SelectIndexesUiHelper.SelectItemsToDownloadListener; import net.osmand.plus.download.MultipleIndexItem; import net.osmand.plus.download.ui.LocalIndexesFragment.LocalIndexOperationTask; import net.osmand.plus.helpers.FileNameTranslationHelper; @@ -56,9 +56,9 @@ import java.util.List; import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; import static net.osmand.plus.download.DownloadActivityType.isSRTMItem; -import static net.osmand.plus.download.MultipleIndexesUiHelper.getSRTMAbbrev; -import static net.osmand.plus.download.MultipleIndexesUiHelper.isBaseSRTMItem; -import static net.osmand.plus.download.MultipleIndexesUiHelper.isBaseSRTMMetricSystem; +import static net.osmand.plus.download.SelectIndexesUiHelper.getSRTMAbbrev; +import static net.osmand.plus.download.SelectIndexesUiHelper.isBaseSRTMItem; +import static net.osmand.plus.download.SelectIndexesUiHelper.isBaseSRTMMetricSystem; public class ItemViewHolder { @@ -523,7 +523,7 @@ public class ItemViewHolder { private void selectIndexesToDownload(DownloadItem item) { OsmandApplication app = context.getMyApplication(); - MultipleIndexesUiHelper.showDialog(item, context, app, dateFormat, showRemoteDate, + SelectIndexesUiHelper.showDialog(item, context, app, dateFormat, showRemoteDate, new SelectItemsToDownloadListener() { @Override public void onItemsToDownloadSelected(List indexes) { diff --git a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java index 91b1aabc76..d56420c29c 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java @@ -75,8 +75,8 @@ import java.util.Map; import java.util.Set; import static net.osmand.plus.download.DownloadActivityType.isSRTMItem; -import static net.osmand.plus.download.MultipleIndexesUiHelper.getSRTMAbbrev; -import static net.osmand.plus.download.MultipleIndexesUiHelper.isBaseSRTMItem; +import static net.osmand.plus.download.SelectIndexesUiHelper.getSRTMAbbrev; +import static net.osmand.plus.download.SelectIndexesUiHelper.isBaseSRTMItem; public class LocalIndexesFragment extends OsmandExpandableListFragment implements DownloadEvents, OnMapSourceUpdateListener, RenameCallback { From e3c257b255cc85a28cb77543e191849a94a7e570 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Mon, 12 Apr 2021 20:16:16 +0300 Subject: [PATCH 05/86] small fix --- .../net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java index 5aee912d0d..2c0680d690 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java @@ -14,6 +14,8 @@ import java.util.List; public class SelectMultipleWithModeBottomSheet extends SelectMultipleItemsBottomSheet { + public static final String TAG = SelectMultipleWithModeBottomSheet.class.getSimpleName(); + private List modes; @Override From 1ec3189a86ed1b5e0467b832558453d9c5b272c6 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Tue, 13 Apr 2021 00:42:16 +0300 Subject: [PATCH 06/86] refactoring p.2 --- .../plus/download/DownloadActivityType.java | 15 +- .../plus/download/DownloadResources.java | 41 +++- .../plus/download/MultipleIndexItem.java | 4 +- .../plus/download/SelectIndexesUiHelper.java | 213 ++++++++---------- .../plus/download/SrtmDownloadItem.java | 137 +++++++++++ .../plus/download/ui/ItemViewHolder.java | 35 +-- .../download/ui/LocalIndexesFragment.java | 8 +- .../plus/widgets/MultiStateToggleButton.java | 8 + 8 files changed, 300 insertions(+), 161 deletions(-) create mode 100644 OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java index 715d2f02f5..8024709969 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java @@ -29,7 +29,6 @@ import java.util.Map; import static net.osmand.IndexConstants.BINARY_MAP_INDEX_EXT; import static net.osmand.plus.activities.LocalIndexHelper.LocalIndexType.SRTM_DATA; -import static net.osmand.plus.download.SelectIndexesUiHelper.getSRTMExt; public class DownloadActivityType { private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("dd.MM.yyyy", Locale.US); @@ -231,7 +230,7 @@ public class DownloadActivityType { } else if (FONT_FILE == this) { return IndexConstants.FONT_INDEX_EXT; } else if (SRTM_COUNTRY_FILE == this) { - return getSRTMExt(indexItem); + return SrtmDownloadItem.getExtension(indexItem); } else if (WIKIPEDIA_FILE == this) { return IndexConstants.BINARY_WIKI_MAP_INDEX_EXT; } else if (WIKIVOYAGE_FILE == this) { @@ -427,7 +426,7 @@ public class DownloadActivityType { } String baseNameWithoutVersion = fileName.substring(0, l); if (this == SRTM_COUNTRY_FILE) { - return baseNameWithoutVersion + getSRTMExt(item); + return baseNameWithoutVersion + SrtmDownloadItem.getExtension(item); } if (this == WIKIPEDIA_FILE) { return baseNameWithoutVersion + IndexConstants.BINARY_WIKI_MAP_INDEX_EXT; @@ -505,14 +504,4 @@ public class DownloadActivityType { return fileName; } - public static boolean isSRTMItem(Object item) { - if (item instanceof IndexItem) { - return ((IndexItem) item).getType() == SRTM_COUNTRY_FILE; - } else if (item instanceof DownloadItem) { - return ((DownloadItem) item).getType() == SRTM_COUNTRY_FILE; - } else if (item instanceof LocalIndexInfo) { - return ((LocalIndexInfo) item).getType() == SRTM_DATA; - } - return false; - } } diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java index fc4ddc2a01..cc8647804b 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java @@ -471,14 +471,49 @@ public class DownloadResources extends DownloadResourceGroup { addGroup(otherGroup); createHillshadeSRTMGroups(); - collectMultipleIndexesItems(); + replaceIndividualSrtmWithGroups(region); + collectMultipleIndexesItems(region); trimEmptyGroups(); updateLoadedFiles(); return true; } - private void collectMultipleIndexesItems() { - collectMultipleIndexesItems(region); + private void replaceIndividualSrtmWithGroups(@NonNull WorldRegion region) { + DownloadResourceGroup group = getRegionMapsGroup(region); + if (group != null) { + boolean useMetersByDefault = SrtmDownloadItem.shouldUseMetersByDefault(app); + boolean listModified = false; + DownloadActivityType srtmType = DownloadActivityType.SRTM_COUNTRY_FILE; + List indexesList = group.getIndividualResources(); + List individualDownloadItems = group.getIndividualDownloadItems(); + if (doesListContainIndexWithType(indexesList, srtmType)) { + IndexItem meters = null; + IndexItem feet = null; + for (IndexItem item : indexesList) { + if (item.getType() == srtmType) { + if (SrtmDownloadItem.isMetersItem(item)) { + meters = item; + } else { + feet = item; + } + } + } + individualDownloadItems.remove(meters); + individualDownloadItems.remove(feet); + group.addItem(new SrtmDownloadItem(meters, feet, useMetersByDefault)); + listModified = true; + } + if (listModified) { + sortDownloadItems(individualDownloadItems); + } + } + + List subRegions = region.getSubregions(); + if (!Algorithms.isEmpty(subRegions)) { + for (WorldRegion subRegion : subRegions) { + replaceIndividualSrtmWithGroups(subRegion); + } + } } private void collectMultipleIndexesItems(@NonNull WorldRegion region) { diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java b/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java index caf47d71f5..c9530cb9e0 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java @@ -12,7 +12,6 @@ import java.util.ArrayList; import java.util.List; import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; -import static net.osmand.plus.download.SelectIndexesUiHelper.isBaseSRTMItem; public class MultipleIndexItem extends DownloadItem { @@ -121,7 +120,8 @@ public class MultipleIndexItem extends DownloadItem { if (this.type == SRTM_COUNTRY_FILE) { for (IndexItem item : items) { if (item.hasActualDataToDownload()) { - if (baseSRTM && isBaseSRTMItem(item) || !baseSRTM && !isBaseSRTMItem(item)) { + boolean isBase = SrtmDownloadItem.isMetersItem(item); + if (baseSRTM && isBase || !baseSRTM && !isBase) { totalSizeMb += item.getSizeToDownloadInMb(); } } diff --git a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java index ff0401215d..357eb4f377 100644 --- a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java @@ -6,11 +6,9 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; -import net.osmand.IndexConstants; import net.osmand.map.OsmandRegions; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; -import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.base.SelectMultipleItemsBottomSheet; import net.osmand.plus.base.SelectMultipleItemsBottomSheet.SelectionUpdateListener; import net.osmand.plus.base.SelectModeBottomSheet; @@ -19,7 +17,6 @@ import net.osmand.plus.base.SelectionBottomSheet; import net.osmand.plus.base.SelectionBottomSheet.OnApplySelectionListener; import net.osmand.plus.base.SelectionBottomSheet.OnUiInitializedListener; import net.osmand.plus.base.SelectionBottomSheet.SelectableItem; -import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener; import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; import net.osmand.util.Algorithms; @@ -29,35 +26,56 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import static net.osmand.IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; -import static net.osmand.IndexConstants.BINARY_SRTM_MAP_INDEX_EXT_ZIP; import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; public class SelectIndexesUiHelper { - public static void showDialog(@NonNull DownloadItem item, + private OsmandApplication app; + private AppCompatActivity activity; + + private DateFormat dateFormat; + private boolean showRemoteDate; + private DownloadItem downloadItem; + private SelectItemsToDownloadListener listener; + private SelectionBottomSheet dialog; + + private SelectIndexesUiHelper(@NonNull DownloadItem item, @NonNull AppCompatActivity activity, - @NonNull final OsmandApplication app, @NonNull DateFormat dateFormat, boolean showRemoteDate, - @NonNull final SelectItemsToDownloadListener listener) { - if (item.getType() == SRTM_COUNTRY_FILE) { - if (item instanceof MultipleIndexItem) { - showMultipleSrtmDialog(item, activity, app, dateFormat, showRemoteDate, listener); + @NonNull SelectItemsToDownloadListener listener) { + this.activity = activity; + this.app = (OsmandApplication) activity.getApplicationContext(); + this.downloadItem = item; + this.dateFormat = dateFormat; + this.showRemoteDate = showRemoteDate; + this.listener = listener; + } + + public static void showDialog(@NonNull DownloadItem item, + @NonNull AppCompatActivity activity, + @NonNull DateFormat dateFormat, + boolean showRemoteDate, + @NonNull SelectItemsToDownloadListener listener) { + SelectIndexesUiHelper helper = + new SelectIndexesUiHelper(item, activity, dateFormat, showRemoteDate, listener); + helper.showDialogInternal(); + } + + private void showDialogInternal() { + if (downloadItem.getType() == SRTM_COUNTRY_FILE) { + if (downloadItem instanceof MultipleIndexItem) { + showMultipleSrtmDialog(); } else { - showSingleSrtmDialog(item, activity, app, dateFormat, showRemoteDate, listener); + showSingleSrtmDialog(); } - } else if (item instanceof MultipleIndexItem) { - showBaseDialog((MultipleIndexItem) item, activity, app, dateFormat, showRemoteDate, listener); + } else if (downloadItem instanceof MultipleIndexItem) { + showBaseDialog(); } } - public static void showBaseDialog(@NonNull MultipleIndexItem multipleIndexItem, - @NonNull AppCompatActivity activity, - @NonNull final OsmandApplication app, - @NonNull DateFormat dateFormat, - boolean showRemoteDate, - @NonNull final SelectItemsToDownloadListener listener) { + private void showBaseDialog() { + MultipleIndexItem multipleIndexItem = (MultipleIndexItem) downloadItem; List indexesToDownload = getIndexesToDownload(multipleIndexItem); List allItems = new ArrayList<>(); List selectedItems = new ArrayList<>(); @@ -65,10 +83,12 @@ public class SelectIndexesUiHelper { for (IndexItem indexItem : multipleIndexItem.getAllIndexes()) { SelectableItem selectableItem = new SelectableItem(); selectableItem.setTitle(indexItem.getVisibleName(app, osmandRegions, false)); + String size = indexItem.getSizeDescription(app); String date = indexItem.getDate(dateFormat, showRemoteDate); String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); selectableItem.setDescription(description); + selectableItem.setIconId(indexItem.getType().getIconResource()); selectableItem.setObject(indexItem); allItems.add(selectableItem); @@ -91,90 +111,37 @@ public class SelectIndexesUiHelper { dialog.setSelectionUpdateListener(new SelectionUpdateListener() { @Override public void onSelectionUpdate() { - updateSize(app, dialog, true); + updateSize(dialog, true); } }); dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); } - public static void showSingleSrtmDialog(@NonNull final DownloadItem downloadItem, - @NonNull AppCompatActivity activity, - @NonNull final OsmandApplication app, - @NonNull final DateFormat dateFormat, - final boolean showRemoteDate, - @NonNull final SelectItemsToDownloadListener listener) { - List allIndexes = getIndexesToDownload(downloadItem); - boolean baseSRTM = isBaseSRTMMetricSystem(app); + private void showSingleSrtmDialog() { + boolean baseSRTM = SrtmDownloadItem.shouldUseMetersByDefault(app); + SrtmDownloadItem srtmItem = (SrtmDownloadItem) downloadItem; + + SelectableItem meterItem = createSrtmSelectableItem(srtmItem.getMeterItem()); + SelectableItem feetItem = createSrtmSelectableItem(srtmItem.getFeetItem()); List radioItems = new ArrayList<>(); - final RadioItem meters = new RadioItem(Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_meters))); - RadioItem feet = new RadioItem(Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_feets))); + RadioItem meters = createRadioItem(meterItem, R.string.shared_string_meters); + RadioItem feet = createRadioItem(feetItem, R.string.shared_string_feets); radioItems.add(meters); radioItems.add(feet); - SelectableItem meterItem = null; - SelectableItem feetItem = null; - for (IndexItem indexItem : allIndexes) { - boolean baseItem = isBaseSRTMItem(indexItem); - SelectableItem selectableItem = new SelectableItem(); - selectableItem.setTitle(indexItem.getVisibleName(app, app.getRegions(), false)); - String size = indexItem.getSizeDescription(app); - size += " (" + getSRTMAbbrev(app, baseItem) + ")"; - String date = indexItem.getDate(dateFormat, showRemoteDate); - String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); - selectableItem.setDescription(description); - selectableItem.setIconId(indexItem.getType().getIconResource()); - selectableItem.setObject(indexItem); - if (baseItem) { - meterItem = selectableItem; - } else { - feetItem = selectableItem; - } - } - final SelectableItem initMeter = meterItem; - final SelectableItem initFeet = feetItem; - - final SelectModeBottomSheet dialog = SelectModeBottomSheet.showInstance(activity, + dialog = SelectModeBottomSheet.showInstance(activity, baseSRTM ? meterItem : feetItem, radioItems, true); - meters.setOnClickListener(new OnRadioItemClickListener() { - @Override - public boolean onRadioItemClick(RadioItem radioItem, View view) { - dialog.setPreviewItem(initMeter); - updateSize(app, dialog, false); - return true; - } - }); - - feet.setOnClickListener(new OnRadioItemClickListener() { - @Override - public boolean onRadioItemClick(RadioItem radioItem, View view) { - dialog.setPreviewItem(initFeet); - - double sizeToDownload = getDownloadSizeInMb(Collections.singletonList(initFeet)); - String size = DownloadItem.getFormattedMb(app, sizeToDownload); - String btnTitle = app.getString(R.string.shared_string_download); - if (sizeToDownload > 0) { - btnTitle = app.getString(R.string.ltr_or_rtl_combine_via_dash, btnTitle, size); - } - dialog.setApplyButtonTitle(btnTitle); - return true; - } - }); - final RadioItem initRadio = baseSRTM ? meters : feet; + final SelectableItem initItem = baseSRTM ? meterItem : feetItem; dialog.setUiInitializedListener(new OnUiInitializedListener() { @Override public void onUiInitialized() { + SelectModeBottomSheet dialog = (SelectModeBottomSheet) SelectIndexesUiHelper.this.dialog; dialog.setTitle(app.getString(R.string.srtm_unit_format)); dialog.setDescription(app.getString(R.string.srtm_download_single_help_message)); - double sizeToDownload = getDownloadSizeInMb(Collections.singletonList(initFeet)); - String size = DownloadItem.getFormattedMb(app, sizeToDownload); - String btnTitle = app.getString(R.string.shared_string_download); - if (sizeToDownload > 0) { - btnTitle = app.getString(R.string.ltr_or_rtl_combine_via_dash, btnTitle, size); - } - dialog.setApplyButtonTitle(btnTitle); + updateSize(dialog, false); dialog.setSelectedMode(initRadio); } }); @@ -182,17 +149,40 @@ public class SelectIndexesUiHelper { dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); } - public static void showMultipleSrtmDialog(@NonNull final DownloadItem downloadItem, - @NonNull AppCompatActivity activity, - @NonNull final OsmandApplication app, - @NonNull final DateFormat dateFormat, - final boolean showRemoteDate, - @NonNull final SelectItemsToDownloadListener listener) { + private SelectableItem createSrtmSelectableItem(IndexItem indexItem) { + boolean baseItem = SrtmDownloadItem.isMetersItem(indexItem); + SelectableItem selectableItem = new SelectableItem(); + selectableItem.setTitle(indexItem.getVisibleName(app, app.getRegions(), false)); + String size = indexItem.getSizeDescription(app); + size += " (" + SrtmDownloadItem.getAbbreviation(app, baseItem) + ")"; + String date = indexItem.getDate(dateFormat, showRemoteDate); + String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); + selectableItem.setDescription(description); + selectableItem.setIconId(indexItem.getType().getIconResource()); + selectableItem.setObject(indexItem); + return selectableItem; + } + + private RadioItem createRadioItem(final SelectableItem selectableItem, int titleId) { + String title = Algorithms.capitalizeFirstLetter(app.getString(titleId)); + RadioItem radioItem = new RadioItem(title); + radioItem.setOnClickListener(new OnRadioItemClickListener() { + @Override + public boolean onRadioItemClick(RadioItem radioItem, View view) { + ((SelectModeBottomSheet)dialog).setPreviewItem(selectableItem); + updateSize(dialog, false); + return true; + } + }); + return radioItem; + } + + private void showMultipleSrtmDialog() { List selectedItems = new ArrayList<>(); final List meterItems = new ArrayList<>(); final List feetItems = new ArrayList<>(); List indexesToDownload = getIndexesToDownload(downloadItem); - boolean baseSRTM = isBaseSRTMMetricSystem(app); + boolean baseSRTM = SrtmDownloadItem.shouldUseMetersByDefault(app); List allIndexes = new ArrayList<>(); if (downloadItem instanceof MultipleIndexItem) { @@ -206,11 +196,11 @@ public class SelectIndexesUiHelper { } for (IndexItem indexItem : allIndexes) { - boolean baseItem = isBaseSRTMItem(indexItem); + boolean baseItem = SrtmDownloadItem.isMetersItem(indexItem); SelectableItem selectableItem = new SelectableItem(); selectableItem.setTitle(indexItem.getVisibleName(app, app.getRegions(), false)); String size = indexItem.getSizeDescription(app); - size += " (" + getSRTMAbbrev(app, baseItem) + ")"; + size += " (" + SrtmDownloadItem.getAbbreviation(app, baseItem) + ")"; String date = indexItem.getDate(dateFormat, showRemoteDate); String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); selectableItem.setDescription(description); @@ -232,7 +222,7 @@ public class SelectIndexesUiHelper { List radioItems = new ArrayList<>(); RadioItem meters = new RadioItem(Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_meters))); RadioItem feet = new RadioItem(Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_feets))); - final RadioItem selectedMode = isBaseSRTMItem(downloadItem) ? meters : feet; + final RadioItem selectedMode = SrtmDownloadItem.isMetersItem(downloadItem) ? meters : feet; radioItems.add(meters); radioItems.add(feet); @@ -267,13 +257,13 @@ public class SelectIndexesUiHelper { dialog.setSelectionUpdateListener(new SelectionUpdateListener() { @Override public void onSelectionUpdate() { - updateSize(app, dialog, true); + updateSize(dialog, true); } }); dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); } - private static OnApplySelectionListener getOnApplySelectionListener(final SelectItemsToDownloadListener listener) { + private OnApplySelectionListener getOnApplySelectionListener(final SelectItemsToDownloadListener listener) { return new OnApplySelectionListener() { @Override public void onSelectionApplied(List selectedItems) { @@ -289,9 +279,8 @@ public class SelectIndexesUiHelper { }; } - private static void updateSize(OsmandApplication app, - SelectionBottomSheet dialog, - boolean updateDescription) { + private void updateSize(SelectionBottomSheet dialog, + boolean updateDescription) { double sizeToDownload = getDownloadSizeInMb(dialog.getSelection()); String size = DownloadItem.getFormattedMb(app, sizeToDownload); if (updateDescription) { @@ -306,28 +295,6 @@ public class SelectIndexesUiHelper { dialog.setApplyButtonTitle(btnTitle); } - public static String getSRTMAbbrev(Context context, boolean base) { - return context.getString(base ? R.string.m : R.string.foot); - } - - public static String getSRTMExt(IndexItem indexItem) { - return isBaseSRTMItem(indexItem) - ? IndexConstants.BINARY_SRTM_MAP_INDEX_EXT : IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT; - } - - public static boolean isBaseSRTMItem(Object item) { - if (item instanceof IndexItem) { - return ((IndexItem) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT_ZIP); - } else if (item instanceof LocalIndexInfo) { - return ((LocalIndexInfo) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT); - } - return false; - } - - public static boolean isBaseSRTMMetricSystem(OsmandApplication app) { - return app.getSettings().METRIC_SYSTEM.get() != MetricsConstants.MILES_AND_FEET; - } - private static List getIndexesToDownload(DownloadItem downloadItem) { if (downloadItem instanceof MultipleIndexItem) { if (downloadItem.hasActualDataToDownload()) { diff --git a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java new file mode 100644 index 0000000000..2f528423d1 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java @@ -0,0 +1,137 @@ +package net.osmand.plus.download; + +import android.content.Context; + +import androidx.annotation.NonNull; + +import net.osmand.IndexConstants; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.activities.LocalIndexInfo; +import net.osmand.plus.helpers.enums.MetricsConstants; + +import java.io.File; +import java.text.DateFormat; +import java.util.List; + +import static net.osmand.IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; +import static net.osmand.IndexConstants.BINARY_SRTM_MAP_INDEX_EXT_ZIP; +import static net.osmand.plus.activities.LocalIndexHelper.LocalIndexType.SRTM_DATA; +import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; + +public class SrtmDownloadItem extends DownloadItem { + + private IndexItem meterItem; + private IndexItem feetItem; + + private boolean useMetersByDefault; + + public SrtmDownloadItem(IndexItem meterItem, + IndexItem feetItem, + boolean useMetersByDefault) { + super(SRTM_COUNTRY_FILE); + this.meterItem = meterItem; + this.feetItem = feetItem; + this.useMetersByDefault = useMetersByDefault; + } + + public boolean isUseMetersByDefault() { + return useMetersByDefault; + } + + public void setUseMetersByDefault(boolean useMetersByDefault) { + this.useMetersByDefault = useMetersByDefault; + } + + public IndexItem getIndexItem() { + return useMetersByDefault ? getMeterItem() : getFeetItem(); + } + + public IndexItem getMeterItem() { + return meterItem; + } + + public IndexItem getFeetItem() { + return feetItem; + } + + @Override + protected double getSizeToDownloadInMb() { + return getIndexItem().getSizeToDownloadInMb(); + } + + @Override + public double getArchiveSizeMB() { + return getIndexItem().getArchiveSizeMB(); + } + + @Override + public boolean isDownloaded() { + return getIndexItem().isDownloaded(); + } + + @Override + public boolean isOutdated() { + return getIndexItem().isOutdated(); + } + + @Override + public boolean hasActualDataToDownload() { + return getIndexItem().hasActualDataToDownload(); + } + + @Override + public boolean isDownloading(@NonNull DownloadIndexesThread thread) { + return getMeterItem().isDownloading(thread) || getFeetItem().isDownloading(thread); + } + + @Override + public String getFileName() { + return getIndexItem().getFileName(); + } + + @NonNull + @Override + public List getDownloadedFiles(@NonNull OsmandApplication app) { + return getIndexItem().getDownloadedFiles(app); + } + + public String getDate(@NonNull DateFormat dateFormat, boolean remote) { + return getIndexItem().getDate(dateFormat, remote); + } + + public static boolean shouldUseMetersByDefault(@NonNull OsmandApplication app) { + return app.getSettings().METRIC_SYSTEM.get() != MetricsConstants.MILES_AND_FEET; + } + + public static String getAbbreviation(Context context, boolean base) { + return context.getString(base ? R.string.m : R.string.foot); + } + + public static String getExtension(IndexItem indexItem) { + return isMetersItem(indexItem) ? + IndexConstants.BINARY_SRTM_MAP_INDEX_EXT : + IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT; + } + + public static boolean isMetersItem(Object item) { + if (item instanceof IndexItem) { + return ((IndexItem) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT_ZIP); + } else if (item instanceof LocalIndexInfo) { + return ((LocalIndexInfo) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT); + } + return false; + } + + public static boolean isSRTMItem(Object item) { + if (item instanceof IndexItem) { + return ((IndexItem) item).getType() == SRTM_COUNTRY_FILE; + } else if (item instanceof DownloadItem) { + return ((DownloadItem) item).getType() == SRTM_COUNTRY_FILE; + } else if (item instanceof LocalIndexInfo) { + return ((LocalIndexInfo) item).getType() == SRTM_DATA; + } + return false; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index 498b122547..872234795d 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -44,6 +44,7 @@ import net.osmand.plus.download.IndexItem; import net.osmand.plus.download.SelectIndexesUiHelper; import net.osmand.plus.download.SelectIndexesUiHelper.SelectItemsToDownloadListener; import net.osmand.plus.download.MultipleIndexItem; +import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.plus.download.ui.LocalIndexesFragment.LocalIndexOperationTask; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.plus.inapp.InAppPurchaseHelper; @@ -55,10 +56,6 @@ import java.util.ArrayList; import java.util.List; import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; -import static net.osmand.plus.download.DownloadActivityType.isSRTMItem; -import static net.osmand.plus.download.SelectIndexesUiHelper.getSRTMAbbrev; -import static net.osmand.plus.download.SelectIndexesUiHelper.isBaseSRTMItem; -import static net.osmand.plus.download.SelectIndexesUiHelper.isBaseSRTMMetricSystem; public class ItemViewHolder { @@ -196,8 +193,8 @@ public class ItemViewHolder { } descrTextView.setTextColor(textColorSecondary); if (!isDownloading) { - boolean srtmItem = isSRTMItem(downloadItem); - boolean baseMetricSystem = isBaseSRTMMetricSystem(context.getMyApplication()); + boolean srtmItem = SrtmDownloadItem.isSRTMItem(downloadItem); + boolean baseMetricSystem = SrtmDownloadItem.shouldUseMetersByDefault(context.getMyApplication()); progressBar.setVisibility(View.GONE); descrTextView.setVisibility(View.VISIBLE); if (downloadItem instanceof CustomIndexItem && (((CustomIndexItem) downloadItem).getSubName(context) != null)) { @@ -218,10 +215,10 @@ public class ItemViewHolder { String regionsHeader = context.getString(R.string.regions); String allRegionsCount; String leftToDownloadCount; - if (isSRTMItem(item)) { + if (SrtmDownloadItem.isSRTMItem(item)) { List items = new ArrayList<>(); for (IndexItem indexItem : item.getAllIndexes()) { - boolean baseItem = isBaseSRTMItem(indexItem); + boolean baseItem = SrtmDownloadItem.isMetersItem(indexItem); if (baseMetricSystem && baseItem || !baseMetricSystem && !baseItem) { items.add(indexItem); } @@ -229,7 +226,7 @@ public class ItemViewHolder { allRegionsCount = String.valueOf(items.size()); items.clear(); for (IndexItem indexItem : item.getIndexesToDownload()) { - boolean baseItem = isBaseSRTMItem(indexItem); + boolean baseItem = SrtmDownloadItem.isMetersItem(indexItem); if (!indexItem.isDownloaded() && (baseMetricSystem && baseItem || !baseMetricSystem && !baseItem)) { items.add(indexItem); @@ -259,7 +256,7 @@ public class ItemViewHolder { } String fullDescription = context.getString(R.string.ltr_or_rtl_combine_via_colon, header, count); if (srtmItem) { - fullDescription += " (" + getSRTMAbbrev(context, baseMetricSystem) + ")"; + fullDescription += " (" + SrtmDownloadItem.getAbbreviation(context, baseMetricSystem) + ")"; } if (item.hasActualDataToDownload()) { fullDescription = context.getString( @@ -267,14 +264,23 @@ public class ItemViewHolder { ? item.getSizeDescription(context, baseMetricSystem) : item.getSizeDescription(context)); } descrTextView.setText(fullDescription); + } else if (downloadItem instanceof SrtmDownloadItem) { + SrtmDownloadItem item = (SrtmDownloadItem) downloadItem; + String pattern = context.getString(R.string.ltr_or_rtl_combine_via_bold_point); + String type = item.getType().getString(context); + String size = item.getSizeDescription(context) + + " (" + SrtmDownloadItem.getAbbreviation(context, SrtmDownloadItem.isMetersItem(item)) + ")"; + String date = item.getDate(dateFormat, showRemoteDate); + String fullDescription = String.format(pattern, size, date); + if (showTypeInDesc) { + fullDescription = String.format(pattern, type, fullDescription); + } + descrTextView.setText(fullDescription); } else { IndexItem item = (IndexItem) downloadItem; String pattern = context.getString(R.string.ltr_or_rtl_combine_via_bold_point); String type = item.getType().getString(context); String size = item.getSizeDescription(context); - if (srtmItem) { - size += " (" + getSRTMAbbrev(context, isBaseSRTMItem(item)) + ")"; - } String date = item.getDate(dateFormat, showRemoteDate); String fullDescription = String.format(pattern, size, date); if (showTypeInDesc) { @@ -522,8 +528,7 @@ public class ItemViewHolder { } private void selectIndexesToDownload(DownloadItem item) { - OsmandApplication app = context.getMyApplication(); - SelectIndexesUiHelper.showDialog(item, context, app, dateFormat, showRemoteDate, + SelectIndexesUiHelper.showDialog(item, context, dateFormat, showRemoteDate, new SelectItemsToDownloadListener() { @Override public void onItemsToDownloadSelected(List indexes) { diff --git a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java index d56420c29c..2c695cd1e6 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java @@ -55,6 +55,7 @@ import net.osmand.plus.dialogs.DirectionsDialogs; import net.osmand.plus.download.DownloadActivity; import net.osmand.plus.download.DownloadIndexesThread.DownloadEvents; import net.osmand.plus.download.IndexItem; +import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.plus.inapp.InAppPurchaseHelper; import net.osmand.plus.mapsource.EditMapSourceDialogFragment.OnMapSourceUpdateListener; @@ -74,9 +75,6 @@ import java.util.List; import java.util.Map; import java.util.Set; -import static net.osmand.plus.download.DownloadActivityType.isSRTMItem; -import static net.osmand.plus.download.SelectIndexesUiHelper.getSRTMAbbrev; -import static net.osmand.plus.download.SelectIndexesUiHelper.isBaseSRTMItem; public class LocalIndexesFragment extends OsmandExpandableListFragment implements DownloadEvents, OnMapSourceUpdateListener, RenameCallback { @@ -1034,8 +1032,8 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement builder.append(AndroidUtils.formatSize(ctx, child.getSize() * 1024l)); } - if (isSRTMItem(child)) { - builder.append(" (").append(getSRTMAbbrev(ctx, isBaseSRTMItem(child))).append(")"); + if (SrtmDownloadItem.isSRTMItem(child)) { + builder.append(" (").append(SrtmDownloadItem.getAbbreviation(ctx, SrtmDownloadItem.isMetersItem(child))).append(")"); } if (!Algorithms.isEmpty(child.getDescription())) { diff --git a/OsmAnd/src/net/osmand/plus/widgets/MultiStateToggleButton.java b/OsmAnd/src/net/osmand/plus/widgets/MultiStateToggleButton.java index 605584f5b8..75ac8410d5 100644 --- a/OsmAnd/src/net/osmand/plus/widgets/MultiStateToggleButton.java +++ b/OsmAnd/src/net/osmand/plus/widgets/MultiStateToggleButton.java @@ -18,6 +18,7 @@ import net.osmand.plus.UiUtilities; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; public class MultiStateToggleButton { @@ -37,6 +38,13 @@ public class MultiStateToggleButton { this.nightMode = nightMode; } + public void setItems(Collection radioItems) { + if (radioItems == null || radioItems.size() < 2) return; + items.clear(); + items.addAll(radioItems); + initView(); + } + public void setItems(RadioItem firstBtn, RadioItem secondBtn, RadioItem... other) { items.clear(); items.add(firstBtn); From e6ad6f3c38b913094749d1352f49a366b5dd268a Mon Sep 17 00:00:00 2001 From: Skalii Date: Thu, 15 Apr 2021 05:30:30 +0300 Subject: [PATCH 07/86] fix export custom poi types with all categories selected --- .../search/QuickSearchCustomPoiFragment.java | 3 ++- .../search/QuickSearchSubCategoriesFragment.java | 2 +- .../backend/backup/PoiUiFiltersSettingsItem.java | 16 +++++++++++++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/search/QuickSearchCustomPoiFragment.java b/OsmAnd/src/net/osmand/plus/search/QuickSearchCustomPoiFragment.java index e211569f4c..2e2b43a943 100644 --- a/OsmAnd/src/net/osmand/plus/search/QuickSearchCustomPoiFragment.java +++ b/OsmAnd/src/net/osmand/plus/search/QuickSearchCustomPoiFragment.java @@ -542,7 +542,8 @@ public class QuickSearchCustomPoiFragment extends DialogFragment implements OnFi titleView.setText(textString); Set subtypes = filter.getAcceptedSubtypes(category); if (categorySelected) { - if (subtypes == null) { + LinkedHashSet poiTypes = filter.getAcceptedTypes().get(category); + if (subtypes == null || (poiTypes != null && category.getPoiTypes().size() == poiTypes.size())) { descView.setText(getString(R.string.shared_string_all)); } else { StringBuilder sb = new StringBuilder(); diff --git a/OsmAnd/src/net/osmand/plus/search/QuickSearchSubCategoriesFragment.java b/OsmAnd/src/net/osmand/plus/search/QuickSearchSubCategoriesFragment.java index 6b6f21aba1..a6dfbdbdec 100644 --- a/OsmAnd/src/net/osmand/plus/search/QuickSearchSubCategoriesFragment.java +++ b/OsmAnd/src/net/osmand/plus/search/QuickSearchSubCategoriesFragment.java @@ -101,7 +101,7 @@ public class QuickSearchSubCategoriesFragment extends BaseOsmAndDialogFragment { updateAddBtnVisibility(); } }); - if (selectAll || acceptedCategories == null) { + if (selectAll || acceptedCategories == null || poiCategory.getPoiTypes().size() == acceptedCategories.size()) { adapter.setSelectedItems(poiTypeList); selectAll = true; } else { diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java index 16be1f26bc..3d4d1ba2c8 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java @@ -10,6 +10,7 @@ import com.google.gson.reflect.TypeToken; import net.osmand.osm.MapPoiTypes; import net.osmand.osm.PoiCategory; +import net.osmand.osm.PoiType; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.poi.PoiUIFilter; @@ -148,7 +149,20 @@ public class PoiUiFiltersSettingsItem extends CollectionSettingsItem> acceptedTypes = filter.getAcceptedTypes(); + for (PoiCategory category : acceptedTypes.keySet()) { + LinkedHashSet poiTypes = acceptedTypes.get(category); + if (poiTypes == null) { + poiTypes = new LinkedHashSet<>(); + for (PoiType poiType : category.getPoiTypes()) { + poiTypes.add(poiType.getKeyName()); + } + acceptedTypes.put(category, poiTypes); + } + } + + jsonObject.put("acceptedTypes", gson.toJson(acceptedTypes, type)); jsonArray.put(jsonObject); } json.put("items", jsonArray); From 4a6ab0c6bbf0531fe6874b06bb6e17164a50f7db Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Thu, 15 Apr 2021 21:55:52 +0300 Subject: [PATCH 08/86] refactoring p.3 --- OsmAnd/res/layout/settings_group_title.xml | 22 ++++- .../plus/activities/LocalIndexHelper.java | 4 +- .../plus/base/SelectModeBottomSheet.java | 14 ++-- .../base/SelectMultipleItemsBottomSheet.java | 7 +- .../SelectMultipleWithModeBottomSheet.java | 2 +- .../plus/base/SelectionBottomSheet.java | 26 ++++-- .../plus/download/DownloadActivityType.java | 4 + .../plus/download/DownloadResources.java | 15 ++-- .../plus/download/SelectIndexesUiHelper.java | 20 +++-- .../plus/download/SrtmDownloadItem.java | 81 +++++++++++++------ .../ui/ActiveDownloadsDialogFragment.java | 2 +- .../ui/DownloadResourceGroupAdapter.java | 2 +- .../plus/download/ui/ItemViewHolder.java | 18 ++--- .../download/ui/LocalIndexesFragment.java | 5 +- .../download/ui/SearchDialogFragment.java | 6 +- .../download/ui/UpdatesIndexFragment.java | 2 +- .../resources/IncrementalChangesManager.java | 4 +- .../plus/resources/ResourceManager.java | 4 +- .../backend/backup/FileSettingsItem.java | 4 +- .../fragments/ExportItemsBottomSheet.java | 4 +- 20 files changed, 151 insertions(+), 95 deletions(-) diff --git a/OsmAnd/res/layout/settings_group_title.xml b/OsmAnd/res/layout/settings_group_title.xml index 4ac7fbfe05..6c2c220b4e 100644 --- a/OsmAnd/res/layout/settings_group_title.xml +++ b/OsmAnd/res/layout/settings_group_title.xml @@ -31,7 +31,7 @@ tools:text="Some title" /> + + getSelection() { return Collections.singletonList(previewItem); } + @Override + protected boolean shouldShowDivider() { + return false; + } + public static SelectModeBottomSheet showInstance(@NonNull AppCompatActivity activity, @NonNull SelectableItem previewItem, @NonNull List radioItems, diff --git a/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java index ab8ae1f3e9..d9a6204e2c 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java @@ -122,11 +122,8 @@ public class SelectMultipleItemsBottomSheet extends SelectionBottomSheet { } private void updateApplyButtonEnable() { - if (Algorithms.isEmpty(selectedItems)) { - rightButton.setEnabled(false); - } else { - rightButton.setEnabled(true); - } + boolean noEmptySelection = !Algorithms.isEmpty(selectedItems); + rightButton.setEnabled(noEmptySelection); } private void updateItems(boolean checked) { diff --git a/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java index 2c0680d690..4318d9c3c7 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java @@ -24,7 +24,7 @@ public class SelectMultipleWithModeBottomSheet extends SelectMultipleItemsBottom radioGroup.setItems(modes); AndroidUiHelper.setVisibility(View.VISIBLE, secondaryDescription, toggleContainer, - checkBox, checkBoxTitle, primaryDescription, selectedSize, selectAllButton); + checkBox, checkBoxTitle, titleDescription, selectedSize, selectAllButton); } private void setModes(@NonNull List modes) { diff --git a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java index 978f649083..6f788a9be2 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java @@ -27,6 +27,7 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment protected UiUtilities uiUtilities; protected TextView title; + protected TextView titleDescription; protected TextView primaryDescription; protected TextView secondaryDescription; protected TextView selectedSize; @@ -35,7 +36,7 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment protected View selectAllButton; protected TextView checkBoxTitle; protected ThreeStateCheckbox checkBox; - protected LinearLayout selectionListView; + protected LinearLayout listContainer; protected TextView applyButtonTitle; protected int activeColorRes; @@ -60,7 +61,9 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment secondaryColorRes = nightMode ? R.color.icon_color_secondary_dark : R.color.icon_color_secondary_light; items.add(createHeaderUi()); - items.add(new SimpleDividerItem(app)); + if (shouldShowDivider()) { + items.add(new SimpleDividerItem(app)); + } createSelectionUi(); } @@ -77,8 +80,9 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment View view = themedInflater.inflate(R.layout.settings_group_title, null); title = view.findViewById(R.id.title); - primaryDescription = view.findViewById(R.id.description); - secondaryDescription = view.findViewById(R.id.additional_description); + titleDescription = view.findViewById(R.id.title_description); + primaryDescription = view.findViewById(R.id.primary_description); + secondaryDescription = view.findViewById(R.id.secondary_description); selectedSize = view.findViewById(R.id.selected_size); toggleContainer = view.findViewById(R.id.custom_radio_buttons); radioGroup = new MultiStateToggleButton(app, toggleContainer, nightMode); @@ -94,6 +98,10 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment protected abstract void initHeaderUi(); + protected boolean shouldShowDivider() { + return true; + } + @Override protected void setupRightButton() { super.setupRightButton(); @@ -104,12 +112,16 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment this.title.setText(title); } - public void setDescription(@NonNull String description) { - this.primaryDescription.setText(description); + public void setTitleDescription(@NonNull String description) { + titleDescription.setText(description); + } + + public void setPrimaryDescription(@NonNull String description) { + primaryDescription.setText(description); } public void setSecondaryDescription(@NonNull String description) { - this.secondaryDescription.setText(description); + secondaryDescription.setText(description); } public void setApplyButtonTitle(@NonNull String title) { diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java index 8024709969..e00e59fc53 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java @@ -210,6 +210,10 @@ public class DownloadActivityType { return this == VOICE_FILE && indexItem.fileName.endsWith(IndexConstants.VOICE_INDEX_EXT_ZIP); } + public boolean mayProvideSeveralIndexes() { + return this == SRTM_COUNTRY_FILE; + } + public String getUnzipExtension(OsmandApplication ctx, IndexItem indexItem) { if (NORMAL_FILE == this) { if (indexItem.fileName.endsWith(IndexConstants.BINARY_MAP_INDEX_EXT_ZIP)) { diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java index cc8647804b..6b13be8d29 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java @@ -487,20 +487,14 @@ public class DownloadResources extends DownloadResourceGroup { List indexesList = group.getIndividualResources(); List individualDownloadItems = group.getIndividualDownloadItems(); if (doesListContainIndexWithType(indexesList, srtmType)) { - IndexItem meters = null; - IndexItem feet = null; + List srtmIndexes = new ArrayList<>(); for (IndexItem item : indexesList) { if (item.getType() == srtmType) { - if (SrtmDownloadItem.isMetersItem(item)) { - meters = item; - } else { - feet = item; - } + srtmIndexes.add(item); } } - individualDownloadItems.remove(meters); - individualDownloadItems.remove(feet); - group.addItem(new SrtmDownloadItem(meters, feet, useMetersByDefault)); + individualDownloadItems.removeAll(srtmIndexes); + group.addItem(new SrtmDownloadItem(srtmIndexes, useMetersByDefault)); listModified = true; } if (listModified) { @@ -563,6 +557,7 @@ public class DownloadResources extends DownloadResourceGroup { if (index.getType() == type) { found = true; collectedIndexes.add(index); + if (!type.mayProvideSeveralIndexes()) break; } } } diff --git a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java index 357eb4f377..23e1462576 100644 --- a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java @@ -1,6 +1,5 @@ package net.osmand.plus.download; -import android.content.Context; import android.view.View; import androidx.annotation.NonNull; @@ -23,7 +22,6 @@ import net.osmand.util.Algorithms; import java.text.DateFormat; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; @@ -36,14 +34,14 @@ public class SelectIndexesUiHelper { private DateFormat dateFormat; private boolean showRemoteDate; private DownloadItem downloadItem; - private SelectItemsToDownloadListener listener; + private ItemsToDownloadSelectedListener listener; private SelectionBottomSheet dialog; private SelectIndexesUiHelper(@NonNull DownloadItem item, @NonNull AppCompatActivity activity, @NonNull DateFormat dateFormat, boolean showRemoteDate, - @NonNull SelectItemsToDownloadListener listener) { + @NonNull ItemsToDownloadSelectedListener listener) { this.activity = activity; this.app = (OsmandApplication) activity.getApplicationContext(); this.downloadItem = item; @@ -56,7 +54,7 @@ public class SelectIndexesUiHelper { @NonNull AppCompatActivity activity, @NonNull DateFormat dateFormat, boolean showRemoteDate, - @NonNull SelectItemsToDownloadListener listener) { + @NonNull ItemsToDownloadSelectedListener listener) { SelectIndexesUiHelper helper = new SelectIndexesUiHelper(item, activity, dateFormat, showRemoteDate, listener); helper.showDialogInternal(); @@ -140,7 +138,7 @@ public class SelectIndexesUiHelper { public void onUiInitialized() { SelectModeBottomSheet dialog = (SelectModeBottomSheet) SelectIndexesUiHelper.this.dialog; dialog.setTitle(app.getString(R.string.srtm_unit_format)); - dialog.setDescription(app.getString(R.string.srtm_download_single_help_message)); + dialog.setPrimaryDescription(app.getString(R.string.srtm_download_single_help_message)); updateSize(dialog, false); dialog.setSelectedMode(initRadio); } @@ -154,7 +152,7 @@ public class SelectIndexesUiHelper { SelectableItem selectableItem = new SelectableItem(); selectableItem.setTitle(indexItem.getVisibleName(app, app.getRegions(), false)); String size = indexItem.getSizeDescription(app); - size += " (" + SrtmDownloadItem.getAbbreviation(app, baseItem) + ")"; + size += " " + SrtmDownloadItem.getAbbreviationInScopes(app, baseItem); String date = indexItem.getDate(dateFormat, showRemoteDate); String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); selectableItem.setDescription(description); @@ -200,7 +198,7 @@ public class SelectIndexesUiHelper { SelectableItem selectableItem = new SelectableItem(); selectableItem.setTitle(indexItem.getVisibleName(app, app.getRegions(), false)); String size = indexItem.getSizeDescription(app); - size += " (" + SrtmDownloadItem.getAbbreviation(app, baseItem) + ")"; + size += " " + SrtmDownloadItem.getAbbreviationInScopes(app, baseItem); String date = indexItem.getDate(dateFormat, showRemoteDate); String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); selectableItem.setDescription(description); @@ -263,7 +261,7 @@ public class SelectIndexesUiHelper { dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); } - private OnApplySelectionListener getOnApplySelectionListener(final SelectItemsToDownloadListener listener) { + private OnApplySelectionListener getOnApplySelectionListener(final ItemsToDownloadSelectedListener listener) { return new OnApplySelectionListener() { @Override public void onSelectionApplied(List selectedItems) { @@ -286,7 +284,7 @@ public class SelectIndexesUiHelper { if (updateDescription) { String total = app.getString(R.string.shared_string_total); String description = app.getString(R.string.ltr_or_rtl_combine_via_colon, total, size); - dialog.setDescription(description); + dialog.setTitleDescription(description); } String btnTitle = app.getString(R.string.shared_string_download); if (sizeToDownload > 0) { @@ -330,7 +328,7 @@ public class SelectIndexesUiHelper { return totalSizeMb; } - public interface SelectItemsToDownloadListener { + public interface ItemsToDownloadSelectedListener { void onItemsToDownloadSelected(List items); } diff --git a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java index 2f528423d1..d060a196a0 100644 --- a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java @@ -3,6 +3,7 @@ package net.osmand.plus.download; import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import net.osmand.IndexConstants; import net.osmand.plus.OsmandApplication; @@ -21,38 +22,53 @@ import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; public class SrtmDownloadItem extends DownloadItem { - private IndexItem meterItem; - private IndexItem feetItem; + private List indexes; + private IndexItem meter; + private IndexItem feet; - private boolean useMetersByDefault; + private boolean shouldUseMeters; - public SrtmDownloadItem(IndexItem meterItem, - IndexItem feetItem, - boolean useMetersByDefault) { + public SrtmDownloadItem(List indexes, + boolean shouldUseMeters) { super(SRTM_COUNTRY_FILE); - this.meterItem = meterItem; - this.feetItem = feetItem; - this.useMetersByDefault = useMetersByDefault; + this.indexes = indexes; + this.shouldUseMeters = shouldUseMeters; } - public boolean isUseMetersByDefault() { - return useMetersByDefault; + public boolean isShouldUseMeters() { + return shouldUseMeters; } - public void setUseMetersByDefault(boolean useMetersByDefault) { - this.useMetersByDefault = useMetersByDefault; + public void setShouldUseMeters(boolean shouldUseMeters) { + this.shouldUseMeters = shouldUseMeters; } public IndexItem getIndexItem() { - return useMetersByDefault ? getMeterItem() : getFeetItem(); + return shouldUseMeters ? getMeterItem() : getFeetItem(); } + @Nullable public IndexItem getMeterItem() { - return meterItem; + if (meter == null && indexes != null) { + for (IndexItem index : indexes) { + if (isMetersItem(index)) { + meter = index; + } + } + } + return meter; } + @Nullable public IndexItem getFeetItem() { - return feetItem; + if (feet == null && indexes != null) { + for (IndexItem index : indexes) { + if (!isMetersItem(index)) { + feet = index; + } + } + } + return feet; } @Override @@ -67,12 +83,12 @@ public class SrtmDownloadItem extends DownloadItem { @Override public boolean isDownloaded() { - return getIndexItem().isDownloaded(); + return meter.isDownloaded() || feet.isDownloaded(); } @Override public boolean isOutdated() { - return getIndexItem().isOutdated(); + return meter.isOutdated() || feet.isOutdated(); } @Override @@ -104,14 +120,14 @@ public class SrtmDownloadItem extends DownloadItem { return app.getSettings().METRIC_SYSTEM.get() != MetricsConstants.MILES_AND_FEET; } - public static String getAbbreviation(Context context, boolean base) { - return context.getString(base ? R.string.m : R.string.foot); + @NonNull + public static String getAbbreviationInScopes(Context ctx, boolean base) { + return "(" + getAbbreviation(ctx, base) + ")"; } - public static String getExtension(IndexItem indexItem) { - return isMetersItem(indexItem) ? - IndexConstants.BINARY_SRTM_MAP_INDEX_EXT : - IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT; + @NonNull + public static String getAbbreviation(Context context, boolean base) { + return context.getString(base ? R.string.m : R.string.foot); } public static boolean isMetersItem(Object item) { @@ -123,6 +139,23 @@ public class SrtmDownloadItem extends DownloadItem { return false; } + public static boolean containsSrtmExtension(@NonNull String fileName) { + return fileName.contains(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + || fileName.contains(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT); + } + + public static boolean isSrtmFile(@NonNull String fileName) { + return fileName.endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) + || fileName.endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT); + } + + @NonNull + public static String getExtension(IndexItem indexItem) { + return isMetersItem(indexItem) ? + IndexConstants.BINARY_SRTM_MAP_INDEX_EXT : + IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT; + } + public static boolean isSRTMItem(Object item) { if (item instanceof IndexItem) { return ((IndexItem) item).getType() == SRTM_COUNTRY_FILE; diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ActiveDownloadsDialogFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/ActiveDownloadsDialogFragment.java index 1b8667685c..31a0205f0e 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ActiveDownloadsDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ActiveDownloadsDialogFragment.java @@ -87,7 +87,7 @@ public class ActiveDownloadsDialogFragment extends DialogFragment implements Dow } ItemViewHolder viewHolder = (ItemViewHolder) convertView.getTag(); IndexItem item = getItem(position); - viewHolder.bindIndexItem(item); + viewHolder.bindDownloadItem(item); return convertView; } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupAdapter.java b/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupAdapter.java index d2e281bbfc..168888434d 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupAdapter.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/DownloadResourceGroupAdapter.java @@ -78,7 +78,7 @@ public class DownloadResourceGroupAdapter extends OsmandBaseExpandableListAdapte } else { viewHolder.setShowTypeInDesc(true); } - viewHolder.bindIndexItem(item); + viewHolder.bindDownloadItem(item); } else { DownloadResourceGroup group = (DownloadResourceGroup) child; DownloadGroupViewHolder viewHolder; diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index 872234795d..99d1dc1024 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -42,7 +42,7 @@ import net.osmand.plus.download.DownloadResourceGroup; import net.osmand.plus.download.DownloadResources; import net.osmand.plus.download.IndexItem; import net.osmand.plus.download.SelectIndexesUiHelper; -import net.osmand.plus.download.SelectIndexesUiHelper.SelectItemsToDownloadListener; +import net.osmand.plus.download.SelectIndexesUiHelper.ItemsToDownloadSelectedListener; import net.osmand.plus.download.MultipleIndexItem; import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.plus.download.ui.LocalIndexesFragment.LocalIndexOperationTask; @@ -150,11 +150,11 @@ public class ItemViewHolder { depthContoursPurchased = InAppPurchaseHelper.isDepthContoursPurchased(context.getMyApplication()); } - public void bindIndexItem(final DownloadItem downloadItem) { - bindIndexItem(downloadItem, null); + public void bindDownloadItem(final DownloadItem downloadItem) { + bindDownloadItem(downloadItem, null); } - public void bindIndexItem(final DownloadItem downloadItem, final String cityName) { + public void bindDownloadItem(final DownloadItem downloadItem, final String cityName) { initAppStatusVariables(); boolean isDownloading = downloadItem.isDownloading(context.getDownloadThread()); int progress = -1; @@ -256,7 +256,7 @@ public class ItemViewHolder { } String fullDescription = context.getString(R.string.ltr_or_rtl_combine_via_colon, header, count); if (srtmItem) { - fullDescription += " (" + SrtmDownloadItem.getAbbreviation(context, baseMetricSystem) + ")"; + fullDescription += " " + SrtmDownloadItem.getAbbreviationInScopes(context, baseMetricSystem); } if (item.hasActualDataToDownload()) { fullDescription = context.getString( @@ -269,7 +269,7 @@ public class ItemViewHolder { String pattern = context.getString(R.string.ltr_or_rtl_combine_via_bold_point); String type = item.getType().getString(context); String size = item.getSizeDescription(context) - + " (" + SrtmDownloadItem.getAbbreviation(context, SrtmDownloadItem.isMetersItem(item)) + ")"; + + " " + SrtmDownloadItem.getAbbreviationInScopes(context, SrtmDownloadItem.isMetersItem(item)); String date = item.getDate(dateFormat, showRemoteDate); String fullDescription = String.format(pattern, size, date); if (showTypeInDesc) { @@ -316,9 +316,9 @@ public class ItemViewHolder { } } - public void bindIndexItem(final CityItem cityItem) { + public void bindDownloadItem(final CityItem cityItem) { if (cityItem.getIndexItem() != null) { - bindIndexItem(cityItem.getIndexItem(), cityItem.getName()); + bindDownloadItem(cityItem.getIndexItem(), cityItem.getName()); } else { nameTextView.setText(cityItem.getName()); nameTextView.setTextColor(textColorPrimary); @@ -529,7 +529,7 @@ public class ItemViewHolder { private void selectIndexesToDownload(DownloadItem item) { SelectIndexesUiHelper.showDialog(item, context, dateFormat, showRemoteDate, - new SelectItemsToDownloadListener() { + new ItemsToDownloadSelectedListener() { @Override public void onItemsToDownloadSelected(List indexes) { IndexItem[] indexesArray = new IndexItem[indexes.size()]; diff --git a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java index 2c695cd1e6..b4e11d6bf7 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java @@ -965,8 +965,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement return ctx.getString(R.string.download_roads_only_item); } else if (child.isBackupedData() && child.getFileName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) { return ctx.getString(R.string.download_wikipedia_maps); - } else if (child.isBackupedData() && (child.getFileName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) - || child.getFileName().endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT))) { + } else if (child.isBackupedData() && (SrtmDownloadItem.isSrtmFile(child.getFileName()))) { return ctx.getString(R.string.download_srtm_maps); } return ""; @@ -1033,7 +1032,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement } if (SrtmDownloadItem.isSRTMItem(child)) { - builder.append(" (").append(SrtmDownloadItem.getAbbreviation(ctx, SrtmDownloadItem.isMetersItem(child))).append(")"); + builder.append(" ").append(SrtmDownloadItem.getAbbreviationInScopes(ctx, SrtmDownloadItem.isMetersItem(child))); } if (!Algorithms.isEmpty(child.getDescription())) { diff --git a/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java index ac9a87b32e..e6e3508ade 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/SearchDialogFragment.java @@ -382,10 +382,10 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven if (obj instanceof IndexItem) { IndexItem item = (IndexItem) obj; viewHolder.setShowTypeInDesc(true); - viewHolder.bindIndexItem(item); + viewHolder.bindDownloadItem(item); } else { CityItem item = (CityItem) obj; - viewHolder.bindIndexItem(item); + viewHolder.bindDownloadItem(item); if (item.getIndexItem() == null) { new IndexItemResolverTask(viewHolder, item).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } @@ -461,7 +461,7 @@ public class SearchDialogFragment extends DialogFragment implements DownloadEven if (viewHolder != null) { if (indexItem != null) { cityItem.setIndexItem(indexItem); - viewHolder.bindIndexItem(indexItem, cityItem.getName()); + viewHolder.bindDownloadItem(indexItem, cityItem.getName()); } } } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/UpdatesIndexFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/UpdatesIndexFragment.java index ae0ab9b560..2cf1555428 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/UpdatesIndexFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/UpdatesIndexFragment.java @@ -410,7 +410,7 @@ public class UpdatesIndexFragment extends OsmAndListFragment implements Download holder.setShowRemoteDate(true); holder.setShowTypeInDesc(true); holder.setShowParentRegionName(true); - holder.bindIndexItem(getItem(position)); + holder.bindDownloadItem(getItem(position)); } return view; } diff --git a/OsmAnd/src/net/osmand/plus/resources/IncrementalChangesManager.java b/OsmAnd/src/net/osmand/plus/resources/IncrementalChangesManager.java index efcb487fd4..565d300e47 100644 --- a/OsmAnd/src/net/osmand/plus/resources/IncrementalChangesManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/IncrementalChangesManager.java @@ -5,6 +5,7 @@ import net.osmand.PlatformUtil; import net.osmand.binary.BinaryMapIndexReader; import net.osmand.osm.io.NetworkUtils; import net.osmand.plus.R; +import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; @@ -45,8 +46,7 @@ public class IncrementalChangesManager { Set existingFiles = new HashSet(); for (File f : files) { if (!f.getName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT) && - !f.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) && - !f.getName().endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)) { + !SrtmDownloadItem.isSrtmFile(f.getName())) { existingFiles.add(Algorithms.getFileNameWithoutExtension(f)); } } diff --git a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java index 9271a3b1c2..7263eab1d8 100644 --- a/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java +++ b/OsmAnd/src/net/osmand/plus/resources/ResourceManager.java @@ -41,6 +41,7 @@ import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.download.DownloadOsmandIndexesHelper; import net.osmand.plus.download.DownloadOsmandIndexesHelper.AssetEntry; +import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.plus.inapp.InAppPurchaseHelper; import net.osmand.plus.render.MapRenderRepositories; import net.osmand.plus.render.NativeOsmandLibrary; @@ -701,8 +702,7 @@ public class ResourceManager { log.error(String.format("File %s could not be read", f.getName()), e); } boolean wikiMap = (f.getName().contains("_wiki") || f.getName().contains(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)); - boolean srtmMap = f.getName().contains(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) - || f.getName().contains(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT); + boolean srtmMap = SrtmDownloadItem.containsSrtmExtension(f.getName()); if (mapReader == null || (!Version.isPaidVersion(context) && wikiMap && !f.getName().equals(DEFAULT_WIKIVOYAGE_TRAVEL_OBF))) { warnings.add(MessageFormat.format(context.getString(R.string.version_index_is_not_supported), f.getName())); //$NON-NLS-1$ } else { diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java index e549c74406..53b6ec0500 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java @@ -7,6 +7,7 @@ import androidx.annotation.Nullable; import net.osmand.IndexConstants; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; +import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.util.Algorithms; import org.json.JSONException; @@ -92,8 +93,7 @@ public class FileSettingsItem extends StreamSettingsItem { case OTHER: break; case SRTM_MAP: - if (name.endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) - || name.endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)) { + if (SrtmDownloadItem.isSrtmFile(name)) { return subtype; } break; diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java index b78a4bd0aa..5e4b4f0449 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java @@ -36,6 +36,7 @@ import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton.Bu import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.SimpleDividerItem; +import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.plus.helpers.AvoidSpecificRoads.AvoidRoadInfo; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.plus.helpers.GpxUiHelper; @@ -530,8 +531,7 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { return getString(R.string.download_roads_only_item); } else if (file.getName().endsWith(IndexConstants.BINARY_WIKI_MAP_INDEX_EXT)) { return getString(R.string.download_wikipedia_maps); - } else if (file.getName().endsWith(IndexConstants.BINARY_SRTM_MAP_INDEX_EXT) - || file.getName().endsWith(IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT)) { + } else if (SrtmDownloadItem.isSrtmFile(file.getName())) { return getString(R.string.download_srtm_maps); } else if (file.getName().endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { return getString(R.string.download_regular_maps); From 45be6b1919d2b8894db70ab0673bc4f87a6e6c33 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Fri, 16 Apr 2021 12:04:19 +0300 Subject: [PATCH 09/86] refactoring p.4: save SrtmDownloadItem elements in MultipleDownloadItem --- .../main/java/net/osmand/map/WorldRegion.java | 21 +++ .../plus/download/DownloadActivityType.java | 4 - .../plus/download/DownloadIndexesThread.java | 6 +- .../plus/download/DownloadResources.java | 99 ++++++------ ...dexItem.java => MultipleDownloadItem.java} | 69 ++++----- .../plus/download/SelectIndexesUiHelper.java | 142 ++++++++---------- .../plus/download/SrtmDownloadItem.java | 109 ++++++++------ .../plus/download/ui/ItemViewHolder.java | 52 ++----- .../download/ui/LocalIndexesFragment.java | 2 +- 9 files changed, 237 insertions(+), 267 deletions(-) rename OsmAnd/src/net/osmand/plus/download/{MultipleIndexItem.java => MultipleDownloadItem.java} (67%) diff --git a/OsmAnd-java/src/main/java/net/osmand/map/WorldRegion.java b/OsmAnd-java/src/main/java/net/osmand/map/WorldRegion.java index e3b2aa2a69..d54e4eb92c 100644 --- a/OsmAnd-java/src/main/java/net/osmand/map/WorldRegion.java +++ b/OsmAnd-java/src/main/java/net/osmand/map/WorldRegion.java @@ -5,8 +5,11 @@ import net.osmand.data.QuadRect; import net.osmand.util.Algorithms; import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; public class WorldRegion implements Serializable { @@ -212,4 +215,22 @@ public class WorldRegion implements Serializable { } return false; } + + public static List removeDuplicates(List regions) { + List copy = new ArrayList<>(regions); + Set duplicates = new HashSet<>(); + for (int i = 0; i < copy.size() - 1; i++) { + WorldRegion r1 = copy.get(i); + for (int j = i + 1; j < copy.size(); j++) { + WorldRegion r2 = copy.get(j); + if (r1.containsRegion(r2)) { + duplicates.add(r2); + } else if (r2.containsRegion(r1)) { + duplicates.add(r1); + } + } + } + copy.removeAll(duplicates); + return copy; + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java index e00e59fc53..8024709969 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java @@ -210,10 +210,6 @@ public class DownloadActivityType { return this == VOICE_FILE && indexItem.fileName.endsWith(IndexConstants.VOICE_INDEX_EXT_ZIP); } - public boolean mayProvideSeveralIndexes() { - return this == SRTM_COUNTRY_FILE; - } - public String getUnzipExtension(OsmandApplication ctx, IndexItem indexItem) { if (NORMAL_FILE == this) { if (indexItem.fileName.endsWith(IndexConstants.BINARY_MAP_INDEX_EXT_ZIP)) { diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java b/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java index 8b16e48d84..ab45abe6d0 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java @@ -240,9 +240,9 @@ public class DownloadIndexesThread { } public void cancelDownload(DownloadItem item) { - if (item instanceof MultipleIndexItem) { - MultipleIndexItem multipleIndexItem = (MultipleIndexItem) item; - cancelDownload(multipleIndexItem.getAllIndexes()); + if (item instanceof MultipleDownloadItem) { + MultipleDownloadItem multipleDownloadItem = (MultipleDownloadItem) item; + cancelDownload(multipleDownloadItem.getAllIndexes()); } else if (item instanceof IndexItem) { IndexItem indexItem = (IndexItem) item; cancelDownload(indexItem); diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java index 6b13be8d29..e59e5f9de9 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java @@ -25,12 +25,10 @@ import java.io.InputStream; import java.text.DateFormat; import java.text.ParseException; import java.util.ArrayList; -import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; import static net.osmand.plus.download.DownloadResourceGroup.DownloadResourceGroupType.REGION_MAPS; @@ -117,6 +115,16 @@ public class DownloadResources extends DownloadResourceGroup { return res; } + @NonNull + public List getDownloadItems(WorldRegion region) { + DownloadResourceGroup group = getRegionMapsGroup(region); + if (group != null) { + return group.getIndividualDownloadItems(); + } + return new LinkedList<>(); + } + + @NonNull public List getIndexItems(WorldRegion region) { if (groupByRegion != null) { List res = groupByRegion.get(region); @@ -472,7 +480,7 @@ public class DownloadResources extends DownloadResourceGroup { createHillshadeSRTMGroups(); replaceIndividualSrtmWithGroups(region); - collectMultipleIndexesItems(region); + createMultipleDownloadItems(region); trimEmptyGroups(); updateLoadedFiles(); return true; @@ -484,21 +492,22 @@ public class DownloadResources extends DownloadResourceGroup { boolean useMetersByDefault = SrtmDownloadItem.shouldUseMetersByDefault(app); boolean listModified = false; DownloadActivityType srtmType = DownloadActivityType.SRTM_COUNTRY_FILE; - List indexesList = group.getIndividualResources(); - List individualDownloadItems = group.getIndividualDownloadItems(); - if (doesListContainIndexWithType(indexesList, srtmType)) { + List individualItems = group.getIndividualDownloadItems(); + if (isListContainsType(individualItems, srtmType)) { List srtmIndexes = new ArrayList<>(); - for (IndexItem item : indexesList) { - if (item.getType() == srtmType) { - srtmIndexes.add(item); + for (DownloadItem item : individualItems) { + if (item.getType() == srtmType && item instanceof IndexItem) { + srtmIndexes.add((IndexItem) item); } } - individualDownloadItems.removeAll(srtmIndexes); - group.addItem(new SrtmDownloadItem(srtmIndexes, useMetersByDefault)); + if (srtmIndexes.size() == 2) { + individualItems.removeAll(srtmIndexes); + group.addItem(new SrtmDownloadItem(srtmIndexes, useMetersByDefault)); + } listModified = true; } if (listModified) { - sortDownloadItems(individualDownloadItems); + sortDownloadItems(individualItems); } } @@ -510,20 +519,20 @@ public class DownloadResources extends DownloadResourceGroup { } } - private void collectMultipleIndexesItems(@NonNull WorldRegion region) { + private void createMultipleDownloadItems(@NonNull WorldRegion region) { List subRegions = region.getSubregions(); if (Algorithms.isEmpty(subRegions)) return; DownloadResourceGroup group = getRegionMapsGroup(region); if (group != null) { boolean listModified = false; - List indexesList = group.getIndividualResources(); - List regionsToCollect = removeDuplicateRegions(subRegions); + List downloadItems = group.getIndividualDownloadItems(); + List uniqueSubRegions = WorldRegion.removeDuplicates(subRegions); for (DownloadActivityType type : DownloadActivityType.values()) { - if (!doesListContainIndexWithType(indexesList, type)) { - List indexesFromSubRegions = collectIndexesOfType(regionsToCollect, type); - if (indexesFromSubRegions != null) { - group.addItem(new MultipleIndexItem(region, indexesFromSubRegions, type)); + if (!isListContainsType(downloadItems, type)) { + List itemsFromSubRegions = collectItemsOfType(uniqueSubRegions, type); + if (itemsFromSubRegions != null) { + group.addItem(new MultipleDownloadItem(region, itemsFromSubRegions, type)); listModified = true; } } @@ -533,7 +542,7 @@ public class DownloadResources extends DownloadResourceGroup { } } for (WorldRegion subRegion : subRegions) { - collectMultipleIndexesItems(subRegion); + createMultipleDownloadItems(subRegion); } } @@ -546,43 +555,21 @@ public class DownloadResources extends DownloadResourceGroup { } @Nullable - private List collectIndexesOfType(@NonNull List regions, - @NonNull DownloadActivityType type) { - List collectedIndexes = new ArrayList<>(); + private List collectItemsOfType(@NonNull List regions, + @NonNull DownloadActivityType type) { + List collectedItems = new ArrayList<>(); for (WorldRegion region : regions) { - List regionIndexes = getIndexItems(region); boolean found = false; - if (regionIndexes != null) { - for (IndexItem index : regionIndexes) { - if (index.getType() == type) { - found = true; - collectedIndexes.add(index); - if (!type.mayProvideSeveralIndexes()) break; - } + for (DownloadItem item : getDownloadItems(region)) { + if (item.getType() == type) { + found = true; + collectedItems.add(item); + break; } } if (!found) return null; } - return collectedIndexes; - } - - private List removeDuplicateRegions(List regions) { - Set duplicates = new HashSet<>(); - for (int i = 0; i < regions.size() - 1; i++) { - WorldRegion r1 = regions.get(i); - for (int j = i + 1; j < regions.size(); j++) { - WorldRegion r2 = regions.get(j); - if (r1.containsRegion(r2)) { - duplicates.add(r2); - } else if (r2.containsRegion(r1)) { - duplicates.add(r1); - } - } - } - for (WorldRegion region : duplicates) { - regions.remove(region); - } - return regions; + return collectedItems; } private void buildRegionsGroups(WorldRegion region, DownloadResourceGroup group) { @@ -709,11 +696,11 @@ public class DownloadResources extends DownloadResourceGroup { && isIndexItemDownloaded(downloadThread, type, downloadRegion.getSuperregion(), res); } - private boolean doesListContainIndexWithType(List indexItems, - DownloadActivityType type) { - if (indexItems != null) { - for (IndexItem indexItem : indexItems) { - if (indexItem.getType() == type) { + private boolean isListContainsType(List items, + DownloadActivityType type) { + if (items != null) { + for (DownloadItem item : items) { + if (item.getType() == type) { return true; } } diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java b/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java similarity index 67% rename from OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java rename to OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java index c9530cb9e0..394a116241 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleIndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java @@ -1,8 +1,7 @@ package net.osmand.plus.download; -import android.content.Context; - import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import net.osmand.map.WorldRegion; import net.osmand.plus.OsmandApplication; @@ -11,26 +10,35 @@ import java.io.File; import java.util.ArrayList; import java.util.List; -import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; +public class MultipleDownloadItem extends DownloadItem { -public class MultipleIndexItem extends DownloadItem { + private final List items; - private final List items; - - public MultipleIndexItem(@NonNull WorldRegion region, - @NonNull List items, - @NonNull DownloadActivityType type) { + public MultipleDownloadItem(@NonNull WorldRegion region, + @NonNull List items, + @NonNull DownloadActivityType type) { super(type); this.items = items; } public List getAllIndexes() { + List indexes = new ArrayList<>(); + for (DownloadItem item : items) { + IndexItem index = getIndexItem(item); + if (index != null) { + indexes.add(index); + } + } + return indexes; + } + + public List getItems() { return items; } @Override public boolean isOutdated() { - for (IndexItem item : items) { + for (DownloadItem item : items) { if (item.isOutdated()) { return true; } @@ -40,7 +48,7 @@ public class MultipleIndexItem extends DownloadItem { @Override public boolean isDownloaded() { - for (IndexItem item : items) { + for (DownloadItem item : items) { if (item.isDownloaded()) { return true; } @@ -50,8 +58,8 @@ public class MultipleIndexItem extends DownloadItem { @Override public boolean isDownloading(@NonNull DownloadIndexesThread thread) { - for (IndexItem item : items) { - if (thread.isDownloading(item)) { + for (DownloadItem item : items) { + if (item.isDownloading(thread)) { return true; } } @@ -82,7 +90,7 @@ public class MultipleIndexItem extends DownloadItem { @Override public List getDownloadedFiles(@NonNull OsmandApplication app) { List result = new ArrayList<>(); - for (IndexItem item : items) { + for (DownloadItem item : items) { result.addAll(item.getDownloadedFiles(app)); } return result; @@ -90,7 +98,7 @@ public class MultipleIndexItem extends DownloadItem { public List getIndexesToDownload() { List indexesToDownload = new ArrayList<>(); - for (IndexItem item : items) { + for (IndexItem item : getAllIndexes()) { if (item.hasActualDataToDownload()) { indexesToDownload.add(item); } @@ -106,7 +114,7 @@ public class MultipleIndexItem extends DownloadItem { @Override public double getSizeToDownloadInMb() { double totalSizeMb = 0.0d; - for (IndexItem item : items) { + for (DownloadItem item : items) { if (item.hasActualDataToDownload()) { totalSizeMb += item.getSizeToDownloadInMb(); } @@ -114,30 +122,23 @@ public class MultipleIndexItem extends DownloadItem { return totalSizeMb; } - @NonNull - public String getSizeDescription(Context ctx, boolean baseSRTM) { - double totalSizeMb = 0.0d; - if (this.type == SRTM_COUNTRY_FILE) { - for (IndexItem item : items) { - if (item.hasActualDataToDownload()) { - boolean isBase = SrtmDownloadItem.isMetersItem(item); - if (baseSRTM && isBase || !baseSRTM && !isBase) { - totalSizeMb += item.getSizeToDownloadInMb(); - } - } - } - return getFormattedMb(ctx, totalSizeMb); - } - return getFormattedMb(ctx, getSizeToDownloadInMb()); - } - @Override public double getArchiveSizeMB() { double result = 0.0d; - for (IndexItem item : items) { + for (DownloadItem item : items) { result += item.getArchiveSizeMB(); } return result; } + @Nullable + public static IndexItem getIndexItem(@NonNull DownloadItem obj) { + if (obj instanceof IndexItem) { + return (IndexItem) obj; + } else if (obj instanceof SrtmDownloadItem) { + return ((SrtmDownloadItem) obj).getIndexItem(); + } + return null; + } + } diff --git a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java index 23e1462576..7933b4eeff 100644 --- a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java @@ -25,60 +25,60 @@ import java.util.ArrayList; import java.util.List; import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; +import static net.osmand.plus.download.MultipleDownloadItem.getIndexItem; public class SelectIndexesUiHelper { - private OsmandApplication app; - private AppCompatActivity activity; + private final OsmandApplication app; + private final AppCompatActivity activity; + + private final ItemsToDownloadSelectedListener listener; + private final DateFormat dateFormat; + private final boolean showRemoteDate; + private final DownloadItem downloadItem; - private DateFormat dateFormat; - private boolean showRemoteDate; - private DownloadItem downloadItem; - private ItemsToDownloadSelectedListener listener; private SelectionBottomSheet dialog; - private SelectIndexesUiHelper(@NonNull DownloadItem item, + private SelectIndexesUiHelper(@NonNull DownloadItem downloadItem, @NonNull AppCompatActivity activity, @NonNull DateFormat dateFormat, boolean showRemoteDate, @NonNull ItemsToDownloadSelectedListener listener) { - this.activity = activity; this.app = (OsmandApplication) activity.getApplicationContext(); - this.downloadItem = item; + this.activity = activity; + this.downloadItem = downloadItem; this.dateFormat = dateFormat; this.showRemoteDate = showRemoteDate; this.listener = listener; } - public static void showDialog(@NonNull DownloadItem item, - @NonNull AppCompatActivity activity, - @NonNull DateFormat dateFormat, + public static void showDialog(@NonNull DownloadItem i, + @NonNull AppCompatActivity a, + @NonNull DateFormat df, boolean showRemoteDate, - @NonNull ItemsToDownloadSelectedListener listener) { - SelectIndexesUiHelper helper = - new SelectIndexesUiHelper(item, activity, dateFormat, showRemoteDate, listener); - helper.showDialogInternal(); + @NonNull ItemsToDownloadSelectedListener l) { + new SelectIndexesUiHelper(i, a, df, showRemoteDate, l).showDialogInternal(); } private void showDialogInternal() { if (downloadItem.getType() == SRTM_COUNTRY_FILE) { - if (downloadItem instanceof MultipleIndexItem) { + if (downloadItem instanceof MultipleDownloadItem) { showMultipleSrtmDialog(); } else { showSingleSrtmDialog(); } - } else if (downloadItem instanceof MultipleIndexItem) { + } else if (downloadItem instanceof MultipleDownloadItem) { showBaseDialog(); } } private void showBaseDialog() { - MultipleIndexItem multipleIndexItem = (MultipleIndexItem) downloadItem; - List indexesToDownload = getIndexesToDownload(multipleIndexItem); + MultipleDownloadItem multipleDownloadItem = (MultipleDownloadItem) downloadItem; + List indexesToDownload = getIndexesToDownload(multipleDownloadItem); List allItems = new ArrayList<>(); List selectedItems = new ArrayList<>(); OsmandRegions osmandRegions = app.getRegions(); - for (IndexItem indexItem : multipleIndexItem.getAllIndexes()) { + for (IndexItem indexItem : multipleDownloadItem.getAllIndexes()) { SelectableItem selectableItem = new SelectableItem(); selectableItem.setTitle(indexItem.getVisibleName(app, osmandRegions, false)); @@ -119,8 +119,11 @@ public class SelectIndexesUiHelper { boolean baseSRTM = SrtmDownloadItem.shouldUseMetersByDefault(app); SrtmDownloadItem srtmItem = (SrtmDownloadItem) downloadItem; - SelectableItem meterItem = createSrtmSelectableItem(srtmItem.getMeterItem()); - SelectableItem feetItem = createSrtmSelectableItem(srtmItem.getFeetItem()); + srtmItem.setUseMeters(true); + SelectableItem meterItem = createSrtmSelectableItem(srtmItem.getIndexItem()); + srtmItem.setUseMeters(false); + SelectableItem feetItem = createSrtmSelectableItem(srtmItem.getIndexItem()); + srtmItem.setUseMeters(baseSRTM); List radioItems = new ArrayList<>(); RadioItem meters = createRadioItem(meterItem, R.string.shared_string_meters); @@ -148,11 +151,10 @@ public class SelectIndexesUiHelper { } private SelectableItem createSrtmSelectableItem(IndexItem indexItem) { - boolean baseItem = SrtmDownloadItem.isMetersItem(indexItem); SelectableItem selectableItem = new SelectableItem(); selectableItem.setTitle(indexItem.getVisibleName(app, app.getRegions(), false)); String size = indexItem.getSizeDescription(app); - size += " " + SrtmDownloadItem.getAbbreviationInScopes(app, baseItem); + size += " " + SrtmDownloadItem.getAbbreviationInScopes(app, indexItem); String date = indexItem.getDate(dateFormat, showRemoteDate); String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); selectableItem.setDescription(description); @@ -177,42 +179,26 @@ public class SelectIndexesUiHelper { private void showMultipleSrtmDialog() { List selectedItems = new ArrayList<>(); - final List meterItems = new ArrayList<>(); - final List feetItems = new ArrayList<>(); - List indexesToDownload = getIndexesToDownload(downloadItem); - boolean baseSRTM = SrtmDownloadItem.shouldUseMetersByDefault(app); + List indexesToDownload = getIndexesToDownload((MultipleDownloadItem) downloadItem); - List allIndexes = new ArrayList<>(); - if (downloadItem instanceof MultipleIndexItem) { - allIndexes.addAll(((MultipleIndexItem) downloadItem).getAllIndexes()); - } else { - for (IndexItem indexItem : downloadItem.getRelatedGroup().getIndividualResources()) { - if (indexItem.getType() == SRTM_COUNTRY_FILE) { - allIndexes.add(indexItem); - } - } - } + List allItems = new ArrayList<>(((MultipleDownloadItem) downloadItem).getItems()); + List itemsList = new ArrayList<>(); - for (IndexItem indexItem : allIndexes) { - boolean baseItem = SrtmDownloadItem.isMetersItem(indexItem); + for (DownloadItem downloadItem : allItems) { + SrtmDownloadItem srtmItem = (SrtmDownloadItem) downloadItem; SelectableItem selectableItem = new SelectableItem(); - selectableItem.setTitle(indexItem.getVisibleName(app, app.getRegions(), false)); - String size = indexItem.getSizeDescription(app); - size += " " + SrtmDownloadItem.getAbbreviationInScopes(app, baseItem); - String date = indexItem.getDate(dateFormat, showRemoteDate); + selectableItem.setTitle(downloadItem.getVisibleName(app, app.getRegions(), false)); + String size = downloadItem.getSizeDescription(app); + size += " " + SrtmDownloadItem.getAbbreviationInScopes(app, srtmItem); + String date = srtmItem.getDate(dateFormat, showRemoteDate); String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); selectableItem.setDescription(description); - selectableItem.setIconId(indexItem.getType().getIconResource()); - selectableItem.setObject(indexItem); + selectableItem.setIconId(downloadItem.getType().getIconResource()); + selectableItem.setObject(downloadItem); - if (baseItem) { - meterItems.add(selectableItem); - } else { - feetItems.add(selectableItem); - } + itemsList.add(selectableItem); - if (indexesToDownload.contains(indexItem) - && (baseSRTM && baseItem || !baseSRTM && !baseItem)) { + if (indexesToDownload.contains(downloadItem)) { selectedItems.add(selectableItem); } } @@ -225,7 +211,7 @@ public class SelectIndexesUiHelper { radioItems.add(feet); final SelectMultipleWithModeBottomSheet dialog = SelectMultipleWithModeBottomSheet.showInstance( - activity, baseSRTM ? meterItems : feetItems, selectedItems, radioItems, true); + activity, itemsList, selectedItems, radioItems, true); meters.setOnClickListener(new OnRadioItemClickListener() { @Override @@ -267,9 +253,9 @@ public class SelectIndexesUiHelper { public void onSelectionApplied(List selectedItems) { List indexItems = new ArrayList<>(); for (SelectableItem item : selectedItems) { - Object obj = item.getObject(); - if (obj instanceof IndexItem) { - indexItems.add((IndexItem) obj); + IndexItem index = getIndexItem((DownloadItem) item.getObject()); + if (index != null) { + indexItems.add(index); } } listener.onItemsToDownloadSelected(indexItems); @@ -293,41 +279,31 @@ public class SelectIndexesUiHelper { dialog.setApplyButtonTitle(btnTitle); } - private static List getIndexesToDownload(DownloadItem downloadItem) { - if (downloadItem instanceof MultipleIndexItem) { - if (downloadItem.hasActualDataToDownload()) { - // download left regions - return ((MultipleIndexItem) downloadItem).getIndexesToDownload(); - } else { - // download all regions again - return ((MultipleIndexItem) downloadItem).getAllIndexes(); - } - } else { - List indexesToDownload = new ArrayList<>(); - for (IndexItem indexItem : downloadItem.getRelatedGroup().getIndividualResources()) { - if (indexItem.getType() == SRTM_COUNTRY_FILE && indexItem.hasActualDataToDownload()) { - indexesToDownload.add(indexItem); - } - } - return indexesToDownload; - } - } - - private static double getDownloadSizeInMb(@NonNull List selectableItems) { - List indexItems = new ArrayList<>(); + private double getDownloadSizeInMb(@NonNull List selectableItems) { + List downloadItems = new ArrayList<>(); for (SelectableItem i : selectableItems) { Object obj = i.getObject(); - if (obj instanceof IndexItem) { - indexItems.add((IndexItem) obj); + if (obj instanceof DownloadItem) { + downloadItems.add((DownloadItem) obj); } } double totalSizeMb = 0.0d; - for (IndexItem item : indexItems) { + for (DownloadItem item : downloadItems) { totalSizeMb += item.getSizeToDownloadInMb(); } return totalSizeMb; } + private static List getIndexesToDownload(MultipleDownloadItem multipleDownloadItem) { + if (multipleDownloadItem.hasActualDataToDownload()) { + // download left regions + return multipleDownloadItem.getIndexesToDownload(); + } else { + // download all regions again + return multipleDownloadItem.getAllIndexes(); + } + } + public interface ItemsToDownloadSelectedListener { void onItemsToDownloadSelected(List items); } diff --git a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java index d060a196a0..c89194910e 100644 --- a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java @@ -10,9 +10,11 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.util.Algorithms; import java.io.File; import java.text.DateFormat; +import java.util.Collections; import java.util.List; import static net.osmand.IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; @@ -22,53 +24,28 @@ import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; public class SrtmDownloadItem extends DownloadItem { - private List indexes; - private IndexItem meter; - private IndexItem feet; - - private boolean shouldUseMeters; + private final List indexes; + private boolean useMeters; public SrtmDownloadItem(List indexes, - boolean shouldUseMeters) { + boolean useMeters) { super(SRTM_COUNTRY_FILE); this.indexes = indexes; - this.shouldUseMeters = shouldUseMeters; + this.useMeters = useMeters; } - public boolean isShouldUseMeters() { - return shouldUseMeters; - } - - public void setShouldUseMeters(boolean shouldUseMeters) { - this.shouldUseMeters = shouldUseMeters; + public void setUseMeters(boolean useMeters) { + this.useMeters = useMeters; } + @Nullable public IndexItem getIndexItem() { - return shouldUseMeters ? getMeterItem() : getFeetItem(); - } - - @Nullable - public IndexItem getMeterItem() { - if (meter == null && indexes != null) { - for (IndexItem index : indexes) { - if (isMetersItem(index)) { - meter = index; - } + for (IndexItem index : indexes) { + if (useMeters && isMetersItem(index) || !useMeters && !isMetersItem(index)) { + return index; } } - return meter; - } - - @Nullable - public IndexItem getFeetItem() { - if (feet == null && indexes != null) { - for (IndexItem index : indexes) { - if (!isMetersItem(index)) { - feet = index; - } - } - } - return feet; + return null; } @Override @@ -82,52 +59,84 @@ public class SrtmDownloadItem extends DownloadItem { } @Override - public boolean isDownloaded() { - return meter.isDownloaded() || feet.isDownloaded(); + public boolean isOutdated() { + for (DownloadItem item : indexes) { + if (item.isOutdated()) { + return true; + } + } + return false; } @Override - public boolean isOutdated() { - return meter.isOutdated() || feet.isOutdated(); + public boolean isDownloaded() { + for (DownloadItem item : indexes) { + if (item.isDownloaded()) { + return true; + } + } + return false; } @Override public boolean hasActualDataToDownload() { - return getIndexItem().hasActualDataToDownload(); + // may be check only downloaded items if any downloaded + for (IndexItem item : indexes) { + if (item.hasActualDataToDownload()) { + return true; + } + } + return false; } @Override public boolean isDownloading(@NonNull DownloadIndexesThread thread) { - return getMeterItem().isDownloading(thread) || getFeetItem().isDownloading(thread); + for (IndexItem item : indexes) { + if (thread.isDownloading(item)) { + return true; + } + } + return false; } @Override public String getFileName() { + // may be check only downloaded items if any downloaded return getIndexItem().getFileName(); } @NonNull @Override public List getDownloadedFiles(@NonNull OsmandApplication app) { - return getIndexItem().getDownloadedFiles(app); + // may be check both indexes files + List result; + for (IndexItem index : indexes) { + result = index.getDownloadedFiles(app); + if (!Algorithms.isEmpty(result)) { + return result; + } + } + return Collections.emptyList(); } public String getDate(@NonNull DateFormat dateFormat, boolean remote) { + // may be check only downloaded items if any downloaded return getIndexItem().getDate(dateFormat, remote); } public static boolean shouldUseMetersByDefault(@NonNull OsmandApplication app) { - return app.getSettings().METRIC_SYSTEM.get() != MetricsConstants.MILES_AND_FEET; + MetricsConstants metricSystem = app.getSettings().METRIC_SYSTEM.get(); + return metricSystem != MetricsConstants.MILES_AND_FEET; } @NonNull - public static String getAbbreviationInScopes(Context ctx, boolean base) { - return "(" + getAbbreviation(ctx, base) + ")"; + public static String getAbbreviationInScopes(Context ctx, Object obj) { + return "(" + getAbbreviation(ctx, obj) + ")"; } @NonNull - public static String getAbbreviation(Context context, boolean base) { - return context.getString(base ? R.string.m : R.string.foot); + public static String getAbbreviation(Context context, Object obj) { + return context.getString(isMetersItem(obj) ? R.string.m : R.string.foot); } public static boolean isMetersItem(Object item) { @@ -135,6 +144,10 @@ public class SrtmDownloadItem extends DownloadItem { return ((IndexItem) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT_ZIP); } else if (item instanceof LocalIndexInfo) { return ((LocalIndexInfo) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT); + } else if (item instanceof SrtmDownloadItem) { + return ((SrtmDownloadItem) item).useMeters; + } else if (item instanceof MultipleDownloadItem) { + return isMetersItem(((MultipleDownloadItem) item).getItems().get(0)); } return false; } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index 99d1dc1024..31d228f50e 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -43,7 +43,7 @@ import net.osmand.plus.download.DownloadResources; import net.osmand.plus.download.IndexItem; import net.osmand.plus.download.SelectIndexesUiHelper; import net.osmand.plus.download.SelectIndexesUiHelper.ItemsToDownloadSelectedListener; -import net.osmand.plus.download.MultipleIndexItem; +import net.osmand.plus.download.MultipleDownloadItem; import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.plus.download.ui.LocalIndexesFragment.LocalIndexOperationTask; import net.osmand.plus.helpers.FileNameTranslationHelper; @@ -193,8 +193,6 @@ public class ItemViewHolder { } descrTextView.setTextColor(textColorSecondary); if (!isDownloading) { - boolean srtmItem = SrtmDownloadItem.isSRTMItem(downloadItem); - boolean baseMetricSystem = SrtmDownloadItem.shouldUseMetersByDefault(context.getMyApplication()); progressBar.setVisibility(View.GONE); descrTextView.setVisibility(View.VISIBLE); if (downloadItem instanceof CustomIndexItem && (((CustomIndexItem) downloadItem).getSubName(context) != null)) { @@ -209,34 +207,12 @@ public class ItemViewHolder { } else { descrTextView.setText(downloadItem.getType().getString(context)); } - } else if (downloadItem instanceof MultipleIndexItem) { - MultipleIndexItem item = (MultipleIndexItem) downloadItem; + } else if (downloadItem instanceof MultipleDownloadItem) { + MultipleDownloadItem item = (MultipleDownloadItem) downloadItem; String allRegionsHeader = context.getString(R.string.shared_strings_all_regions); String regionsHeader = context.getString(R.string.regions); - String allRegionsCount; - String leftToDownloadCount; - if (SrtmDownloadItem.isSRTMItem(item)) { - List items = new ArrayList<>(); - for (IndexItem indexItem : item.getAllIndexes()) { - boolean baseItem = SrtmDownloadItem.isMetersItem(indexItem); - if (baseMetricSystem && baseItem || !baseMetricSystem && !baseItem) { - items.add(indexItem); - } - } - allRegionsCount = String.valueOf(items.size()); - items.clear(); - for (IndexItem indexItem : item.getIndexesToDownload()) { - boolean baseItem = SrtmDownloadItem.isMetersItem(indexItem); - if (!indexItem.isDownloaded() - && (baseMetricSystem && baseItem || !baseMetricSystem && !baseItem)) { - items.add(indexItem); - } - } - leftToDownloadCount = String.valueOf(items.size()); - } else { - allRegionsCount = String.valueOf(item.getAllIndexes().size()); - leftToDownloadCount = String.valueOf(item.getIndexesToDownload().size()); - } + String allRegionsCount = String.valueOf(item.getItems().size()); + String leftToDownloadCount = String.valueOf(item.getIndexesToDownload().size()); String header; String count; if (item.hasActualDataToDownload()) { @@ -255,13 +231,13 @@ public class ItemViewHolder { count = allRegionsCount; } String fullDescription = context.getString(R.string.ltr_or_rtl_combine_via_colon, header, count); - if (srtmItem) { - fullDescription += " " + SrtmDownloadItem.getAbbreviationInScopes(context, baseMetricSystem); + if (SrtmDownloadItem.isSRTMItem(downloadItem)) { + fullDescription += " " + SrtmDownloadItem.getAbbreviationInScopes(context, item); } if (item.hasActualDataToDownload()) { fullDescription = context.getString( - R.string.ltr_or_rtl_combine_via_bold_point, fullDescription, srtmItem - ? item.getSizeDescription(context, baseMetricSystem) : item.getSizeDescription(context)); + R.string.ltr_or_rtl_combine_via_bold_point, fullDescription, + item.getSizeDescription(context)); } descrTextView.setText(fullDescription); } else if (downloadItem instanceof SrtmDownloadItem) { @@ -269,7 +245,7 @@ public class ItemViewHolder { String pattern = context.getString(R.string.ltr_or_rtl_combine_via_bold_point); String type = item.getType().getString(context); String size = item.getSizeDescription(context) - + " " + SrtmDownloadItem.getAbbreviationInScopes(context, SrtmDownloadItem.isMetersItem(item)); + + " " + SrtmDownloadItem.getAbbreviationInScopes(context, item); String date = item.getDate(dateFormat, showRemoteDate); String fullDescription = String.format(pattern, size, date); if (showTypeInDesc) { @@ -358,7 +334,7 @@ public class ItemViewHolder { } private int getDownloadActionIconId(@NonNull DownloadItem item) { - return item instanceof MultipleIndexItem ? + return item instanceof MultipleDownloadItem ? R.drawable.ic_action_multi_download : R.drawable.ic_action_gsave_dark; } @@ -519,11 +495,11 @@ public class ItemViewHolder { } private void startDownload(DownloadItem item) { - if (item instanceof MultipleIndexItem || item.getType() == SRTM_COUNTRY_FILE) { - selectIndexesToDownload(item); - } else if (item instanceof IndexItem) { + if (item instanceof IndexItem) { IndexItem indexItem = (IndexItem) item; context.startDownload(indexItem); + } else { + selectIndexesToDownload(item); } } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java index b4e11d6bf7..2c691a78de 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/LocalIndexesFragment.java @@ -1032,7 +1032,7 @@ public class LocalIndexesFragment extends OsmandExpandableListFragment implement } if (SrtmDownloadItem.isSRTMItem(child)) { - builder.append(" ").append(SrtmDownloadItem.getAbbreviationInScopes(ctx, SrtmDownloadItem.isMetersItem(child))); + builder.append(" ").append(SrtmDownloadItem.getAbbreviationInScopes(ctx, child)); } if (!Algorithms.isEmpty(child.getDescription())) { From ae2eef75f301f70ef26d4149a4051baa046191d4 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Fri, 16 Apr 2021 18:28:21 +0300 Subject: [PATCH 10/86] refactoring p.5: optimize bottom sheets dialogs --- .../plus/base/ModeSelectionBottomSheet.java | 80 ++++++++++ ...java => MultipleSelectionBottomSheet.java} | 118 +++++++------- .../base/MultipleWithModeBottomSheet.java | 40 +++++ .../plus/base/SelectModeBottomSheet.java | 96 ------------ .../SelectMultipleWithModeBottomSheet.java | 53 ------- .../plus/base/SelectionBottomSheet.java | 144 +++++++++++++----- .../plus/download/SelectIndexesUiHelper.java | 22 +-- 7 files changed, 299 insertions(+), 254 deletions(-) create mode 100644 OsmAnd/src/net/osmand/plus/base/ModeSelectionBottomSheet.java rename OsmAnd/src/net/osmand/plus/base/{SelectMultipleItemsBottomSheet.java => MultipleSelectionBottomSheet.java} (54%) create mode 100644 OsmAnd/src/net/osmand/plus/base/MultipleWithModeBottomSheet.java delete mode 100644 OsmAnd/src/net/osmand/plus/base/SelectModeBottomSheet.java delete mode 100644 OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java diff --git a/OsmAnd/src/net/osmand/plus/base/ModeSelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/ModeSelectionBottomSheet.java new file mode 100644 index 0000000000..f8355c8805 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/base/ModeSelectionBottomSheet.java @@ -0,0 +1,80 @@ +package net.osmand.plus.base; + +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentManager; + +import net.osmand.AndroidUtils; +import net.osmand.plus.R; +import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; + +import java.util.Collections; +import java.util.List; + +public class ModeSelectionBottomSheet extends SelectionBottomSheet { + + public static final String TAG = ModeSelectionBottomSheet.class.getSimpleName(); + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + showElements(primaryDescription, toggleContainer); + hideElements(checkBox, checkBoxTitle, titleDescription, + secondaryDescription, selectedSize, selectAllButton); + } + + @Override + protected void updateItemView(SelectableItem item, View view) { + ImageView ivIcon = view.findViewById(R.id.icon); + TextView tvTitle = view.findViewById(R.id.title); + TextView tvDescr = view.findViewById(R.id.description); + + Drawable icon = uiUtilities.getIcon(item.getIconId(), activeColorRes); + ivIcon.setImageDrawable(icon); + tvTitle.setText(item.getTitle()); + tvDescr.setText(item.getDescription()); + tvDescr.setTextColor(ContextCompat.getColor(app, AndroidUtils.getSecondaryTextColorId(nightMode))); + } + + @Override + protected int getItemLayoutId() { + return R.layout.bottom_sheet_item_with_descr_56dp; + } + + public void setItem(SelectableItem item) { + setItems(Collections.singletonList(item)); + } + + @NonNull + @Override + public List getSelectedItems() { + return allItems; + } + + @Override + protected boolean shouldShowDivider() { + return false; + } + + public static ModeSelectionBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull SelectableItem previewItem, + @NonNull List radioItems, + boolean usedOnMap) { + ModeSelectionBottomSheet fragment = new ModeSelectionBottomSheet(); + fragment.setUsedOnMap(usedOnMap); + fragment.setModes(radioItems); + fragment.setItems(Collections.singletonList(previewItem)); + FragmentManager fm = activity.getSupportFragmentManager(); + fragment.show(fm, TAG); + return fragment; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java similarity index 54% rename from OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java rename to OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java index d9a6204e2c..728dfbaa65 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectMultipleItemsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java @@ -1,7 +1,12 @@ package net.osmand.plus.base; import android.content.res.ColorStateList; +import android.os.Bundle; import android.view.View; +import android.view.View.OnClickListener; +import android.widget.CheckBox; +import android.widget.ImageView; +import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -12,9 +17,7 @@ import androidx.fragment.app.FragmentManager; import net.osmand.AndroidUtils; import net.osmand.plus.R; -import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; -import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton; -import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton.Builder; +import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.util.Algorithms; import java.util.ArrayList; @@ -24,16 +27,16 @@ import static net.osmand.view.ThreeStateCheckbox.State.CHECKED; import static net.osmand.view.ThreeStateCheckbox.State.MISC; import static net.osmand.view.ThreeStateCheckbox.State.UNCHECKED; -public class SelectMultipleItemsBottomSheet extends SelectionBottomSheet { +public class MultipleSelectionBottomSheet extends SelectionBottomSheet { - public static final String TAG = SelectMultipleItemsBottomSheet.class.getSimpleName(); + public static final String TAG = MultipleSelectionBottomSheet.class.getSimpleName(); - private final List allItems = new ArrayList<>(); private final List selectedItems = new ArrayList<>(); private SelectionUpdateListener selectionUpdateListener; @Override - protected void initHeaderUi() { + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); selectAllButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -45,39 +48,49 @@ public class SelectMultipleItemsBottomSheet extends SelectionBottomSheet { selectedItems.clear(); } onSelectedItemsChanged(); - updateItems(checked); + updateItemsSelection(checked); } }); } @Override - protected void createSelectionUi() { - for (final SelectableItem item : allItems) { - boolean checked = selectedItems.contains(item); - final BottomSheetItemWithCompoundButton[] uiItem = new BottomSheetItemWithCompoundButton[1]; - final Builder builder = (BottomSheetItemWithCompoundButton.Builder) new Builder(); - builder.setChecked(checked) - .setButtonTintList(AndroidUtils.createCheckedColorStateList(app, secondaryColorRes, activeColorRes)) - .setLayoutId(R.layout.bottom_sheet_item_with_descr_and_checkbox_56dp) - .setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - boolean checked = !uiItem[0].isChecked(); - uiItem[0].setChecked(checked); - SelectableItem tag = (SelectableItem) uiItem[0].getTag(); - if (checked) { - selectedItems.add(tag); - } else { - selectedItems.remove(tag); - } - onSelectedItemsChanged(); - } - }) - .setTag(item); - setupListItem(builder, item); - uiItem[0] = builder.create(); - items.add(uiItem[0]); - } + protected boolean shouldShowDivider() { + return true; + } + + @Override + protected void updateItemView(final SelectableItem item, View view) { + boolean checked = selectedItems.contains(item); + ImageView imageView = view.findViewById(R.id.icon); + TextView title = view.findViewById(R.id.title); + TextView description = view.findViewById(R.id.description); + final CheckBox checkBox = view.findViewById(R.id.compound_button); + AndroidUiHelper.setVisibility(View.VISIBLE, imageView, title, description, checkBox); + + checkBox.setChecked(checked); + CompoundButtonCompat.setButtonTintList(checkBox, AndroidUtils.createCheckedColorStateList(app, secondaryColorRes, activeColorRes)); + + view.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + boolean checked = !checkBox.isChecked(); + checkBox.setChecked(checked); + if (checked) { + selectedItems.add(item); + } else { + selectedItems.remove(item); + } + onSelectedItemsChanged(); + } + }); + title.setText(item.getTitle()); + description.setText(item.getDescription()); + imageView.setImageDrawable(uiUtilities.getIcon(item.getIconId(), activeColorRes)); + } + + @Override + protected int getItemLayoutId() { + return R.layout.bottom_sheet_item_with_descr_and_checkbox_56dp; } @Override @@ -95,12 +108,6 @@ public class SelectMultipleItemsBottomSheet extends SelectionBottomSheet { } } - private void setupListItem(Builder builder, SelectableItem item) { - builder.setTitle(item.getTitle()); - builder.setDescription(item.getDescription()); - builder.setIcon(uiUtilities.getIcon(item.getIconId(), activeColorRes)); - } - private void updateSelectAllButton() { String checkBoxTitle; if (Algorithms.isEmpty(selectedItems)) { @@ -126,21 +133,16 @@ public class SelectMultipleItemsBottomSheet extends SelectionBottomSheet { rightButton.setEnabled(noEmptySelection); } - private void updateItems(boolean checked) { - for (BaseBottomSheetItem item : items) { - if (item instanceof BottomSheetItemWithCompoundButton) { - ((BottomSheetItemWithCompoundButton) item).setChecked(checked); + private void updateItemsSelection(boolean checked) { + for (SelectableItem item : allItems) { + View v = listViews.get(item); + CheckBox checkBox = v != null ? (CheckBox) v.findViewById(R.id.compound_button) : null; + if (checkBox != null) { + checkBox.setChecked(checked); } } } - protected void setItems(List allItems) { - if (!Algorithms.isEmpty(allItems)) { - this.allItems.clear(); - this.allItems.addAll(allItems); - } - } - protected void setSelectedItems(List selected) { if (!Algorithms.isEmpty(selected)) { selectedItems.clear(); @@ -150,7 +152,7 @@ public class SelectMultipleItemsBottomSheet extends SelectionBottomSheet { @NonNull @Override - public List getSelection() { + public List getSelectedItems() { return selectedItems; } @@ -158,11 +160,11 @@ public class SelectMultipleItemsBottomSheet extends SelectionBottomSheet { this.selectionUpdateListener = selectionUpdateListener; } - public static SelectMultipleItemsBottomSheet showInstance(@NonNull AppCompatActivity activity, - @NonNull List items, - @Nullable List selected, - boolean usedOnMap) { - SelectMultipleItemsBottomSheet fragment = new SelectMultipleItemsBottomSheet(); + public static MultipleSelectionBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull List items, + @Nullable List selected, + boolean usedOnMap) { + MultipleSelectionBottomSheet fragment = new MultipleSelectionBottomSheet(); fragment.setUsedOnMap(usedOnMap); fragment.setItems(items); fragment.setSelectedItems(selected); diff --git a/OsmAnd/src/net/osmand/plus/base/MultipleWithModeBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/MultipleWithModeBottomSheet.java new file mode 100644 index 0000000000..0c3eb8c795 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/base/MultipleWithModeBottomSheet.java @@ -0,0 +1,40 @@ +package net.osmand.plus.base; + +import android.os.Bundle; +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.FragmentManager; + +import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; + +import java.util.List; + +public class MultipleWithModeBottomSheet extends MultipleSelectionBottomSheet { + + public static final String TAG = MultipleWithModeBottomSheet.class.getSimpleName(); + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + showElements(secondaryDescription, toggleContainer, checkBox, + checkBoxTitle, titleDescription, selectedSize, selectAllButton); + } + + public static MultipleWithModeBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull List items, + @Nullable List selected, + @NonNull List modes, + boolean usedOnMap) { + MultipleWithModeBottomSheet fragment = new MultipleWithModeBottomSheet(); + fragment.setUsedOnMap(usedOnMap); + fragment.setItems(items); + fragment.setSelectedItems(selected); + fragment.setModes(modes); + FragmentManager fm = activity.getSupportFragmentManager(); + fragment.show(fm, TAG); + return fragment; + } + +} diff --git a/OsmAnd/src/net/osmand/plus/base/SelectModeBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectModeBottomSheet.java deleted file mode 100644 index d16e61896e..0000000000 --- a/OsmAnd/src/net/osmand/plus/base/SelectModeBottomSheet.java +++ /dev/null @@ -1,96 +0,0 @@ -package net.osmand.plus.base; - -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.FragmentManager; - -import net.osmand.AndroidUtils; -import net.osmand.plus.R; -import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescription; -import net.osmand.plus.helpers.AndroidUiHelper; -import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; - -import java.util.Collections; -import java.util.List; - -public class SelectModeBottomSheet extends SelectionBottomSheet { - - public static final String TAG = SelectModeBottomSheet.class.getSimpleName(); - - private BottomSheetItemWithDescription previewUi; - - private List modes; - private SelectableItem previewItem; - - @Override - protected void initHeaderUi() { - radioGroup.setItems(modes); - - AndroidUiHelper.setVisibility(View.VISIBLE, primaryDescription, toggleContainer); - - AndroidUiHelper.setVisibility(View.GONE, checkBox, checkBoxTitle, - titleDescription, secondaryDescription, selectedSize, selectAllButton); - } - - @Override - protected void createSelectionUi() { - previewUi = (BottomSheetItemWithDescription) new BottomSheetItemWithDescription.Builder() - .setDescription(previewItem.getDescription()) - .setDescriptionColorId(AndroidUtils.getSecondaryTextColorId(nightMode)) - .setTitle(previewItem.getTitle()) - .setIcon(uiUtilities.getIcon(previewItem.getIconId(), activeColorRes)) - .setTag(previewItem) - .setLayoutId(R.layout.bottom_sheet_item_with_descr_56dp) - .create(); - items.add(previewUi); - } - - private void updatePreviewUi() { - previewUi.setTitle(previewItem.getTitle()); - previewUi.setIcon(uiUtilities.getIcon(previewItem.getIconId(), activeColorRes)); - previewUi.setDescription(previewItem.getDescription()); - previewUi.setTag(previewItem); - } - - private void setModes(@NonNull List modes) { - this.modes = modes; - } - - public void setSelectedMode(@NonNull RadioItem mode) { - radioGroup.setSelectedItem(mode); - } - - public void setPreviewItem(@NonNull SelectableItem preview) { - this.previewItem = preview; - if (previewUi != null) { - updatePreviewUi(); - } - } - - @NonNull - @Override - public List getSelection() { - return Collections.singletonList(previewItem); - } - - @Override - protected boolean shouldShowDivider() { - return false; - } - - public static SelectModeBottomSheet showInstance(@NonNull AppCompatActivity activity, - @NonNull SelectableItem previewItem, - @NonNull List radioItems, - boolean usedOnMap) { - SelectModeBottomSheet fragment = new SelectModeBottomSheet(); - fragment.setUsedOnMap(usedOnMap); - fragment.setModes(radioItems); - fragment.setPreviewItem(previewItem); - FragmentManager fm = activity.getSupportFragmentManager(); - fragment.show(fm, TAG); - return fragment; - } - -} diff --git a/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java deleted file mode 100644 index 4318d9c3c7..0000000000 --- a/OsmAnd/src/net/osmand/plus/base/SelectMultipleWithModeBottomSheet.java +++ /dev/null @@ -1,53 +0,0 @@ -package net.osmand.plus.base; - -import android.view.View; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import androidx.fragment.app.FragmentManager; - -import net.osmand.plus.helpers.AndroidUiHelper; -import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; - -import java.util.List; - -public class SelectMultipleWithModeBottomSheet extends SelectMultipleItemsBottomSheet { - - public static final String TAG = SelectMultipleWithModeBottomSheet.class.getSimpleName(); - - private List modes; - - @Override - protected void initHeaderUi() { - super.initHeaderUi(); - radioGroup.setItems(modes); - - AndroidUiHelper.setVisibility(View.VISIBLE, secondaryDescription, toggleContainer, - checkBox, checkBoxTitle, titleDescription, selectedSize, selectAllButton); - } - - private void setModes(@NonNull List modes) { - this.modes = modes; - } - - public void setSelectedMode(@NonNull RadioItem mode) { - radioGroup.setSelectedItem(mode); - } - - public static SelectMultipleWithModeBottomSheet showInstance(@NonNull AppCompatActivity activity, - @NonNull List items, - @Nullable List selected, - @NonNull List modes, - boolean usedOnMap) { - SelectMultipleWithModeBottomSheet fragment = new SelectMultipleWithModeBottomSheet(); - fragment.setUsedOnMap(usedOnMap); - fragment.setItems(items); - fragment.setSelectedItems(selected); - fragment.setModes(modes); - FragmentManager fm = activity.getSupportFragmentManager(); - fragment.show(fm, TAG); - return fragment; - } - -} diff --git a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java index 6f788a9be2..26a66cdb61 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java @@ -1,10 +1,12 @@ package net.osmand.plus.base; +import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; +import android.widget.LinearLayout.LayoutParams; import android.widget.TextView; import androidx.annotation.NonNull; @@ -16,14 +18,21 @@ import net.osmand.plus.UiUtilities; import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.SimpleDividerItem; +import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.widgets.MultiStateToggleButton; +import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; +import net.osmand.util.Algorithms; import net.osmand.view.ThreeStateCheckbox; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment { protected OsmandApplication app; + protected LayoutInflater inflater; protected UiUtilities uiUtilities; protected TextView title; @@ -45,6 +54,10 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment private OnUiInitializedListener uiInitializedListener; private OnApplySelectionListener applySelectionListener; + protected List allItems = new ArrayList<>(); + protected Map listViews = new HashMap<>(); + private List modes; + @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { @@ -57,27 +70,19 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment public void createMenuItems(Bundle savedInstanceState) { app = requiredMyApplication(); uiUtilities = app.getUIUtilities(); + inflater = UiUtilities.getInflater(requireContext(), nightMode); activeColorRes = nightMode ? R.color.icon_color_active_dark : R.color.icon_color_active_light; secondaryColorRes = nightMode ? R.color.icon_color_secondary_dark : R.color.icon_color_secondary_light; - items.add(createHeaderUi()); + items.add(createHeaderView()); if (shouldShowDivider()) { items.add(new SimpleDividerItem(app)); } - createSelectionUi(); + items.add(createSelectionView()); } - @Override - public void onPause() { - super.onPause(); - if (requireActivity().isChangingConfigurations()) { - dismiss(); - } - } - - private BaseBottomSheetItem createHeaderUi() { - LayoutInflater themedInflater = UiUtilities.getInflater(requireContext(), nightMode); - View view = themedInflater.inflate(R.layout.settings_group_title, null); + private BaseBottomSheetItem createHeaderView() { + View view = inflater.inflate(R.layout.settings_group_title, null); title = view.findViewById(R.id.title); titleDescription = view.findViewById(R.id.title_description); @@ -90,22 +95,22 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment checkBoxTitle = view.findViewById(R.id.check_box_title); checkBox = view.findViewById(R.id.check_box); - initHeaderUi(); + if (modes != null) { + radioGroup.setItems(modes); + } + return new SimpleBottomSheetItem.Builder().setCustomView(view).create(); } - protected abstract void createSelectionUi(); - - protected abstract void initHeaderUi(); - - protected boolean shouldShowDivider() { - return true; - } - - @Override - protected void setupRightButton() { - super.setupRightButton(); - applyButtonTitle = rightButton.findViewById(R.id.button_text); + private BaseBottomSheetItem createSelectionView() { + Context themedCtx = UiUtilities.getThemedContext(requireContext(), nightMode); + listContainer = new LinearLayout(themedCtx); + listContainer.setLayoutParams(new LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT)); + listContainer.setOrientation(LinearLayout.VERTICAL); + fillInSelectionList(); + return new SimpleBottomSheetItem.Builder().setCustomView(listContainer).create(); } public void setTitle(@NonNull String title) { @@ -128,20 +133,23 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment applyButtonTitle.setText(title); } - @Override - protected void onRightBottomButtonClick() { - if (applySelectionListener != null) { - applySelectionListener.onSelectionApplied(getSelection()); + public void setModes(@NonNull List modes) { + this.modes = modes; + if (radioGroup != null) { + radioGroup.setItems(modes); } - dismiss(); } - @NonNull - public abstract List getSelection(); + public void setSelectedMode(@NonNull RadioItem mode) { + radioGroup.setSelectedItem(mode); + } - @Override - protected int getRightBottomButtonTextId() { - return R.string.shared_string_apply; + public void setItems(List allItems) { + if (!Algorithms.isEmpty(allItems)) { + this.allItems.clear(); + this.allItems.addAll(allItems); + fillInSelectionList(); + } } public void setUiInitializedListener(OnUiInitializedListener uiInitializedListener) { @@ -152,17 +160,81 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment this.applySelectionListener = onApplySelectionListener; } + private void fillInSelectionList() { + if (listContainer != null && allItems != null) { + recreateList(); + } + } + + private void recreateList() { + listViews.clear(); + listContainer.removeAllViews(); + for (SelectableItem item : allItems) { + setupItemView(item, inflater.inflate(getItemLayoutId(), null)); + } + } + + private void setupItemView(SelectableItem item, View view) { + updateItemView(item, view); + listViews.put(item, view); + listContainer.addView(view); + } + + @NonNull + public abstract List getSelectedItems(); + + protected abstract void updateItemView(SelectableItem item, View view); + + protected abstract int getItemLayoutId(); + + protected abstract boolean shouldShowDivider(); + protected void notifyUiInitialized() { if (uiInitializedListener != null) { uiInitializedListener.onUiInitialized(); } } + protected void showElements(View... views) { + AndroidUiHelper.setVisibility(View.VISIBLE, views); + } + + protected void hideElements(View... views) { + AndroidUiHelper.setVisibility(View.GONE, views); + } + + @Override + protected void setupRightButton() { + super.setupRightButton(); + applyButtonTitle = rightButton.findViewById(R.id.button_text); + } + + @Override + protected void onRightBottomButtonClick() { + if (applySelectionListener != null) { + applySelectionListener.onSelectionApplied(getSelectedItems()); + } + dismiss(); + } + + @Override + protected int getRightBottomButtonTextId() { + return R.string.shared_string_apply; + } + @Override protected boolean useVerticalButtons() { return true; } + @Override + public void onPause() { + super.onPause(); + if (requireActivity().isChangingConfigurations()) { + dismiss(); + } + } + public interface OnUiInitializedListener { void onUiInitialized(); } diff --git a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java index 7933b4eeff..03b9bc7b9b 100644 --- a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java @@ -8,10 +8,10 @@ import androidx.appcompat.app.AppCompatActivity; import net.osmand.map.OsmandRegions; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; -import net.osmand.plus.base.SelectMultipleItemsBottomSheet; -import net.osmand.plus.base.SelectMultipleItemsBottomSheet.SelectionUpdateListener; -import net.osmand.plus.base.SelectModeBottomSheet; -import net.osmand.plus.base.SelectMultipleWithModeBottomSheet; +import net.osmand.plus.base.MultipleSelectionBottomSheet; +import net.osmand.plus.base.MultipleSelectionBottomSheet.SelectionUpdateListener; +import net.osmand.plus.base.ModeSelectionBottomSheet; +import net.osmand.plus.base.MultipleWithModeBottomSheet; import net.osmand.plus.base.SelectionBottomSheet; import net.osmand.plus.base.SelectionBottomSheet.OnApplySelectionListener; import net.osmand.plus.base.SelectionBottomSheet.OnUiInitializedListener; @@ -96,8 +96,8 @@ public class SelectIndexesUiHelper { } } - final SelectMultipleItemsBottomSheet dialog = - SelectMultipleItemsBottomSheet.showInstance(activity, allItems, selectedItems, true); + final MultipleSelectionBottomSheet dialog = + MultipleSelectionBottomSheet.showInstance(activity, allItems, selectedItems, true); dialog.setUiInitializedListener(new OnUiInitializedListener() { @Override @@ -131,7 +131,7 @@ public class SelectIndexesUiHelper { radioItems.add(meters); radioItems.add(feet); - dialog = SelectModeBottomSheet.showInstance(activity, + dialog = ModeSelectionBottomSheet.showInstance(activity, baseSRTM ? meterItem : feetItem, radioItems, true); final RadioItem initRadio = baseSRTM ? meters : feet; @@ -139,7 +139,7 @@ public class SelectIndexesUiHelper { dialog.setUiInitializedListener(new OnUiInitializedListener() { @Override public void onUiInitialized() { - SelectModeBottomSheet dialog = (SelectModeBottomSheet) SelectIndexesUiHelper.this.dialog; + ModeSelectionBottomSheet dialog = (ModeSelectionBottomSheet) SelectIndexesUiHelper.this.dialog; dialog.setTitle(app.getString(R.string.srtm_unit_format)); dialog.setPrimaryDescription(app.getString(R.string.srtm_download_single_help_message)); updateSize(dialog, false); @@ -169,7 +169,7 @@ public class SelectIndexesUiHelper { radioItem.setOnClickListener(new OnRadioItemClickListener() { @Override public boolean onRadioItemClick(RadioItem radioItem, View view) { - ((SelectModeBottomSheet)dialog).setPreviewItem(selectableItem); + ((ModeSelectionBottomSheet)dialog).setItem(selectableItem); updateSize(dialog, false); return true; } @@ -210,7 +210,7 @@ public class SelectIndexesUiHelper { radioItems.add(meters); radioItems.add(feet); - final SelectMultipleWithModeBottomSheet dialog = SelectMultipleWithModeBottomSheet.showInstance( + final MultipleWithModeBottomSheet dialog = MultipleWithModeBottomSheet.showInstance( activity, itemsList, selectedItems, radioItems, true); meters.setOnClickListener(new OnRadioItemClickListener() { @@ -265,7 +265,7 @@ public class SelectIndexesUiHelper { private void updateSize(SelectionBottomSheet dialog, boolean updateDescription) { - double sizeToDownload = getDownloadSizeInMb(dialog.getSelection()); + double sizeToDownload = getDownloadSizeInMb(dialog.getSelectedItems()); String size = DownloadItem.getFormattedMb(app, sizeToDownload); if (updateDescription) { String total = app.getString(R.string.shared_string_total); From 64677aa4349e5b7b6c79239e36e0c718aae77991 Mon Sep 17 00:00:00 2001 From: Eduardo Addad de Oliveira Date: Thu, 15 Apr 2021 19:26:57 +0000 Subject: [PATCH 11/86] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (3717 of 3717 strings) --- OsmAnd/res/values-pt-rBR/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OsmAnd/res/values-pt-rBR/strings.xml b/OsmAnd/res/values-pt-rBR/strings.xml index 620a3dd28e..43f7b7f543 100644 --- a/OsmAnd/res/values-pt-rBR/strings.xml +++ b/OsmAnd/res/values-pt-rBR/strings.xml @@ -3758,7 +3758,7 @@ Nome do arquivo %s arquivos de trilha selecionados O registro de trilhas fará uma pausa quando o aplicativo for encerrado (por meio de aplicativos recentes). (A indicação de segundo plano do OsmAnd desaparece da barra de notificação do Android.) - Especifique o intervalo de registro para a gravação geral da trilha (ligado por meio do widget de \'gravação de viagem\' no mapa). + Especifique o intervalo de registro para o registro geral da trilha (ativado por meio do widget de \'gravação de viagem\' no mapa). Pausar gravação de viagem Retomar a gravação da viagem Padrão do sistema @@ -4053,7 +4053,7 @@ \n \n • As trilhas agora podem ser coloridas por altitude, velocidade ou inclinação. \n -\n • Adicionada opção para alterar a aparência da linha da rota de navegação +\n • Adicionada opção para alterar a aparência da linha da rota de navegação \n \n • Caixa de diálogo \"Gravação de viagem\" atualizada \n From 1474f17c301978b587efafb7c4bbad5fa388a3b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Morais?= Date: Fri, 16 Apr 2021 18:32:42 +0000 Subject: [PATCH 12/86] Translated using Weblate (Portuguese) Currently translated at 100.0% (3717 of 3717 strings) --- OsmAnd/res/values-pt/strings.xml | 220 +++++++++++++++---------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/OsmAnd/res/values-pt/strings.xml b/OsmAnd/res/values-pt/strings.xml index dbdfdef7bf..f8430d5e5d 100644 --- a/OsmAnd/res/values-pt/strings.xml +++ b/OsmAnd/res/values-pt/strings.xml @@ -37,7 +37,7 @@ Supermercado Para turistas Combustível - Pesquisar nome on-line + Pesquisar nome online A ler mosaicos temporários… O índice \'\'{0}\'\' não coube na memória A versão do índice \'\'{0}\'\' não é suportada @@ -159,12 +159,12 @@ \nAgora só é possível ver o mapa pré-carregado, não descarregar novas áreas. A descomprimir o ficheiro… Vire à direita e continue em - Vire fortemente à direita e continue em + Vire acentuadamente à direita e continue em Vire ligeiramente à direita e continue em Vire à esquerda e continue em - Vire fortemente à esquerda e continue em + Vire acentuadamente à esquerda e continue em Vire ligeiramente à esquerda e continue em - Inverta o sentido da marcha e continue em + Faça inversão de marcha e continue em Comece em Continuar Descarregar regiões @@ -214,14 +214,14 @@ Não foi encontrado nada A pesquisar… A pesquisar o endereço … - Pesquisa on-line usando OSM Nominatim + Pesquisa na Internet usando OSM Nominatim Pesquisa online: número da casa, rua, cidade Pesquisa offline Pesquisa online Nível máximo de zoom - Não navegar em mapas on-line para níveis de ampliação além deste. + Não navegar em mapas online para níveis de ampliação além deste. Distância total %1$s, tempo de viagem %2$d h %3$d min. - Serviço de navegação on-line ou off-line. + Serviço de navegação online ou offline. Serviço de navegação A pasta de armazenamento não está acessível no cartão de memória! Descarregar {0} - {1} \? @@ -395,18 +395,18 @@ Definir a transparência (0 - transparente, 255 - opaco) Cancelar o descarregamento\? O mapa base, necessário para fornecer funcionalidade básica, está na fila de descarregamentos. - Mosaicos de mapas on-line e em ficheiros temporários + Mosaicos de mapas online e em ficheiros temporários Mapas padrão (vetorial) - Ative a extensão de \'Mapas on-line\' para selecionar diferentes origens de mapas - Mapas on-line e mosaicos + Ative a extensão de \'Mapas online\' para selecionar diferentes origens de mapas + Mapas online e mosaicos Usar mapas online (descarregar e armazenar mosaicos no cartão de memória). Mapas on-line - Configurar origens de mosaicos ou de mapas on-line. - Aceda a muitos tipos de mapas on-line (também chamados de mosaico ou raster) pré-definidos do OpenStreetMap (como Mapnik) para imagens de satélite e camadas especiais, como mapas aquáticos, climáticos, geológicos, camadas de sombra de relevo, etc. + Configurar origens de mosaicos ou de mapas online. + Aceda a muitos tipos de mapas online (também chamados de mosaico ou raster) pré-definidos do OpenStreetMap (como Mapnik) para imagens de satélite e camadas especiais, como mapas aquáticos, climáticos, geológicos, camadas de sombra de relevo, etc. \n -\nQuaisquer desses mapas podem ser usados como mapa principal (base) para ser mostrado no OsmAnd ou na camada superior ou inferior para outro mapa base (como o mapa off-line normal de OsmAnd). Para tornar qualquer camada inferior do mapa mais visível, certos elementos do mapa vetorial do OsmAnd podem facilmente ser ocultados através do menu \'Configurar mapa\'. +\nQuaisquer desses mapas podem ser usados como mapa principal (base) para ser mostrado no OsmAnd ou na camada superior ou inferior para outro mapa base (como o mapa offline normal de OsmAnd). Para tornar qualquer camada inferior do mapa mais visível, certos elementos do mapa vetorial do OsmAnd podem facilmente ser ocultados através do menu \'Configurar mapa\'. \n -\nOs mosaicos dos mapas podem ser obtidos diretamente através de origens on-line ou podem ser preparados para uso off-line (e copiados manualmente para o diretório de dados do OsmAnd) como uma base de dados sqlite, que pode ser produzido por uma variedade de ferramentas de terceiros para preparação de mapas. +\nOs mosaicos dos mapas podem ser obtidos diretamente através de origens online ou podem ser preparados para uso offline (e copiados manualmente para o diretório de dados do OsmAnd) como uma base de dados sqlite, que pode ser produzido por uma variedade de ferramentas de terceiros para preparação de mapas. Mostra as configurações para ativar o rastreamento em segundo plano e a navegação, despertando periodicamente o dispositivo GPS (com o ecrã desligado). Mostra os recursos de acessibilidade do dispositivo disponíveis diretamente no OsmAnd. Facilita, por exemplo, o ajuste da velocidade da fala para vozes TTS, configurando a navegação no ecrã do teclado direcional, usando um trackball para controlo da ampliação, ou feedback texto-para-fala, por exemplo, para anunciar automaticamente a sua posição. Mostra configurações para recursos de desenvolvimento e depuração como testar ou simular roteamento, o desempenho de renderização do ecrã ou solicitação de voz. Essas configurações são destinadas a programadores e não são necessárias para o utilizador em geral. @@ -465,7 +465,7 @@ para a esquerda para a frente à esquerda horas - em direção a + para Precisão Altitude Sem informação @@ -493,10 +493,10 @@ Edição assíncrona de OSM: POIs / notas guardados no dispositivo Mostrar e gerir POIs/notas do OSM guardados localmente. - Especifique o intervalo de rastreamento on-line. - Intervalo de rastreamento on-line + Especifique o intervalo de rastreamento online. + Intervalo de rastreamento online Especifique o endereço web com a sintaxe de parâmetros: lat={0}, lon={1}, data/hora={2}, hdop={3}, altitude={4}, velocidade={5}, bearing={6}. - Endereço web de rastreamento on-line + Endereço web de rastreamento online Registo de trajeto usando widget GPX ou via \'Gravação de viagem\' nas configurações. Mostrar rota atual Pode descarregar ou atualizar %1$s mapas. @@ -562,7 +562,7 @@ Sem definir Centro do mapa atual Origem: - Pesquisar nas proximidades + Pesquisar perto daqui Rota guardada como \'%1$s\'. Nome do ficheiro: Já existe um ficheiro com o mesmo nome. @@ -665,9 +665,9 @@ Partilhar localização Foi adicionado o ponto de passagem GPX \'\'{0}\'\' Acrescentar ponto ao trilho GPX gravado - Navegação OsmAnd off-line ainda é uma função experimental e não funciona em distâncias superiores a cerca de 20 km. + Navegação OsmAnd offline ainda é uma função experimental e não funciona em distâncias superiores a cerca de 20 km. \n -\nO serviço de navegação está temporariamente mudado para CloudMade on-line. +\nO serviço de navegação está temporariamente mudado para CloudMade online. Não foi possível encontrar a pasta especificada. Local de armazenamento Todos os dados offline na aplicação instalada antiga serão suportados pela nova, mas os pontos Favoritos devem ser exportados da aplicação antiga e depois importados na nova. @@ -717,7 +717,7 @@ Permite gravar onde o seu carro foi estacionado e quanto tempo de estacionamento resta (se houver um limite de tempo). \nA localização e o tempo ficam visíveis no painel de controlo do OsmAnd e num widget no ecrã do mapa. Um alarme pode ser adicionado ao calendário Android como lembrete. Local de estacionamento - Marcar local de estacionamento + Marcar estacionamento Eliminar um marcador de estacionamento Ponto de partida demasiado distante da estrada mais próxima. Local partilhado @@ -736,7 +736,7 @@ Continuar a navegação a seguir que ficou antes inacabada\? (%1$s segundos) Radares de velocidade Informações de circulação - Sem estradas com portagem + Evitar estradas com portagem Nome da rua Configuração do ecrã Onde estou @@ -763,10 +763,10 @@ \na execução em segundo plano Mostrar alertas… Configure avisos de trânsito (limites de velocidade, portagens, lombas, passadeiras, túneis), radares e a faixa de rodagem. - Sem autoestradas + Evitar autoestradas Ajustar à estrada Visualização e navegação móvel de mapas globais do OSM offline e online - OsmAnd é uma aplicação de navegação de código aberto para mapas off-line e on-line + OsmAnd é uma aplicação de navegação de código aberto para mapas offline e online Criar filtro de POI Meio de transporte: Nascer do sol: %1$s @@ -776,8 +776,8 @@ Estilo de renderização Configurar ecrã Mostrar faixas de rodagem - Sem estradas não pavimentadas - Sem balsas/ferries + Evitar estradas não pavimentadas + Evitar balsas/ferries Evitar… Rotas fluorescentes Régua @@ -810,7 +810,7 @@ Selecione um esquema de cores de estrada: Esquema de cores Ver direção para o destino - Ative a extensão de \"Gravação de viagem\" para usar serviços de registo de posição (registo GPX, rastreamento on-line) + Ative a extensão de \"Gravação de viagem\" para usar serviços de registo de posição (registo GPX, rastreamento online) Calcular rota possivelmente não ideal em longas distâncias Ative o GPS nas configurações Serviços de registo @@ -883,7 +883,7 @@ Tem a certeza que quer limpar o seu destino (e destinos intermédios)\? Calcula rotas precisas sem falhas, mas com distância limitada e lento. Roteamento preciso (alfa) - Tirar uma foto + Tirar uma fotografia Extensão Dropbox Alterar ordem Por favor, considere comprar a extensão \'Curvas de nível\' para apoiar o desenvolvimento. @@ -891,7 +891,7 @@ A pedido\? Formato de saída de vídeo: Usar gravador do sistema para vídeo. - Utilizar a aplicação do sistema para fotos. + Utilizar a aplicação do sistema para tirar fotografias. Usar aplicação da câmara A reproduzir o áudio da gravação. \n%1$s @@ -901,7 +901,7 @@ Parar Iniciar Notas de áudio/vídeo - Extensão OsmAnd para curvas de nível off-line + Extensão OsmAnd para curvas de nível offline Medição da distância O local para associar à nota ainda não está definido. Toque em \"Usar posição…\" para atribuir uma nota ao local especificado. Notas de áudio @@ -909,8 +909,8 @@ Notas de áudio/vídeo Partes Curvas de nível - Foto %1$s de %2$s - Tirar uma foto + Fotografia %1$s de %2$s + Tirar uma fotografia Esta extensão disponibiliza \'Curvas de nível\' e \'Sombras de relevo\', que podem ser aplicadas nos mapas padrão do OsmAnd. Estas funcionalidades podem ser apreciadas por atletas, caminhantes e qualquer pessoa interessada na informação do relevo de uma paisagem. \n \nOs dados globais (entre as latitudes 70° norte e 70° sul) são baseados nas medições do SRTM (Shuttle Radar Topography Mission) e do ASTER (Advanced Spaceborn Thermal Emission and Reflection Radiometer), um instrumento de imagens no satélite \'Terra\', o satélite principal do Sistema de Observação da Terra da NASA. O ASTER é um esforço conjunto da NASA, do Ministério da Economia, Comércio e Indústria do Japão e do Sistema Espacial Japonês (J-spacesystems). @@ -948,7 +948,7 @@ %1$d ficheiros restantes Faltam %1$d ficheiros para descarregar Versão completa - Descartar a rota\? + Cancelar a rota\? Parar navegação Ficheiro de alterações OSM %1$s gerado Não foi possível fazer a cópia de segurança das alterações do OSM. @@ -1082,7 +1082,7 @@ Aeronave Tem a certeza que quer eliminar %1$d alterações no OSM\? Eliminar tudo - Wikipédia (off-line) + Wikipédia (offline) Marca marítima Escolha os perfis a mostrar. Perfis da aplicação @@ -1113,7 +1113,7 @@ Inglês Africâner Arménio - Cálculo off-line do segmento de rota OsmAnd + Cálculo offline do segmento de rota OsmAnd Calcular rota de OsmAnd para o primeiro e último segmento da rota Usar o trajeto indicado para a navegação\? Adicionar como destino posterior @@ -1125,13 +1125,13 @@ Informações da rota Preferir autoestradas Preferir autoestradas - Sem estradas com portagem + Evitar portagens Evitar estradas com portagem - Sem estradas não pavimentadas + Evitar estradas não pavimentadas Evitar estradas não pavimentadas - Sem balsas/ferries + Evitar balsas/ferries Evita balsas/ferries - Sem autoestradas + Evitar autoestradas Evita autoestradas Peso máximo Especifique o limite de peso permitido para veículos em rotas. @@ -1141,7 +1141,7 @@ A copiar o ficheiro (%s) para novo destino… A copiar os ficheiros OsmAnd para o novo destino (%s)… A copiar ficheiros de dados do OsmAnd… - Cálculo de rota OsmAnd off-line + Cálculo de rota OsmAnd offline Camião Basco Bielorrusso @@ -1323,7 +1323,7 @@ Armazenamento do mapa Copiar Filtrar por nome - Digite para pesquisar tudo + Digite para pesquisar em tudo Aberto agora Assistente de mapeador OSM Informação A-GPS @@ -1353,7 +1353,7 @@ Iluminação pública Predefinido Estradas em alto contraste - OsmAnd fornece mapas de navegação globais e navegação off-line. + OsmAnd fornece mapas de navegação globais e navegação offline. Bem-vindo(a) Rota atual Mudanças no OSM adicionadas ao conjunto de alterações local @@ -1398,7 +1398,7 @@ A gravar o trajeto Áudio Vídeo - Foto + Fotografia Pontos de rota Segmentos do trajeto Pontos do trajeto @@ -1410,13 +1410,13 @@ Partilhar nota Posição:\n Lat %1$s\n Lon %2$s Notas de áudio/vídeo - Mapa on-line + Mapa online Apenas estradas Pistas de esqui %1$s livre Memória do dispositivo - Para mostrar mapas de esqui, tem de descarregar o mapa off-line especial. - Para mostrar mapas náuticos, tem de descarregar o mapa off-line especial. + Para mostrar mapas de esqui, tem de descarregar o mapa offline especial. + Para mostrar mapas náuticos, tem de descarregar o mapa offline especial. Editar grupo Lugar de estacionamento Compilações @@ -1426,7 +1426,7 @@ Ligar o ecrã Configurar mapa Pontos de rota - Descartar + Cancelar Outros atributos do mapa Mostrar restrições de acesso e portagens Mostrar qualidade da via @@ -1471,7 +1471,7 @@ \'Desligado\' abre o mapa diretamente. Mostrar no arranque Copiado para a área de transferência - Guardar off-line + Guardar offline POI do OpenStreetMap alterado Escolha as estradas que quer evitar durante a navegação. Som @@ -1535,7 +1535,7 @@ Ficheiro GPX com localizações. Localizações Extensões - Sem comboio vaivém + Evitar comboios vaivém Evita usar comboios vaivém, em pequenos trajetos predefinidos como aeroportos Perigo Contorno em negrito @@ -1545,8 +1545,8 @@ Padrão (ciano translúcido) Cor GPX Cor GPX - Espessura GPX - Espessura GPX + Espessura dos trilhos GPX + Espessura dos trilhos GPX Vermelho Vermelho translúcido Laranja translúcido @@ -1593,7 +1593,7 @@ Quer descarregar dados adicionais da Wikipédia (%1$s MB)\? O serviço de localização não está ativado. Quer ativá-lo\? Importar para o OsmAnd - Ler o artigo completo (on-line) + Ler o artigo completo (na Internet) Wikipédia Wikipédia Mostrar detalhes @@ -1659,7 +1659,7 @@ Modo do mapa Fino Médio - Negrito + Grosso Agora a aplicação está autorizada a gravar no armazenamento externo, mas primeiro é necessário reiniciar a aplicação. Mover ↑ Mover ↓ @@ -1807,14 +1807,14 @@ Selecionar outra região Alterar Cartão de memória - Não tem nenhum mapa off-line instalado. Pode escolher um mapa na lista ou descarregar mapas mais tarde através do \'menu - %1$s\'. + Não tem nenhum mapa offline instalado. Pode escolher um mapa na lista ou descarregar mapas mais tarde através do \'menu - %1$s\'. Marcadores Formato de coordenadas Usar teclado do sistema Escolher formato de introdução de coordenada. Poderá sempre alterá-lo ao selecionar \'Opções\'. Introdução rápida de coordenadas - Sem estradas de gelo ou vaus - Evitar estradas de gelo e vaus. + Evitar estradas de gelo ou vaus + Evitar estradas de gelo e vaus (passagens em riachos baixos). Usar posição Adicionar a sua posição como ponto de partida para planear uma rota perfeita. A minha posição @@ -1900,15 +1900,15 @@ Instalar Melhorar cobertura fotográfica com Mapillary Instale o Mapillary para adicionar fotos a este local do mapa. - Fotos on-line - Adicionar fotos - Não há fotos aqui. + Fotografias na Internet + Adicionar fotografias + Não há fotografias aqui. Partilhe as suas imagens ao nível do solo no Mapillary. Widget Mapillary Permite contribuir rapidamente para o Mapillary. - Fotos on-line ao nível da rua para todos. Descubra locais, colabore, capture o mundo. + Fotografias online ao nível da rua para todos. Descubra locais, colabore, capture o mundo. Mapillary - Fotos ao nível da rua para todos. Descubra locais, colabore, capture o mundo. + Fotografias ao nível da rua para todos. Descubra locais, colabore, capture o mundo. O seu destino está localizado numa área de acesso privado. Permitir uso de estradas privadas para esta viagem\? Reiniciar pesquisa Aumentar raio de pesquisa @@ -2023,8 +2023,8 @@ Alto Médio Baixo - Largura das curvas de nível - Largura das curvas de nível + Espessura das curvas de nível + Espessura das curvas de nível Água Ocultar água Utilizar autoestradas @@ -2046,7 +2046,7 @@ Em pausa Viagem Gravado - Registo + Gravar Sem dados Esquema de cores das curvas de nível Velocidade mínima para registo @@ -2213,7 +2213,7 @@ Apenas em Wi-Fi Selecione um livro de viagem Livro de viagens - Página disponível apenas on-line. Abrir no navegador da web\? + Página disponível apenas online. Abrir no navegador da web\? Cache de imagens Eliminar histórico de pesquisa Descarregar imagens @@ -2300,7 +2300,7 @@ Toque num marcador no mapa para movê-lo para a parte superior dos marcadores ativos sem abrir o menu de contexto. \'Um toque\' ativo Faça notas! - Adicione notas de áudio, vídeo ou foto em qualquer ponto do mapa, usando o widget ou o menu de contexto. + Adicione notas de áudio, vídeo ou fotografia em qualquer ponto do mapa, usando o widget ou o menu de contexto. Notas de áudio/vídeo por data Por data Por tipo @@ -2330,7 +2330,7 @@ Mostrar teclado numérico Colar Próximo campo - Renomear marcador + Alterar nome do marcador Um toque no mapa mostra/esconde os botões de controlo e widgets. Modo ecrã cheio Marcador visitado @@ -2459,9 +2459,9 @@ Avançar Painel de controlo Enviar o rastreamento para um serviço web especificado, se o registo de GPX estiver ligado. - Rastreamento on-line (requer GPX) - Iniciar rastreamento on-line - Parar rastreamento on-line + Rastreamento online (requer GPX) + Iniciar rastreamento online + Parar rastreamento online Iniciar novo segmento Estradas não transitáveis Texto @@ -2487,9 +2487,9 @@ Chinês (simplificado) Chinês (Hong Kong) Chinês (tradicional) - Sem escadas + Evitar escadas Evita escadas - Sem passagens por fronteiras + Evitar passagens por fronteiras Evita cruzar fronteiras nacionais Altura máxima Especifique a altura permitida do veículo nas rotas. @@ -2504,7 +2504,7 @@ \n \nPoderá usar o navegador visual e por voz, ver POIs (pontos de interesse), criar e gerir trilhos GPX, usar (através de uma extensão) curvas de nível e dados de altitude, escolher entre os modos motorista, ciclista e pedestre, editar o OpenStreetMap e muito mais. Navegação GPS -\n• Escolha entre modos off-line (sem tarifa de roaming quando estiver no exterior) ou on-line (mais rápido) +\n• Escolha entre modos offline (sem tarifa de roaming quando estiver no exterior) ou online (mais rápido) \n• Orientação por voz passo-a-passo lhe guia ao longo do caminho (vozes gravadas e sintetizadas) \n• A rota é recalculada sempre que se desviar dela \n• Orientação de pista, nomes de ruas e tempo estimado de chegada ajudará ao longo do caminho @@ -2515,14 +2515,14 @@ \n• Suporta pontos intermédios no seu itinerário \n• Grave ou envie um trilho GPX e siga-a \n - Mapa -\n• Mostra POIs (ponto de interesse) perto de si -\n• Ajusta o mapa na sua direção de movimento (ou bússola) -\n• Mostra a sua posição e direção para onde está olhando -\n• Partilhe sua posição para que seus amigos possam encontrá-lo -\n• Mantém seus lugares mais importantes em \'Favoritos\' -\n• Permite-lhe escolher como mostrar nomes no mapa: em inglês, local ou escrita fonética -\n• Mostra mapas on-line especializados, vista de satélite (do Bing), sobreposições diferentes como trajetos GPX de navegação/turismo e camadas adicionais com transparência personalizável + Mapa +\n• Mostra POIs (ponto de interesse) perto de si +\n• Ajusta o mapa na sua direção de movimento (ou bússola) +\n• Mostra a sua posição e direção para onde está olhando +\n• Partilhe sua posição para que seus amigos possam encontrá-lo +\n• Mantém seus lugares mais importantes em \'Favoritos\' +\n• Permite-lhe escolher como mostrar nomes no mapa: em inglês, local ou escrita fonética +\n• Mostra mapas online especializados, vista de satélite (do Bing), sobreposições diferentes como trajetos GPX de navegação/turismo e camadas adicionais com transparência personalizável \n Esqui \nA extensão de mapas de esqui OsmAnd Ski permite que veja pistas de esqui com nível de complexidade e algumas informações adicionais, como localização de elevadores e outras instalações invernais. @@ -2565,7 +2565,7 @@ \n \nAlgumas das características principais: Navegação -\n• Funciona on-line (rápido) ou off-line (sem custos de roaming quando estiver no estrangeiro) +\n• Funciona online (rápido) ou offline (sem custos de roaming quando estiver no estrangeiro) \n• Orientação por voz passo a passo (vozes gravadas e sintetizadas) \n• Orientação de trajetos opcionais, visualização do nome da rua e tempo estimado de chegada \n• Suporta pontos intermédios do seu itinerário @@ -2585,7 +2585,7 @@ \n • Dados do OpenStreetMap disponíveis por país ou região \n • POIs da Wikipédia, ótimo para passeios turísticos \n • Descarregamentos grátis ilimitados, diretamente da aplicação -\n • Mapas off-line vetoriais compactos e atualizados mensalmente +\n • Mapas offline vetoriais compactos e atualizados mensalmente \n \n • Escolha entre região completa ou apenas a rede rodoviária (exemplo: o Japão inteiro tem 700 MB e a rede rodoviária tem apenas 200 MB) Recursos de segurança @@ -2598,7 +2598,7 @@ \n• Visualização de caminhos a pé, pistas de caminhadas e ciclovias, ideal para atividades ao ar livre \n• Navegação e modos de visualização especiais para bicicleta e pedestres \n• Paragens de transporte público opcionais (autocarro, elétrico, comboio) incluindo nomes de linhas -\n• Gravação opcional de viagem para ficheiro GPX local ou serviço on-line +\n• Gravação opcional de viagem para ficheiro GPX local ou serviço online \n• Visualização opcional de velocidade e altitudes \n• Visualização de curvas de nível e sombreamento de relevo (com uma extensão adicional) Contribua diretamente para o OpenStreetMap @@ -2627,7 +2627,7 @@ Normal Atrasado Nos últimos metros - Buffer de tempo para rastreamento on-line + Buffer de tempo para rastreamento online Especificar um buffer de tempo para manter locais para enviar sem conexão Europa - Países Baixos Outros @@ -2636,7 +2636,7 @@ Adicionar favorito eliminar Reabrir - Tamanho da foto + Tamanho da fotografia Definir o tamanho da imagem Obter Recálculo de rota inteligente @@ -2657,7 +2657,7 @@ Desfazer OsmAnd Mapas e navegação -\noff-line +\noffline Enviar POI Básico Avançado @@ -2694,7 +2694,7 @@ O estilo do mapa foi alterado para \"%s\". Nova nota de áudio Nova nota de vídeo - Nova nota de foto + Nova nota de fotografia Adicionar nota OSM Ligar/desligar voz Ativar voz @@ -2712,7 +2712,7 @@ Um botão para adicionar um ponto de rota GPX no local do centro do ecrã. Um botão para adicionar uma nota de áudio no local do centro do ecrã. Um botão para adicionar uma nota de vídeo no local do centro do ecrã. - Um botão para adicionar uma nota de foto no local do centro do ecrã. + Um botão para adicionar uma nota de fotografia no local do centro do ecrã. Um botão para adicionar uma nota OSM no local do centro do ecrã. Um botão para adicionar um POI no local do centro do ecrã. Um botão para ativar ou desativar a orientação por voz durante a navegação. @@ -2756,7 +2756,7 @@ Mudar posição do botão Segure e arraste o botão para mudar a sua posição no ecrã. Nome da ação - As fotos do Mapillary só estão disponíveis on-line. + As fotografias do Mapillary só estão disponíveis online. Repetir Adicionar pontos de rota Adicionar ponto de passagem @@ -2782,7 +2782,7 @@ Túneis Descarregar artigos da Wikipédia de %1$s para lê-los offline. Descarregar dados da Wikipédia - Abrir artigo on-line + Abrir artigo na Internet Ver artigo no navegador web. esta região A procurar o artigo wiki correspondente @@ -2890,17 +2890,17 @@ Mostrar mais Trilhos mostrados Um botão para mostrar ou ocultar trilhos selecionados no mapa. - Sem elétricos + Evitar elétricos Evita elétricos - Sem autocarros + Evitar autocarros Evita autocarros e troleicarros - Sem táxis partilhados + Evitar táxis partilhados Evita táxis partilhados - Sem comboios + Evitar comboios Evitar comboios - Sem metropolitanos + Evitar metropolitanos Evitar metropolitanos subterrâneos e de superfície - Sem balsas/ferries + Evitar balsas/ferries Evita balsas/ferries Milirradianos Paralelos @@ -3133,7 +3133,7 @@ O formato selecionado será aplicado em toda a aplicação. Esta configuração é selecionada por padrão para os perfis: %s Alterar a configuração - Descartar alteração + Cancelar alteração Aplicar só a \"%1$s\" Aplicar a todos os perfis Mensagem de inicialização @@ -3604,9 +3604,9 @@ Projeção pseudo-Mercator Um ficheiro de imagem por mosaico Ficheiro SQLiteDB - Forneça um nome para a origem do mapa on-line. - Introduza ou cole o URL para a origem on-line. - Editar origem on-line + Forneça um nome para a origem do mapa online. + Introduza ou cole o URL para a origem online. + Editar origem online Tempo de validade Projeção de Mercator Formato de armazenamento @@ -3648,7 +3648,7 @@ \nSelecione %1$s e receberá alertas e avisos sobre radares de velocidade. \n \nSelecione %2$s e yodos os dados relacionados a radares de velocidade: alertas, notificações, POIs serão eliminados até que o OsmAnd seja completamente reinstalado. - Obter informações sobre pontos de interesse da Wikipédia. Um guia de bolso off-line para ver artigos sobre locais e destinos. + Obter informações sobre pontos de interesse da Wikipédia. Um guia de bolso offline para ver artigos sobre locais e destinos. Scooter Eliminar o ponto de destino mais próximo Descarregar mapas da Wikipédia @@ -3704,7 +3704,7 @@ Adicionar a um trilho O ponto adicionado não será visível no mapa, já que o grupo selecionado está escondido, pode encontrá-lo em \"%s\". Mostrar ícones de início e fim - Selecionar a largura + Selecionar espessura da linha do trilho Selecione o intervalo em que as marcas com distância ou tempo no trilho serão mostradas. Selecione a opção de divisão desejada: por tempo ou por distância. Personalizado @@ -3715,7 +3715,7 @@ Abrir trilho existente Selecione um ficheiro de trilho para abrir. Criar nova rota - Pronto + Feito Substituir trilho Guardar como novo trilho Rota inversa @@ -3865,10 +3865,10 @@ Fechar nota do OSM Comentário de nota do OSM Pode iniciar sessão pelo método seguro OAuth ou use o seu nome de utilizador e a palavra-passe. - Adicionar foto + Adicionar fotografia Crie uma conta em \nOpenPlaceReviews.org - As fotos são fornecidas pelo projeto de dados abertos OpenPlaceReviews.org. Para enviar as suas fotos tem de criar uma conta no site. + As fotografias são fornecidas pelo projeto de dados abertos OpenPlaceReviews.org. Para enviar as suas fotografias tem de criar uma conta nesse site. Criar uma conta Já tenho uma conta Histórico de pesquisa @@ -3932,12 +3932,12 @@ Analisar por intervalos Enviar para OpenStreetMap Editar trilho - Renomear trilho + Alterar nome do trilho Mudar pasta Sombras de relevo / declives / curvas de nível - OpenPlaceReviews é um projeto comunitário sobre lugares públicos como restaurantes, hotéis, museus, pontos de passagem. Recolhe toda a informação pública sobre eles, como fotos, resenhas, ligações para outros sistemas OpenStreetMap, Wikipédia. + OpenPlaceReviews é um projeto comunitário sobre lugares públicos como restaurantes, hotéis, museus, locais etc. Recolhe toda a informação pública sobre eles, como fotografias, classificações/avaliações, ligações para outros sistemas como o OpenStreetMap e a Wikipédia. \n -\nTodos os dados do OpenPlaceReviews estão abertos e disponíveis para todos: http://openplacereviews.org/data. +\nTodos os dados do OpenPlaceReviews estão abertos e disponíveis para todos: http://openplacereviews.org/data \n \nPode ler mais em: http://openplacereviews.org OpenPlaceReviews From a727eb862b5023abe576a386ca34555aa962df8b Mon Sep 17 00:00:00 2001 From: Shjosan Date: Fri, 16 Apr 2021 09:40:10 +0000 Subject: [PATCH 13/86] Translated using Weblate (Swedish) Currently translated at 75.5% (2808 of 3717 strings) --- OsmAnd/res/values-sv/strings.xml | 215 +++++++++++++++++++------------ 1 file changed, 132 insertions(+), 83 deletions(-) diff --git a/OsmAnd/res/values-sv/strings.xml b/OsmAnd/res/values-sv/strings.xml index 13c1dff07a..a64a0e4961 100644 --- a/OsmAnd/res/values-sv/strings.xml +++ b/OsmAnd/res/values-sv/strings.xml @@ -470,7 +470,7 @@ Indexerar kartan … Indexerar Intressepunkter … Indexerar transport … - Ett I/O-fel har inträffat + I/O-fel km km/h m @@ -736,7 +736,7 @@ Favoritpunkten {0} togs bort utan problem. Meddelande Författarnamn - Kommentaren lades till utan problem + Kommentaren tillagd Det gick inte att lägga till kommentar. Redigera POI Skapa POI @@ -759,7 +759,7 @@ OsmAnd-modul för höjdkurvor offline Avståndsmätning Ljudanteckningar - Denna modul för ljud- och videoanteckningar erbjuder möjlighet att skapa ljud-/foto-/videoanteckningar under en resa, antingen med hjälp av en knapp på kartskärmen eller direkt i kontextmenyn för en position på kartan. + Gör ljud-/foto-/videoanteckningar under en resa, med antingen en kartknapp eller platsens snabbmeny. Ljud-/Videoanteckningar delar Höjdkurvor @@ -1259,7 +1259,10 @@ Vi ändrade namnet på din favoritpunkt till %1$s för att underlätta din smiley i filnamnet. Välj hur mycket hastighetsgränsen måste överskridas för att du ska få ett röstmeddelande. Tolerans för hastighetsgräns - En anonym användare kan inte:\n- skapa grupper;\n- synkronisera grupper och enheter med servern;\n- hantera grupper och enheter på ett personligt ställe på webbplatsen. + Anonyma användare kan inte: +\n- Skapa grupper; +\n- Synkronisera grupper och enheter med servern; +\n- Hantera grupper och enheter i en personlig instrumentpanel på webbplatsen. Anonym användare Inloggad som %1$s Konfigurera kartan @@ -1357,11 +1360,11 @@ Anteckningar Online-karta Dela anteckning - Om du aktiverar denna vy ändras kartstilen till Nautisk och visar alla sjömärken och sjökortssymboler. + Detta plugin berikar OsmAnds kart- och navigationsapp för att också producera nautiska kartor för båtliv, segling och andra typer av vattensporter. \n -\nEn kartfil som innehåller alla nautiska symboler globalt finns tillgänglig som en enda nedladdning med namnet \'World seamarks\'. +\nEtt speciellt karttillägg för OsmAnd kommer att tillhandahålla alla nautiska navigationsmärken och kartsymboler, både för inland och för närliggande navigering. Beskrivningen av varje navigeringsmärke innehåller de detaljer som behövs för att identifiera dem och deras betydelse (kategori, form, färg, sekvens, referens etc.). \n -\nDenna vy kan ändras genom att antingen inaktivera den här igen eller genom att ändra kartstilen under Konfigurera kartor. +\nFör att återgå till en av OsmAnds konventionella kartstilar, helt enkelt avaktivera detta plugin igen, eller ändra \'Kartstil\' under \'Konfigurera karta\' efter önskemål. Detta plugin för OsmAnd ger dig detaljer om globala utförsåkning, längdskidspår, alpina skidvägar, linbanor och skidliftar. Rutter och pister visas färgkodade av svårigheter och avbildas i en speciell \"vinter\" -stil som liknar ett snöfärgat vinterlandskap. \n \nOm du aktiverar den här vyn ändras kartstilen till \"Vinter och skidor\" och visar alla landskapsfunktioner under vinterförhållanden. Denna vy kan återställas genom att antingen avaktivera den igen här, eller genom att ändra \'Kartstil\' under \'Konfigurera karta\' efter önskemål. @@ -1415,12 +1418,12 @@ Mer… Fler åtgärder Visa inte nästa gång - Kom ihåg mitt val + Kom ihåg valet Uppdatera Hämta Laddar ner… - Hämtningen lyckades - Ett oväntat fel uppstod + Nedladdat + Oväntat fel Åtgärd {0} Stäng Utgång @@ -1430,10 +1433,10 @@ Karta Favorit Favoriter - Lägg till i Favoriter + Lägg till i \'Favoriter\' Min position Mina platser - Mina favoriter + Favoriter Mina spår Spelar för tillfället in spår Ljud @@ -1459,21 +1462,21 @@ Dina redigeringar över tiden Ange väntetid att stanna kvar på ruttplaneringsskärmen. - Börja sväng-efter-sväng-navigering efter … + Starta sväng-för-sväng-vägledning efter … Kör - Du håller på att skicka %1$d ändring(ar) till OSM. Är du säker? - Vill du tömma historiken? + Är du säker på att du vill ladda upp %1$d ändringar till OSM\? + Rensa historik\? Aktuell rutt OSM-ändringar lades till i lokala ändringar Markera för att ta bort - I många länder (Tyskland, Frankrike, Italien och andra) är det inte tillåtet att använda varningar för hastighetskameror. OsmAnd tar inget ansvar om du bryter mot lagen. Tryck på Ja endast om du får använda denna funktion. + I många länder (Tyskland, Frankrike, Italien och andra) är användning av hastighetskameravarningar olaglig. OsmAnd tar inget ansvar om du bryter mot lagen. Klicka bara på \'Ja\' om du är berättigad att använda den här funktionen. Hämta kartor För att avspegla dina trafiksignaler och -förordningar på ett korrekt sätt, välj den region du kör i: OsmAnd erbjuder global frånkopplad kartsurfning och frånkopplad navigering. Välkommen A-GPS-info Meddelande - A-GPS-data senast nedladdade: %1$s + A-GPS-data har laddats ner: %1$s Använd inte Adress Visa beskrivning. @@ -1488,9 +1491,9 @@ Europa - Nederländerna Högkontrastvägar Standard - Kopiera OsmAnds datafiler till den nya destinationen? + Flytta OsmAnds datafiler till den nya destinationen\? Kunde inte skapa kartor i den angivna mappen - Kopieringen misslyckades + Det gick inte att flytta filer Extern lagring Lagring för flera användare Internt appminne @@ -1512,16 +1515,16 @@ Wikipedia Wikipedia Visa detaljer - Du håller på att ta bort %1$d anteckningar. Är du säker? + Är du säker på att du vill ta bort %1$d anteckningar\? Wikipedia Importera till OsmAnd Platstjänsten är av. Vill du slå på den\? Du har gamla och inkompatibla data från Wikipedia. Vill du arkivera dem? - Hämta ytterligare data från Wikipedia (%1$s MB)? + Hämta ytterligare data från Wikipedia (%1$s MB)\? Volapük Thai Telugu - Norska (nynorska) + Norska (Nynorska) Newar/Nepal Bhasa Malajiska Haitiska @@ -1565,7 +1568,7 @@ Genomskinlig blå Purpur Genomskinlig purpur - Starta om appen manuellt för att ändringarna ska börja gälla. + En omstart krävs för att tillämpa ändringen. GPX-färg Gul Standard (13) @@ -1589,8 +1592,8 @@ ÅNGRA Hoppa över OsmAnd - Undvik pendeltåg - Undvik att använda pendeltåg + Inget pendeltåg + Undviker att använda pendeltåg Insticksprogram Varning Grundläggande @@ -1600,7 +1603,7 @@ Öppnar Stänger Platser - GPX-fil med anteckningar om platser. + GPX-fil med platser. Typ av POI Lägg till öppettider Kontaktinformation @@ -1620,7 +1623,7 @@ Sluta simulera din plats. Simulera din plats med en beräknad rutt eller med ett inspelat GPX-spår. %1$s nedladdningar kvar - Information om favoriter + Favoritinformation Vägar Lägg till favorit Ange enhet för hastighet. @@ -1634,7 +1637,7 @@ Sjömil per timme (knop) Inspelning av resa Navigering - Körs i bakgrunden + Kör i bakgrunden Lägg till ny Välj kategori Antal rader @@ -1653,7 +1656,7 @@ Aktivera modulen SRTM Senare Fullversionen - Det angivna kategorinamnet finns redan. Ange ett annat namn. + Använd ett kategorinamn som inte redan finns. Kategorinamn Lägg till en ny kategori %.1f MB @@ -1668,9 +1671,9 @@ Dela platsen Sänd Skuggad relief-lager inaktiverat - \'Av\' startar kartskärmen direkt. + \'Av\' startar kartan direkt. Karta hämtad - Kartan över %1$s har hämtats, och du kan nu börja använda den. + %1$s -kartan är redo att användas. Visa kartan QR-kod Ange land @@ -1693,7 +1696,7 @@ Versioner Kontakta oss Skapat en OSM POI - Baskarta över världen (täcker hela världen med låg zoomningsgrad) saknas eller är gammal. Hämta gärna denna karta för en global översikt. + Världskarta (täcker hela världen vid låg zoomnivå) saknas eller är föråldrad. Överväg att ladda ner den för en global översikt. Skicka Teckenförklaring Uppdatering @@ -1721,9 +1724,9 @@ Lägg till kommentar Öppna anteckning igen Stäng anteckning - En anteckning har skapats utan problem + Anteckningen har skapats Det gick inte att skapa anteckningen. - Anteckningen stängdes utan problem + Anteckningen stängd Det gick inte att stänga anteckningen. Ta bort GPX-waypoint? Redigera GPX-waypoint @@ -1767,8 +1770,8 @@ Tid: Appen har inte tillåtelse att använda SD-kortet Ge platsåtkomst. - Appen har inte tillräckliga behörigheter för att komma åt kameran. - Appen har inte tillräckliga behörigheter för att komma åt mikrofonen. + Ge kameraåtkomst. + Ge mikrofonåtkomst. Välj röstvägledning Välj eller hämta röstvägledning för ditt språk. Välj de vägar du vill undvika under navigering. @@ -1790,7 +1793,7 @@ Startpunkt Post borttagen poster raderade - ÅNGRA ALLA + Ångra allt POI-ikoner Skifta startpunkt och destination Antal bidragsgivare @@ -1798,10 +1801,10 @@ Rapport för Välj Ta bort - Du kan ta bort hämtade uppdateringar och få tillbaka originalkartan + Ta bort hämtade uppdateringar och få tillbaka originalkartan Lägg till tidsspann Blockerad väg - Inga data tillgängliga + Ingen data tillgänglig Underjordiska objekt Läs mer Lagringsutrymme @@ -1810,18 +1813,18 @@ Spara ändringar Hitta en parkeringsplats Visa polygoner - Visa MTB-rutter + Visa mountainbike spår Välj kartmarkörer Omvänd ordning Aktivera kartmarkörerna. - Vill du ta bort alla aktiva markörer? - Vill du radera kartmarkörshistoriken? + Ta bort alla aktiva markörer\? + Radera kartmarkörshistoriken\? Aktiva markörer Kartmarkörer Kartmarkör - Vill du lägga till alla punkter i Kartmarkörer? + Lägg till alla punkter som kartmarkörer\? Lägg till i Kartmarkörer - Rekommendationen är att slå av rendering av polygoner. + Det är rekommenderat att slå av rendering av polygoner. Första kartmarkör Andra kartmarkör Verktygsfält @@ -1839,34 +1842,34 @@ Donationer Antal mottagare Redigeringar %1$s, rang %2$s, redigeringar totalt %3$s - Ranglista OSM-redigerare + OSM Editor rankning OsmAnd Live-prenumeration Prenumerera - Krävs för att ge er information om bidrag. - Publikt namn + Behövs för att uppdatera dig om dina bidrag. + Offentligt namn Visa inte mitt namn i rapporter - Månadskostnad + Kostnad per månad Månadsbetalning Aktiv Inaktiv Ange en giltig e-postadress - Ange publikt namn + Ange ett offentligt namn Tack för att du stödjer OsmAnd! \nFör att aktivera alla nya funktioner behöver du starta om OsmAnd. Delar av din donation kommer att skickas till OSM-användare som skickar in kartändringar i det området. Prenumerationsinställningar - Köp en OsmAnd Live-prenumeration först + Köp först ett abonnemang på OsmAnd Live Visa transparent sökfält Beräkna om rutten Toppfält Delat minne Undvik väg - Den för tillfället valda datalagringsmappen är skrivskyddad. Lagringsmappen har tillfälligt ändrats till internminnet. Välj en giltig datalagringsmapp. - Flytta upp - Flytta ned + Växlade till internminnet eftersom den valda datalagringsmappen är skrivskyddad. Välj en skrivbar lagringskatalog. + Flytta ↑ + Flytta ↓ Avsluta navigeringen Fullständig rapport - OpenStreetMap inloggning och lösenord + OSM-användarnamn och lösenord Lägg till kartmarkörer via kartan Hittar inga waypoints Relativ bäring @@ -1897,12 +1900,12 @@ Mellan Tjock Rapporter - Nu har appen tillåtelse att skriva till extern lagringsplats. En manuell omstart av appen krävs. + Appen får nu skriva till extern lagring, men måste startas igen för att göra det. Stödregion Denna prenumeration aktiverar uppdateringar varje timme av kartor runt omkring i världen. \nEn del av inkomsterna går tillbaka till OSM-gemenskapen och betalas ut för varje OSM-bidrag. \nOm du tycker om OsmAnd och OSM och vill stödja och stödjas av dem så är detta ett utmärkt sätt att göra det på. - Destinationen är inte fastställd + Tillgänglighetsplugin: Ingen destination inställd Magnetisk bäring Klipplängd Filnamnet innehåller ogiltiga tecken @@ -1968,7 +1971,7 @@ Krävs för att hämta kartor. Söker efter plats… Oanvänt utrymme - OsmAnds lagringsplats för data (för kartor, GPX-filer med mera): %1$s. + OsmAnds lagringsplats för data (för kartor, spårfiler med mera): %1$s. Bevilja tillstånd Tillåt platsåtkomst Uppdatera alla kartor nu\? @@ -1978,7 +1981,7 @@ Är du säker på att du vill ersätta favoriten %1$s? Rensa alla rutor Ange stad, adress och POI-namn - Donation till OpenStreetMap-gemenskapen + Donation till OSM-communityn Skaffa det Skaffa för %1$s Du har inte några offline-kartor installerade. Du kan välja en karta från listan eller hämta kartor senare via \'Meny - %1$s\'. @@ -1993,7 +1996,7 @@ Minsta loggningsprecision Filter: Ingen loggning om inte denna noggrannhet uppnås. Jul-POI - Visa Jul-POI? + Visa POI:er för julhelgen\? Ljusbrun Mörkbrun Färgschema för konturer @@ -2004,26 +2007,26 @@ Inspelat Spela in Inga data - Visa en systemavisering som gör att du kan starta trippinspelning. - Tillåt motorvägar - Tillåt motorvägar. + Visa ett systemmeddelande som gör det möjligt att starta trippinspelning. + Använd motorvägar + Tillåter motorvägar. Närliggande Wikipedia-artiklar - Sök stad eller region + Stad eller region Tag den %1$d avfarten och kör Ladda upp POI Ruttberäkning - Du har inga GPX-filer ännu - Du kan också lägga till GPX-filer i mappen + Du har inga spårfiler än + Du kan också lägga till spårfiler i mappen Lägg till mer… Utseende - Aktivera snabbstart av inspelning - Med tanke på den kommande julen och nyåret så kan du välja att visa POI som har med julen att göra: julgranar, julmarknader med mera. + Aktivera snabbinspelning + Förutse jul- och nyårsferier kan du välja att visa tillhörande intressepunkter som julgranar och marknader etc. Bredd på konturlinjen Vatten - Prenumerationsavgiften faktureras varje månad. Avbryt den när som helst i Google Play. - En del av din donation skickas till OSM-användare som skickar in ändringar till OpenStreetMap. Kostnaden för abonnemanget förblir densamma. + Prenumerationen debiteras per vald period. Avbryt det på Google Play när som helst. + En del av din donation skickas till OSM-bidragsgivare. Prenumerationskostnaden förblir densamma. En prenumeration aktiverar uppdateringar varje timme, dag eller vecka och obegränsade nedladdningar av alla kartor globalt. - Erhåll obegränsade karthämtningar och kartuppdateringar oftare än en gång i månaden: varje vecka, dagligen eller varje timme. + Få obegränsad nedladdning av kartor, lägg till uppdateringar varje vecka, dagligen eller till och med varje timme. Låg Bredd på konturlinjen Hög @@ -2069,7 +2072,7 @@ \n Kort OLC \nVänligen tillhandahåll fullständig kod - Filen kan inte flyttas. + Det gick inte att flytta filen. Flytta Spår Körstil @@ -2104,7 +2107,7 @@ Tryck på åtgärdsknappen sätter en kartmarkör i centrum av skärmen. Lägg till ny mapp Punkt(er) togs bort. - Du håller på att ta bort %1$d punkt(er). Är du säker? + Är du säker på att du vill ta bort %1$d punkt(er)\? Kurvor att passera längs rutten Vägpunkter, sevärdheter, namngivna funktioner Spår @@ -2135,11 +2138,11 @@ Välj gator i %1$s Ange adress - Ange stad + Skriv stad/samhälle/ort Ange postnummer Närmaste städer Välj stad - Välj postnummer + Postnummer sökning Autozoomning på/av Knapp för att visa eller dölja OSM-anteckningar på kartan. Starta om sökningen @@ -2153,7 +2156,7 @@ Gruppnamn Ändra färg Redigera namn - Lägg till destination + Ange destination Ersätt destination Inget överlägg Inget underlägg @@ -2212,7 +2215,7 @@ Datum Ange användarnamn Användarnamn - Animera min plats + Animera din egen position Analysera på kartan Återställ köp Pausad @@ -2396,15 +2399,15 @@ Tillåt privat åtkomst Tillåta åtkomst till privata områden. Visa zoom-nivå: %1$s - Aktivera kartpanoreringsanimation av \'Min Position\' under navigering. + Aktivera animerad kartpanning av \"Min position\" under navigering. Översikt - Ett tryck på den här åtgärdsknappen kommer att slå på/av automatisk zoomkarta enligt din hastighet. - Aktivera autozoom karta - Inaktivera autozoom karta + Knapp för att slå på eller av hastighetsstyrd automatisk zoom. + Aktivera autozoom + Inaktivera autozoom Lägg till första mellanliggande - Genom att trycka på den här åtgärdsknappen blir skärmens mittpunkt resmålet, alla tidigare valda mål blir den senaste mellanliggande destinationen. - Genom att trycka på den här åtgärdsknappen kan skärmen centrera den nya ruttdestinationen och ersätta den tidigare valda destinationen (om någon). - Genom att trycka på denna åtgärdsknapp kommer skärmens mittpunkt att bli den första mellanliggande destinationen. + En knapp för att göra skärmen centrerad till ruttdestinationen, en tidigare vald destination skulle bli den sista mellandestinationen. + En knapp för att göra skärmen centrerad till den nya ruttdestinationen och ersätta den tidigare valda destinationen (om någon). + En knapp för att göra skärmens centrum till den första mellanliggande destinationen. Anmäl dig till vår e-postlista om apprabatter och få 3 extra kartnedladdningar! Havsdjupskonturer och sjömärken. Tack för att du köpt \"Nautiska djupkonturer\" @@ -3358,4 +3361,50 @@ Förnya prenumerationen I nådeperioden Pausad + Nattkarta + Lägg till onlinekälla + Om du använder dessa ändringar raderas cachade data för denna kakelkälla + Ställ in fartygets höjd + Du kan ställa in fartygets höjd för att undvika låga broar. Tänk på att om bron är rörlig kommer vi att använda dess höjd i öppet tillstånd. + Ställ in fartygets höjd för att undvika låga broar. Tänk på att om bron är rörlig kommer vi att använda dess höjd i öppet tillstånd. + Ställ in fartygets bredd för att undvika smala broar + En växling för att visa eller dölja Mapillary-lagret på kartan. + Ange tillåten fordonslängd på rutter. + Längdsgräns + Bäring + %1$s har tagits bort + Starta om appen för att radera all hastighetskameradata. + Avinstallera och starta om + Den här enheten har inte hastighetskameror. + Rullskridskor + Ta bort närmaste destination + Kontrollera zoomningsnivån för kartan med volymknapparna på enheten. + Volymknappar som zoom + Ange ett namn för punkten + Raderar nästa destination på din rutt. Om det är den slutliga destinationen kommer navigationen att stoppas. + Den tillagda punkten kommer inte att synas på kartan, eftersom den valda gruppen är dold kan du hitta den i \"%s\". + Enduro motorcykel + Skoter + Rullstol + Rullstol framåt + Visa startdialogrutan + Om den är inaktiverad startar inspelningen direkt efter att du har tryckt på widgeten eller menyalternativet och hoppat över bekräftelsedialogrutan. + Anpassa ruttlinjen + Ruttlinje + Ruttlinje skulle användas %1$s som anges i vald kartstil: %2$s. + Upphörd + • OsmAnd Live-uppdateringar flyttade till \"Nedladdningar> Uppdateringar\" +\n +\n• Spår kan nu färga efter höjd, hastighet eller lutning. +\n +\n• Lagt till alternativ för att ändra utseendet på navigeringslinjen +\n +\n• Uppdaterad \"Trip recording\" -dialog +\n +\n + Uppdatera alla kartor som lagts till i %1$s\? + Utgångsnummer + Meddela när den överskrids + Användarpunkter + Utgång \ No newline at end of file From 06102f6c964428f7ee213d642d07991953eaf926 Mon Sep 17 00:00:00 2001 From: WaldiS Date: Fri, 16 Apr 2021 12:47:27 +0000 Subject: [PATCH 14/86] Translated using Weblate (Polish) Currently translated at 98.9% (3679 of 3717 strings) --- OsmAnd/res/values-pl/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OsmAnd/res/values-pl/strings.xml b/OsmAnd/res/values-pl/strings.xml index 90220cf9e8..6a236b130d 100644 --- a/OsmAnd/res/values-pl/strings.xml +++ b/OsmAnd/res/values-pl/strings.xml @@ -4056,13 +4056,13 @@ Zmień trasę nawigacji Trasa nawigacji Wygasł - • OsmAnd Live aktualizacje przeniesiono do \"Pobrane > Aktualizacje\" + • Aktualizacje OsmAnd Live przeniesiono do \"Pobrane > Aktualizacje\" \n \n •Ślady mogą być pokolorowane względem wysokości, prędkości, wzniesień. \n \n • Dodano opcję zmiany wyglądu trasy nawigacji \n -\n • zaktualizowano okno \"Nagrywanie Trasy\" +\n • Zaktualizowano okno \"Nagrywanie Trasy\" \n \n %1$s → … From a78e66d301cf60b3f0230c1593fc698e3d9db9eb Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 16 Apr 2021 06:08:01 +0000 Subject: [PATCH 15/86] Translated using Weblate (German) Currently translated at 100.0% (3926 of 3926 strings) --- OsmAnd/res/values-de/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-de/phrases.xml b/OsmAnd/res/values-de/phrases.xml index 8073e59ecb..7f2a668260 100644 --- a/OsmAnd/res/values-de/phrases.xml +++ b/OsmAnd/res/values-de/phrases.xml @@ -3926,4 +3926,5 @@ Karate Diplomatische Vertretung Art der Bucht + Plateau \ No newline at end of file From 84a9f0482e8edd0dd425cf3981fc6552958eaf71 Mon Sep 17 00:00:00 2001 From: Tymofij Lytvynenko Date: Thu, 15 Apr 2021 14:43:13 +0000 Subject: [PATCH 16/86] Translated using Weblate (Ukrainian) Currently translated at 100.0% (3926 of 3926 strings) --- OsmAnd/res/values-uk/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-uk/phrases.xml b/OsmAnd/res/values-uk/phrases.xml index 9c97460fd4..fda012a2e9 100644 --- a/OsmAnd/res/values-uk/phrases.xml +++ b/OsmAnd/res/values-uk/phrases.xml @@ -3926,4 +3926,5 @@ Карате Дипломатичне відомство Тип затоки + Плато \ No newline at end of file From 251e64341e6dbf4adb195e81a05d85c73ab685ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Babos=20G=C3=A1bor?= Date: Thu, 15 Apr 2021 20:12:33 +0000 Subject: [PATCH 17/86] Translated using Weblate (Hungarian) Currently translated at 99.9% (3924 of 3926 strings) --- OsmAnd/res/values-hu/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-hu/phrases.xml b/OsmAnd/res/values-hu/phrases.xml index 42e9bcd346..9e1280b90c 100644 --- a/OsmAnd/res/values-hu/phrases.xml +++ b/OsmAnd/res/values-hu/phrases.xml @@ -3924,4 +3924,5 @@ Kosárlabdapalánkok Diplomáciai iroda Öböl típusa + Fennsík \ No newline at end of file From 6d53e37fbb0a120a28e733510f65f0e220cb322c Mon Sep 17 00:00:00 2001 From: Franco Date: Thu, 15 Apr 2021 11:10:37 +0000 Subject: [PATCH 18/86] Translated using Weblate (Spanish (Argentina)) Currently translated at 100.0% (3926 of 3926 strings) --- OsmAnd/res/values-es-rAR/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-es-rAR/phrases.xml b/OsmAnd/res/values-es-rAR/phrases.xml index 0505f3549f..900c89112c 100644 --- a/OsmAnd/res/values-es-rAR/phrases.xml +++ b/OsmAnd/res/values-es-rAR/phrases.xml @@ -3926,4 +3926,5 @@ Aros Oficina diplomática Tipo de bahía + Meseta (altiplano) \ No newline at end of file From 94e1318bf9dc96ba1c0e5666e41372211ef63e1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Morais?= Date: Fri, 16 Apr 2021 18:34:43 +0000 Subject: [PATCH 19/86] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (3717 of 3717 strings) --- OsmAnd/res/values-pt-rBR/strings.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/OsmAnd/res/values-pt-rBR/strings.xml b/OsmAnd/res/values-pt-rBR/strings.xml index 43f7b7f543..18e838ce27 100644 --- a/OsmAnd/res/values-pt-rBR/strings.xml +++ b/OsmAnd/res/values-pt-rBR/strings.xml @@ -4049,15 +4049,15 @@ Linha de rota A linha de rota seria usada %1$s especificado no estilo de mapa selecionado: %2$s. Especifique a cor para o modo de mapa: %1$s. - "• As atualizações do OsmAnd Live foram movidas para \"Downloads > Atualizações\" + • As atualizações do OsmAnd Live foram movidas para \"Downloads > Atualizações\" \n -\n • As trilhas agora podem ser coloridas por altitude, velocidade ou inclinação. +\n• As trilhas agora podem ser coloridas por altitude, velocidade ou inclinação. \n -\n • Adicionada opção para alterar a aparência da linha da rota de navegação +\n• Adicionada opção para alterar a aparência da linha da rota de navegação \n -\n • Caixa de diálogo \"Gravação de viagem\" atualizada +\n• Caixa de diálogo \"Gravação de viagem\" atualizada \n -\n" +\n Você não tem nenhuma compra Novo dispositivo / nova conta Se você tiver alguma dúvida, entre em contato conosco em %1$s. From 897c6d290c1eb38c13b7b5db26584fdc62be4e85 Mon Sep 17 00:00:00 2001 From: Shjosan Date: Thu, 15 Apr 2021 16:03:32 +0000 Subject: [PATCH 20/86] Translated using Weblate (Swedish) Currently translated at 100.0% (3926 of 3926 strings) --- OsmAnd/res/values-sv/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-sv/phrases.xml b/OsmAnd/res/values-sv/phrases.xml index bd0a59e189..a621b2a6d3 100644 --- a/OsmAnd/res/values-sv/phrases.xml +++ b/OsmAnd/res/values-sv/phrases.xml @@ -3926,4 +3926,5 @@ Ringar Diplomatiskt kontor Typ av vik + Platå \ No newline at end of file From 3e593d9b0e72c2afb4595dc102ab7a9f9bde7314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Morais?= Date: Fri, 16 Apr 2021 18:04:11 +0000 Subject: [PATCH 21/86] Translated using Weblate (Portuguese) Currently translated at 100.0% (3926 of 3926 strings) --- OsmAnd/res/values-pt/phrases.xml | 105 ++++++++++++++++--------------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/OsmAnd/res/values-pt/phrases.xml b/OsmAnd/res/values-pt/phrases.xml index ed7298f704..ce8d36d89f 100644 --- a/OsmAnd/res/values-pt/phrases.xml +++ b/OsmAnd/res/values-pt/phrases.xml @@ -58,7 +58,7 @@ Loja de frutas e verduras Peixaria Confeitaria - Geladaria + Gelataria Supermercado Loja de chás Loja de massas @@ -80,7 +80,7 @@ Loja de tapetes Loja de caridade Loja de produtos de higiene pessoal - Pronto a vestir + Pronto-a-vestir Vestuário infantil Sapataria Loja de velas @@ -248,7 +248,7 @@ Tipo Tipo Localização - Fonte de água + Origem da água Forma de pagamento Som Tipo @@ -296,7 +296,7 @@ Lareira Superfície Nudismo - Dieta + Pratos Tipo de massagem Tendas Máquina de lavar roupa @@ -576,7 +576,7 @@ Farmácia Hospital Consultório médico - Clínica;Centro de saúde;Unidade de Saúde;Posto Médico + Clínica/centro de saúde/USF/posto médico Primeiros socorros Dentista Clínica geriátrica @@ -732,24 +732,24 @@ Galpão Cabana de caça Local de culto - Cristianismo - Judaísmo - Islamismo - Sikhismo - Budismo - Hinduísmo - Xintoísmo - Taoísmo + Cristã + Judaica + Islâmica + Siquista + Budista + Hinduísta + Xintoísma + Taoísma Vodu - Unitário-Universalismo + Unitário-universalista Multirreligiosa - Jainismo - Espiritualismo - Bahaísmo - Cientologismo - Paganismo + Jainista + Espiritualista + Bahaísta + Cientologista + Paganista Tenrikyo - Zoroastrismo + Zoroastrista Católica Batista Católica romana @@ -828,7 +828,7 @@ Agência de viagens Miradouro Local de acampamento - Local de caravanas + Parque de caravanas Mesa de piquenique Nascente Fonte termal @@ -914,7 +914,7 @@ Bar Taberna Praça de alimentação - Fonte de água potável + Bebedouro (água potável para beber) Churrasqueira Máquinas agrícolas Cesteiro @@ -1004,7 +1004,7 @@ Sumidouro Queda de água;Cascata;Salto;Catarata Rio - Ribeiro;Ribeira;Córrego + Ribeiro(a) Rápidos Cabo Praia @@ -1122,7 +1122,7 @@ Wiki em chinês Wiki em africâner Wiki em alsaciano - Wiki em azeri + Wiki em azerbaijanês Wiki em bengali Wiki em bishnupriya Wiki em bretão @@ -1384,7 +1384,7 @@ Mineral Lama Sulfurosa - Ponto de água + Ponto de água em grande quantidade (para caravanas ou garrafões) Poste com direções Painel Mapa @@ -1650,32 +1650,32 @@ Não aceita Yandex.Money Detalhes de pagamento Salão de eventos - Vegetariana - Vegetariana (alguns) - Apenas vegetariana - Vegetariana - Dieta vegetariana: não - Vegana - Apenas vegana - Vegana - Dieta vegana: não - Livre de glúten - Apenas livre de glúten - Livre de glúten - Dieta livre de glúten: não - Kosher (judaica) - Apenas kosher - Kosher - Dieta kosher: não - Halal (árabe) - Apenas halal - Halal - Dieta halal: não - Livre de lactose - Apenas livre de lactose - Livre de lactose - Dieta livre de lactose: não - Piscitariana (peixes e vegetais) + Pratos vegetarianos: sim + Pratos vegetarianos: alguns + Pratos vegetarianos: só vegetarianos + Pratos vegetarianos: sim + Pratos vegetarianos: não + Pratos veganos: sim + Pratos veganos: só veganos + Pratos veganos: sim + Pratos veganos: não + Pratos livres de glúten: sim + Pratos livres de glúten: unicamente + Pratos livres de glúten: sim + Pratos livres de glúten: não + Pratos kosher (judaica): sim + Pratos kosher (judaica): unicamente + Pratos kosher (judaica): sim + Pratos kosher (judaica): não + Pratos halal (árabe): sim + Pratos halal (árabe): apenas + Pratos halal (árabe): sim + Pratos halal (árabe): não + Pratos sem lactose: sim + Pratos sem lactose: unicamente + Pratos sem lactose: sim + Pratos sem lactose: não + Pratos piscitariana (peixes e vegetais): sim Sim Serviço a conduzir: não Sim @@ -2365,7 +2365,7 @@ Eira histórica Forca histórica Ferrovia histórica - Praça;Praceta + Praça/praceta/largo Artista Escultor Tipo de edifício: igreja @@ -3926,4 +3926,5 @@ Poço Gabinete diplomático Tipo de baía + Planalto \ No newline at end of file From a6999badb1b098fe99d110d5f270988a5a037e33 Mon Sep 17 00:00:00 2001 From: Verdulo Date: Thu, 15 Apr 2021 13:12:40 +0000 Subject: [PATCH 22/86] Translated using Weblate (Esperanto) Currently translated at 100.0% (3926 of 3926 strings) --- OsmAnd/res/values-eo/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-eo/phrases.xml b/OsmAnd/res/values-eo/phrases.xml index 433722b7f3..078dad2d9a 100644 --- a/OsmAnd/res/values-eo/phrases.xml +++ b/OsmAnd/res/values-eo/phrases.xml @@ -3926,4 +3926,5 @@ Korboj Tipo de golfo Diplomatia oficejo + Altebenaĵo \ No newline at end of file From bd1ce16524c066821a8c368c221b267e71128ac3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sveinn=20=C3=AD=20Felli?= Date: Thu, 15 Apr 2021 11:49:29 +0000 Subject: [PATCH 23/86] Translated using Weblate (Icelandic) Currently translated at 100.0% (3926 of 3926 strings) --- OsmAnd/res/values-is/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-is/phrases.xml b/OsmAnd/res/values-is/phrases.xml index 2e3ad0b907..b5b20c54b7 100644 --- a/OsmAnd/res/values-is/phrases.xml +++ b/OsmAnd/res/values-is/phrases.xml @@ -3926,4 +3926,5 @@ Karate Sendiskrifstofa Gerð flóa + Háslétta \ No newline at end of file From 6226c00fac9f5916617bbb583a932d2905900e11 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Fri, 16 Apr 2021 01:28:10 +0000 Subject: [PATCH 24/86] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3926 of 3926 strings) --- OsmAnd/res/values-zh-rTW/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-zh-rTW/phrases.xml b/OsmAnd/res/values-zh-rTW/phrases.xml index 09f71464d7..3b6a127f11 100644 --- a/OsmAnd/res/values-zh-rTW/phrases.xml +++ b/OsmAnd/res/values-zh-rTW/phrases.xml @@ -3926,4 +3926,5 @@ 籃圈 外交部 海灣類型 + 高原 \ No newline at end of file From 2f1e43147da47817646b2dc8bae7ef7b214418ad Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Fri, 16 Apr 2021 22:19:26 +0300 Subject: [PATCH 25/86] refactoring p.6: dialog preparation and update --- ...MultipleSelectionWithModeBottomSheet.java} | 17 +- .../plus/base/SelectionBottomSheet.java | 4 + .../osmand/plus/download/DownloadItem.java | 11 +- .../net/osmand/plus/download/IndexItem.java | 14 +- .../plus/download/MultipleDownloadItem.java | 41 ++- .../plus/download/SelectIndexesUiHelper.java | 267 ++++++++---------- .../plus/download/SrtmDownloadItem.java | 18 +- .../plus/download/ui/ItemViewHolder.java | 34 +-- 8 files changed, 214 insertions(+), 192 deletions(-) rename OsmAnd/src/net/osmand/plus/base/{MultipleWithModeBottomSheet.java => MultipleSelectionWithModeBottomSheet.java} (50%) diff --git a/OsmAnd/src/net/osmand/plus/base/MultipleWithModeBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionWithModeBottomSheet.java similarity index 50% rename from OsmAnd/src/net/osmand/plus/base/MultipleWithModeBottomSheet.java rename to OsmAnd/src/net/osmand/plus/base/MultipleSelectionWithModeBottomSheet.java index 0c3eb8c795..261e8dfdea 100644 --- a/OsmAnd/src/net/osmand/plus/base/MultipleWithModeBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionWithModeBottomSheet.java @@ -12,22 +12,23 @@ import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; import java.util.List; -public class MultipleWithModeBottomSheet extends MultipleSelectionBottomSheet { +public class MultipleSelectionWithModeBottomSheet extends MultipleSelectionBottomSheet { - public static final String TAG = MultipleWithModeBottomSheet.class.getSimpleName(); + public static final String TAG = MultipleSelectionWithModeBottomSheet.class.getSimpleName(); @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); showElements(secondaryDescription, toggleContainer, checkBox, checkBoxTitle, titleDescription, selectedSize, selectAllButton); } - public static MultipleWithModeBottomSheet showInstance(@NonNull AppCompatActivity activity, - @NonNull List items, - @Nullable List selected, - @NonNull List modes, - boolean usedOnMap) { - MultipleWithModeBottomSheet fragment = new MultipleWithModeBottomSheet(); + public static MultipleSelectionWithModeBottomSheet showInstance(@NonNull AppCompatActivity activity, + @NonNull List items, + @Nullable List selected, + @NonNull List modes, + boolean usedOnMap) { + MultipleSelectionWithModeBottomSheet fragment = new MultipleSelectionWithModeBottomSheet(); fragment.setUsedOnMap(usedOnMap); fragment.setItems(items); fragment.setSelectedItems(selected); diff --git a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java index 26a66cdb61..66778f134c 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java @@ -180,6 +180,10 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment listContainer.addView(view); } + public List getAllItems() { + return allItems; + } + @NonNull public abstract List getSelectedItems(); diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadItem.java b/OsmAnd/src/net/osmand/plus/download/DownloadItem.java index 366f3554a7..46b3855a10 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadItem.java @@ -9,6 +9,7 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import java.io.File; +import java.text.DateFormat; import java.util.List; import java.util.Locale; @@ -55,6 +56,13 @@ public abstract class DownloadItem { return type.getBasename(this); } + @NonNull + public abstract List getDownloadedFiles(@NonNull OsmandApplication app); + + public abstract boolean isUseAbbreviation(); + + public abstract String getAbbreviationInScopes(Context ctx); + protected abstract double getSizeToDownloadInMb(); public abstract double getArchiveSizeMB(); @@ -69,8 +77,7 @@ public abstract class DownloadItem { public abstract String getFileName(); - @NonNull - public abstract List getDownloadedFiles(@NonNull OsmandApplication app); + public abstract String getDate(@NonNull DateFormat dateFormat, boolean remote); @NonNull public static String getFormattedMb(@NonNull Context ctx, double sizeInMb) { diff --git a/OsmAnd/src/net/osmand/plus/download/IndexItem.java b/OsmAnd/src/net/osmand/plus/download/IndexItem.java index da0103fdb0..2cd75b2a5e 100644 --- a/OsmAnd/src/net/osmand/plus/download/IndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/IndexItem.java @@ -1,5 +1,7 @@ package net.osmand.plus.download; +import android.content.Context; + import androidx.annotation.NonNull; import net.osmand.IndexConstants; @@ -19,6 +21,7 @@ import java.util.Date; import java.util.List; public class IndexItem extends DownloadItem implements Comparable { + private static final Log log = PlatformUtil.getLog(IndexItem.class); String description; @@ -225,6 +228,16 @@ public class IndexItem extends DownloadItem implements Comparable { public String getDate(java.text.DateFormat format) { return format.format(new Date(timestamp)); } + + @Override + public boolean isUseAbbreviation() { + return false; + } + + @Override + public String getAbbreviationInScopes(Context ctx) { + return ""; + } public static class DownloadEntry { public long dateModified; @@ -254,5 +267,4 @@ public class IndexItem extends DownloadItem implements Comparable { } } - } diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java b/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java index 394a116241..268ae412b8 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java @@ -1,5 +1,7 @@ package net.osmand.plus.download; +import android.content.Context; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -7,6 +9,7 @@ import net.osmand.map.WorldRegion; import net.osmand.plus.OsmandApplication; import java.io.File; +import java.text.DateFormat; import java.util.ArrayList; import java.util.List; @@ -32,7 +35,7 @@ public class MultipleDownloadItem extends DownloadItem { return indexes; } - public List getItems() { + public List getAllItems() { return items; } @@ -96,19 +99,19 @@ public class MultipleDownloadItem extends DownloadItem { return result; } - public List getIndexesToDownload() { - List indexesToDownload = new ArrayList<>(); - for (IndexItem item : getAllIndexes()) { + public List getItemsToDownload() { + List itemsToDownload = new ArrayList<>(); + for (DownloadItem item : getAllItems()) { if (item.hasActualDataToDownload()) { - indexesToDownload.add(item); + itemsToDownload.add(item); } } - return indexesToDownload; + return itemsToDownload; } @Override public boolean hasActualDataToDownload() { - return getIndexesToDownload().size() > 0; + return getItemsToDownload().size() > 0; } @Override @@ -141,4 +144,28 @@ public class MultipleDownloadItem extends DownloadItem { return null; } + @Override + public boolean isUseAbbreviation() { + for (DownloadItem item : items) { + if (item.isUseAbbreviation()) { + return true; + } + } + return false; + } + + @Override + public String getAbbreviationInScopes(Context ctx) { + for (DownloadItem item : items) { + return item.getAbbreviationInScopes(ctx); + } + return ""; + } + + @Override + public String getDate(@NonNull DateFormat dateFormat, boolean remote) { + return ""; + } + + } diff --git a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java index 03b9bc7b9b..fc062c7ddc 100644 --- a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java @@ -5,13 +5,12 @@ import android.view.View; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; -import net.osmand.map.OsmandRegions; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.base.MultipleSelectionBottomSheet; import net.osmand.plus.base.MultipleSelectionBottomSheet.SelectionUpdateListener; import net.osmand.plus.base.ModeSelectionBottomSheet; -import net.osmand.plus.base.MultipleWithModeBottomSheet; +import net.osmand.plus.base.MultipleSelectionWithModeBottomSheet; import net.osmand.plus.base.SelectionBottomSheet; import net.osmand.plus.base.SelectionBottomSheet.OnApplySelectionListener; import net.osmand.plus.base.SelectionBottomSheet.OnUiInitializedListener; @@ -24,7 +23,6 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.List; -import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; import static net.osmand.plus.download.MultipleDownloadItem.getIndexItem; public class SelectIndexesUiHelper { @@ -57,221 +55,196 @@ public class SelectIndexesUiHelper { @NonNull DateFormat df, boolean showRemoteDate, @NonNull ItemsToDownloadSelectedListener l) { + new SelectIndexesUiHelper(i, a, df, showRemoteDate, l).showDialogInternal(); } private void showDialogInternal() { - if (downloadItem.getType() == SRTM_COUNTRY_FILE) { + if (downloadItem.getType() == DownloadActivityType.SRTM_COUNTRY_FILE) { if (downloadItem instanceof MultipleDownloadItem) { - showMultipleSrtmDialog(); + showSrtmMultipleSelectionDialog(); } else { - showSingleSrtmDialog(); + showSrtmModeSelectionDialog(); } } else if (downloadItem instanceof MultipleDownloadItem) { - showBaseDialog(); + showMultipleSelectionDialog(); } } - private void showBaseDialog() { - MultipleDownloadItem multipleDownloadItem = (MultipleDownloadItem) downloadItem; - List indexesToDownload = getIndexesToDownload(multipleDownloadItem); + private void showMultipleSelectionDialog() { List allItems = new ArrayList<>(); List selectedItems = new ArrayList<>(); - OsmandRegions osmandRegions = app.getRegions(); - for (IndexItem indexItem : multipleDownloadItem.getAllIndexes()) { - SelectableItem selectableItem = new SelectableItem(); - selectableItem.setTitle(indexItem.getVisibleName(app, osmandRegions, false)); + prepareItems(allItems, selectedItems); - String size = indexItem.getSizeDescription(app); - String date = indexItem.getDate(dateFormat, showRemoteDate); - String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); - selectableItem.setDescription(description); + MultipleSelectionBottomSheet msDialog = MultipleSelectionBottomSheet.showInstance( + activity, allItems, selectedItems, true); + this.dialog = msDialog; - selectableItem.setIconId(indexItem.getType().getIconResource()); - selectableItem.setObject(indexItem); - allItems.add(selectableItem); - - if (indexesToDownload.contains(indexItem)) { - selectedItems.add(selectableItem); - } - } - - final MultipleSelectionBottomSheet dialog = - MultipleSelectionBottomSheet.showInstance(activity, allItems, selectedItems, true); - - dialog.setUiInitializedListener(new OnUiInitializedListener() { + msDialog.setUiInitializedListener(new OnUiInitializedListener() { @Override public void onUiInitialized() { dialog.setTitle(app.getString(R.string.welmode_download_maps)); } }); - dialog.setSelectionUpdateListener(new SelectionUpdateListener() { + msDialog.setSelectionUpdateListener(new SelectionUpdateListener() { @Override public void onSelectionUpdate() { - updateSize(dialog, true); + updateSize(); } }); - dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); + + msDialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); } - private void showSingleSrtmDialog() { - boolean baseSRTM = SrtmDownloadItem.shouldUseMetersByDefault(app); + private void showSrtmMultipleSelectionDialog() { + List allItems = new ArrayList<>(); + List selectedItems = new ArrayList<>(); + prepareItems(allItems, selectedItems); + + SrtmDownloadItem srtmItem = (SrtmDownloadItem) ((MultipleDownloadItem)downloadItem).getAllItems().get(0); + final int selectedModeOrder = srtmItem.isUseMeters() ? 0 : 1; + final List radioItems = createSrtmRadioItems(); + + MultipleSelectionBottomSheet msDialog = MultipleSelectionWithModeBottomSheet.showInstance( + activity, allItems, selectedItems, radioItems, true); + this.dialog = msDialog; + + msDialog.setUiInitializedListener(new OnUiInitializedListener() { + @Override + public void onUiInitialized() { + dialog.setTitle(app.getString(R.string.welmode_download_maps)); + dialog.setSelectedMode(radioItems.get(selectedModeOrder)); + dialog.setSecondaryDescription(app.getString(R.string.srtm_download_list_help_message)); + } + }); + + msDialog.setSelectionUpdateListener(new SelectionUpdateListener() { + @Override + public void onSelectionUpdate() { + updateSize(); + } + }); + + msDialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); + } + + private void showSrtmModeSelectionDialog() { SrtmDownloadItem srtmItem = (SrtmDownloadItem) downloadItem; + final int selectedModeOrder = srtmItem.isUseMeters() ? 0 : 1; - srtmItem.setUseMeters(true); - SelectableItem meterItem = createSrtmSelectableItem(srtmItem.getIndexItem()); - srtmItem.setUseMeters(false); - SelectableItem feetItem = createSrtmSelectableItem(srtmItem.getIndexItem()); - srtmItem.setUseMeters(baseSRTM); + final List radioItems = createSrtmRadioItems(); + SelectableItem preview = createSelectableItem(srtmItem); - List radioItems = new ArrayList<>(); - RadioItem meters = createRadioItem(meterItem, R.string.shared_string_meters); - RadioItem feet = createRadioItem(feetItem, R.string.shared_string_feets); - radioItems.add(meters); - radioItems.add(feet); + dialog = ModeSelectionBottomSheet.showInstance(activity, preview, radioItems, true); - dialog = ModeSelectionBottomSheet.showInstance(activity, - baseSRTM ? meterItem : feetItem, radioItems, true); - - final RadioItem initRadio = baseSRTM ? meters : feet; - final SelectableItem initItem = baseSRTM ? meterItem : feetItem; dialog.setUiInitializedListener(new OnUiInitializedListener() { @Override public void onUiInitialized() { ModeSelectionBottomSheet dialog = (ModeSelectionBottomSheet) SelectIndexesUiHelper.this.dialog; dialog.setTitle(app.getString(R.string.srtm_unit_format)); dialog.setPrimaryDescription(app.getString(R.string.srtm_download_single_help_message)); - updateSize(dialog, false); - dialog.setSelectedMode(initRadio); + updateSize(); + dialog.setSelectedMode(radioItems.get(selectedModeOrder)); } }); dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); } - private SelectableItem createSrtmSelectableItem(IndexItem indexItem) { - SelectableItem selectableItem = new SelectableItem(); - selectableItem.setTitle(indexItem.getVisibleName(app, app.getRegions(), false)); - String size = indexItem.getSizeDescription(app); - size += " " + SrtmDownloadItem.getAbbreviationInScopes(app, indexItem); - String date = indexItem.getDate(dateFormat, showRemoteDate); - String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); - selectableItem.setDescription(description); - selectableItem.setIconId(indexItem.getType().getIconResource()); - selectableItem.setObject(indexItem); - return selectableItem; + private void prepareItems(List allItems, + List selectedItems) { + final MultipleDownloadItem multipleDownloadItem = (MultipleDownloadItem) downloadItem; + final List itemsToDownload = getItemsToDownload(multipleDownloadItem); + for (DownloadItem downloadItem : multipleDownloadItem.getAllItems()) { + SelectableItem selectableItem = createSelectableItem(downloadItem); + allItems.add(selectableItem); + + if (itemsToDownload.contains(downloadItem)) { + selectedItems.add(selectableItem); + } + } } - private RadioItem createRadioItem(final SelectableItem selectableItem, int titleId) { + private List createSrtmRadioItems() { + List radioItems = new ArrayList<>(); + radioItems.add(createSrtmRadioBtn(R.string.shared_string_meters, true)); + radioItems.add(createSrtmRadioBtn(R.string.shared_string_feets, false)); + return radioItems; + } + + private RadioItem createSrtmRadioBtn(int titleId, + final boolean useMeters) { String title = Algorithms.capitalizeFirstLetter(app.getString(titleId)); RadioItem radioItem = new RadioItem(title); radioItem.setOnClickListener(new OnRadioItemClickListener() { @Override public boolean onRadioItemClick(RadioItem radioItem, View view) { - ((ModeSelectionBottomSheet)dialog).setItem(selectableItem); - updateSize(dialog, false); + updateDialogListItems(useMeters); + updateSize(); return true; } }); return radioItem; } - private void showMultipleSrtmDialog() { - List selectedItems = new ArrayList<>(); - List indexesToDownload = getIndexesToDownload((MultipleDownloadItem) downloadItem); - - List allItems = new ArrayList<>(((MultipleDownloadItem) downloadItem).getItems()); - List itemsList = new ArrayList<>(); - - for (DownloadItem downloadItem : allItems) { - SrtmDownloadItem srtmItem = (SrtmDownloadItem) downloadItem; - SelectableItem selectableItem = new SelectableItem(); - selectableItem.setTitle(downloadItem.getVisibleName(app, app.getRegions(), false)); - String size = downloadItem.getSizeDescription(app); - size += " " + SrtmDownloadItem.getAbbreviationInScopes(app, srtmItem); - String date = srtmItem.getDate(dateFormat, showRemoteDate); - String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); - selectableItem.setDescription(description); - selectableItem.setIconId(downloadItem.getType().getIconResource()); - selectableItem.setObject(downloadItem); - - itemsList.add(selectableItem); - - if (indexesToDownload.contains(downloadItem)) { - selectedItems.add(selectableItem); + private void updateDialogListItems(boolean useMeters) { + List items = new ArrayList<>(dialog.getAllItems()); + for (SelectableItem item : items) { + DownloadItem downloadItem = (DownloadItem) item.getObject(); + if (downloadItem instanceof SrtmDownloadItem) { + ((SrtmDownloadItem) downloadItem).setUseMeters(useMeters); + updateSelectableItem(item, downloadItem); } } + dialog.setItems(items); + } - List radioItems = new ArrayList<>(); - RadioItem meters = new RadioItem(Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_meters))); - RadioItem feet = new RadioItem(Algorithms.capitalizeFirstLetter(app.getString(R.string.shared_string_feets))); - final RadioItem selectedMode = SrtmDownloadItem.isMetersItem(downloadItem) ? meters : feet; - radioItems.add(meters); - radioItems.add(feet); + private SelectableItem createSelectableItem(DownloadItem item) { + SelectableItem selectableItem = new SelectableItem(); + updateSelectableItem(selectableItem, item); + return selectableItem; + } - final MultipleWithModeBottomSheet dialog = MultipleWithModeBottomSheet.showInstance( - activity, itemsList, selectedItems, radioItems, true); + private void updateSelectableItem(SelectableItem selectableItem, + DownloadItem downloadItem) { + selectableItem.setTitle( + downloadItem.getVisibleName(app, app.getRegions(), false)); - meters.setOnClickListener(new OnRadioItemClickListener() { - @Override - public boolean onRadioItemClick(RadioItem radioItem, View view) { -// dialog.recreateList(meterItems); - return true; - } - }); + String size = downloadItem.getSizeDescription(app); + if (downloadItem.isUseAbbreviation()) { + size += " " + downloadItem.getAbbreviationInScopes(app); + } + String date = downloadItem.getDate(dateFormat, showRemoteDate); + String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); + selectableItem.setDescription(description); - feet.setOnClickListener(new OnRadioItemClickListener() { - @Override - public boolean onRadioItemClick(RadioItem radioItem, View view) { -// dialog.recreateList(feetItems); - return true; - } - }); - - dialog.setUiInitializedListener(new OnUiInitializedListener() { - @Override - public void onUiInitialized() { - dialog.setTitle(app.getString(R.string.welmode_download_maps)); - dialog.setSelectedMode(selectedMode); - dialog.setSecondaryDescription(app.getString(R.string.srtm_download_list_help_message)); - } - }); - - dialog.setSelectionUpdateListener(new SelectionUpdateListener() { - @Override - public void onSelectionUpdate() { - updateSize(dialog, true); - } - }); - dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); + selectableItem.setIconId(downloadItem.getType().getIconResource()); + selectableItem.setObject(downloadItem); } private OnApplySelectionListener getOnApplySelectionListener(final ItemsToDownloadSelectedListener listener) { return new OnApplySelectionListener() { @Override public void onSelectionApplied(List selectedItems) { - List indexItems = new ArrayList<>(); + List indexes = new ArrayList<>(); for (SelectableItem item : selectedItems) { IndexItem index = getIndexItem((DownloadItem) item.getObject()); if (index != null) { - indexItems.add(index); + indexes.add(index); } } - listener.onItemsToDownloadSelected(indexItems); + listener.onItemsToDownloadSelected(indexes); } }; } - private void updateSize(SelectionBottomSheet dialog, - boolean updateDescription) { + private void updateSize() { double sizeToDownload = getDownloadSizeInMb(dialog.getSelectedItems()); String size = DownloadItem.getFormattedMb(app, sizeToDownload); - if (updateDescription) { - String total = app.getString(R.string.shared_string_total); - String description = app.getString(R.string.ltr_or_rtl_combine_via_colon, total, size); - dialog.setTitleDescription(description); - } + String total = app.getString(R.string.shared_string_total); + String description = app.getString(R.string.ltr_or_rtl_combine_via_colon, total, size); + dialog.setTitleDescription(description); String btnTitle = app.getString(R.string.shared_string_download); if (sizeToDownload > 0) { btnTitle = app.getString(R.string.ltr_or_rtl_combine_via_dash, btnTitle, size); @@ -280,27 +253,23 @@ public class SelectIndexesUiHelper { } private double getDownloadSizeInMb(@NonNull List selectableItems) { - List downloadItems = new ArrayList<>(); + double totalSizeMb = 0.0d; for (SelectableItem i : selectableItems) { Object obj = i.getObject(); if (obj instanceof DownloadItem) { - downloadItems.add((DownloadItem) obj); + totalSizeMb += ((DownloadItem) obj).getSizeToDownloadInMb(); } } - double totalSizeMb = 0.0d; - for (DownloadItem item : downloadItems) { - totalSizeMb += item.getSizeToDownloadInMb(); - } return totalSizeMb; } - private static List getIndexesToDownload(MultipleDownloadItem multipleDownloadItem) { - if (multipleDownloadItem.hasActualDataToDownload()) { + private static List getItemsToDownload(MultipleDownloadItem md) { + if (md.hasActualDataToDownload()) { // download left regions - return multipleDownloadItem.getIndexesToDownload(); + return md.getItemsToDownload(); } else { // download all regions again - return multipleDownloadItem.getAllIndexes(); + return md.getAllItems(); } } diff --git a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java index c89194910e..aada6acd55 100644 --- a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java @@ -38,6 +38,10 @@ public class SrtmDownloadItem extends DownloadItem { this.useMeters = useMeters; } + public boolean isUseMeters() { + return useMeters; + } + @Nullable public IndexItem getIndexItem() { for (IndexItem index : indexes) { @@ -124,6 +128,16 @@ public class SrtmDownloadItem extends DownloadItem { return getIndexItem().getDate(dateFormat, remote); } + @Override + public boolean isUseAbbreviation() { + return true; + } + + @Override + public String getAbbreviationInScopes(Context ctx) { + return getAbbreviationInScopes(ctx, this); + } + public static boolean shouldUseMetersByDefault(@NonNull OsmandApplication app) { MetricsConstants metricSystem = app.getSettings().METRIC_SYSTEM.get(); return metricSystem != MetricsConstants.MILES_AND_FEET; @@ -147,7 +161,9 @@ public class SrtmDownloadItem extends DownloadItem { } else if (item instanceof SrtmDownloadItem) { return ((SrtmDownloadItem) item).useMeters; } else if (item instanceof MultipleDownloadItem) { - return isMetersItem(((MultipleDownloadItem) item).getItems().get(0)); + for (DownloadItem downloadItem : ((MultipleDownloadItem) item).getAllItems()) { + return isMetersItem(downloadItem); + } } return false; } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index 31d228f50e..1bca97cfe1 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -44,7 +44,6 @@ import net.osmand.plus.download.IndexItem; import net.osmand.plus.download.SelectIndexesUiHelper; import net.osmand.plus.download.SelectIndexesUiHelper.ItemsToDownloadSelectedListener; import net.osmand.plus.download.MultipleDownloadItem; -import net.osmand.plus.download.SrtmDownloadItem; import net.osmand.plus.download.ui.LocalIndexesFragment.LocalIndexOperationTask; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.plus.inapp.InAppPurchaseHelper; @@ -52,11 +51,8 @@ import net.osmand.util.Algorithms; import java.io.File; import java.text.DateFormat; -import java.util.ArrayList; import java.util.List; -import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; - public class ItemViewHolder { protected final TextView nameTextView; @@ -211,8 +207,8 @@ public class ItemViewHolder { MultipleDownloadItem item = (MultipleDownloadItem) downloadItem; String allRegionsHeader = context.getString(R.string.shared_strings_all_regions); String regionsHeader = context.getString(R.string.regions); - String allRegionsCount = String.valueOf(item.getItems().size()); - String leftToDownloadCount = String.valueOf(item.getIndexesToDownload().size()); + String allRegionsCount = String.valueOf(item.getAllItems().size()); + String leftToDownloadCount = String.valueOf(item.getItemsToDownload().size()); String header; String count; if (item.hasActualDataToDownload()) { @@ -231,8 +227,8 @@ public class ItemViewHolder { count = allRegionsCount; } String fullDescription = context.getString(R.string.ltr_or_rtl_combine_via_colon, header, count); - if (SrtmDownloadItem.isSRTMItem(downloadItem)) { - fullDescription += " " + SrtmDownloadItem.getAbbreviationInScopes(context, item); + if (item.isUseAbbreviation()) { + fullDescription += " " + item.getAbbreviationInScopes(context); } if (item.hasActualDataToDownload()) { fullDescription = context.getString( @@ -240,24 +236,14 @@ public class ItemViewHolder { item.getSizeDescription(context)); } descrTextView.setText(fullDescription); - } else if (downloadItem instanceof SrtmDownloadItem) { - SrtmDownloadItem item = (SrtmDownloadItem) downloadItem; - String pattern = context.getString(R.string.ltr_or_rtl_combine_via_bold_point); - String type = item.getType().getString(context); - String size = item.getSizeDescription(context) - + " " + SrtmDownloadItem.getAbbreviationInScopes(context, item); - String date = item.getDate(dateFormat, showRemoteDate); - String fullDescription = String.format(pattern, size, date); - if (showTypeInDesc) { - fullDescription = String.format(pattern, type, fullDescription); - } - descrTextView.setText(fullDescription); } else { - IndexItem item = (IndexItem) downloadItem; String pattern = context.getString(R.string.ltr_or_rtl_combine_via_bold_point); - String type = item.getType().getString(context); - String size = item.getSizeDescription(context); - String date = item.getDate(dateFormat, showRemoteDate); + String type = downloadItem.getType().getString(context); + String size = downloadItem.getSizeDescription(context); + if (downloadItem.isUseAbbreviation()) { + size += " " + downloadItem.getAbbreviationInScopes(context); + } + String date = downloadItem.getDate(dateFormat, showRemoteDate); String fullDescription = String.format(pattern, size, date); if (showTypeInDesc) { fullDescription = String.format(pattern, type, fullDescription); From adbc331fce11f662989fae618f26c164ad6810e1 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Fri, 16 Apr 2021 22:32:14 +0300 Subject: [PATCH 26/86] fix: can't cancel srtm file downloading --- .../osmand/plus/download/DownloadIndexesThread.java | 3 +++ .../net/osmand/plus/download/SrtmDownloadItem.java | 13 ++++--------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java b/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java index ab45abe6d0..568dcc9ed6 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java @@ -243,6 +243,9 @@ public class DownloadIndexesThread { if (item instanceof MultipleDownloadItem) { MultipleDownloadItem multipleDownloadItem = (MultipleDownloadItem) item; cancelDownload(multipleDownloadItem.getAllIndexes()); + } else if (item instanceof SrtmDownloadItem) { + IndexItem indexItem = ((SrtmDownloadItem) item).getIndexItem(); + cancelDownload(indexItem); } else if (item instanceof IndexItem) { IndexItem indexItem = (IndexItem) item; cancelDownload(indexItem); diff --git a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java index aada6acd55..a9a184d57c 100644 --- a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java @@ -10,11 +10,10 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.helpers.enums.MetricsConstants; -import net.osmand.util.Algorithms; import java.io.File; import java.text.DateFormat; -import java.util.Collections; +import java.util.ArrayList; import java.util.List; import static net.osmand.IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; @@ -105,7 +104,6 @@ public class SrtmDownloadItem extends DownloadItem { @Override public String getFileName() { - // may be check only downloaded items if any downloaded return getIndexItem().getFileName(); } @@ -113,14 +111,11 @@ public class SrtmDownloadItem extends DownloadItem { @Override public List getDownloadedFiles(@NonNull OsmandApplication app) { // may be check both indexes files - List result; + List result = new ArrayList<>(); for (IndexItem index : indexes) { - result = index.getDownloadedFiles(app); - if (!Algorithms.isEmpty(result)) { - return result; - } + result.addAll(index.getDownloadedFiles(app)); } - return Collections.emptyList(); + return result; } public String getDate(@NonNull DateFormat dateFormat, boolean remote) { From 54069a1106487900f6590be0d54447115248ff24 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Fri, 16 Apr 2021 22:36:29 +0300 Subject: [PATCH 27/86] Fix: icon not shown for first element in list --- OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java index 66778f134c..f4bea22e62 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java @@ -62,6 +62,7 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { View mainView = super.onCreateView(inflater, parent, savedInstanceState); + createSelectionListIfPossible(); notifyUiInitialized(); return mainView; } @@ -109,7 +110,6 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); listContainer.setOrientation(LinearLayout.VERTICAL); - fillInSelectionList(); return new SimpleBottomSheetItem.Builder().setCustomView(listContainer).create(); } @@ -148,7 +148,7 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment if (!Algorithms.isEmpty(allItems)) { this.allItems.clear(); this.allItems.addAll(allItems); - fillInSelectionList(); + createSelectionListIfPossible(); } } @@ -160,7 +160,7 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment this.applySelectionListener = onApplySelectionListener; } - private void fillInSelectionList() { + private void createSelectionListIfPossible() { if (listContainer != null && allItems != null) { recreateList(); } From 2e955acda6fc4a479cfefe8aa249a1fbd162ca9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Morais?= Date: Sat, 17 Apr 2021 20:55:30 +0000 Subject: [PATCH 28/86] Translated using Weblate (Portuguese) Currently translated at 100.0% (3717 of 3717 strings) --- OsmAnd/res/values-pt/strings.xml | 66 ++++++++++++++++---------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/OsmAnd/res/values-pt/strings.xml b/OsmAnd/res/values-pt/strings.xml index f8430d5e5d..9b9f73d7a6 100644 --- a/OsmAnd/res/values-pt/strings.xml +++ b/OsmAnd/res/values-pt/strings.xml @@ -497,7 +497,7 @@ Intervalo de rastreamento online Especifique o endereço web com a sintaxe de parâmetros: lat={0}, lon={1}, data/hora={2}, hdop={3}, altitude={4}, velocidade={5}, bearing={6}. Endereço web de rastreamento online - Registo de trajeto usando widget GPX ou via \'Gravação de viagem\' nas configurações. + Registo de trajeto usando widget GPX ou via \'Gravar viagem\' nas configurações. Mostrar rota atual Pode descarregar ou atualizar %1$s mapas. Versão gratuita @@ -810,7 +810,7 @@ Selecione um esquema de cores de estrada: Esquema de cores Ver direção para o destino - Ative a extensão de \"Gravação de viagem\" para usar serviços de registo de posição (registo GPX, rastreamento online) + Ative a extensão de \"Gravar viagem\" para usar serviços de registo de posição (registo GPX, rastreamento online) Calcular rota possivelmente não ideal em longas distâncias Ative o GPS nas configurações Serviços de registo @@ -852,7 +852,7 @@ Pesquisar mais povoações/códigos postais Sincroniza trilhos e notas de vídeo/áudio com a sua conta Dropbox. Gravar vídeo - Gravar audio + Gravar áudio Ação predefinida do widget: Ação widget padrão Formato de saída de vídeo @@ -1113,7 +1113,7 @@ Inglês Africâner Arménio - Cálculo offline do segmento de rota OsmAnd + Calcular segmento de rota OsmAnd sem Internet Calcular rota de OsmAnd para o primeiro e último segmento da rota Usar o trajeto indicado para a navegação\? Adicionar como destino posterior @@ -1825,8 +1825,8 @@ Exporte os seus marcadores para um ficheiro que pode especificar aqui: Mover para histórico O grupo terá desaparecido na próxima vez que iniciar a aplicação. - Mostrar linhas direcionais - Mostrar setas no mapa + Mostrar linhas para marcadores + Mostrar setas para marcadores Mostrar passado Esconder o passado Remover dos \'Marcadores do mapa\' @@ -2132,8 +2132,8 @@ Obrigado pelos seus comentários Procurar rua Restaurar - Os marcadores adicionados como um grupo de favoritos ou ponto de rota GPX marcados como Passado permanecerão no mapa. Se o grupo não estiver ativo, os marcadores desaparecerão do mapa. - Manter marcadores passados no mapa + Os marcadores adicionados como um grupo de favoritos ou ponto de rota GPX marcados como visitado (passado) permanecerão no mapa. Se o grupo não estiver ativo, os marcadores desaparecerão do mapa. + Manter marcadores visitados Outros transportes disponíveis nesta paragem. Por favor, conceda ao OsmAnd o acesso à localização para continuar. Eliminar o marcador de mapa \'%s\'\? @@ -2240,11 +2240,11 @@ Digite a longitude Digite a latitude Digite a latitude e a longitude - DD°MM′SS″ - DD.DDDDDD° - DD.DDDDD° - DD°MM.MMMM′ - DD°MM.MMM′ + GG°MM′SS″ + GG.GGGGGG° + GG.GGGGG° + GG°MM.MMMM′ + GG°MM.MMM′ E O S @@ -2321,7 +2321,7 @@ Dois Um Mostrar linha direcional desde a sua posição até os locais dos marcadores ativos. - Mostrar uma ou duas setas indicando a direção para os marcadores ativos. + Mostrar uma ou duas setas indicando a direção em linha reta para os marcadores ativos. Escolher como mostrar a distância para os marcadores ativos. Especifique a quantidade de indicadores de orientação. Número de casas decimais @@ -2602,11 +2602,11 @@ \n• Visualização opcional de velocidade e altitudes \n• Visualização de curvas de nível e sombreamento de relevo (com uma extensão adicional) Contribua diretamente para o OpenStreetMap -\n • Envie relatórios de erros. -\n • Envie trilhos GPX para o OpenStreetMap diretamente da aplicação. -\n • Adicione POIs e envie-os diretamente para o OpenStreetMap (ou mais tarde se estiver desconectado da Internet). -\n • Gravação de viagem opcional também em plano de fundo (enquanto o dispositivo está no modo adormecido). -\n O OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a aplicação reportando erros, a melhorar as traduções ou a programar novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades. +\n• Envie relatórios de erros. +\n• Envie trilhos GPX para o OpenStreetMap diretamente da aplicação. +\n• Adicione POIs e envie-os diretamente para o OpenStreetMap (ou mais tarde se estiver desconectado da Internet). +\n• Gravar viagem opcional também em plano de fundo (enquanto o dispositivo está no modo adormecido). +\n• O OsmAnd é um programa de fonte aberta desenvolvido ativamente. Todos podem contribuir para a aplicação reportando erros, a melhorar as traduções ou a programar novas funcionalidades. Além disso, o projeto conta com contribuições financeiras para financiar a programação e testes de novas funcionalidades. \n Cobertura de mapa e qualidade aproximada: \n• Europa Ocidental: **** @@ -2685,7 +2685,7 @@ %.1f MB Atualizar tudo (%1$s MB) O nome contém demasiadas letras maiúsculas. Continuar\? - Ação rápida + Botão de ação rápida Ação %d Ecrã %d Adicionar marcador de mapa @@ -2701,11 +2701,11 @@ Desativar voz Adicionar local de estacionamento Adicionar ação - Editar ação + Editar botão de ação rápida Adicionar favorito Adicionar ação - Eliminar ação - Tem a certeza de que quer eliminar a ação \"%s\"\? + Eliminar botão de ação rápida + Tem a certeza de que quer eliminar o botão de ação rápida \"%s\"\? Mostrar lista dos favoritos Nome do modelo O botão é para adicionar um marcador de mapa no local do centro do ecrã. @@ -2720,8 +2720,8 @@ Mostrar uma janela temporal " guardado em " Local - O nome da ação rápida foi alterado para %1$s para evitar duplicação. - Nome de ação rápida duplicado + O nome do botão de ação rápida foi alterado para %1$s para evitar duplicação. + Nome do botão de ação rápida duplicado Uma alternância para mostrar ou ocultar os pontos favoritos no mapa. Uma alternância para mostrar ou ocultar POIs no mapa. Mostrar %1$s @@ -2754,7 +2754,7 @@ Adicionar origem do mapa A origem do mapa foi alterada para \"%s\". Mudar posição do botão - Segure e arraste o botão para mudar a sua posição no ecrã. + Pressione e segure arrastando o botão para mudar a sua posição no ecrã. Nome da ação As fotografias do Mapillary só estão disponíveis online. Repetir @@ -2997,7 +2997,7 @@ Roteamento de terceiros Escolha os perfis mostrados na aplicação. Perfis da aplicação - Adicione pelo menos um item à lista nas configurações de \'Ação rápida\' + Adicione pelo menos um item à lista nas configurações de \'Botão de ações rápidas\' Esqui alpino e descendente Pistas para esqui alpino ou downhill e acesso a teleféricos de esqui. Esqui de travessia e nórdico @@ -3525,7 +3525,7 @@ Selecione as línguas dos artigos da Wikipédia no mapa. Mude para qualquer língua disponível enquanto lê o artigo. OsmAnd + Mapillary OsmAnd Tracker - Ação rápida + Botão de ações rápidas Régua radial Medir distância Viagem (Wikivoyage e Wikipédia) @@ -3759,9 +3759,9 @@ Registar o trilho num ficheiro GPX Trilhos Adicionar ponto de passagem de trilho - Gravação de viagem + Gravar viagem Gravar - Especifique o intervalo de registo para a gravação geral do trilho (ligado através do widget de \'gravação de viagem\' no mapa). + Especifique o intervalo de registo para a gravação geral do trilho (ligado através do widget de \'Gravar viagem\' no mapa). Pausar a gravação da viagem Retomar a gravação da viagem Predefinição do sistema @@ -3962,11 +3962,11 @@ Tem a certeza que quer eliminar todas as %s atualizações OsmAnd Live\? • Atualizações OsmAnd Live movidas para \"Descarregamentos > Atualizações\" \n -\n • Os trilhos podem ser agora coloridos conforme a altitude, velocidade e declive. +\n• Os trilhos podem ser agora coloridos conforme a altitude, velocidade e declive. \n -\n • Adicionada opção para alterar a aparência da linha de rota de navegação +\n• Adicionada opção para alterar a aparência da linha de rota de navegação \n -\n • Janela de diálogo \"Gravação do trilho\" atualizada +\n• Janela de diálogo \"Gravação do trilho\" atualizada \n \n O roteamento pode evitar subidas íngremes. From 6005f111247ad27a317c305b653803d44a1a758c Mon Sep 17 00:00:00 2001 From: Evgenii Martynenko Date: Sat, 17 Apr 2021 21:15:38 +0000 Subject: [PATCH 29/86] Translated using Weblate (Russian) Currently translated at 100.0% (3717 of 3717 strings) --- OsmAnd/res/values-ru/strings.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/OsmAnd/res/values-ru/strings.xml b/OsmAnd/res/values-ru/strings.xml index dd9c8f6526..726ed7bd3d 100644 --- a/OsmAnd/res/values-ru/strings.xml +++ b/OsmAnd/res/values-ru/strings.xml @@ -141,8 +141,8 @@ Использовать данные о высотах Действие переименовано в %1$s, чтобы избежать дублирования. Обнаружен дубликат имени - Переключатель, чтобы показать или скрыть избранные точки на карте. - Переключатель, чтобы показать или скрыть POI на карте. + Переключатель для отображения или скрытия избранных точек на карте. + Переключатель для отображения или скрытия POI на карте. Категория Действия Если оставить это поле пустым, то оно будет автоматически заполнено адресом или названием места. @@ -3660,7 +3660,7 @@ Вы можете указать высоту судна, чтобы избегать низких мостов. Имейте в виду, что если мост раздвижной, будет использована его высота в открытом состоянии. Укажите высоту судна, чтобы избежать низких мостов. Имейте в виду, что если мост раздвижной, будет использована его высота в открытом состоянии. Укажите ширину судна, чтобы избежать узких мостов - Переключатель, чтобы показать или скрыть слой Mapillary на карте. + Переключатель для отображения или скрытия слоя Mapillary на карте. Законодательство В некоторых странах и регионах использование предупреждений о камерах контроля скорости запрещено законом. \n From 7f43d7060908a6508eeff43a1804edbd0117d734 Mon Sep 17 00:00:00 2001 From: Branko Kokanovic Date: Sat, 17 Apr 2021 21:53:02 +0000 Subject: [PATCH 30/86] Translated using Weblate (Serbian) Currently translated at 99.1% (3687 of 3717 strings) --- OsmAnd/res/values-sr/strings.xml | 125 ++++++++++++++++++++++++------- 1 file changed, 99 insertions(+), 26 deletions(-) diff --git a/OsmAnd/res/values-sr/strings.xml b/OsmAnd/res/values-sr/strings.xml index f3e3dd9e2b..735f5382be 100644 --- a/OsmAnd/res/values-sr/strings.xml +++ b/OsmAnd/res/values-sr/strings.xml @@ -1005,7 +1005,7 @@ Молимо, унесите име новог филтера који ће бити придодат језичку „Категорије“. Чланарина се наплаћује по одабраном периоду. Можете је отказати на Гугл плеју кад год пожелите. Прилог ОСМ заједници - Део Вашег прилога ће бити послат корисницима ОСМ-а. Чланарина остаје иста. + Део Ваше чланарине ће бити послат корисницима ОСМ-а. Чланарина остаје иста. Чланарина омогућава часовне, дневне и седмичне надоградње, и неограничена преузимања свих карата. Добавите је Добавите за %1$s @@ -1062,7 +1062,7 @@ Указуј на правац циљне тачке звуком. Упутства трешењем Указуј на правац циљне тачке трешњом. - Омогући навођење живих измена ОСМ-а. + Омогући навођење измена ОСМ-а уживо. Навођење уживо ОСМ-а Одредиште није подешено Изаберите категорију @@ -2543,7 +2543,7 @@ Не могу да ажурирам локални списак тачака од интереса. Дугме за додавање GPX пролазне тачке на средину екрана. Прикажи слике - Укинули сте чланарину за OsmAnd Live + Отказали сте чланарину за OsmAnd уживо Обновите чланарину да наставите да користите све ове функционалности: Може да се користи за идентификацију Може да се користи за праћење @@ -2581,7 +2581,7 @@ Стил опште намене. Густи градови су приказани јасно. Приказује изохипсе, путање, квалитет подлоге, забране приступа, блокаде путева, исцртавање путева по SAC алпској скали, објекти за спортове на брзацима. Отвори Википедија везу са интернетом Веза ће бити отворена у веб читачу. - Да бисте читали чланке са Википедије и Wikivoyage-а, претплатите се на OsmAnd Live. + Да бисте читали чланке са Википедије и Wikivoyage-а, купите чланарину на OsmAnd уживо. Како да отворим везу? Читај Википедију ван мреже Преузми све @@ -2703,7 +2703,7 @@ \n - Окретањем мапе према компасу или правцу кретања \n - Навођењем у праву траку, приказ ограничења брзине, снимљени и синтетизовани гласови за навођење \n - Набавите OsmAnd Live да откључате ове могућности: дневна ажурирања карти са неограниченим бројем скидања, сви и плаћени и бесплатни додаци, Википедија, Wikivoyage и још много тога. + Набавите OsmAnd уживо да откључате ове могућности: дневна ажурирања карти са неограниченим бројем скидања, сви и плаћени и бесплатни додаци, Википедија, Wikivoyage и још много тога. Карта \n • Приказ тачака од интереса око Вас \n • Подешавање карте према правцу кретања (или компасу) @@ -2777,14 +2777,14 @@ %1$s / месечно %1$.2f %2$s / месечно Уштедите %1$s - Тренутна претплата + Тренутна чланарина Месечно обнављање Квартално обнављање Годишње обнављање %1$.2f %2$s Период плаћања: Донације помажу финансирање OSM картографа. - Претплате + Чланарине Прикажи само слике од 360° Покрени Гварани @@ -2894,8 +2894,8 @@ Приказане путање Укрцавање на стајању Дугме које приказује или сакрива одабране путање са карте. - Омогући јавни превоз на OsmAnd Live изменама. - OsmAnd Live јавни превоз + Омогући јавни превоз на OsmAnd уживо изменама. + OsmAnd уживо јавни превоз Калдрма Поплочано камење Ауто-пут @@ -3119,7 +3119,7 @@ %1$s првих %2$s %1$s првих %2$s онда %1$s - Поништи претплату + Поништи чланарину %1$s • Уштеди %2$s Поставке за профил: OsmAnd користи UTM Standard format који је сличан, али није истоветан као UTM NATO format. @@ -3484,7 +3484,7 @@ Измери удаљеност Путовање (Wikivoyage и Википедија) Омиљени - Претплата - OsmAnd уживо + Чланарина - OsmAnd уживо OsmAnd куповине Упутство за легенду карте. Профили навођења @@ -3648,11 +3648,11 @@ \nОнемогућите некоришћене додатке да сакријете све њихове контроле. %1$s. Елементи Фиоке, Контекст мени Акције Контекст менија - Наплатом ће бити оптерећен ваш Гугл Плеј налог при потврди куповине. + "Наплатом ће бити оптерећен ваш Гугл Плеј налог при потврди куповине. \n -\n Претплата се аутоматски обнавља уколико није отказана пре датума обнове. Ваш налог биће задужен периодом обнове (месец / три месеца / годину дана) само на дан обнове. +\n Чланарина се аутоматски обнавља уколико није отказана пре датума обнове. Ваш налог биће задужен периодом обнове (месец / три месеца / годину дана) само на дан обнове. \n -\n Претплатама можете управљати и отказати их тако што ћете отићи на ваша Гугл Плеј подешавања. +\n Чланаринама можете управљати и отказати их тако што ћете отићи на ваша Гугл Плеј подешавања." • Нове офлајн мапе нагиба \n \n • Пуно прилагођавање Фаворита и ГПКС тачака – прилагођавање боја, икона, облика @@ -3827,17 +3827,17 @@ Име: З - А Име: А - З Хвала вам што сте купили „Контурне линије“ - Претплата се наплаћује по изабраном периоду. Откажите га у АппГалери у било ком тренутку. - Уплата ће бити наплаћена са вашег рачуна АппГалери при потврди куповине. + Чланарина се наплаћује по изабраном периоду. Откажите је у AppGallery у било ком тренутку. + Уплата ће бити наплаћена са вашег рачуна AppGallery при потврди куповине. \n -\nПретплата се аутоматски обнавља уколико није отказана пре датума обнове. Ваш рачун ће бити задужен за период обнове (месец / три месеца / година) само на датум обнове. +\nЧланарина се аутоматски обнавља уколико није отказана пре датума обнове. Ваш рачун ће бити задужен за период обнове (месец / три месеца / година) само на датум обнове. \n -\nПретплатама можете управљати и отказати их тако што ћете отићи у подешавања апликације АппГалери. +\nЧланаринама можете управљати и отказати их тако што ћете отићи у подешавања апликације AppGallery. Избегавајте пешачке стазе Избегавајте пешачке стазе Развој - ОсмАнд лајв подаци - ОсмАнд лајв подаци + OsmAnd уживо подаци + OsmAnd уживо подаци Двофазно усмеравање за аутомобилску навигацију. Развој матичног јавног превоза Пребаците се на Јава (безбедан) прорачун рутирања јавног превоза @@ -3871,11 +3871,11 @@ Користите корисничко име и лозинку Налог Пријавите се - Управљајте претплатом - Постоји проблем са вашом претплатом. Кликните на дугме да бисте отворили подешавања претплате за Гугле Плеј и да бисте поправили начин плаћања. - Претплата на ОсмАнд лајв је истекла - Претплата на ОсмАнд лајв је паузирана - Претплата на ОсмАнд лајв је на чекању + Управљајте чланарином + Постоји проблем са Вашом чланарином. Кликните на дугме да бисте отворили подешавања претплате за Гугле Плеј и да бисте поправили начин плаћања. + Чланарина за OsmAnd уживо је истекла + Чланарина на OsmAnd уживо је паузирана + Чланарина на OsmAnd уживо је на чекању Историја маркера Пошаљите ГПКС датотеку на ОпенСтритМап Унесите ознаке одвојене зарезом. @@ -3959,7 +3959,7 @@ Екетрични бициклизам Планински бициклизам Друмски бициклизам - Стаднардни бициклизам + Стандардни бициклизам Теретни камион Камионет Камион @@ -3988,4 +3988,77 @@ Преименуј путању Сачувај и настави Сви несачувани подаци ће бити изгубљени. + Преферира планинске пешачке путеве + Преферирај планинске пешачке путеве + Дозвољава водене токове који некад пресуше + Дозвољава потоке и одвоје + Дозволи потоке и одводе + Дозволи несталне водене токове + Кључ API-ја + Адреса сервера + Унесите параметар + Задржи је празном ако није + Адреса са свим параметрима ће изгледати овако: + Тестирај израчунавање пута + Фасцикле + Одаберите фасциклу + Одаберите фасциклу или додајте нову + Анализирај интервале поделе + Отпреми на OpenStreetMap + Измени фасциклу + сек + Пролажење + Прилазак + Припрема унапред + Припрема + Ван пута + Долазак на одредиште + Скретање + Интервали времена и удаљености + Времена објава различитих гласовних навођења зависе од типа објаве, као и тренутне и подразумеване брзине навођења. + Мрежни усмеривачи + Пешке + Вожња аутомобила + Измени мрежни усмеривач + Додај мрежни усмеривач + Мрежни усмеривач + Обриши овај мрежни усмеривач\? + Обриши пролазну тачку + Копирај у ознаке карте + Рељеф / Нагиб / Контурне линије + OpenPlaceReviews + Пријавите се за OpenPlaceReviews + Вожња ван пута + Прекидач да на карти покаже или сакрије справицу са координатама. + Удаљеност по кликтању + Последње OpenStreetMap ажурирање доступно: + Временски интервал бележења на који ће OsmAnd да пита за податке о тренутној позицији. + Прикажи почетни дијалог + Ако се искључи, снимање ће почети одмах после кликтања на справицу или на ставку менија, прескакајући дијалог потврде. + Прилагоди линију руте + Линија руте ће користити %1$s одабран на означеном стилу карте: %2$s. + Линија руте + Одаберите боју за режим карте: %1$s. + Немате ниједну куповину + Нови уређај / нови налог + Ако имате икаквих питања, контактирајте нас на %1$s. + Ако се Ваша куповина не појави овде, кликните на „%1$s” или контактирајте нашу подршку. + Контактирајте подршку + Решавање проблема + Пратите ову везу ако имате икаквих проблема са куповином. + OsmAnd уживо + Обнови чланарину + Годишња чланарина + Месечна чланарина + Тромесечна чланарина + Следећи датум наплате: %1$s + Отказано + У грејс периоду + На чекању + Истекло + Ажурирај све карте додате на %1$s\? + Број излаза + Објави када се премаши + Кориснички поени + Излаз \ No newline at end of file From 092e129f358fbde9354110249e5f4397f772c3a3 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Fri, 16 Apr 2021 22:16:18 +0000 Subject: [PATCH 31/86] Translated using Weblate (Ukrainian) Currently translated at 100.0% (3927 of 3927 strings) --- OsmAnd/res/values-uk/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-uk/phrases.xml b/OsmAnd/res/values-uk/phrases.xml index fda012a2e9..4ddbb1778a 100644 --- a/OsmAnd/res/values-uk/phrases.xml +++ b/OsmAnd/res/values-uk/phrases.xml @@ -3927,4 +3927,5 @@ Дипломатичне відомство Тип затоки Плато + Суспільний клуб \ No newline at end of file From ae0fa4edc9cfc8e94e57bba95fa7fe72fb6fa5c4 Mon Sep 17 00:00:00 2001 From: Ajeje Brazorf Date: Sat, 17 Apr 2021 19:20:19 +0000 Subject: [PATCH 32/86] Translated using Weblate (Sardinian) Currently translated at 99.4% (3907 of 3927 strings) --- OsmAnd/res/values-sc/phrases.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OsmAnd/res/values-sc/phrases.xml b/OsmAnd/res/values-sc/phrases.xml index 7d9420b4f3..c3891f8100 100644 --- a/OsmAnd/res/values-sc/phrases.xml +++ b/OsmAnd/res/values-sc/phrases.xml @@ -3926,4 +3926,6 @@ Canisteddos Pratzola de acampamentu Casta de baia + Tzìrculu sotziale + Artipranu \ No newline at end of file From 2995a6bc9bb41973badd36627558ac4a9dab04f4 Mon Sep 17 00:00:00 2001 From: Franco Date: Fri, 16 Apr 2021 18:46:13 +0000 Subject: [PATCH 33/86] Translated using Weblate (Spanish (Argentina)) Currently translated at 100.0% (3927 of 3927 strings) --- OsmAnd/res/values-es-rAR/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-es-rAR/phrases.xml b/OsmAnd/res/values-es-rAR/phrases.xml index 900c89112c..bcbc9d8b88 100644 --- a/OsmAnd/res/values-es-rAR/phrases.xml +++ b/OsmAnd/res/values-es-rAR/phrases.xml @@ -3927,4 +3927,5 @@ Oficina diplomática Tipo de bahía Meseta (altiplano) + Club social \ No newline at end of file From e131eeaee5652d719df36b4696bc1757253fe4e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Morais?= Date: Sat, 17 Apr 2021 10:57:19 +0000 Subject: [PATCH 34/86] Translated using Weblate (Portuguese) Currently translated at 100.0% (3927 of 3927 strings) --- OsmAnd/res/values-pt/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-pt/phrases.xml b/OsmAnd/res/values-pt/phrases.xml index ce8d36d89f..294787f8f3 100644 --- a/OsmAnd/res/values-pt/phrases.xml +++ b/OsmAnd/res/values-pt/phrases.xml @@ -3927,4 +3927,5 @@ Gabinete diplomático Tipo de baía Planalto + Clube social \ No newline at end of file From ec33e20fe427072065471e82638208472d534d2d Mon Sep 17 00:00:00 2001 From: Verdulo Date: Fri, 16 Apr 2021 19:57:37 +0000 Subject: [PATCH 35/86] Translated using Weblate (Esperanto) Currently translated at 100.0% (3927 of 3927 strings) --- OsmAnd/res/values-eo/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-eo/phrases.xml b/OsmAnd/res/values-eo/phrases.xml index 078dad2d9a..d0982ff069 100644 --- a/OsmAnd/res/values-eo/phrases.xml +++ b/OsmAnd/res/values-eo/phrases.xml @@ -3927,4 +3927,5 @@ Tipo de golfo Diplomatia oficejo Altebenaĵo + Socia klubejo \ No newline at end of file From 1ed12a42a5d0c12d43b8833324d0e9a5a921e28d Mon Sep 17 00:00:00 2001 From: phlostically Date: Sat, 17 Apr 2021 01:12:12 +0000 Subject: [PATCH 36/86] Translated using Weblate (Esperanto) Currently translated at 52.7% (143 of 271 strings) Translation: OsmAnd/Telegram Translate-URL: https://hosted.weblate.org/projects/osmand/telegram/eo/ --- OsmAnd-telegram/res/values-eo/strings.xml | 24 +++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/OsmAnd-telegram/res/values-eo/strings.xml b/OsmAnd-telegram/res/values-eo/strings.xml index b902b91d73..980f304973 100644 --- a/OsmAnd-telegram/res/values-eo/strings.xml +++ b/OsmAnd-telegram/res/values-eo/strings.xml @@ -119,4 +119,28 @@ Konektante al Interreto Komencante Sendante lokon + Pasvorto de Telegram + Tajpu pasvorton + Montri sur mapo + %1$d h + %1$d min + %1$d h %2$d min + Sendita + Vi ne estas salutinta + Fermante + Adiaŭante + Lanĉante + Adiaŭi + Saluti + Fona reĝimo + Malaktiva + Ĉiuj + Fermi + Eliri + Konservi + Malaktivigi + Apliki + Ŝanĝi unuojn por reprezenti distancoj. + Bufro logcat + Legi kaj kunhavigi detalajn protokolojn de la aplikaĵo \ No newline at end of file From 95e1e8e7b65770997b1a8880aa6ed09626838bd2 Mon Sep 17 00:00:00 2001 From: Branko Kokanovic Date: Sat, 17 Apr 2021 22:17:32 +0000 Subject: [PATCH 37/86] Translated using Weblate (Serbian) Currently translated at 100.0% (3717 of 3717 strings) --- OsmAnd/res/values-sr/strings.xml | 91 ++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 35 deletions(-) diff --git a/OsmAnd/res/values-sr/strings.xml b/OsmAnd/res/values-sr/strings.xml index 735f5382be..e5e9e86e8d 100644 --- a/OsmAnd/res/values-sr/strings.xml +++ b/OsmAnd/res/values-sr/strings.xml @@ -662,7 +662,7 @@ ОСМ измене дељене преко OsmAnd-а OSM тачка од интереса је направљена nmi - kn + чв min/m min/km m/s @@ -922,7 +922,7 @@ Образац боја путева Омогућите додатак за „снимање путовања“ ради коришћења услуга бележења (GPX бележење, праћење положаја на мрежи) Рачунај могућу приближну путању за велике раздаљине - Омогућите GPS у поставкама + Омогућите GPS у подешавањима Услуге бележења путање Нема пута Стигли сте на пролазно одредиште @@ -1064,7 +1064,7 @@ Указуј на правац циљне тачке трешњом. Омогући навођење измена ОСМ-а уживо. Навођење уживо ОСМ-а - Одредиште није подешено + Додатак за приступачност: Одредиште није подешено Изаберите категорију Пребачено на интерну меморију пошто је означено складиште за податке заштићено од писања. Изаберите фасциклу за складиште у коју може да се пише. Наведите интервал логовања снимања путање приликом навођења @@ -1221,7 +1221,7 @@ Коњске стазе положај: Бретонски - Преузмите слој са сенчењем да бисте видели рељеф на карти. + Преузмите карту слоја са сенчењем да бисте видели рељеф на карти. Инсталирајте додатак \"Изохипсе\" да прикажете нагиб вертикалних области. Додатак Приказ нивоа увеличања: %1$s @@ -1234,11 +1234,11 @@ Додај слике Нема слика овде. Поделите Ваш поглед са улице преко Мапилара. - Справица Мапилара - Омогућава брзи допринос Мапилару. - Отвори Мапилар + Mapillary справица + Омогућава брзи допринос Mapillary-ју. + Отвори Mapillary Мрежне слике улица за све. Откријте места, сарађујте, освојите свет. - Мапилари + Mapillary Мрежне слике улица за све. Откријте места, сарађујте, освојите свет. Ваше одредиште се налази на приватном поседу. Дозволити коришћење приватних путева на овом путовању\? Препокрени претрагу @@ -1512,8 +1512,8 @@ Не могу да увезем фајл. Проверите да ли OsmAnd има дозволе за читање фајла. Растојање је исправљено Слика са Мапилара - Побољшајте покривеност слика користећи Мапилар - Инсталирајте програм Мапилар (Mapillary) да додате слике на ову локацију на карти. + Побољшајте покривеност слика користећи Mapillary + Инсталирајте програм Mapillary да додате слике на ову локацију на карти. Претплатите се на нашу дописну листу за попуст и добијте још 3 преузимања карти! Изобате мора (изохипсе дубине) и карте поморских ознака. Хвала Вам на куповини „Поморских изобата“ @@ -1861,7 +1861,7 @@ Помери дугме Дуго држање и превлачење дугмета га помера по екрану. Име радње - Слике са Мапилара је могуће видети само ако сте повезани на интернет. + Слике са Mapillary-ја је могуће видети само ако сте повезани на интернет. Покушај поново Додај омиљене Увезите Омиљене тачке или их додајте означавајући их као ознаке на карти. @@ -2134,7 +2134,7 @@ Морате имати интернет да бисте инсталирали овај додатак. Преузми Пмаетно прерачунавање пута - Прерачунава само почетни део руте. Може се користити за дуга путовања. + Прерачунава само почетни део руте, корисно за дуга путовања. Да ли Вам се OsmAnd свиђа? Важно нам је да чујемо Ваше мишљење. Оцените ову апликацију @@ -3330,7 +3330,7 @@ Корисничко име и лозинка Ова подешавања додатака су глобална и примењују се на све профиле OSM уређивање - Све своје још увек не отпремљене измене или ОСМ белешке можете погледати у %1$s. Отпремљене тачке се не приказују у ОсмАнду. + Погледајте све Ваше још увек неотпремљене измене или ОСМ белешке у %1$s. Отпремљене тачке се не више приказују. OSM Иконица која се приказује за време навођења или померања. Иконица која се приказује у мировању. @@ -3648,11 +3648,11 @@ \nОнемогућите некоришћене додатке да сакријете све њихове контроле. %1$s. Елементи Фиоке, Контекст мени Акције Контекст менија - "Наплатом ће бити оптерећен ваш Гугл Плеј налог при потврди куповине. + Наплатом ће бити оптерећен ваш Гугл Плеј налог при потврди куповине. \n -\n Чланарина се аутоматски обнавља уколико није отказана пре датума обнове. Ваш налог биће задужен периодом обнове (месец / три месеца / годину дана) само на дан обнове. +\nЧланарина се аутоматски обнавља уколико није отказана пре датума обнове. Ваш налог биће задужен периодом обнове (месец / три месеца / годину дана) само на дан обнове. \n -\n Чланаринама можете управљати и отказати их тако што ћете отићи на ваша Гугл Плеј подешавања." +\nЧланаринама можете управљати и отказати их тако што ћете отићи на ваша Гугл Плеј подешавања. • Нове офлајн мапе нагиба \n \n • Пуно прилагођавање Фаворита и ГПКС тачака – прилагођавање боја, икона, облика @@ -3683,9 +3683,9 @@ Унесите дужину возила, нека ограничења пута могу бити примењена за дужа возила. Обриши најближу одредишну тачку Молимо одредите име тачке - Тренутна одредишна тачка биће уклоњена. Ако је она одредишна, стопираће се навигација. + Уклања тренутну одредишну тачку. Ако је она и завршна тачка, навођење ће се зауставити. Преузмите мапе Википедије - Информације о тачкама од интереса потражите на Википедији. То је ваш џепни ванмрежни водич - само укључите додатак Википедија и уживајте у чланцима о објектима око вас. + Информације о тачкама од интереса потражите на Википедији, џепном ванмрежном водичу који има чланке о местима и одредиштима. Ендуро скутер Скутер Инвалидска колица @@ -3723,7 +3723,7 @@ Навигацијски профил Изаберите датотеку записа којој ће се додати нови сегмент. Слике на нивоу улице - Да ли сте сигурни да желите да одбаците све промене на планираној рути затварањем\? + Да ли сте сигурни да желите да одбаците све промене на планираној рути\? У случају обрнутог правца Сачувај као нову датотеку стазе Додај у датотеку стазе @@ -3761,9 +3761,9 @@ Паузирај снимање пута Додајте бар две тачке. Одјављен - „Приватно“ значи да се траг не појављује ни на једној јавној листи, али су тачке праћења у њему у нехронолошком редоследу доступне путем јавног ГПС АПИ-ја без временских ознака. - „Могуће је идентификовати“ значи да ће се траг јавно приказати у вашим ГПС траговима и у јавним списковима ГПС трагова, тј. други корисници ће моћи да преузму необрађени траг и повежу га са вашим корисничким именом. Јавни подаци о временским тачкама трага из ГПС АПИ-ја који се сервирају путем АПИ-ја за тачке праћења имаће референцу на вашу оригиналну страницу праћења. - „Следљиво“ значи да се траг не приказује нигде на јавним листама, али обрађене тачке праћења са временским ознакама у њима (које не могу бити директно повезане са вама) иду кроз преузимања са јавног ГПС АПИ-ја. + „Приватно“ значи да се траг не појављује ни на једној јавној листи, али су тачке праћења у њему у нехронолошком редоследу доступне путем јавног GPS API-ја без временских ознака. + „Могуће је идентификовати“ значи да ће се траг јавно приказати у Вашим GPS траговима и у јавним списковима GPS трагова, тј. други корисници ће моћи да преузму необрађени траг и повежу га са Вашим корисничким именом. Јавни подаци о временским тачкама трага из GPS API-ја који се сервирају путем API-ја за тачке праћења имаће референцу на вашу оригиналну страницу праћења. + „Следљиво“ значи да се траг не приказује нигде на јавним листама, али обрађене тачке праћења са временским ознакама у њима (које не могу бити директно повезане са вама) иду кроз преузимања са јавног GPS API-ја. Затвори ОСМ белешку Коментар ОСМ напомене Можете се пријавити користећи безбедан ОАут метод или користити своје корисничко име и лозинку. @@ -3776,12 +3776,12 @@ Претрага Кајак Моторни чамац - Додај у Мапилари - Додај у ОпенПлејсРевјуз - ОсмАнд приказује фотографије из неколико извора: -\nОпенПлејсРевјуз - ПОИ фотографије; -\nМапилари - слике на нивоу улице; -\nВеб / Викимедиа - ПОИ фотографије наведене у подацима ОпенСтритМап. + Додај у Mapillary + Додај у OpenPlaceReviews + OsmAnd приказује фотографије из неколико извора: +\nOpenPlaceReviews - фотографије тачака од интереса; +\nMapillary - слике на нивоу улице; +\nВеб / Викимедија - фотографије тачака од интереса наведене у OpenStreetMap подацима. Ресурси Приближна величина датотеке Изаберите податке за извоз у датотеку. @@ -3795,7 +3795,7 @@ ОсмАнд користи МГРС, који је сличан УТМ НАТО формату. Поједностављена стаза Само линија руте ће бити сачувана, а путне тачке ће бити избрисане. - Паузираће евидентирање стазе када се апликација убије (преко скорашњих програма). (Индикатор рада OsmAnd-а у позадини тада нестаје из обавештајне траке.) + Евидентирање стазе ће бити паузирано када се апликација убије (преко скорашњих програма). (Индикатор рада OsmAnd-а у позадини тада нестаје из обавештајне траке.) Наведите интервал евидентирања за опште снимање стаза (укључено помоћу виџета „Снимање путовања“ на мапи). Наставите снимање путовања Системско подразумевана @@ -3847,9 +3847,9 @@ Датотека је већ увезена у ОсмАнд Користите двофазни алгоритам усмеравања А* Графикон - %1$s подаци доступни само на путевима, морате израчунати руту користећи „Рута између тачака“ да бисте је добили. - Сачекајте поновно израчунавање руте. -\nГрафикон ће бити доступан након поновног израчунавања. + %1$s подаци доступни само на путевима, израчунајте руту користећи „Рута између тачака“ да бисте видели графике. + Молимо сачекајте. +\nГрафикон ће бити доступан након поновног израчунавања путање. Локалне мапе %1$s — %2$s Размак @@ -3879,7 +3879,7 @@ Историја маркера Пошаљите ГПКС датотеку на ОпенСтритМап Унесите ознаке одвојене зарезом. - „Јавно“ значи да је траг јавно приказан у вашим ГПС траговима и на јавним ГПС траговима, као и на јавном списку трагова са временским ознакама у сировом облику. Подаци који се приказују путем АПИ-ја не упућују на вашу страницу трагова. Временске ознаке праћења нису доступне путем јавног ГПС АПИ-ја и тачке праћења нису хронолошки поређане. + „Јавно“ значи да је траг јавно приказан у вашим GPS траговима и на јавним GPS траговима, као и на јавном списку трагова са временским ознакама у сировом облику. Подаци који се приказују путем API-ја не упућују на вашу страницу трагова. Временске ознаке праћења нису доступне путем јавног GPS API-ја и тачке праћења нису хронолошки поређане. Није могуће отпремити слику, покушајте поново касније Изаберите слику Изаберите групе које ће бити увезене. @@ -3895,11 +3895,11 @@ Додајте нови сегмент • Додата је опција за извоз и увоз свих података, укључујући подешавања, ресурсе, моја места \n -\n • Планирање руте: графикони за сегменте са рутом, додата је могућност креирања и уређивања вишеструких сегмената стаза +\n • Планирање руте: графикони за сегменте са рутом, и додата је могућност креирања и уређивања вишеструких сегмената стаза \n \n • Додан је метод аутентификације ОАут за ОпенСтритМап, побољшан кориснички интерфејс ОСМ дијалога \n -\n • Прилагођене боје за омиљене и путне тачаке стаза +\n • Подршка за прилагођене боје за омиљене и пролазне тачке путања \n \n Вода @@ -4061,4 +4061,25 @@ Објави када се премаши Кориснички поени Излаз + OpenPlaceReviews је пројакат покретан од стране заједнице о јавним местима, као што су ресторани, хотели, пролазне тачке. Пројекат сакупља јавне информације о њима, као што су слике, рецензије, везе ка другим местима као OpenStreetMap или Википедија. +\n +\nСви подаци на OpenPlaceReview су отворени и доступни свима: http://openplacereviews.org/data. +\n +\nМожете прочитати више на: http://openplacereviews.org + • Ажурирања OsmAnd уживо су премештена у „Преузимања > Ажурирања” +\n +\n • Путање се сада могу обојити по висини, брзини или нагибу. +\n +\n • Додата опција да се измени изглед линије која се приказује приликом навођења +\n +\n • Ажуриран дијалог за „Снимање пута” +\n +\n + Копирај адресу + Времена гласовних навођења + OsmAnd профил + Кориснички профил + Обрни све тачке + Одаберите профил које ће се користити по покретању апликације. + Последње коришћено \ No newline at end of file From 5c08b1d1b3d3d1959abbd48e29e25c8e724ac0d6 Mon Sep 17 00:00:00 2001 From: Branko Kokanovic Date: Sat, 17 Apr 2021 22:33:28 +0000 Subject: [PATCH 38/86] Translated using Weblate (Serbian) Currently translated at 97.6% (3835 of 3927 strings) --- OsmAnd/res/values-sr/phrases.xml | 41 ++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/OsmAnd/res/values-sr/phrases.xml b/OsmAnd/res/values-sr/phrases.xml index fae32d16dc..247a5f4aea 100644 --- a/OsmAnd/res/values-sr/phrases.xml +++ b/OsmAnd/res/values-sr/phrases.xml @@ -3867,4 +3867,45 @@ Река Цепљење: Ковид 19 Цепљење + Друштвени клуб + Плато + Дипломатско представништво + Кик-бокс + Мачевање + Курлинг + Кросфит + Туча петлова + Борба са биковима + Боб + Биатлон + Аикидо + Водено скијање + Ватерполо + Рвање + Дизање тегова + Вејкбординг + Ултимат + Таеквондо + Футсал + Сумо + Снукер + Стање стазе: отворена + Обилажена: не + Обилажена: да + Име стазе + Ски скок + Тунел за слепе мишеве + Мост за слепе мишеве + Прелаз за дивљач + Део за пливање + Појило + Станица за пренос отпада + Колска вага + Шумарска станица + Цевни бунар + Бунар + Електрична пумпа + Танк са водом + Чесма + Тип залива \ No newline at end of file From 848744477904b4deb6b90eb6ce1091883bef4edc Mon Sep 17 00:00:00 2001 From: Branko Kokanovic Date: Sat, 17 Apr 2021 22:27:30 +0000 Subject: [PATCH 39/86] Translated using Weblate (Serbian (latin)) Currently translated at 100.0% (3717 of 3717 strings) --- OsmAnd/res/values-b+sr+Latn/strings.xml | 4221 +++++++++++++++++++++-- 1 file changed, 3980 insertions(+), 241 deletions(-) diff --git a/OsmAnd/res/values-b+sr+Latn/strings.xml b/OsmAnd/res/values-b+sr+Latn/strings.xml index 209c3f8e1a..174e5b2515 100644 --- a/OsmAnd/res/values-b+sr+Latn/strings.xml +++ b/OsmAnd/res/values-b+sr+Latn/strings.xml @@ -1,193 +1,195 @@ - + + Verzija: Verzija info, licence, članovi projekta - Izaberite da li da se čuje zvuk prilikom snimanja fotografija -Fokus je podešen na beskonačno - Makro režim fokusa - Auto fokus - Obriši sve - Prvo izračunajte putanju + Postavite da li se čuje zvuk prilikom škljocanja ili ne. + Fokus je podešen na beskonačno + Makro režim fokusa + Autofokus + Obriši sve + Najpre izračunajte putanju Simulacija pomoću izračunate putanje Simulacija pomoću GPX putanje - Zapamti moj izbor + Zapamti izbor i ili - Bez auto-zuma - Osnovna karta sveta - Ističe(minuta): %1$s + Bez auto-zuma + Osnovna karta sveta + Ističe za (minuta): %1$s Maksimalno uveličanje: %1$s - Ekliptični prikaz zemljine kugle + Eliptična Merkator projekcija Maksimalno uveličanje Minimalno uveličanje URL Izaberite postojeći… Podaci o greškama brzine prikaza sličica - Japan - Sjedinjene Američke Države - Kanada - Evropa i Azija - Velika Britanija, Indija, Australija & Ostalo - Minimalni zum: %1$s + Japan + Sjedinjene Američke Države + Kanada + Evropa, Azija, Južna Amerika i slične + Velika Britanija, Indija i slične + Minimalni zum: %1$s Najavi… - Podešavanje najave naziva ulica, saobraćajnih upozorenja, ograničenja brzine - Obaveštenje - naziv ulice - Obaveštenje - ograničenje brzine - Obaveštenje - kamera - Obaveštenje - upozorenja u saobraćaju - Definišite OSM korisnika i lozinku u podešavanjima - Obriši međutačke - Sačuvaj međutačke - Već imate definisane međutačke. - Uputstvo do - Uputstvo od + Podesite najave naziva ulica, saobraćajnih upozorenja (ležeći, znaci zaustavljanja), radara i ograničenja brzine. + Nazivi ulica (TTS) + Ograničenje brzine + Kamere + Upozorenja u saobraćaju + Definišite OSM korisničko ime i lozinku u „Podešavanjima“ + Obriši prolazna odredišta + Sačuvaj prolazna odredišta + Već imate podešena usputna odredišta. + Navođenje do + Navođenje od Karta : Šir %1$.3f, duž %2$.3f Do: Preko: Iz: - Pregledaj mapu - Primarni profil - Destinacija %1$s + Pregled karte + Primarni profil + Destinacija %1$s Postavi kao destinaciju - Izaberite prvo grad ili ulicu + Postavite prvo grad ili ulicu Obriši tačku vreme - preciznost + tačnost brzina visina Tačka - naziv GPX fajla - GPX datoteka uspešno sačuvana na {0} - Kalkulator rastojanja i Alat za planiranje + Naziv GPX fajla + GPX fajl sačuvan u {0} + Kalkulator rastojanja i alat za planiranje Ne prikazuj ponovo Počnite uređivanje Završi uređivanje Obrišite sve tačke - Otvori postojeći GPX + Otvori postojeći GPX fajl Molimo sačekajte dok se ne završi trenutni zadatak Koristite Kalman filter Koristi magnetni senzor Ostalo %1$d fajlova ostalo - Dostupno %1$d datoteka za preuzimanje + %1$d fajlova preostalo da se preuzme Puna verzija - Konfigurisanje audio i video podešavanja + Podesite audio i video. Audio/video podešavanja - Došlo je do greške prilikom snimanja - Kamera nije dostupna - Da li želite da uklonite ovaj snimak ? + Snimanje nije uspelo + Kamera nedostupna + Izbrisati ovu stavku\? nedostupan Napravi audio belešku Napravi video belešku Sloj za snimanje Obriši zapis Reprodukuj - snimanje + Snimanje Počni Audio/video beleške Merenje udaljenosti Audio beleške - Ostale mape + Ostale karte Samo putevi - izohipse - Sakrij granice + Izohipse + Granice Ograničenje brzine Nema pronađenih objekata. Traži grad inkrementalno - Pretraga sela/Poštanski kod + Pretraga za još sela/poštanskih kodova Nativna biblioteka nije podržana na ovom uređaju . - Pokretanje nativne biblioteke… - Autocentriranje mape + Pokretanje nativne biblioteke… + Autocentriranje karte Podešavanje vektorske mapa - Predgrađe - Selo - Grad - Zaustavi simulaciju - Započni simulaciju - Fajl ne može biti preimenovan . - Fajl sa tim imenom već postoji. - Pretraga po imenu - Mesna datoteka za održavanje promene POI-a nije pronađena i ne može da se napravi . - Nadogradite Osmand+ - Server sadrži mape datoteke koje nisu u skladu sa trenutnim izdanjem aplikacije. Da biste ih preuzeli i koristili, molimo Vas da, nadogradite aplikaciju na novije izdanje. - Preimenovanje + Predgrađe + Selo + Grad + Zaustavi simulaciju + Započni simulaciju + Ne mogu da preimenujem fajl. + Fajl sa tim imenom već postoji. + Pretraga po imenu + Lokalni fajl koji održava promene tačaka od interesa nije pronađen i ne može da se napravi. + Nadogradite OsmAnd+ + Preuzmite novu verziju ove aplikacije da biste mogli da koristite nove fajlove karata. + Preimenuj Online Nominatim - Adresa … - Neodređeno + Adresa… + Neodređeno Konfiguracija ekrana - Prikaži trake - Izbegavaj neasfaltirane puteve - Izbegavaj trajekte - Fluorescentne rute - Omogući\n režim spavanja - Naziv ulice - Podešavanje ekrana - Gde sam - Zaključaj ekran - Kompas + Trake + Bez neasfaltiranih puteva + Bez trajekata + Fluorescentne rute + Pokreći +\n aplikaciju u pozadini + Naziv ulice + Podešavanje ekrana + Gde sam + Zaključaj + Kompas Vrati na podrazumevano Brzina - Destinacija - Visina - Sledeće skretanje - Sledeće skretanje (malo) - Drugo sledeće skretanje - Zaključaj ekran - Otključaj ekran - Ekran je zaključan - ukucajte za filtriranje - Ekran visoke rezolucije - glas - Vektorske mape nisu učitane - GPX datoteke se ne nalaze u direktorijumu - Greška pri čitanju GPX podataka + Odredište + Visina + Sledeće skretanje + Sledeće skretanje (malo) + Drugo sledeće skretanje + Zaključaj + Otključaj + Ekran je zaključan + ukucajte za filtriranje + Ekran visoke rezolucije + Snimljeni glas + Vektorske karte nisu učitane + Nema GPX fajlova u fascikli staza + GPX podaci ne mogu da se pročitaju. Izmena POI - Brisanje POI - Bez rotacije (sever je gore) - Orjentacija mape + Brisanje POI + Bez rotacije (sever je uvek gore) + Orijentacija karte Lenjir Uvezi - Došlo je do greške prilikom učitavanja GPX - Pošalji izveštaj - Nema podataka za datu oblast na SD kartici. Preuzmite podatke sa Interneta. - Bilo koji - Ruta - Auto-centriranje karte samo tokom navigacije. - Auto-centriranje karte tokom upotrebe. - Izlazak sunca: %1$s \nZalazak sunca: %2$s - Dan/noć informacije - Stil mape + GPX ne može da se učita. + Pošalji izveštaj + Skinute karte na memorijskoj kartici nisu nađene. + Bilo koji + Ruta + Auto-centriranje karte samo tokom navigacije. + Auto-centriranje karte tokom upotrebe. + Izlazak sunca: %1$s \nZalazak sunca: %2$s + Dan/noć informacije + Stil karte Izbegavati… - Došlo je do greške tokom renderovanja izabrane oblasti - Koristi lokaciju… - Render uspešno učitan - Greška: render nije bio učitan - Vektorski renderer - Odaberite poravnavanje ekrana mape - Detalji rute - Uneti upit za pretragu POI - Dnevnik grešaka OSM-a na mreži - POI… - Pretraga POI - Gde sam ja? - OsmAnd navigacioni servis - Mreža - GPS - sekunde - min. + Ne mogu da iscrtam izabranu oblast. + Koristi lokaciju… + Render učitan + Renderer ne može da se učita. + Vektorski renderer + Poravnavanje karte: + Detalji rute + Kucaj da pretražuješ tačke od interesa + OSM beleške (na mreži) + Sloj sa tačkama od interesa… + Pretraga tačaka od interesa + Gde sam ja? + OsmAnd navigacioni servis + Mreža + GPS + sekunde + min. nijedan Da li ste sigurni da želite da prekinete navigaciju? Prikaži - Fotografija %1$s od %2$s + Fotografija %1$s %2$s Slikaj Slikaj - Dropbox dodatak omogućava da sinhronizujete putanje i audio/video beleške sa vašim Dropbox nalogom. + Sinhronizujete putanje i audio/video beleške sa vašim Dropbox nalogom. Dropbox dodatak Promeni redosled Podrazumevano Pešački prelaz - Prikazuj pešačke prelaze - Da li želite da preuzmete karte za korišćenje van mreže? + Pešački prelazi + Preuzeti karte za korišćenje van mreže\? Preuzeli ste %1$s karata Preuzmi novu kartu Upravljajte @@ -195,160 +197,3897 @@ Oblast Sva preuzimanja Nadogradnje - Mesno - Preuzimanje je nemoguće, proverite vezu sa Internetom. + Lokalno + Preuzimanje nemoguće, proverite vezu sa Internetom. Otkaži - Sve datoteke su sveže + Svi fajlovi su sveži Koristi OpenGL za prikaz - Koristi hardversko ubrzanje OpenGL-a za prikaz (možda ne radi na nekim uređajima) + Koristi hardversko ubrzanje OpenGL-a za prikaz (može da koristi više baterije, a može i da ne radi na nekim uređajima). Dostupne su nadogradnje %1$s karata - Potraži - PRIKAŽI SVE - Koordinate - Nikad + Pretraga + Prikaži sve + Koordinate + Nikad Sakrij - Pojedinosti + Detalji Pretraga položaja… - Položaj (pronađen) - Omiljeno… - Središte trenutne mape - Izvor: - Traži obližnje - "Ime datoteke: " - Datoteka sa istim imenom već postoji. - Sačuvaj - Otpremite GPIks datoteke zajednici OSM. Upotrebiće se za poboljšanje mapa. - %1$d od %2$d stavki je uspešno otpremljeno. - Pošalji OSM-u - Prikaži više pojedinosti mape - Omiljena tačka(e) je uspešno izbrisana. - Prijatelji - Mesta + Moj položaj (pronađen) + Omiljeno… + Centar trenutne karte + Izvor: + Traži u blizini + Ime fajla: + Fajl sa istim imenom već postoji. + Sačuvaj + Otpremite GPX fajlove OSM zajednici, i tako poboljšajte karte. + %1$d od %2$d stavki otpremljeno. + Pošalji OSM-u + Prikaži više detalja na karti + Omiljena tačka(e) izbrisana. + Prijatelji + Mesta Ime - Vrsta - Osnovna mapa sveta (koja pokriva ceo svet na malom uveličanju) nedostaje. Molim, preuzmite World_basemap_x.obf radi celokupnosti sredine. - Izdanje - Nema stavki za %1$s - Upravljajte datotekama mapa - Pokreni - Isključi + Kategorija + Preuzmite osnovnu mapa sveta da dobijete pregled celog sveta na malom uveličanju. + Lokalno izdanje + Nema stavki za %1$s + Upravljajte fajlovima karata. + Pokreni + Isključi Preuzmi - Podaci POI-a - Podaci adresa - Podaci javnog prevoza - Podaci mape - Isključeno - Glasovni podaci (TTS) - Glasovni podaci (snimci) + Podaci POI-a + Podaci adresa + Podaci javnog prevoza + Podaci karte + Isključeno + Glasovna navođenja (TTS) + Glasovna navođenja (snimljena) Podaci POI-a - Glas TTS-a - Nova pretraga - Izaberite veličinu pisma za imena na mapama - Veličina pisma na mapi - Podaci o otklonu grešaka prikaza - Prikazuj učinak prikaza + Glas TTS-a + Nova pretraga + Veličinu teksta za imena na kartama: + Veličina teksta na karti + Podaci o otklonu grešaka prikaza + Prikazuj učinak prikaza. Raspakujem nove podatke… - Izabrana je usluga navođenja preko mrežne veze, ali je mrežna veza nedostupna. - Jezik nije podržan - Nedostaju podaci - Nema ugrađenih podataka izabranog jezika. Da li želite da odete do prodavnice radi ugradnje? - Suprotan pravac GPIks-a - Izlaz navođenja glasom - Zvuk govornog poziva - Zvučno obaveštenje + Navigacija preko interneta ne radi kad niste na internetu. + Jezik nije podržan + Nedostaju podaci + Otići do prodavnice da skinete izabrani jezik\? + Obrni smer staze + Izlaz glasovnog navođenja + Zvuk dolaznog poziva (prekida i blutut u kolima) + Zvučno obaveštenje %1$s zahteva ovlašćenja da isključi ekran radi uštede energije. Uključi ekran - Uključi ekran uređaja (ako je isključen) kada se približi skretanje - Ostala svojstva mape - Preostali činioci + Uključi ekran uređaja (ako je isključen) kada se približi skretanje. + Ostala svojstva karte + Ostali elementi Svojstva prikaza Traka stanja - Desna površ - Leva površ + Desna površ + Leva površ Prikaži - Podesite mapu - Bezimeni korisnik ne može\n- da pravi skupove;\n- da usklađuje skupove i uređaje sa serverom;\n- da upravlja skupovima i uređajima u ličnom uredu. + Podesi kartu + Anonimni korisnici ne mogu: +\n- da prave grupe; +\n- da sinhronizuju grupe i uređaje sa serverom; +\n- da upravljaju grupama i uređajima na svojoj komandnoj tabli na sajtu. Bezimeni korisnik Prijavljen kao %1$s - Dopušteno prekoračenje brzine - Granica dopuštenog prekoračenja brzine, iznad koje ćete dobiti glasovno upozorenje. - Ime omiljene tačke je izmenjeno u %1$s da bi se olakšalo pravilno čuvanje niske sa znakom osećanja u datoteku. - Ime omiljene tačke je udvostručeno - Izmenio sam ime Vaše omiljene tačke u %1$s radi izbegavanja istih imena za više tačaka. - Podesi veličinu pisma na mapi. - Veličina pisma + Dopušteno prekoračenje brzine + Odaberite granicu dopuštenog prekoračenja brzine, preko koje ćete dobiti glasovno upozorenje. + Omiljena tačka preimenovana u %1$s da bi se sačuvao tekst koji sadrži emodžije u fajl. + Dat duplikat imena omiljene tačke + Omiljeno ime je izmenjeno u %1$s da se izbegnu ista imena. + Podesi veličinu teksta na karti. + Veličina teksta Ograničenje brzine Znak stop - Najavljuj prešačke prelaze - Vrsta puta - Možete da izaberete zamensku putanju izborom puteva za izbegavanje - Da li započeti navođenje po stazi? - Narandžasta + Pešački prelazi + Tip puta + Izaberete alternativne putanje izborom puteva za izbegavanje + Da započnem navođenje duž putanje\? + Narandžasta Pružni prelaz - Prikazuj pružne prelaze - Osvetljenje ulice - Mrežni posrednik - Podesite mrežnog posrednika za internet + Pružni prelazi + Osvetljenje ulice + Mrežni proksi + Navedite proksi server. Privatnost Bez usmeravanja po izdanju v1.9 - Nemoj da koristiš pravila usmeravanja iz izdanja v1.9 + Nemoj da koristiš pravila usmeravanja iz izdanja v1.9. Stanice Geografska širina Geografska dužina Nisam pronašao zaobilaznice Preimenovanje nije uspelo. - Staza koja se trenutno snima - Nazad na kartu - Prelazak pregleda karte prikazuje pomorski izgled, čime se prikazuju sve pomorske oznake navođenja i grafički znaci. - \n\nDatoteka karte koja sadrži sve opšte pomorske znake je dostupna u vidu jednog preuzimanja i naziva se „Svetske pomorske karte“. - \n\nPregled može da se vrati na stari njegovim isključivanjem ovde, ili izmenom „Načina prikaza karte“ pod „Podesite kartu“. - - Pokretanje ove izmene menja pregled karte na „Zima i skijanje“, koje prikazuje u položenom pogledu osobine predela i zimske uslove. - \n\nSvetska karta prikazuje sva skijališta i skijaške staze u ovom pregledu je dostupna preuzimanjem „Svetske skijaške karte“. - - Zvuk + Staza koja se trenutno snima + Nazad na kartu + Ovaj dodatak obogaćuje OsmAnd aplikaciju da prikazuje i pomorske karte za vožnju brodova, jedrenje i druge tipove vodenih sportova. +\n +\nFajl karte koja sadrži sve opšte pomorske znake je dostupna u vidu jednog preuzimanja i naziva se „Svetske pomorske karte“. +\n +\nPregled može da se vrati na stari njegovim isključivanjem ovde, ili izmenom „Načina prikaza karte“ pod „Podesite kartu“. + Ovaj OsmAnd dodatak Vam pod prste stavlja detalje globalnih ski padina, kros kantri trka, alpskih ski ruta, žičara i ski liftova. Rute i piste su prikazane obojene po težini i nalaze se u specijalnom stilu karte zvanom \"Zima i skijanje\" koja vizuelno i podseća na zimske pejzaže. +\n +\nAktiviranjem ovog pregleda će se promeniti stil karte na „Zima i skijanje“ koja će prikazivati sve odlike oblasti u zimskih uslovima. Ovo može da se vrati ili tako što se deaktivira ovaj dodatak ovde, ili tako što se promeni stil karte na „Podesi kartu“ na novi, željeni stil. + Zvuk Deli belešku Geografski položaj:\nŠirina %1$s↵\nDužina %2$s - Pregledaj - Beležnica - Karta sa mreže - Samo putevi - "Slobodno %1$s " - Skladište uređaja + Pregledaj + A/V beleške + Karta sa mreže + Samo putevi + Slobodno %1$s + Skladište uređaja Pregled skijaške karte Pregled pomorske karte - Da bi se prikazivala skijaška karta, naročita karta za pregled van mreže treba da se preuzme - Da bi se prikazivale pomorske karte, naročita karta za pregled van mreže treba da se preuzme - Uredi skup + Preuzmite posebnu kartu van mreže da bi prikazali skijašku kartu. + Preuzmite posebnu kartu van mreže da bi prikazivali pomorske detalje. + Uredi skup UKLONI OZNAKU Stanje GPS-a - Preuzmi noćne izgradnje + Preuzmi noćne izgradnje. Izgradnje Tačke Moj položaj Kuća Linije metroa U blizini -Gustina linija - Gustoća linija - Velika - Srednje - Mala - Širina konturnih linija - Širina konturnih linija - Voda - Sakriti vodu - Omogućiti autoputeve - Navigacija - Konfiguracija mape - Prikazati %1$s - Sakriti %1$s - Molimo dodajte barem jednu tačku. - GPX ime datoteke: - Prikazati na karti nakon snimanja - Pretražite mapu i dodajte tačke - Mera za daljinu - Nastaviti/pauzirati navigaciju - Dodirnite ovo dugme da biste zaustavili ili nastavili navigaciju. - Prikazati navigacioni završni dijalog - \ No newline at end of file + Gustina linije izohipse + Gustina linije izohipse + Gusta + Srednja + Retka + Debljina linije izohipse + Debljina linije izohipsi + Vode + Sakrij vodu + Koristi autoputeve + Navođenje + Podesite kartu + Prikaži %1$s + Sakrij %1$s + Molim dodajte bar jednu tačku. + Ime GPX fajla: + Prikaži na karti posle čuvanja + Pregledaj kartu i dodaj tačke + Lenjir + Pauziraj/Nastavi navođenje + Dugme za pauziranje ili nastavak navođenja. + Prikaži dijalog da je navođenje završeno + Vremena glasovnih navođenja + ’Slobodna vožnja’ i ’Van staze’ su nezvanične rute i prolazi. Uglavnom su nesređene, neodržavane i ne proveravaju se uveče. Ulazite na sopstvenu odgovornost. + Van staze + Preferiraj staze ove težine, ali ostavi moguće navođenje preko težih ili lakših staza ako je kraće. + Željena težina + Staze pripremljene za klasični stil bez puteva za klizanje. Ovo uključuje i state pripremljene malim vozilima za sneg i putanjama koje su skijaši napravili. + Dozvoli samo klasične puteve + Putevi spremljeni za slobodni stil ili samo klizanje bez klasičnih putanja. + Dozvoli puteve samo za klizanje + Užasno teški putevi, sa opasnim preprekama i okolinom. + Dozvoli ekspertske puteve + Teži putevi, sa opasnim preprekama i strmim delovima. + Dozvoli napredne puteve + Teži putevi sa strmijim delovima. Generalno gledano, neke prepreke koje treba izbegavati. + Dozvoli usputne puteve + Padine za sankanje. + Sanke + Putanje ski tura. + Ski ture + Putevi za nordijsko ili kros-kantri skijanje. + Kros-kantri i nordijsko skijanje + Padine alpskog skijanja ili spusta i pristup ski liftovima. + Alpsko i spust skijanje + Dodajte bar jednu stavku na postavkama u „Brzim radnjama“ + Izbegavaj kaldrme + Bez kaldrma + Pokreni OsmAnd\? + Koristite kartu {0} koju je omogućio OsmAnd. Da li želite da pokrenete OsmAnd punu verziju\? + Nagib + Uglačanost + Podloga + Tip puta + Kliknite dugme i slušajte odgovarajuće glasovno navođenje da čujete da li je mu nešto nedostaje ili je pogrešno + Kako da otvorite članke sa Vikipedije\? + Članak nije nađen + Pretražujem odgovarajući viki članak + ova regija + Pogledaj članak u veb čitaču. + Otvori članak na internetu + Preuzmi podatke sa Vikipedije + Preuzmite Vikipedija članke za %1$s da biste mogli da ih čitate van mreže. + Tuneli + Tunel ispred + Položaj bez imena + Kopiraj poziciju/ime tačke + Pomorsko + Turistički pregled + Zima i skijanje + isklj. + Ukupno donacija + OSM primaoci + Pomeri tačku + Fajl %1$s ne sadrži prolazne tačke, da ga uvezem kao putanju\? + Uvezi GPX fajl + Uvezite Omiljene tačke ili ih dodajte označavajući ih kao oznake na karti. + Dodaj omiljene + Tačka puta 1 + Prolazna tačka 1 + Sačuvaj tačku puta + Sačuvaj GPX prolaznu tačku + Dodaj liniju + Dodaj prolaznu tačku + Dodaj tačke puta + Pokušaj ponovo + Slike sa Mapillary-ja je moguće videti samo ako ste povezani na internet. + Ime radnje + Dugo držanje i prevlačenje dugmeta ga pomera po ekranu. + Pomeri dugme + Izvor karte promenjen na „%s“. + Dodaj izvor karte + Dodaj izvor karte + Izmeni izvor karte + Dodaj sloj ispod karte + Slojevi ispod karte + Izmeni sloj ispod karte + Sloj ispod karte promenjen na „%s“. + Sloj iznad kartom promenjen na „%s“. + Dodaj sloj nad kartom + Slojevi iznad karte + Izmeni sloj nad kartom + Stilovi karte + Popunite sve parametre + Dodaj stil karte + Dugme za prolaženje kroz spisak ispod. + Dodajte jednu ili više kategorija tačaka od interesa koje će biti prikazivane na karti. + Spisak tačaka od interesa + Izaberite opcionu kategoriju. + Kategorija u koju da se čuvaju Omiljene: + Poruka + Ova poruke je uključena u polje sa komentarima. + Ostavite prazno da se koristi adresa ili ime mesta. + Napravi stavke + Dodaj kategoriju + Dugme za prikazivanje i sakrivanje tačke od interesa na karti. + Dugme za prikazivanje i sakrivanje Omiljenih tačaka na karti. + Već postoji brza radnja sa ovim imenom + Brza radnja preimenovana u %1$s da se izbegne dupliranje. + Mesto + " sačuvano u " + Prikaži dijalog međuvremena + Menjaj dnevni/noćni režim + Noćni režim + Dnevni režim + Dugmeta da menja dnevni i noćni režim u OsmAnd-u. + Dugme za dodavanje parking mesta na sredinu ekrana. + Dugme da uključi ili isključi glasovno navođenje. + Dugme za dodavanje tačke od interesa na sredinu ekrana. + Dugme za dodavanje OSM beleške na sredinu ekrana. + Dugme za dodavanje slikovne beleške na sredinu ekrana. + Dugme za dodavanje video beleške na sredinu ekrana. + Dugme za dodavanje zvučne beleške na sredinu ekrana. + Dugme za dodavanje GPX prolazne tačke na sredinu ekrana. + Dugme za dodavanje oznake na karti na lokaciji na centru ekrana. + Unapred postavljeno ime + Prikaži dijalog za Omiljene + Da li ste stvarno želite da obrišete radnju „%s“\? + Obriši radnju + Dodaj radnju + Dodaj Omiljeno + Uredi akciju + Dodaj akciju + Dodaj parking mesto + Isključi glas + Uključi glas + Glas uklj/isklj + Dodaj OSM belešku + Nova slikovna beleška + Nova video beleška + Nova zvučna beleška + Stil karte je izmenjen na „%s“. + Izmeni stil karte + Dodaj tačku od interesa + Dodaj oznaku na karti + Ekran %d + Radnja %d + Tip tačke od interesa + Dodaj vremena otvaranja + Kontakt informacije + Zatvara se u + Otvara se u + Sledeće + Broj zgrade + Napredno + Osnovno + Predaj tačku od interesa + Karte van mreže +\n& Navođenje + OsmAnd + Preskoči + Poništi + Kartica je sakrivena + Podešavanje komandne table + Greška: {0} + Pokušajte ponovo + Otpremljeno {0}/{1} + Obriši izmenu + Ne mogu da otpremim + Rcite nam Vaše predloge. + Recite nam zašto. + Molimo dajte OsmAnd-u ocenu na Google Play prodavnici + Ocenite ovu aplikaciju + Važno nam je da čujemo Vaše mišljenje. + Da li Vam se OsmAnd sviđa\? + Pmaetno preračunavanje puta + Preuzmi + Morate imati internet da biste instalirali ovaj dodatak. + Neispravni format: %s + Beličinu slike ugrađenog fotoaparata + Veličina slike fotoaparata + Pusti zvuk škljocanja + Fotoaparat neprestano pokušava da fokusira + Produžena dubina polja (EDOF) + Hiperfokalni fokus + Način fokusa fotoaparata: + Tip fokusa fotoaparata + e-pošta + Filter \'%1$s\' napravljen + Filter \'%1$s\' izbrisan + Obriši ovaj filter\? + Sačuvaj kao + Filter + Predaj + Sve druge oznake su očuvane + Tačka od interesa se menja + Ponovo otvori + Komentar + Otvoreno + Informacije o čvoru nisu učitane + I/O greška tokom izvođenja radnje {0}. + Greška prilikom izvođenja radnje {0}. + Radnja {0} izvršena. + obriši + izmeni + dodaj + Izbrisana + Obriši tačku od interesa + Obriši {0} (komentar)\? + Napravi tačku od interesa + Izmeni tačku od interesa + Omiljena tačka {0} izbrisana. + Izbriši Omiljenu tačku \'%s\'\? + Izbriši Omiljenu + Izmeni Omiljenu + Dodaj Omiljenu + Omiljena tačka „{0}“ dodata. + Omiljena + Unesi ime Omiljene tačke + Napravi tačku od interesa + Ažuriraj kartu + Raskrsnica ulica + Zgrada + Zgrada + Ulica + Grad + Region + Adresa + DDD MM SS.S + DDD MM.MMM + DDD.DDDDD + Geografska dužina + Geografska širina + Unesite širinu i dužinu u odabranom formatu (D - stepeni, M - minuti, S - sekunde) + Na dnu + Na sredini + Pešačenje + Bicikla + Vožnja + Najbliže usluge + Odaberite ulicu koja je seče + Odaberite oblast sa spiska + Priraštajno traži zgrade + Priraštajno traži ulice + Nađi još + Izaberite tačke od interesa + Traži + Traži + Aplikacija za navođenje + Skidajte sličice koje nedostaju + Prikaži GPS koordinate na karti + Prikaži Vašu poziciju + Koristi internet + Izvor karte + Izvor sličice karte + Izaberite izvor mrežnih ili zahvaćenih pločica karata. + Prikaži sloj sa tačkama od interesa iznad karte + Prikaži poslednje korišćeni sloj sa tačkama od interesa. + 3D prikaz karte + Uključi 3D prikaz karte. + Prikaži smer gledanja + Izaberite državu + Izaberite grad ili poštanski broj + Izaberite ulicu + Izaberite zgradu + Traži adresu + Postavke aplikacije + Koristi engleska imena na kartama + Izaberite između lokalnih ili engleskih imena. + Odredište + Ponovo učitaj sličicu + Ažuriraj kartu + Dodatne postavke + Podaci + Odaberi jezik, preuzmi/ponovo učitaj podatke. + Odredite OpenStreetMap.org (OSM) postavke potrebne za slanje na OSM. + Koristite mrežno navođenje + Koristite internet da izračunate put. + Gotovo + Čuvam GPX fajl… + OsmAnd se srušio na prošlom pokretanju. Zapisnik je u {0}. Molimo prijavite problem i zakačite ovaj fajl. + Čitanje lokalnih podataka… + Učitavanje podataka… + Vrati se na kartu + Neispravne koordinate + Stigli ste. + Put: udaljenost %1$s, vreme puta %2$s +\nRačunica: %3$.1f sek %4$d puta, %5$d sličica) + Izračunati put je prazan. + Izračunavanja puta nije uspelo. + Izračunavanja puta nije uspelo. + GPX fajl ne može da se sačuva. + Tačke od interesa + Učitavanje gradova… + Učitavanje ulica… + Učitavanje poštanskih brojeva… + Učitavanje ulica/zgrada… + Pretvaranje lokalnih/engleskih imena… + nije uspela + Provera ovlašćenja nije uspela + Učitavam tačke od interesa… + Predajem čvor… + Zatvaram skup promena… + Otvaram skup promena… + Radno vreme + Navođenje + Prvo odaberite odredište + Pokreni navođenje + Prikaži put + Zameni + Nema Omiljenih tačaka + Izmenjena omiljena tačka + Skidanje spiska regiona sa sajta https://osmand.net nije uspelo. + Preuzimam spisak dostupnih regiona… + Adresa + Lokalni podaci za {0} već postoje ({1}). Ažurirati ({2})\? + Preuzmi {0} - {1}\? + Fascikla sa skladištem na memorijskoj kartici nije pristupačna! + Usluga navođenja + Mrežna ili vanmrežna usluga navođenja. + Ukupna udaljenost %1$s, vreme putovanja %2$d h %3$d min. + Ne gledaj karea sa mreže na uvećanju preko ovoga. + Maksimalni zum na mreži + Mrežna pretraga + Vanmrežna pretraga + Mrežna pretraga: kućni broj, ulica, grad + Mrežna pretraga pomoću Imenika OSM-a + Tražim adresu… + Pretražujem… + Ništa nije pronađeno + Otpremam… + Otpremam podatke… + Omiljeno + Zgrada: {0}, {1}, {2} + Raskrsnica: {0} x {1} u {2} + Ulica: {0}, {1} + Grad: {0} + Ažuriraj lokalne podatke preko interneta\? + Ažuriraj tačke od interesa + Ažuriraćete tačke od interesa ako još zumirate + Nema tačaka od interesa bez interneta za ovo područje + Učitavanje podataka sa servera neuspešno. + Ne mogu da ažuriram lokalni spisak tačaka od interesa. + Podaci tačaka od interesa ažuriranu ({0}je učitano) + OsmAnd aplikacija za navođenje + Prikaži stanice prevoza + Prikaži stanice javnog prevoza na karti. + Prevoz + Dužina maršrute + stanica do izlaza + naredna udaljenost + prethodna udaljenost + Izaberite stajalište za izlaz + Završi pretragu + Prethodna maršruta + Sledeća maršruta + stanice + Stanica + Putevi + Dodaj novo pravilo + Ne mogu da izmenim format radnog vremena. + Portret, pejzaž ili kao i uređaj. + Skrenite oštro desno i nastavite + Skrenite desno i nastavite + Otpakivanje fajlova… + Memorijska kartica može samo da se čita. +\nSad je moguće samo gledati več učitane karte, ne možete da skidate nove oblasti. + Memorijska kartica nije dostupna. +\nNećete moći da vidite karte ili da pretražujete stvari. + Odabrani paket glasovnog navođenja nije dostupan + Željeni glasovni podaci su oštećeni + Nepodržava verzija glasovnih podataka + Inicijalizacija glasovnih podataka… + Zvuk isključen + Zvuk uključen + Filter + Prikaži filter + Sakrij filter + Servisu za navođenje iz pozadine je potrebno da provajder za položaj bude uključen. + Drži OsmAnd pokrenutim u pozadini + Prati Vaš položaj dok je ekran isključen. + Provajder položaja + Način nalaženja položaja koji koristi pozadinski servis: + Interval buđenja pozadinskog servisa: + Maksimalno čekanje na popravku + Postavlja najduže dozvoljeno vreme čekanja za popravljanje položaja u pozadini. + Upotreba trekbola + Koristite trekbol da pomerate kartu. + Slojevi karte + Izvor karte… + Yandex saobraćaj + Hvala Yandex-u za informacije o saobraćaju. + Omiljene tačke podeljene preko OsmAnd-a + Nema Omiljenih tačaka za snimanje + Omiljene tačke sačuvane u {0} + GPX fajl sa Omiljenim tačkama nije nađen u {0} + Sačuvaj podatke kao GPX fajl ili uvezi prolazne tačke u „Omiljene tačke“\? + Omiljene tačke uvezene + U smeru kretanja + Pravac kompasa + Pretraži stanice prevoza + Vektorske karte van mreže + Glasovna navođenja + Poništi pretragu prevoza + Rezultati prevoza ({0} do odredišta): + Rezultati prevoza (bez odredišta): + Pretraži javni prevoz + Ne rasteži (i ne čini mutnim) sličice karte na ekranima sa velikom gustinom. + Prikaži telefone u tačkama od interesa + Prikaži veb sajtove u tačkama od interesa + Izaberite izgled prikaza + Iscrtavaj neprekidno umesto crtanje slike po slike. + Neprekidno iscrtavanje + Ova karta ne može da se preuzme + Najveći zum za učitavanje unapred + Preuzmi kartu + Na nivou uvećanja {0} treba skinuti {1} sličica ({2} MB) + Uključite da računate najbrži put ili isključite da računate put sa najvećom uštednjom goriva. + Najbrži put + Preuzeto + {0} stavka(e) izabrana + Preuzmi {0} fajl(ova) ({1} MB)\? + Dnevni/noćni režim rada + Prilagodite prelazak između dnevnog i noćnog prikaza. + Po senzoru svetla + Izlazak/zalazak sunca + Noć + Dan + Odaberite paket glasovnog navođenja + Nema dostupnog glasovnog navođenja, idite u „Postavke“ → „Postavke navođenja“ → „Glasovno navođenje“ i odaberite ili skinite paket sa glasovima. + Aplikacija za stanje GPS-a nije instalirana. Da je potražite u prodavnici\? + Odaberite OsmAnd verziju za instaliranje + Učitavam OsmAnd verzije… + Preuzimanje spiska OsmAnd verzija nije uspelo + Instalirati OsmAnd - {0} od {1} {2} MB \? + Preuzimam verziju… + Verzija {0} instalirana ({1}). + Prethodna OsmAnd verzija je već instalirana. Svi postojeći podaci će biti podržani i u novoj verziji. Potrebno je ipak da izvezete Omiljene tačke iz stare aplikacije i uvezete naknadno u novoj verziji. + Ne mogu da nađen zadatu fasciklu. + OsmAnd navođenje van mreže je eksperimentalna funkcionalnost i ne radi za udaljenosti preko 20 km. +\n +\nNavođenje privremeno prebačeno na mrežni CloudMade servis. + Verzija indeksa „{0}“ nije podržana + Indeks „{0}“ ne može da stane u memoriju + Čitam keširane sličice… + NameFinder na mreži + Prilagođeni filter + Najbliže tačke od interesa + Stari format podataka karte \'\'{0}\'\' nije podržan + m + km/h + km + Indeksiram prevoz… + Indeksiram tačke od interesa… + Indeksiram kartu… + Indeksiram adrese… + Prevoz + Turizam + Prehrana + Sport + Trgovina + Ostalo + Kancelarija + Priroda + Vojska + Građevine + Slobodno vreme + Zemlja u upotrebi + Istorijsko mesto + Zdravstvo + Geokeš + Finansije + Zabava + Hitna pomoć + Obrazovanje + Prepreka + Administracija + Dodaj prolaznu tačku na snimljenu GPX putanju + GPX prolazna tačka „{0}“ dodata + Podeli položaj + Pošalji položaj + Da vidite položaj, pratite vezu ka veb lokaciji %1$s ili Android intent vezu %2$s + Lokacija: %1$s +\n%2$s + Podeli položaj koristeći + mi + mph + ft + yd + Kilometri/metri + Milje/jardi + Milje/stope + Jedinice dužine + Promeni jedinice za dužinu. + nepotpun + Jezik prikaza + Jezik za prikaz u aplikaciji (koristiće se od kada se OsmAnd ponovo pokrene). + Sistem + Pretraži po geografskom položaju + Ne mogu da rasčlanim geo nameru ’%s’. + Pretraga van mreže ne može da se obavi. + Internet OSM klasifikacija karti sa slikama. + Minimalni nivo uvećanja za vektorske karte + Koristi rasterske karte za sve preko ovog nivoa. + Instaliraj još… + Bez interneta je nemoguće izvršavanje ove operacije. + Izaberite (sličice) karte za instalaciju ili ažuriranje. + Karta već instalirana, „Podešavanja“ će se ažurirati. + Izaberite kartu sloja iznad + Karta sloja iznad + Karta sloja iznad… + Izaberite kartu sloja ispod + Karta ispod + Karta ispod… + Providnost osnovne karte + Podesite providnost osnovne karte. + Providnost sloja iznad + Podesite providnost gornjeg sloja. + Aplikacija ne može da skine sloj sa kartama %1$s, ponovna instalacija aplikacije možda pomogne. + Zvuk multimedije/navođenja + Odaberite govornika za glasovno navođenje. + Postojeće vektorske karte za ovaj položaj. +\n\t +\n\tDa biste ih koristili, idite na \'Meni\' → \'Podesi kartu\' → \'Izvor karte…\' → \'Vektorske karte van mreže\'. + Postavi duž cele putanje + Koristi trenutno odredište + Odabrani jezik nije podržan od strane instalirane Androidove sinteze teksta u govor, biće korišćen već podešeni jezik. Potražite drugi sintetizator teksta u govor u prodavnici\? + Treba da %1$s %2$s stavku(e). Nastaviti\? + %1$d of %2$d stavke(i) aktivirano. + Brze radnje + Nedozvoljen znak u imenu datoteke + Izveštaj za + Broj izmena + Broj saradnika + Mesec i država: + Noć + Ujutro + Sedmično + Dnevno + Na svakih sat vremena + Poslednja izmena karte: %s + Ažuriraj veličinu + Ažuriraj vreme + Poslednje ažuriranje: %s + OsmAnd nema dozvole da koristi memorijsku karticu + Ažuriraj sada + Uživo ažuriranje + Skidaj samo na bežičnoj mreži + Postoji izbor da li želite da kontrolišete aplikaciju preko menija ili preko komandne table. Vaš izbor uvek možete promeniti u podešavanjima komandne table. + Komandna tabla ili meniji + Koristi meni + Koristi komandnu tablu + Promeni OSM izmenu + Da li stvarno želite da sačuvate tačku od interesa bez tipa\? + Ime sadrži previše velikih slova. Ipak nastaviti\? + Legenda na karti + Kontakt + Povratne informacije + Verzije + Tehnički članci + Instalacija i otklanjanje problema + Pretraga na karti + Pregled karte + Često postavljana pitanja + ČPP + Planiranje puta + Podesite navođenje. + Kako da preuzmete karte i postavite osnovne stvari. + Prvo korišćenje + Dodaci + Ostalo + Pomozite da poboljšamo OsmAnd + Funkcionalnosti + Prvi koraci na OsmAnd aplikaciji + Nova verzija + Unesite ime države + Izaberite gde želite skladištiti karte i ostale podatke. + Prikazuje broj preostalih besplatnih preuzimanja. + Iskorišćenih besplatnih preuzimanja + Ažuriraj sve (%1$s MB) + %.1f MB + %1$.1f od %2$.1f MB + Karta koja sadrži samo puteve nije potrebna, pošto imate standardnu (celu) kartu. Svejedno je skinuti\? + Skidanja + Puna verzija + Kasnije + Uključite \'SRTM\' dodatak + Uključite dodatak \'Pomorske oznake\' + Kupi + Prikazuj baner besplatne verzije i u plaćenoj verziji. + Prikaži baner besplatne verzije + Preuzimam - %1$d fajl + Putevi + %1$s preostalih skidanja + Sve nesačuvane izmene će biti izgubljene. Da nastavim\? + Da li ste sigurni\? + Broj linija + Tačka od interesa će biti obrisana kada otpremite Vaše izmene + Sačuvano sad u: %1$s + Omiljene + Skorašnja mesta + Radni dani + Naznačite tip tačke od interesa. + Broj redova na tabli %1$s + %1$d of %2$d stavke(i) izbrisano. + %1$d od %2$d stavke(i) deaktivirano. + Izdat + Skinite podatke (\'van mreže\') da koristiti karte van mreže. + Ne, hvala + Ostalo + Kuća + Da li ste sigurni da želite da obrišete %1$d Omiljenih tačaka i %2$d Omiljenih grupa\? + Prikazuj neke detalje vektorskih karti (putevi itd.) već na manjim zumovima. + Put sačuvan kao \'%1$s\'. + Poslati GPX fajl na OSM\? + Preuzmite vektorske karte za ovaj položaj iz „Podešavanja“ („Upravljanje fajlovima karata“) ili se prebacite na dodatak „Mrežne karte“. + Testiraj glasovno navođenje + Ugrađeno iscrtavanje + Odaberite glas i pustite obaveštenje da ga testirate: + Vektorske karte se uglavnom brže prikazuju. To ne znači da će raditi dobro na svim uređajima. + Dodaci + Dodaci aktiviraju napredna podešavanja i dodatne funkcionalnosti. + Dodaci + Odaberite izvore mrežnih ili keširanih sličica karata. + Koristi mrežne karte (skidaj i keširaj sličice na memorijsku karticu). + Mrežne i karte sa sličicama + Omogućite dodatak za „Mrežne karte“ da odaberete različite izvore karata + Skidajte i upravljajte kartama uskladištenim na Vašem uređaju. + Standardne karte (vektorske) + Mrežne i keširene karte sa sličicama + Osnovna karta potrebna da obezbedi osnovne funkcionalnosti čeka u redu na preuzimanje. + Hvala Vam što koristite OsmAnd. Skinite regionalne podatke preko \'Podešavanja\' → \'Upravljanje fajlovima karata\' da i bez interneta vidite karte, nađete adrese, tačke od interesa, javni prevoz i još mnogo toga. + Otkaži preuzimanje\? + Postavi transparentnost (0 - skroz transparentno, 255 - skroz neprovidno) + Položaj još nije poznat. + Polazna tačkajoš nije određena. + Tapnite na postojeću stavku da vidite više detalja, dugo držite da je deaktivirate ili obrišete. Trenutno podataka na uređaju (%1$s slobodno): + Za zemlje u kojima se vozi levom stranom. + Vožnja levom trakom + OsmAnd navigacija van mreže je privremeno nedostupna. + Način za pretragu Omiljenih tačaka + Pretraga javnog prevoza + Koordinate + Pretraga adresa + Pretraga POI (tačaka od interesa) + Format + Podaci u rutiranju + Prikaži više detalja na karti + Povećaj nivo detalja na karti. + Prikaži izohipse + Prikaz od kog nivoa uvećanja (zahteva podatke o izohipsama): + Optimizuj kartu za + Način iscrtavanja + Poligone + Načini providnim sve vazdušne odlike terena. + Držite desno i nastavite + Držite levo i nastavite + Kružni tok: Izađite na %1$d. izlazu i nastavite + Put podeljen preko OsmAnd-a + Podelite put kao GPX fajl + Što pre + Privatni + Može da se koristi za praćenje + Može da se koristi za identifikaciju + Javni + Izbriši oznaku parkinga + Označi kao poziciju parkinga + Mesto za parkiranje + AM + PM + Uzeti vozilo sa parkinga u: + Položaj parkiranja Vašeg vozila. %1$s + Parking bez vremenskog ograničenja + Parking sa vremenskim ograničenjem + Dodajte obaveštenje u aplikaciju kalendara + Vremenski neograničeno + Vremenski ograničeno + Izaberite vrstu parkiranja + Izbriši oznaku parkinga + Obrisati oznaku položaja parkinga\? + Odaberite maksimalno vreme parkiranja + Obaveštenje da preuzmete kola je dodato u Vaš kalendar i tamo možete da ga izmenite ili uklonite. + Upozorenje + Uzmite kola sa parkinga + Deljeni položaj + Tačka polaska je previše daleko od najbližeg puta. + Ukupno memorije + Ukupno memorije koje je alocirala aplikacija %1$s MB (Dalvik %2$s MB, ostalo %3$s MB). +\nProporcionalno memorije %4$s MB (Android limit %5$s MB, Dalvik %6$s MB). + Iskorišćena memorija + Zauzeta memorija %1$s MB (Android limit %2$s MB, Dalvik %3$s MB). + Brzina simulacije puta: + Kola su parkirana na + Minuta + Časova + Čekam nalaženje položaja za izračunavanje puta + Nastaviti prethodno nezavršeno navođenje\? (%1$s sekundi) + Bez naplate putarina + Upozorenja o saobraćaju + Radari + Podesite interval buđenja: + Mini karta puta + Vreme pristizanja + Parking + Pritisnite ikonicu za zaključavanje da otključate + Neprekidno + Zaustavi +\n rad u pozadini + Prozirne spravice + Pravac pregleda + Tip prevoza: + Način prevoza: + Napravi filter tačaka od interesa + Približna pokrivenost karte i kvalitet: +\n • Zapadna Evropa: **** +\n • Istočna Evropa: *** +\n • Rusija: *** +\n • Severna Amerika: *** +\n • Južna Amerika: ** +\n • Azija: ** +\n • Japan i Koreja: *** +\n • Bliski istok: ** +\n • Afrika: ** +\n • Antarktik: * +\n Većina država na svetu se može skinuti +\n Od Avganistana do Šri Lanke, gore od Aljaske do Australije. Argentina, Brazil, Kanada, Francuska, Nemačka, Meksiko, Velika Britanija, Španija, … +\n + Doprinesite direktno OSM-u +\n • Prijavljujte greške u podacima +\n • Otpremajte GPX putanje direktno iz aplikacije +\n • Dodajte tačke od interesa i otpremajte ih direktno na OSM (ili kasnije ako niste na mreži) +\n • Opciono snimanje puta i u pozadini (dok je uređaj u režimu spavanja) +\n OsmAnd je softver otvorenog koda koji se aktivno razvija. Svako može da doprinese aplikaciji prijavljujući greške, poboljšavajući prevode ili razvijajući nove funkcionalnosti. Dodatno se projekat oslanja i na novčanu pomoć da se finansira programiranje i testiranje novih funkcionalnosti. +\n + Funkcionalnosti za bicikliste i pešake +\n • Pregled staza za pešanjenje, hodanje i bicikle, sjajno za aktivnosti van kuće +\n • Posebno traženje puta i režimi izgleda za bicikle i pešake +\n • Opcione stanice javnog prevoza (autobus, tramvaj, voz) uključujući imena linija +\n • Opciono snimanje puta u lokalni GPX fajl ili mrežni servis +\n • Opcioni prikaz brzine i nadmorske visine +\n • Prikaz konturnih linija i visinskih obrisa (preko dodatnog dodatka) + Bezbedonosne funkcionalnosti +\n • Opciono automatsko prekazivanje dnevnog/noćnog režima +\n • Opcioni prikaz ograničenja brzine, sa podsetnikom ako je prekoračite +\n • Opciono zumiranje puta relativno na brzinu kretanja +\n • Deljenje Vašeg položaja da prijatelji mogu da Vas nađu +\n + Korišćenje OSM i podataka sa Vikipedije +\n • Visokokvalitetne informacije sa najboljih projekata sa zajedničkim radom +\n • OSM podaci dostupni po državi ili regiji +\n • Vikipedija tačke od interesan, odlično za turističko razgledanje +\n • Neograničeno besplatnih skidanja, direktno iz aplikacije +\n • Kompaktne vektorske karte van mreže koje se ažuriraju bar jednom mesečno +\n +\n • Izabir između celokupnih podataka regije ili samo mreže puteva (primer: ceo Japan je 700MB, a samo putevi su 200MB) + Pregled karte +\n • Prikazuje Vaš položaj i pravac +\n • Opciono poravnjavanje slike prema kompasu ili smeru kretanja +\n • Čuvanje najbitnijih mesta u Omiljenim mestima +\n • Prikaz tačaka od interesa oko Vas +\n • Prikazuje specijalizovane sličice sa mreže, satelitske snimke (sa Binga), različite preklapajuće slojeve za obilaske/navođenje sa GPX stazama i dodatne slojeve sa podesivom providnošću +\n • Opcioni prikazujete imena na karti: na engleskom, lokalnom jeziku ili fonetski izgovor +\n + Navođenje +\n • Radi preko interneta (brzo) ili bez interneta (ne plaćate roming kada ste van zemlje) +\n • Glasovno navođenje od skretanja do skretanja (snimljeni ili sintetizovani glasovi) +\n • Opciono navođenje u odgovarajuću traku, prikaz imena ulica i procenjeno vreme dolaska +\n • Podrška za usputne tačke na putu +\n • Automatsko preračunavanje puta kada se odaljite sa putanje +\n • Pretraga mesta po adresi, tipu (npr. restorani, hoteli, benzinske pumpe, muzeji), ili po koordinatama +\n + OsmAnd+ (OSM Automated Navigation Directions) je aplikacija za karte i navođenje koja pristupa besplatnim visokokvalitetnim OSM podacima iz celog sveta. +\n Uživajte u optičkom i glasovnom navođenju, pregledu tačaka od interesa, pravljenjem i upravljanjem GPX putanjama, korišćenjem izobata i podacima o visini, izboru između režima vožnje, biciklizma, pešačenja, izmena na OSM-u i još mnogo toga. +\n +\n OsmAnd+ je verzija aplikacije koja se plaća. Njenom kupovinom, podržavate projekat, finansirate razvoj novih funkcionalnosti i primate najnovija ažuriranja. +\n +\n Neke od glavnih karakteristika: + OsmAnd+ (OSM Automated Navigation Directions) +\n +\n OsmAnd+ je is aplikacija otvorenog koda za navođenje sa pristupom raznim globalnim podacima OSM-a. Svi podaci na karti (vektorski ili sličice karte) se mogu uskladištiti na memorijsku karticu telefona za upotrebu i bez interneta. Nude se mogućnosti navođenja i sa i bez internet konekcije, uključujući u glasovno navođenje. +\n +\n OsmAnd+ je verzija aplikacije koja se plaća, a njenom kupovinom, podržavate projekat, finansirate razvoj novih funkcionalnosti i primate najnovija ažuriranja. +\n +\n Neke od osnovnih mogućnosti: +\n - Kompletna funkcionalnost rada van mreže (skladištenje skinutih vektorskih karti i sličica na skladište telefona) +\n - Dostupne su celokupne vektorske karte celog sveta za rad van mreže +\n - Neograničena preuzimanja država ili regiona direktno iz aplikacije +\n - Funkcionalnost Vikipedije van mreže (preuzimanje Vikipedijih tačaka od interesa), sjajno za turistička razgledanja +\n - Mogućnost dodavanja različitih dodatnih slojeva, kao što su GPX sloj ili staze za navođenje. Tačke od interesa, Omiljene tačke, konturne linije, stanice javnog prevoza, dodatne mape sa podesivom providnošću +\n +\n - Pretraga van mreže za adrese i tačke od interesa +\n - Rutiranje bez potrebe za internetom za udaljenosti srednjeg dometa +\n - Režimi rada za automobil, biciklu i pešake sa opcionim: +\n - Automatskim prebacivanjem dnevnog/noćnog režima rada +\n - Zumiranjem relativnim na brzinu +\n - Okretanjem mape prema kompasu ili pravcu kretanja +\n - Navođenjem u pravu traku, prikaz ograničenja brzine, snimljeni i sintetizovani glasovi za navođenje +\n + Globalni pregled karti na mobilnim uređajima & navigacija za OSM karte na mreži i van mreže + OsmAnd+ karte & navođenje + OsmAnd je softver otvorenog koda na kome se aktivno radi. Svako može da doprinese aplikacija tako što će da prijavljuje greške, poboljšava prevode ili da programira nove funkcionalnosti. Dodatno, projekat se oslanja na novčane priloge da bi se finansirao razvoj i testiranje novih funkcija. +\n +\n Približna pokrivenost karte i kvalitet: +\n • Zapadna Evropa: **** +\n • Istočna Evropa: *** +\n • Rusija: *** +\n • Severna Amerika: *** +\n • Južna Amerika: ** +\n • Azija: ** +\n • Japan i Koreja: *** +\n • Bliski istok: ** +\n • Afrika: ** +\n • Antarktik: * +\n Većina država na svetu se može skinuti! +\n Dobijte pouzdanog navigatora u Vašoj državi - bilo da je to Francuska, Nemačka, Meksiko, Velika Britanija, Holandija, SAD, Rusija, Brazil ili neka druga. + Doprinesite OSM-u +\n • Prijavljujte greške u podacima +\n • Otpremajte GPX putanje direktno iz aplikacije +\n • Dodajte tačke od interesa i otpremajte ih direktno na OSM (ili kasnije ako niste na mreži) +\n + Šetanje, planinarenje, gradske ture +\n • Karta Vam pokazuje staze za pešačenje i planinarenje +\n • Vikipedija na Vašem jeziku Vam može reći mnogo za vreme tura po gradu +\n • Stanice javnog prevoza (autobusi, tramvaji, vozovi), uključujući i imena linija će Vam pomoći kretanje po nepoznatom gradu +\n • GPS navođenje u režimu pešačenja Vam pravi putanju koristeći puteve na kojima se može proći peške +\n • Otpremajte i pratite GPX putanje ili snimajte i delite Vaše sopstvene +\n + Bickle +\n • Nađite staze za bicikle na kartama +\n • GPS navigacija u režimu za bicikle Vam pronalazi putanju na putevima za bicikle +\n • Vidite Vašu brzinu i nadmorsku visinu +\n • Opcija GPX snimanja Vam omogućava da snimiti Vaše putovanje i da ga delite +\n • Uz još dodataka možete da uključite i izohipse i reljef + Skijanje +\n OsmAnd dodatak za skijaške mape Vam omogućava da vidite staze za skijanje uz njihovu težinu i dodatne informacije, kao što su položaj ski liftova i ostalih objekata. + Karta +\n • Prikaz tačaka od interesa oko Vas +\n • Podešavanje karte prema pravcu kretanja (ili kompasu) +\n • Prikazuje Vaš položaj i pravac u kom je telefon usmeren +\n • Delite položaj da prijatelji mogu da Vas nađu +\n • Držite najbitnija mesta u „Omiljenim“ +\n • Dozvoljava da izaberete kako prikazujete imena na karti: na engleskom, lokalnom jeziku ili fonetski izgovor +\n • Prikazuje specijalizovane sličice sa mreže, satelitske snimke (sa Binga), različite preklapajuće slojeve za obilaske/navođenje sa GPX stazama i dodatne slojeve sa podesivom providnošću +\n + GPS navođenje +\n • Odabir između navođenja preko interneta (brzo) ili bez interneta (ne plaćate roming kada ste van zemlje) +\n • Glasovno navođenje od skretanja do skretanja (snimljeni ili sintetizovani glasovi) +\n • Automatsko preračunavanje puta kada se odaljite sa putanje +\n • Opciono navođenje u odgovarajuću traku, prikaz imena ulica i procenjeno vreme dolaska +\n • Automatsko menjanje režima dana/noći za bezbednije putovanje +\n • Prikaz ograničenja brzine i podsetnik kada je prekoračite +\n • Zumiranje karte se prilagođava brzini +\n • Pretraga mesta po adresi, tipu (npr. restorani, hoteli, benzinske pumpe, muzeji), ili po koordinatama +\n • Podrška za usputne tačke na putu +\n • Snimanje Vaših, ili otpremanje GPX staza i njihovo praćenje +\n + OsmAnd (OSM Automated Navigation Directions) je aplikacija za karte i navođenje koja pristupa besplatnim visokokvalitetnim OSM podacima iz celog sveta. +\n +\n Uživajte u optičkom i glasovnom navođenju, pregledu tačaka od interesa, pravljenjem i upravljanjem GPX putanjama, korišćenjem izobata i podacima o visini (kroz dodatak), izboru između režima vožnje, biciklizma, pešačenja, izmena na OSM-u i još mnogo toga. + OsmAnd (OSM Automated Navigation Directions) +\n +\n OsmAnd je is aplikacija otvorenog koda za navođenje sa pristupom raznim globalnim podacima OSM-a. Svi podaci na karti (vektorski ili sličice karte) se mogu uskladištiti na memorijsku karticu telefona za upotrebu i bez interneta. Nude se mogućnosti navođenja i sa i bez internet konekcije, uključujući u glasovno navođenje. +\n +\n Neke od osnovnih mogućnosti: +\n - Kompletna funkcionalnost rada van mreže (skladištenje skinutih vektorskih karti i sličica na skladište telefona) +\n - Dostupne su celokupne vektorske karte celog sveta za rad van mreže +\n - Preuzimanje države ili regiona direktno iz aplikacije +\n - Mogućnost dodavanja različitih dodatnih slojeva, kao što su GPX sloj ili staze za navođenje. Tačke od interesa, Omiljene tačke, konturne linije, stanice javnog prevoza, dodatne mape sa podesivom providnošću +\n - Pretraga van mreže za adrese i tačke od interesa +\n - Rutiranje bez potrebe za internetom za udaljenosti srednjeg dometa +\n - Režimi rada za automobil, biciklu i pešake sa opcionim: +\n - Automatskim prebacivanjem dnevnog/noćnog režima rada +\n - Zumiranjem relativnim na brzinu +\n - Okretanjem mape prema kompasu ili pravcu kretanja +\n - Navođenjem u pravu traku, prikaz ograničenja brzine, snimljeni i sintetizovani glasovi za navođenje +\n +\n Ograničenja besplatne verzije OsmAnd-a: +\n - Ograničen broj skidanja karti +\n - Bez pristupa tačkama od interesa sa Vikepdije kada nema interneta +\n +\n OsmAnd se aktivno razvija i naš projekat i kontinuirani napredak se oslanja na finansijske priloge za razvoj i testiranje novih funkcionalnosti. Molimo razmislite o kupovini OsmAnd+ aplikacije ili finansiranju specifičnih novih funkcionalnosti ili davanju donacije na https://osmand.net. + Globalni pregled karti na mobilnim uređajima i navigacija za OSM karte na mreži i van mreže + OsmAnd karte & navođenje + Pauziraj muziku + Glasovni upiti pauziraju puštenu muziku. + Prilepi na put + Automatsko zumiranje karte + Nivo zumiranja prema Vašoj brzini (dok je karta sinhronizovana sa Vašom pozicijom). + Bez autoputeva + Koristi kompas + Koristi kompas kada pravac nije prepoznat na drugi način. + Podesite upozorenja u saobraćaju (ograničenja brzine, forsirana zaustavljanja, ležeći policajci, tuneli), radare i podatke o traci. + Prikazuj upozorenja… + Gorivo + Za turiste + Prodavnica hrane + Pomoć na putu + Razgledanje znamenitosti + Restorani + Smeštaj + Zabava + Javni prevoz + Prva pomoć + Parking + Napredni način rada… + Dodaj oznaku + Završna tačka je predaleko od najbližeg puta. + Međuvreme + Prolazno odredište + Dodaj kao prolazno odredište + Stigli ste na prolazno odredište + Prolazno odredište %1$s je predaleko od najbližeg puta. + Odredišta + Već ste postavili odredišnu tačku: + Zameni odredište + Dodaj kao početno prolazno odredište + Dodaj kao završno prolazno odredište + Dodaj kao početno prolazno odredište + Dodaj kao poslednje prolazno odredište + Prolazno odredište %1$s + Odredište %1$s + Ukloni odredište + Nema puta + Usluge beleženja putanje + Omogućite GPS u podešavanjima + Računaj moguću približnu putanju za velike razdaljine + Omogućite dodatak za „snimanje putovanja“ radi korišćenja usluga beleženja (GPX beleženje, praćenje položaja na mreži) + Prikazuj pravac odredišta + Obrazac boja puteva + Izaberite obrazac boja puteva: + Aplikacija ZXing barkod skener nije instalirana. Pretražiti na Guglovoj prodavnici\? + Zatvori promene + Program je u bezbednom načinu rada (isključite u „Postavkama“). + Bezbedni način rada + Pokreni aplikaciju u bezbednom načinu rada (koristeći sporiji Androidov kod umesto bržeg programskog). + Samo putne karte + Odaberite kada će se prikazivati samo putne karte: + Zabrani prikaz regionalnih granica (admin nivoi 5-9). + Prikazuj puteve prema putanjama OSMC-a. + Sloj nad kartom sa simbolima za pešačenje + Prikazuj puteve prema lestvici SAC-a. + Alpska skala za planinarenje (SAC) + Karta puteva + Orijentacija ekrana + Pejzaž + Portret + Kao i uređaj + Pretraži okolinu + Pretraži blizu trenutnog središta karte + Tražim signal… + Skinite regione + Kasnije + Početak + Napravite polukružno i nastavite + Skrenite blago levo i nastavite + Skrenite oštro levo i nastavite + Skrenite levo i nastavite + Skrenite blago desno i nastavite + Uobičajena karta + Izohipse + delovi + Kliknite „Koristi položaj…“ da dodate belešku određenoj lokaciji. + Snimam %1$s %3$s %2$s + Ne mogu da pustim snimak. + Otvori program spoljnog izvođača + Izvodi se određeni zvučni zapis +\n%1$s + Snimanje zvuka/video snimaka. Zaustavite snimanje pritiskom na AV spravicu. + Koristi program kamere + Koristi sistemsku aplikaciju za slike. + Koristi sklopnog snimača videa + Koristi sistemsku aplikaciju za video snimanje. + Oblik izlaznog snimka + Izlazni video format: + Podrazumevana radnju spravice + Podrazumevana radnja spravice: + Snimi zvuk + Snimi video + Grčki + Nemački (govorni) + Nemački + Gruzijski + Francuski + Finski + Holandski + Danski + Češki + Hrvatski + Katalonski + Bugarski + Bošnjački + Beloruski (latinični) + Beloruski + Baskijski + Armenijski + Arapski + Albanski + Afrikans + Engleski (britanski) + Engleski + Portugalski (brazilski) + Kineski + Nebeska karta + Svetska karta + Svetska plaćanja bitskim novcem + Svetske pomorske oznake + Ispravka svetske geografske visine + Adresa u državi + Prevod + Razno + Glas + Uvek prikazuj položaj u sredini + Računaj put između tačaka + dana + Poveži se + Boja prikaza + Prazan GPX fajl + %1$s +\nStaza %2$s + %1$s +\nTačaka + %1$s +\nTačke putanje %2$s + Tačka %1$s + %1$s tačaka + Odsečak + Vreme kretanja: %1$s + Vremenski raspon: %1$s + Padina/uspon: %1$s + Raspon visine: %1$s + Prosečna visina: %1$s + Najveća brzina: %1$s + Prosečna brzina: %1$s + Vreme završetka: %1$tF, %1$tT + Početno vreme: %1$tF, %1$tT + Rastojanje: %1$s (%2$s tačaka) + Prolazne tačke: %1$s + Podputanje: %1$s + " +\n +\nPritisnite i držite za mogućnosti" + Automatski pokreni vodiča za skretanje + " +\n +\nPritisnite i držite radi prikaza karte" + Prolazna tačka + Trenutno vreme + Učitavam %1$s… + Nazivi tačaka od interesa na sloju iznad + Postavite odredište + Sačuvaj kao skup Omiljenih + Prikazuj dugmad za uveličavanje + Prikazuj dugmad za uveličavanje prilikom navođenja. + Raspored po imenu + Raspored po daljini + Sortiraj po kategoriji + Vreme razdvajanja + Odaberite stazu + Odaberite GPX fajl dugim pritiskanjem. + Nema ničeg pronađenog. Ako program ne može naći ovu oblast, možete to učiniti sami (pogledajte https://osmand.net). + Nema pronađenih preuzimanja, proverite vezu sa Internetom. + Prikazuj biciklističke staze + Prikazuj materijale puteva + Prikazuj stanje puteva + Prikazuj zabrane pristupa i putarine + Zabrane pristupa + Manje detalja + Više detalja + Prikaz zgrada je na uveličanju 15 + Šume i šumarke + Tekst + Puteve koji nisu za motorna vozila + Zgrade + Pokreni novi odsečak + Zaustavi praćenje na mreži + Započni praćenje na mreži + Praćenje na mreži (potreban GPX) + Ako je GPX beleženje uključeno, šalje podatke o putanji odabranom veb servisu. + Komandna tabla + Napred + Lokalna imena + Željeni jezik karte + Željeni jezik za oznake na karti (ako nije dostupan, koristiće se engleski ili lokalna imena). + Drži + Pauziraj navođenje + Nastavi navođenje + Oboji zgrade po tipu + Pešice + Bicikl + Kola + Pregledaj kartu + Da preuzmem nedostajuće karte %1$s (%2$d MB)\? + Prolazne tačke + Sve + Ture + Min/Maks + Maks/Min + Vreme u pokretu + Uspon/Spust + %1$d od %2$d + Prosek + Rastojanje + Trajanje + h + Podesite kako snimati Vaša putovanja. + Snimanje putovanja + Odaberite broj porta Vašeg proksija (npr. 8118). + Proksi port + Navedite ime domaćina Vašeg proksija (npr. 127.0.0.1). + Proksi domaćin + Podesite HTTP proksi za sve mrežne zahteve. + Omogući HTTP proksi + Glasovno navođenje + Izaberite glasovno navođenje. + Navedite interval logovanja snimanja putanje prilikom navođenja + Interval beleženja za vreme navođenja + Interval beleženja + GPX putanja se automatski čuva u fasciklu sa putanjama dok je navođenje uključeno. + Automatski snimaj putanju za vreme navođenja + Sačuvaj trenutnu putanju + Sačuvaj odmah trenutnu putanju kao GPX fajl. + Glavno beleženje pozicije u GPX fajl može da se uključi i isključi korišćenjem spravice za GPX beleženje na karti. + Praćenje putanje po zahtevu + Uključi da GPS radi u pozadini + Međuvreme buđenja GPS-a + Međuvreme opšte prijave + Uvek pitaj + Zaustavi + Prekinuti pozadinski režim GPS-a\? + Pozadinske usluge OsmAnd-a su i dalje pokrenute. Prekinuti i njih\? + Upozorenja o gužvama u saobraćaju + Obližnje zanimljive tačke + Omiljeno u blizini + Upozorenje saobraćajne gužve + Brzinska kamera + Smirivanje saobraćaja + Naplata putarine + Upravljanje granicama + Štampaj putanju + Prevoz + Putevi + Autobuski putevi + Putevi trolejbusa + Putevi deljenog taksija + Tramvajski putevi + Železnički putevi + Izbegavaj puteve… + Tramvajski i železnički putevi + Autobus, trolejbus, šatl bus + Ispravan kôd lokacije +\nPredstavlja površinu: %1$s x %2$s + Prekratak kôd lokacije +\nNavedite puni kôd + Neispravni kôd lokacije +\n + Otvoreni kôd lokacije + MGRS + Američki atlas puteva + Fajl sa podacima tačaka od interesa \'%1$s\' je nepotreban i može biti izbrisan. + Preuzmi podatke za pretragu tačaka od interesa bez interneta. + Nađeno nekoliko kategorija tačaka od interesa. + Grad + Zaseok + Obriši %1$s\? + Postavke izvora karte + Sloj iznad / ispod + Opcije specifične za vektorski renderer + Automatsko centriranje samo za vreme navođenja + Ponovi instrukcije navođenja + Ponavljaj instrukcije navođenja u pravilnim razmacima. + Samo ručno (klikom na strelicu) + Vreme dok se pregled karte ne sinhronizuje sa trenutnom pozicijom. + Prozirna tema + Nema dovoljno slobodnog prostora da se skine %1$s MB (slobodno: %2$s). + OsmAnd radi u pozadini dok je ekran isključen. + Pozadinski režim + Potrebno za podnošenje na openstreetmap.org. + Vaše OSM korisničko ime + Opšte postavke aplikacije + Podesite prikaz i opšte postavke aplikacije. + Opšte + Upravljanje fajlovima karata + Opšte postavke + Odaberite opcije navođenja. + Navođenje + Postavke specifične za profil + Fajl sa već izvezenim Omiljenim tačkama već postoji. Zameniti ga\? + Korisnički određeno + Vikipedija (van mreže) + Glasovna navođenja (TTS, preferirana) + Glasovna navođenja (snimljena, ograničene funkcionalnosti) + Svetske tačke od interesa sa Vikipedije + Svetske i tematske karte + Antarktik + Australija i Okeanija + Azija + Afrika + Rusija + Evropa - Nemačka + Evropa - Francuska + Evropa + Južna Amerika + Centralna Amerika + Severna Amerika - Sjedinjene Američke Države + Evropa - Holandija + Severna Amerika + Prikazuj opis tačaka od interesa. + Besplatna verzija + Možete skinuti ili ažurirati %1$s karti. + Prikaži trenutnu putanju + Zapisujte putanju koristeći GPX spravice ili preko podešavanja za \"Snimanje putovanja\". + Odredite vremenski bafer u kojem se drže lokacije da se pošalju dok nema mreže + Vremenski bafer za onlajn praćenje + Veb adresa za onlajn praćenje + Unesite veb adresu sa sledećom sintaksom za parametre: lat={0}, lon={1}, timestamp={2}, hdop={3}, altitude={4}, speed={5}, bearing={6}. + Interval za onlajn praćenje + Odredite interval praćenja dok ste na mreži. + Prikazuj i upravljaj OSM tačkama od interesa/beleškama iz baze podataka sa uređaja. + Tačke od interesa/beleške sa OSM-a sačuvane na uređaju + Asinhrono OSM uređivanje: + Izbriši izmenu + Otpremi izmene u OSM + Otpremi sve + {0} tačaka od interesa/beleški otpremljeno + Otpremam… + Izmene tačaka od interesa unutar aplikacija ne utiče na skinute fajlove karata. Izmene su snimljene u lokalni fajl na uređaju. + Ako je omogućeno uređivanje van mreže, tada će se promene prvo sačuvati lokalno i otpremiti po zahtevu, u suprotnom će se promene odmah otpremiti. + Uređivanje van mreže + Koristi fluorescentne boje za prikaz putanja i puteva. + Fluorescentni sloj iznad karte + Nedovoljno memorije da se prikaže odabrano područje + Koliko brzo želite da dobijete obaveštenje o pristizanju\? + Obaveštenje o dolasku + U poslednjih par metara + Kasno + Normalno + Rano + Opcije vezane za pristupačnost. + Koristite trekbol za zumiranje + Promenite zumiranje vodoravnim pokretima trekbola. + Ja sam ovde + Zaustavi automatsko najavljivanje + Započni automatsko najavljivanje + Izaberite način izražavanja odnosnih pravaca prilikom kretanja + Stil smera + U smeru kazaljke na satu (12 sektora) + Bočno (8 sektora) + Nema informacija + Nadmorska visina + Tačnost + prema + sati + levo-napred + na levo + levo-nazad + nazad + desno-nazad + na desno + desno-napred + napred + sever-severozapad + severozapad + zapad-severozapad + zapad + zapad-jugozapad + jugozapad + jug-jugozapad + jug + jug-jugoistok + jugoistok + istok-jugoistok + istok + istok-severoistok + severoistok + sever-severoistok + sever + Nivo zuma je + Zumiraj + Odzumiraj + Nazad na meni + Prema Android sistemskim postavkama + Uključite funkcionalnosti za bolju pristupačnost. + Režim pristupačnosti + Vrati se na položaj + Informacije + Prikaži lenjir + Donirajte da biste videli nove funkcionalnosti u aplikaciji. + Podržite nove funkcionalnosti + Podrška + Da biste otpremili GPX fajlove morate da navedete OSM korisničko ime i lozinku. + Opis + Oznake + Vidljivost + Putevi sa velikim kontrastom + Nemački atlas puteva + Podrazumevano + Stil puteva + Mesta za parkiranje + Ravnanje staze + Skijališta + prethodni dani + Razvoj OsmAnd-a + Podešavanja za razvoj i otklanjanje grrešaka kao što su simulacija navođenja, performanse iscrtavanja ili glasovni upiti. Namenjeno programerima, nepotrebno prilikom normalnog korišćenja. + Doprinosite OSM-u, preko pravljenja i izmena OSM objekata tačaka od interesa, otvaranja ili komentarisanja OSM beleški, i prilaganjem snimljenih GPX fajlova iz OsmAnd-a tako što date Vaše korisničko ime i lozinku. OpenStreetMap.org je globalni projekat mapiranja sveta u javnom domenu, vođen zajednicom. + OpenStreetMap uređivanje + Čini da se funkcije pristupačnosti iz uređaja mogu koristiti direktno u OsmAnd-u. Može da upravlja brzinom izgovora sintetizovanih glasova, podešavanje D-pada za navođenje, korišćenje trekbola za kontrolu zumiranja, izgovara tekst, npr. da Vam kaže položaj. + Pristupačnost + Karta je preuzeta + QR kod + Osnovna karta sveta (koja pokriva celu Zemlju pri pogledu izdaleka) ne postoji ili je zastarela. Preuzmite osnovnu kartu sveta za upotpunjavanje radne sredine. + OSM tačka od interesa je napravljena + Otpremi + Ažuriraj + Predložene objekte + Šta je novo + Šta je novo + Pročitaj više + Albanski (toskijski) + Zapadnofrizijski + Makedonski + Niskonemački + OSM izmene deljene preko OsmAnd-a + Položaj + Izmeni GPX prolaznu tačku + Izbrisati prolaznu tačku GPX-a\? + Predaj + Komentar dodat + Komentar ne može da se doda. + Ime autora + Poruka + Lozinka OSM-a (opciono) + Beleška ne može da se zatvori. + Beleška zatvorena + Ne mogu da napravim belešku. + Beleška napravljena + Zatvori belešku + Ponovo otvori belešku + Dodaj komentar + Napravi belešku + Beleška OSM-a + Napravi belešku OSM-a + Iskomentarisana beleška OSM-a + Ponovo otvorena beleška OSM-a + Otvori belešku OSM-a + Izbrisane OSM tačke od interesa + Izmenjene OSM tačke od interesa + Sačuvaj za pregled van mreže + Umnoženo je u ostavu isečaka + Prikaži pri pokretanju + Ako je isključeno, odmah se prikazuje karta. + Pristupi sa karte + Dugme za meni ne pokreće meni, nego komandnu tablu + Izaberite tačnu vrstu tačke od interesa ili je preskočite. + Izaberite bitski protok zvuka. + Bitski protok zvuka + Izaberite oblik za zvučni izlaz. + Oblik zvučnog izlaza + Izaberite svojstva prikaza videja. + Kvalitet izlaza slike + Najviša kakvoća + Najniža kakvoća + Sakrij + Blizu + Nije određena adresa + Konjske staze + Vreme: + Rastojanje: + Odobri pristup mikrofonu. + Odobri pristup kameri. + Odobri pristup podacima o položaju. + Zvuk + Izaberite koje puteve želite da izbegavate tokom navođenja. + Izaberite ili preuzmite glasovno navođenje svog jezika. + Izaberite glasovno navođenje + Dostupne karte + Nadogradnje uživo + Veličina zauzeća svih snimljenih odsečaka. + Veličina ostave + Povećaj vremensko ograničenje za snimke. + Dužina odsečka snimka + Prepisuje snimke kada se prepuni mesto za njihovo skladištenje. + Koristi seckanje snimaka + Seckanje snimanja + Nije označeno + Početna tačka + Tip + Opozovi sve + izbrisane stavke + Stavka uklonjena + Sličice tačaka od interesa + Zameni polaznu i ciljnu tačku + Označi + Put je preprečen + Dodaj vremenski razmak + Uklonite preuzete nadogradnje i vratiti se na izvornu kartu + Saznajte više + Ukloni + Nema dostupnih podataka + Podzemne objekte + Adresa e-pošte + Sačuvaj izmene + Stanje + Pronađi parkiralište + Prikazuj mnogouglove + Prikaži puteve planinskih bicikli + Preporučuje se da isključite prikaz poligona. + Oznaka karte + Oznake na karti + Broj aktivnih oznaka + Očistiti istoriju oznaka karata\? + Ukloniti sve aktivne oznake\? + Pokreni mogućnost označavanja na karti. + Obrnut redosled + Odaberi oznake na karti + Dodaj u oznake na karti + Dodati sve tačke u oznake na karti\? + Spravice + Traka pribora + Druga oznaka karte + Prva oznaka karte + Otpremi belešku OSM-a + Preuzeti {0} fajla\? +\nBiće iskorišćeno {1} MB (od {2} MB). + Preuzeti {0} fajla\? +\n {3} MB je iskorišćeno privremenog prostora, a {1} MB trajnog. (od {2} MB.) + Nema dovoljno prostora! +\n {3} MB je privremeno potrebno, a {1} MB trajno. +\n (dostupno je samo {2} MB.) + Prikazuj prozirnu traku pretrage + Otpremi bezimeno + Ostale oznake + Izaberite oznaku karte + Ova članarina omogućava nadogradnje svakog sata svih svetskih karata. +\n Deo vrednosti se vraća zajednici OSM-a i isplaćuje svakom doprinosiocu OSM-a. +\n Ako volite OsmAnd i OSM i želite da ih podržite i da oni podrže Vas, ovo je savršen način za to. + Molimo prvo kupite članarinu na OsmAnd uživo + Postavke članarine + Deo Vaših priloga će biti poslan korisnicima OSM-a koji otpremaju izmene karata te oblasti. + Hvala Vam na podršci OsmAnd-u! +\nDa aktivirate sve nove funkcionalnosti treba da ponovo pokrenete aplikaciju. + Unesite javno ime + Molimo unesite ispravnu adresu e-pošte + Neaktivno + Aktivno + Mesečno plaćanje + Cena po mesecu + Podržana oblast + Ne prikazuj moje ime u izveštajima + Javno ime + Potrebno da bi Vam obezbedili informacije o Vašim doprinosima. + Pretplati se + Članarina na OsmAnd uživo + Rangiranje OSM uređivača + %1$s izmena, u zbiru %2$s mBTC + Izmene %1$s, ugled %2$s, ukupno izmena %3$s + Broj primaoca + Prilozi + OSM korisničko ime i lozinka + Preračunaj putanju + Potpuni izveštaj + Gornja traka + Deljeno skladište + Prebačeno na internu memoriju pošto je označeno skladište za podatke zaštićeno od pisanja. Izaberite fasciklu za skladište u koju može da se piše. + Izbegavaj put + Završi navođenje + Pomeri ↓ + Pomeri ↑ + Aplikaciji je sada dozvoljen upis na spoljno skladište, ali morate ponovo da je pokrenete. + Izveštaj + Nema pronađenih prolaznih tačaka + Dodajte oznake karata na karti + Podebljano + Srednje + Tanko + Nežno + Prikaz karte + Sortiraj + Navedi gore + Prostri + Nije ugrađeno + Razgranat spisak + Prazan spisak + Prostrt spisak + Skupljeni spisak + Karta vezana za položaj + Unesite opis. + Unesite kategoriju + Unesite ime + Izaberite kategoriju + Podrazumevana boja + Najkraće vreme između najava. + Međuvreme pametnih najava + Obavesti me samo ako je pravac ka ciljnoj tačci izmenjen. + Pametna najava + Bez automatskog preračunavanja putanje ako se samo krećete u suprotnom pravcu. + Nema proračuna puta zbog pogrešnog pravca + Bez preračunavanja putanje kada se ona napusti. + Nema proračunavanja putanja po njegovom napuštanju + Usmereno vreteno + Magnetno vreteno + Dodatak za pristupačnost: Odredište nije podešeno + Navođenje uživo OSM-a + Omogući navođenje izmena OSM-a uživo. + Na zahtev\? + Razmislite o plaćanju dodatka za „izohipse“ radi davanja podrške daljem razvoju. + Tačno računanje putanja (sporo) + Računanje tačnih putanja bez grešaka. Radnja je ograničena daljinom i spora. + Da li sigurno želite da očistite odredište (i prolazna odredišta)\? + Podaci zvuka/videa + Izohipse + Senke brda + Vikipedija + Otvorene karte EU-a + Prednost putevima za motorna vozila. + Prednost… + Prednost autoputevima + neoznačeno + označeno + Vreme dolaska u međutačku + Vreme pristizanja + GPS podaci + Sloj sa osenčenim reljefima + Izmene OSM-a + Označi Omiljeno + Navedite adresu + Opcije pristupačnosti + Parrot + WunderLINQ + Tastatura + Nijedan + Odaberite uređaj za eksternu kontrolu, kao što je tastatura ili WunderLINQ. + Eksterni ulazni uređaj + Tema progama + Prilagodite izgled aplikacije. + Ugrađeno izdanje + Prikazuje podešavanja za uključivanje praćenja u pozadini i navođenja tako što se GPS uređaj periodično budi (a ekran ostaje ugašen). + Telefon + Veb strana + Kućni broj + Ime ulice + Nepovezan na bežičnu mrežu. Iskoristite trenutnu internet vezu za preuzimanje\? + Očisti odredište + Zaustavi navođenje + Odbaci putanju\? + Za isčitavanje kompasa, koristite magnetni senzor umesto orijentacionog senzora. + Smanjuje greške u iščitavanju kompasa, ali i unosi kašnjenje. + Započni novu podputanju + * Tapnite da označite tačku. +\n * Dugo držite da obrišete prethodnu tačku. +\n * Dugo držite na tačku da pogledate i dodate opis. +\n * Tapnite na spravicu za merenje da vidite još radnji. + Pravi rezervnu kopiju kako se OSM menja + Pravljenje rezervne kopije OSM izmena nije uspelo. + Generisan fajl %1$s sa OSM izmenama + Poređaj od vrata do vrata + Optimizuj redosled usputnih odredišta ka krajnjem odredištu. + Traži ulicu u okolnim gradovima + Izgled karte i podešavanja navigacije se pamte po korisničkom profilu. Postavite podrazumevani profil ovde. + Odredište + Australija + Oblast vožnje + Odaberite region vožnje: SAD, Evropa, Ujedinjeno Kraljevstvo, Azija i ostalo. + Odredi/uredi… + Ističe za (minuta) + Izvor pločica %1$s sačuvan + Podaci sličice: %1$s + Skinuto: %1$s + Skinutih zum nivoa: %1$s + O programu + Zumiranje karte + Na velikoj razdaljini + Na srednjim razdaljinama + Na bližim razdaljinama + Za duže destinacije: Dodajte usputna odredišta ako nijedan put nije nađen za ovu dugačku putanju ni posle 10 minuta. + Da li ste sigurni da želite da izbrišete %1$d OSM izmena\? + Avion + Brod + Motocikl + Planinarenje + Prikaz karte + Aplikativni profili + Izaberite profile za prikaz. + Vodena oznaka + Isključi komplikovano izračunavanje puta + Isključi dvofazno usmeravanje za navođenje u kolima. + Brzo izračunavanje puta nije uspelo (%s), prebacujem se na sporo izračunavanje. + Brzina izgovora + Odaberite brzinu izgovora sintetičkog TTS glasa. + Postavke putovanja + Postavke navođenja + Kamion + Računanje OsmAnd putanje van mreže + Umnožavam podatke OsMa… + Umnožavam podatke OsMa na novo odredište (%s)… + Kopiram fajl (%s) na novo odredište… + Od Androida 4.4 (KitKat), fasciklu za skladište (%s) je zastarela. Kopiraj sve OsmAnd fajlove na novi lokaciju za skladište\? +\n Beleška 1: Vaši stari fajlovi neće viti dirani (ali možete ih Vi sami obrisati). +\n Beleška 2: U novoj lokaciji za skladište, neće biti moguće deliti fajlove između OsmAnd i OsmAnd+ aplikacija. + Odredi visinu vozila koja je dozvoljena na putevima. + Ograničenje visine + Naznačite dozvoljenu širinu vozila na putu. + Ograničenje širine + Odredi dozvoljenu težinu vozila na putanji. + Ograničenje težine + Izbegavanje prelaženja državnih granica + Bez graničnih prelaza + Izbegavanje stepenica + Bez stepenica + Izbegavanjeautoputeva + Bez puteva za motorna vozila + Izbegavanje trajekata + Bez trajekata + Izbegavanje neasfaltiranih puteva + Bez neasfaltiranih puteva + Izbegavanje puteva sa naplatnim stanicama + Bez puteva sa naplatnim stanicama + Prednost putevima za motorna vozila + Prednost putevima za motorna vozila + Informacije o putu + Svojstva puta + Postavite odredište + Izaberite GPX… + Dodaj kao prolazno odredište + Koristi prikazanu stazu za navođenje\? + Računaj od prvog do poslednjeg odsečka staze OsmAnd-a + Računanje OsmAnd odsečaka staze van mreže + Evropa - Velika Britanija + Evropa - Italija + Severna Amerika - Kanada + Gvarani + Velški + Vijetnamski + Ukrajinski + Turski + Kineski (tradicionalni) + Švedski + Španski + Slovenački + Slovački + Kineski (hongkonški) + Kineski (pojadnostavljeni) + Svahili + Srpski (latinica) + Srpski (ćirilica) + Sardinijski + Ruski + Rumunski + Portugalski + Poljski + Persijski + Norveški bokmal + Marati + Litvanski + Letonski + Korejski + Kanada + Japanski + Italijanski + Indonezijski + Mađarski (zvanični) + Mađarski + Indijski + Hebrejski + Hebrejski + Ukazuj na pravac ciljne tačke trešnjom. + Uputstva trešenjem + Ukazuj na pravac ciljne tačke zvukom. + Zvučna uputstva + Pratite nas + Pomeri kartu radi izmena položaja oznake + Izmeni položaj oznake + Popunjenost baterije + Trenutna staza + Voz + Autobus + Oblik geografskih koordinata. + Oblik koordinata + Skladišna kartica + Otvoreno stalno + Traži + Sused + Oblast + iz + Poštanski broj + Kategorije + deljeno preko OsmAnd-a + Traži na rastojanju %1$s + Prikaži na karti %1$s + Ukloniti izabrane stavke iz „Istorije“\? + Nazad na pretragu + Pomorske karte + Pomorske izobate + Pomorske dubinske tačke severne polulopte + Pomorske dubinske tačke južne polulopte + Svetski Wikivoyage članci + Pomorske izobate + Hvala Vam na kupovini „Pomorskih izobata“ + Izobate mora (izohipse dubine) i karte pomorskih oznaka. + Pretplatite se na našu dopisnu listu za popust i dobijte još 3 preuzimanja karti! + Greška + Bez podvlačenja + Bez sloja nad kartom + Dugme za centriranje položaja ekrana na prvu međutačku ka odredištu. + Dugme koje centrira novo odredište na sredinu ekrana, i zamenjuje prethodno označeno odredište (ako postoji). + Dugme koje centar ekrana uzima za tačku polaska. Posle pita da odaberete odredište ili računa put do odredišta. + Dugme za centriranje odredišta na sredinu ekrana, a svako prethodno označeno odredište će postati poslednja međutačka. + Dodaj prvu prolaznu međutačku + Zameni odredište + Postavi odredište + Isključi samouvećanje karte + Uključi samouvećanje karte + Dugme za uključivanje/isključivanje automatskog zumiranja karte. + Uključiti/isključiti automatsko uvećanje karte + Pretraga po poštanskom broju + Izaberite grad + Najbliži gradovi + Ukucajte poštanski broj + Ukucajte grad/naselje/lokalitet + Ukucajte adresu + u %1$s + Izaberite ulicu + Sažetak + Uključi animaciju mog položaja za vreme navođenja. + Animiraj moj položaj + Promeni ime + Promeni boju + Ime grupe + Prikaz nivoa uveličanja: %1$s + Dozvoli pristup oblastima privatnih poseda. + Dozvoli pristup privatnim posedima + Prikaz počinje od nivoa uvećanja + Obrazac boja + Kupite i ugradite dodatak za izohipse da prikažete nagib vertikalnih oblasti. + Dodatak + Preuzmite kartu izohipsa za ovu oblast. + Sakrij počinjanje sa nivoa uvećanja + Instalirajte dodatak \"Izohipse\" da prikažete nagib vertikalnih oblasti. + Preuzmite kartu sloja sa senčenjem da biste videli reljef na karti. + Traži u Omiljenim + Razvrstano po udaljenosti + Dugme da prikažite ili da sakrijete OSM beleške na karti. + Promenite pretragu ili povećajte oblast pretrage. + Nema ničeg pronađenog + Povećaj oblast pretrage + Prepokreni pretragu + Vaše odredište se nalazi na privatnom posedu. Dozvoliti korišćenje privatnih puteva na ovom putovanju\? + Mrežne slike ulica za sve. Otkrijte mesta, sarađujte, osvojite svet. + Mapillary + Mrežne slike ulica za sve. Otkrijte mesta, sarađujte, osvojite svet. + Omogućava brzi doprinos Mapillary-ju. + Mapillary spravica + Podelite Vaš pogled sa ulice preko Mapilara. + Nema slika ovde. + Dodaj slike + Mrežne slike + Instalirajte program Mapillary da dodate slike na ovu lokaciju na karti. + Poboljšajte pokrivenost slika koristeći Mapillary + Instaliraj + Otvori Mapillary + Slika sa Mapilara + Rastojanje je ispravljeno + Ne mogu da uvezem fajl. Proverite da li OsmAnd ima dozvole za čitanje fajla. + Dozvole + Obim lenjira + Prikaži samo slike od 360° + Filtrirajte slike prema pošiljaocu, prema datumu ili prema tipu. Filtriranje se primenjuje samo pri zumiranju. + Korisničko ime + Pregledaj samo slike koje je dodao + Unesite korisničko ime + Dan + Pregledaj samo dodate slike + Od + Ka + Pogrešno korisničko ime + Ostavka pločica + Ponovo učitaj pločice radi postavljanja podataka dana. + Ponovo učitaj + Vrati na podrazumevano + Sačuvaj snimljene putanje u podfasciklama prema mesecima snimanja (npr. 2018-01). + Sačuvaj snimljene putanje u mesečnim fasciklama + Dugme za početak ili završetak navođenja. + Počni/zaustavi navođenje + Izaberite profil navođenja + Sačuvajte tačke ili kao tačke puta, ili kao liniju. + OsmAnd će se povezati tačke sa putevima izabranog profila. + Opcije + Dodaj tačku posle + Dodaj tačku pre + Uredi liniju + Tačka puta + Sačuvaj kao liniju + Sačuvaj kao tačke puta + Linija + Izađi pre čuvanja\? + Prikazuj na karti + Isključuje animacije u aplikaciji. + Bez animacija + Rasporedi po + Prikaz udaljenosti + Sve premesti u istoriju + Ove godine + Poslednje sedmice + Juče + Danas + Aktiviraj + Poslednje korišćeno: %1$s + Grupe + Spisak + Oznaka karte je premeštena u aktivne + Oznake karte su premeštene u istoriju + Sve oznake karte su premeštene u istoriju + Ispod izaberite kojom brzinom će promena usmerenja preći iz „u pravcu kretanja“ na „po pravcu kompasa“. + Zadrška usmerenja karte + Izaberite kako ukazati na rastojanje i pravac ka oznakama karte na karti: + Rasporedi po: + Dodato + A-Z + Z-A + Ukloni iz ’Oznaka karti’ + Sakrij pređene + Prikaži pređene + Prikazuj strelice na karti + Prikaži vođice + Grupa će nestati posle sledećeg pokretanja programa. + Premesti u istoriju + Izvezite oznake u sledeći GPX fajl: + Unos koordinata + Sortiraj + Plan putovanja + Kraj + Moj položaj + Dodajte svoj položaj kao tačku polaska da isplanirate savršeno putovanje. + Koristi položaj + Izbegavanje puteva sa poledicama i plićake. + Bez puteve sa poledicama ili plićacima + Brzi unos koordinata + Izaberite oblik unosa koordinata. Možete uvek da ga promenite tapkanjem na \"Opcije\". + Koristi sistemsku tastaturu + Oblik koordinate + Oznake + Napravi povratno putovanje + Dodajte kopiju tačke početka kao odredište. + Izmenite belešku OSM-a + Izmeni belešku + Ne mogu da izmenim belešku. + Morate dodati najmanje jednu oznaku za korišćenje ove mogućnosti. + Povratno putovanje + Put sračunat + Prikaži kartu + Put + Pogrešan oblik + Prolazne tačke dodate oznakama na karti + Pregled + Nazad + Unesite novo ime + Pogrešan unos + Uvezi fajl + Uvezi kao Omiljeno + Uvezi kao GPX fajl + može se uvesti ili kao Omiljene tačke ili kao GPX fajl. + Oznaka prođena + Režim preko celog ekrana + Tupkanje na kartu je uključuje i isključuje kontrolna dugmad i spravice. + Preimenuj oznaku + Sledeće polje + Nalepi + Prikazuj brojčanu stavku + Levo + Desno + Broj decimalnih brojki + Odaberite broj pokazivača pravca. + Izaberite način prikaza rastojanja do aktivnih oznaka. + Ne koristi + Da + Otkaži + U redu + + Opšte postavke + Postavke navođenja + Često postavljana pitanja, skorašnje izmene i ostalo. + Šir %1$s +\nDuž %2$s + Prazan spisak + Oponašaj svoj položaj + Poseti pre + Poseti nakon + Vaše izmene + preostalo + Vreme parkiranja je ograničeno na + min + h + OSM izmene + Dejstvo brisanja + Dejstvo izmene + Dejstvo stvaranja + zakasneo + Idi + Pokreni navođenje skretanja nakon… + Odredite preostalo vreme za čekanje ekrana za iscrtavanje putanje. + Očistiti istoriju\? + Da li ste sigurni da želite da otpremite %1$d izmena na OSM\? + Da li ste sigurni da želite da izbrišete %1$d beleški\? + Označi za brisanje + Izmene OSM-a dodate skupu mesnih izmena + Trenutna putanja + Dobrodošli + OsmAnd ima mogućnost pregleda zemaljskih karata van mreže i navođenja van mreže svetom. + Izaberite svoju regiju radi pravilnog čitanja saobraćajnih znakova i propisa: + Preuzmi karte + U mnogim državama (Nemačkoj, Francuskoj, Italiji i dr.) je upotreba pokretne kamere zabranjena zakonom. OsmAnd nije odgovoran za korisnikovo kršenje zakona. Molim, kliknite na \"da\" samo ako je za Vas ova mogućnost snimanja zakonita. + Podaci A-GPS-a preuzeti: %1$s + Poruka + Prikazuj opise. + Traži + Mesta + Uredi + Upravljaj + Obaveštenja A-GPS-a + Pomoćnik karata OSM-a + Otvoreno sad + Kucajte za pretragu + Filtriraj po imenu + Umnoži + Skladište karti + Fascikla skladišta podataka + Unutrašnje skladište + Ručno određeno + Unutrašnja memorija aplikacije + Skladište za više korisnika + Spoljne skladište + Premeštanje fajlova nije uspelo + Nemoguće je obrazovati karte u određenoj fascikli + Premestiti OsmAnd fajlove podataka na novo odredište\? + Kućne brojeve + Odjavi se + Bojenje prema OSMC planinarskom simbolu + Bojenje prema vrsti puta + Isključen + Izbriši OSM izmene + Prikaži detalje + Vikipedija + Vikipedija + Čitaj potpuni članak (na mreži) + Uvoz u OsmAnd + Spreči automatsko beleženje + Usluga određivanja položaja je isključena. Uključiti je\? + Preuzeti dodatne podatke sa Vikipedije (%1$s MB)\? + Imate stare nekompatibilne podatke sa Vikipedije. Da ih arhiviramo\? + Berberski + Kabilski + Gornjolužičkosrpski + Asturijski + Sebuano + Estonski + Galicijski + Haićanski + Malezijski + Nevarski + Oksitanski + Novonorveški + Telugu + Tajlandski + Volapuk + Norveški bukmol + Argentinski španski + Američki španski + Esperanto + Osetski + Laoski + Luksemburški + Malajalam + Tamilski + Kurdski + Latinski + Irski + Navaho + Bišnuprijski + Islandski + Albanski + Bretonski + Azerski + Srpskohrvatski + Tagalog + Bengalski + Pijemontanski + Tamna + Svetla + Potrebno je ponovno pokretanje programa za primenu izmena. + Prozirna ljubičasta + Crna + Smeđa + Prozirna ružičasta + Ružičasta + Ljubičasta + Prozirna plava + Plava + Prozirna svetlo plava + Svetlo plava + Prozirna zelena + Zelena + Prozirna svetlo zelena + Svetla zelena + Prozirna žuta + Žuta + Prozirna narandžasta + Prozirna crvena + Crvena + Tamno-žuta + Širina GPX traga + GPX širina + GPX boja + GPX boja + Podrazumevano (prozirna zelenkasto-plava) + Podrazumevano (13) + Nadogradnje uživo + Nema dostupnih nadogradnji + Zadebljaj obrisne linije + Opasnost + Izbegavanje šatl vozova + Bez šatl vozova + Dodaci + Položaji + GPX fajl sa položajima. + Pretražujem adresu + Simulirajte Vašu poziciju koristeću snimljenu GPX stazu. + Oponašaj upotrebom proračunate putanje ili GPX snimka. + Zaustavi oponašanje svog položaja. + Omiljena obaveštenja + Međuvreme buđenja GPS-a + Radi u pozadini + Navođenje + Snimanje putovanja + m/s + min/km + min/m + čv + Nautičkih milja na sat (čvorovi) + Minuta po milji + Minuta po kilometru + Metara u sekundi + Milja na sat + Kilometara na sat + Nautičke milje + nmi + Jedinica za brzinu + Odredite merne jedinice za brzinu. + Odaberite kategoriju + Dodaj novo + Izohipse su isključene + Sloj senčenja padina je isključen + Svetske karte + Karte velikih oblasti + Oblasti + Dodaj novu kategoriju + Ime kategorije + Upotrebite ime kategorije koje već ne postoji. + Šalji + Deli svoj položaj + položaj: + Podražavaj prvo pokretanje programa + Postavlja indikator kao da se OsmAnd pokreće prvi put, sve ostale postavke ostavlja nepromenjenim. + Prikaži kartu + Karta %1$s je spremna za korišćenje. + Prikazuj jednu ili dve strelice koje pokazuju pravac aktivnih oznaka. + Prikazuj vođice od svog položaja do položaja aktivne oznake. + Jedan + Dva + Oznake označene kao pređene će se pojaviti na ekranu. + Uvozite Omiljene grupe ili prolazne tačke kao oznake. + Uvezi grupe + Dugo ili kratko pritisnite na „Mesta“, onda pritisnite zastavicu kad se pojavi. + Napravite oznake na karti! + Uvozite grupe iz Omiljenih ili iz GPX prolaznih tačaka. + Dodaj skup + Omiljena kategorija + Prolazne tačke putanje + Izaberite kategoriju Omiljenih za dodavanje u oznake. + Odaberite sa koje putanje da dodate prolazne tačke na oznake. + Izgled na karti + Još + Pretraga za putanjama sa prolaznim tačkama + Po vrsti + Po danu + A/V beleške po datumu + Dodajte zvučnu, video ili slikovnu belešku na svaku tačku karte koristeći vidžete ili kontekstne menije. + Pravite beleške! + Pokretanje na \'jedan tup\' + Tupnite oznaku na karti radi njenog pomeranja na vrh aktivnih oznaka bez otvaranja priručnog izbornika. + Aktivirana oznaka %s. + Veb adresa + Interval praćenja + Vremenski bafer + Preporuka: Postavka od 5 metara ako ne treba da beležite preciznije detalje od ovoga i nedvosmisleno ne želite da beležite podatke prilikom odmaranja. + Nuspojave: periodi odmaranja se ne snimaju uopšte ili se ne snima samo po jedna tačka. Mala (stvarna) pomeranja (npr. u stranu, da obeležite moguće skretanje na Vašem putu) mogu da budu isfiltrirana. Vaš fajl sadrži manje informacija za naknadnu obradu i ima lošije statistike time što se filtriraju očigledno redudantne tačke za vreme snimanja, dok se potencijalno čuvaju artifakti koji nastaju lošim signalom ili efektima GPS čipa. + Ovaj filter izbegava da se snimaju tačke duplikati kada ima malo stvarnog kretanja, i daje bolji izgled staze na karti. + Opaska: Ako je GPS bio isključen odmah pre snimanja, prva tačka će imati umanjenu tačnost, pa u našem kodu bi želeli da sačekamo par sekundi pre snimanja tačke (ili snimanja najbolje od 3 uzastopne tačke itd.), ali ovo još nije napravljeno. + Preporuka: teško je predvideti šta će se beležiti, a šta neće, pa je najbolje da isključite ovaj filter. + Nuspojava: Kao rezultat filtriranja po tačnosti, neke tačke mogu skroz nedostajati npr. ispod mostova, drveća, između visokih zgrada, ili u određenim vremenskim uslovima. + Ovo će snimati samo tačke izmerene sa naznačivanjem minimalne tačnosti (u metrima/stopama, kao što prijavljuje Android za Vaš čipset). Tačnost je približna merenjima stvarne pozicije, i nije direktno vezana za preciznost, koja je rasipanje ponovljenih merenja. + Opaska: provera brzine > 0: većina GPS čipova prijavljuje vrednost brzine samo ako algoritam primeti da ste u pokretu i ništa ukoliko niste. Korišćenje postavke >0 u ovom filteru je zapravo korišćenje senzora pokreta iz GPS čipa. Čak iako se ne filtrira ovde za vreme snimanja, mi i dalje koristimo ovu funkcionalnost u našoj GPX analizi da odredimo pređenu udaljenost, tj. vrednost koju prikazujemo u tom polju je pređena udaljenost za vreme kretanja. + Preporuka: Pokušajte prvo da koristite detektor kretanja preko filtera minimalnog pomeraja (B) prvo, pošto može dati bolje rezultate i izgubićete manje podataka. Ako Vaše staze ostanu pune šuma pri malim brzinama, probajte sa ne-nultim vrednostima ovde. Pazite da neki tipovi merenja neće uopšte davati merenja brzine (neki metodi preko mreže) u kom slučaju nećete snimiti ništa. + Nuspojave: Vašim stazama će nedostajati sve sekcije gde kriterijum minimuma brzine nije bio ispunjen (npr. gde ste gurali bicikl uzbrdo). Takođe, neće biti informacija o periodima mirovanja, kao što su pravljenje pauza. Ovo može da ima efekta na analizu ili post-procesiranje, kao što je određivanje ukupne dužine puta, vremena kretanja, ili prosečne brzine. + Ovo je filter za odsecanje malih brzina koji ne beleži tačke ispod određene brzine. Ovim snimljene staze izgledaju glatkije kada se gledaju na karti. + Umesto toga odaberite podržani %1$s fajl ekstenzije. + Nema pravila za rutiranje u ’%1$s’. Odaberite drugi fajl. + Proverite i podelite detaljne zapise aplikacije + Potrebne su dozvole za korišćenje ove opcije. + Pored profila, možete odabrati dodatne podatke za izvoz. + Uvezeni profil sadrži dodatne podatke. Kliknite na Uvoz da uvezete samo profilne podatke ili odaberite dodatne podatke. + Uključi dodatne podatke + Stil iscrtavanja + Rutiranje + Sunce izlazi u %1$s + Sunce zalazi u %1$s + %1$s/%2$s + Sve postavke profila vraćene na podrazumevano. + Sve postavke dodatka vraćene na podrazumevano. + Prikaži samo noću + Dodaj proizvoljnu kategoriju + Dostupno + Možete da dodate nove proizvoljne kategorije označavajući jednu ili više kategorija. + Promenite redosled sortiranja spiska, sakrijte kategorije. Možete uvesti i izvesti sve promene sa profilima. + Preuredi kategorije + Režim pristupačnosti je isključen na Vašem Android sistemu. + Podrazumevano je isključeno: Dok OsmAnd radi u prednjem planu, ekran neće da se gasi. +\n +\nAko je uključeno, OsmAnd će koristiti sistemsko vreme odlaganja gašenja ekrana. + Koristi sistemsko vreme odlaganja gašenja ekrana + Resetovanje na podrazumevano će postaviti redosled sortiranja na podrazumevano stanje posle instalacije. + Kopiraj koordinate + • Profili: sada možete promeniti redosled, postaviti ikonicu za kartu, promeniti sve postavke osnovnih profila i povratiti ih nazad na podrazumevane vrednosti +\n +\n • Dodati brojevi izlaza prilikom navođenja +\n +\n • Preuređene postavke dodataka +\n +\n • Preuređen ekran Postavki za brži pristup svih profilima +\n +\n • Dodata opcija za kopiranje postavki iz drugog profila +\n +\n • Dodata mogućnost da se promeni redosled ili da se sakriju kategorije tačaka od interesa u Pretrazi +\n +\n • Ispravno poravnate ikonice tačaka od interesa na karti +\n +\n • Dodati podaci izlaska i zalaska sunca na Podešavanju Karte +\n +\n • Na kartu dodata ikonica Kuća/Posao +\n +\n • Dodata podrška za opise iz više redova u Postavkama +\n +\n • Dodata ispravna transliteracija na kartu Japana +\n +\n • Dodata karta Antarktika +\n +\n + Očisti snimljene podatke + Dikretno-na-tačku + Put će biti preračunat ako je udaljenost do puta veća od odabranog parametra + Minimalna udaljenost za preračunavanje puta + %1$s — %2$s — %3$s + Meni + Ovaj dodatak je posebna aplikacija, morate ga posebno ukloniti ako ne planirate da ga koristite. +\n +\nDodatak će ostati na uređaju i posle uklanjanja OsmAnd aplikacije. + Dodatak isključen + Otvori postavke + Bez preračunavanja + Unesite ime profila + Odaberite podatke za uvoženje. + Neke stavke već postoje + OsmAnd već ima elemente sa istim imenom kao oni koji se uvoze. +\n +\nOdaberite radnju. + Uvežene stavke će biti dodate uz prefiks + Zadrži obe + Zameni sve + Trenutne stavke će biti zamenjene stavkama iz fajla + Izlistan %1$s, već postoji u OsmAnd-u. + Profili + Brze radnje + Ništa nije označeno + Tipovi tačaka od interesa + Prepremam + Podrazumevano u aplikaciju (%s) + Minimalni ugao između moje lokacije i puta + Totalno pravi segment između moje lokacije i izračunatog puta biće prikazan dok se put ne izračuna + Ugao + Ugao: %s° + Proizvoljni profil + Uvozim fajl za iscrtavanje + Teren + Mapa senka brda koristi tamne nijanse za prikazivanje nagiba, vrhova i nizina. + Pri nagibu koriste se boje za vizuelizaciju strmine terena. + Postavite minimalni i maksimalni nivo zuma, pri kome će sloj biti prikazan. + Dodatne mape su potrebne da biste videli senke brda na mapi. + Dodatne mape su potrebne da biste videli nagibe na mapi. + Možete pročitati više o nagibima u %1$s. + Providnost + Nivoi uvećanja + Legenda + Omogućite da bi videli senke brda ili mape nagiba. Možete pročitati više o ovim vrstama karta na našem sajtu. + Senke brda + Svi podaci iz %1$s su uvezeni, možete da upotrebite dugmad ispod da bi ste otvorili neophodni deo aplikacije radi upravljanja njima. + Uvoz završen + Dodate stavke + Osmand proverava %1$s postojanje duplikata sa postojećim elementima u aplikaciji. +\n +\nTo može da potraje neko vreme. + Uvozim + Uvozim podatke iz %1$s + Ne mogu da napravim rezervnu kopiju profila. + Čuvam novi profil + Povrati sve postavke profila\? + Sve postavke profila će biti vraćene na originalno stanje pre kreiranja/uvoženja ovog profila. + Da li ste sigurni da želite da očistite snimljene podatke\? + Preračunaj put u slučaju odstupanja + Odaberite udaljenost posle koje se put preračunava. + Put će biti preračunat ukoliko je udaljenost trenutne pozicije od puta veća od odabrane vrednosti. + %1$s od %2$s + Nagibi + Dugme da prikaže ili sakrite sloj terena na karti. + Obriši opis + Dodaj opis + Odaberite grupu + Odaberite oblik + Krug + Oktagon + Kvadrat + Min + Prilagodite količinu elemenata u \"Fioci\", \"Podešavanju mape\" i \"Kontekst meniju\". +\n +\nOnemogućite nekorišćene dodatke da sakrijete sve njihove kontrole. %1$s. + Elementi Fioke, Kontekst meni + Prilagođavanje korisničkog interfejsa + Fioka + Akcije Kontekst menija + Preuredi ili sakrij stavke sa %1$s. + Razdelnik + Elementi ispod ove tačke su odvojeni razdelnikom. + Sakrivena + Iako su ove stavke sakrivene sa glavnog menija, opcije ili dodaci koje oni predstavljaju i dalje rade. + Sakrivanje postavki ih resetuje na originalno stanje. + Ima samo četiri dugmeta. + Glavne radnje + Možete pristupiti ovim radnjama kliktanjem na dugme „%1$s”. + Možete pomerati stavke samo unutar ove kategorije. + Dodatak za programere + Zameni drugu tačku sa ovom. + Ski turing + Snoumobil + Proizvoljni OsmAnd dodatak + Stavke + Izmene primenjene na ’%1$s’ profil. + Ne mogu da čitam iz „%1$s”. + Ne mogu da pišem %1$s. + Ne mogu da uvezem iz „%1$s”. + Izaberite fajl staze + Jezici + Jezik + Svi jezici + Za prikaz Vikipedijinih tačaka od interesa su potrebne dodatne karte. + Odaberite jezik za Vikipedijine članke na karti. Prebacite na bilo koji drugi dostupni jezik dok čitate članak. + Neki članci sa Vikipedija su možda dostupni na Vašem jeziku. + Kantonski + Južnominski + Joruba + Varajski + Uzbečki + Urdu + Tatarski + Tadžički + Škotski + Sicilijanski + Pandžabi + Nepalski + Napolitanski + Burmanski + Mongolski + Minangkabajski + Malgaški + Kirgiski + Kazaški + Javanski + Gudžarati + Čuvaški + Čečenski + Bavarski + Baškirski + Aragonski + Lombardijski + Proizvoljna boja + %1$s / %2$s + Naplatom će biti opterećen vaš Gugl Plej nalog pri potvrdi kupovine. +\n +\nČlanarina se automatski obnavlja ukoliko nije otkazana pre datuma obnove. Vaš nalog biće zadužen periodom obnove (mesec / tri meseca / godinu dana) samo na dan obnove. +\n +\nČlanarinama možete upravljati i otkazati ih tako što ćete otići na vaša Gugl Plej podešavanja. + Pretraga tipova tačaka od interesa + Kombinujte tipove tačaka od interesa iz različitih kategorija. Kliknite prekidač da odabere sve, kliknite na levu stranu da odaberete kategoriju. + Dodatne karte + Nepodržana radnja %1$s + OsmAnd pratilac + OsmAnd + Mapillary + Brze radnje + Lenjir prečnika + Izmeri udaljenost + Putovanje (Wikivoyage i Vikipedija) + Markeri na karti + Omiljeni + Članarina - OsmAnd uživo + OsmAnd kupovine + Uputstvo za legendu karte. + Profili navođenja + • Nove oflajn mape nagiba +\n +\n • Puno prilagođavanje Favorita i GPKS tačaka – prilagođavanje boja, ikona, oblika +\n +\n • Prilagođavanje redosleda elemenata \"Kontekst menija\", \"Podešavanje mape\" i \"Kutije\" +\n +\n • Vikipedija kao poseban sloj u Podešavanju mape, izaberite samo potrebne jezike +\n +\n • Napravite svoj TOI filter / mapu sa punom fleksibilnošću +\n +\n • Dodata mogućnost da se povrate podešavanja korisničkih profila +\n +\n • Celovite GPKS rute sa podrškom za saobraćajne trake i celovita uputstva skretanja +\n +\n • Ispravljene veličine korisničkog interfejsa na tabletama +\n +\n • Ispravljeni bagovi sa RTL +\n +\n + Dugme koje prikazuje ili skriva javni prevoz na karti. + Napravite ili izmenite POI + Parking pozicije + Kreirajte putanje tapkanjem na kartu, ili upotrebom i izmenom postojećih GPX fajlova, da biste isplanirali put i merili rastojanja između tačaka. Rezultat se može sačuvati kao GPX fajlovi za kasnije navođenje. + Omiljeno + Omiljeno + Karta + Prikaži na karti + Sklopi + Izlaz + Zatvori + Dejstvo {0} + Neočekivana greška + Ulazno-izlazna greška + Preuzeto + Preuzimam… + Osveži + Još dejstava + Još… + Izvezi + Zaustavi + Primeni + Deli + Izbriši + Očisti sve + Očisti + Označi sve + Odznači + Označi sve + Izaberi na karti + Istorijat + Postavke + Pomoć + Ništa + izabrano + Izabrano + Isključen + Uključen + Onemogući + Omogući + Sledeće + Prethodno + Isključeno + Uključeno + Ne + Napredna pretraga koordinata + Pretraga koordinata + %1$s stajanja pre + Počnimo + Izmeni + Nadzemne građevine + Da li ste sigurni da želite zameniti Omiljeni %1$s\? + Put koji štedi gorivo + Očisti sve pločice + Osvežiti sve karte sada\? + Pronađi moj položaj + Uzmi uputstva i otkrij nova mesta, i bez interneta + Omogućite pristup određivanju položaja + Dajte dozvole + OsmAnd skladište podataka (za karte, fajlove staza itd.): %1$s. + Slobodno mesto skladišta + Pretražujem položaj… + Zahtevane za preuzimanje. + Nema mrežne veze + Nije prepoznat položaj + Pusti da OsmAnd odredi Vaš položaj i da prema tome predloži karte za preuzimanje te oblasti. + Pretražujem karte… + Odaberite drugu oblast + Nemate ugrađenih karata. Možete izabrati kartu sa spiska ili preuzeti karte kasnije preko \'Meni - %1$s\'. + Preskoči preuzimanje karata + Milje/metri + Neograničena preuzimanja karti, nadogradnje, dodatak za Vikipediju. + Dobavite neograničeno preuzimanje karata, uz sedmična, dnevna i čak časovna ažuriranja. + Dobavite za %1$s + Dobavite je + Članarina omogućava časovne, dnevne i sedmične nadogradnje, i neograničena preuzimanja svih karata. + Deo Vaše članarine će biti poslat korisnicima OSM-a. Članarina ostaje ista. + Prilog OSM zajednici + Članarina se naplaćuje po odabranom periodu. Možete je otkazati na Gugl pleju kad god poželite. + Molimo, unesite ime novog filtera koji će biti pridodat jezičku „Kategorije“. + Novi filter + Izbriši filter + Sačuvaj filter + Primeni filtere + Filteri + Proizvoljna pretraga + Napravi proizvoljni filter + Izabrane kategorije + Potkategorije + Izmeni kategorije + Preslovi imena + Preslovi ako %1$s prevod ne postoji + Ukucajte grad, adresu, ime tačke od interesa + Celovitost površi puta + Obrazac boja linija izohipsi + Tamna smeđa + Svetla smeđa + Prikazuj božićne tačke od interesa\? + U čekanju Božića i Nove Godine, možete izabrati prikaz tačaka od interesa vezanih za Božić: Badnjake, jelke, trgovine, itd. + Božićne tačke + Filter: Nema zapisa dok se ne dostigne ova tačnost. + Najmanja tačnost beleženja + Filter: Postavi minimalnu udaljenost od tačke da bi se beležila nove tačke. + Najmanji pomeraj beleženja + Filter: Nema zapisa tačaka ispod ove brzine. + Najmanja brzina beleženja + Obrazac boja linija izohipsi + Nema podataka + Snimaj + Snimljeno + Putovanje + Pauzirano + Pauziraj + Nastavi + Nastavi + Obaveštenja + Prikazuj obaveštenje koje omogućava pokretanje snimanja putovanja. + Uključi brzo snimanje + Izgled + Dodaj još… + Takođe, možete dodati fajlove staza u fasciklu + Još uvek nemate nijednu datoteku staza + Proračun putanje + Otpremi tačku od interesa + Odredi %1$d izlaz i idi + Grad ili regija + Članci Vikipedije o okolini + Otpremajte svoju belešku OSM-a anonimno ili korišćenjem svog naloga na OpenStreetMap.org-u. + Dozvoli autoputeve. + Počni novi odsečak posle razmaka od 6 minuta, novu putanju posle razmaka od 2 sata, ili novi fajl posle dužeg razmaka ako je dan izmenjen. + Samopodeli snimke posle razmaka + Pomorske izobate + Prikazuj tačke i izobate. + Koristi podatke o visini + Izaberite visinsko odstupanje + Ukupno rastojanje + Vreme + Prosečna visina + Raspon visine + Uzlazan + Silazan + Uspon puta + Način vožnje + Putanje + Premesti + Ne mogu da premestim fajl. + Izaberite fasciklu GPX fajlova + Boja + Vreme dolaska + Vreme polaska + Najviši + Vremenski raspon + Vreme kretanja + Prosečna brzina + Najveća brzina + Putanja + Istaknute tačke na ovom putu + Broj skretanja na ovom putu + Da li ste sigurni da želite da izbrišete %1$d tačku(i)\? + Tačka(e) izbrisana(e). + Dodaj novu fasciklu + Nagib + Omiljeni teren: pavno ili brdovito. + Preferiraj sporedne + Uravnoteženo + Kraće rute + Ravno + Manje brdovito + Brdovito + Hvala Vam za kupovinu plaćenog izdanja OsmAnd-a. + Opcije parkiranja + Ne prikazuj ponude popusta iz programa i specijalne poruke o lokalnim događajima. + Ne prikazuj poruke pri pokretanju + OsmAnd prikuplja podatke o tome koje delove programa otvarate. Vaš položaj se ne šalje, niti išta što unesete u program niti pojedinosti o oblastima koje vidite, tražite ili preuzmete. + Ne šalji bezimene podatke o korišćenom programu + Automatsko + Vožnja desnom trakom + Fontovi na karti + Povrati trgovinu + Vidljivo + Raščlani na karti + Dodajte ili izmenite omiljeno + Povrati podrazumevani poredak stavki + Povratak uređivanju + Akciono dugme menja između izabranih profila. + Dodaj profil + Promeni profil aplikacije + Ne može se naći takav profil. + Svetska opšta mapa (detaljna) + Nepodržani tip + Unesite širinu vozila, neka ograničenja puta mogu biti primenjena za široka vozila. + Unesite visinu vozila, neka ograničenja puta mogu biti primenjena za visoka vozila. + Unesite težinu vozila, neka ograničenja puta mogu biti primenjena za teška vozila. + Unesite dužinu vozila, neka ograničenja puta mogu biti primenjena za duža vozila. + OsmAnd GPX nije dobro formiran, molimo obratite se timu podrške radi dalje istrage. + Uvek> + Kontrola ekrana + Isključi ekran prema sistemskom vremenu zaključavanja. + Koristi sistemsko vreme zaključavanja ekrana + Odaberite opcije paljenja ekrana (OsmAnd treba da bude iu prednjem planu kada se uređaj zaključa): + Svaka instrukcija navođenja će upaliti ekran. + Instrukcije navođenja + Onemogućeno. Zahteva \'Drži ekran upaljenim\' pod \'Tajmaut posle buđenja\'. + Pritiskanjem dugmeta za gašenje uređaja ćete upaliti ekran sa OsmAnd-om preko zaključanog ekrana. + Dugme za gašenje + Senzor blizine + Odaberite tajmaut ekrana posle buđenja. (\"%1$s\" se odnosi na tajmaut.) + Drži ekran upaljenim + Drži ekran ugašenim + Pseudo-Merkator projekcija + Jedan fajl po sličici + SQLiteDB fajl + Unesite ime za ovaj mrežni izvor karte. + Unesite ili nalepite adresu mrežnog izvora. + Izmeni mrežni izvor + Vreme isteka + Merkator projekcija + Format skladišta + Postavite minimalni i maksimalni nivo uveličavanja koji prikazuje ili učitava mrežnu kartu. + Utiče na ekran kada se koristi kao mapa ili podsloj/nadsloj. +\n +\n%1$s: Mapa je ograničena odabranim nivoom zuma. +\n +\n%2$s: su nivoi pri kojima će originalne pločice biti vidljive, a umanjenje ili uvećanje biće izvan ovih vrednosti. + Iskeširane sličice će biti ponovo preuzete posle zadatog broja minuta. Ostavite prazno da nikad ne osvežavate sličice iz ovog izvora. +\n +\nJedan dan je 1440 minuta. +\nNedelju dana je 10 080 minuta. +\nMesec dana je 43829 minuta. + Odaberite kako da skladištite preuzete sličice. + Možete izvoziti ili uvoziti brze radnje sa aplikativnim profilima. + Obriši sve\? + Da li želite bespovratno da obrišete %d brzih radnji\? + Vreme iksljučivanja ekrana + Izbriši radare + Pravno + Radarske tačke od interesa + U nekim državama ili oblastima, korišćenje aplikacija za obaveštavanje o radarima na putu je zabranjeno zakonom. +\n +\nMorate napraviti izbor ovde, u zavisnosti od zakona Vaše države. +\n +\nOdaberite %1$s i primažete obaveštenja i upozorenja o radarima i kamerama za brzinu. +\n +\nOdaberite %2$s. Svi podaci vezani za radere: upozorenja, obaveštenja, tačke od interesa će biti obrisane dok se OsmAnd ponovo ne instalira. + Drži aktivnim + Izbriši + Upozorenja za radare su zakonom zabranjene u nekim državama. + Ako je „%1$s” uključen, vreme aktivnosti će zavisiti od toga. + Podrazumevano vreme iksljučivanja ekrana + tona + metara + Prikaži ili sakrij dodatne detalje karte + Noćna karta + Dodaj mrežni izvor + Primenom ovih izmena ćete očistiti iskeširane podatke za ovaj izvor sličica + Podesi visinu plovila + Možete podesiti visinu plovila da izbegnete niske mostove. Imajte na umu da, ukoliko most može da se pomera, koristićemo njegovu visinu kada je otvoren. + Podesite visinu plovila da izbegnete niske mostove. Imajte na umu da, ukoliko most može da se pomera, koristićemo njegovu visinu kada je otvoren. + Podesite širinu plovila i izbegnite uske mostove + Prikaži ili sakrij Mapillary sloj na karti. + Odaberite dužinu vozika koja je dozvoljena na putevima. + Limit dužine + Kurs + %1$s obrisano + Ponovo pokrenite aplikaciju da biste izbrisali sve podatke brzinskih kamera. + Deinstaliraj i ponovo pokreni + Ovaj uređaj nema radare. + Roleri + Obriši najbližu odredišnu tačku + Kontrolišite nivo zumiranja na mapi pomoću dugmića za jačinu zvuka na uređaju. + Dugmići za jačinu zvuka za zumiranje + Molimo odredite ime tačke + Uklanja trenutnu odredišnu tačku. Ako je ona i završna tačka, navođenje će se zaustaviti. + Preuzmite mape Vikipedije + Informacije o tačkama od interesa potražite na Vikipediji, džepnom vanmrežnom vodiču koji ima članke o mestima i odredištima. + Dodata tačka neće biti vidljiva na mapi, pošto je odabrana grupa sakrivena, možete je pronaći u „%s“. + Enduro skuter + Skuter + Invalidska kolica + Invalidska kolica napred + Korpa + Zatvorena OSM beleška + Za nastavak odredite radne dane + Ruta između tačaka + Planiraj rutu + Dodajte stazi + Prikaži početne i krajne ikone + Izaberite širinu + Izaberite interval na kome će se prikazivati oznake sa razdaljinom ili vremenom na stazi. + Izaberite željenu opciju podele: po vremenu ili po udaljenosti. + Prilagođen + Strelice smera + Čvrst + Poslednje izmenjena + Uvezi putanju + Otvori postojeću putanju + Kreiraj novu rutu + Izaberite fajl putanje za otvaranje. + Završeno + Zameni stazu + Sačuvaj kao novu stazu + Obrnuta ruta + Čitava staza će se preračunati koristeći odabrani profil. + Samo će sledeći segment biti preračunat koristeći odabrani profil. + Izaberite kako povezati tačke, po pravoj liniji, ili izračunajte rutu između njih kako je dole naznačeno. + Cela staza + Sledeći segment + Zatim pomoću jednog od vaših navigacionih profila prislonite stazu na najbliži dozvoljeni put da biste koristili ovu opciju. + Prag udaljenosti + Navigacijski profil + Izaberite datoteku zapisa kojoj će se dodati novi segment. + Slike na nivou ulice + Da li ste sigurni da želite da odbacite sve promene na planiranoj ruti\? + U slučaju obrnutog pravca + Sačuvaj kao novu datoteku staze + Dodaj u datoteku staze + Putanje + Putanje + Putanje + Upisuj putanju u GPX fajl + Trasa rute + Dodajte datoteke staza + Uvezite ili snimite datoteke staza + Dodajte tačku staze + Dodajte tačku staze + Snimanje putovanja + Sačuvaj kao datoteku staze + Sledi stazu + Izaberite datoteku staze radi praćenja + Izaberite datoteku staze radi praćenja ili uvoza sa vašeg uređaja. + Izaberite drugu stazu + Navodite od moje pozicije do staze + Tačka staze za navigaciju + Početak staze + Najbliža tačka + Pričvrstite na puteve + Izbrišite adresu + Dodajte adresu + Unesite adresu + Skrati pre + Skrati posle + Promenite vrstu rute pre + Promenite vrstu rute nakon + Pojednostavljena staza + Samo linija rute će biti sačuvana, a putne tačke će biti izbrisane. + Ime fajla + %s izabranih datoteka staza + REK + Evidentiranje staze će biti pauzirano kada se aplikacija ubije (preko skorašnjih programa). (Indikator rada OsmAnd-a u pozadini tada nestaje iz obaveštajne trake.) + Navedite interval evidentiranja za opšte snimanje staza (uključeno pomoću vidžeta „Snimanje putovanja“ na mapi). + Pauziraj snimanje puta + Nastavite snimanje putovanja + Sistemsko podrazumevana + Svi naredni segmenti + Prethodni segment + Svi prethodni segmenti + Samo izabrani segment će se ponovo izračunati pomoću izabranog profila. + Svi naredni segmenti će se ponovo izračunati pomoću izabranog profila. + Svi prethodni segmenti će se ponovo izračunati pomoću izabranog profila. + Otvori sačuvanu + je sačuvano + Dodajte bar dve tačke. + Ponovi + • Ažurirano Funkcija planiranja rute: omogućava upotrebu različitih tipova navigacije po segmentu i uključivanje staza +\n +\n • Novi meni Izgled za staze: odaberite boju, debljinu, strelice smera prikaza, ikone početka i završetka +\n +\n • Poboljšana vidljivost biciklističkih čvorova. +\n +\n • Staze se sada mogu dodirnuti, imaju kontekstualni meni sa osnovnim informacijama. +\n +\n • Poboljšani algoritmi pretrage +\n +\n • Poboljšane opcije praćenja staza u Navigaciji +\n +\n • Rešeni problemi sa uvozom / izvozom podešavanja profila +\n +\n + Poslednja izmena + Ime: Z - A + Ime: A - Z + Ikone za početak i završetak + Hvala vam što ste kupili „Konturne linije“ + Članarina se naplaćuje po izabranom periodu. Otkažite je u AppGallery u bilo kom trenutku. + Uplata će biti naplaćena sa vašeg računa AppGallery pri potvrdi kupovine. +\n +\nČlanarina se automatski obnavlja ukoliko nije otkazana pre datuma obnove. Vaš račun će biti zadužen za period obnove (mesec / tri meseca / godina) samo na datum obnove. +\n +\nČlanarinama možete upravljati i otkazati ih tako što ćete otići u podešavanja aplikacije AppGallery. + Izbegavajte pešačke staze + Izbegavajte pešačke staze + Razvoj + OsmAnd uživo podaci + OsmAnd uživo podaci + Dvofazno usmeravanje za automobilsku navigaciju. + Preračunava samo početni deo rute, korisno za duga putovanja. + Razvoj matičnog javnog prevoza + Prebacite se na Java (bezbedan) proračun rutiranja javnog prevoza + Prijavite se pomoću OAut-a da biste koristili osm uređivačke funkcije + Prijavite se preko OAut-a + Obrišite OpenStritMap OAut token + Odjavljen + Datoteka je već uvezena u OsmAnd + Koristite dvofazni algoritam usmeravanja A* + Grafikon + %1$s podaci dostupni samo na putevima, izračunajte rutu koristeći „Ruta između tačaka“ da biste videli grafike. + Molimo sačekajte. +\nGrafikon će biti dostupan nakon ponovnog izračunavanja putanje. + Lokalne mape + %1$s — %2$s + Razmak + Ameniti + Special + Prevoz + Usluga + Simboli + Sport + Hitna pomoć + Putovanje + Dodajte najmanje dve tačke + Ova podešavanja dodataka su globalna i primenjuju se na sve profile + Prijavite se na OpenStreetMap.org + Prijavite se na OpenStreetMap.org + Prijavite se pomoću OpenStritMap + Pogledajte sve Vaše još uvek neotpremljene izmene ili OSM beleške u %1$s. Otpremljene tačke se ne više prikazuju. + Morate se prijaviti da biste otpremili nove ili izmenjene promene. +\n +\nMožete se prijaviti koristeći bezbedan OAut metod ili koristite svoje korisničko ime i lozinku. + Koristite korisničko ime i lozinku + Nalog + Lozinka + Prijavite se + Tačke putanje + Pokreni + Slika + Snimak + Putanje + Omiljeno + Moja mesta + Dodaj u „Omiljeno“ + Dodaj + Adresa + Dodato + Uređeno + Izbrisano + Stvarajte ili menjajte OSM tačke od interesa, otvorite ili komentarišite OSM beleške i doprinosite snimanjem GPX fajlova. + Stvarajte ili menjajte predmete OSM-a + Oznaka + Radnje + Izmenite pretragu. + GPX fajl sa koordinatama i podacima svih beležaka. + GPX fajl sa koordinatama i podacima izabrane beleške. + Dodatne radnje + Otvara se u + Otvara se u + Zatvara se u + Otvoreno do + Otvoreno od + Sve tačke grupe + Čitati članak + Čitati ceo članak + Bez vremenskog ograničenja + Pokupiti do + ostavljena u + Ovde su: + Bez imena + Oznake tačaka od interesa + Otvara se sutra u + OSM beleške + Svi podaci + Izvezi kao OSM beleške, tačke od interesa ili oba. + Odaberite tip fajla + OSC fajl + GPX fajl + OSC - pogodno za izvoz u OSM. + GPX - pogodno za izvoz u JOSM i druge OSM uređivače. + Prikaži ili sakrij OSM beleške na karti. + Prikaži zatvorene beleške + Pomeri odredište gore i napravi ga + Dodaje prvo stajanje + Dodaje prolazno stajanje + Trenutna + Označi ovo kao tačku polaska + Karta uvezena + Greška pri uvoženju karte + Unesite ime fajla. + Blizu + Prevoz u blizini + Opciono ime tačke + N + S + W + E + DD°MM.MMM′ + DD°MM.MMMM′ + DD.DDDDD° + DD.DDDDDD° + DD°MM′SS″ + Unesite geografsku širinu i dužinu + Unesite geografsku širinu + Unesite geografsku dužinu + Prvo najbliži + Prvo najudaljeniji + Sportovi na brzacima + Grupa izbrisana + Očisti sve prolazne tačke + Ukupno + Dodajte sve prolazne tačke sa putanje, ili odaberite zasebne kategorije. + Nije nađeno ništa: + Prolazne tačke uklonjene sa oznaka karte + Putovanje + Koristi dve cifre za geografsku dužinu + Rezultat + Sadržaji + Istraži + Zabeleženi članci + Čitaj + Pretraga države, grada ili pokrajine + Članak uklonjen + Wikivoyage + Turistički vodiči + Skidaj slike + Obriši istorijat pretraga + Keš slika + Strana dostupna samo dok ste na mreži. Otvoriti je u veb čitaču\? + Knjiga putovanja + Odaberite knjigu putovanja + Samo bežično + Slike iz članaka mogu biti skidane zbog pristupa i van mreže. +\nUvek dostupno iz „Istraži“ → „Opcije“. + Preuzimaj slike + Samo na bežičnoj mreži + Da + Ne + Odaberite pogodne stavke + Kupite jedno od sledećeg da možete da čitate turističke članke van mreže: + Odaberite plan + Otključaj sve OsmAnd funkcionalnosti + Karte izohipsi i reljefa + Vikipedija van mreže + Neograničeno preuzimanja + Wikivoyage van mreže + Kupi - %1$s + Kada se jednom kupi, biće Vam zauvek dostupno. + Jednokratno plaćanje + Kupovina iz aplikacije + Satna ažuriranja karata + Mesečna ažuriranja karata + Vodiči za najzanimljivija mesta na planeti, unutar OsmAnd-a, bez potrebe za internet konekcijom. + Turistički vodiči + Dobrodošli u otvorenu betu + Uzmi neograničeni pristup + Započni uređivanje + Možete i trebali biste da menjate bilo koji članak na Wikivoyage-u, a i nadamo se da hoćete. Delite Vaše znanje, iskustvo, talenat i pažnju. + Turistički vodiči su trenutno bazirani na Wikivoyage-u. Testirajte sve funkcionalnosti besplatno. + Besplatni svetski turistički vodič koji svako može da uređuje. + Preuzmi fajl + Nadogradnja dostupna + Preuzmite Wikivoyage turističke vodiče da biste čitali članke o mestima po celom svetu bez internet konekcije. + Dostupni su novi Wikivoyage podaci, ažurirajte ih da biste uživali. + Plaćeni dodatak + Plaćena aplikacija + Popularna odredišta + OsmAnd tim + Karte koje Vam trebaju + Na osnovu članaka koji ste zabeležili, predloženo je da skinete sledeće karte: + Obnovite članarinu da nastavite da koristite sve ove funkcionalnosti: + Otkazali ste članarinu za OsmAnd uživo + Prikaži slike + Ponovo pokreni aplikaciju + Preuzmi sve + Čitaj Vikipediju van mreže + Kako da otvorim vezu\? + Da biste čitali članke sa Vikipedije i Wikivoyage-a, kupite članarinu na OsmAnd uživo. + Veza će biti otvorena u veb čitaču. + Otvori Vikipedija vezu sa internetom + Stil opšte namene. Gusti gradovi su prikazani jasno. Prikazuje izohipse, putanje, kvalitet podloge, zabrane pristupa, blokade puteva, iscrtavanje puteva po SAC alpskoj skali, objekti za sportove na brzacima. + Stil za turističke ture sa visokim kontrastom i maksimumom detalja. Uključuje sve opcije OsmAnd podrazumevanog stila, pritom prikazujući što je više detalja moguće, konkretno puteve, staze i ostale načine putovanja. Jasna vizuelna razlika između različitih tipova puteva, tako da podseća na turističke atlase. Pogodan za dnevni i noćni režim rada, kao i korišćenje napolju. + Stari „Mapnik“ stil iscrtavanja. Slične boje kao u „Mapnik“ stilu. + Za šetanje, pešačenje, treking i biciklizam u prirodi. Dobra čitljivost u uslovima spoljne osvetljenosti. Dobar kontrast za puteve i prirodne objekte, različiti tipovi putanja, izohipse sa naprednim podešavanjima, više detalja. Opcija integriteta podloge Vam omogućava da razlikujete puteve sa različitim kvalitetima podloga. Nema noćnog režima. + Jednostavan stil za vožnju. Prijatan za oči noću, izohipse, narandžasti putevi sa dobrim kontrastom, zamagljuje manje bitne objekte sa karte. + Za skijanje. Prikazuje staze, ski liftova, kros-kantri staze itd. Zamagljuje manje bitne objekte sa karte. + Za rečno i morsko navođenje. Prikazuje bove, svetionike, rečne i morske puteve, luke, pomorske oznake i izobate. + Za vožnju van puta, baziran na „Topo“ stilu i pogodan za upotrebu sa zelenim satelitskim slikama kao donji sloj. Smanjena debljina glavnog puta, povećana debljina putanja, staza, biciklističkih i drugih puteva. + Za vožnju motornih sanki sa namenskim putevima i stazama. + Prikaži ceo opis + Sakrij ceo opis + Zabeleži + Izmena podrazumevanog stila za bolji kontrast pešačkih i biciklističkih puteva. Koristi stare Mapnik boje. + Nabavite OsmAnd uživo da otključate ove mogućnosti: dnevna ažuriranja karti sa neograničenim brojem skidanja, svi i plaćeni i besplatni dodaci, Vikipedija, Wikivoyage i još mnogo toga. + Izmeni radnje + Pošaljite snimak ekrana ovog obaveštenja na support@osmand.net + Dodali ste %1$s tačke(i). Unesite ime fajl i kliknite „Snimi“. + Snimi kao putanju + Dodaj tačku + Izmeni tačku + Tačka %1$s obrisana + Svet + Pošalji upit pretrage\? + Poslaćemo upit Vaše pretrage: „%1$s“, kao i Vašu lokaciju. +\n +\n Ne sakupljamo lične podatke, samo nam trebaju podaci o pretragama da bismo poboljšavali algoritam pretrage. + Povećaj poluprečnik pretrage na %1$s + Nema rezultata u pretrazi\? +\nJavite nam + Ne mogu da nađem čvor ili put. + Hvala za povratnu informaciju + Odobrite OsmAnd-u pristup lokaciji da nastavite. + Postoje još prevoza na ovoj stanici. + Zadrži odobrene markere na mapi + Markeri dodati kao grupa Omiljenih ili GPIks prolaznih tačaka označeni kao Odobreni ostaće na mapi. Ako grupa nije aktivna, markeri će se izgubiće sa mape. + Povrati + Prvo odaberite grad/naselje/lokalitet + Traži ulicu + Aplikacija treće strane + Izmeni oznaku karte + Obriši oznaku „%s“ sa karte\? + Donacije pomažu finansiranje OSM kartografa. + Period plaćanja: + %1$.2f %2$s + Godišnje obnavljanje + Kvartalno obnavljanje + Mesečno obnavljanje + Trenutna članarina + Uštedite %1$s + %1$.2f %2$s / mesečno + %1$s / mesečno + godišnje + Svaka 3 meseca + Mesečno + Paketi & cenovnik + Omogućio OsmAnd + Članarine + Dolazak u %1$s + Usputna odredišta + Glasovne najave + Odaberite fajl putanje za praćenje + Simuliraj navođenje + Prikaži uz put + Odaberite put koji biste izbegli u navođenju, ili na mapi ili sa liste ispod: + Javni saobraćaj + Izračunavanje puta… + Tačke od interesa (POI) + Put + pešice + presedanja + Usputne tačke + Postavi početnu tačku + Dodaj usputnu stanicu + Postavi odredište + Visina + Širina + Srednje + Dobro + Izvrsno + Presovano + Fini šljunak + Šljunak + Drvo + Metal + Kamen + Kamenčići + Popločano kamenje + Kaldrma + Kaldrma + Beton + Asfaltirano + Asfalt + Sneg + So + Led + Blato + Prašina + Zemlja + Popločano + Trava + Pesak + Neasfaltirano + Odaberi tačku polaska + Dodaj tačku polaska + Dodaj polazište i odredište + %1$d skidanja + Dodaj prolaznu tačku + Navođenje javnim prevozom je trenutno u beta testiranju, očekujte greške i nepravilnosti. + Pročitajte kako OsmAnd izračunava puteve na našem blogu. + Dužina vrednosti „%s“ + Skratite veličinu oznake „%s“ na manje od 255 karaktera. + Peške + Izbegavaj tipove prevoza… + %s režim + Odaberite tipove javnog prevoza koje treba izbegavati: + Izmeni u čemu se meri azimut. + Jedinice za uglove + Miliradijani + Stepeni + Izbegavanje trajekata + Bez trajekata + Izbegavanje podzemne i lake železnice + Bez podzemne + Izbegavanje vozova + Bez vozova + Izbegavanje deljenog taksija + Bez deljenih taksija + Izbegavanje autobusa i trolejbusa + Bez autobusa + Izbegavanje tramvaja + Bez tramvaja + Pošalji dnevnik + Tip prevoza + Izračunaj put pešaka + Pokušajte sa promenom postavki. + Pokušajte navođenje za pešake. + Nažalost, OsmAnd ne može da nađe put koji odgovara Vašim postavkama. + Put pešice je približno %1$s i možda može biti brži nego sa javnim prevozom + Nemoj premeštati + Premesti karte + %1$d fajlova (%2$s) postoje na prethodnoj lokaciji \'%3$s\'. + Ne mogu da kopiram %1$d fajlova (%2$s). + Kopirano %1$d fajlova (%2$s). + Pomereno %1$d fajlova (%2$s). + Spravica za koordinate + Tražim GPS + Aplikativni profili + Odaberite profile vidljive iz aplikacije. + Rutiranje treće strane + Specijalno rutiranje + Proizvoljni profil rutiranja + OsmAnd rutiranje + BRouter (ne treba internet) + Prava linija + Geokodiranje + Avion, paraglajding + Brod, veslanje, jedrenje + Tipovi javnog prevoza + Hodanje, šetanje, trčanje + Planinarski bicikl, moped, konj + Kola, kamion, motor + Odaberite tip navođenja + Bazirajte Vaš proizvoljni profil na jednom od podrazumevanih profila, ovim se određuju osnovne postavke kao što su podrazuemvana vidljivost spravica ili jedinice brzine i daljine. Ovo su podrazumevani aplikativni profili, zajedno sa primerima proizvoljnih profila kojima se može proširiti: + Odaberite početni profil + Da li ste sigurni da želite da obrišete profil „%s” + Obriši profil + Prvo snimite izmene na profilu + Snimite izmene + Ne možete obrisati OsmAnd osnovni profil + Već postoji profil sa ovim imenom + Ime već postoji + Prvo morate uneti ime profila. + Unesite ime profila + Odaberite tip navođenja za novi aplikativni profil + Odaberite tip navođenja + Osnovni profil + Tip: %s + Skijanje + Korisnički režim, izveden iz: %s + Režim: %s + Odaberite ikonicu + Sakrij lenjir kompas + Prikaži lenjir kompas + Skijanje + Skijanje + Možete dodati Vašu sopstvenu izmenjenu verziju fajla routing.xml u ..osmand/routing + Helikopter + Konj + Podzemna + Autobus + Taksi + Tip navođenja + Ime profila + Dozvoli + Ne, hvala + Odaberite koje podatke delite + Privatnost i bezbednost + Pritisnite na „Dozvoli“ ako se slažete sa našom %1$s + Pomozite nam da razumemo popularnost OsmAnd funkcionalnosti. + Pomozite nam da razumemo popularnost karti država i regija. + Definišite koje podatke dozvoljavate OsmAnd-u da deli. + Preuzeti podaci + Posećeni ekrani + Preuzete karte + Odaberite koje tipove podataka želite da delite: + Dozvoli OsmAnd aplikaciji da sakuplja i obrađuje anonimne podatke o korišćenju aplikacije. Ne sakupljamo podatke o nijednoj lokaciji na karti koju posetite ili pogledate. +\n +\nPromenite Vaš izbor u svakom trenutku sa ’Postavke’ → ’Privatnost i Bezbednost’. + Pomozite nam da poboljšamo OsmAnd + Politika privatnosti + Oceni + Podeliti Vaše iskustvo sa nama i oceniti naš rad na Google Play prodavnici. + Ikonica + Magenta + OsmAnd servis preuzimanja + Pritisnite ponovo da promenite orijentaciju karte + Poslednje izvršavanje aplikacije nije uspelo. Pomozite nam da poboljšamo OsmAnd tako što ćete podeliti poruku o grešci sa nama. + Rušenje + Novi profil + Postavi min/maks brzinu + Promeni podešavanja podrazumevane brzine + Podrazumevana brzina + Maks. brzina + Min. brzina + Skuter + Monocikl + Lično prevozno sredstvo + Horizontalna preciznost: %s + Horizontalna preciznost: %1$s, vertikalna: %2$s + • Aplikativni profili: kreiranje proizvoljnih profila za sopstvene potrebe, sa proizvoljnom ikonicom i bojom +\n +\n • Postavi proizvoljne min/maks brzine na podrazumevane profile +\n +\n • Dodata spravica za trenutne koordinate +\n +\n • Dodata opcija da se prikazuje kompas i lenjir za prečnik na karti +\n +\n • Popravljeno pozadinsko logovanje putanja +\n +\n • Unapređeno pozadinsko preuzimanje karata +\n +\n • Vraćena opcija \'Uključivanje ekrana\' +\n +\n • Popravljen izbor jezika na Vikipediji +\n +\n • Ispravljeno ponašanje dugmeta za kompas prilikom navođenja +\n +\n • Ostale ispravke grešaka +\n +\n + NLO + Broj izmena + Odaberite gornju granicu broja izmena + Odaberite postavke navođenja za profil + Odaberite opcije ekrana za profil + Odaberite opcije mape za profil + Profil zadržava svoja podešavanja + Podesi profil + Van puta + Mahanjem rukom preko ekrana uključiće te ekran. + Koristi senzor blizine + Podesite koliko dugo ekran treba da bude upaljen. + Probudi se kod skretanja + Putanja %s sačuvana + Otvori putanju + %s je sačuvan + Čvrstina podloge + Mekana + Uglavnom mekana + Uglavnom čvrsta + Čvrsta (nepopločana) + Čvrsta (popločana) + Snežni i zaleđeni kolovozi + Zaleđeni kolovoz + Snežni kolovoz + Uključite bar jedan aplikativni profil da koristite ovo podešavanje. + Uključi u obzir i privremena ograničenja + Prikaži zone niske emisije zagađenja + Prikaži zone niske emisije zagađenja. Ne utiče na rutiranje. + Kamp prikolica (RV) + Kamper + Spoj razmake + Podrazumevano + Kamion dostave + Vagon + %1$s • Uštedi %2$s + Poništi članarinu + onda %1$s + Omogućava zapis položaja ostavljenog vozila i koliko je vremena preostalo. +\n I vreme i položaj su vidljivi na komandnoj tabli, a i kao spravica na karti. Može se dodati alarm na Android kalendaru. + Položaj parkiranja + Pravite audio/video beleške za vreme putovanja, ili kroz dugme na karti ili kroz kontekstni meni položaja. + Zabeleške zvuka i snimaka + Uključivanjem ovog pogleda se menja stil mape na „Turistički pregled“, koji je specijalni visokodetaljni pregled za turiste i profesionalne vozače. +\n +\nOvaj pogled nudi, na svakom nivou zuma, najveću količinu turističkih detalja na podacima karte (posebno puteve, putanje, staze i znakove za orijentaciju). +\n +\nTakođe i jasno predstavlja sve vrste puteva po boji, tako da nikad ne bude zabune, što je korisno kada se npr. voze veća vozila. +\n +\nA nudi se i posebne turističke opcije kao što su prikaz biciklističkih ili planinskih ruta. +\n +\nNije potrebno skidanje posebne karte, pogled se pravi od standardnih karti. +\n +\nOvaj pogled se može isključiti tako što se ili ovde deaktivira, ili tako što se, po želji, promeni \"Stil karte\" pod \"Podesi kartu\". + Turistički pregled karte + Ovaj dodatak omogućava prikaz sloja izohipsi i reljefa iznad standardnih OsmAnd karti. Ovu funkcionalnost će najviše znati da cene sportisti, planinari, trekeri i svako koga zanima reljefna struktura predela (primetite da su izohipse i reljefni podaci odvojeni, posebna skidanja za njih su dostupna kada se ovaj dodatak aktivira.) +\n +\nGlobalni podaci (između 70° severno i 70° južno) se bazirani na SRTM (Shuttle Radar Topography Mission) i ASTER (Advanced Spaceborne Thermal Emission and Reflection Radiometer), instrumentima za slikanje na vodećem NASA-inom satelitu Terra. ASTER je zajednički napor NASA-e, japanskog ministarstva ekonomije, trgovine i industrije (METI), i japanskog svemirskog sistema (J-spacesystems). + Izohipse + Ovaj dodatak omogućava prikaz sloja izohipsi i reljefa iznad standardnih OsmAnd karti. Ovu funkcionalnost će najviše znati da cene sportisti, planinari, trekeri i svako koga zanima reljefna struktura predela. +\n +\nGlobalni podaci (između 70° severno i 70° južno) se bazirani na SRTM (Shuttle Radar Topography Mission) i ASTER (Advanced Spaceborne Thermal Emission and Reflection Radiometer), instrumentima za slikanje na vodećem NASA-inom satelitu Terra. ASTER je zajednički napor NASA-e, japanskog ministarstva ekonomije, trgovine i industrije (METI), i japanskog svemirskog sistema (J-spacesystems). + OsmAnd dodatak za linije izohipsi van mreže + Dodatak za izohipse + Ovaj dodatak aktivira funkcionalnost snimanja i čuvanja Vaših putanja kada se pritisne GPX spravica za beleženje na karti, a može i da automatski beleži sve Vaše puteve kojima ste navođeni u GPX fajl. +\n +\nSnimljene putanje se mogu deliti sa prijateljima ili slati na OSM. Sportisti mogu da koriste snimljene putanje da prate svoje treninge. Direktno u OsmAnd-u može da se radi osnovna analiza putanja, kao što su računanje brzine kruga, prosečna brzina itd. Naravno, putanje se dalje mogu analizirati u drugim specijalnim alatima za analizu. + Snimanje putovanja + Pristupite različitim tipovima mrežnih (takozvanim sličicama ili rasterskim) karata, od predefinisanih OSM sličica (kao što je Mapnik) do satelitskih slika i slojevima sa uskom specijalizacijom, kao što su meteorološke, klimatske, geološke karte, osenčeni slojevi, itd. +\n +\nSvaka od ovih mapa može biti korišćena ili kao glavna (osnovna) mapa koja se prikazuje, ili kao sloj iznad ili ispod osnovne karte. Određeni elementi OsmAnd vektorskih karata se mogu sakriti preko „Podesi kartu“ menija. +\n +\nPreuzmite karte sa sličicama direktno sa intereneta, ili ih pripremite za oflajn upotrebu (ručno prekopirati u OsmAnd fasciklu sa podacima), i to kao SQLite baza podataka koju može da napravi veliki broj alata treće strane. + Mrežne karte + Tačke putanje + Odsečci putanje + %1$s prvih %2$s + %1$s prvih %2$s + Nabavite %1$d %2$s za %3$s niže. + Besplatno + Tri meseca + Godina + Godine + Godina + Meseci + Meseca + Mesec + Nedelja + Nedelje + Nedelja + Dana + Dana + Dan + Parametri puta + Konfiguriši parametre puta + Upozorenja na ekranu + Glasovna obaveštenja + Instrukcije i obaveštenja prilikom navođenja + Glasovna obaveštenja su dešavaju samo za vreme navođenja. + Parametri vozika + Težina, visina, dužina, brzina + Ostalo + Karta za vreme navođenja + Karta za vreme navođenja + Uključivanje ekrana + Kopiraj iz drugog profila + Osmand postavke + Utiče na celu aplikaciju + Upravljaj aplikativnim profilima… + Napravi, uvezi, izmeni profile + Resetuj na podrazumevano + Jezik i ulaz + Upozorenja se prikazuju u donjem levom uglu ekrana za vreme navođenja. + Tema aplikacije, jedinice, regija + Podesi navođenje + Instalirani dodaci + Izgled karte + Izgled karte + Merne jedinice & formatiranja + Tajmaut posle buđenja + Postavke za kreiranje puta u označenom profilu „%1$s”. + Prikaži kartu na zaključanom ekranu za vreme navođenja. + Analitike + Poruka po otvaranju + Primeni na sve profile + Primeni samo na „%1$s” + Odbaci promenu + Promeni postavke + Ova postavka je podrazumevano odabrana za profile: %s + Označeni format će biti primenjen kroz celu aplikaciju. + Otvoreni kod lokacije (OLC) + MGRS + UTM Standard + Primer + OsmAnd koristi MGRS, koji je sličan UTM NATO formatu. + OsmAnd koristi UTM Standard format koji je sličan, ali nije istovetan kao UTM NATO format. + Postavke za profil: + Konfiguriši profil + Zameni profil + Aplikativni profil promenjen na „%s“ + Logcat bafer + Postavke dodataka + Podrazumevano + Preuzmi detaljnu kartu mesta %s, da vidite ovu oblast. + Težina staze + Nedefinisano + Ekstremna + Slobodna vožnja + Ekspertska + Napredna + Srednja + Laka + Početnička + Tip staze + Nordijska + Nizbrdo + Skitura + PisteVeza + Pešačenje + Sanke + Saonice + Zimski park + Promeni fasciklu skladišta + Interno skladište za OsmAnd (skriveno od korisnika i drugih aplikacija). + Premesti na novo odredište + Promeni OsmAnd fasciklu sa podacima\? + Umetni putanju do fascikle sa OsmAnd podacima + Fascikla… + Unesite putanju do fascikle + %1$s GB slobodno (od %2$s GB) + %1$s • %2$s + Premesti OsmAnd fajlove podataka na novo odredište\? +\n%1$s > %2$s + Izbegavaj određene puteve i tipove puteva + Veza + Vazdušni put + Uporedno + %1$s kB + %1$s MB + %1$s GB + %1$s TB + Karte + Sličice + Korišćenje OsmAnd-a + Izračunaj + Snimaj staze u podfascikle po danu snimanja (npr. 2018-01-01). + Snimaj staze u dnevnim fasciklama + Snimaj staze u \'rec\' fascikli + Staze mogu biti skladištene u \'rec\' fascikli, ili u mesečnim ili dnevnim fasciklama. + Skladišni direktorijum staza + Da li ste sigurni da želite da ažurirate sve (%1$d) karte\? + Ažuriraj sve karte + Prednost neasfaltiranim putevima. + Prednost neasfaltiranim putevima + Izohipse i reljef + Iskorišćeno %1$s kB + Iskorišćeno %1$s MB + Iskorišćeno %1$s GB + Iskorišćeno %1$s TB + • Ažurirane postavke aplikacija i profila. Zgodno ređanje postavki po njihovom tipu, i mogućnosti da menjaju svaki profil +\n +\n • Novi dijalog za preuzimanje karti koji preporučuje karte za preuzimanje dok se razgleda karta +\n +\n • Popravke za tamnu temu +\n +\n • Ispravljeno nekoliko grešaka u navigaciji svuda po svetu +\n +\n • Ažurirana osnovna karta sa detaljnijom mrežom puteva +\n +\n • Ppravljene poplavljene oblasti svuda po svetu +\n +\n • Ski navigacija: dodati profili nadmorske visine i kompleksnost staza prilikom gledanja detalja staza +\n +\n • Ostale ispravke grešaka +\n +\n + Preferira neasfaltirane puteve ispred asfaltriranih za rutiranje. + Preferiraj neasfaltirane puteve + Deljene + Možete da primenite ovu izmenu na sve ili na samo trenutno odabranom profilu. + Dugme koje prikazuje ili sakriva reljef na karti. + Dugme koje prikazuje ili sakriva linije izohipsi na karti. + OSM izmene + Ne mogu da pokrenem motor za sintetizovanje glasa. + %1$s uvezen. + %1$s greška uvoza: %2$s + Dodajte profil tako što otvorite njegov fajl u OsmAnd-u. + Uvoz profila + Ne mogu da izvezem profil. + ’%1$s’ već postoji. Prebrisati ga\? + OsmAnd profil: %1$s + Izvezi profil + Početna tačka + Zameni %1$s i %2$s + Belo + Procenjuje vreme stizanja kod nepoznatih tipova puteva, ograničava brzinu na svim putevima (može da promeni rutu) + Prazno ime datoteke + Staza sačuvana + Povrati + Očisti %1$s\? + Novi dodatak dodat + Isključi + Profili dodati od strane dodatka + Dodati profili + Ove karte su potrebne za rad ovog dodatka. + Predložene karte + Kontroliše iskačuće prozore, dijaloge i obaveštenja. + Dijalozi i obaveštenja + Dijalog preuzimanja karte + Spoji segmente + Prikaži čvorove mreže biciklističkih puteva + Čvorovi mreže + Izabrani profil + Izmeni listu profila + Ikonica, boja i ime + Izgled profila + Sačuvaj zaglavlje svakoj tački praćenja prilikom snimanja. + Uključi zaglavlje + Dodaj novi profil ’%1$s’\? + Lični + %1$s, %2$s + %1$s • %2$s + ’Tip navođenja’ određuje kako se put izračunava. + Izmeni profile + Podrazumevani OsmAnd profile ne mogu da se izbrišu, ali mogu da se isključe (na prethodnom ekranu), ili da se stave na dno. + Preuzimam %s + Za pustinje i ostale retko naseljene oblasti. Detaljnije. + Debelo + Odaberite boju + Glavni profil + Izbrisani profili će zauvek biti izbrisani kada kliknete ’Primeni’. + Pozicija ikonice pri mirovanje + Ikonica položaja dok se krećete + Resetuj sva podešavanja profila\? + Resetuj sva podešavanja profila na stanje posle instalacije. + Klikom na %1$s odbacuju se sve vaše promene. + %1$s: %2$s + %1$s %2$s + Ikonica koja se prikazuje u mirovanju. + Ikonica koja se prikazuje za vreme navođenja ili pomeranja. + OSM + OSM uređivanje + Korisničko ime i lozinka + Objavi + Preračunavanje puta + Foto beleške + Video beleške + Vaše OSM beleške su u %1$s. + Vaše snimljene staze su u %1$s, ili u OsmAnd fascikli. + Tačnost beleženja + Praćenje preko interneta + Dozvoljava deljenje trenutne pozicije koristeći snimanje puta. + Odaberite ikonu, boju i ime + Prijava, lozinka, uređivanje van mreže + Veličina slike, audio i video kvalitet + Navođenje, tačnost beleženja + Uvezi profil + Uvezi fajl za rutiranje + Uvezi iz fajla + Provera identiteta uspela + Zvuk školjcanja fotoaparata + Koristi sistemsku aplikaciju + Izdeljivanje snimanja + Resetuj postavke dodataka na podrazumevane vrednosti + Minimalni pomeraj + Minimalna preciznost + Minimalna brzina + Obaveštenje + Odaberite veb adreus sa sintaksom za parametre: lat={0}, lon={1}, timestamp={2}, hdop={3}, altitude={4}, speed={5}, bearing={6}. + Kapacitet + t + + Posao + Dodaj posao + Dodaj kuću + Prethodni put + Prvo postavite odredište + Dugme koje prikazuje ili sakriva odabrane putanje sa karte. + Prikazane putanje + Prikaži još + Zameni + Ukrcavanje na stajanju + Izlaz na + Tipovi puteva + Korak-po-korak + Do %1$s + Doba dana + OsmAnd uživo javni prevoz + Omogući javni prevoz na OsmAnd uživo izmenama. + Kvalitet 5 + Kvalitet 4 + Kvalitet 3 + Kvalitet 2 + Kvalitet 1 + Nedefinisano + Biciklistička staza + Staza + Stepenice + Konjička staza + Staza + Pešačka staza + Pristupni put + Ulica + Put + Državni put + Auto-put + Neprohodno + Preužasno + Užasno + Veoma loše + Loše + Upravljajte članarinom + Postoji problem sa Vašom članarinom. Kliknite na dugme da biste otvorili podešavanja pretplate za Gugle Plej i da biste popravili način plaćanja. + Članarina za OsmAnd uživo je istekla + Članarina na OsmAnd uživo je pauzirana + Članarina na OsmAnd uživo je na čekanju + Istorija markera + Pošaljite GPKS datoteku na OpenStritMap + Unesite oznake odvojene zarezom. + „Javno“ znači da je trag javno prikazan u vašim GPS tragovima i na javnim GPS tragovima, kao i na javnom spisku tragova sa vremenskim oznakama u sirovom obliku. Podaci koji se prikazuju putem API-ja ne upućuju na vašu stranicu tragova. Vremenske oznake praćenja nisu dostupne putem javnog GPS API-ja i tačke praćenja nisu hronološki poređane. + „Privatno“ znači da se trag ne pojavljuje ni na jednoj javnoj listi, ali su tačke praćenja u njemu u nehronološkom redosledu dostupne putem javnog GPS API-ja bez vremenskih oznaka. + „Moguće je identifikovati“ znači da će se trag javno prikazati u Vašim GPS tragovima i u javnim spiskovima GPS tragova, tj. drugi korisnici će moći da preuzmu neobrađeni trag i povežu ga sa Vašim korisničkim imenom. Javni podaci o vremenskim tačkama traga iz GPS API-ja koji se serviraju putem API-ja za tačke praćenja imaće referencu na vašu originalnu stranicu praćenja. + „Sledljivo“ znači da se trag ne prikazuje nigde na javnim listama, ali obrađene tačke praćenja sa vremenskim oznakama u njima (koje ne mogu biti direktno povezane sa vama) idu kroz preuzimanja sa javnog GPS API-ja. + Zatvori OSM belešku + Komentar OSM napomene + Možete se prijaviti koristeći bezbedan OAut metod ili koristiti svoje korisničko ime i lozinku. + Dodaj fotografiju + Registrujte se na +\nOpenPlaceReviews.org + Fotografije pruža projekat otvorenih podataka OpenPlaceReviews.org. Da biste postavili svoje fotografije, morate se prijaviti na veb stranicu. + Napravite novi nalog + Već imam + Pretraga + Kajak + Motorni čamac + Nije moguće otpremiti sliku, pokušajte ponovo kasnije + Izaberite sliku + Resursi + Približna veličina datoteke + Izaberite podatke za izvoz u datoteku. + Potrebno za uvoz + Uređaj ima slobodno samo %1$s. Oslobodite malo prostora ili poništite odabir nekih predmeta za izvoz. + Nema dovoljno prostora + Izaberite grupe koje će biti uvezene. + Izaberite stavke koje će biti uvezene. + Dodaj u Mapillary + Dodaj u OpenPlaceReviews + Pređite na upotrebu dev.openstreetmap.org umesto na openstreetmap.org da biste testirali otpremanje OSM beleške / POI / GPKS. + Upotrebi dev.openstreetmap.org + OsmAnd prikazuje fotografije iz nekoliko izvora: +\nOpenPlaceReviews - fotografije tačaka od interesa; +\nMapillary - slike na nivou ulice; +\nVeb / Vikimedija - fotografije tačaka od interesa navedene u OpenStreetMap podacima. + %1$s * %2$s + Laka letelica + Spoji segmente + Podeli pre + Posle podeli + Dodajte novi segment + OsmAnd profil + Korisnički profil + • Dodata je opcija za izvoz i uvoz svih podataka, uključujući podešavanja, resurse, moja mesta +\n +\n • Planiranje rute: grafikoni za segmente sa rutom, i dodata je mogućnost kreiranja i uređivanja višestrukih segmenata staza +\n +\n • Dodan je metod autentifikacije OAut za OpenStritMap, poboljšan korisnički interfejs OSM dijaloga +\n +\n • Podrška za prilagođene boje za omiljene i prolazne tačke putanja +\n +\n + Obrni sve tačke + Odaberite profil koje će se koristiti po pokretanju aplikacije. + Poslednje korišćeno + Preferira planinske pešačke puteve + Preferiraj planinske pešačke puteve + Dozvoljava potoke i odvoje + Dozvoli potoke i odvode + Dozvoljava vodene tokove koji nekad presuše + Dozvoli nestalne vodene tokove + Dodaj mrežni usmerivač + Izmeni mrežni usmerivač + Podvrsta + Vozilo + Ključ API-ja + Adresa servera + Unesite parametar + Zadrži je praznom ako nije + Adresa sa svim parametrima će izgledati ovako: + Testiraj izračunavanje puta + Vožnja automobila + Peške + Bicikl + Automobil + Kopiraj adresu + Mrežni usmerivač + Mrežni usmerivači + Fascikle + Odaberite fasciklu + Odaberite fasciklu ili dodajte novu + Prazno + Analiziraj intervale podele + Otpremi na OpenStreetMap + Uredi putanju + Preimenuj putanju + Izmeni fasciklu + sek + Prolaženje + Prilazak + Priprema unapred + Priprema + Van puta + Dolazak na odredište + Skretanje + Intervali vremena i udaljenosti + Vremena objava različitih glasovnih navođenja zavise od tipa objave, kao i trenutne i podrazumevane brzine navođenja. + Vreme objave + Započni snimanje + Prikaži putanju na karti + Invalidska kolica + Planinarenje + Pešačenje + Eketrični biciklizam + Planinski biciklizam + Drumski biciklizam + Standardni biciklizam + Teretni kamion + Kamionet + Kamion + Skuter + Trkački bicikl + MTB + Serverska greška: %1$s + Ime već postoji + Obriši ovaj mrežni usmerivač\? + Pročitaj u celosti + Izmeni opis + Obriši prolaznu tačku + Kopiraj u oznake karte + Kopiraj u omiljene + Otpremam + Otpremanje završeno + Otprema se %1$d od %2$d + Odaberite segmente + %1$s sadrži više od jednog segmenta, morate odabrati traženi deo za navođenje. + Segment %1$d + Otpremljeno %1$d od %2$d + Odaberite izmene za otpremanje + Reljef / Nagib / Konturne linije + OpenPlaceReviews je projakat pokretan od strane zajednice o javnim mestima, kao što su restorani, hoteli, prolazne tačke. Projekat sakuplja javne informacije o njima, kao što su slike, recenzije, veze ka drugim mestima kao OpenStreetMap ili Vikipedija. +\n +\nSvi podaci na OpenPlaceReview su otvoreni i dostupni svima: http://openplacereviews.org/data. +\n +\nMožete pročitati više na: http://openplacereviews.org + OpenPlaceReviews + Koristi test.openplacereviews.org + Prijavite se za OpenPlaceReviews + Voda + Zima + Motorne sanke + Jahanje + Trkanje + Planinska bicikla + Bicikla + Planinarenje + Trčanje + Pešačenje + Vožnja van puta + Motor + Automobil + Koristi ograničenja na putu trenutno aktivna na karti + Optimizovani kraći put (ušteda energije) + Odaberite razlog putovanja da dobijete kraći, brži ili sigurniji put + Ikonica trenutne lokacije će biti prilepljena za trenutno navođeni put + Ne rotiraj kartu ako je brzina ispod donje granice + Restart + Sve regije + Obriši %1$d fajlova\? + Zaustavi bez snimanja + Sačuvaj i zaustavi snimanje + Snimanje putanje zaustavljeno + Da li ste sigurni da želite da zaustavite snimanje\? +\nSve nesačuvani podaci će biti izgubljeni. + Pauzirano + Potreban je restart aplikacije da se neka podešavanja primene. + Rutiranje može da zaobiđe velike uzbrdice. + Prekidač da na karti pokaže ili sakrije spravicu sa koordinatama. + Udaljenost po kliktanju + Poslednje OpenStreetMap ažuriranje dostupno: + Ažurirano: %s + Poslednje vreme provere: %s + Učestalost ažuriranja + Provera ažuriranja karte će biti svakih nedelju dana. Sledeće vreme %1$s za %2$s. + Provera ažuriranja karte će biti svakog dana. Sledeće vreme %1$s za %2$s. + Provera ažuriranja karte će biti na svakih sat vremena. Sledeće vreme %1$s za %2$s. + Obriši ažuriranja + Da li ste sigurni da želite da obrišete %s ažuriranja uživo\? + Kupovine + Odaberite kategoriju ili dodajte novu + Snimanje će biti nastavljeno. + Kopiraj ime tačke od interesa + Prikaži/sakrij + Interval + Sakrij rezervate prirode, zaštićena područja i granice nacionalnih parkova + Prirodne granice + Putanja ne sadrži podatke o nadmorskoj visini. + Putanja ne sadrži podatke o brzini. + Odaberite drugi tip bojenja. + Vremenski interval beleženja na koji će OsmAnd da pita za podatke o trenutnoj poziciji. + Sačuvaj i nastavi + Svi nesačuvani podaci će biti izgubljeni. + Prikaži početni dijalog + Ako se isključi, snimanje će početi odmah posle kliktanja na spravicu ili na stavku menija, preskakajući dijalog potvrde. + Prilagodi liniju rute + Linija rute + Linija rute će koristiti %1$s odabran na označenom stilu karte: %2$s. + Odaberite boju za režim karte: %1$s. + Nemate nijednu kupovinu + Novi uređaj / novi nalog + Ako imate ikakvih pitanja, kontaktirajte nas na %1$s. + Ako se Vaša kupovina ne pojavi ovde, kliknite na „%1$s” ili kontaktirajte našu podršku. + Kontaktirajte podršku + Rešavanje problema + Pratite ovu vezu ako imate ikakvih problema sa kupovinom. + OsmAnd uživo + Godišnja članarina + Mesečna članarina + Tromesečna članarina + Sledeći datum naplate: %1$s + Otkazano + Obnovi članarinu + U grejs periodu + Na čekanju + Isteklo + • Ažuriranja OsmAnd uživo su premeštena u „Preuzimanja > Ažuriranja” +\n +\n • Putanje se sada mogu obojiti po visini, brzini ili nagibu. +\n +\n • Dodata opcija da se izmeni izgled linije koja se prikazuje prilikom navođenja +\n +\n • Ažuriran dijalog za „Snimanje puta” +\n +\n + Ažuriraj sve karte dodate na %1$s\? + Broj izlaza + Objavi kada se premaši + Korisnički poeni + Izlaz + \ No newline at end of file From 15fb2b677438195f5354feec6add797ee39a921c Mon Sep 17 00:00:00 2001 From: cepprice Date: Sun, 18 Apr 2021 13:23:10 +0500 Subject: [PATCH 40/86] Add drawing track border by params --- .../src/net/osmand/plus/views/Renderable.java | 39 +++++++++++++++---- .../osmand/plus/views/layers/GPXLayer.java | 1 + 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/views/Renderable.java b/OsmAnd/src/net/osmand/plus/views/Renderable.java index dd8ba55b8a..992bc06a8f 100644 --- a/OsmAnd/src/net/osmand/plus/views/Renderable.java +++ b/OsmAnd/src/net/osmand/plus/views/Renderable.java @@ -58,6 +58,7 @@ public class Renderable { public static abstract class RenderableSegment { protected static final int MIN_CULLER_ZOOM = 16; + protected static final int BORDER_TYPE_ZOOM_THRESHOLD = MapTileLayer.DEFAULT_MAX_ZOOM + MapTileLayer.OVERZOOM_IN; public List points = null; // Original list of points protected List culled = new ArrayList<>(); // Reduced/resampled list of points @@ -70,6 +71,7 @@ public class Renderable { protected Paint paint = null; // MUST be set by 'updateLocalPaint' before use protected Paint borderPaint; protected GradientScaleType scaleType = null; + protected boolean drawBorder = false; protected GpxGeometryWay geometryWay; @@ -101,6 +103,10 @@ public class Renderable { this.scaleType = type; } + public void shouldDrawBorder(boolean drawBorder) { + this.drawBorder = drawBorder; + } + public GpxGeometryWay getGeometryWay() { return geometryWay; } @@ -119,8 +125,10 @@ public class Renderable { updateLocalPaint(p); canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); if (scaleType != null) { - drawSolid(points, borderPaint, canvas, tileBox); - drawGradient(points, paint, canvas, tileBox); + if (drawBorder && zoom < BORDER_TYPE_ZOOM_THRESHOLD) { + drawSolid(points, borderPaint, canvas, tileBox); + } + drawGradient(zoom, points, paint, canvas, tileBox); } else { drawSolid(getPointsForDrawing(), paint, canvas, tileBox); } @@ -186,8 +194,9 @@ public class Renderable { } } - protected void drawGradient(List pts, Paint p, Canvas canvas, RotatedTileBox tileBox) { + protected void drawGradient(double zoom, List pts, Paint p, Canvas canvas, RotatedTileBox tileBox) { QuadRect tileBounds = tileBox.getLatLonBounds(); + boolean drawSegmentBorder = drawBorder && zoom >= BORDER_TYPE_ZOOM_THRESHOLD; Path path = new Path(); boolean recalculateLastXY = true; WptPt lastPt = pts.get(0); @@ -196,6 +205,9 @@ public class Renderable { List gradientColors = new ArrayList<>(); float gradientAngle = 0; + List paths = new ArrayList<>(); + List gradients = new ArrayList<>(); + for (int i = 1; i < pts.size(); i++) { WptPt pt = pts.get(i); WptPt nextPt = i + 1 < pts.size() ? pts.get(i + 1) : null; @@ -209,8 +221,8 @@ public class Renderable { lastX = tileBox.getPixXFromLatLon(lastPt.lat, lastPt.lon); lastY = tileBox.getPixYFromLatLon(lastPt.lat, lastPt.lon); if (!path.isEmpty()) { - p.setShader(createGradient(gradientPoints, gradientColors)); - canvas.drawPath(path, p); + paths.add(new Path(path)); + gradients.add(createGradient(gradientPoints, gradientColors)); } path.reset(); path.moveTo(lastX, lastY); @@ -241,8 +253,21 @@ public class Renderable { lastPt = pt; } if (!path.isEmpty()) { - p.setShader(createGradient(gradientPoints, gradientColors)); - canvas.drawPath(path, p); + paths.add(new Path(path)); + gradients.add(createGradient(gradientPoints, gradientColors)); + } + + if (!paths.isEmpty()) { + if (drawSegmentBorder) { + canvas.drawPath(paths.get(0), borderPaint); + } + for (int i = 0; i < paths.size(); i++) { + if (drawSegmentBorder && i + 1 < paths.size()) { + canvas.drawPath(paths.get(i + 1), borderPaint); + } + p.setShader(gradients.get(i)); + canvas.drawPath(paths.get(i), p); + } } } diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index 007e6fe700..3e040a534c 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -721,6 +721,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM Renderable.RenderableSegment renderableSegment = (Renderable.RenderableSegment) ts.renderer; renderableSegment.setBorderPaint(borderPaint); renderableSegment.setGradientScaleType(scaleType); + renderableSegment.shouldDrawBorder(true); renderableSegment.drawSegment(view.getZoom(), paint, canvas, tileBox); } } From dfdf9104c89c5edc503057fa1bcda017c476013d Mon Sep 17 00:00:00 2001 From: Hinagiku Zeppeki Date: Sun, 18 Apr 2021 09:12:06 +0000 Subject: [PATCH 41/86] Translated using Weblate (Japanese) Currently translated at 98.5% (3664 of 3717 strings) --- OsmAnd/res/values-ja/strings.xml | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/OsmAnd/res/values-ja/strings.xml b/OsmAnd/res/values-ja/strings.xml index 78c1373194..ce082a96a2 100644 --- a/OsmAnd/res/values-ja/strings.xml +++ b/OsmAnd/res/values-ja/strings.xml @@ -1173,7 +1173,7 @@ POIの更新は利用できません お気に入りのグループとして保存 目的地の設定 施設の名称 - ロード中 %1$s … + %1$sをロード中… 現在時刻 経由地点 " @@ -1446,7 +1446,7 @@ POIの更新は利用できません ナビゲーション設定 全般設定 - 有効 + 有効化 無効化 選択済み 選択解除 @@ -3172,7 +3172,7 @@ POIの更新は利用できません 横並び(サイドバイサイド)形式 索道(リフトやロープウェイなど) リフト間接続 - 計算 + 計算する OsmAndの合計使用容量 タイル マップ @@ -3703,9 +3703,9 @@ POIの更新は利用できません \n \n国の法律に基づいて、使用を望むかどうかを決定する必要があります。 \n -\n%1$sを選択すると、スピードカメラに関するアラートと警告機能を使用できます。 +\n『%1$s』を選択すると、スピードカメラに関するアラートと警告機能を使用できます。 \n -\n%2$sを選択すると、スピードカメラに関するすべてのデータ(警告、通知、POI)が、OsmAndの再インストールを行うまで削除されます。 +\n『%2$s』を選択すると、スピードカメラに関するすべてのデータ(警告、通知、POI)が、OsmAndの再インストールを行うまで削除されます。 機能を維持 アンインストール 一部の国では、スピードカメラの事前警告は法律で禁止されています。 @@ -4078,4 +4078,26 @@ POIの更新は利用できません 基本前進のみの車椅子 通常色 逆方向の場合 + 購入済みの品目はありません + 新しい端末 / 新規アカウント + ご不明な点がございましたら、%1$sまでお問い合わせください。 + 購入した品目がここに表示されない場合は、\"%1$s\"をタップするか、サポートチームにお問い合わせください。 + サポート問い合わせ先 + トラブルシューティング + 購入に関して問題が発生した場合は、このリンクを参照してください。 + OsmAnd Live + 次回請求日: %1$s + キャンセル済み + 猶予期間中 + 保留中 + 期限切れ + %1$sに追加された全マップを更新しますか? + ユーザーポイント + 出口番号 + 超過した場合の通知 + 出力 + 年毎のサブスクリプション + 月毎のサブスクリプション + 3ヶ月毎のサブスクリプション + サブスクリプションの更新 \ No newline at end of file From 95a1d5c9eedf10075f6484317bdc673cdf2d75e8 Mon Sep 17 00:00:00 2001 From: cepprice Date: Sun, 18 Apr 2021 14:26:10 +0500 Subject: [PATCH 42/86] Add clearing of tracks' cache --- .../osmand/plus/views/layers/GPXLayer.java | 68 ++++++++++++------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index 3e040a534c..7873dcbbab 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -83,6 +83,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_COLOR_ATTR; import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_WIDTH_ATTR; @@ -276,6 +277,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM } } cache.clear(); + fireUnselectedGpxFiles(selectedGPXFiles); if (!selectedGPXFiles.isEmpty()) { drawSelectedFilesSegments(canvas, tileBox, selectedGPXFiles, settings); canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); @@ -455,22 +457,26 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM private void drawDirectionArrows(Canvas canvas, RotatedTileBox tileBox, List selectedGPXFiles) { if (!tileBox.isZoomAnimated()) { + QuadRect correctedQuadRect = getCorrectedQuadRect(tileBox.getLatLonBounds()); for (SelectedGpxFile selectedGpxFile : selectedGPXFiles) { boolean showArrows = isShowArrowsForTrack(selectedGpxFile.getGpxFile()); - if (showArrows) { - QuadRect correctedQuadRect = getCorrectedQuadRect(tileBox.getLatLonBounds()); - String width = getTrackWidthName(selectedGpxFile.getGpxFile(), defaultTrackWidthPref.get()); - float trackWidth = getTrackWidth(width, defaultTrackWidth); - int trackColor = getTrackColor(selectedGpxFile.getGpxFile(), cachedColor); - int arrowColor = UiUtilities.getContrastColor(view.getApplication(), trackColor, false); - GradientScaleType scaleType = getGradientScaleType(selectedGpxFile.getGpxFile()); - List segments = scaleType != null ? - getCachedSegments(selectedGpxFile, scaleType) : selectedGpxFile.getPointsToDisplay(); - for (TrkSegment segment : segments) { - if (segment.renderer instanceof Renderable.RenderableSegment) { - ((Renderable.RenderableSegment) segment.renderer) - .drawGeometry(canvas, tileBox, correctedQuadRect, arrowColor, trackColor, trackWidth); - } + if (!showArrows) { + continue; + } + if (!QuadRect.trivialOverlap(correctedQuadRect, GPXUtilities.calculateTrackBounds(selectedGpxFile.getPointsToDisplay()))) { + continue; + } + String width = getTrackWidthName(selectedGpxFile.getGpxFile(), defaultTrackWidthPref.get()); + float trackWidth = getTrackWidth(width, defaultTrackWidth); + int trackColor = getTrackColor(selectedGpxFile.getGpxFile(), cachedColor); + int arrowColor = UiUtilities.getContrastColor(view.getApplication(), trackColor, false); + GradientScaleType scaleType = getGradientScaleType(selectedGpxFile.getGpxFile()); + List segments = scaleType != null ? + getCachedSegments(selectedGpxFile, scaleType) : selectedGpxFile.getPointsToDisplay(); + for (TrkSegment segment : segments) { + if (segment.renderer instanceof Renderable.RenderableSegment) { + ((Renderable.RenderableSegment) segment.renderer) + .drawGeometry(canvas, tileBox, correctedQuadRect, arrowColor, trackColor, trackWidth); } } } @@ -690,6 +696,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM RotatedTileBox tileBox, DrawSettings settings) { boolean visible = QuadRect.trivialOverlap(tileBox.getLatLonBounds(), GPXUtilities.calculateTrackBounds(selectedGpxFile.getPointsToDisplay())); if (!selectedGpxFile.getGpxFile().hasTrkPt() || !visible) { + segmentsCache.remove(selectedGpxFile.getGpxFile().path); return; } @@ -730,13 +737,12 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM private List getCachedSegments(SelectedGpxFile selectedGpxFile, GradientScaleType scaleType) { GPXFile gpxFile = selectedGpxFile.getGpxFile(); String path = gpxFile.path; - long modifiedTime = gpxFile.modifiedTime; CachedTrack cachedTrack = segmentsCache.get(path); if (cachedTrack == null) { - cachedTrack = new CachedTrack(view.getApplication(), modifiedTime); + cachedTrack = new CachedTrack(view.getApplication(), selectedGpxFile); segmentsCache.put(path, cachedTrack); } - return cachedTrack.getCachedSegments(selectedGpxFile, view.getZoom(), scaleType, getColorizationPalette(gpxFile, scaleType)); + return cachedTrack.getCachedSegments(view.getZoom(), scaleType, getColorizationPalette(gpxFile, scaleType)); } private float getTrackWidth(String width, float defaultTrackWidth) { @@ -1052,6 +1058,19 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM return name.replace('_', ' '); } + private void fireUnselectedGpxFiles(List selectedGpxFiles) { + Set cachedTracksPaths = segmentsCache.keySet(); + List selectedTracksPaths = new ArrayList<>(); + for (SelectedGpxFile gpx : selectedGpxFiles) { + selectedTracksPaths.add(gpx.getGpxFile().path); + } + for (String cachedPath : cachedTracksPaths) { + if (!selectedTracksPaths.contains(cachedPath)) { + cachedTracksPaths.remove(cachedPath); + } + } + } + @Override public boolean disableSingleTap() { return isInTrackAppearanceMode(); @@ -1221,20 +1240,21 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM private OsmandApplication app; - private long modifiedTime; + private final SelectedGpxFile selectedGpxFile; private final Map> cache = new HashMap<>(); - public CachedTrack(@NonNull OsmandApplication app, long modifiedTime) { + private long prevModifiedTime = -1; + + public CachedTrack(@NonNull OsmandApplication app, @NonNull SelectedGpxFile selectedGpxFile) { this.app = app; - this.modifiedTime = modifiedTime; + this.selectedGpxFile = selectedGpxFile; } - public List getCachedSegments(@NonNull SelectedGpxFile selectedGpxFile, int zoom, - @NonNull GradientScaleType scaleType, + public List getCachedSegments(int zoom, @NonNull GradientScaleType scaleType, int[] gradientPalette) { GPXFile gpxFile = selectedGpxFile.getGpxFile(); String trackId = zoom + "_" + scaleType.toString(); - if (modifiedTime == gpxFile.modifiedTime) { + if (prevModifiedTime == gpxFile.modifiedTime) { List segments = cache.get(trackId); if (segments == null) { segments = calculateGradientTrack(selectedGpxFile, zoom, scaleType, gradientPalette); @@ -1243,7 +1263,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM return segments; } else { cache.clear(); - modifiedTime = gpxFile.modifiedTime; + prevModifiedTime = gpxFile.modifiedTime; List segments = calculateGradientTrack(selectedGpxFile, zoom, scaleType, gradientPalette); cache.put(trackId, segments); return segments; From f5974f7cf3213c1f094d4c68677f569348a97bf6 Mon Sep 17 00:00:00 2001 From: cepprice Date: Sun, 18 Apr 2021 14:33:42 +0500 Subject: [PATCH 43/86] Small refactoring --- OsmAnd/src/net/osmand/plus/views/Renderable.java | 14 ++++---------- .../src/net/osmand/plus/views/layers/GPXLayer.java | 4 +--- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/views/Renderable.java b/OsmAnd/src/net/osmand/plus/views/Renderable.java index 992bc06a8f..2dbf1eb3aa 100644 --- a/OsmAnd/src/net/osmand/plus/views/Renderable.java +++ b/OsmAnd/src/net/osmand/plus/views/Renderable.java @@ -95,16 +95,10 @@ public class Renderable { } } - public void setBorderPaint(@NonNull Paint paint) { - borderPaint = paint; - } - - public void setGradientScaleType(GradientScaleType type) { - this.scaleType = type; - } - - public void shouldDrawBorder(boolean drawBorder) { - this.drawBorder = drawBorder; + public void setGradientTrackParams(GradientScaleType gradientScaleType, @NonNull Paint borderPaint, boolean shouldDrawBorder) { + this.scaleType = gradientScaleType; + this.borderPaint = borderPaint; + this.drawBorder = shouldDrawBorder; } public GpxGeometryWay getGeometryWay() { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index 7873dcbbab..7185fbc7c7 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -726,9 +726,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM updatePaints(color, width, selectedGpxFile.isRoutePoints(), currentTrack, settings, tileBox); if (ts.renderer instanceof Renderable.RenderableSegment) { Renderable.RenderableSegment renderableSegment = (Renderable.RenderableSegment) ts.renderer; - renderableSegment.setBorderPaint(borderPaint); - renderableSegment.setGradientScaleType(scaleType); - renderableSegment.shouldDrawBorder(true); + renderableSegment.setGradientTrackParams(scaleType, borderPaint, true); renderableSegment.drawSegment(view.getZoom(), paint, canvas, tileBox); } } From 2524a3f73a604f52bf04d607df9ff75ed60d896e Mon Sep 17 00:00:00 2001 From: cepprice Date: Sun, 18 Apr 2021 16:00:54 +0500 Subject: [PATCH 44/86] Review fixes --- .../net/osmand/plus/views/layers/GPXLayer.java | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index 7185fbc7c7..7ce7ec3feb 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -277,7 +277,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM } } cache.clear(); - fireUnselectedGpxFiles(selectedGPXFiles); + removeCachedUnselectedTracks(selectedGPXFiles); if (!selectedGPXFiles.isEmpty()) { drawSelectedFilesSegments(canvas, tileBox, selectedGPXFiles, settings); canvas.rotate(-tileBox.getRotate(), tileBox.getCenterPixelX(), tileBox.getCenterPixelY()); @@ -460,10 +460,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM QuadRect correctedQuadRect = getCorrectedQuadRect(tileBox.getLatLonBounds()); for (SelectedGpxFile selectedGpxFile : selectedGPXFiles) { boolean showArrows = isShowArrowsForTrack(selectedGpxFile.getGpxFile()); - if (!showArrows) { - continue; - } - if (!QuadRect.trivialOverlap(correctedQuadRect, GPXUtilities.calculateTrackBounds(selectedGpxFile.getPointsToDisplay()))) { + if (!showArrows || !QuadRect.trivialOverlap(correctedQuadRect, GPXUtilities.calculateTrackBounds(selectedGpxFile.getPointsToDisplay()))) { continue; } String width = getTrackWidthName(selectedGpxFile.getGpxFile(), defaultTrackWidthPref.get()); @@ -1056,15 +1053,15 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM return name.replace('_', ' '); } - private void fireUnselectedGpxFiles(List selectedGpxFiles) { + private void removeCachedUnselectedTracks(List selectedGpxFiles) { Set cachedTracksPaths = segmentsCache.keySet(); List selectedTracksPaths = new ArrayList<>(); for (SelectedGpxFile gpx : selectedGpxFiles) { selectedTracksPaths.add(gpx.getGpxFile().path); } - for (String cachedPath : cachedTracksPaths) { - if (!selectedTracksPaths.contains(cachedPath)) { - cachedTracksPaths.remove(cachedPath); + for (Iterator iterator = cachedTracksPaths.iterator(); iterator.hasNext();) { + if (!selectedTracksPaths.contains(iterator.next())) { + iterator.remove(); } } } From 42ea7d1d7e6e8ae574ccc4dcfe0aff5099edc5a4 Mon Sep 17 00:00:00 2001 From: Skalii Date: Sun, 18 Apr 2021 14:28:53 +0300 Subject: [PATCH 45/86] small fix --- .../plus/settings/backend/backup/PoiUiFiltersSettingsItem.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java index 3d4d1ba2c8..e0a0ced9a8 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/PoiUiFiltersSettingsItem.java @@ -155,7 +155,8 @@ public class PoiUiFiltersSettingsItem extends CollectionSettingsItem poiTypes = acceptedTypes.get(category); if (poiTypes == null) { poiTypes = new LinkedHashSet<>(); - for (PoiType poiType : category.getPoiTypes()) { + List types = category.getPoiTypes(); + for (PoiType poiType : types) { poiTypes.add(poiType.getKeyName()); } acceptedTypes.put(category, poiTypes); From 3de36e5eef773ba1acf0a70b22d6cc6a8480dc42 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Sun, 18 Apr 2021 14:47:54 +0300 Subject: [PATCH 46/86] Fix transliterate switch in map language dialog --- OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java b/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java index 08d37e556a..55d6e0219b 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/ConfigureMapMenu.java @@ -530,7 +530,7 @@ public class ConfigureMapMenu { TextView switchText = (TextView) v.findViewById(R.id.switchText); switchText.setText(activity.getString(R.string.translit_name_if_miss, txtValues[position])); SwitchCompat check = (SwitchCompat) v.findViewById(R.id.check); - check.setChecked(settings.MAP_TRANSLITERATE_NAMES.isSet() ? transliterateNames : txtIds[position].equals("en")); + check.setChecked(transliterateNames); check.setOnCheckedChangeListener(translitChangdListener); UiUtilities.setupCompoundButton(nightMode, selectedProfileColor, check); } else { @@ -548,6 +548,7 @@ public class ConfigureMapMenu { @Override public void onClick(DialogInterface dialog, int which) { selectedLanguageIndex = which; + transliterateNames = settings.MAP_TRANSLITERATE_NAMES.isSet() ? transliterateNames : txtIds[which].equals("en"); ((AlertDialog) dialog).getListView().setSelection(which); singleChoiceAdapter.notifyDataSetChanged(); } From cc152a64f94a13a75a804c1a1e4d591f8fffbd23 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Sun, 18 Apr 2021 17:40:01 +0300 Subject: [PATCH 47/86] Fix transliterate --- OsmAnd-java/src/main/java/net/osmand/data/MapObject.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java b/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java index 79b3b8a349..b38a884906 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java @@ -16,13 +16,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; import java.util.zip.GZIPInputStream; @@ -183,7 +181,8 @@ public abstract class MapObject implements Comparable { public String getName(String lang, boolean transliterate) { if (lang != null && lang.length() > 0) { if (lang.equals("en")) { - return getEnName(transliterate); + String enName = getEnName(transliterate); + return !Algorithms.isEmpty(enName) ? enName : getEnName(true); } else { // get name if (names != null) { From cf26ccc7abb203849691938742c73cb653210763 Mon Sep 17 00:00:00 2001 From: cepprice Date: Mon, 19 Apr 2021 13:08:06 +0500 Subject: [PATCH 48/86] Hide support region setting --- OsmAnd/res/layout/subscriptions_card.xml | 110 +++++++++--------- .../settings/fragments/SubscriptionsCard.java | 56 ++++----- 2 files changed, 83 insertions(+), 83 deletions(-) diff --git a/OsmAnd/res/layout/subscriptions_card.xml b/OsmAnd/res/layout/subscriptions_card.xml index 4635957ad7..d8ec722375 100644 --- a/OsmAnd/res/layout/subscriptions_card.xml +++ b/OsmAnd/res/layout/subscriptions_card.xml @@ -21,69 +21,69 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> - + + + + + + + + + + - + + + + + + + + + - + + + + + + + + - + + + + + + + + - + + + + + + + + + + + - + - + + + + + + - + - + Date: Sun, 18 Apr 2021 20:54:22 +0000 Subject: [PATCH 49/86] Translated using Weblate (Japanese) Currently translated at 98.5% (3664 of 3717 strings) --- OsmAnd/res/values-ja/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OsmAnd/res/values-ja/strings.xml b/OsmAnd/res/values-ja/strings.xml index ce082a96a2..fdca4a0e3b 100644 --- a/OsmAnd/res/values-ja/strings.xml +++ b/OsmAnd/res/values-ja/strings.xml @@ -2818,9 +2818,9 @@ POIの更新は利用できません サブスクリプション By OsmAnd プランと料金 - 月間 + 月毎 3ヶ月毎 - 年間 + 年毎 %1$s / 月 %1$.2f %2$s / 月 %1$s割引 From 73b1adb16854185df20a33f96188b5383a5274ba Mon Sep 17 00:00:00 2001 From: Felix Wiemuth Date: Mon, 19 Apr 2021 08:14:31 +0000 Subject: [PATCH 50/86] Translated using Weblate (German) Currently translated at 99.9% (3716 of 3717 strings) --- OsmAnd/res/values-de/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OsmAnd/res/values-de/strings.xml b/OsmAnd/res/values-de/strings.xml index dae511a7f4..4a199996b5 100644 --- a/OsmAnd/res/values-de/strings.xml +++ b/OsmAnd/res/values-de/strings.xml @@ -3856,7 +3856,7 @@ \nSie können sich mit der sicheren OAuth-Methode anmelden oder Ihren Benutzernamen und Ihr Passwort verwenden. Benutzername und Passwort verwenden Konto - Benutzername + Anmelden Historie der Marker GPX-Datei an OpenStreetMap senden Geben Sie durch Komma getrennte Tags ein. From 608b44f213a9ae3d660f47beba15f064c10d409c Mon Sep 17 00:00:00 2001 From: ace shadow Date: Sun, 18 Apr 2021 08:42:06 +0000 Subject: [PATCH 51/86] Translated using Weblate (Slovak) Currently translated at 100.0% (3717 of 3717 strings) --- OsmAnd/res/values-sk/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OsmAnd/res/values-sk/strings.xml b/OsmAnd/res/values-sk/strings.xml index 995426c282..77dc42838a 100644 --- a/OsmAnd/res/values-sk/strings.xml +++ b/OsmAnd/res/values-sk/strings.xml @@ -4006,7 +4006,7 @@ Zvoľte účel jazdy pre získanie kratšej, rýchlejšej alebo bezpečnejšej trasy Neotáčať mapu, ak je rýchlosť nižšia ako hranica Reštartovať - Všetky regióny + Všetky oblasti Zmazať %1$d súborov\? Zastaviť bez uloženia Uložiť a zastaviť záznam From 4a22d257216535f1edc5fa644674f6f244ac9288 Mon Sep 17 00:00:00 2001 From: Guntis Ozols Date: Sun, 18 Apr 2021 14:27:26 +0000 Subject: [PATCH 52/86] Translated using Weblate (Latvian) Currently translated at 61.3% (2279 of 3717 strings) --- OsmAnd/res/values-lv/strings.xml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/OsmAnd/res/values-lv/strings.xml b/OsmAnd/res/values-lv/strings.xml index 27ad892e50..5b04fd8c4b 100644 --- a/OsmAnd/res/values-lv/strings.xml +++ b/OsmAnd/res/values-lv/strings.xml @@ -1802,7 +1802,7 @@ failu(s)? Sākt lietot Kā lejupielādēt kartes un pamatiestatījumu lietošana Navigācija - Ceļa plānošana + Ceļojuma plānošana BUJ Biežāk uzdotie jautājumi Kartes skatīšana @@ -2162,7 +2162,6 @@ Apraksta laukumu: %1$s x %2$s Laiks Maršruta garums Lietot elevācijas datus - Ieteicamais reljefs: līdzens vai kalnains. Slīpums Berberu @@ -2415,7 +2414,7 @@ No Afganistānas līdz Zimbabvei, no Austrālijas līdz ASV, Argentīna, Brazīl Rādīt karti Maršruts aprēķināts Turp un atpakaļ - Jums vajag pievienot vismaz vienu marķieri, lai lietotu šo funkciju. + Lai lietotu šo funkciju, vajag pievienot vismaz vienu marķieri. Piezīmei nevarēja veikt izmaiņas Labot piezīmi !abot OSM piezīmi @@ -2429,7 +2428,7 @@ No Afganistānas līdz Zimbabvei, no Austrālijas līdz ASV, Argentīna, Brazīl No ledus ceļiem un brasla Izvairīties no ledus ceļiem un brasliem. Lietot pozīciju - Pievienot jūsu atrašanās vietu kā sākuma punktu maršrutam. + Pievienot patreizējo atrašanās vietu kā sākuma punktu ideālā maršruta izveidei. Mana pozīcija Finišs Plānot maršrutu @@ -3333,4 +3332,19 @@ No Afganistānas līdz Zimbabvei, no Austrālijas līdz ASV, Argentīna, Brazīl Izvēlieties lietas, ko importēt. Lietot dev.openstreetmap.org nevis openstreetmap.org OSM piezīmju/ POI / GPX augšuielādei un testēšanai. Lietot dev.openstreetmap.org + Plānot maršrutu + Pēdējo reizi labots + Importēt treku + Atvērt saglabāto treku + Veidot jaunu maršrutu + Izvēlieties treka failu atvēršanai. + Vai tiešām gribat atmest visas izmaiņas ieplānotajā maršrutā\? + Griezt pirms + Griezt pēc + Mainīt maršruta veidu pirms + Mainīt maršruta veidu pēc + Pievienot segmentus + Sadalīt pirms + Sadalīt pēc + Pievienot jaunu segmentu \ No newline at end of file From e89a6ab502e896cf011cecf01d3c77e5822d22f9 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 18 Apr 2021 19:42:27 +0000 Subject: [PATCH 53/86] Translated using Weblate (German) Currently translated at 100.0% (3927 of 3927 strings) --- OsmAnd/res/values-de/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-de/phrases.xml b/OsmAnd/res/values-de/phrases.xml index 7f2a668260..433d3766aa 100644 --- a/OsmAnd/res/values-de/phrases.xml +++ b/OsmAnd/res/values-de/phrases.xml @@ -3927,4 +3927,5 @@ Diplomatische Vertretung Art der Bucht Plateau + Freizeitclub \ No newline at end of file From f6245ebce3d9d4f3bc2d999826470d0b653a8945 Mon Sep 17 00:00:00 2001 From: Eduardo Addad de Oliveira Date: Mon, 19 Apr 2021 02:55:03 +0000 Subject: [PATCH 54/86] Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.9% (3926 of 3927 strings) --- OsmAnd/res/values-pt-rBR/phrases.xml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/OsmAnd/res/values-pt-rBR/phrases.xml b/OsmAnd/res/values-pt-rBR/phrases.xml index bfd6941d61..12ab9fa85f 100644 --- a/OsmAnd/res/values-pt-rBR/phrases.xml +++ b/OsmAnd/res/values-pt-rBR/phrases.xml @@ -3918,4 +3918,13 @@ Jiu-jitsu Karatê Aros + Clube social + Planalto + Escritório diplomático + Kickboxe + Polo de bicicleta + Curling + Mergulho de falésia + Zurkhaneh + Tipo de baía \ No newline at end of file From 6291b18fc30c787b47abe608b0cbb263b0803fa5 Mon Sep 17 00:00:00 2001 From: Shjosan Date: Sun, 18 Apr 2021 07:26:50 +0000 Subject: [PATCH 55/86] Translated using Weblate (Swedish) Currently translated at 100.0% (3927 of 3927 strings) --- OsmAnd/res/values-sv/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-sv/phrases.xml b/OsmAnd/res/values-sv/phrases.xml index a621b2a6d3..4e9ec5b680 100644 --- a/OsmAnd/res/values-sv/phrases.xml +++ b/OsmAnd/res/values-sv/phrases.xml @@ -3927,4 +3927,5 @@ Diplomatiskt kontor Typ av vik Platå + Social klubb \ No newline at end of file From f6ebee979ad30ca733c4990f53c70865a53f50c2 Mon Sep 17 00:00:00 2001 From: Jeff Huang Date: Mon, 19 Apr 2021 03:36:55 +0000 Subject: [PATCH 56/86] Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (3927 of 3927 strings) --- OsmAnd/res/values-zh-rTW/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-zh-rTW/phrases.xml b/OsmAnd/res/values-zh-rTW/phrases.xml index 3b6a127f11..caff9ec09b 100644 --- a/OsmAnd/res/values-zh-rTW/phrases.xml +++ b/OsmAnd/res/values-zh-rTW/phrases.xml @@ -3927,4 +3927,5 @@ 外交部 海灣類型 高原 + 社交俱樂部 \ No newline at end of file From e6907b2153b4fd8acb417c1e771c94a3a7bc5a96 Mon Sep 17 00:00:00 2001 From: Hinagiku Zeppeki Date: Sun, 18 Apr 2021 07:33:06 +0000 Subject: [PATCH 57/86] Translated using Weblate (Japanese) Currently translated at 89.2% (242 of 271 strings) Translation: OsmAnd/Telegram Translate-URL: https://hosted.weblate.org/projects/osmand/telegram/ja/ --- OsmAnd-telegram/res/values-ja/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OsmAnd-telegram/res/values-ja/strings.xml b/OsmAnd-telegram/res/values-ja/strings.xml index 1829c1fcc9..57bf1c209e 100644 --- a/OsmAnd-telegram/res/values-ja/strings.xml +++ b/OsmAnd-telegram/res/values-ja/strings.xml @@ -22,7 +22,7 @@ ポート サーバー 接続 - 有効 + 有効化 プロキシタイプ 接続しました 切断しました From aa846ab405ffcc72953a83a2a942e147ff171982 Mon Sep 17 00:00:00 2001 From: Victor Shcherb Date: Mon, 19 Apr 2021 12:38:33 +0200 Subject: [PATCH 58/86] English name fix #11463 --- OsmAnd-java/src/main/java/net/osmand/data/MapObject.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java b/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java index 79b3b8a349..e02b884e0a 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java @@ -183,7 +183,10 @@ public abstract class MapObject implements Comparable { public String getName(String lang, boolean transliterate) { if (lang != null && lang.length() > 0) { if (lang.equals("en")) { - return getEnName(transliterate); + // for some objects like wikipedia, english name is stored 'name' tag + if (Algorithms.isEmpty(enName)) { + return getEnName(transliterate); + } } else { // get name if (names != null) { From 2be03d04ed8fbfafab2e15dd873bcb55ed90e690 Mon Sep 17 00:00:00 2001 From: cepprice Date: Mon, 19 Apr 2021 16:35:26 +0500 Subject: [PATCH 59/86] Purchases screen RTL fixes --- OsmAnd/res/layout/subscription_layout.xml | 1 - OsmAnd/res/layout/troubleshooting_card.xml | 1 - OsmAnd/src/net/osmand/plus/UiUtilities.java | 7 +++---- .../src/net/osmand/plus/activities/PluginInfoFragment.java | 2 +- OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java | 2 +- .../plus/settings/fragments/BaseSettingsFragment.java | 2 +- .../osmand/plus/settings/fragments/PurchasesFragment.java | 1 + .../fragments/TroubleshootingOrPurchasingCard.java | 5 +++++ 8 files changed, 12 insertions(+), 9 deletions(-) diff --git a/OsmAnd/res/layout/subscription_layout.xml b/OsmAnd/res/layout/subscription_layout.xml index 411e49db31..044ab6ba03 100644 --- a/OsmAnd/res/layout/subscription_layout.xml +++ b/OsmAnd/res/layout/subscription_layout.xml @@ -64,7 +64,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/content_padding_small" - android:layout_gravity="center_vertical" android:paddingStart="@dimen/content_padding_small" android:paddingLeft="@dimen/content_padding_small" android:paddingTop="@dimen/content_padding_small_half" diff --git a/OsmAnd/res/layout/troubleshooting_card.xml b/OsmAnd/res/layout/troubleshooting_card.xml index e7de139dc1..3ce447d5d0 100644 --- a/OsmAnd/res/layout/troubleshooting_card.xml +++ b/OsmAnd/res/layout/troubleshooting_card.xml @@ -32,7 +32,6 @@ android:id="@+id/troubleshooting_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center_vertical" android:layout_marginEnd="@dimen/map_widget_height" android:layout_marginRight="@dimen/map_widget_height" android:paddingBottom="@dimen/dialog_content_bottom_margin" diff --git a/OsmAnd/src/net/osmand/plus/UiUtilities.java b/OsmAnd/src/net/osmand/plus/UiUtilities.java index a7c0223c22..886b1c7f8c 100644 --- a/OsmAnd/src/net/osmand/plus/UiUtilities.java +++ b/OsmAnd/src/net/osmand/plus/UiUtilities.java @@ -442,15 +442,14 @@ public class UiUtilities { } catch (Throwable e) { } } - public static void rotateImageByLayoutDirection(ImageView image, int layoutDirection) { - if (image == null) { + public static void rotateImageByLayoutDirection(ImageView image, Context context) { + if (image == null || context == null) { return; } - int rotation = layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL ? 180 : 0; + int rotation = AndroidUtils.getLayoutDirection(context) == ViewCompat.LAYOUT_DIRECTION_RTL ? 180 : 0; image.setRotationY(rotation); } - public static void updateCustomRadioButtons(Context app, View buttonsView, boolean nightMode, CustomRadioButtonType buttonType) { int activeColor = ContextCompat.getColor(app, nightMode diff --git a/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java b/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java index ced666e32b..90fccc5c3e 100644 --- a/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java +++ b/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java @@ -109,7 +109,7 @@ public class PluginInfoFragment extends BaseOsmAndFragment implements PluginStat } } }); - UiUtilities.rotateImageByLayoutDirection(closeButton, AndroidUtils.getLayoutDirection(app)); + UiUtilities.rotateImageByLayoutDirection(closeButton, app); Drawable pluginImage = plugin.getAssetResourceImage(); if (pluginImage != null) { diff --git a/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java b/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java index 04666e9607..5b6c302d84 100644 --- a/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java +++ b/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java @@ -101,7 +101,7 @@ public class PluginsFragment extends BaseOsmAndFragment implements PluginStateLi } } }); - UiUtilities.rotateImageByLayoutDirection(closeButton, AndroidUtils.getLayoutDirection(app)); + UiUtilities.rotateImageByLayoutDirection(closeButton, app); adapter = new PluginsListAdapter(requireContext()); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java index 05c4c7e323..f89afd91a2 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java @@ -493,7 +493,7 @@ public abstract class BaseSettingsFragment extends PreferenceFragmentCompat impl }); if (closeButton instanceof ImageView) { UiUtilities.rotateImageByLayoutDirection( - (ImageView) closeButton, AndroidUtils.getLayoutDirection(app)); + (ImageView) closeButton, app); } } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java index 461f51e14d..8cd4d61179 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java @@ -152,6 +152,7 @@ public class PurchasesFragment extends BaseOsmAndFragment implements InAppPurcha } }); ImageButton backButton = toolbar.findViewById(R.id.close_button); + UiUtilities.rotateImageByLayoutDirection(backButton, getContext()); backButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java b/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java index f0ed9d8968..db33837202 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java @@ -7,10 +7,12 @@ import android.text.Spannable; import android.text.SpannableString; import android.text.style.StyleSpan; import android.view.View; +import android.widget.ImageView; import android.widget.TextView; import net.osmand.AndroidUtils; import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.chooseplan.ChoosePlanDialogFragment; import net.osmand.plus.inapp.InAppPurchaseHelper; @@ -74,6 +76,9 @@ public class TroubleshootingOrPurchasingCard extends BaseCard { } else { AndroidUtils.setBackground(mapActivity, getItButton, nightMode, R.drawable.btn_unstroked_light, R.drawable.btn_unstroked_dark); } + + ImageView getItArrow = view.findViewById(R.id.additional_button_icon); + UiUtilities.rotateImageByLayoutDirection(getItArrow, app); } } From ced9a3555e54316d4e44cc6a57de74097079060c Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Mon, 19 Apr 2021 16:49:03 +0300 Subject: [PATCH 60/86] Implement icon radio toggle Implement landscape for Plan Route --- .../layout-land/fragment_measurement_tool.xml | 14 +++ OsmAnd/res/layout-land/map_hud_top.xml | 1 + .../res/layout/custom_icon_radio_buttons.xml | 23 ++++ .../res/layout/custom_radio_btn_icon_item.xml | 20 ++++ .../layout/measurement_tool_graph_card.xml | 1 + .../LiveUpdatesSettingsBottomSheet.java | 27 ++--- .../plus/measurementtool/GraphsCard.java | 8 ++ .../MeasurementToolFragment.java | 110 ++++++++++-------- .../routing/cards/RouteLineColorCard.java | 17 +-- .../plus/track/SplitIntervalBottomSheet.java | 20 ++-- .../multistatetoggle/IconToggleButton.java | 70 +++++++++++ .../MultiStateToggleButton.java | 97 +++++++-------- .../widgets/multistatetoggle/RadioItem.java | 20 ++++ .../multistatetoggle/TextToggleButton.java | 54 +++++++++ 14 files changed, 349 insertions(+), 133 deletions(-) create mode 100644 OsmAnd/res/layout/custom_icon_radio_buttons.xml create mode 100644 OsmAnd/res/layout/custom_radio_btn_icon_item.xml create mode 100644 OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/IconToggleButton.java rename OsmAnd/src/net/osmand/plus/widgets/{ => multistatetoggle}/MultiStateToggleButton.java (70%) create mode 100644 OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/RadioItem.java create mode 100644 OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/TextToggleButton.java diff --git a/OsmAnd/res/layout-land/fragment_measurement_tool.xml b/OsmAnd/res/layout-land/fragment_measurement_tool.xml index 505e680262..85e4cfd4d5 100644 --- a/OsmAnd/res/layout-land/fragment_measurement_tool.xml +++ b/OsmAnd/res/layout-land/fragment_measurement_tool.xml @@ -53,6 +53,19 @@ android:background="@null" tools:src="@drawable/ic_action_ruler"/> + + diff --git a/OsmAnd/res/layout/custom_icon_radio_buttons.xml b/OsmAnd/res/layout/custom_icon_radio_buttons.xml new file mode 100644 index 0000000000..bdfe2f4f5f --- /dev/null +++ b/OsmAnd/res/layout/custom_icon_radio_buttons.xml @@ -0,0 +1,23 @@ + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/custom_radio_btn_icon_item.xml b/OsmAnd/res/layout/custom_radio_btn_icon_item.xml new file mode 100644 index 0000000000..8a54559283 --- /dev/null +++ b/OsmAnd/res/layout/custom_radio_btn_icon_item.xml @@ -0,0 +1,20 @@ + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/measurement_tool_graph_card.xml b/OsmAnd/res/layout/measurement_tool_graph_card.xml index fecbe7ae89..272b9a4d2e 100644 --- a/OsmAnd/res/layout/measurement_tool_graph_card.xml +++ b/OsmAnd/res/layout/measurement_tool_graph_card.xml @@ -4,6 +4,7 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" + android:background="?attr/list_background_color" android:orientation="vertical"> { + + public IconToggleButton(@NonNull OsmandApplication app, + @NonNull LinearLayout container, + boolean nightMode) { + super(app, container, nightMode); + } + + @Override + protected int getRadioItemLayoutId() { + return R.layout.custom_radio_btn_icon_item; + } + + @Override + protected void initItemView(@NonNull ViewGroup view, + @NonNull IconRadioItem item) { + if (item.isUseDefaultColor()) { + ImageView ivIcon = view.findViewById(R.id.icon); + ivIcon.setImageDrawable(uiUtilities.getIcon(item.getIconId())); + } + } + + @Override + protected void updateItemView(@NonNull ViewGroup view, + @NonNull IconRadioItem item, + @ColorInt int color) { + if (!item.isUseDefaultColor()) { + ImageView ivIcon = view.findViewById(R.id.icon); + Drawable icon = uiUtilities.getPaintedIcon(item.getIconId(), color); + ivIcon.setImageDrawable(icon); + } + } + + public static class IconRadioItem extends RadioItem { + + private final int iconId; + private final boolean useDefaultColor; + + public IconRadioItem(int iconId) { + this(iconId, false); + } + + public IconRadioItem(int iconId, boolean useDefaultColor) { + this.iconId = iconId; + this.useDefaultColor = useDefaultColor; + } + + public int getIconId() { + return iconId; + } + + public boolean isUseDefaultColor() { + return useDefaultColor; + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/widgets/MultiStateToggleButton.java b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/MultiStateToggleButton.java similarity index 70% rename from OsmAnd/src/net/osmand/plus/widgets/MultiStateToggleButton.java rename to OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/MultiStateToggleButton.java index 605584f5b8..2207933acc 100644 --- a/OsmAnd/src/net/osmand/plus/widgets/MultiStateToggleButton.java +++ b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/MultiStateToggleButton.java @@ -1,4 +1,4 @@ -package net.osmand.plus.widgets; +package net.osmand.plus.widgets.multistatetoggle; import android.graphics.Color; import android.graphics.drawable.GradientDrawable; @@ -6,38 +6,49 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; -import android.widget.TextView; +import androidx.annotation.ColorInt; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import net.osmand.AndroidUtils; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; +import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -public class MultiStateToggleButton { +public abstract class MultiStateToggleButton<_Radio extends RadioItem> { - private List items = new ArrayList<>(); - private OsmandApplication app; - private List buttons = new ArrayList<>(); - private List dividers = new ArrayList<>(); - private RadioItem selectedItem; + protected final OsmandApplication app; + protected final UiUtilities uiUtilities; - private LinearLayout container; - private boolean nightMode; + private final LinearLayout container; + private final List buttons = new ArrayList<>(); + private final List dividers = new ArrayList<>(); - public MultiStateToggleButton(OsmandApplication app, LinearLayout container, boolean nightMode) { + protected final List<_Radio> items = new ArrayList<>(); + protected final boolean nightMode; + protected boolean isEnabled; + protected RadioItem selectedItem; + + public MultiStateToggleButton(@NonNull OsmandApplication app, + @NonNull LinearLayout container, + boolean nightMode) { this.app = app; + this.uiUtilities = app.getUIUtilities(); this.container = container; this.nightMode = nightMode; } - public void setItems(RadioItem firstBtn, RadioItem secondBtn, RadioItem... other) { + @SafeVarargs + public final void setItems(@NonNull _Radio firstBtn, + @NonNull _Radio secondBtn, + @Nullable _Radio... other) { items.clear(); items.add(firstBtn); items.add(secondBtn); @@ -47,6 +58,11 @@ public class MultiStateToggleButton { initView(); } + public final void setSelectedItem(@Nullable RadioItem selectedItem) { + this.selectedItem = selectedItem; + updateView(); + } + private void initView() { buttons.clear(); dividers.clear(); @@ -60,16 +76,10 @@ public class MultiStateToggleButton { updateView(); } - private boolean isLastItem(int index) { - return index == items.size() - 1; - } - - private void createBtn(@NonNull final RadioItem item) { + private void createBtn(@NonNull final _Radio item) { LayoutInflater inflater = UiUtilities.getInflater(app, nightMode); ViewGroup button = (ViewGroup) inflater.inflate( - R.layout.custom_radio_btn_text_item, container, false); - TextView title = button.findViewById(R.id.title); - title.setText(item.getTitle()); + getRadioItemLayoutId(), container, false); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -79,6 +89,7 @@ public class MultiStateToggleButton { } } }); + initItemView(button, item); buttons.add(button); container.addView(button); } @@ -95,11 +106,6 @@ public class MultiStateToggleButton { container.addView(divider); } - public void setSelectedItem(RadioItem selectedItem) { - this.selectedItem = selectedItem; - updateView(); - } - private void updateView() { updateView(true); } @@ -123,15 +129,14 @@ public class MultiStateToggleButton { showAllDividers(); for (int i = 0; i < items.size(); i++) { - RadioItem item = items.get(i); + _Radio item = items.get(i); ViewGroup container = buttons.get(i); container.setEnabled(isEnabled); - TextView tvTitle = (TextView) container.findViewById(R.id.title); if (selectedItem == item) { if (i == 0) { background.setCornerRadii(isLayoutRtl ? rightBtnRadii : leftBtnRadii); hideDividers(0); - } else if (i == items.size() - 1) { + } else if (isLastItem(i)) { background.setCornerRadii(isLayoutRtl ? leftBtnRadii : rightBtnRadii); hideDividers(dividers.size() - 1); } else { @@ -139,14 +144,21 @@ public class MultiStateToggleButton { hideDividers(i - 1, i); } container.setBackgroundDrawable(background); - tvTitle.setTextColor(textColor); + updateItemView(container, item, textColor); } else { container.setBackgroundColor(Color.TRANSPARENT); - tvTitle.setTextColor(activeColor); + updateItemView(container, item, activeColor); } } } + protected abstract int getRadioItemLayoutId(); + + protected abstract void initItemView(@NonNull ViewGroup view, @NonNull _Radio item); + + protected abstract void updateItemView(@NonNull ViewGroup view, @NonNull _Radio item, + @ColorInt int color); + private void showAllDividers() { for (View divider : dividers) { divider.setVisibility(View.VISIBLE); @@ -161,28 +173,7 @@ public class MultiStateToggleButton { } } - public static class RadioItem { - private String title; - private OnRadioItemClickListener listener; - - public RadioItem(String title) { - this.title = title; - } - - public void setOnClickListener(OnRadioItemClickListener listener) { - this.listener = listener; - } - - public String getTitle() { - return title; - } - - public OnRadioItemClickListener getListener() { - return listener; - } - } - - public interface OnRadioItemClickListener { - boolean onRadioItemClick(RadioItem radioItem, View view); + private boolean isLastItem(int index) { + return index == items.size() - 1; } } diff --git a/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/RadioItem.java b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/RadioItem.java new file mode 100644 index 0000000000..c23f6a925f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/RadioItem.java @@ -0,0 +1,20 @@ +package net.osmand.plus.widgets.multistatetoggle; + +import android.view.View; + +public class RadioItem { + + private OnRadioItemClickListener listener; + + public void setOnClickListener(OnRadioItemClickListener listener) { + this.listener = listener; + } + + public OnRadioItemClickListener getListener() { + return listener; + } + + public interface OnRadioItemClickListener { + boolean onRadioItemClick(RadioItem radioItem, View view); + } +} diff --git a/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/TextToggleButton.java b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/TextToggleButton.java new file mode 100644 index 0000000000..048ebca622 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/widgets/multistatetoggle/TextToggleButton.java @@ -0,0 +1,54 @@ +package net.osmand.plus.widgets.multistatetoggle; + +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; + +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.R; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton.TextRadioItem; + +public class TextToggleButton extends MultiStateToggleButton { + + public TextToggleButton(@NonNull OsmandApplication app, + @NonNull LinearLayout container, + boolean nightMode) { + super(app, container, nightMode); + } + + @Override + protected int getRadioItemLayoutId() { + return R.layout.custom_radio_btn_text_item; + } + + @Override + protected void initItemView(@NonNull ViewGroup view, @NonNull TextRadioItem item) { + TextView title = view.findViewById(R.id.title); + title.setText(item.getTitle()); + } + + @Override + protected void updateItemView(@NonNull ViewGroup view, + @NonNull TextRadioItem item, + @ColorInt int color) { + TextView tvTitle = (TextView) view.findViewById(R.id.title); + tvTitle.setTextColor(color); + } + + public static class TextRadioItem extends RadioItem { + + private final String title; + + public TextRadioItem(String title) { + this.title = title; + } + + public String getTitle() { + return title; + } + } + +} From ed8b9973a91befd598678ccd46043b0c2f47d4a1 Mon Sep 17 00:00:00 2001 From: androiddevkotlin <64539346+androiddevkotlin@users.noreply.github.com> Date: Mon, 19 Apr 2021 17:33:40 +0300 Subject: [PATCH 61/86] Untranslated Travel guides (wikivoyage) file names https://github.com/osmandapp/OsmAnd/issues/11398 --- .../plus/download/DownloadActivityType.java | 3 + .../helpers/FileNameTranslationHelper.java | 56 ++++++++++++++++--- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java index 071a856454..f675c38455 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivityType.java @@ -361,6 +361,9 @@ public class DownloadActivityType { if (basename.endsWith(FileNameTranslationHelper.WIKI_NAME)) { return FileNameTranslationHelper.getWikiName(ctx, basename); } + if (basename.endsWith(FileNameTranslationHelper.WIKIVOYAGE_NAME)) { + return FileNameTranslationHelper.getWikivoyageName(ctx, basename); + } // if (this == HILLSHADE_FILE){ // return FileNameTranslationHelper.getHillShadeName(ctx, osmandRegions, bn); // } diff --git a/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java b/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java index e366d1e8bb..296351624e 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java @@ -1,9 +1,11 @@ package net.osmand.plus.helpers; import android.content.Context; + import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.map.OsmandRegions; +import net.osmand.map.WorldRegion; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.download.DownloadResources; @@ -19,6 +21,7 @@ import java.lang.reflect.Field; public class FileNameTranslationHelper { private static final Log LOG = PlatformUtil.getLog(FileNameTranslationHelper.class); public static final String WIKI_NAME = "_wiki"; + public static final String WIKIVOYAGE_NAME = "_wikivoyage"; public static final String HILL_SHADE = "Hillshade"; public static final String SLOPE = "Slope"; public static final String SEA_DEPTH = "Depth_"; @@ -31,6 +34,8 @@ public class FileNameTranslationHelper { String basename = getBasename(fileName); if (basename.endsWith(WIKI_NAME)) { //wiki files return getWikiName(ctx, basename); + } else if (basename.endsWith(WIKIVOYAGE_NAME)) { + return getWikivoyageName(ctx, basename); } else if (fileName.endsWith("tts")) { //tts files return getVoiceName(ctx, fileName); } else if (fileName.endsWith(IndexConstants.FONT_INDEX_EXT)) { //otf files @@ -75,10 +80,10 @@ public class FileNameTranslationHelper { return ctx.getString(R.string.ltr_or_rtl_combine_via_space, locName, "(" + terrain + ")"); } - public static String getWikiName(Context ctx, String basename){ + public static String getWikiName(Context ctx, String basename) { String cutted = basename.substring(0, basename.indexOf("_wiki")); String wikiName = getStandardLangName(ctx, cutted); - if (wikiName == null){ + if (wikiName == null) { wikiName = cutted; } String wikiWord = ctx.getString(R.string.amenity_type_osmwiki); @@ -87,7 +92,21 @@ public class FileNameTranslationHelper { //removing word in "()" from recourse file return wikiName + " " + wikiWord.substring(0, index).trim(); } - return wikiName + " " + ctx.getString(R.string.amenity_type_osmwiki); + return wikiName + " " + ctx.getString(R.string.amenity_type_osmwiki); + } + + public static String getWikivoyageName(Context ctx, String basename) { + String formattedName = basename.substring(0, basename.indexOf(WIKIVOYAGE_NAME)).replaceAll("-", "").replaceAll("all", ""); + String wikiVoyageName = getSuggestedWikivoyageMaps(ctx, formattedName); + if (wikiVoyageName == null) { + wikiVoyageName = formattedName; + } + String wikiVoyageWord = ctx.getString(R.string.shared_string_wikivoyage); + int index = wikiVoyageWord.indexOf("("); + if (index >= 0) { + return ctx.getString(R.string.ltr_or_rtl_combine_via_space, wikiVoyageName, wikiVoyageWord.substring(0, index).trim()); + } + return ctx.getString(R.string.ltr_or_rtl_combine_via_space, wikiVoyageName, wikiVoyageWord); } public static String getVoiceName(Context ctx, String fileName) { @@ -196,8 +215,8 @@ public class FileNameTranslationHelper { return ctx.getString(R.string.lang_pl); } else if (filename.equalsIgnoreCase("Portuguese")) { return ctx.getString(R.string.lang_pt); - //} else if (filename.equalsIgnoreCase("Portuguese")) { - // return ctx.getString(R.string.lang_pt_br); + //} else if (filename.equalsIgnoreCase("Portuguese")) { + // return ctx.getString(R.string.lang_pt_br); } else if (filename.equalsIgnoreCase("Romanian")) { return ctx.getString(R.string.lang_ro); } else if (filename.equalsIgnoreCase("Russian")) { @@ -227,11 +246,11 @@ public class FileNameTranslationHelper { 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_basemap_detailed")){ + } else if (basename.equals("world_basemap_detailed")) { return ctx.getString(R.string.index_item_world_basemap_detailed); } else if (basename.equals("world_bitcoin_payments")) { return ctx.getString(R.string.index_item_world_bitcoin_payments); - } else if (basename.equals(DownloadResources.WORLD_SEAMARKS_KEY) || + } else if (basename.equals(DownloadResources.WORLD_SEAMARKS_KEY) || basename.equals(DownloadResources.WORLD_SEAMARKS_OLD_KEY)) { return ctx.getString(R.string.index_item_world_seamarks); } else if (basename.equals("world_wikivoyage")) { @@ -245,4 +264,27 @@ public class FileNameTranslationHelper { } return null; } + + private static String getSuggestedWikivoyageMaps(Context ctx, String filename) { + if (WorldRegion.AFRICA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_africa); + } else if (WorldRegion.AUSTRALIA_AND_OCEANIA_REGION_ID.replaceAll("-", "").equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_oceania); + } else if (WorldRegion.ASIA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_asia); + } else if (WorldRegion.CENTRAL_AMERICA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_central_america); + } else if (WorldRegion.EUROPE_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_europe); + } else if (WorldRegion.RUSSIA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_russia); + } else if (WorldRegion.NORTH_AMERICA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_north_america); + } else if (WorldRegion.SOUTH_AMERICA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_south_america); + } else if (WorldRegion.ANTARCTICA_REGION_ID.equalsIgnoreCase(filename)) { + return ctx.getString(R.string.index_name_antarctica); + } + return null; + } } From a2a3087b68d452796f5173db33a2fbd2bdc861d0 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Mon, 19 Apr 2021 17:46:42 +0300 Subject: [PATCH 62/86] Handle "+", "-" and "=" keycode for change map zoom, regardless external input device settings --- .../plus/activities/MapActivityKeyListener.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java index 33ce52296b..846ac975ab 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java @@ -94,6 +94,12 @@ public class MapActivityKeyListener implements KeyEvent.Callback { mapActivity.getMapViewTrackingUtilities().backToLocationImpl(); } else if (keyCode == KeyEvent.KEYCODE_D) { mapActivity.getMapViewTrackingUtilities().switchRotateMapMode(); + } if (keyCode == KeyEvent.KEYCODE_MINUS) { + mapActivity.changeZoom(-1); + return true; + } else if (keyCode == KeyEvent.KEYCODE_PLUS || keyCode == KeyEvent.KEYCODE_EQUALS) { + mapActivity.changeZoom(1); + return true; } else if (mapScrollHelper.isAvailableKeyCode(keyCode)) { return mapScrollHelper.onKeyUp(keyCode, event); } else if (settings.EXTERNAL_INPUT_DEVICE.get() == PARROT_EXTERNAL_DEVICE) { @@ -120,14 +126,6 @@ public class MapActivityKeyListener implements KeyEvent.Callback { mapActivity.startActivity(intent); return true; } - } else if (settings.EXTERNAL_INPUT_DEVICE.get() == GENERIC_EXTERNAL_DEVICE) { - if (keyCode == KeyEvent.KEYCODE_MINUS) { - mapActivity.changeZoom(-1); - return true; - } else if (keyCode == KeyEvent.KEYCODE_PLUS || keyCode == KeyEvent.KEYCODE_EQUALS) { - mapActivity.changeZoom(1); - return true; - } } else if (OsmandPlugin.onMapActivityKeyUp(mapActivity, keyCode)) { return true; } From 324397cc25290f167bd07ae57faf6a36bf7ce102 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Mon, 19 Apr 2021 17:51:21 +0300 Subject: [PATCH 63/86] Small fixes --- OsmAnd/res/layout/subscriptions_card.xml | 72 +------------------ OsmAnd/src/net/osmand/plus/UiUtilities.java | 6 +- .../plus/activities/PluginInfoFragment.java | 2 +- .../plus/activities/PluginsFragment.java | 2 +- .../fragments/BaseSettingsFragment.java | 3 +- .../settings/fragments/PurchasesFragment.java | 16 ++--- .../settings/fragments/SubscriptionsCard.java | 44 +----------- .../TroubleshootingOrPurchasingCard.java | 10 +-- 8 files changed, 23 insertions(+), 132 deletions(-) diff --git a/OsmAnd/res/layout/subscriptions_card.xml b/OsmAnd/res/layout/subscriptions_card.xml index d8ec722375..7d2058a487 100644 --- a/OsmAnd/res/layout/subscriptions_card.xml +++ b/OsmAnd/res/layout/subscriptions_card.xml @@ -1,8 +1,6 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/OsmAnd/src/net/osmand/plus/UiUtilities.java b/OsmAnd/src/net/osmand/plus/UiUtilities.java index 886b1c7f8c..cba86b7f8b 100644 --- a/OsmAnd/src/net/osmand/plus/UiUtilities.java +++ b/OsmAnd/src/net/osmand/plus/UiUtilities.java @@ -442,11 +442,11 @@ public class UiUtilities { } catch (Throwable e) { } } - public static void rotateImageByLayoutDirection(ImageView image, Context context) { - if (image == null || context == null) { + public static void rotateImageByLayoutDirection(ImageView image) { + if (image == null) { return; } - int rotation = AndroidUtils.getLayoutDirection(context) == ViewCompat.LAYOUT_DIRECTION_RTL ? 180 : 0; + int rotation = AndroidUtils.getLayoutDirection(image.getContext()) == ViewCompat.LAYOUT_DIRECTION_RTL ? 180 : 0; image.setRotationY(rotation); } diff --git a/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java b/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java index 90fccc5c3e..5d32ee228d 100644 --- a/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java +++ b/OsmAnd/src/net/osmand/plus/activities/PluginInfoFragment.java @@ -109,7 +109,7 @@ public class PluginInfoFragment extends BaseOsmAndFragment implements PluginStat } } }); - UiUtilities.rotateImageByLayoutDirection(closeButton, app); + UiUtilities.rotateImageByLayoutDirection(closeButton); Drawable pluginImage = plugin.getAssetResourceImage(); if (pluginImage != null) { diff --git a/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java b/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java index 5b6c302d84..54439daefa 100644 --- a/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java +++ b/OsmAnd/src/net/osmand/plus/activities/PluginsFragment.java @@ -101,7 +101,7 @@ public class PluginsFragment extends BaseOsmAndFragment implements PluginStateLi } } }); - UiUtilities.rotateImageByLayoutDirection(closeButton, app); + UiUtilities.rotateImageByLayoutDirection(closeButton); adapter = new PluginsListAdapter(requireContext()); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java index f89afd91a2..73c62eacfe 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsFragment.java @@ -492,8 +492,7 @@ public abstract class BaseSettingsFragment extends PreferenceFragmentCompat impl } }); if (closeButton instanceof ImageView) { - UiUtilities.rotateImageByLayoutDirection( - (ImageView) closeButton, app); + UiUtilities.rotateImageByLayoutDirection((ImageView) closeButton); } } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java index 8cd4d61179..9c60348d65 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/PurchasesFragment.java @@ -10,6 +10,11 @@ import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentActivity; +import androidx.fragment.app.FragmentManager; + import com.google.android.material.appbar.AppBarLayout; import net.osmand.AndroidUtils; @@ -30,11 +35,6 @@ import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.FragmentActivity; -import androidx.fragment.app.FragmentManager; - public class PurchasesFragment extends BaseOsmAndFragment implements InAppPurchaseListener, OnFragmentInteractionListener { private static final Log log = PlatformUtil.getLog(PurchasesFragment.class); @@ -152,7 +152,7 @@ public class PurchasesFragment extends BaseOsmAndFragment implements InAppPurcha } }); ImageButton backButton = toolbar.findViewById(R.id.close_button); - UiUtilities.rotateImageByLayoutDirection(backButton, getContext()); + UiUtilities.rotateImageByLayoutDirection(backButton); backButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -198,9 +198,7 @@ public class PurchasesFragment extends BaseOsmAndFragment implements InAppPurcha @Override public void onSearchResult(CountrySelectionFragment.CountryItem name) { - if (subscriptionsCard != null) { - subscriptionsCard.onSupportRegionSelected(name); - } + } @Override diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsCard.java b/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsCard.java index 12c34ede1e..0957969ccd 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsCard.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/SubscriptionsCard.java @@ -5,30 +5,26 @@ import android.net.Uri; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; -import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import net.osmand.AndroidUtils; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.inapp.InAppPurchaseHelper; -import net.osmand.plus.liveupdates.CountrySelectionFragment; import net.osmand.plus.liveupdates.LiveUpdatesFragment; import net.osmand.plus.liveupdates.OsmLiveActivity; import net.osmand.plus.routepreparationmenu.cards.BaseCard; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.util.Algorithms; -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; - public class SubscriptionsCard extends BaseCard { private static final String PLAY_STORE_SUBSCRIPTION_URL = "https://play.google.com/store/account/subscriptions"; private static final String PLAY_STORE_SUBSCRIPTION_DEEPLINK_URL = "https://play.google.com/store/account/subscriptions?sku=%s&package=%s"; private Fragment target; -// private CountrySelectionFragment countrySelectionFragment = new CountrySelectionFragment(); private SubscriptionsListCard subscriptionsListCard; private InAppPurchaseHelper purchaseHelper; @@ -54,7 +50,6 @@ public class SubscriptionsCard extends BaseCard { } updateSubscriptionsListCard(); - setupSupportRegion(); LinearLayout reportContainer = view.findViewById(R.id.report_container); reportContainer.setOnClickListener(new View.OnClickListener() { @@ -99,26 +94,6 @@ public class SubscriptionsCard extends BaseCard { } } - private void setupSupportRegion() { -// String region = LiveUpdatesFragment.getSupportRegionName(app, purchaseHelper); -// String header = LiveUpdatesFragment.getSupportRegionHeader(app, region); -// TextView supportRegionHeader = view.findViewById(R.id.support_region_header); -// TextView supportRegion = view.findViewById(R.id.support_region); -// supportRegionHeader.setText(header); -// supportRegion.setText(region); -// -// View supportRegionContainer = view.findViewById(R.id.support_region_container); -// supportRegionContainer.setOnClickListener(new View.OnClickListener() { -// @Override -// public void onClick(View v) { -// CountrySelectionFragment countryCountrySelectionFragment = countrySelectionFragment; -// countryCountrySelectionFragment.show(target.getChildFragmentManager(), CountrySelectionFragment.TAG); -// } -// }); -// -// countrySelectionFragment.initCountries(app); - } - private String getSubscriptionUrl() { InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper(); if (purchaseHelper != null && purchaseHelper.getFullVersion() != null) { @@ -129,17 +104,4 @@ public class SubscriptionsCard extends BaseCard { return PLAY_STORE_SUBSCRIPTION_URL; } } - - public void onSupportRegionSelected(CountrySelectionFragment.CountryItem selectedCountryItem) { -// String countryName = selectedCountryItem != null ? selectedCountryItem.getLocalName() : ""; -// String countryDownloadName = selectedCountryItem != null ? -// selectedCountryItem.getDownloadName() : OsmandSettings.BILLING_USER_DONATION_WORLD_PARAMETER; -// -// TextView supportRegionHeader = view.findViewById(R.id.support_region_header); -// TextView supportRegion = view.findViewById(R.id.support_region); -// supportRegionHeader.setText(LiveUpdatesFragment.getSupportRegionHeader(app, countryName)); -// supportRegion.setText(countryName); -// app.getSettings().BILLING_USER_COUNTRY.set(countryName); -// app.getSettings().BILLING_USER_COUNTRY_DOWNLOAD_NAME.set(countryDownloadName); - } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java b/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java index db33837202..6758292334 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/TroubleshootingOrPurchasingCard.java @@ -10,6 +10,10 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.cardview.widget.CardView; +import androidx.core.content.ContextCompat; + import net.osmand.AndroidUtils; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; @@ -19,10 +23,6 @@ import net.osmand.plus.inapp.InAppPurchaseHelper; import net.osmand.plus.routepreparationmenu.cards.BaseCard; import net.osmand.plus.wikipedia.WikipediaDialogFragment; -import androidx.annotation.NonNull; -import androidx.cardview.widget.CardView; -import androidx.core.content.ContextCompat; - public class TroubleshootingOrPurchasingCard extends BaseCard { private static final String OSMAND_NEW_DEVICE_URL = "https://docs.osmand.net/en/main@latest/osmand/purchases#new-device--new-account"; @@ -78,7 +78,7 @@ public class TroubleshootingOrPurchasingCard extends BaseCard { } ImageView getItArrow = view.findViewById(R.id.additional_button_icon); - UiUtilities.rotateImageByLayoutDirection(getItArrow, app); + UiUtilities.rotateImageByLayoutDirection(getItArrow); } } From 116742f3480b33ae8d861380f98b6c6a41508b54 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Mon, 19 Apr 2021 17:57:09 +0300 Subject: [PATCH 64/86] small fixes --- .../net/osmand/plus/activities/MapActivityKeyListener.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java b/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java index 846ac975ab..4b99e84350 100644 --- a/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java +++ b/OsmAnd/src/net/osmand/plus/activities/MapActivityKeyListener.java @@ -94,7 +94,7 @@ public class MapActivityKeyListener implements KeyEvent.Callback { mapActivity.getMapViewTrackingUtilities().backToLocationImpl(); } else if (keyCode == KeyEvent.KEYCODE_D) { mapActivity.getMapViewTrackingUtilities().switchRotateMapMode(); - } if (keyCode == KeyEvent.KEYCODE_MINUS) { + } else if (keyCode == KeyEvent.KEYCODE_MINUS) { mapActivity.changeZoom(-1); return true; } else if (keyCode == KeyEvent.KEYCODE_PLUS || keyCode == KeyEvent.KEYCODE_EQUALS) { @@ -126,6 +126,8 @@ public class MapActivityKeyListener implements KeyEvent.Callback { mapActivity.startActivity(intent); return true; } + } else if (settings.EXTERNAL_INPUT_DEVICE.get() == GENERIC_EXTERNAL_DEVICE) { + // currently doesn't process specific commands } else if (OsmandPlugin.onMapActivityKeyUp(mapActivity, keyCode)) { return true; } From d62c288e4aff6698b5f9848c94da05b4d0ac9739 Mon Sep 17 00:00:00 2001 From: androiddevkotlin <64539346+androiddevkotlin@users.noreply.github.com> Date: Mon, 19 Apr 2021 20:02:59 +0300 Subject: [PATCH 65/86] Remove copypasta --- .../net/osmand/plus/helpers/FileNameTranslationHelper.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java b/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java index 296351624e..203088d44e 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/FileNameTranslationHelper.java @@ -102,10 +102,7 @@ public class FileNameTranslationHelper { wikiVoyageName = formattedName; } String wikiVoyageWord = ctx.getString(R.string.shared_string_wikivoyage); - int index = wikiVoyageWord.indexOf("("); - if (index >= 0) { - return ctx.getString(R.string.ltr_or_rtl_combine_via_space, wikiVoyageName, wikiVoyageWord.substring(0, index).trim()); - } + return ctx.getString(R.string.ltr_or_rtl_combine_via_space, wikiVoyageName, wikiVoyageWord); } From 8aeb8b16a388d7b50f3da083cd8d869c32e791d4 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Mon, 19 Apr 2021 20:59:21 +0300 Subject: [PATCH 66/86] Fix after code review --- OsmAnd/res/values/strings.xml | 2 +- .../base/MultipleSelectionBottomSheet.java | 2 +- .../plus/base/SelectionBottomSheet.java | 22 ++--- .../osmand/plus/download/DownloadItem.java | 6 +- .../plus/download/DownloadResources.java | 7 +- .../net/osmand/plus/download/IndexItem.java | 11 +-- .../plus/download/MultipleDownloadItem.java | 19 +--- .../plus/download/SelectIndexesUiHelper.java | 24 ++--- .../plus/download/SrtmDownloadItem.java | 93 +++++++++---------- .../plus/download/ui/ItemViewHolder.java | 10 +- 10 files changed, 91 insertions(+), 105 deletions(-) diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index db67d2bf0d..b6598b916b 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -20,7 +20,7 @@ Please select the needed format. You will need to re-download the file to change the format. OsmAnd provides contour lines data in meters and feet. You will need to re-download the file to change the format. Contour lines unit format - feets + feet Update all maps added to %1$s? • OsmAnd Live updates moved to \"Downloads > Updates\"\n\n diff --git a/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java index 728dfbaa65..fc7d75416a 100644 --- a/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java @@ -144,8 +144,8 @@ public class MultipleSelectionBottomSheet extends SelectionBottomSheet { } protected void setSelectedItems(List selected) { + selectedItems.clear(); if (!Algorithms.isEmpty(selected)) { - selectedItems.clear(); selectedItems.addAll(selected); } } diff --git a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java index f4bea22e62..18c91f8cf5 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java @@ -51,8 +51,8 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment protected int activeColorRes; protected int secondaryColorRes; - private OnUiInitializedListener uiInitializedListener; - private OnApplySelectionListener applySelectionListener; + private OnUiInitializedAdapter onUiInitializedAdapter; + private OnApplySelectionListener onApplySelectionListener; protected List allItems = new ArrayList<>(); protected Map listViews = new HashMap<>(); @@ -145,19 +145,19 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment } public void setItems(List allItems) { + this.allItems.clear(); if (!Algorithms.isEmpty(allItems)) { - this.allItems.clear(); this.allItems.addAll(allItems); createSelectionListIfPossible(); } } - public void setUiInitializedListener(OnUiInitializedListener uiInitializedListener) { - this.uiInitializedListener = uiInitializedListener; + public void setOnUiInitializedAdapter(OnUiInitializedAdapter onUiInitializedAdapter) { + this.onUiInitializedAdapter = onUiInitializedAdapter; } public void setOnApplySelectionListener(OnApplySelectionListener onApplySelectionListener) { - this.applySelectionListener = onApplySelectionListener; + this.onApplySelectionListener = onApplySelectionListener; } private void createSelectionListIfPossible() { @@ -194,8 +194,8 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment protected abstract boolean shouldShowDivider(); protected void notifyUiInitialized() { - if (uiInitializedListener != null) { - uiInitializedListener.onUiInitialized(); + if (onUiInitializedAdapter != null) { + onUiInitializedAdapter.onUiInitialized(); } } @@ -215,8 +215,8 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment @Override protected void onRightBottomButtonClick() { - if (applySelectionListener != null) { - applySelectionListener.onSelectionApplied(getSelectedItems()); + if (onApplySelectionListener != null) { + onApplySelectionListener.onSelectionApplied(getSelectedItems()); } dismiss(); } @@ -239,7 +239,7 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment } } - public interface OnUiInitializedListener { + public interface OnUiInitializedAdapter { void onUiInitialized(); } diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadItem.java b/OsmAnd/src/net/osmand/plus/download/DownloadItem.java index 46b3855a10..6b758f8af8 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadItem.java @@ -3,6 +3,7 @@ package net.osmand.plus.download; import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import net.osmand.map.OsmandRegions; import net.osmand.plus.OsmandApplication; @@ -59,9 +60,8 @@ public abstract class DownloadItem { @NonNull public abstract List getDownloadedFiles(@NonNull OsmandApplication app); - public abstract boolean isUseAbbreviation(); - - public abstract String getAbbreviationInScopes(Context ctx); + @Nullable + public abstract String getAdditionalDescription(Context ctx); protected abstract double getSizeToDownloadInMb(); diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java index e59e5f9de9..e2320b16a8 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java @@ -25,6 +25,7 @@ import java.io.InputStream; import java.text.DateFormat; import java.text.ParseException; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -121,7 +122,7 @@ public class DownloadResources extends DownloadResourceGroup { if (group != null) { return group.getIndividualDownloadItems(); } - return new LinkedList<>(); + return Collections.emptyList(); } @NonNull @@ -132,7 +133,7 @@ public class DownloadResources extends DownloadResourceGroup { return res; } } - return new LinkedList<>(); + return Collections.emptyList(); } public void updateLoadedFiles() { @@ -500,7 +501,7 @@ public class DownloadResources extends DownloadResourceGroup { srtmIndexes.add((IndexItem) item); } } - if (srtmIndexes.size() == 2) { + if (srtmIndexes.size() > 1) { individualItems.removeAll(srtmIndexes); group.addItem(new SrtmDownloadItem(srtmIndexes, useMetersByDefault)); } diff --git a/OsmAnd/src/net/osmand/plus/download/IndexItem.java b/OsmAnd/src/net/osmand/plus/download/IndexItem.java index 2cd75b2a5e..82da2619a3 100644 --- a/OsmAnd/src/net/osmand/plus/download/IndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/IndexItem.java @@ -3,6 +3,7 @@ package net.osmand.plus.download; import android.content.Context; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; @@ -229,14 +230,10 @@ public class IndexItem extends DownloadItem implements Comparable { return format.format(new Date(timestamp)); } + @Nullable @Override - public boolean isUseAbbreviation() { - return false; - } - - @Override - public String getAbbreviationInScopes(Context ctx) { - return ""; + public String getAdditionalDescription(Context ctx) { + return null; } public static class DownloadEntry { diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java b/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java index 268ae412b8..d30c9fc83b 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java @@ -144,28 +144,17 @@ public class MultipleDownloadItem extends DownloadItem { return null; } + @Nullable @Override - public boolean isUseAbbreviation() { + public String getAdditionalDescription(Context ctx) { for (DownloadItem item : items) { - if (item.isUseAbbreviation()) { - return true; - } + return item.getAdditionalDescription(ctx); } - return false; - } - - @Override - public String getAbbreviationInScopes(Context ctx) { - for (DownloadItem item : items) { - return item.getAbbreviationInScopes(ctx); - } - return ""; + return null; } @Override public String getDate(@NonNull DateFormat dateFormat, boolean remote) { return ""; } - - } diff --git a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java index fc062c7ddc..f7835ce348 100644 --- a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java @@ -13,7 +13,7 @@ import net.osmand.plus.base.ModeSelectionBottomSheet; import net.osmand.plus.base.MultipleSelectionWithModeBottomSheet; import net.osmand.plus.base.SelectionBottomSheet; import net.osmand.plus.base.SelectionBottomSheet.OnApplySelectionListener; -import net.osmand.plus.base.SelectionBottomSheet.OnUiInitializedListener; +import net.osmand.plus.base.SelectionBottomSheet.OnUiInitializedAdapter; import net.osmand.plus.base.SelectionBottomSheet.SelectableItem; import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener; import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; @@ -80,7 +80,7 @@ public class SelectIndexesUiHelper { activity, allItems, selectedItems, true); this.dialog = msDialog; - msDialog.setUiInitializedListener(new OnUiInitializedListener() { + msDialog.setOnUiInitializedAdapter(new OnUiInitializedAdapter() { @Override public void onUiInitialized() { dialog.setTitle(app.getString(R.string.welmode_download_maps)); @@ -103,14 +103,14 @@ public class SelectIndexesUiHelper { prepareItems(allItems, selectedItems); SrtmDownloadItem srtmItem = (SrtmDownloadItem) ((MultipleDownloadItem)downloadItem).getAllItems().get(0); - final int selectedModeOrder = srtmItem.isUseMeters() ? 0 : 1; + final int selectedModeOrder = srtmItem.isUseMetric() ? 0 : 1; final List radioItems = createSrtmRadioItems(); MultipleSelectionBottomSheet msDialog = MultipleSelectionWithModeBottomSheet.showInstance( activity, allItems, selectedItems, radioItems, true); this.dialog = msDialog; - msDialog.setUiInitializedListener(new OnUiInitializedListener() { + msDialog.setOnUiInitializedAdapter(new OnUiInitializedAdapter() { @Override public void onUiInitialized() { dialog.setTitle(app.getString(R.string.welmode_download_maps)); @@ -131,14 +131,14 @@ public class SelectIndexesUiHelper { private void showSrtmModeSelectionDialog() { SrtmDownloadItem srtmItem = (SrtmDownloadItem) downloadItem; - final int selectedModeOrder = srtmItem.isUseMeters() ? 0 : 1; + final int selectedModeOrder = srtmItem.isUseMetric() ? 0 : 1; final List radioItems = createSrtmRadioItems(); SelectableItem preview = createSelectableItem(srtmItem); dialog = ModeSelectionBottomSheet.showInstance(activity, preview, radioItems, true); - dialog.setUiInitializedListener(new OnUiInitializedListener() { + dialog.setOnUiInitializedAdapter(new OnUiInitializedAdapter() { @Override public void onUiInitialized() { ModeSelectionBottomSheet dialog = (ModeSelectionBottomSheet) SelectIndexesUiHelper.this.dialog; @@ -169,7 +169,7 @@ public class SelectIndexesUiHelper { private List createSrtmRadioItems() { List radioItems = new ArrayList<>(); radioItems.add(createSrtmRadioBtn(R.string.shared_string_meters, true)); - radioItems.add(createSrtmRadioBtn(R.string.shared_string_feets, false)); + radioItems.add(createSrtmRadioBtn(R.string.shared_string_feet, false)); return radioItems; } @@ -193,7 +193,7 @@ public class SelectIndexesUiHelper { for (SelectableItem item : items) { DownloadItem downloadItem = (DownloadItem) item.getObject(); if (downloadItem instanceof SrtmDownloadItem) { - ((SrtmDownloadItem) downloadItem).setUseMeters(useMeters); + ((SrtmDownloadItem) downloadItem).setUseMetric(useMeters); updateSelectableItem(item, downloadItem); } } @@ -208,12 +208,12 @@ public class SelectIndexesUiHelper { private void updateSelectableItem(SelectableItem selectableItem, DownloadItem downloadItem) { - selectableItem.setTitle( - downloadItem.getVisibleName(app, app.getRegions(), false)); + selectableItem.setTitle(downloadItem.getVisibleName(app, app.getRegions(), false)); String size = downloadItem.getSizeDescription(app); - if (downloadItem.isUseAbbreviation()) { - size += " " + downloadItem.getAbbreviationInScopes(app); + String addDescr = downloadItem.getAdditionalDescription(app); + if (addDescr != null) { + size += " " + addDescr; } String date = downloadItem.getDate(dateFormat, showRemoteDate); String description = app.getString(R.string.ltr_or_rtl_combine_via_bold_point, size, date); diff --git a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java index a9a184d57c..8567f97409 100644 --- a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java @@ -10,6 +10,7 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.helpers.enums.MetricsConstants; +import net.osmand.util.Algorithms; import java.io.File; import java.text.DateFormat; @@ -24,31 +25,40 @@ import static net.osmand.plus.download.DownloadActivityType.SRTM_COUNTRY_FILE; public class SrtmDownloadItem extends DownloadItem { private final List indexes; - private boolean useMeters; + private boolean useMetric; - public SrtmDownloadItem(List indexes, - boolean useMeters) { + public SrtmDownloadItem(List indexes, boolean useMetric) { super(SRTM_COUNTRY_FILE); this.indexes = indexes; - this.useMeters = useMeters; + this.useMetric = useMetric; } - public void setUseMeters(boolean useMeters) { - this.useMeters = useMeters; + public void setUseMetric(boolean useMetric) { + this.useMetric = useMetric; } - public boolean isUseMeters() { - return useMeters; + public boolean isUseMetric() { + for (IndexItem index : indexes) { + if (index.isDownloaded()) { + return isMetricItem(index); + } + } + return useMetric; } - @Nullable + @NonNull public IndexItem getIndexItem() { for (IndexItem index : indexes) { - if (useMeters && isMetersItem(index) || !useMeters && !isMetersItem(index)) { + if (index.isDownloaded()) { return index; } } - return null; + for (IndexItem index : indexes) { + if (useMetric && isMetricItem(index) || !useMetric && !isMetricItem(index)) { + return index; + } + } + return indexes.get(0); } @Override @@ -83,13 +93,12 @@ public class SrtmDownloadItem extends DownloadItem { @Override public boolean hasActualDataToDownload() { - // may be check only downloaded items if any downloaded for (IndexItem item : indexes) { - if (item.hasActualDataToDownload()) { - return true; + if (!item.hasActualDataToDownload()) { + return false; } } - return false; + return true; } @Override @@ -110,7 +119,6 @@ public class SrtmDownloadItem extends DownloadItem { @NonNull @Override public List getDownloadedFiles(@NonNull OsmandApplication app) { - // may be check both indexes files List result = new ArrayList<>(); for (IndexItem index : indexes) { result.addAll(index.getDownloadedFiles(app)); @@ -119,17 +127,11 @@ public class SrtmDownloadItem extends DownloadItem { } public String getDate(@NonNull DateFormat dateFormat, boolean remote) { - // may be check only downloaded items if any downloaded return getIndexItem().getDate(dateFormat, remote); } @Override - public boolean isUseAbbreviation() { - return true; - } - - @Override - public String getAbbreviationInScopes(Context ctx) { + public @Nullable String getAdditionalDescription(Context ctx) { return getAbbreviationInScopes(ctx, this); } @@ -140,27 +142,8 @@ public class SrtmDownloadItem extends DownloadItem { @NonNull public static String getAbbreviationInScopes(Context ctx, Object obj) { - return "(" + getAbbreviation(ctx, obj) + ")"; - } - - @NonNull - public static String getAbbreviation(Context context, Object obj) { - return context.getString(isMetersItem(obj) ? R.string.m : R.string.foot); - } - - public static boolean isMetersItem(Object item) { - if (item instanceof IndexItem) { - return ((IndexItem) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT_ZIP); - } else if (item instanceof LocalIndexInfo) { - return ((LocalIndexInfo) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT); - } else if (item instanceof SrtmDownloadItem) { - return ((SrtmDownloadItem) item).useMeters; - } else if (item instanceof MultipleDownloadItem) { - for (DownloadItem downloadItem : ((MultipleDownloadItem) item).getAllItems()) { - return isMetersItem(downloadItem); - } - } - return false; + String abbreviation = ctx.getString(isMetricItem(obj) ? R.string.m : R.string.foot); + return "(" + abbreviation + ")"; } public static boolean containsSrtmExtension(@NonNull String fileName) { @@ -175,15 +158,13 @@ public class SrtmDownloadItem extends DownloadItem { @NonNull public static String getExtension(IndexItem indexItem) { - return isMetersItem(indexItem) ? + return isMetricItem(indexItem) ? IndexConstants.BINARY_SRTM_MAP_INDEX_EXT : IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT; } public static boolean isSRTMItem(Object item) { - if (item instanceof IndexItem) { - return ((IndexItem) item).getType() == SRTM_COUNTRY_FILE; - } else if (item instanceof DownloadItem) { + if (item instanceof DownloadItem) { return ((DownloadItem) item).getType() == SRTM_COUNTRY_FILE; } else if (item instanceof LocalIndexInfo) { return ((LocalIndexInfo) item).getType() == SRTM_DATA; @@ -191,4 +172,20 @@ public class SrtmDownloadItem extends DownloadItem { return false; } + private static boolean isMetricItem(Object item) { + if (item instanceof IndexItem) { + return ((IndexItem) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT_ZIP); + } else if (item instanceof LocalIndexInfo) { + return ((LocalIndexInfo) item).getFileName().endsWith(BINARY_SRTM_MAP_INDEX_EXT); + } else if (item instanceof SrtmDownloadItem) { + return isMetricItem(((SrtmDownloadItem) item).getIndexItem()); + } else if (item instanceof MultipleDownloadItem) { + List items = ((MultipleDownloadItem) item).getAllItems(); + if (!Algorithms.isEmpty(items)) { + return isMetricItem(items.get(0)); + } + } + return false; + } + } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index 1bca97cfe1..135db84bc9 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -227,8 +227,9 @@ public class ItemViewHolder { count = allRegionsCount; } String fullDescription = context.getString(R.string.ltr_or_rtl_combine_via_colon, header, count); - if (item.isUseAbbreviation()) { - fullDescription += " " + item.getAbbreviationInScopes(context); + String addDescr = item.getAdditionalDescription(context); + if (addDescr != null) { + fullDescription += " " + addDescr; } if (item.hasActualDataToDownload()) { fullDescription = context.getString( @@ -240,8 +241,9 @@ public class ItemViewHolder { String pattern = context.getString(R.string.ltr_or_rtl_combine_via_bold_point); String type = downloadItem.getType().getString(context); String size = downloadItem.getSizeDescription(context); - if (downloadItem.isUseAbbreviation()) { - size += " " + downloadItem.getAbbreviationInScopes(context); + String addDescr = downloadItem.getAdditionalDescription(context); + if (addDescr != null) { + size += " " + addDescr; } String date = downloadItem.getDate(dateFormat, showRemoteDate); String fullDescription = String.format(pattern, size, date); From 08baaf7004e4c743734ab7ddb0ac915d80d8fd57 Mon Sep 17 00:00:00 2001 From: Victor Shcherb Date: Mon, 19 Apr 2021 20:10:19 +0200 Subject: [PATCH 67/86] Fix #11376 --- .../src/main/java/net/osmand/search/core/SearchResult.java | 4 +++- .../src/test/java/net/osmand/search/SearchUICoreTest.java | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchResult.java b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchResult.java index 79d11a3fe0..60269fe5d4 100644 --- a/OsmAnd-java/src/main/java/net/osmand/search/core/SearchResult.java +++ b/OsmAnd-java/src/main/java/net/osmand/search/core/SearchResult.java @@ -55,7 +55,9 @@ public class SearchResult { public double getSumPhraseMatchWeight() { // if result is a complete match in the search we prioritize it higher - boolean match = requiredSearchPhrase.countWords(localeName) <= getSelfWordCount(); + int localWordsMatched = alternateName != null ? + requiredSearchPhrase.countWords(alternateName) : requiredSearchPhrase.countWords(localeName) ; + boolean match = localWordsMatched <= getSelfWordCount(); double res = ObjectType.getTypeWeight(match ? objectType : null); if (parentSearchResult != null) { res = res + parentSearchResult.getSumPhraseMatchWeight() / MAX_TYPE_WEIGHT; diff --git a/OsmAnd-java/src/test/java/net/osmand/search/SearchUICoreTest.java b/OsmAnd-java/src/test/java/net/osmand/search/SearchUICoreTest.java index f3ed25ca0b..07e7339401 100644 --- a/OsmAnd-java/src/test/java/net/osmand/search/SearchUICoreTest.java +++ b/OsmAnd-java/src/test/java/net/osmand/search/SearchUICoreTest.java @@ -80,9 +80,9 @@ public class SearchUICoreTest { if (files != null) { for (File file : files) { String fileName = file.getName(); - if(fileName.endsWith(".json")) { + if (fileName.endsWith(".json")) { String name = fileName.substring(0, fileName.length() - ".json".length()); - arrayList.add(new Object[] {name, file}); + arrayList.add(new Object[] { name, file }); } } } @@ -191,10 +191,10 @@ public class SearchUICoreTest { if (!Algorithms.stringsEqual(expected, present)) { System.out.println(String.format("Phrase: %s", phrase)); System.out.println(String.format("Mismatch for '%s' != '%s'. Result: ", expected, present)); + } for (SearchResult r : searchResults) { System.out.println(String.format("\t\"%s\",", formatResult(false, r, phrase))); } - } Assert.assertEquals(expected, present); } } From 551dd084ad53d00c4b0e0a0a6d70376db4a0acf4 Mon Sep 17 00:00:00 2001 From: cepprice Date: Tue, 20 Apr 2021 12:01:27 +0500 Subject: [PATCH 68/86] Fix jumping map controls --- .../layout-land/fragment_measurement_tool.xml | 32 +- .../res/layout/fragment_measurement_tool.xml | 535 +++++++++--------- .../MeasurementToolFragment.java | 5 +- 3 files changed, 303 insertions(+), 269 deletions(-) diff --git a/OsmAnd/res/layout-land/fragment_measurement_tool.xml b/OsmAnd/res/layout-land/fragment_measurement_tool.xml index 505e680262..54ba1513ec 100644 --- a/OsmAnd/res/layout-land/fragment_measurement_tool.xml +++ b/OsmAnd/res/layout-land/fragment_measurement_tool.xml @@ -16,20 +16,34 @@ android:focusable="true" android:orientation="vertical" android:clickable="true" - tools:background="@drawable/bg_bottom_menu_dark"> + tools:ignore="UselessParent"> - + android:layout_height="wrap_content"> + + + + + + + android:layout_height="match_parent" + android:background="?attr/list_background_color"> - - - - - - - - - - - - - - - - - - - + android:focusable="true" + tools:ignore="UselessParent"> - + android:layout_height="0dp" + android:background="@drawable/bg_contextmenu_shadow_top_light" /> + + - - - - + android:layout_height="wrap_content" + android:background="?attr/list_background_color" + android:orientation="vertical"> - + - + + + + + + + + - + android:textColor="?android:textColorSecondary" + android:textSize="@dimen/default_desc_text_size" + tools:text=" – 700 m" /> - + - + + + android:id="@+id/info_type_buttons_container" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + layout="@layout/custom_radio_buttons" + android:layout_width="match_parent" + android:layout_height="@dimen/measurement_tool_button_height" + android:layout_marginStart="@dimen/content_padding" + android:layout_marginEnd="@dimen/content_padding" + android:layout_marginBottom="@dimen/measurement_tool_content_padding_medium" /> - - - - - - - - - - - - + + android:id="@+id/bottom_panel_divider" + android:layout_width="match_parent" + android:layout_height="1dp" + android:background="?attr/dashboard_divider" /> - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java index 49815c05dd..4ae798967b 100644 --- a/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java +++ b/OsmAnd/src/net/osmand/plus/measurementtool/MeasurementToolFragment.java @@ -261,7 +261,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route @Override public void hideProgressBar() { - ((ProgressBar) mainView.findViewById(R.id.snap_to_road_progress_bar)).setVisibility(View.GONE); + ((ProgressBar) mainView.findViewById(R.id.snap_to_road_progress_bar)).setVisibility(View.INVISIBLE); progressBarVisible = false; updateInfoView(); updateCardContainerSize(); @@ -287,7 +287,6 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route .inflate(R.layout.fragment_measurement_tool, container, false); mainView = view.findViewById(R.id.main_view); - AndroidUtils.setBackground(mapActivity, mainView, nightMode, R.drawable.bg_bottom_menu_light, R.drawable.bg_bottom_menu_dark); detailsMenu = new GraphDetailsMenu(); if (portrait) { cardsContainer = mainView.findViewById(R.id.cards_container); @@ -1390,7 +1389,7 @@ public class MeasurementToolFragment extends BaseOsmAndFragment implements Route editingCtx.cancelSnapToRoad(); MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { - mainView.findViewById(R.id.snap_to_road_progress_bar).setVisibility(View.GONE); + mainView.findViewById(R.id.snap_to_road_progress_bar).setVisibility(View.INVISIBLE); mapActivity.refreshMap(); } } From e65eb4cb84a19244f118a4df35bdc08869f79afd Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Tue, 20 Apr 2021 11:02:03 +0300 Subject: [PATCH 69/86] Fix graphs card touches passing through --- OsmAnd/res/layout/measurement_tool_graph_card.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OsmAnd/res/layout/measurement_tool_graph_card.xml b/OsmAnd/res/layout/measurement_tool_graph_card.xml index 272b9a4d2e..aebc1554a7 100644 --- a/OsmAnd/res/layout/measurement_tool_graph_card.xml +++ b/OsmAnd/res/layout/measurement_tool_graph_card.xml @@ -5,6 +5,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/list_background_color" + android:clickable="true" + android:focusable="true" android:orientation="vertical"> Date: Tue, 20 Apr 2021 11:23:27 +0300 Subject: [PATCH 70/86] fix after merge --- .../src/net/osmand/plus/base/ModeSelectionBottomSheet.java | 2 +- .../plus/base/MultipleSelectionWithModeBottomSheet.java | 2 +- OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java | 7 ++++--- .../net/osmand/plus/download/SelectIndexesUiHelper.java | 7 ++++--- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/base/ModeSelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/ModeSelectionBottomSheet.java index f8355c8805..a19e304299 100644 --- a/OsmAnd/src/net/osmand/plus/base/ModeSelectionBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/ModeSelectionBottomSheet.java @@ -14,7 +14,7 @@ import androidx.fragment.app.FragmentManager; import net.osmand.AndroidUtils; import net.osmand.plus.R; -import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem; import java.util.Collections; import java.util.List; diff --git a/OsmAnd/src/net/osmand/plus/base/MultipleSelectionWithModeBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionWithModeBottomSheet.java index 261e8dfdea..9ac4948937 100644 --- a/OsmAnd/src/net/osmand/plus/base/MultipleSelectionWithModeBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionWithModeBottomSheet.java @@ -8,7 +8,7 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.FragmentManager; -import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem; import java.util.List; diff --git a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java index 18c91f8cf5..f861500078 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java @@ -19,8 +19,9 @@ import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.SimpleDividerItem; import net.osmand.plus.helpers.AndroidUiHelper; -import net.osmand.plus.widgets.MultiStateToggleButton; -import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.MultiStateToggleButton; +import net.osmand.plus.widgets.multistatetoggle.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton; import net.osmand.util.Algorithms; import net.osmand.view.ThreeStateCheckbox; @@ -91,7 +92,7 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment secondaryDescription = view.findViewById(R.id.secondary_description); selectedSize = view.findViewById(R.id.selected_size); toggleContainer = view.findViewById(R.id.custom_radio_buttons); - radioGroup = new MultiStateToggleButton(app, toggleContainer, nightMode); + radioGroup = new TextToggleButton(app, toggleContainer, nightMode); selectAllButton = view.findViewById(R.id.select_all_button); checkBoxTitle = view.findViewById(R.id.check_box_title); checkBox = view.findViewById(R.id.check_box); diff --git a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java index f7835ce348..845082daae 100644 --- a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java @@ -15,8 +15,9 @@ import net.osmand.plus.base.SelectionBottomSheet; import net.osmand.plus.base.SelectionBottomSheet.OnApplySelectionListener; import net.osmand.plus.base.SelectionBottomSheet.OnUiInitializedAdapter; import net.osmand.plus.base.SelectionBottomSheet.SelectableItem; -import net.osmand.plus.widgets.MultiStateToggleButton.OnRadioItemClickListener; -import net.osmand.plus.widgets.MultiStateToggleButton.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem; +import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener; +import net.osmand.plus.widgets.multistatetoggle.TextToggleButton.TextRadioItem; import net.osmand.util.Algorithms; import java.text.DateFormat; @@ -176,7 +177,7 @@ public class SelectIndexesUiHelper { private RadioItem createSrtmRadioBtn(int titleId, final boolean useMeters) { String title = Algorithms.capitalizeFirstLetter(app.getString(titleId)); - RadioItem radioItem = new RadioItem(title); + RadioItem radioItem = new TextRadioItem(title); radioItem.setOnClickListener(new OnRadioItemClickListener() { @Override public boolean onRadioItemClick(RadioItem radioItem, View view) { From 11ddc65409efaa9791ddff5542c6a229f4439c03 Mon Sep 17 00:00:00 2001 From: vshcherb Date: Tue, 20 Apr 2021 10:35:39 +0200 Subject: [PATCH 71/86] Update MapObject.java --- OsmAnd-java/src/main/java/net/osmand/data/MapObject.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java b/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java index e02b884e0a..c149b79528 100644 --- a/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java +++ b/OsmAnd-java/src/main/java/net/osmand/data/MapObject.java @@ -184,7 +184,7 @@ public abstract class MapObject implements Comparable { if (lang != null && lang.length() > 0) { if (lang.equals("en")) { // for some objects like wikipedia, english name is stored 'name' tag - if (Algorithms.isEmpty(enName)) { + if (!Algorithms.isEmpty(enName)) { return getEnName(transliterate); } } else { From 6eb665c9a8c0a43d97b999b3bb3a3e1501e388e7 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Tue, 20 Apr 2021 12:32:23 +0300 Subject: [PATCH 72/86] Live Updates App Bar: fix back arrow --- .../src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java index 9f91391af3..c4fa160e08 100644 --- a/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java +++ b/OsmAnd/src/net/osmand/plus/liveupdates/LiveUpdatesFragment.java @@ -282,6 +282,9 @@ public class LiveUpdatesFragment extends BaseOsmAndDialogFragment implements OnL dismiss(); } }); + if (closeButton instanceof ImageView) { + UiUtilities.rotateImageByLayoutDirection((ImageView) closeButton); + } FrameLayout iconHelpContainer = toolbar.findViewById(R.id.action_button); int iconColorResId = nightMode ? R.color.active_buttons_and_links_text_dark : R.color.active_buttons_and_links_text_light; From d2598612ff75e7cb9ed97c2cce6577cb287388fd Mon Sep 17 00:00:00 2001 From: Dmitry Date: Tue, 20 Apr 2021 12:36:01 +0300 Subject: [PATCH 73/86] Added new changes to release notes --- OsmAnd/res/values/strings.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 3897ad1806..3008963ea3 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -22,6 +22,8 @@ feet Update all maps added to %1$s? + • Added option to download Contour lines in feet\n\n + • Plan Route landscape: added tabs to switch between points or graphs\n\n • OsmAnd Live updates moved to \"Downloads > Updates\"\n\n • Tracks now could be colorizing by altitude, speed, or slope.\n\n • Added option to change the appearance of the navigation route line\n\n From ec0ea31b53419104146b6a55cd5f64230a73a56f Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Tue, 20 Apr 2021 13:42:39 +0300 Subject: [PATCH 74/86] Fix rtept drawing --- .../osmand/plus/views/layers/GPXLayer.java | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index 7ce7ec3feb..db26c11019 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -85,6 +85,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import static net.osmand.GPXUtilities.calculateTrackBounds; import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_COLOR_ATTR; import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_WIDTH_ATTR; @@ -460,7 +461,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM QuadRect correctedQuadRect = getCorrectedQuadRect(tileBox.getLatLonBounds()); for (SelectedGpxFile selectedGpxFile : selectedGPXFiles) { boolean showArrows = isShowArrowsForTrack(selectedGpxFile.getGpxFile()); - if (!showArrows || !QuadRect.trivialOverlap(correctedQuadRect, GPXUtilities.calculateTrackBounds(selectedGpxFile.getPointsToDisplay()))) { + if (!showArrows || !QuadRect.trivialOverlap(correctedQuadRect, calculateTrackBounds(selectedGpxFile.getPointsToDisplay()))) { continue; } String width = getTrackWidthName(selectedGpxFile.getGpxFile(), defaultTrackWidthPref.get()); @@ -689,18 +690,18 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM } } - private void drawSelectedFileSegments(SelectedGpxFile selectedGpxFile, boolean currentTrack, Canvas canvas, - RotatedTileBox tileBox, DrawSettings settings) { - boolean visible = QuadRect.trivialOverlap(tileBox.getLatLonBounds(), GPXUtilities.calculateTrackBounds(selectedGpxFile.getPointsToDisplay())); - if (!selectedGpxFile.getGpxFile().hasTrkPt() || !visible) { + private void drawSelectedFileSegments(SelectedGpxFile selectedGpxFile, boolean currentTrack, + Canvas canvas, RotatedTileBox tileBox, DrawSettings settings) { + GPXFile gpxFile = selectedGpxFile.getGpxFile(); + GradientScaleType scaleType = getGradientScaleType(gpxFile); + + boolean visible = QuadRect.trivialOverlap(tileBox.getLatLonBounds(), calculateTrackBounds(selectedGpxFile.getPointsToDisplay())); + if (!gpxFile.hasTrkPt() && scaleType != null || !visible) { segmentsCache.remove(selectedGpxFile.getGpxFile().path); return; } - GPXFile gpxFile = selectedGpxFile.getGpxFile(); - GradientScaleType scaleType = getGradientScaleType(gpxFile); List segments = new ArrayList<>(); - if (scaleType == null) { segments.addAll(selectedGpxFile.getPointsToDisplay()); } else { @@ -1059,7 +1060,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM for (SelectedGpxFile gpx : selectedGpxFiles) { selectedTracksPaths.add(gpx.getGpxFile().path); } - for (Iterator iterator = cachedTracksPaths.iterator(); iterator.hasNext();) { + for (Iterator iterator = cachedTracksPaths.iterator(); iterator.hasNext(); ) { if (!selectedTracksPaths.contains(iterator.next())) { iterator.remove(); } @@ -1271,7 +1272,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM RouteColorize colorize = new RouteColorize(zoom, gpxFile, selectedGpxFile.getTrackAnalysis(app), scaleType.toColorizationType(), app.getSettings().getApplicationMode().getMaxSpeed()); colorize.setPalette(gradientPalette); - List colorsOfPoints = colorize.getResult(true); + List colorsOfPoints = colorize.getResult(true); return createSimplifiedSegments(selectedGpxFile.getGpxFile(), colorsOfPoints, scaleType); } From 658b157bb2cb02bbf075d662f0d6c60a1eb28344 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Tue, 20 Apr 2021 14:29:35 +0300 Subject: [PATCH 75/86] Fix description id --- .../osmand/plus/settings/fragments/ExportItemsBottomSheet.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java index 5e4b4f0449..6357eb94bf 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java @@ -278,7 +278,7 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { } private String setupDescription(View view) { - TextView description = view.findViewById(R.id.description); + TextView description = view.findViewById(R.id.title_description); if (type == ExportSettingsType.FAVORITES) { description.setText(R.string.select_groups_for_import); } else { From 2e9238aff8354a189a73ab276aadb8562e623352 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Tue, 20 Apr 2021 17:36:32 +0300 Subject: [PATCH 76/86] Fix check for multi-user storage --- OsmAnd/src/net/osmand/AndroidUtils.java | 44 +++++++++++++++---- .../audionotes/AudioVideoNotesPlugin.java | 9 +--- .../audionotes/MultimediaNotesFragment.java | 13 +----- .../dashboard/DashChooseAppDirFragment.java | 17 +------ .../plus/download/DownloadActivity.java | 2 +- .../plus/download/DownloadIndexesThread.java | 19 ++------ .../ui/DataStoragePlaceDialogFragment.java | 28 ++---------- .../firstusage/FirstUsageWizardFragment.java | 26 +++-------- .../fragments/BaseSettingsListFragment.java | 4 +- 9 files changed, 56 insertions(+), 106 deletions(-) diff --git a/OsmAnd/src/net/osmand/AndroidUtils.java b/OsmAnd/src/net/osmand/AndroidUtils.java index 4f0729d355..96e44c652e 100644 --- a/OsmAnd/src/net/osmand/AndroidUtils.java +++ b/OsmAnd/src/net/osmand/AndroidUtils.java @@ -22,6 +22,8 @@ import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.StateListDrawable; import android.net.Uri; import android.os.Build; +import android.os.Build.VERSION; +import android.os.Build.VERSION_CODES; import android.os.IBinder; import android.os.PowerManager; import android.os.StatFs; @@ -264,6 +266,11 @@ public class AndroidUtils { return ""; } + public static String getFreeSpace(Context ctx, File dir) { + long size = AndroidUtils.getAvailableSpace(dir); + return AndroidUtils.formatSize(ctx, size); + } + public static View findParentViewById(View view, int id) { ViewParent viewParent = view.getParent(); @@ -856,11 +863,39 @@ public class AndroidUtils { return result; } + public static long getAvailableSpace(@NonNull OsmandApplication app) { + return getAvailableSpace(app.getAppPath(null)); + } + + public static long getTotalSpace(@NonNull OsmandApplication app) { + return getTotalSpace(app.getAppPath(null)); + } + public static long getAvailableSpace(@Nullable File dir) { if (dir != null && dir.canRead()) { try { StatFs fs = new StatFs(dir.getAbsolutePath()); - return fs.getAvailableBlocksLong() * fs.getBlockSize(); + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) { + return fs.getAvailableBlocksLong() * fs.getBlockSizeLong(); + } else { + return fs.getAvailableBlocks() * fs.getBlockSize(); + } + } catch (IllegalArgumentException e) { + LOG.error(e); + } + } + return -1; + } + + public static long getTotalSpace(@Nullable File dir) { + if (dir != null && dir.canRead()) { + try { + StatFs fs = new StatFs(dir.getAbsolutePath()); + if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) { + return fs.getBlockCountLong() * fs.getBlockSizeLong(); + } else { + return fs.getBlockCount() * fs.getBlockSize(); + } } catch (IllegalArgumentException e) { LOG.error(e); } @@ -887,13 +922,6 @@ public class AndroidUtils { return -1; } - public static float getUsedSpaceGb(File dir) { - if (dir.canRead()) { - return getTotalSpaceGb(dir) - getFreeSpaceGb(dir); - } - return -1; - } - public static CharSequence getStyledString(CharSequence baseString, CharSequence stringToInsertAndStyle, CharacterStyle baseStyle, CharacterStyle replaceStyle) { int indexOfPlaceholder = baseString.toString().indexOf(STRING_PLACEHOLDER); diff --git a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java index 0bf4bb55fb..2a09ffe3e9 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/AudioVideoNotesPlugin.java @@ -22,7 +22,6 @@ import android.media.MediaRecorder; import android.media.SoundPool; import android.net.Uri; import android.os.Build; -import android.os.StatFs; import android.provider.MediaStore; import android.view.Display; import android.view.KeyEvent; @@ -1607,13 +1606,7 @@ public class AudioVideoNotesPlugin extends OsmandPlugin { double bitrate = (((p.videoBitRate + p.audioBitRate) / 8f) * 60f) / (1 << 30); // gigabytes per minute double clipSpace = bitrate * AV_RS_CLIP_LENGTH.get(); double storageSize = AV_RS_STORAGE_SIZE.get(); - - double availableSpace = storageSize; - File dir = app.getAppPath("").getParentFile(); - if (dir.canRead()) { - StatFs fs = new StatFs(dir.getAbsolutePath()); - availableSpace = (double) (fs.getAvailableBlocks()) * fs.getBlockSize() / (1 << 30) - clipSpace; - } + double availableSpace = (double) AndroidUtils.getAvailableSpace(app) / (1 << 30) - clipSpace; if (usedSpace + clipSpace > storageSize || clipSpace > availableSpace) { Arrays.sort(files, new Comparator() { diff --git a/OsmAnd/src/net/osmand/plus/audionotes/MultimediaNotesFragment.java b/OsmAnd/src/net/osmand/plus/audionotes/MultimediaNotesFragment.java index 3123b81c9b..5481908653 100644 --- a/OsmAnd/src/net/osmand/plus/audionotes/MultimediaNotesFragment.java +++ b/OsmAnd/src/net/osmand/plus/audionotes/MultimediaNotesFragment.java @@ -11,7 +11,6 @@ import android.media.CamcorderProfile; import android.media.MediaRecorder; import android.os.Build; import android.os.Bundle; -import android.os.StatFs; import android.text.SpannableString; import android.view.LayoutInflater; import android.view.View; @@ -42,7 +41,6 @@ import net.osmand.plus.widgets.style.CustomTypefaceSpan; import org.apache.commons.logging.Log; -import java.io.File; import java.util.ArrayList; import java.util.List; @@ -381,16 +379,7 @@ public class MultimediaNotesFragment extends BaseSettingsFragment implements Cop private void setupStorageSizePref(AudioVideoNotesPlugin plugin) { ListPreferenceEx storageSize = (ListPreferenceEx) findPreference(plugin.AV_RS_STORAGE_SIZE.getId()); - File dir = app.getAppPath("").getParentFile(); - long size = 0; - if (dir.canRead()) { - try { - StatFs fs = new StatFs(dir.getAbsolutePath()); - size = ((long) fs.getBlockSize() * (long) fs.getBlockCount()) / (1 << 30); - } catch (IllegalArgumentException e) { - log.error(e); - } - } + long size = AndroidUtils.getTotalSpace(app) / (1 << 30); if (size > 0) { int value = 1; ArrayList gbList = new ArrayList<>(); diff --git a/OsmAnd/src/net/osmand/plus/dashboard/DashChooseAppDirFragment.java b/OsmAnd/src/net/osmand/plus/dashboard/DashChooseAppDirFragment.java index bd8918ac4d..34c5659a4f 100644 --- a/OsmAnd/src/net/osmand/plus/dashboard/DashChooseAppDirFragment.java +++ b/OsmAnd/src/net/osmand/plus/dashboard/DashChooseAppDirFragment.java @@ -11,7 +11,6 @@ import android.content.DialogInterface; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.os.StatFs; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; @@ -34,10 +33,10 @@ import net.osmand.FileUtils; import net.osmand.PlatformUtil; import net.osmand.ValueHolder; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.ProgressImplementation; import net.osmand.plus.R; import net.osmand.plus.download.DownloadActivity; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; @@ -93,18 +92,6 @@ public class DashChooseAppDirFragment { selectePathTemp = null; } - private String getFreeSpace(File dir) { - if (dir.canRead()) { - try { - StatFs fs = new StatFs(dir.getAbsolutePath()); - return AndroidUtils.formatSize(activity, (long) fs.getAvailableBlocks() * fs.getBlockSize()); - } catch (IllegalArgumentException e) { - LOG.error(e); - } - } - return ""; - } - public void updateView() { if (type == OsmandSettings.EXTERNAL_STORAGE_TYPE_INTERNAL_FILE) { locationPath.setText(R.string.storage_directory_internal_app); @@ -117,7 +104,7 @@ public class DashChooseAppDirFragment { } else if (type == OsmandSettings.EXTERNAL_STORAGE_TYPE_SPECIFIED) { locationPath.setText(R.string.storage_directory_manual); } - locationDesc.setText(selectedFile.getAbsolutePath() + " \u2022 " + getFreeSpace(selectedFile)); + locationDesc.setText(selectedFile.getAbsolutePath() + " \u2022 " + AndroidUtils.getFreeSpace(activity, selectedFile)); boolean copyFiles = !currentAppFile.getAbsolutePath().equals(selectedFile.getAbsolutePath()) && !mapsCopied; warningReadonly.setVisibility(copyFiles ? View.VISIBLE : View.GONE); if (copyFiles) { diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadActivity.java b/OsmAnd/src/net/osmand/plus/download/DownloadActivity.java index ed45d50e84..65e6a8f855 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadActivity.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadActivity.java @@ -652,7 +652,7 @@ public class DownloadActivity extends AbstractDownloadActivity implements Downlo TextView messageTextView = (TextView) view.findViewById(R.id.leftTextView); ProgressBar sizeProgress = (ProgressBar) view.findViewById(R.id.progressBar); - File dir = activity.getMyApplication().getAppPath("").getParentFile(); + File dir = activity.getMyApplication().getAppPath(null); String size = ""; int percent = 0; if (dir.canRead()) { diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java b/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java index 568dcc9ed6..45d9b77402 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadIndexesThread.java @@ -9,7 +9,6 @@ import android.net.TrafficStats; import android.net.Uri; import android.os.AsyncTask; import android.os.AsyncTask.Status; -import android.os.StatFs; import android.view.View; import android.widget.Toast; @@ -17,13 +16,12 @@ import androidx.annotation.UiThread; import androidx.appcompat.app.AlertDialog; import net.osmand.AndroidNetworkUtils; +import net.osmand.AndroidUtils; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.map.WorldRegion; import net.osmand.map.WorldRegion.RegionParams; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; -import net.osmand.plus.settings.backend.OsmandPreference; import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.base.BasicProgressAsyncTask; @@ -31,6 +29,8 @@ import net.osmand.plus.download.DownloadFileHelper.DownloadFileShowWarning; import net.osmand.plus.helpers.DatabaseHelper; import net.osmand.plus.notifications.OsmandNotification; import net.osmand.plus.resources.ResourceManager; +import net.osmand.plus.settings.backend.OsmandPreference; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.util.Algorithms; import org.apache.commons.logging.Log; @@ -302,19 +302,8 @@ public class DownloadIndexesThread { return null; } - @SuppressWarnings("deprecation") public double getAvailableSpace() { - File dir = app.getAppPath("").getParentFile(); - double asz = -1; - if (dir.canRead()) { - try { - StatFs fs = new StatFs(dir.getAbsolutePath()); - asz = (((long) fs.getAvailableBlocks()) * fs.getBlockSize()) / (1 << 20); - } catch (IllegalArgumentException e) { - LOG.error(e); - } - } - return asz; + return AndroidUtils.getAvailableSpace(app) / (1 << 20); } /// PRIVATE IMPL diff --git a/OsmAnd/src/net/osmand/plus/download/ui/DataStoragePlaceDialogFragment.java b/OsmAnd/src/net/osmand/plus/download/ui/DataStoragePlaceDialogFragment.java index be619f324f..76bc14f5af 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/DataStoragePlaceDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/DataStoragePlaceDialogFragment.java @@ -5,7 +5,6 @@ import android.content.DialogInterface; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.os.StatFs; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -17,8 +16,6 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; -import com.ibm.icu.impl.IllegalIcuArgumentException; - import androidx.annotation.NonNull; import androidx.fragment.app.FragmentManager; @@ -28,12 +25,12 @@ import net.osmand.IProgress; import net.osmand.PlatformUtil; import net.osmand.plus.OnDismissDialogFragmentListener; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.base.BottomSheetDialogFragment; import net.osmand.plus.dashboard.DashChooseAppDirFragment; import net.osmand.plus.download.DownloadActivity; import net.osmand.plus.download.DownloadIndexesThread; +import net.osmand.plus.settings.backend.OsmandSettings; import org.apache.commons.logging.Log; @@ -119,7 +116,7 @@ public class DataStoragePlaceDialogFragment extends BottomSheetDialogFragment { deviceStorageImageView.setImageDrawable(getContentIcon(R.drawable.ic_action_phone)); TextView deviceStorageDescription = (TextView) view.findViewById(R.id.deviceMemoryDescription); deviceStorageDescription.setText(deviceStorageName); - deviceStorageDescription.setText(getFreeSpace(deviceStorage)); + deviceStorageDescription.setText(AndroidUtils.getFreeSpace(activity, deviceStorage)); View sharedMemoryRow = view.findViewById(R.id.sharedMemoryRow); if (hasExternalStoragePermission && sharedStorage != null) { @@ -127,7 +124,7 @@ public class DataStoragePlaceDialogFragment extends BottomSheetDialogFragment { ImageView sharedMemoryImageView = (ImageView) view.findViewById(R.id.sharedMemoryImageView); sharedMemoryImageView.setImageDrawable(getContentIcon(R.drawable.ic_action_phone)); TextView sharedMemoryDescription = (TextView) view.findViewById(R.id.sharedMemoryDescription); - sharedMemoryDescription.setText(getFreeSpace(sharedStorage)); + sharedMemoryDescription.setText(AndroidUtils.getFreeSpace(activity, sharedStorage)); } else { view.findViewById(R.id.divSharedStorage).setVisibility(View.GONE); sharedMemoryRow.setVisibility(View.GONE); @@ -139,7 +136,7 @@ public class DataStoragePlaceDialogFragment extends BottomSheetDialogFragment { ImageView memoryStickImageView = (ImageView) view.findViewById(R.id.memoryStickImageView); memoryStickImageView.setImageDrawable(getContentIcon(R.drawable.ic_sdcard)); TextView memoryStickDescription = (TextView) view.findViewById(R.id.memoryStickDescription); - memoryStickDescription.setText(getFreeSpace(cardStorage)); + memoryStickDescription.setText(AndroidUtils.getFreeSpace(activity, cardStorage)); } else { view.findViewById(R.id.divExtStorage).setVisibility(View.GONE); memoryStickRow.setVisibility(View.GONE); @@ -192,23 +189,6 @@ public class DataStoragePlaceDialogFragment extends BottomSheetDialogFragment { .getDefaultInternalStorage(); } - private String getFreeSpace(File dir) { - String sz = ""; - if (dir != null && dir.canRead()) { - try { - StatFs fs = new StatFs(dir.getAbsolutePath()); - @SuppressWarnings("deprecation") - long size = (long) fs.getAvailableBlocks() * fs.getBlockSize(); - if (size > 0) { - sz = AndroidUtils.formatSize(getActivity(), size); - } - } catch (IllegalIcuArgumentException e) { - LOG.error(e); - } - } - return sz; - } - private void checkAssets() { getMyApplication().getResourceManager().checkAssets(IProgress.EMPTY_PROGRESS, true); } diff --git a/OsmAnd/src/net/osmand/plus/firstusage/FirstUsageWizardFragment.java b/OsmAnd/src/net/osmand/plus/firstusage/FirstUsageWizardFragment.java index f8c3935b25..8bbc8c456e 100644 --- a/OsmAnd/src/net/osmand/plus/firstusage/FirstUsageWizardFragment.java +++ b/OsmAnd/src/net/osmand/plus/firstusage/FirstUsageWizardFragment.java @@ -5,7 +5,6 @@ import android.content.DialogInterface; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; -import android.os.StatFs; import android.provider.Settings.Secure; import android.util.Log; import android.view.LayoutInflater; @@ -37,7 +36,6 @@ import net.osmand.plus.AppInitializer.AppInitializeListener; import net.osmand.plus.OsmAndLocationProvider; import net.osmand.plus.OsmAndLocationProvider.OsmAndLocationListener; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.R; import net.osmand.plus.Version; import net.osmand.plus.activities.MapActivity; @@ -51,12 +49,12 @@ import net.osmand.plus.download.IndexItem; import net.osmand.plus.download.ui.DataStoragePlaceDialogFragment; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.resources.ResourceManager; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; import org.json.JSONObject; -import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.LinkedHashMap; @@ -331,7 +329,7 @@ public class FirstUsageWizardFragment extends BaseOsmAndFragment implements OsmA FragmentActivity activity = getActivity(); if (!OsmAndLocationProvider.isLocationPermissionAvailable(activity)) { ActivityCompat.requestPermissions(activity, - new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + new String[] {Manifest.permission.ACCESS_FINE_LOCATION}, FIRST_USAGE_LOCATION_PERMISSION); } else { app.getLocationProvider().addLocationListener(this); @@ -387,13 +385,13 @@ public class FirstUsageWizardFragment extends BaseOsmAndFragment implements OsmA @Override public void onResume() { super.onResume(); - ((MapActivity)getActivity()).disableDrawer(); + ((MapActivity) getActivity()).disableDrawer(); } @Override public void onPause() { super.onPause(); - ((MapActivity)getActivity()).enableDrawer(); + ((MapActivity) getActivity()).enableDrawer(); } @Override @@ -697,7 +695,7 @@ public class FirstUsageWizardFragment extends BaseOsmAndFragment implements OsmA TextView freeSpaceValue = (TextView) storageView.findViewById(R.id.storage_free_space_value); String freeSpaceStr = getString(R.string.storage_free_space) + ": "; freeSpace.setText(freeSpaceStr); - freeSpaceValue.setText(getFreeSpace(settings.getExternalStorageDirectory())); + freeSpaceValue.setText(AndroidUtils.getFreeSpace(storageView.getContext(), settings.getExternalStorageDirectory())); AppCompatButton changeStorageButton = (AppCompatButton) storageView.findViewById(R.id.storage_change_button); if (wizardType == WizardType.MAP_DOWNLOAD) { @@ -709,7 +707,7 @@ public class FirstUsageWizardFragment extends BaseOsmAndFragment implements OsmA public void onClick(View v) { if (!DownloadActivity.hasPermissionToWriteExternalStorage(getContext())) { ActivityCompat.requestPermissions(getActivity(), - new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, + new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, FIRST_USAGE_REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION); } else { @@ -737,18 +735,6 @@ public class FirstUsageWizardFragment extends BaseOsmAndFragment implements OsmA } } - private String getFreeSpace(File dir) { - if (dir.canRead()) { - try { - StatFs fs = new StatFs(dir.getAbsolutePath()); - return AndroidUtils.formatSize(getActivity(), (long) fs.getAvailableBlocks() * fs.getBlockSize()); - } catch (IllegalArgumentException e) { - LOG.error(e); - } - } - return ""; - } - public static void showSearchLocationFragment(FragmentActivity activity, boolean searchByIp) { Fragment fragment = new FirstUsageWizardFragment(); Bundle args = new Bundle(); diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java index 6e2129deab..b7c5c6a0f9 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java @@ -36,7 +36,6 @@ import net.osmand.plus.settings.fragments.ExportSettingsAdapter.OnItemSelectedLi import net.osmand.plus.widgets.TextViewEx; import net.osmand.util.Algorithms; -import java.io.File; import java.util.ArrayList; import java.util.EnumMap; import java.util.LinkedHashMap; @@ -221,8 +220,7 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem if (calculatedSize != 0) { selectedItemsSize.setText(AndroidUtils.formatSize(app, calculatedSize)); - File dir = app.getAppPath("").getParentFile(); - long availableSizeBytes = AndroidUtils.getAvailableSpace(dir); + long availableSizeBytes = AndroidUtils.getAvailableSpace(app); if (calculatedSize > availableSizeBytes) { String availableSize = AndroidUtils.formatSize(app, availableSizeBytes); availableSpaceDescr.setText(getString(R.string.export_not_enough_space_descr, availableSize)); From 7525c925cab9ded4c84815c1db8a8176bf5be1b6 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Tue, 20 Apr 2021 17:54:22 +0300 Subject: [PATCH 77/86] Fix #11337 --- .../net/osmand/plus/GpxSelectionHelper.java | 4 ++- .../net/osmand/plus/helpers/GpxUiHelper.java | 14 ++++---- .../SelectedGpxMenuController.java | 33 ++++++------------- .../plus/track/GpxBlockStatisticsBuilder.java | 1 - 4 files changed, 21 insertions(+), 31 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java b/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java index 3c95d75e9f..5ec4f28736 100644 --- a/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java +++ b/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java @@ -291,10 +291,12 @@ public class GpxSelectionHelper { return group; } - private String getGroupName(GPXFile g) { + public String getGroupName(GPXFile g) { String name = g.path; if (g.showCurrentTrack) { name = getString(R.string.shared_string_currently_recording_track); + } else if (Algorithms.isEmpty(name)) { + name = getString(R.string.current_route); } else { int i = name.lastIndexOf('/'); if (i >= 0) { diff --git a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java index 87f9d4e579..c5eca0edb1 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/GpxUiHelper.java @@ -79,6 +79,7 @@ import net.osmand.plus.ContextMenuItem; import net.osmand.plus.GPXDatabase.GpxDataItem; import net.osmand.plus.GpxDbHelper; import net.osmand.plus.GpxDbHelper.GpxDataItemCallback; +import net.osmand.plus.GpxSelectionHelper; import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; @@ -2228,17 +2229,18 @@ public class GpxUiHelper { return dataSet; } - public static GpxDisplayItem makeGpxDisplayItem(OsmandApplication app, GPXUtilities.GPXFile gpx) { - GpxDisplayItem gpxItem = null; - String groupName = app.getString(R.string.current_route); - GpxDisplayGroup group = app.getSelectedGpxHelper().buildGpxDisplayGroup(gpx, 0, groupName); + public static GpxDisplayItem makeGpxDisplayItem(OsmandApplication app, GPXFile gpxFile) { + GpxSelectionHelper helper = app.getSelectedGpxHelper(); + String groupName = helper.getGroupName(gpxFile); + GpxDisplayGroup group = helper.buildGpxDisplayGroup(gpxFile, 0, groupName); if (group != null && group.getModifiableList().size() > 0) { - gpxItem = group.getModifiableList().get(0); + GpxDisplayItem gpxItem = group.getModifiableList().get(0); if (gpxItem != null) { gpxItem.route = true; } + return gpxItem; } - return gpxItem; + return null; } public static void saveAndShareGpx(@NonNull final Context context, @NonNull final GPXFile gpxFile) { diff --git a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/SelectedGpxMenuController.java b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/SelectedGpxMenuController.java index 76b8ff1c7b..ef38b823ed 100644 --- a/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/SelectedGpxMenuController.java +++ b/OsmAnd/src/net/osmand/plus/mapcontextmenu/controllers/SelectedGpxMenuController.java @@ -7,12 +7,13 @@ import android.os.AsyncTask; import androidx.annotation.NonNull; import net.osmand.AndroidUtils; -import net.osmand.GPXUtilities; import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.WptPt; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.plus.GpxSelectionHelper; +import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup; +import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; @@ -24,7 +25,6 @@ import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.track.TrackMenuFragment; import net.osmand.util.Algorithms; -import java.io.File; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -93,27 +93,14 @@ public class SelectedGpxMenuController extends MenuController { @Override protected GpxSelectionHelper.GpxDisplayItem doInBackground(Void... voids) { - GpxSelectionHelper.GpxDisplayGroup gpxDisplayGroup = null; - GPXUtilities.GPXFile gpxFile = null; - GPXUtilities.Track generalTrack = null; - if (selectedGpxFile.getGpxFile().path != null) { - gpxFile = GPXUtilities.loadGPXFile(new File(selectedGpxFile.getGpxFile().path)); - } - if (gpxFile != null) { - generalTrack = gpxFile.getGeneralTrack(); - } - if (generalTrack != null) { - gpxFile.addGeneralTrack(); - gpxDisplayGroup = app.getSelectedGpxHelper().buildGeneralGpxDisplayGroup(gpxFile, generalTrack); - } else if (gpxFile != null && gpxFile.tracks.size() > 0) { - gpxDisplayGroup = app.getSelectedGpxHelper().buildGeneralGpxDisplayGroup(gpxFile, gpxFile.tracks.get(0)); - } - List items = null; - if (gpxDisplayGroup != null) { - items = gpxDisplayGroup.getModifiableList(); - } - if (items != null && items.size() > 0) { - return items.get(0); + GPXFile gpxFile = selectedGpxFile.getGpxFile(); + if (gpxFile.tracks.size() > 0) { + GpxDisplayGroup gpxDisplayGroup = app.getSelectedGpxHelper().buildGeneralGpxDisplayGroup(gpxFile, gpxFile.tracks.get(0)); + + List items = gpxDisplayGroup.getModifiableList(); + if (!Algorithms.isEmpty(items)) { + return items.get(0); + } } return null; } diff --git a/OsmAnd/src/net/osmand/plus/track/GpxBlockStatisticsBuilder.java b/OsmAnd/src/net/osmand/plus/track/GpxBlockStatisticsBuilder.java index d4bef49ecf..f3048a2867 100644 --- a/OsmAnd/src/net/osmand/plus/track/GpxBlockStatisticsBuilder.java +++ b/OsmAnd/src/net/osmand/plus/track/GpxBlockStatisticsBuilder.java @@ -45,7 +45,6 @@ import java.util.Date; import java.util.List; import static net.osmand.plus.liveupdates.LiveUpdatesFragment.getDefaultIconColorId; -import static net.osmand.plus.myplaces.GPXTabItemType.GPX_TAB_ITEM_SPEED; public class GpxBlockStatisticsBuilder { From a2a7cb77bff4589316c25f83739887d9a6811a8b Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Tue, 20 Apr 2021 18:01:55 +0300 Subject: [PATCH 78/86] Fix gpxItem locationOnMap --- .../net/osmand/plus/monitoring/TripRecordingBottomSheet.java | 4 ++-- .../src/net/osmand/plus/myplaces/SegmentActionsListener.java | 4 +++- .../src/net/osmand/plus/myplaces/TrackSegmentFragment.java | 2 +- OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java | 5 ++++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java index 89734b7415..41cff45f1e 100644 --- a/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/monitoring/TripRecordingBottomSheet.java @@ -83,7 +83,7 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl public static final String UPDATE_DYNAMIC_ITEMS = "update_dynamic_items"; private static final int GPS_UPDATE_INTERVAL = 1000; public static final GPXTabItemType[] INIT_TAB_ITEMS = - new GPXTabItemType[]{GPX_TAB_ITEM_GENERAL, GPX_TAB_ITEM_ALTITUDE, GPX_TAB_ITEM_SPEED}; + new GPXTabItemType[] {GPX_TAB_ITEM_GENERAL, GPX_TAB_ITEM_ALTITUDE, GPX_TAB_ITEM_SPEED}; private OsmandApplication app; private OsmandSettings settings; @@ -651,7 +651,7 @@ public class TripRecordingBottomSheet extends MenuBottomSheetDialogFragment impl } @Override - public void openAnalyzeOnMap(GpxDisplayItem gpxItem) { + public void openAnalyzeOnMap(@NonNull GpxDisplayItem gpxItem) { } public interface DismissTargetFragment { diff --git a/OsmAnd/src/net/osmand/plus/myplaces/SegmentActionsListener.java b/OsmAnd/src/net/osmand/plus/myplaces/SegmentActionsListener.java index 3851e7e102..586b22634d 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/SegmentActionsListener.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/SegmentActionsListener.java @@ -2,6 +2,8 @@ package net.osmand.plus.myplaces; import android.view.View; +import androidx.annotation.NonNull; + import net.osmand.GPXUtilities.TrkSegment; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; @@ -19,5 +21,5 @@ public interface SegmentActionsListener { void showOptionsPopupMenu(View view, TrkSegment trkSegment, boolean confirmDeletion, GpxDisplayItem gpxItem); - void openAnalyzeOnMap(GpxDisplayItem gpxItem); + void openAnalyzeOnMap(@NonNull GpxDisplayItem gpxItem); } diff --git a/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java index c95d511526..9352cfc0a2 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/TrackSegmentFragment.java @@ -292,7 +292,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit } @Override - public void openAnalyzeOnMap(GpxDisplayItem gpxItem) { + public void openAnalyzeOnMap(@NonNull GpxDisplayItem gpxItem) { OsmandSettings settings = app.getSettings(); settings.setMapLocationToShow(gpxItem.locationOnMap.lat, gpxItem.locationOnMap.lon, settings.getLastKnownMapZoom(), diff --git a/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java b/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java index 2eedf2eab5..086ffcce4f 100644 --- a/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java +++ b/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java @@ -1131,7 +1131,10 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card } @Override - public void openAnalyzeOnMap(GpxDisplayItem gpxItem) { + public void openAnalyzeOnMap(@NonNull GpxDisplayItem gpxItem) { + if (gpxPoint != null) { + gpxItem.locationOnMap = gpxPoint.getSelectedPoint(); + } TrackDetailsMenu trackDetailsMenu = getMapActivity().getTrackDetailsMenu(); trackDetailsMenu.setGpxItem(gpxItem); trackDetailsMenu.setSelectedGpxFile(selectedGpxFile); From fed2e356c381e8bf04ac906fa55b1c6eca60db7c Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Tue, 20 Apr 2021 19:30:33 +0300 Subject: [PATCH 79/86] SRTM Meter / Feet dialogs - fix bugs and refactoring --- .../base/MultipleSelectionBottomSheet.java | 4 +- .../plus/base/SelectionBottomSheet.java | 27 ++- .../plus/download/DownloadResources.java | 2 +- .../net/osmand/plus/download/IndexItem.java | 3 + .../plus/download/MultipleDownloadItem.java | 3 - ...UiHelper.java => SelectIndexesHelper.java} | 199 +++++++++++------- .../plus/download/SrtmDownloadItem.java | 7 +- .../plus/download/ui/ItemViewHolder.java | 6 +- 8 files changed, 157 insertions(+), 94 deletions(-) rename OsmAnd/src/net/osmand/plus/download/{SelectIndexesUiHelper.java => SelectIndexesHelper.java} (60%) diff --git a/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java index fc7d75416a..35481944f2 100644 --- a/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/MultipleSelectionBottomSheet.java @@ -94,9 +94,9 @@ public class MultipleSelectionBottomSheet extends SelectionBottomSheet { } @Override - protected void notifyUiInitialized() { + protected void notifyUiCreated() { onSelectedItemsChanged(); - super.notifyUiInitialized(); + super.notifyUiCreated(); } private void onSelectedItemsChanged() { diff --git a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java index f861500078..dd6dc993eb 100644 --- a/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/base/SelectionBottomSheet.java @@ -52,7 +52,7 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment protected int activeColorRes; protected int secondaryColorRes; - private OnUiInitializedAdapter onUiInitializedAdapter; + private DialogStateListener dialogStateListener; private OnApplySelectionListener onApplySelectionListener; protected List allItems = new ArrayList<>(); @@ -64,7 +64,7 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { View mainView = super.onCreateView(inflater, parent, savedInstanceState); createSelectionListIfPossible(); - notifyUiInitialized(); + notifyUiCreated(); return mainView; } @@ -153,8 +153,8 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment } } - public void setOnUiInitializedAdapter(OnUiInitializedAdapter onUiInitializedAdapter) { - this.onUiInitializedAdapter = onUiInitializedAdapter; + public void setDialogStateListener(DialogStateListener dialogStateListener) { + this.dialogStateListener = dialogStateListener; } public void setOnApplySelectionListener(OnApplySelectionListener onApplySelectionListener) { @@ -194,9 +194,9 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment protected abstract boolean shouldShowDivider(); - protected void notifyUiInitialized() { - if (onUiInitializedAdapter != null) { - onUiInitializedAdapter.onUiInitialized(); + protected void notifyUiCreated() { + if (dialogStateListener != null) { + dialogStateListener.onDialogCreated(); } } @@ -240,8 +240,17 @@ public abstract class SelectionBottomSheet extends MenuBottomSheetDialogFragment } } - public interface OnUiInitializedAdapter { - void onUiInitialized(); + @Override + public void onDestroy() { + if (dialogStateListener != null) { + dialogStateListener.onCloseDialog(); + } + super.onDestroy(); + } + + public interface DialogStateListener { + void onDialogCreated(); + void onCloseDialog(); } public interface OnApplySelectionListener { diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java index e2320b16a8..3966f68814 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadResources.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadResources.java @@ -490,7 +490,7 @@ public class DownloadResources extends DownloadResourceGroup { private void replaceIndividualSrtmWithGroups(@NonNull WorldRegion region) { DownloadResourceGroup group = getRegionMapsGroup(region); if (group != null) { - boolean useMetersByDefault = SrtmDownloadItem.shouldUseMetersByDefault(app); + boolean useMetersByDefault = SrtmDownloadItem.isUseMetricByDefault(app); boolean listModified = false; DownloadActivityType srtmType = DownloadActivityType.SRTM_COUNTRY_FILE; List individualItems = group.getIndividualDownloadItems(); diff --git a/OsmAnd/src/net/osmand/plus/download/IndexItem.java b/OsmAnd/src/net/osmand/plus/download/IndexItem.java index 82da2619a3..4f12d032dc 100644 --- a/OsmAnd/src/net/osmand/plus/download/IndexItem.java +++ b/OsmAnd/src/net/osmand/plus/download/IndexItem.java @@ -233,6 +233,9 @@ public class IndexItem extends DownloadItem implements Comparable { @Nullable @Override public String getAdditionalDescription(Context ctx) { + if (getType() == DownloadActivityType.SRTM_COUNTRY_FILE) { + return SrtmDownloadItem.getAbbreviationInScopes(ctx, this); + } return null; } diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java b/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java index d30c9fc83b..eaa3b4b92a 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java @@ -147,9 +147,6 @@ public class MultipleDownloadItem extends DownloadItem { @Nullable @Override public String getAdditionalDescription(Context ctx) { - for (DownloadItem item : items) { - return item.getAdditionalDescription(ctx); - } return null; } diff --git a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java b/OsmAnd/src/net/osmand/plus/download/SelectIndexesHelper.java similarity index 60% rename from OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java rename to OsmAnd/src/net/osmand/plus/download/SelectIndexesHelper.java index 845082daae..a6af99212a 100644 --- a/OsmAnd/src/net/osmand/plus/download/SelectIndexesUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/SelectIndexesHelper.java @@ -13,7 +13,7 @@ import net.osmand.plus.base.ModeSelectionBottomSheet; import net.osmand.plus.base.MultipleSelectionWithModeBottomSheet; import net.osmand.plus.base.SelectionBottomSheet; import net.osmand.plus.base.SelectionBottomSheet.OnApplySelectionListener; -import net.osmand.plus.base.SelectionBottomSheet.OnUiInitializedAdapter; +import net.osmand.plus.base.SelectionBottomSheet.DialogStateListener; import net.osmand.plus.base.SelectionBottomSheet.SelectableItem; import net.osmand.plus.widgets.multistatetoggle.RadioItem; import net.osmand.plus.widgets.multistatetoggle.RadioItem.OnRadioItemClickListener; @@ -22,11 +22,12 @@ import net.osmand.util.Algorithms; import java.text.DateFormat; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import static net.osmand.plus.download.MultipleDownloadItem.getIndexItem; -public class SelectIndexesUiHelper { +public class SelectIndexesHelper { private final OsmandApplication app; private final AppCompatActivity activity; @@ -34,58 +35,70 @@ public class SelectIndexesUiHelper { private final ItemsToDownloadSelectedListener listener; private final DateFormat dateFormat; private final boolean showRemoteDate; + private final List itemsToDownload; private final DownloadItem downloadItem; + private final boolean useMetricByDefault; private SelectionBottomSheet dialog; - private SelectIndexesUiHelper(@NonNull DownloadItem downloadItem, - @NonNull AppCompatActivity activity, - @NonNull DateFormat dateFormat, - boolean showRemoteDate, - @NonNull ItemsToDownloadSelectedListener listener) { + private SelectIndexesHelper(@NonNull DownloadItem downloadItem, + @NonNull AppCompatActivity activity, + @NonNull DateFormat dateFormat, + boolean showRemoteDate, + @NonNull ItemsToDownloadSelectedListener listener) { this.app = (OsmandApplication) activity.getApplicationContext(); this.activity = activity; - this.downloadItem = downloadItem; this.dateFormat = dateFormat; this.showRemoteDate = showRemoteDate; this.listener = listener; + this.downloadItem = downloadItem; + this.itemsToDownload = getItemsToDownload(downloadItem); + this.useMetricByDefault = SrtmDownloadItem.isUseMetricByDefault(app); } - public static void showDialog(@NonNull DownloadItem i, + public static void showDialog(@NonNull DownloadItem di, @NonNull AppCompatActivity a, @NonNull DateFormat df, boolean showRemoteDate, @NonNull ItemsToDownloadSelectedListener l) { - new SelectIndexesUiHelper(i, a, df, showRemoteDate, l).showDialogInternal(); - } - - private void showDialogInternal() { - if (downloadItem.getType() == DownloadActivityType.SRTM_COUNTRY_FILE) { - if (downloadItem instanceof MultipleDownloadItem) { - showSrtmMultipleSelectionDialog(); + SelectIndexesHelper h = new SelectIndexesHelper(di, a, df, showRemoteDate, l); + if (di.getType() == DownloadActivityType.SRTM_COUNTRY_FILE) { + if (di instanceof MultipleDownloadItem) { + h.showSrtmMultipleSelectionDialog(); } else { - showSrtmModeSelectionDialog(); + h.showSrtmTypeSelectionDialog(); } - } else if (downloadItem instanceof MultipleDownloadItem) { - showMultipleSelectionDialog(); + } else if (di instanceof MultipleDownloadItem) { + h.showMultipleSelectionDialog(); } } private void showMultipleSelectionDialog() { + MultipleDownloadItem mdi = (MultipleDownloadItem) downloadItem; List allItems = new ArrayList<>(); List selectedItems = new ArrayList<>(); - prepareItems(allItems, selectedItems); - MultipleSelectionBottomSheet msDialog = MultipleSelectionBottomSheet.showInstance( - activity, allItems, selectedItems, true); + for (DownloadItem di : mdi.getAllItems()) { + SelectableItem si = createSelectableItem(di); + allItems.add(si); + if (itemsToDownload.contains(di)) { + selectedItems.add(si); + } + } + + MultipleSelectionBottomSheet msDialog = + MultipleSelectionBottomSheet.showInstance(activity, allItems, selectedItems, true); this.dialog = msDialog; - msDialog.setOnUiInitializedAdapter(new OnUiInitializedAdapter() { + msDialog.setDialogStateListener(new DialogStateListener() { @Override - public void onUiInitialized() { + public void onDialogCreated() { dialog.setTitle(app.getString(R.string.welmode_download_maps)); } + + @Override + public void onCloseDialog() { } }); msDialog.setSelectionUpdateListener(new SelectionUpdateListener() { @@ -99,25 +112,40 @@ public class SelectIndexesUiHelper { } private void showSrtmMultipleSelectionDialog() { + MultipleDownloadItem mdi = (MultipleDownloadItem) downloadItem; List allItems = new ArrayList<>(); List selectedItems = new ArrayList<>(); - prepareItems(allItems, selectedItems); - SrtmDownloadItem srtmItem = (SrtmDownloadItem) ((MultipleDownloadItem)downloadItem).getAllItems().get(0); - final int selectedModeOrder = srtmItem.isUseMetric() ? 0 : 1; - final List radioItems = createSrtmRadioItems(); + for (DownloadItem di : mdi.getAllItems()) { + SelectableItem si = createSrtmSelectableItem((SrtmDownloadItem) di); + allItems.add(si); + if (itemsToDownload.contains(di)) { + selectedItems.add(si); + } + } + + final RadioItem meterBtn = createSrtmRadioBtn(true); + final RadioItem feetBtn = createSrtmRadioBtn(false); + List radioItems = new ArrayList<>(); + radioItems.add(meterBtn); + radioItems.add(feetBtn); MultipleSelectionBottomSheet msDialog = MultipleSelectionWithModeBottomSheet.showInstance( activity, allItems, selectedItems, radioItems, true); this.dialog = msDialog; - msDialog.setOnUiInitializedAdapter(new OnUiInitializedAdapter() { + msDialog.setDialogStateListener(new DialogStateListener() { @Override - public void onUiInitialized() { + public void onDialogCreated() { dialog.setTitle(app.getString(R.string.welmode_download_maps)); - dialog.setSelectedMode(radioItems.get(selectedModeOrder)); + dialog.setSelectedMode(useMetricByDefault ? meterBtn : feetBtn); dialog.setSecondaryDescription(app.getString(R.string.srtm_download_list_help_message)); } + + @Override + public void onCloseDialog() { + resetUseMeters(); + } }); msDialog.setSelectionUpdateListener(new SelectionUpdateListener() { @@ -130,58 +158,47 @@ public class SelectIndexesUiHelper { msDialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); } - private void showSrtmModeSelectionDialog() { + private void showSrtmTypeSelectionDialog() { SrtmDownloadItem srtmItem = (SrtmDownloadItem) downloadItem; - final int selectedModeOrder = srtmItem.isUseMetric() ? 0 : 1; - final List radioItems = createSrtmRadioItems(); - SelectableItem preview = createSelectableItem(srtmItem); + final RadioItem meterBtn = createSrtmRadioBtn(true); + final RadioItem feetBtn = createSrtmRadioBtn(false); + List radioItems = new ArrayList<>(); + radioItems.add(meterBtn); + radioItems.add(feetBtn); + + SelectableItem preview = createSrtmSelectableItem(srtmItem); dialog = ModeSelectionBottomSheet.showInstance(activity, preview, radioItems, true); - dialog.setOnUiInitializedAdapter(new OnUiInitializedAdapter() { + dialog.setDialogStateListener(new DialogStateListener() { @Override - public void onUiInitialized() { - ModeSelectionBottomSheet dialog = (ModeSelectionBottomSheet) SelectIndexesUiHelper.this.dialog; + public void onDialogCreated() { + ModeSelectionBottomSheet dialog = (ModeSelectionBottomSheet) SelectIndexesHelper.this.dialog; dialog.setTitle(app.getString(R.string.srtm_unit_format)); dialog.setPrimaryDescription(app.getString(R.string.srtm_download_single_help_message)); updateSize(); - dialog.setSelectedMode(radioItems.get(selectedModeOrder)); + dialog.setSelectedMode(useMetricByDefault ? meterBtn : feetBtn); + } + + @Override + public void onCloseDialog() { + resetUseMeters(); } }); dialog.setOnApplySelectionListener(getOnApplySelectionListener(listener)); } - private void prepareItems(List allItems, - List selectedItems) { - final MultipleDownloadItem multipleDownloadItem = (MultipleDownloadItem) downloadItem; - final List itemsToDownload = getItemsToDownload(multipleDownloadItem); - for (DownloadItem downloadItem : multipleDownloadItem.getAllItems()) { - SelectableItem selectableItem = createSelectableItem(downloadItem); - allItems.add(selectableItem); - - if (itemsToDownload.contains(downloadItem)) { - selectedItems.add(selectableItem); - } - } - } - - private List createSrtmRadioItems() { - List radioItems = new ArrayList<>(); - radioItems.add(createSrtmRadioBtn(R.string.shared_string_meters, true)); - radioItems.add(createSrtmRadioBtn(R.string.shared_string_feet, false)); - return radioItems; - } - - private RadioItem createSrtmRadioBtn(int titleId, - final boolean useMeters) { + private RadioItem createSrtmRadioBtn(final boolean useMeters) { + int titleId = useMeters ? R.string.shared_string_meters : R.string.shared_string_feet; String title = Algorithms.capitalizeFirstLetter(app.getString(titleId)); RadioItem radioItem = new TextRadioItem(title); radioItem.setOnClickListener(new OnRadioItemClickListener() { @Override public boolean onRadioItemClick(RadioItem radioItem, View view) { - updateDialogListItems(useMeters); + setUseMetersForAllItems(useMeters); + updateListItems(); updateSize(); return true; } @@ -189,22 +206,45 @@ public class SelectIndexesUiHelper { return radioItem; } - private void updateDialogListItems(boolean useMeters) { + private SelectableItem createSelectableItem(DownloadItem item) { + SelectableItem selectableItem = new SelectableItem(); + updateSelectableItem(selectableItem, item); + selectableItem.setObject(item); + return selectableItem; + } + + private SelectableItem createSrtmSelectableItem(SrtmDownloadItem item) { + SelectableItem selectableItem = new SelectableItem(); + updateSelectableItem(selectableItem, item.getDefaultIndexItem()); + selectableItem.setObject(item); + return selectableItem; + } + + private void updateListItems() { List items = new ArrayList<>(dialog.getAllItems()); - for (SelectableItem item : items) { - DownloadItem downloadItem = (DownloadItem) item.getObject(); - if (downloadItem instanceof SrtmDownloadItem) { - ((SrtmDownloadItem) downloadItem).setUseMetric(useMeters); - updateSelectableItem(item, downloadItem); + for (SelectableItem selectableItem : items) { + DownloadItem di = (DownloadItem) selectableItem.getObject(); + if (di instanceof SrtmDownloadItem) { + di = ((SrtmDownloadItem) di).getDefaultIndexItem(); } + updateSelectableItem(selectableItem, di); } dialog.setItems(items); } - private SelectableItem createSelectableItem(DownloadItem item) { - SelectableItem selectableItem = new SelectableItem(); - updateSelectableItem(selectableItem, item); - return selectableItem; + private void resetUseMeters() { + boolean useMeters = SrtmDownloadItem.isUseMetricByDefault(app); + setUseMetersForAllItems(useMeters); + } + + private void setUseMetersForAllItems(boolean useMeters) { + for (SelectableItem item : dialog.getAllItems()) { + DownloadItem downloadItem = (DownloadItem) item.getObject(); + if (downloadItem instanceof SrtmDownloadItem) { + SrtmDownloadItem srtmItem = (SrtmDownloadItem) downloadItem; + srtmItem.setUseMetric(useMeters); + } + } } private void updateSelectableItem(SelectableItem selectableItem, @@ -221,7 +261,6 @@ public class SelectIndexesUiHelper { selectableItem.setDescription(description); selectableItem.setIconId(downloadItem.getType().getIconResource()); - selectableItem.setObject(downloadItem); } private OnApplySelectionListener getOnApplySelectionListener(final ItemsToDownloadSelectedListener listener) { @@ -257,13 +296,23 @@ public class SelectIndexesUiHelper { double totalSizeMb = 0.0d; for (SelectableItem i : selectableItems) { Object obj = i.getObject(); - if (obj instanceof DownloadItem) { + if (obj instanceof SrtmDownloadItem) { + SrtmDownloadItem srtm = (SrtmDownloadItem) obj; + totalSizeMb += srtm.getDefaultIndexItem().getSizeToDownloadInMb(); + } else if (obj instanceof DownloadItem) { totalSizeMb += ((DownloadItem) obj).getSizeToDownloadInMb(); } } return totalSizeMb; } + private static List getItemsToDownload(DownloadItem di) { + if (di instanceof MultipleDownloadItem) { + return getItemsToDownload((MultipleDownloadItem) di); + } + return Collections.emptyList(); + } + private static List getItemsToDownload(MultipleDownloadItem md) { if (md.hasActualDataToDownload()) { // download left regions diff --git a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java index 8567f97409..217b9eb0ee 100644 --- a/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/SrtmDownloadItem.java @@ -53,6 +53,11 @@ public class SrtmDownloadItem extends DownloadItem { return index; } } + return getDefaultIndexItem(); + } + + @NonNull + public IndexItem getDefaultIndexItem() { for (IndexItem index : indexes) { if (useMetric && isMetricItem(index) || !useMetric && !isMetricItem(index)) { return index; @@ -135,7 +140,7 @@ public class SrtmDownloadItem extends DownloadItem { return getAbbreviationInScopes(ctx, this); } - public static boolean shouldUseMetersByDefault(@NonNull OsmandApplication app) { + public static boolean isUseMetricByDefault(@NonNull OsmandApplication app) { MetricsConstants metricSystem = app.getSettings().METRIC_SYSTEM.get(); return metricSystem != MetricsConstants.MILES_AND_FEET; } diff --git a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java index 135db84bc9..ec4347eb71 100644 --- a/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java +++ b/OsmAnd/src/net/osmand/plus/download/ui/ItemViewHolder.java @@ -41,8 +41,8 @@ import net.osmand.plus.download.DownloadActivityType; import net.osmand.plus.download.DownloadResourceGroup; import net.osmand.plus.download.DownloadResources; import net.osmand.plus.download.IndexItem; -import net.osmand.plus.download.SelectIndexesUiHelper; -import net.osmand.plus.download.SelectIndexesUiHelper.ItemsToDownloadSelectedListener; +import net.osmand.plus.download.SelectIndexesHelper; +import net.osmand.plus.download.SelectIndexesHelper.ItemsToDownloadSelectedListener; import net.osmand.plus.download.MultipleDownloadItem; import net.osmand.plus.download.ui.LocalIndexesFragment.LocalIndexOperationTask; import net.osmand.plus.helpers.FileNameTranslationHelper; @@ -492,7 +492,7 @@ public class ItemViewHolder { } private void selectIndexesToDownload(DownloadItem item) { - SelectIndexesUiHelper.showDialog(item, context, dateFormat, showRemoteDate, + SelectIndexesHelper.showDialog(item, context, dateFormat, showRemoteDate, new ItemsToDownloadSelectedListener() { @Override public void onItemsToDownloadSelected(List indexes) { From 1629183994dd862336fd662c2aadada794503296 Mon Sep 17 00:00:00 2001 From: max-klaus Date: Tue, 20 Apr 2021 20:53:39 +0300 Subject: [PATCH 80/86] Finished backup test activity --- OsmAnd/res/layout/test_backup_layout.xml | 8 + .../src/net/osmand/AndroidNetworkUtils.java | 214 ++++++- .../src/net/osmand/plus/AnalyticsHelper.java | 3 +- .../src/net/osmand/plus/AppInitializer.java | 2 + .../net/osmand/plus/FavouritesDbHelper.java | 8 + OsmAnd/src/net/osmand/plus/GPXDatabase.java | 76 ++- OsmAnd/src/net/osmand/plus/GpxDbHelper.java | 6 + .../net/osmand/plus/OsmandApplication.java | 6 + .../osmand/plus/ProgressImplementation.java | 4 +- .../net/osmand/plus/backup/BackupHelper.java | 581 ++++++++++++++++++ .../net/osmand/plus/backup/BackupTask.java | 335 ++++++++++ .../net/osmand/plus/backup/GpxFileInfo.java | 65 ++ .../osmand/plus/backup/PrepareBackupTask.java | 211 +++++++ .../src/net/osmand/plus/backup/UserFile.java | 102 +++ .../backup/UserNotRegisteredException.java | 9 + .../plus/development/TestBackupActivity.java | 575 +++++------------ .../plus/importfiles/FavoritesImportTask.java | 12 +- .../plus/inapp/InAppPurchaseHelper.java | 11 +- .../plus/settings/backend/OsmandSettings.java | 3 + 19 files changed, 1736 insertions(+), 495 deletions(-) create mode 100644 OsmAnd/src/net/osmand/plus/backup/BackupHelper.java create mode 100644 OsmAnd/src/net/osmand/plus/backup/BackupTask.java create mode 100644 OsmAnd/src/net/osmand/plus/backup/GpxFileInfo.java create mode 100644 OsmAnd/src/net/osmand/plus/backup/PrepareBackupTask.java create mode 100644 OsmAnd/src/net/osmand/plus/backup/UserFile.java create mode 100644 OsmAnd/src/net/osmand/plus/backup/UserNotRegisteredException.java diff --git a/OsmAnd/res/layout/test_backup_layout.xml b/OsmAnd/res/layout/test_backup_layout.xml index 216820a9bf..d24c50eddc 100644 --- a/OsmAnd/res/layout/test_backup_layout.xml +++ b/OsmAnd/res/layout/test_backup_layout.xml @@ -42,6 +42,7 @@ @@ -121,6 +122,13 @@ android:textColor="?android:textColorPrimary" android:textSize="@dimen/default_list_text_size" /> + + errors); } + public interface OnFilesDownloadCallback { + @Nullable + Map getAdditionalParams(@NonNull File file); + void onFileDownloadProgress(@NonNull File file, int percent); + @WorkerThread + void onFileDownloadedAsync(@NonNull File file); + void onFilesDownloadDone(@NonNull Map errors); + } + public static class RequestResponse { private Request request; private String response; @@ -74,35 +84,46 @@ public class AndroidNetworkUtils { } } - public interface OnRequestsResultListener { - void onResult(@NonNull List results); + public interface OnSendRequestsListener { + void onRequestSent(@NonNull RequestResponse response); + void onRequestsSent(@NonNull List results); } - public static void sendRequestsAsync(final OsmandApplication ctx, - final List requests, - final OnRequestsResultListener listener) { + public static void sendRequestsAsync(@Nullable final OsmandApplication ctx, + @NonNull final List requests, + @Nullable final OnSendRequestsListener listener) { - new AsyncTask>() { + new AsyncTask>() { @Override protected List doInBackground(Void... params) { List responses = new ArrayList<>(); for (Request request : requests) { + RequestResponse requestResponse; try { String response = sendRequest(ctx, request.getUrl(), request.getParameters(), request.getUserOperation(), request.isToastAllowed(), request.isPost()); - responses.add(new RequestResponse(request, response)); + requestResponse = new RequestResponse(request, response); } catch (Exception e) { - responses.add(new RequestResponse(request, null)); + requestResponse = new RequestResponse(request, null); } + responses.add(requestResponse); + publishProgress(requestResponse); } return responses; } + @Override + protected void onProgressUpdate(RequestResponse... values) { + if (listener != null) { + listener.onRequestSent(values[0]); + } + } + @Override protected void onPostExecute(@NonNull List results) { if (listener != null) { - listener.onResult(results); + listener.onRequestsSent(results); } } @@ -146,7 +167,7 @@ public class AndroidNetworkUtils { @Override protected String doInBackground(Void... params) { - return downloadFile(url, fileToSave); + return downloadFile(url, fileToSave, false, null); } @Override @@ -158,8 +179,80 @@ public class AndroidNetworkUtils { }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); } - public static String sendRequest(OsmandApplication ctx, String url, Map parameters, - String userOperation, boolean toastAllowed, boolean post) { + public static void downloadFilesAsync(final @NonNull String url, + final @NonNull List files, + final @NonNull Map parameters, + final @Nullable OnFilesDownloadCallback callback) { + + new AsyncTask>() { + + @Override + @NonNull + protected Map doInBackground(Void... v) { + Map errors = new HashMap<>(); + for (final File file : files) { + final int[] progressValue = {0}; + publishProgress(file, 0); + IProgress progress = null; + if (callback != null) { + progress = new NetworkProgress() { + @Override + public void progress(int deltaWork) { + progressValue[0] += deltaWork; + publishProgress(file, progressValue[0]); + } + }; + } + try { + Map params = new HashMap<>(parameters); + if (callback != null) { + Map additionalParams = callback.getAdditionalParams(file); + if (additionalParams != null) { + params.putAll(additionalParams); + } + } + boolean firstPrm = !url.contains("?"); + StringBuilder sb = new StringBuilder(url); + for (Entry entry : params.entrySet()) { + sb.append(firstPrm ? "?" : "&").append(entry.getKey()).append("=").append(URLEncoder.encode(entry.getValue(), "UTF-8")); + firstPrm = false; + } + String res = downloadFile(sb.toString(), file, true, progress); + if (res != null) { + errors.put(file, res); + } else { + if (callback != null) { + callback.onFileDownloadedAsync(file); + } + } + } catch (Exception e) { + errors.put(file, e.getMessage()); + } + publishProgress(file, Integer.MAX_VALUE); + } + return errors; + } + + @Override + protected void onProgressUpdate(Object... objects) { + if (callback != null) { + callback.onFileDownloadProgress((File) objects[0], (Integer) objects[1]); + } + } + + @Override + protected void onPostExecute(@NonNull Map errors) { + if (callback != null) { + callback.onFilesDownloadDone(errors); + } + } + + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null); + } + + public static String sendRequest(@Nullable OsmandApplication ctx, @NonNull String url, + @Nullable Map parameters, + @Nullable String userOperation, boolean toastAllowed, boolean post) { HttpURLConnection connection = null; try { @@ -177,7 +270,7 @@ public class AndroidNetworkUtils { String paramsSeparator = url.indexOf('?') == -1 ? "?" : "&"; connection = NetworkUtils.getHttpURLConnection(params == null || post ? url : url + paramsSeparator + params); connection.setRequestProperty("Accept-Charset", "UTF-8"); - connection.setRequestProperty("User-Agent", Version.getFullVersion(ctx)); + connection.setRequestProperty("User-Agent", ctx != null ? Version.getFullVersion(ctx) : "OsmAnd"); connection.setConnectTimeout(15000); if (params != null && post) { connection.setDoInput(true); @@ -200,9 +293,10 @@ public class AndroidNetworkUtils { } if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { - if (toastAllowed) { - String msg = userOperation - + " " + ctx.getString(R.string.failed_op) + ": " + connection.getResponseMessage(); + if (toastAllowed && ctx != null) { + String msg = (!Algorithms.isEmpty(userOperation) ? userOperation + " " : "") + + ctx.getString(R.string.failed_op) + ": " + + connection.getResponseMessage(); showToast(ctx, msg); } } else { @@ -233,17 +327,17 @@ public class AndroidNetworkUtils { } catch (NullPointerException e) { // that's tricky case why NPE is thrown to fix that problem httpClient could be used - if (toastAllowed) { + if (toastAllowed && ctx != null) { String msg = ctx.getString(R.string.auth_failed); showToast(ctx, msg); } } catch (MalformedURLException e) { - if (toastAllowed) { + if (toastAllowed && ctx != null) { showToast(ctx, MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template) + ": " + ctx.getResources().getString(R.string.shared_string_unexpected_error), userOperation)); } } catch (IOException e) { - if (toastAllowed) { + if (toastAllowed && ctx != null) { showToast(ctx, MessageFormat.format(ctx.getResources().getString(R.string.shared_string_action_template) + ": " + ctx.getResources().getString(R.string.shared_string_io_error), userOperation)); } @@ -277,18 +371,23 @@ public class AndroidNetworkUtils { return res; } - public static String downloadFile(@NonNull String url, @NonNull File fileToSave) { + public static String downloadFile(@NonNull String url, @NonNull File fileToSave, boolean gzip, @Nullable IProgress progress) { String error = null; try { URLConnection connection = NetworkUtils.getHttpURLConnection(url); connection.setConnectTimeout(CONNECTION_TIMEOUT); connection.setReadTimeout(CONNECTION_TIMEOUT); - BufferedInputStream inputStream = new BufferedInputStream(connection.getInputStream(), 8 * 1024); + if (gzip) { + connection.setRequestProperty("Accept-Encoding", "deflate, gzip"); + } + InputStream inputStream = gzip + ? new GZIPInputStream(connection.getInputStream()) + : new BufferedInputStream(connection.getInputStream(), 8 * 1024); fileToSave.getParentFile().mkdirs(); OutputStream stream = null; try { stream = new FileOutputStream(fileToSave); - Algorithms.streamCopy(inputStream, stream); + Algorithms.streamCopy(inputStream, stream, progress, 1024); stream.flush(); } finally { Algorithms.closeStream(inputStream); @@ -307,12 +406,17 @@ public class AndroidNetworkUtils { private static final String BOUNDARY = "CowMooCowMooCowCowCow"; public static String uploadFile(@NonNull String urlText, @NonNull File file, boolean gzip, - @NonNull Map additionalParams, @Nullable Map headers) throws IOException { - return uploadFile(urlText, new FileInputStream(file), file.getName(), gzip, additionalParams, headers); + @NonNull Map additionalParams, + @Nullable Map headers, + @Nullable IProgress progress) throws IOException { + return uploadFile(urlText, new FileInputStream(file), file.getName(), gzip, additionalParams, headers, progress); } - public static String uploadFile(@NonNull String urlText, @NonNull InputStream inputStream, @NonNull String fileName, boolean gzip, - Map additionalParams, @Nullable Map headers) { + public static String uploadFile(@NonNull String urlText, @NonNull InputStream inputStream, + @NonNull String fileName, boolean gzip, + @NonNull Map additionalParams, + @Nullable Map headers, + @Nullable IProgress progress) { URL url; try { boolean firstPrm = !urlText.contains("?"); @@ -350,11 +454,11 @@ public class AndroidNetworkUtils { ous.flush(); if (gzip) { GZIPOutputStream gous = new GZIPOutputStream(ous, 1024); - Algorithms.streamCopy(bis, gous); + Algorithms.streamCopy(bis, gous, progress, 1024); gous.flush(); gous.finish(); } else { - Algorithms.streamCopy(bis, ous); + Algorithms.streamCopy(bis, ous, progress, 1024); } ous.write(("\r\n--" + BOUNDARY + "--\r\n").getBytes()); @@ -406,8 +510,19 @@ public class AndroidNetworkUtils { @NonNull protected Map doInBackground(Void... v) { Map errors = new HashMap<>(); - for (File file : files) { + for (final File file : files) { + final int[] progressValue = {0}; publishProgress(file, 0); + IProgress progress = null; + if (callback != null) { + progress = new NetworkProgress() { + @Override + public void progress(int deltaWork) { + progressValue[0] += deltaWork; + publishProgress(file, progressValue[0]); + } + }; + } try { Map params = new HashMap<>(parameters); if (callback != null) { @@ -416,14 +531,14 @@ public class AndroidNetworkUtils { params.putAll(additionalParams); } } - String res = uploadFile(url, file, gzip, params, headers); + String res = uploadFile(url, file, gzip, params, headers, progress); if (res != null) { errors.put(file, res); } } catch (Exception e) { errors.put(file, e.getMessage()); } - publishProgress(file, 100); + publishProgress(file, Integer.MAX_VALUE); } return errors; } @@ -484,4 +599,39 @@ public class AndroidNetworkUtils { return post; } } + + private abstract static class NetworkProgress implements IProgress { + @Override + public void startTask(String taskName, int work) { + } + + @Override + public void startWork(int work) { + } + + @Override + public abstract void progress(int deltaWork); + + @Override + public void remaining(int remainingWork) { + } + + @Override + public void finishTask() { + } + + @Override + public boolean isIndeterminate() { + return false; + } + + @Override + public boolean isInterrupted() { + return false; + } + + @Override + public void setGeneralProgress(String genProgress) { + } + } } diff --git a/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java b/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java index e8d04254c3..52dff8e6d0 100644 --- a/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java +++ b/OsmAnd/src/net/osmand/plus/AnalyticsHelper.java @@ -184,7 +184,8 @@ public class AnalyticsHelper extends SQLiteOpenHelper { String jsonStr = json.toString(); InputStream inputStream = new ByteArrayInputStream(jsonStr.getBytes()); - String res = AndroidNetworkUtils.uploadFile(ANALYTICS_UPLOAD_URL, inputStream, ANALYTICS_FILE_NAME, true, additionalData, null); + String res = AndroidNetworkUtils.uploadFile(ANALYTICS_UPLOAD_URL, inputStream, + ANALYTICS_FILE_NAME, true, additionalData, null, null); if (res != null) { return; } diff --git a/OsmAnd/src/net/osmand/plus/AppInitializer.java b/OsmAnd/src/net/osmand/plus/AppInitializer.java index af3ea4f475..d5afd31937 100644 --- a/OsmAnd/src/net/osmand/plus/AppInitializer.java +++ b/OsmAnd/src/net/osmand/plus/AppInitializer.java @@ -31,6 +31,7 @@ import net.osmand.osm.MapPoiTypes; import net.osmand.plus.activities.LocalIndexHelper; import net.osmand.plus.activities.LocalIndexInfo; import net.osmand.plus.activities.SavingTrackHelper; +import net.osmand.plus.backup.BackupHelper; import net.osmand.plus.base.MapViewTrackingUtilities; import net.osmand.plus.download.DownloadActivity; import net.osmand.plus.download.ui.AbstractLoadLocalIndexTask; @@ -473,6 +474,7 @@ public class AppInitializer implements IProgress { app.oprAuthHelper = startupInit(new OprAuthHelper(app), OprAuthHelper.class); app.onlineRoutingHelper = startupInit(new OnlineRoutingHelper(app), OnlineRoutingHelper.class); app.itineraryHelper = startupInit(new ItineraryHelper(app), ItineraryHelper.class); + app.backupHelper = startupInit(new BackupHelper(app), BackupHelper.class); initOpeningHoursParser(); } diff --git a/OsmAnd/src/net/osmand/plus/FavouritesDbHelper.java b/OsmAnd/src/net/osmand/plus/FavouritesDbHelper.java index c90ecccc02..be36c5197d 100644 --- a/OsmAnd/src/net/osmand/plus/FavouritesDbHelper.java +++ b/OsmAnd/src/net/osmand/plus/FavouritesDbHelper.java @@ -146,6 +146,14 @@ public class FavouritesDbHelper { } } + public long getLastUploadedTime() { + return context.getSettings().FAVORITES_LAST_UPLOADED_TIME.get(); + } + + public void setLastUploadedTime(long time) { + context.getSettings().FAVORITES_LAST_UPLOADED_TIME.set(time); + } + @Nullable public Drawable getColoredIconForGroup(String groupName) { String groupIdName = FavoriteGroup.convertDisplayNameToGroupIdName(context, groupName); diff --git a/OsmAnd/src/net/osmand/plus/GPXDatabase.java b/OsmAnd/src/net/osmand/plus/GPXDatabase.java index 385ec79484..f1e5f64099 100644 --- a/OsmAnd/src/net/osmand/plus/GPXDatabase.java +++ b/OsmAnd/src/net/osmand/plus/GPXDatabase.java @@ -18,7 +18,7 @@ import androidx.annotation.Nullable; public class GPXDatabase { - private static final int DB_VERSION = 11; + private static final int DB_VERSION = 12; private static final String DB_NAME = "gpx_database"; private static final String GPX_TABLE_NAME = "gpxTable"; @@ -48,6 +48,7 @@ public class GPXDatabase { private static final String GPX_COL_COLOR = "color"; private static final String GPX_COL_FILE_LAST_MODIFIED_TIME = "fileLastModifiedTime"; + private static final String GPX_COL_FILE_LAST_UPLOADED_TIME = "fileLastUploadedTime"; private static final String GPX_COL_SPLIT_TYPE = "splitType"; private static final String GPX_COL_SPLIT_INTERVAL = "splitInterval"; @@ -98,6 +99,7 @@ public class GPXDatabase { GPX_COL_WPT_POINTS + " int, " + GPX_COL_COLOR + " TEXT, " + GPX_COL_FILE_LAST_MODIFIED_TIME + " long, " + + GPX_COL_FILE_LAST_UPLOADED_TIME + " long, " + GPX_COL_SPLIT_TYPE + " int, " + GPX_COL_SPLIT_INTERVAL + " double, " + GPX_COL_API_IMPORTED + " int, " + // 1 = true, 0 = false @@ -133,6 +135,7 @@ public class GPXDatabase { GPX_COL_WPT_POINTS + ", " + GPX_COL_COLOR + ", " + GPX_COL_FILE_LAST_MODIFIED_TIME + ", " + + GPX_COL_FILE_LAST_UPLOADED_TIME + ", " + GPX_COL_SPLIT_TYPE + ", " + GPX_COL_SPLIT_INTERVAL + ", " + GPX_COL_API_IMPORTED + ", " + @@ -184,6 +187,7 @@ public class GPXDatabase { private int splitType; private double splitInterval; private long fileLastModifiedTime; + private long fileLastUploadedTime; private boolean apiImported; private boolean showAsMarkers; private boolean joinSegments; @@ -200,6 +204,11 @@ public class GPXDatabase { this.color = color; } + public GpxDataItem(File file, long fileLastUploadedTime) { + this.file = file; + this.fileLastUploadedTime = fileLastUploadedTime; + } + public GpxDataItem(File file, @NonNull GPXFile gpxFile) { this.file = file; readGpxParams(gpxFile); @@ -263,6 +272,10 @@ public class GPXDatabase { return fileLastModifiedTime; } + public long getFileLastUploadedTime() { + return fileLastUploadedTime; + } + public int getSplitType() { return splitType; } @@ -441,10 +454,13 @@ public class GPXDatabase { db.execSQL("UPDATE " + GPX_TABLE_NAME + " SET " + GPX_COL_SHOW_START_FINISH + " = ? " + "WHERE " + GPX_COL_SHOW_START_FINISH + " IS NULL", new Object[]{1}); } + if (oldVersion < 12) { + db.execSQL("ALTER TABLE " + GPX_TABLE_NAME + " ADD " + GPX_COL_FILE_LAST_UPLOADED_TIME + " long"); + } db.execSQL("CREATE INDEX IF NOT EXISTS " + GPX_INDEX_NAME_DIR + " ON " + GPX_TABLE_NAME + " (" + GPX_COL_NAME + ", " + GPX_COL_DIR + ");"); } - private boolean updateLastModifiedTime(GpxDataItem item) { + private boolean updateLastModifiedTime(@NonNull GpxDataItem item) { SQLiteConnection db = openConnection(false); if (db != null) { try { @@ -464,6 +480,25 @@ public class GPXDatabase { return false; } + public boolean updateLastUploadedTime(@NonNull GpxDataItem item, long fileLastUploadedTime) { + SQLiteConnection db = openConnection(false); + if (db != null) { + try { + String fileName = getFileName(item.file); + String fileDir = getFileDir(item.file); + db.execSQL("UPDATE " + GPX_TABLE_NAME + " SET " + + GPX_COL_FILE_LAST_UPLOADED_TIME + " = ? " + + " WHERE " + GPX_COL_NAME + " = ? AND " + GPX_COL_DIR + " = ?", + new Object[] { fileLastUploadedTime, fileName, fileDir }); + item.fileLastUploadedTime = fileLastUploadedTime; + } finally { + db.close(); + } + return true; + } + return false; + } + public boolean rename(@Nullable GpxDataItem item, File currentFile, File newFile) { SQLiteConnection db = openConnection(false); if (db != null){ @@ -721,11 +756,11 @@ public class GPXDatabase { String gradientScaleType = item.gradientScaleType != null ? item.gradientScaleType.getTypeName() : null; if (a != null) { db.execSQL( - "INSERT INTO " + GPX_TABLE_NAME + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + "INSERT INTO " + GPX_TABLE_NAME + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", new Object[] {fileName, fileDir, a.totalDistance, a.totalTracks, a.startTime, a.endTime, a.timeSpan, a.timeMoving, a.totalDistanceMoving, a.diffElevationUp, a.diffElevationDown, a.avgElevation, a.minElevation, a.maxElevation, a.maxSpeed, a.avgSpeed, a.points, a.wptPoints, - color, item.file.lastModified(), item.splitType, item.splitInterval, item.apiImported ? 1 : 0, + color, item.file.lastModified(), item.fileLastUploadedTime, item.splitType, item.splitInterval, item.apiImported ? 1 : 0, Algorithms.encodeStringSet(item.analysis.wptCategoryNames), item.showAsMarkers ? 1 : 0, item.joinSegments ? 1 : 0, item.showArrows ? 1 : 0, item.showStartFinish ? 1 : 0, item.width, item.gradientSpeedPalette, item.gradientAltitudePalette, item.gradientSlopePalette, gradientScaleType}); @@ -735,6 +770,7 @@ public class GPXDatabase { GPX_COL_DIR + ", " + GPX_COL_COLOR + ", " + GPX_COL_FILE_LAST_MODIFIED_TIME + ", " + + GPX_COL_FILE_LAST_UPLOADED_TIME + ", " + GPX_COL_SPLIT_TYPE + ", " + GPX_COL_SPLIT_INTERVAL + ", " + GPX_COL_API_IMPORTED + ", " + @@ -747,8 +783,8 @@ public class GPXDatabase { GPX_COL_GRADIENT_ALTITUDE_COLOR + ", " + GPX_COL_GRADIENT_SLOPE_COLOR + ", " + GPX_COL_GRADIENT_SCALE_TYPE + - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", - new Object[] {fileName, fileDir, color, 0, item.splitType, item.splitInterval, + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + new Object[] {fileName, fileDir, color, 0, item.fileLastUploadedTime, item.splitType, item.splitInterval, item.apiImported ? 1 : 0, item.showAsMarkers ? 1 : 0, item.joinSegments ? 1 : 0, item.showArrows ? 1 : 0, item.showStartFinish ? 1 : 0, item.width, Algorithms.gradientPaletteToString(item.gradientSpeedPalette), @@ -828,19 +864,20 @@ public class GPXDatabase { int wptPoints = (int)query.getInt(17); String color = query.getString(18); long fileLastModifiedTime = query.getLong(19); - int splitType = (int)query.getInt(20); - double splitInterval = query.getDouble(21); - boolean apiImported = query.getInt(22) == 1; - String wptCategoryNames = query.getString(23); - boolean showAsMarkers = query.getInt(24) == 1; - boolean joinSegments = query.getInt(25) == 1; - boolean showArrows = query.getInt(26) == 1; - boolean showStartFinish = query.getInt(27) == 1; - String width = query.getString(28); - String gradientSpeedPalette = query.getString(29); - String gradientAltitudePalette = query.getString(30); - String gradientSlopePalette = query.getString(31); - String gradientScaleType = query.getString(32); + long fileLastUploadedTime = query.getLong(20); + int splitType = (int)query.getInt(21); + double splitInterval = query.getDouble(22); + boolean apiImported = query.getInt(23) == 1; + String wptCategoryNames = query.getString(24); + boolean showAsMarkers = query.getInt(25) == 1; + boolean joinSegments = query.getInt(26) == 1; + boolean showArrows = query.getInt(27) == 1; + boolean showStartFinish = query.getInt(28) == 1; + String width = query.getString(29); + String gradientSpeedPalette = query.getString(30); + String gradientAltitudePalette = query.getString(31); + String gradientSlopePalette = query.getString(32); + String gradientScaleType = query.getString(33); GPXTrackAnalysis a = new GPXTrackAnalysis(); a.totalDistance = totalDistance; @@ -873,6 +910,7 @@ public class GPXDatabase { GpxDataItem item = new GpxDataItem(new File(dir, fileName), a); item.color = parseColor(color); item.fileLastModifiedTime = fileLastModifiedTime; + item.fileLastUploadedTime = fileLastUploadedTime; item.splitType = splitType; item.splitInterval = splitInterval; item.apiImported = apiImported; diff --git a/OsmAnd/src/net/osmand/plus/GpxDbHelper.java b/OsmAnd/src/net/osmand/plus/GpxDbHelper.java index 21154d5819..6bfc639f70 100644 --- a/OsmAnd/src/net/osmand/plus/GpxDbHelper.java +++ b/OsmAnd/src/net/osmand/plus/GpxDbHelper.java @@ -78,6 +78,12 @@ public class GpxDbHelper { return res; } + public boolean updateLastUploadedTime(GpxDataItem item, long fileLastUploadedTime) { + boolean res = db.updateLastUploadedTime(item, fileLastUploadedTime); + putToCache(item); + return res; + } + public boolean updateGradientScalePalette(@NonNull GpxDataItem item, @NonNull GradientScaleType gradientScaleType, int[] palette) { boolean res = db.updateGradientScaleColor(item, gradientScaleType, palette); putToCache(item); diff --git a/OsmAnd/src/net/osmand/plus/OsmandApplication.java b/OsmAnd/src/net/osmand/plus/OsmandApplication.java index 72e4266f35..ddbbd7087e 100644 --- a/OsmAnd/src/net/osmand/plus/OsmandApplication.java +++ b/OsmAnd/src/net/osmand/plus/OsmandApplication.java @@ -52,6 +52,7 @@ import net.osmand.plus.activities.SavingTrackHelper; import net.osmand.plus.activities.actions.OsmAndDialogs; import net.osmand.plus.api.SQLiteAPI; import net.osmand.plus.api.SQLiteAPIImpl; +import net.osmand.plus.backup.BackupHelper; import net.osmand.plus.base.MapViewTrackingUtilities; import net.osmand.plus.download.DownloadIndexesThread; import net.osmand.plus.download.DownloadService; @@ -169,6 +170,7 @@ public class OsmandApplication extends MultiDexApplication { MeasurementEditingContext measurementEditingContext; OnlineRoutingHelper onlineRoutingHelper; ItineraryHelper itineraryHelper; + BackupHelper backupHelper; private Map customRoutingConfigs = new ConcurrentHashMap<>(); private File externalStorageDirectory; @@ -474,6 +476,10 @@ public class OsmandApplication extends MultiDexApplication { return itineraryHelper; } + public BackupHelper getBackupHelper() { + return backupHelper; + } + public TransportRoutingHelper getTransportRoutingHelper() { return transportRoutingHelper; } diff --git a/OsmAnd/src/net/osmand/plus/ProgressImplementation.java b/OsmAnd/src/net/osmand/plus/ProgressImplementation.java index 03d2114c7b..9e673bacdc 100644 --- a/OsmAnd/src/net/osmand/plus/ProgressImplementation.java +++ b/OsmAnd/src/net/osmand/plus/ProgressImplementation.java @@ -5,6 +5,7 @@ import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; +import android.content.res.Resources; import android.os.Handler; import android.os.Message; import android.widget.ProgressBar; @@ -204,7 +205,8 @@ public class ProgressImplementation implements IProgress { work = -1; progress = 0; if (taskName != null) { - message = context.getResources().getString(R.string.finished_task) +" : "+ taskName; //$NON-NLS-1$ + Resources resources = context.getResources(); + message = resources.getString(R.string.ltr_or_rtl_combine_via_colon, resources.getString(R.string.finished_task), taskName); mViewUpdateHandler.sendEmptyMessage(HANDLER_START_TASK); } } diff --git a/OsmAnd/src/net/osmand/plus/backup/BackupHelper.java b/OsmAnd/src/net/osmand/plus/backup/BackupHelper.java new file mode 100644 index 0000000000..be81017d42 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/backup/BackupHelper.java @@ -0,0 +1,581 @@ +package net.osmand.plus.backup; + +import android.annotation.SuppressLint; +import android.os.AsyncTask; +import android.provider.Settings; +import android.util.Pair; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; + +import net.osmand.AndroidNetworkUtils; +import net.osmand.AndroidNetworkUtils.OnFilesDownloadCallback; +import net.osmand.AndroidNetworkUtils.OnFilesUploadCallback; +import net.osmand.AndroidNetworkUtils.OnRequestResultListener; +import net.osmand.AndroidNetworkUtils.OnSendRequestsListener; +import net.osmand.AndroidNetworkUtils.Request; +import net.osmand.AndroidNetworkUtils.RequestResponse; +import net.osmand.AndroidUtils; +import net.osmand.IndexConstants; +import net.osmand.plus.FavouritesDbHelper; +import net.osmand.plus.GPXDatabase.GpxDataItem; +import net.osmand.plus.GpxDbHelper; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.inapp.InAppPurchaseHelper; +import net.osmand.plus.inapp.InAppPurchases.InAppSubscription; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.util.Algorithms; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.File; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BackupHelper { + + private final OsmandApplication app; + private final OsmandSettings settings; + private final FavouritesDbHelper favouritesHelper; + private final GpxDbHelper gpxHelper; + + private static final String SERVER_URL = "https://osmand.net"; + + private static final String USER_REGISTER_URL = SERVER_URL + "/userdata/user-register"; + private static final String DEVICE_REGISTER_URL = SERVER_URL + "/userdata/device-register"; + private static final String UPLOAD_FILE_URL = SERVER_URL + "/userdata/upload-file"; + private static final String LIST_FILES_URL = SERVER_URL + "/userdata/list-files"; + private static final String DOWNLOAD_FILE_URL = SERVER_URL + "/userdata/download-file"; + private static final String DELETE_FILE_URL = SERVER_URL + "/userdata/delete-file"; + + public final static int STATUS_SUCCESS = 0; + public final static int STATUS_PARSE_JSON_ERROR = 1; + public final static int STATUS_EMPTY_RESPONSE_ERROR = 2; + public final static int STATUS_SERVER_ERROR = 3; + + public interface OnResultListener { + void onResult(int status, @Nullable String message, @Nullable JSONObject json); + } + + public interface OnRegisterUserListener { + void onRegisterUser(int status, @Nullable String message); + } + + public interface OnRegisterDeviceListener { + void onRegisterDevice(int status, @Nullable String message); + } + + public interface OnDownloadFileListListener { + void onDownloadFileList(int status, @Nullable String message, @NonNull List userFiles); + } + + public interface OnCollectLocalFilesListener { + void onFileCollected(@NonNull GpxFileInfo fileInfo); + + void onFilesCollected(@NonNull List fileInfos); + } + + public interface OnGenerateBackupInfoListener { + void onBackupInfoGenerated(@Nullable BackupInfo backupInfo, @Nullable String error); + } + + public interface OnUploadFilesListener { + void onFileUploadProgress(@NonNull File file, int progress); + + void onFilesUploadDone(@NonNull Map errors); + } + + public interface OnDeleteFilesListener { + void onFileDeleteProgress(@NonNull UserFile file); + + void onFilesDeleteDone(@NonNull Map errors); + } + + public interface OnDownloadFileListener { + void onFileDownloadProgress(@NonNull UserFile userFile, int progress); + @WorkerThread + void onFileDownloadedAsync(@NonNull File file); + void onFilesDownloadDone(@NonNull Map errors); + } + + public static class BackupInfo { + public List filesToDownload = new ArrayList<>(); + public List filesToUpload = new ArrayList<>(); + public List filesToDelete = new ArrayList<>(); + public List> filesToMerge = new ArrayList<>(); + } + + public BackupHelper(@NonNull OsmandApplication app) { + this.app = app; + this.settings = app.getSettings(); + this.favouritesHelper = app.getFavorites(); + this.gpxHelper = app.getGpxDbHelper(); + } + + @SuppressLint("HardwareIds") + private String getAndroidId() { + try { + return Settings.Secure.getString(app.getContentResolver(), Settings.Secure.ANDROID_ID); + } catch (Exception e) { + return null; + } + } + + public static boolean isTokenValid(@NonNull String token) { + return token.matches("[0-9]+"); + } + + public boolean hasOsmLiveUpdates() { + return InAppPurchaseHelper.isSubscribedToLiveUpdates(app); + } + + @Nullable + public String getOrderId() { + InAppPurchaseHelper purchaseHelper = app.getInAppPurchaseHelper(); + InAppSubscription purchasedSubscription = purchaseHelper.getAnyPurchasedSubscription(); + return purchasedSubscription != null ? purchasedSubscription.getOrderId() : null; + } + + public String getDeviceId() { + return settings.BACKUP_DEVICE_ID.get(); + } + + public String getAccessToken() { + return settings.BACKUP_ACCESS_TOKEN.get(); + } + + public String getEmail() { + return settings.BACKUP_USER_EMAIL.get(); + } + + public boolean isRegistered() { + return !Algorithms.isEmpty(getDeviceId()) && !Algorithms.isEmpty(getAccessToken()); + } + + private void checkRegistered() throws UserNotRegisteredException { + if (Algorithms.isEmpty(getDeviceId()) || Algorithms.isEmpty(getAccessToken())) { + throw new UserNotRegisteredException(); + } + } + + public void registerUser(@NonNull String email, @Nullable final OnRegisterUserListener listener) { + Map params = new HashMap<>(); + params.put("email", email); + params.put("orderid", getOrderId()); + params.put("deviceid", app.getUserAndroidId()); + AndroidNetworkUtils.sendRequestAsync(app, USER_REGISTER_URL, params, "Register user", true, true, new OnRequestResultListener() { + @Override + public void onResult(String resultJson) { + int status; + String message; + if (!Algorithms.isEmpty(resultJson)) { + try { + JSONObject result = new JSONObject(resultJson); + String statusStr = result.getString("status"); + if (statusStr.equals("ok")) { + message = "You have been registered successfully. Please check for email with activation code."; + status = STATUS_SUCCESS; + } else { + message = "User registration error: " + statusStr; + status = STATUS_SERVER_ERROR; + } + } catch (JSONException e) { + message = "User registration error: json parsing"; + status = STATUS_PARSE_JSON_ERROR; + } + } else { + message = "User registration error: empty response"; + status = STATUS_EMPTY_RESPONSE_ERROR; + } + if (listener != null) { + listener.onRegisterUser(status, message); + } + } + }); + } + + public void registerDevice(String token, @Nullable final OnRegisterDeviceListener listener) { + Map params = new HashMap<>(); + params.put("email", getEmail()); + String orderId = getOrderId(); + if (orderId != null) { + params.put("orderid", orderId); + } + String androidId = getAndroidId(); + if (!Algorithms.isEmpty(androidId)) { + params.put("deviceid", androidId); + } + params.put("token", token); + AndroidNetworkUtils.sendRequestAsync(app, DEVICE_REGISTER_URL, params, "Register device", true, true, new OnRequestResultListener() { + @Override + public void onResult(String resultJson) { + int status; + String message; + if (!Algorithms.isEmpty(resultJson)) { + try { + JSONObject result = new JSONObject(resultJson); + settings.BACKUP_DEVICE_ID.set(result.getString("id")); + settings.BACKUP_USER_ID.set(result.getString("userid")); + settings.BACKUP_NATIVE_DEVICE_ID.set(result.getString("deviceid")); + settings.BACKUP_ACCESS_TOKEN.set(result.getString("accesstoken")); + settings.BACKUP_ACCESS_TOKEN_UPDATE_TIME.set(result.getString("udpatetime")); + status = STATUS_SUCCESS; + message = "Device have been registered successfully"; + } catch (JSONException e) { + message = "Device registration error: json parsing"; + status = STATUS_PARSE_JSON_ERROR; + } + } else { + message = "Device registration error: empty response"; + status = STATUS_EMPTY_RESPONSE_ERROR; + } + if (listener != null) { + listener.onRegisterDevice(status, message); + } + } + }); + } + + public void uploadFiles(@NonNull List gpxFiles, @Nullable final OnUploadFilesListener listener) throws UserNotRegisteredException { + checkRegistered(); + + Map params = new HashMap<>(); + params.put("deviceid", getDeviceId()); + params.put("accessToken", getAccessToken()); + Map headers = new HashMap<>(); + headers.put("Accept-Encoding", "deflate, gzip"); + + final Map gpxInfos = new HashMap<>(); + for (GpxFileInfo gpxFile : gpxFiles) { + gpxInfos.put(gpxFile.file, gpxFile); + } + final File favoritesFile = favouritesHelper.getExternalFile(); + AndroidNetworkUtils.uploadFilesAsync(UPLOAD_FILE_URL, new ArrayList<>(gpxInfos.keySet()), true, params, headers, new OnFilesUploadCallback() { + @Nullable + @Override + public Map getAdditionalParams(@NonNull File file) { + Map additionaParams = new HashMap<>(); + GpxFileInfo gpxFileInfo = gpxInfos.get(file); + if (gpxFileInfo != null) { + additionaParams.put("name", gpxFileInfo.getFileName(true)); + additionaParams.put("type", Algorithms.getFileExtension(file)); + gpxFileInfo.uploadTime = System.currentTimeMillis(); + if (file.equals(favoritesFile)) { + favouritesHelper.setLastUploadedTime(gpxFileInfo.uploadTime); + } else { + GpxDataItem gpxItem = gpxHelper.getItem(file); + if (gpxItem != null) { + gpxHelper.updateLastUploadedTime(gpxItem, gpxFileInfo.uploadTime); + } + } + additionaParams.put("clienttime", String.valueOf(gpxFileInfo.uploadTime)); + } + return additionaParams; + } + + @Override + public void onFileUploadProgress(@NonNull File file, int progress) { + if (listener != null) { + listener.onFileUploadProgress(file, progress); + } + } + + @Override + public void onFilesUploadDone(@NonNull Map errors) { + if (errors.isEmpty()) { + settings.BACKUP_LAST_UPLOADED_TIME.set(System.currentTimeMillis() + 1); + } + if (listener != null) { + listener.onFilesUploadDone(errors); + } + } + }); + } + + public void deleteFiles(@NonNull List userFiles, @Nullable final OnDeleteFilesListener listener) throws UserNotRegisteredException { + checkRegistered(); + + Map commonParameters = new HashMap<>(); + commonParameters.put("deviceid", getDeviceId()); + commonParameters.put("accessToken", getAccessToken()); + + final List requests = new ArrayList<>(); + final Map filesMap = new HashMap<>(); + for (UserFile userFile : userFiles) { + Map parameters = new HashMap<>(commonParameters); + parameters.put("name", userFile.getName()); + parameters.put("type", userFile.getType()); + Request r = new Request(DELETE_FILE_URL, parameters, null, false, true); + requests.add(r); + filesMap.put(r, userFile); + } + AndroidNetworkUtils.sendRequestsAsync(null, requests, new OnSendRequestsListener() { + @Override + public void onRequestSent(@NonNull RequestResponse response) { + if (listener != null) { + UserFile userFile = filesMap.get(response.getRequest()); + if (userFile != null) { + listener.onFileDeleteProgress(userFile); + } + } + } + + @Override + public void onRequestsSent(@NonNull List results) { + if (listener != null) { + Map errors = new HashMap<>(); + for (RequestResponse response : results) { + UserFile userFile = filesMap.get(response.getRequest()); + if (userFile != null) { + String responseStr = response.getResponse(); + boolean success; + try { + JSONObject json = new JSONObject(responseStr); + String status = json.getString("status"); + success = status.equalsIgnoreCase("ok"); + } catch (JSONException e) { + success = false; + } + if (!success) { + errors.put(userFile, responseStr); + } + } + } + listener.onFilesDeleteDone(errors); + } + } + }); + } + + public void downloadFileList(@Nullable final OnDownloadFileListListener listener) throws UserNotRegisteredException { + checkRegistered(); + + Map params = new HashMap<>(); + params.put("deviceid", getDeviceId()); + params.put("accessToken", getAccessToken()); + AndroidNetworkUtils.sendRequestAsync(app, LIST_FILES_URL, params, "Download file list", true, false, new OnRequestResultListener() { + @Override + public void onResult(String resultJson) { + int status; + String message; + List userFiles = new ArrayList<>(); + if (!Algorithms.isEmpty(resultJson)) { + try { + JSONObject result = new JSONObject(resultJson); + String totalZipSize = result.getString("totalZipSize"); + String totalFiles = result.getString("totalFiles"); + String totalFileVersions = result.getString("totalFileVersions"); + JSONArray files = result.getJSONArray("uniqueFiles"); + for (int i = 0; i < files.length(); i++) { + userFiles.add(new UserFile(files.getJSONObject(i))); + } + + status = STATUS_SUCCESS; + message = "Total files: " + totalFiles + "\n" + + "Total zip size: " + AndroidUtils.formatSize(app, Long.parseLong(totalZipSize)) + "\n" + + "Total file versions: " + totalFileVersions; + } catch (JSONException | ParseException e) { + status = STATUS_PARSE_JSON_ERROR; + message = "Download file list error: json parsing"; + } + } else { + status = STATUS_EMPTY_RESPONSE_ERROR; + message = "Download file list error: empty response"; + } + if (listener != null) { + listener.onDownloadFileList(status, message, userFiles); + } + } + }); + } + + public void downloadFiles(@NonNull final Map filesMap, @Nullable final OnDownloadFileListener listener) throws UserNotRegisteredException { + checkRegistered(); + + Map params = new HashMap<>(); + params.put("deviceid", getDeviceId()); + params.put("accessToken", getAccessToken()); + AndroidNetworkUtils.downloadFilesAsync(DOWNLOAD_FILE_URL, + new ArrayList<>(filesMap.keySet()), params, new OnFilesDownloadCallback() { + @Nullable + @Override + public Map getAdditionalParams(@NonNull File file) { + UserFile userFile = filesMap.get(file); + Map additionaParams = new HashMap<>(); + additionaParams.put("name", userFile.getName()); + additionaParams.put("type", userFile.getType()); + return additionaParams; + } + + @Override + public void onFileDownloadProgress(@NonNull File file, int percent) { + if (listener != null) { + listener.onFileDownloadProgress(filesMap.get(file), percent); + } + } + + @Override + public void onFileDownloadedAsync(@NonNull File file) { + if (listener != null) { + listener.onFileDownloadedAsync(file); + } + } + + @Override + public void onFilesDownloadDone(@NonNull Map errors) { + if (listener != null) { + listener.onFilesDownloadDone(errors); + } + } + }); + } + + @SuppressLint("StaticFieldLeak") + public void collectLocalFiles(@Nullable final OnCollectLocalFilesListener listener) { + AsyncTask> task = new AsyncTask>() { + + private final OnCollectLocalFilesListener internalListener = new OnCollectLocalFilesListener() { + @Override + public void onFileCollected(@NonNull GpxFileInfo fileInfo) { + publishProgress(fileInfo); + } + + @Override + public void onFilesCollected(@NonNull List fileInfos) { + } + }; + + private void loadGPXData(@NonNull File mapPath, @NonNull List result, + @Nullable OnCollectLocalFilesListener listener) { + if (mapPath.canRead()) { + loadGPXFolder(mapPath, result, "", listener); + } + } + + private void loadGPXFolder(@NonNull File mapPath, @NonNull List result, + @NonNull String gpxSubfolder, @Nullable OnCollectLocalFilesListener listener) { + File[] listFiles = mapPath.listFiles(); + if (listFiles != null) { + for (File gpxFile : listFiles) { + if (gpxFile.isDirectory()) { + String sub = gpxSubfolder.length() == 0 ? gpxFile.getName() : gpxSubfolder + "/" + + gpxFile.getName(); + loadGPXFolder(gpxFile, result, sub, listener); + } else if (gpxFile.isFile() && gpxFile.getName().toLowerCase().endsWith(IndexConstants.GPX_FILE_EXT)) { + GpxFileInfo info = new GpxFileInfo(); + info.subfolder = gpxSubfolder; + info.file = gpxFile; + GpxDataItem gpxItem = gpxHelper.getItem(gpxFile); + if (gpxItem != null) { + info.uploadTime = gpxItem.getFileLastUploadedTime(); + } + result.add(info); + if (listener != null) { + listener.onFileCollected(info); + } + } + } + } + } + + @Override + protected List doInBackground(Void... voids) { + List result = new ArrayList<>(); + + GpxFileInfo favInfo = new GpxFileInfo(); + favInfo.subfolder = ""; + favInfo.file = favouritesHelper.getExternalFile(); + favInfo.uploadTime = favouritesHelper.getLastUploadedTime(); + result.add(favInfo); + if (listener != null) { + listener.onFileCollected(favInfo); + } + + loadGPXData(app.getAppPath(IndexConstants.GPX_INDEX_DIR), result, internalListener); + return result; + } + + @Override + protected void onProgressUpdate(GpxFileInfo... fileInfos) { + if (listener != null) { + listener.onFileCollected(fileInfos[0]); + } + } + + @Override + protected void onPostExecute(List fileInfos) { + if (listener != null) { + listener.onFilesCollected(fileInfos); + } + } + }; + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + @SuppressLint("StaticFieldLeak") + public void generateBackupInfo(@NonNull final List localFiles, @NonNull final List remoteFiles, + @Nullable final OnGenerateBackupInfoListener listener) { + + final long backupLastUploadedTime = settings.BACKUP_LAST_UPLOADED_TIME.get(); + + AsyncTask task = new AsyncTask() { + @Override + protected BackupInfo doInBackground(Void... voids) { + BackupInfo info = new BackupInfo(); + for (UserFile remoteFile : remoteFiles) { + boolean hasLocalFile = false; + for (GpxFileInfo localFile : localFiles) { + if (remoteFile.getName().equals(localFile.getFileName(true))) { + hasLocalFile = true; + long remoteUploadTime = remoteFile.getClienttimems(); + long localUploadTime = localFile.uploadTime; + long localModifiedTime = localFile.file.lastModified(); + if (remoteUploadTime == localUploadTime) { + if (localUploadTime < localModifiedTime) { + info.filesToUpload.add(localFile); + } + } else { + info.filesToMerge.add(new Pair<>(localFile, remoteFile)); + } + break; + } + } + if (!hasLocalFile) { + if (backupLastUploadedTime > 0 && backupLastUploadedTime >= remoteFile.getClienttimems()) { + info.filesToDelete.add(remoteFile); + } else { + info.filesToDownload.add(remoteFile); + } + } + } + for (GpxFileInfo localFile : localFiles) { + boolean hasRemoteFile = false; + for (UserFile remoteFile : remoteFiles) { + if (localFile.getFileName(true).equals(remoteFile.getName())) { + hasRemoteFile = true; + break; + } + } + if (!hasRemoteFile) { + info.filesToUpload.add(localFile); + } + } + return info; + } + + @Override + protected void onPostExecute(BackupInfo backupInfo) { + if (listener != null) { + listener.onBackupInfoGenerated(backupInfo, null); + } + } + }; + task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } +} diff --git a/OsmAnd/src/net/osmand/plus/backup/BackupTask.java b/OsmAnd/src/net/osmand/plus/backup/BackupTask.java new file mode 100644 index 0000000000..a2fc299463 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/backup/BackupTask.java @@ -0,0 +1,335 @@ +package net.osmand.plus.backup; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.AndroidUtils; +import net.osmand.GPXUtilities; +import net.osmand.GPXUtilities.GPXFile; +import net.osmand.plus.GPXDatabase.GpxDataItem; +import net.osmand.plus.GpxDbHelper; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.ProgressImplementation; +import net.osmand.plus.backup.BackupHelper.BackupInfo; +import net.osmand.plus.backup.BackupHelper.OnDeleteFilesListener; +import net.osmand.plus.backup.BackupHelper.OnDownloadFileListener; +import net.osmand.plus.backup.BackupHelper.OnUploadFilesListener; +import net.osmand.plus.importfiles.FavoritesImportTask; +import net.osmand.util.Algorithms; + +import java.io.File; +import java.lang.ref.WeakReference; +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +import static net.osmand.IndexConstants.GPX_INDEX_DIR; +import static net.osmand.IndexConstants.TEMP_DIR; + +public class BackupTask { + + private final OsmandApplication app; + private final BackupHelper backupHelper; + + private final OnBackupListener listener; + private final WeakReference contextRef; + private ProgressImplementation progress; + + private final BackupInfo backupInfo; + private Map uploadErrors; + private Map downloadErrors; + private Map deleteErrors; + private String error; + + private final TaskType[] backupTasks = {TaskType.UPLOAD_FILES, TaskType.DELETE_FILES}; + private final TaskType[] restoreTasks = {TaskType.DOWNLOAD_FILES}; + + private Stack runningTasks = new Stack<>(); + + private enum TaskType { + UPLOAD_FILES, + DOWNLOAD_FILES, + DELETE_FILES + } + + public interface OnBackupListener { + void onBackupDone(@Nullable Map uploadErrors, + @Nullable Map downloadErrors, + @Nullable Map deleteErrors, @Nullable String error); + } + + public BackupTask(@NonNull BackupInfo backupInfo, @NonNull Context context, @Nullable OnBackupListener listener) { + this.contextRef = new WeakReference<>(context); + this.app = (OsmandApplication) context.getApplicationContext(); + this.backupHelper = app.getBackupHelper(); + this.backupInfo = backupInfo; + this.listener = listener; + } + + public BackupInfo getBackupInfo() { + return backupInfo; + } + + public Map getUploadErrors() { + return uploadErrors; + } + + public Map getDownloadErrors() { + return downloadErrors; + } + + public Map getDeleteErrors() { + return deleteErrors; + } + + public String getError() { + return error; + } + + public boolean runBackup() { + if (!runningTasks.empty()) { + return false; + } + initBackupTasks(); + return runTasks(); + } + + public boolean runRestore() { + if (!runningTasks.empty()) { + return false; + } + initRestoreTasks(); + return runTasks(); + } + + private void initBackupTasks() { + initData(); + Stack tasks = new Stack<>(); + for (int i = backupTasks.length - 1; i >= 0; i--) { + tasks.push(backupTasks[i]); + } + this.runningTasks = tasks; + onTasksInit(); + } + + private void initRestoreTasks() { + initData(); + Stack tasks = new Stack<>(); + for (int i = restoreTasks.length - 1; i >= 0; i--) { + tasks.push(restoreTasks[i]); + } + this.runningTasks = tasks; + onTasksInit(); + } + + private void initData() { + uploadErrors = null; + downloadErrors = null; + deleteErrors = null; + error = null; + } + + private boolean runTasks() { + if (runningTasks.empty()) { + return false; + } else { + TaskType taskType = runningTasks.pop(); + runTask(taskType); + return true; + } + } + + private void runTask(@NonNull TaskType taskType) { + switch (taskType) { + case UPLOAD_FILES: + doUploadFiles(); + break; + case DOWNLOAD_FILES: + doDownloadFiles(); + break; + case DELETE_FILES: + doDeleteFiles(); + break; + } + } + + private void onTaskFinished(@NonNull TaskType taskType) { + if (!runTasks()) { + onTasksDone(); + } + } + + private void doUploadFiles() { + if (Algorithms.isEmpty(backupInfo.filesToUpload)) { + onTaskFinished(TaskType.UPLOAD_FILES); + return; + } + onTaskProgressUpdate("Upload files..."); + try { + backupHelper.uploadFiles(backupInfo.filesToUpload, new OnUploadFilesListener() { + @Override + public void onFileUploadProgress(@NonNull File file, int progress) { + if (progress == 0) { + onTaskProgressUpdate(file.getName(), (int) (file.length() / 1024)); + } else { + onTaskProgressUpdate(progress); + } + } + + @Override + public void onFilesUploadDone(@NonNull Map errors) { + uploadErrors = errors; + onTaskFinished(TaskType.UPLOAD_FILES); + } + }); + } catch (UserNotRegisteredException e) { + onError("User is not registered"); + } + } + + private void doDownloadFiles() { + if (Algorithms.isEmpty(backupInfo.filesToDownload)) { + onTaskFinished(TaskType.DOWNLOAD_FILES); + return; + } + onTaskProgressUpdate("Download files..."); + File favoritesFile = app.getFavorites().getExternalFile(); + String favoritesFileName = favoritesFile.getName(); + File tempFavoritesFile = null; + final Map filesMap = new HashMap<>(); + for (UserFile userFile : backupInfo.filesToDownload) { + File file; + String fileName = userFile.getName(); + if (favoritesFileName.equals(fileName)) { + file = new File(app.getAppPath(TEMP_DIR), fileName); + tempFavoritesFile = file; + } else { + file = new File(app.getAppPath(GPX_INDEX_DIR), fileName); + } + filesMap.put(file, userFile); + } + final File finalTempFavoritesFile = tempFavoritesFile; + try { + backupHelper.downloadFiles(filesMap, new OnDownloadFileListener() { + @Override + public void onFileDownloadProgress(@NonNull UserFile userFile, int progress) { + if (progress == 0) { + onTaskProgressUpdate(new File(userFile.getName()).getName(), userFile.getFilesize() / 1024); + } else { + onTaskProgressUpdate(progress); + } + } + + @Override + public void onFileDownloadedAsync(@NonNull File file) { + UserFile userFile = filesMap.get(file); + long userFileTime = userFile.getClienttimems(); + if (file.equals(finalTempFavoritesFile)) { + GPXFile gpxFile = GPXUtilities.loadGPXFile(finalTempFavoritesFile); + FavoritesImportTask.mergeFavorites(app, gpxFile, "", false); + finalTempFavoritesFile.delete(); + app.getFavorites().getExternalFile().setLastModified(userFileTime); + } else { + file.setLastModified(userFileTime); + GpxDataItem item = new GpxDataItem(file, userFileTime); + app.getGpxDbHelper().add(item); + } + } + + @Override + public void onFilesDownloadDone(@NonNull Map errors) { + downloadErrors = errors; + onTaskFinished(TaskType.DOWNLOAD_FILES); + } + }); + } catch (UserNotRegisteredException e) { + onError("User is not registered"); + } + } + + private void doDeleteFiles() { + if (Algorithms.isEmpty(backupInfo.filesToDelete)) { + onTaskFinished(TaskType.DELETE_FILES); + return; + } + onTaskProgressUpdate("Delete files..."); + try { + backupHelper.deleteFiles(backupInfo.filesToDelete, new OnDeleteFilesListener() { + @Override + public void onFileDeleteProgress(@NonNull UserFile userFile) { + onTaskProgressUpdate(userFile.getName()); + } + + @Override + public void onFilesDeleteDone(@NonNull Map errors) { + deleteErrors = errors; + onTaskFinished(TaskType.DELETE_FILES); + } + }); + } catch (UserNotRegisteredException e) { + onError("User is not registered"); + } + } + + private void onTasksInit() { + Context ctx = contextRef.get(); + if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) { + progress = ProgressImplementation.createProgressDialog(ctx, + "Backup data", "Initializing...", ProgressDialog.STYLE_HORIZONTAL); + } + } + + private void onTaskProgressUpdate(Object... objects) { + Context ctx = contextRef.get(); + if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) { + if (objects != null) { + if (objects.length == 1) { + if (objects[0] instanceof String) { + progress.startTask((String) objects[0], -1); + } else if (objects[0] instanceof Integer) { + int progressValue = (Integer) objects[0]; + if (progressValue < Integer.MAX_VALUE) { + progress.progress(progressValue); + } else { + progress.finishTask(); + } + } + } else if (objects.length == 2) { + progress.startTask((String) objects[0], (Integer) objects[1]); + } + } + } + } + + private void onError(@NonNull String message) { + this.error = message; + runningTasks.clear(); + onTasksDone(); + } + + private void onTasksDone() { + if (listener != null) { + listener.onBackupDone(uploadErrors, downloadErrors, deleteErrors, error); + } + Context ctx = contextRef.get(); + if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) { + progress.finishTask(); + app.runInUIThread(new Runnable() { + @Override + public void run() { + try { + if (progress.getDialog().isShowing()) { + progress.getDialog().dismiss(); + } + } catch (Exception e) { + //ignored + } + } + }, 300); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/backup/GpxFileInfo.java b/OsmAnd/src/net/osmand/plus/backup/GpxFileInfo.java new file mode 100644 index 0000000000..228e119fd6 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/backup/GpxFileInfo.java @@ -0,0 +1,65 @@ +package net.osmand.plus.backup; + +import net.osmand.util.Algorithms; + +import java.io.File; + +public class GpxFileInfo { + public File file; + public String subfolder; + public long uploadTime = 0; + + private String name = null; + private int sz = -1; + private String fileName = null; + + public String getName() { + if (name == null) { + name = formatName(file.getName()); + } + return name; + } + + private String formatName(String name) { + int ext = name.lastIndexOf('.'); + if (ext != -1) { + name = name.substring(0, ext); + } + return name.replace('_', ' '); + } + + // Usage: AndroidUtils.formatSize(v.getContext(), getSize() * 1024l); + public int getSize() { + if (sz == -1) { + if (file == null) { + return -1; + } + sz = (int) ((file.length() + 512) >> 10); + } + return sz; + } + + public long getFileDate() { + if (file == null) { + return 0; + } + return file.lastModified(); + } + + public String getFileName(boolean includeSubfolder) { + String result; + if (fileName != null) { + result = fileName; + } else { + if (file == null) { + result = ""; + } else { + result = fileName = file.getName(); + } + } + if (includeSubfolder && !Algorithms.isEmpty(subfolder)) { + result = subfolder + "/" + result; + } + return result; + } +} diff --git a/OsmAnd/src/net/osmand/plus/backup/PrepareBackupTask.java b/OsmAnd/src/net/osmand/plus/backup/PrepareBackupTask.java new file mode 100644 index 0000000000..d3932b5ba8 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/backup/PrepareBackupTask.java @@ -0,0 +1,211 @@ +package net.osmand.plus.backup; + +import android.app.Activity; +import android.app.ProgressDialog; +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.AndroidUtils; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.ProgressImplementation; +import net.osmand.plus.backup.BackupHelper.BackupInfo; +import net.osmand.plus.backup.BackupHelper.OnCollectLocalFilesListener; +import net.osmand.plus.backup.BackupHelper.OnDownloadFileListListener; +import net.osmand.plus.backup.BackupHelper.OnGenerateBackupInfoListener; +import net.osmand.util.Algorithms; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.Stack; + +public class PrepareBackupTask { + + private final OsmandApplication app; + private final BackupHelper backupHelper; + + private final OnPrepareBackupListener listener; + private final WeakReference contextRef; + private ProgressImplementation progress; + + private BackupInfo result; + private List userFiles; + private List fileInfos; + private String error; + + private Stack runningTasks = new Stack<>(); + + private enum TaskType { + COLLECT_LOCAL_FILES, + COLLECT_REMOTE_FILES, + GENERATE_BACKUP_INFO + } + + public interface OnPrepareBackupListener { + void onBackupPrepared(@Nullable BackupInfo backupInfo, @Nullable String error); + } + + public PrepareBackupTask(@NonNull Context context, @Nullable OnPrepareBackupListener listener) { + this.contextRef = new WeakReference<>(context); + this.app = (OsmandApplication) context.getApplicationContext(); + this.backupHelper = app.getBackupHelper(); + this.listener = listener; + } + + public BackupInfo getResult() { + return result; + } + + public String getError() { + return error; + } + + public boolean prepare() { + if (!runningTasks.empty()) { + return false; + } + initTasks(); + return runTasks(); + } + + private void initTasks() { + result = null; + userFiles = null; + fileInfos = null; + error = null; + Stack tasks = new Stack<>(); + TaskType[] types = TaskType.values(); + for (int i = types.length - 1; i >= 0; i--) { + tasks.push(types[i]); + } + this.runningTasks = tasks; + onTasksInit(); + } + + private boolean runTasks() { + if (runningTasks.empty()) { + return false; + } else { + TaskType taskType = runningTasks.pop(); + runTask(taskType); + return true; + } + } + + private void runTask(@NonNull TaskType taskType) { + switch (taskType) { + case COLLECT_LOCAL_FILES: + doCollectLocalFiles(); + break; + case COLLECT_REMOTE_FILES: + doCollectRemoteFiles(); + break; + case GENERATE_BACKUP_INFO: + doGenerateBackupInfo(); + break; + } + } + + private void onTaskFinished(@NonNull TaskType taskType) { + if (!runTasks()) { + onTasksDone(); + } + } + + private void doCollectLocalFiles() { + onTaskProgressUpdate("Collecting local info..."); + backupHelper.collectLocalFiles(new OnCollectLocalFilesListener() { + @Override + public void onFileCollected(@NonNull GpxFileInfo fileInfo) { + } + + @Override + public void onFilesCollected(@NonNull List fileInfos) { + PrepareBackupTask.this.fileInfos = fileInfos; + onTaskFinished(TaskType.COLLECT_LOCAL_FILES); + } + }); + } + + private void doCollectRemoteFiles() { + onTaskProgressUpdate("Downloading remote info..."); + try { + backupHelper.downloadFileList(new OnDownloadFileListListener() { + @Override + public void onDownloadFileList(int status, @Nullable String message, @NonNull List userFiles) { + if (status == BackupHelper.STATUS_SUCCESS) { + PrepareBackupTask.this.userFiles = userFiles; + } else { + onError(!Algorithms.isEmpty(message) ? message : "Download file list error: " + status); + } + onTaskFinished(TaskType.COLLECT_REMOTE_FILES); + } + }); + } catch (UserNotRegisteredException e) { + onError("User is not registered"); + } + } + + private void doGenerateBackupInfo() { + if (fileInfos == null || userFiles == null) { + onTaskFinished(TaskType.GENERATE_BACKUP_INFO); + return; + } + onTaskProgressUpdate("Generating backup info..."); + backupHelper.generateBackupInfo(fileInfos, userFiles, new OnGenerateBackupInfoListener() { + @Override + public void onBackupInfoGenerated(@Nullable BackupInfo backupInfo, @Nullable String error) { + if (Algorithms.isEmpty(error)) { + PrepareBackupTask.this.result = backupInfo; + } else { + onError(error); + } + onTaskFinished(TaskType.GENERATE_BACKUP_INFO); + } + }); + } + + private void onTasksInit() { + Context ctx = contextRef.get(); + if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx)) { + progress = ProgressImplementation.createProgressDialog(ctx, + "Prepare backup", "Initializing...", ProgressDialog.STYLE_HORIZONTAL); + } + } + + private void onTaskProgressUpdate(String message) { + Context ctx = contextRef.get(); + if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) { + progress.startTask(message, -1); + } + } + + private void onError(@NonNull String message) { + this.error = message; + runningTasks.clear(); + onTasksDone(); + } + + private void onTasksDone() { + if (listener != null) { + listener.onBackupPrepared(result, error); + } + Context ctx = contextRef.get(); + if (ctx instanceof Activity && AndroidUtils.isActivityNotDestroyed((Activity) ctx) && progress != null) { + progress.finishTask(); + app.runInUIThread(new Runnable() { + @Override + public void run() { + try { + if (progress.getDialog().isShowing()) { + progress.getDialog().dismiss(); + } + } catch (Exception e) { + //ignored + } + } + }, 300); + } + } +} diff --git a/OsmAnd/src/net/osmand/plus/backup/UserFile.java b/OsmAnd/src/net/osmand/plus/backup/UserFile.java new file mode 100644 index 0000000000..437c46963b --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/backup/UserFile.java @@ -0,0 +1,102 @@ +package net.osmand.plus.backup; + +import androidx.annotation.NonNull; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.text.DateFormat; +import java.text.ParseException; +import java.util.Date; +import java.util.Locale; + +public class UserFile { + + private int userid; + private long id; + private int deviceid; + private int filesize; + private String type; + private String name; + private Date updatetime; + private long updatetimems; + private Date clienttime; + private long clienttimems; + private int zipSize; + + public UserFile(@NonNull JSONObject json) throws JSONException, ParseException { + if (json.has("userid")) { + userid = json.getInt("userid"); + } + if (json.has("id")) { + id = json.getLong("id"); + } + if (json.has("deviceid")) { + deviceid = json.getInt("deviceid"); + } + if (json.has("filesize")) { + filesize = json.getInt("filesize"); + } + if (json.has("type")) { + type = json.getString("type"); + } + if (json.has("name")) { + name = json.getString("name"); + } + if (json.has("updatetimems")) { + updatetimems = json.getLong("updatetimems"); + updatetime = new Date(updatetimems); + } + if (json.has("clienttimems")) { + clienttimems = json.getLong("clienttimems"); + clienttime = new Date(clienttimems); + } + if (json.has("zipSize")) { + zipSize = json.getInt("zipSize"); + } + } + + public int getUserid() { + return userid; + } + + public long getId() { + return id; + } + + public int getDeviceid() { + return deviceid; + } + + public int getFilesize() { + return filesize; + } + + public String getType() { + return type; + } + + public String getName() { + return name; + } + + public Date getUpdatetime() { + return updatetime; + } + + public long getUpdatetimems() { + return updatetimems; + } + + public Date getClienttime() { + return clienttime; + } + + public long getClienttimems() { + return clienttimems; + } + + public int getZipSize() { + return zipSize; + } +} diff --git a/OsmAnd/src/net/osmand/plus/backup/UserNotRegisteredException.java b/OsmAnd/src/net/osmand/plus/backup/UserNotRegisteredException.java new file mode 100644 index 0000000000..a414022b91 --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/backup/UserNotRegisteredException.java @@ -0,0 +1,9 @@ +package net.osmand.plus.backup; + +public class UserNotRegisteredException extends Exception { + private static final long serialVersionUID = -8005954380280822845L; + + public UserNotRegisteredException() { + super("User is not resistered"); + } +} diff --git a/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java b/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java index 0f1d1775bd..516e1cc581 100644 --- a/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java @@ -1,72 +1,64 @@ package net.osmand.plus.development; import android.app.Activity; -import android.app.ProgressDialog; import android.graphics.drawable.Drawable; -import android.os.AsyncTask; import android.os.Bundle; -import android.text.TextUtils; -import android.util.Patterns; import android.util.TypedValue; import android.view.View; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.TextView; -import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.Toolbar; -import net.osmand.AndroidNetworkUtils; -import net.osmand.AndroidNetworkUtils.OnFilesUploadCallback; -import net.osmand.AndroidNetworkUtils.OnRequestResultListener; import net.osmand.AndroidUtils; -import net.osmand.IndexConstants; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.ProgressImplementation; import net.osmand.plus.R; import net.osmand.plus.UiUtilities; import net.osmand.plus.UiUtilities.DialogButtonType; import net.osmand.plus.activities.OsmandActionBarActivity; +import net.osmand.plus.backup.BackupHelper; +import net.osmand.plus.backup.BackupHelper.BackupInfo; +import net.osmand.plus.backup.BackupHelper.OnRegisterUserListener; +import net.osmand.plus.backup.BackupTask; +import net.osmand.plus.backup.BackupTask.OnBackupListener; +import net.osmand.plus.backup.PrepareBackupTask; +import net.osmand.plus.backup.PrepareBackupTask.OnPrepareBackupListener; +import net.osmand.plus.backup.UserFile; import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.widgets.OsmandTextFieldBoxes; import net.osmand.util.Algorithms; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - import java.io.File; import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; import java.util.Map; +import java.util.Map.Entry; public class TestBackupActivity extends OsmandActionBarActivity { - // TODO pass actual sub order id! - private static final String TEST_ORDER_ID = ""; - private OsmandApplication app; private OsmandSettings settings; + private BackupHelper backupHelper; private ProgressBar progressBar; private View buttonRegister; private View buttonVerify; + private View buttonRefresh; private View buttonBackup; private View buttonRestore; private EditText emailEditText; + private OsmandTextFieldBoxes tokenEdit; private EditText tokenEditText; private TextView infoView; - public interface OnResultListener { - void onResult(boolean success, @Nullable String result); - } + private BackupInfo backupInfo; @Override public void onCreate(Bundle savedInstanceState) { app = getMyApplication(); settings = app.getSettings(); + backupHelper = app.getBackupHelper(); final WeakReference activityRef = new WeakReference<>(this); boolean nightMode = !app.getSettings().isLightContent(); @@ -90,15 +82,23 @@ public class TestBackupActivity extends OsmandActionBarActivity { } }); + if (!backupHelper.hasOsmLiveUpdates()) { + findViewById(R.id.main_view).setVisibility(View.GONE); + return; + } + buttonRegister = findViewById(R.id.btn_register); UiUtilities.setupDialogButton(nightMode, buttonRegister, DialogButtonType.PRIMARY, "Register"); buttonVerify = findViewById(R.id.btn_verify); UiUtilities.setupDialogButton(nightMode, buttonVerify, DialogButtonType.PRIMARY, "Verify"); + buttonRefresh = findViewById(R.id.btn_refresh); + UiUtilities.setupDialogButton(nightMode, buttonRefresh, DialogButtonType.PRIMARY, "Refresh"); buttonBackup = findViewById(R.id.btn_backup); UiUtilities.setupDialogButton(nightMode, buttonBackup, DialogButtonType.PRIMARY, "Backup"); buttonRestore = findViewById(R.id.btn_restore); UiUtilities.setupDialogButton(nightMode, buttonRestore, DialogButtonType.PRIMARY, "Restore"); + tokenEdit = findViewById(R.id.edit_token_label); tokenEditText = findViewById(R.id.edit_token); infoView = findViewById(R.id.text_info); progressBar = findViewById(R.id.progress_bar); @@ -109,22 +109,31 @@ public class TestBackupActivity extends OsmandActionBarActivity { if (!Algorithms.isEmpty(email)) { emailEditText.setText(email); } + if (backupHelper.isRegistered()) { + tokenEdit.setVisibility(View.GONE); + buttonVerify.setVisibility(View.GONE); + } else { + tokenEdit.setVisibility(View.VISIBLE); + buttonVerify.setVisibility(View.VISIBLE); + } buttonRegister.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String email = emailEditText.getText().toString(); - if (isEmailValid(email)) { + if (AndroidUtils.isValidEmail(email)) { buttonRegister.setEnabled(false); settings.BACKUP_USER_EMAIL.set(email); progressBar.setVisibility(View.VISIBLE); - registerUser(email, new OnResultListener() { + backupHelper.registerUser(email, new OnRegisterUserListener() { @Override - public void onResult(boolean success, @Nullable String result) { + public void onRegisterUser(int status, @Nullable String message) { TestBackupActivity a = activityRef.get(); if (AndroidUtils.isActivityNotDestroyed(a)) { a.progressBar.setVisibility(View.GONE); - a.buttonRegister.setEnabled(!success); - a.buttonVerify.setEnabled(success); + a.buttonRegister.setEnabled(status != BackupHelper.STATUS_SUCCESS); + a.tokenEdit.setVisibility(View.VISIBLE); + a.buttonVerify.setVisibility(View.VISIBLE); + a.buttonVerify.setEnabled(status == BackupHelper.STATUS_SUCCESS); a.tokenEditText.requestFocus(); } } @@ -139,17 +148,22 @@ public class TestBackupActivity extends OsmandActionBarActivity { @Override public void onClick(View v) { String token = tokenEditText.getText().toString(); - if (isTokenValid(token)) { + if (BackupHelper.isTokenValid(token)) { buttonVerify.setEnabled(false); progressBar.setVisibility(View.VISIBLE); - registerDevice(token, new OnResultListener() { + backupHelper.registerDevice(token, new BackupHelper.OnRegisterDeviceListener() { + @Override - public void onResult(boolean success, @Nullable String result) { + public void onRegisterDevice(int status, @Nullable String message) { TestBackupActivity a = activityRef.get(); if (AndroidUtils.isActivityNotDestroyed(a)) { a.progressBar.setVisibility(View.GONE); - a.buttonVerify.setEnabled(!success); - a.loadBackupInfo(); + a.buttonVerify.setEnabled(status != BackupHelper.STATUS_SUCCESS); + if (status == BackupHelper.STATUS_SUCCESS) { + tokenEdit.setVisibility(View.GONE); + buttonVerify.setVisibility(View.GONE); + } + a.prepareBackup(); } } }); @@ -160,262 +174,120 @@ public class TestBackupActivity extends OsmandActionBarActivity { } } }); + buttonRefresh.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + prepareBackup(); + } + }); buttonBackup.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - uploadFiles(); + if (backupInfo != null) { + BackupTask task = new BackupTask(backupInfo, TestBackupActivity.this, new OnBackupListener() { + @Override + public void onBackupDone(@Nullable Map uploadErrors, @Nullable Map downloadErrors, + @Nullable Map deleteErrors, @Nullable String error) { + TestBackupActivity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + String description; + if (error != null) { + description = error; + } else if (uploadErrors == null && downloadErrors == null) { + description = "No data"; + } else { + description = getBackupDescription(uploadErrors, downloadErrors, deleteErrors, error); + } + a.infoView.setText(description); + a.infoView.requestFocus(); + a.prepareBackup(); + } + } + }); + task.runBackup(); + } } }); buttonRestore.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - } - }); - - loadBackupInfo(); - } - - private void loadBackupInfo() { - if (!Algorithms.isEmpty(getDeviceId()) && !Algorithms.isEmpty(getAccessToken())) { - final WeakReference activityRef = new WeakReference<>(this); - progressBar.setVisibility(View.VISIBLE); - loadBackupInfo(new OnResultListener() { - @Override - public void onResult(boolean success, @Nullable String result) { - TestBackupActivity a = activityRef.get(); - if (AndroidUtils.isActivityNotDestroyed(a)) { - a.progressBar.setVisibility(View.GONE); - a.infoView.setText(result); - a.infoView.requestFocus(); - } - } - }); - } - } - - private boolean isEmailValid(CharSequence target) { - return (!TextUtils.isEmpty(target) && Patterns.EMAIL_ADDRESS.matcher(target).matches()); - } - - private String getOrderId() { - return TEST_ORDER_ID; - } - - private String getDeviceId() { - return settings.BACKUP_DEVICE_ID.get(); - } - - private String getAccessToken() { - return settings.BACKUP_ACCESS_TOKEN.get(); - } - - private void registerUser(@NonNull String email, @Nullable final OnResultListener listener) { - Map params = new HashMap<>(); - params.put("email", email); - params.put("orderid", getOrderId()); - params.put("deviceid", app.getUserAndroidId()); - AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/userdata/user-register", params, "Register user", true, true, new OnRequestResultListener() { - @Override - public void onResult(String resultJson) { - boolean success = false; - if (!Algorithms.isEmpty(resultJson)) { - try { - // {"status":"ok"} - JSONObject result = new JSONObject(resultJson); - String status = result.getString("status"); - success = status.equals("ok"); - app.showToastMessage(success - ? "You have been registered successfully. Please check for email with activation code." - : "User registration error: " + status); - } catch (JSONException e) { - app.showToastMessage("User registration error: json parsing"); - } - } else { - app.showToastMessage("User registration error: empty response"); - } - if (listener != null) { - listener.onResult(success, resultJson); - } - } - }); - } - - private void registerDevice(String token, @Nullable final OnResultListener listener) { - Map params = new HashMap<>(); - params.put("email", settings.BACKUP_USER_EMAIL.get()); - params.put("orderid", getOrderId()); - params.put("deviceid", app.getUserAndroidId()); - params.put("token", token); - AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/userdata/device-register", params, "Register device", true, true, new OnRequestResultListener() { - @Override - public void onResult(String resultJson) { - boolean success = false; - if (!Algorithms.isEmpty(resultJson)) { - try { - /* - { - "id": 1034, - "userid": 1033, - "deviceid": "2fa8080d2985a777", - "orderid": "460000687003939", - "accesstoken": "4bc0a61f-397a-4c3e-9ffc-db382ec00372", - "udpatetime": "Apr 11, 2021, 11:32:20 AM" - } - */ - JSONObject result = new JSONObject(resultJson); - settings.BACKUP_DEVICE_ID.set(result.getString("id")); - settings.BACKUP_USER_ID.set(result.getString("userid")); - settings.BACKUP_NATIVE_DEVICE_ID.set(result.getString("deviceid")); - settings.BACKUP_ACCESS_TOKEN.set(result.getString("accesstoken")); - settings.BACKUP_ACCESS_TOKEN_UPDATE_TIME.set(result.getString("udpatetime")); - success = true; - app.showToastMessage("Device have been registered successfully"); - } catch (JSONException e) { - app.showToastMessage("Device registration error: json parsing"); - } - } else { - app.showToastMessage("Device registration error: empty response"); - } - if (listener != null) { - listener.onResult(success, resultJson); - } - } - }); - } - - private void uploadFiles() { - LoadGpxTask loadGpxTask = new LoadGpxTask(this, new LoadGpxTask.OnLoadGpxListener() { - @Override - public void onLoadGpxDone(@NonNull List result) { - uploadFiles(result); - } - }); - loadGpxTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, this); - } - - private void uploadFiles(List gpxFiles) { - //{"status":"ok"} - final WeakReference activityRef = new WeakReference<>(this); - - Map params = new HashMap<>(); - params.put("deviceid", getDeviceId()); - params.put("accessToken", getAccessToken()); - Map headers = new HashMap<>(); - headers.put("Accept-Encoding", "deflate, gzip"); - - final Map gpxInfos = new HashMap<>(); - for (GpxInfo gpxFile : gpxFiles) { - gpxInfos.put(gpxFile.file, gpxFile); - } - final List files = new ArrayList<>(gpxInfos.keySet()); - File favoritesFile = app.getFavorites().getExternalFile(); - files.add(favoritesFile); - - final ProgressImplementation progress = ProgressImplementation.createProgressDialog(this, - "Create backup", "Uploading " + files.size() + " file(s) to server", ProgressDialog.STYLE_HORIZONTAL); - - AndroidNetworkUtils.uploadFilesAsync("https://osmand.net/userdata/upload-file", files, true, params, headers, new OnFilesUploadCallback() { - @Nullable - @Override - public Map getAdditionalParams(@NonNull File file) { - GpxInfo gpxInfo = gpxInfos.get(file); - Map additionaParams = new HashMap<>(); - additionaParams.put("name", gpxInfo == null ? file.getName() : gpxInfo.getFileName(true)); - additionaParams.put("type", Algorithms.getFileExtension(file)); - return additionaParams; - } - - @Override - public void onFileUploadProgress(@NonNull File file, int percent) { - Activity a = activityRef.get(); - if (AndroidUtils.isActivityNotDestroyed(a)) { - if (percent < 100) { - progress.startTask(file.getName(), percent); - } else { - progress.finishTask(); - } - } - } - - @Override - public void onFilesUploadDone(@NonNull Map errors) { - Activity a = activityRef.get(); - if (AndroidUtils.isActivityNotDestroyed(a)) { - app.runInUIThread(new Runnable() { + if (backupInfo != null) { + BackupTask task = new BackupTask(backupInfo, TestBackupActivity.this, new OnBackupListener() { @Override - public void run() { - try { - if (progress.getDialog().isShowing()) { - progress.getDialog().dismiss(); + public void onBackupDone(@Nullable Map uploadErrors, @Nullable Map downloadErrors, + @Nullable Map deleteErrors, @Nullable String error) { + TestBackupActivity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + String description; + if (error != null) { + description = error; + } else if (uploadErrors == null && downloadErrors == null) { + description = "No data"; + } else { + description = getBackupDescription(uploadErrors, downloadErrors, deleteErrors, error); } - } catch (Exception e) { - //ignored + a.infoView.setText(description); + a.infoView.requestFocus(); + a.prepareBackup(); } } - }, 300); - app.showToastMessage("Uploaded " + (files.size() - errors.size() + " files" + - (errors.size() > 0 ? ". Errors: " + errors.size() : ""))); - loadBackupInfo(); + }); + task.runRestore(); } } }); + + prepareBackup(); } - private void loadBackupInfo(@Nullable final OnResultListener listener) { - Map params = new HashMap<>(); - params.put("deviceid", getDeviceId()); - params.put("accessToken", getAccessToken()); - AndroidNetworkUtils.sendRequestAsync(app, "https://osmand.net/userdata/list-files", params, "Get backup info", true, false, new OnRequestResultListener() { + private String getBackupDescription(@Nullable Map uploadErrors, @Nullable Map downloadErrors, @Nullable Map deleteErrors, @Nullable String error) { + StringBuilder sb = new StringBuilder(); + if (!Algorithms.isEmpty(uploadErrors)) { + sb.append("--- Upload errors ---").append("\n"); + for (Entry uploadEntry : uploadErrors.entrySet()) { + sb.append(uploadEntry.getKey().getName()).append(": ").append(uploadEntry.getValue()).append("\n"); + } + } + if (!Algorithms.isEmpty(downloadErrors)) { + sb.append("--- Download errors ---").append("\n"); + for (Entry downloadEntry : downloadErrors.entrySet()) { + sb.append(downloadEntry.getKey().getName()).append(": ").append(downloadEntry.getValue()).append("\n"); + } + } + if (!Algorithms.isEmpty(deleteErrors)) { + sb.append("--- Delete errors ---").append("\n"); + for (Entry deleteEntry : deleteErrors.entrySet()) { + sb.append(deleteEntry.getKey().getName()).append(": ").append(deleteEntry.getValue()).append("\n"); + } + } + return sb.length() == 0 ? "OK" : sb.toString(); + } + + private void prepareBackup() { + final WeakReference activityRef = new WeakReference<>(this); + PrepareBackupTask prepareBackupTask = new PrepareBackupTask(this, new OnPrepareBackupListener() { @Override - public void onResult(String resultJson) { - boolean success = false; - StringBuilder resultString = new StringBuilder(); - if (!Algorithms.isEmpty(resultJson)) { - try { - /* - { - "totalZipSize": 21792, - "totalFileSize": 185920, - "totalFiles": 1, - "totalFileVersions": 2, - "uniqueFiles": [ - { - "userid": 1033, - "id": 7, - "deviceid": 1034, - "filesize": 92960, - "type": "gpx", - "name": "test/Day 2.gpx", - "updatetime": "Apr 11, 2021, 1:49:01 PM", - "updatetimems": 1618141741822, - "zipSize": 10896 - } - ], - "deviceid": 1034 - } - */ - JSONObject result = new JSONObject(resultJson); - String totalZipSize = result.getString("totalZipSize"); - String totalFiles = result.getString("totalFiles"); - String totalFileVersions = result.getString("totalFileVersions"); - JSONArray files = result.getJSONArray("uniqueFiles"); - resultString.append("Total files: ").append(totalFiles).append("\n"); - resultString.append("Total zip size: ").append(AndroidUtils.formatSize(app, Long.parseLong(totalZipSize))).append("\n"); - resultString.append("Total file versions: ").append(totalFileVersions); - - success = true; - } catch (JSONException e) { + public void onBackupPrepared(@Nullable BackupInfo backupInfo, @Nullable String error) { + TestBackupActivity.this.backupInfo = backupInfo; + TestBackupActivity a = activityRef.get(); + if (AndroidUtils.isActivityNotDestroyed(a)) { + String description; + if (error != null) { + description = error; + } else if (backupInfo == null) { + description = "No data"; + } else { + description = "Files to upload: " + backupInfo.filesToUpload.size() + + "\nFiles to download: " + backupInfo.filesToDownload.size() + + "\nFiles to delete: " + backupInfo.filesToDelete.size() + + "\nConflicts: " + backupInfo.filesToMerge.size(); } - } - if (listener != null) { - listener.onResult(success, resultString.toString()); + a.infoView.setText(description); + a.infoView.requestFocus(); } } }); - } - - private boolean isTokenValid(String token) { - return token.matches("[0-9]+"); + prepareBackupTask.prepare(); } private int resolveResourceId(final Activity activity, final int attr) { @@ -423,173 +295,4 @@ public class TestBackupActivity extends OsmandActionBarActivity { activity.getTheme().resolveAttribute(attr, typedvalueattr, true); return typedvalueattr.resourceId; } - - private static class LoadGpxTask extends AsyncTask> { - - private final OsmandApplication app; - private final OnLoadGpxListener listener; - private final WeakReference activityRef; - private List result; - private ProgressImplementation progress; - - interface OnLoadGpxListener { - void onLoadGpxDone(@NonNull List result); - } - - LoadGpxTask(@NonNull Activity activity, @Nullable OnLoadGpxListener listener) { - this.activityRef = new WeakReference<>(activity); - this.app = (OsmandApplication) activity.getApplication(); - this.listener = listener; - } - - public List getResult() { - return result; - } - - @NonNull - @Override - protected List doInBackground(Activity... params) { - List result = new ArrayList<>(); - loadGPXData(app.getAppPath(IndexConstants.GPX_INDEX_DIR), result, this); - return result; - } - - public void loadFile(GpxInfo... loaded) { - publishProgress(loaded); - } - - @Override - protected void onPreExecute() { - Activity a = activityRef.get(); - if (AndroidUtils.isActivityNotDestroyed(a)) { - progress = ProgressImplementation.createProgressDialog(a, - "Create backup", "Collecting gpx files...", ProgressDialog.STYLE_HORIZONTAL); - } - } - - @Override - protected void onProgressUpdate(GpxInfo... values) { - Activity a = activityRef.get(); - if (AndroidUtils.isActivityNotDestroyed(a)) { - progress.startTask(values[0].getFileName(true), -1); - } - } - - @Override - protected void onPostExecute(@NonNull List result) { - this.result = result; - if (listener != null) { - listener.onLoadGpxDone(result); - } - Activity a = activityRef.get(); - if (AndroidUtils.isActivityNotDestroyed(a)) { - progress.finishTask(); - app.runInUIThread(new Runnable() { - @Override - public void run() { - try { - if (progress.getDialog().isShowing()) { - progress.getDialog().dismiss(); - } - } catch (Exception e) { - //ignored - } - } - }, 300); - } - } - - private void loadGPXData(File mapPath, List result, LoadGpxTask loadTask) { - if (mapPath.canRead()) { - List progress = new ArrayList<>(); - loadGPXFolder(mapPath, result, loadTask, progress, ""); - if (!progress.isEmpty()) { - loadTask.loadFile(progress.toArray(new GpxInfo[0])); - } - } - } - - private void loadGPXFolder(File mapPath, List result, LoadGpxTask loadTask, List progress, - String gpxSubfolder) { - File[] listFiles = mapPath.listFiles(); - if (listFiles != null) { - for (File gpxFile : listFiles) { - if (gpxFile.isDirectory()) { - String sub = gpxSubfolder.length() == 0 ? gpxFile.getName() : gpxSubfolder + "/" - + gpxFile.getName(); - loadGPXFolder(gpxFile, result, loadTask, progress, sub); - } else if (gpxFile.isFile() && gpxFile.getName().toLowerCase().endsWith(IndexConstants.GPX_FILE_EXT)) { - GpxInfo info = new GpxInfo(); - info.subfolder = gpxSubfolder; - info.file = gpxFile; - result.add(info); - progress.add(info); - if (progress.size() > 7) { - loadTask.loadFile(progress.toArray(new GpxInfo[0])); - progress.clear(); - } - } - } - } - } - } - - private static class GpxInfo { - public File file; - public String subfolder; - - private String name = null; - private int sz = -1; - private String fileName = null; - - public String getName() { - if (name == null) { - name = formatName(file.getName()); - } - return name; - } - - private String formatName(String name) { - int ext = name.lastIndexOf('.'); - if (ext != -1) { - name = name.substring(0, ext); - } - return name.replace('_', ' '); - } - - // Usage: AndroidUtils.formatSize(v.getContext(), getSize() * 1024l); - public int getSize() { - if (sz == -1) { - if (file == null) { - return -1; - } - sz = (int) ((file.length() + 512) >> 10); - } - return sz; - } - - public long getFileDate() { - if (file == null) { - return 0; - } - return file.lastModified(); - } - - public String getFileName(boolean includeSubfolder) { - String result; - if (fileName != null) { - result = fileName; - } else { - if (file == null) { - result = ""; - } else { - result = fileName = file.getName(); - } - } - if (includeSubfolder && !Algorithms.isEmpty(subfolder)) { - result = subfolder + "/" + result; - } - return result; - } - } } diff --git a/OsmAnd/src/net/osmand/plus/importfiles/FavoritesImportTask.java b/OsmAnd/src/net/osmand/plus/importfiles/FavoritesImportTask.java index b62de4923c..703e920abc 100644 --- a/OsmAnd/src/net/osmand/plus/importfiles/FavoritesImportTask.java +++ b/OsmAnd/src/net/osmand/plus/importfiles/FavoritesImportTask.java @@ -8,6 +8,7 @@ import androidx.fragment.app.FragmentActivity; import net.osmand.GPXUtilities.GPXFile; import net.osmand.data.FavouritePoint; import net.osmand.plus.FavouritesDbHelper; +import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.base.BaseLoadAsyncTask; @@ -17,7 +18,7 @@ import static net.osmand.plus.importfiles.ImportHelper.asFavourites; import static net.osmand.plus.myplaces.FavoritesActivity.FAV_TAB; import static net.osmand.plus.myplaces.FavoritesActivity.TAB_ID; -class FavoritesImportTask extends BaseLoadAsyncTask { +public class FavoritesImportTask extends BaseLoadAsyncTask { private GPXFile gpxFile; private String fileName; @@ -33,6 +34,12 @@ class FavoritesImportTask extends BaseLoadAsyncTask { @Override protected GPXFile doInBackground(Void... nothing) { + mergeFavorites(app, gpxFile, fileName, forceImportFavourites); + return null; + } + + public static void mergeFavorites(@NonNull OsmandApplication app, @NonNull GPXFile gpxFile, + @NonNull String fileName, boolean forceImportFavourites) { List favourites = asFavourites(app, gpxFile.getPoints(), fileName, forceImportFavourites); FavouritesDbHelper favoritesHelper = app.getFavorites(); checkDuplicateNames(favourites); @@ -42,10 +49,9 @@ class FavoritesImportTask extends BaseLoadAsyncTask { } favoritesHelper.sortAll(); favoritesHelper.saveCurrentPointsIntoFile(); - return null; } - public void checkDuplicateNames(List favourites) { + public static void checkDuplicateNames(List favourites) { for (FavouritePoint fp : favourites) { int number = 1; String index; diff --git a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java index 1a36558b87..8a54df1b86 100644 --- a/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java +++ b/OsmAnd/src/net/osmand/plus/inapp/InAppPurchaseHelper.java @@ -10,7 +10,7 @@ import android.util.Log; import net.osmand.AndroidNetworkUtils; import net.osmand.AndroidNetworkUtils.OnRequestResultListener; -import net.osmand.AndroidNetworkUtils.OnRequestsResultListener; +import net.osmand.AndroidNetworkUtils.OnSendRequestsListener; import net.osmand.AndroidNetworkUtils.RequestResponse; import net.osmand.PlatformUtil; import net.osmand.plus.OsmandApplication; @@ -608,9 +608,14 @@ public abstract class InAppPurchaseHelper { addUserInfo(parameters); requests.add(new AndroidNetworkUtils.Request(url, parameters, userOperation, true, true)); } - AndroidNetworkUtils.sendRequestsAsync(ctx, requests, new OnRequestsResultListener() { + AndroidNetworkUtils.sendRequestsAsync(ctx, requests, new OnSendRequestsListener() { + @Override - public void onResult(@NonNull List results) { + public void onRequestSent(@NonNull RequestResponse response) { + } + + @Override + public void onRequestsSent(@NonNull List results) { for (RequestResponse rr : results) { String sku = rr.getRequest().getParameters().get("sku"); PurchaseInfo info = getPurchaseInfo(sku); diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java index 1a77de50fb..5b770bafda 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java @@ -1172,6 +1172,9 @@ public class OsmandSettings { public final OsmandPreference BACKUP_ACCESS_TOKEN = new StringPreference(this, "backup_access_token", "").makeGlobal(); public final OsmandPreference BACKUP_ACCESS_TOKEN_UPDATE_TIME = new StringPreference(this, "backup_access_token_update_time", "").makeGlobal(); + public final OsmandPreference FAVORITES_LAST_UPLOADED_TIME = new LongPreference(this, "favorites_last_uploaded_time", 0L).makeGlobal(); + public final OsmandPreference BACKUP_LAST_UPLOADED_TIME = new LongPreference(this, "backup_last_uploaded_time", 0L).makeGlobal(); + // this value string is synchronized with settings_pref.xml preference name public final OsmandPreference USER_OSM_BUG_NAME = new StringPreference(this, "user_osm_bug_name", "NoName/OsmAnd").makeGlobal().makeShared(); From 40c060af267ed72d4116fed3ab0fc572a4fdc182 Mon Sep 17 00:00:00 2001 From: nazar-kutz Date: Tue, 20 Apr 2021 23:49:03 +0300 Subject: [PATCH 81/86] Remove srtm file of other type after new downloaded --- .../plus/download/DownloadFileHelper.java | 54 +++++++++++++------ .../plus/download/MultipleDownloadItem.java | 2 +- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/download/DownloadFileHelper.java b/OsmAnd/src/net/osmand/plus/download/DownloadFileHelper.java index c85d63361a..257e46db00 100644 --- a/OsmAnd/src/net/osmand/plus/download/DownloadFileHelper.java +++ b/OsmAnd/src/net/osmand/plus/download/DownloadFileHelper.java @@ -7,6 +7,7 @@ import net.osmand.osm.io.NetworkUtils; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.Version; +import net.osmand.plus.download.IndexItem.DownloadEntry; import net.osmand.plus.helpers.FileNameTranslationHelper; import net.osmand.util.Algorithms; @@ -199,33 +200,35 @@ public class DownloadFileHelper { public boolean isWifiConnected(){ return ctx.getSettings().isWifiConnected(); } - - public boolean downloadFile(IndexItem.DownloadEntry de, IProgress progress, - List toReIndex, DownloadFileShowWarning showWarningCallback, boolean forceWifi) throws InterruptedException { + + public boolean downloadFile(IndexItem.DownloadEntry de, IProgress progress, + List toReIndex, DownloadFileShowWarning showWarningCallback, boolean forceWifi) throws InterruptedException { try { final List downloadInputStreams = new ArrayList(); URL url = new URL(de.urlToDownload); //$NON-NLS-1$ log.info("Url downloading " + de.urlToDownload); downloadInputStreams.add(getInputStreamToDownload(url, forceWifi)); de.fileToDownload = de.targetFile; - if(!de.unzipFolder) { - de.fileToDownload = new File(de.targetFile.getParentFile(), de.targetFile.getName() +".download"); + if (!de.unzipFolder) { + de.fileToDownload = new File(de.targetFile.getParentFile(), de.targetFile.getName() + ".download"); } unzipFile(de, progress, downloadInputStreams); - if(!de.targetFile.getAbsolutePath().equals(de.fileToDownload.getAbsolutePath())){ - boolean successfull = Algorithms.removeAllFiles(de.targetFile); - if (successfull) { + if (!de.targetFile.getAbsolutePath().equals(de.fileToDownload.getAbsolutePath())) { + boolean successful = Algorithms.removeAllFiles(de.targetFile); + if (successful) { ctx.getResourceManager().closeFile(de.targetFile.getName()); } - + boolean renamed = de.fileToDownload.renameTo(de.targetFile); - if(!renamed) { + if (!renamed) { showWarningCallback.showWarning(ctx.getString(R.string.shared_string_io_error) + ": old file can't be deleted"); return false; } } - if (de.type == DownloadActivityType.VOICE_FILE){ + if (de.type == DownloadActivityType.VOICE_FILE) { copyVoiceConfig(de); + } else if (de.type == DownloadActivityType.SRTM_COUNTRY_FILE) { + removePreviousSrtmFile(de); } toReIndex.add(de.targetFile); return true; @@ -238,6 +241,26 @@ public class DownloadFileHelper { } } + private void removePreviousSrtmFile(DownloadEntry entry) { + String meterExt = IndexConstants.BINARY_SRTM_MAP_INDEX_EXT; + String feetExt = IndexConstants.BINARY_SRTM_FEET_MAP_INDEX_EXT; + + String fileName = entry.targetFile.getAbsolutePath(); + if (fileName.endsWith(meterExt)) { + fileName = fileName.replace(meterExt, feetExt); + } else if (fileName.endsWith(feetExt)) { + fileName = fileName.replace(feetExt, meterExt); + } + + File previous = new File(fileName); + if (previous != null && previous.exists()) { + boolean successful = Algorithms.removeAllFiles(previous); + if (successful) { + ctx.getResourceManager().closeFile(previous.getName()); + } + } + } + private void copyVoiceConfig(IndexItem.DownloadEntry de) { File f = ctx.getAppPath("/voice/" + de.baseName + "/_config.p"); if (f.exists()) try { @@ -386,23 +409,20 @@ public class DownloadFileHelper { } return r; } - + @Override public int available() throws IOException { int av = 0; - for(int i = currentRead; i < delegate.length; i++) { + for (int i = currentRead; i < delegate.length; i++) { av += delegate[i].available(); } return av; } - + public int getAndClearReadCount() { int last = count; count = 0; return last; } - - - } } diff --git a/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java b/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java index eaa3b4b92a..a9cfd1e1af 100644 --- a/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java +++ b/OsmAnd/src/net/osmand/plus/download/MultipleDownloadItem.java @@ -139,7 +139,7 @@ public class MultipleDownloadItem extends DownloadItem { if (obj instanceof IndexItem) { return (IndexItem) obj; } else if (obj instanceof SrtmDownloadItem) { - return ((SrtmDownloadItem) obj).getIndexItem(); + return ((SrtmDownloadItem) obj).getDefaultIndexItem(); } return null; } From 396f9354c18c614b12f7158937c2b70cab4aec96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Morais?= Date: Tue, 20 Apr 2021 19:31:21 +0000 Subject: [PATCH 82/86] Translated using Weblate (Portuguese) Currently translated at 100.0% (3717 of 3717 strings) --- OsmAnd/res/values-pt/strings.xml | 34 ++++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/OsmAnd/res/values-pt/strings.xml b/OsmAnd/res/values-pt/strings.xml index 9b9f73d7a6..f6e6b35339 100644 --- a/OsmAnd/res/values-pt/strings.xml +++ b/OsmAnd/res/values-pt/strings.xml @@ -371,7 +371,7 @@ Erro de entrada/saída na execução da ação {0}. As informações sobre o objeto não foram carregadas Aberto - Comentário + Comentar Alterar POI Todas as outras etiquetas são preservadas Enviar @@ -920,7 +920,7 @@ Curvas de nível Outros mapas Curvas de nível - Limites + Fronteiras regionais Ocultar a visualização de limites regionais (níveis de administração 5 – 9). Ver desmarcado @@ -1018,7 +1018,7 @@ Focagem automática Foco hiperfocal Profundidade de campo alargada (EDOF) - O foco está definido como infinito + Focar infinito Focagem macro (close-up) A câmara tenta focar continuadamente Reproduzir o som do obturador da câmara @@ -1435,7 +1435,7 @@ Colorir edifícios por tipo Escala de montanhismo (SAC) Camada superior de símbolos de montanhismo - Mostrar rotas para bicicletas + Rotas para bicicletas Edifícios Rotas de troleicarros Por favor, use um nome de categoria que ainda não exista. @@ -1513,7 +1513,7 @@ Edições OSM partilhadas via OsmAnd Ler mais Novidades - Objetos planeados + Elementos com construção planeada Enviar POI OSM adicionado Mapa base mundial (cobrindo o mundo inteiro em baixo nível de ampliação) ausente ou ultrapassado. Por favor, considere descarregá-lo para uma visão global. @@ -1754,7 +1754,7 @@ Marcadores Marcador de mapa É recomendável desativar a renderização de polígono. - Mostrar trilhos de bicicletas de montanha + Trilhos de bicicletas BTT Mostrar polígonos Encontrar estacionamento Situação @@ -1768,7 +1768,7 @@ Estrada bloqueada Selecionar Inverter ponto de partida e destino - Ícones POI + Ícones dos POI Item removido Itens removidos Desfazer tudo @@ -1776,7 +1776,7 @@ Ponto de partida Divisão das gravações Usar divisão das gravações - Duração do recorte + Duração da divisão Limite de tempo máximo para clipes gravados. Macedónio Servo-Croata @@ -2026,7 +2026,7 @@ Espessura das curvas de nível Espessura das curvas de nível Água - Ocultar água + Corpos de água largos Utilizar autoestradas Permitir autoestradas. Artigos da Wikipédia próximos @@ -2127,7 +2127,7 @@ Adquira o OsmAnd Live para desbloquear todas as funcionalidades: atualizações diárias de mapas com descarregamentos ilimitados, todas as extensões pagas e gratuitas, Wikipédia, Wikivoyage e muito mais. Alteração do estilo padrão para aumentar o contraste de caminhos pedestres e ciclovias. Usa cores clássicas do Mapnik. Favorito - Esconder descrição completa + Ocultar descrição completa Mostrar a descrição completa Obrigado pelos seus comentários Procurar rua @@ -2261,7 +2261,7 @@ Adicionar paragem inicial Mover destino para cima e criar destino Mostrar notas fechadas - Mostrar ou ocultar notas do OpenStreetMap no mapa. + Mostrar notas do OpenStreetMap. GPX - adequado para exportar para o JOSM ou outros editores do OSM. OSC - adequado para exportar para o OSM. Ficheiro GPX @@ -3293,7 +3293,7 @@ O nome do ficheiro está vazio Reverter Um botão para centrar o ecrã no ponto de partida. Em seguida, solicitará para definir o destino ou acionar o cálculo da rota. - Mostrar nós da rede de ciclovias + Nós da rede de ciclovias Limpar %1$s\? Diálogo de descarregar mapas Diálogos e notificações @@ -3442,7 +3442,7 @@ Autorização bem sucedida Som do obturador da câmara Usar aplicação do sistema - Divisão de gravação + Dividir gravações Repor configurações originais da extensão Deslocamento mínimo Precisão mínima @@ -3454,7 +3454,7 @@ Memória intermédia Recomendação: uma configuração de 5 metros pode funcionar bem se não precisar capturar detalhes mais refinados do que isso e não quer capturar dados explicitamente enquanto estiver parado. Efeitos colaterais: os períodos em que está parado não são registados em absoluto ou em apenas um ponto cada. Pequenos movimentos (no mundo real, por exemplo de lado, para marcar um possível desvio na sua viagem) podem ser filtrados. O seu ficheiro contém menos informações para pós-processamento e possui estatísticas piores ao filtrar pontos obviamente redundantes no tempo de gravação, mantendo potencialmente os artefactos causados por má receção ou efeitos do chipset GPS. - Este filtro evita que sejam gravados pontos duplicados onde ocorrer muito pouco movimento real, cria uma aparência espacial mais agradável dos trilhos que não são processados posteriormente. + Este filtro evita que sejam gravados pontos duplicados quando houver muito pouco movimento real e cria uma aparência espacial mais agradável dos trilhos que não são processados posteriormente. Observação: se o GPS estava desligado imediatamente antes de uma gravação, o primeiro ponto medido pode ter uma precisão diminuída; portanto, no nosso código, podemos esperar um segundo antes da gravação de um ponto (ou gravar o melhor de três pontos consecutivos, etc.), mas isso ainda não foi implementado. Recomendação: é difícil prever o que será gravado e o que não será, talvez seja melhor desativar este filtro. Efeito colateral: como resultado da filtragem por precisão, os pontos podem estar totalmente ausentes por ex. debaixo de pontes, sob árvores, entre prédios altos ou com certas condições climáticas. @@ -3501,7 +3501,7 @@ Não foi possível importar de \'%1$s\'. Personalize a quantidade de itens em \"Gaveta\", \"Configurar Mapa\" e \"Menu de Contexto\". \n -\nDesative as extensões não utilizados para ocultar todos os seus controlos. %1$s. +\nDesative as extensões não utilizadas para ocultar todos os seus controlos. %1$s. Itens da gaveta, menu de contexto Personalização da interface Gaveta @@ -3704,7 +3704,7 @@ Adicionar a um trilho O ponto adicionado não será visível no mapa, já que o grupo selecionado está escondido, pode encontrá-lo em \"%s\". Mostrar ícones de início e fim - Selecionar espessura da linha do trilho + Espessura da linha do trilho Selecione o intervalo em que as marcas com distância ou tempo no trilho serão mostradas. Selecione a opção de divisão desejada: por tempo ou por distância. Personalizado @@ -4025,7 +4025,7 @@ Não tem compras Se tiver alguma dúvida, contacte-nos em %1$s. Intervalos de tempo e distância - Distância por toque + Distância com 2 dedos Contacte o suporte Por favor siga este link se tiver algum problema com assinaturas. Atualizar todos os mapas para %1$s\? From 2968aad4389f68b9e9467c451d25e15d58bb2536 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Tue, 20 Apr 2021 09:25:47 +0000 Subject: [PATCH 83/86] Translated using Weblate (Danish) Currently translated at 90.9% (3379 of 3717 strings) --- OsmAnd/res/values-da/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OsmAnd/res/values-da/strings.xml b/OsmAnd/res/values-da/strings.xml index 7a6398afca..4272306e50 100644 --- a/OsmAnd/res/values-da/strings.xml +++ b/OsmAnd/res/values-da/strings.xml @@ -3837,4 +3837,8 @@ Sporet indeholder ikke højdedata. Sporet indeholder ikke hastighedsdata. Vælg en anden type farvelægning. + Udgangsnummer + Meddelelse ved overskridelse + Bruger points + Resultat \ No newline at end of file From f1f7db7842118caf57d0dbf8ae385622f6b86cea Mon Sep 17 00:00:00 2001 From: iman Date: Mon, 19 Apr 2021 21:27:47 +0000 Subject: [PATCH 84/86] Translated using Weblate (Persian) Currently translated at 36.6% (1438 of 3927 strings) --- OsmAnd/res/values-fa/phrases.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/OsmAnd/res/values-fa/phrases.xml b/OsmAnd/res/values-fa/phrases.xml index 7dc383d9c1..63d5c094fa 100644 --- a/OsmAnd/res/values-fa/phrases.xml +++ b/OsmAnd/res/values-fa/phrases.xml @@ -1460,4 +1460,5 @@ بانوان بانوان لباس کار + باغداری گلخانه‌ای \ No newline at end of file From fa17db8756676094b97d90c30288354eab5bc821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Morais?= Date: Mon, 19 Apr 2021 15:42:21 +0000 Subject: [PATCH 85/86] Translated using Weblate (Portuguese) Currently translated at 100.0% (3927 of 3927 strings) --- OsmAnd/res/values-pt/phrases.xml | 52 ++++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/OsmAnd/res/values-pt/phrases.xml b/OsmAnd/res/values-pt/phrases.xml index 294787f8f3..5ae2a5c536 100644 --- a/OsmAnd/res/values-pt/phrases.xml +++ b/OsmAnd/res/values-pt/phrases.xml @@ -411,10 +411,10 @@ Reservatório elevado Comporta de eclusa Ponto de viragem fluvial - Represa;Açude + Represa/açude;Represa;Açude Barragem Moinho de água - Quebra-mar;Molhe + Quebra-mar/molhe;Quebra-mar;Molhe Espigão marítimo Subestação Transformador @@ -565,14 +565,14 @@ Sim Sede de concelho Sede de freguesia - Aldeia;Lugar + Aldeia/lugar;Aldeia;Lugar Moradia isolada Subúrbio Zona de cidade Bairro Localidade Horta comunitária - Quinta;Fazenda + Quinta/fazenda;Quinta;Fazenda Farmácia Hospital Consultório médico @@ -694,7 +694,7 @@ Marco de fronteira Canhão histórico Castelo - Portão da cidade + Portão/arco de cidade Forte Chafariz Ruínas históricas @@ -721,7 +721,7 @@ Tobogã aquático Alojamento Hotel - Pensão;Albergaria;Hospedaria;Estalagem;Residencial + Pensão/albergaria/residenial;Estalagem;Residencial;Albergaria;Hospedaria;Casa de hóspedes Hostel Hotel estrada Abrigo de montanha @@ -890,7 +890,7 @@ Circo Galeria de arte Pista de dança - Discoteca;Danceteria + Discoteca/danceteria;Discoteca;Danceteria Clube de striptease Resort de esqui Resort de praia @@ -912,9 +912,9 @@ Restaurante Comida rápida Bar - Taberna + Taberna/pub/tasca;Taberna;Tasco;Tasca;Pub;Boteco;Buteco;Botequim Praça de alimentação - Bebedouro (água potável para beber) + Bebedouro (água potável) Churrasqueira Máquinas agrícolas Cesteiro @@ -977,8 +977,8 @@ Ponto de boleia solidária de carro Ponto de barcos partilhados Doca - Linha de corte florestal;Atalhada;Linha corta-fogo - Casa de banho;Banheiros + Linha de corte florestal/atalhada;Atalhada;Linha corta-fogo;Linha de corte florestal + Casa de banho;Banheiros;WC Chuveiros públicos Sauna Bordel @@ -1002,7 +1002,7 @@ Cumeeira Glaciar Sumidouro - Queda de água;Cascata;Salto;Catarata + Queda de água/cascata/catarata/salto;Cascata;Salto;Catarata;Queda de água Rio Ribeiro(a) Rápidos @@ -1246,8 +1246,8 @@ Não Sim Não - Vigiado - Não vigiado + Vigiado: sim + Vigiado: não Sim Não Estação seca @@ -1433,7 +1433,7 @@ Centro equestre Área de lazer comum Jardim - Charneca;Mato de vegetação rasteira + Charneca/mato de vegetação rasteira;Charneca;Mato de vegetação rasteira Relvado Pradaria Matagal @@ -1771,7 +1771,7 @@ Brinquedos Gelados Cartões de telemóvel (SIM) - Filial;Sucursal + Filial/sucursal;Filial;Sucursal Memorial de guerra Placa comemorativa Estátua @@ -2242,7 +2242,7 @@ Condição dos degraus: regular Condição dos degraus: irregular Condição dos degraus: acidentada - Moledro;Moledo;Melédro;Mariola + Moledro/mariola;Moledo;Melédro;Mariola;Moledro Desfibrilhador Desfibrilhador: sim Tipo: túmulo de guerra @@ -2360,7 +2360,7 @@ Tipo de fortificação: arandela Tipo de fortificação: vala circular Pa (assentamento fortificado maori) - Quinta histórica;Fazenda histórica + Quinta/fazenda histórica;Quinta histórica;Fazenda histórica Estação ferroviária histórica Eira histórica Forca histórica @@ -2476,7 +2476,7 @@ Comportamental Medicina paliativa Tipo de edifício: pirâmide - Ginásio;Academia desportiva + Ginásio/academia desportiva;Ginásio;Academia desportiva Exercício físico Bilhar Forno microondas: sim @@ -3082,7 +3082,7 @@ Suplementos alimentares Estúdio de fotografia Penhasco - Cativeiro de animais;Refúgio de animais + Refúgio de animais;Cativeiro de animais Cativeiro de animais: cavalos Cativeiro de animais: ovelhas Tipo: cercado @@ -3118,7 +3118,7 @@ Estandes Vendas Vendas: não - Vendas: sim; usados + Vendas: sim, usados Vendas: usados Aluguer Aluguer: não @@ -3203,7 +3203,7 @@ Instituição governamental de transportes Instituição legislativa governamental Canal VHF - Desfiladeiro;Canhão + Desfiladeiro/canhão;Desfiladeiro;Canhão Ravina Área montanhosa Argila @@ -3572,7 +3572,7 @@ Mesa muda-fraldas: sim Mesa muda-fraldas: não Mesa muda-fraldas: limitada - Mesa muda-fraldas; sala + Mesa muda-fraldas: sala Local da mesa muda-fraldas: WC masculino Local da mesa muda-fraldas: WC feminino Local da mesa muda-fraldas: WC unissexo @@ -3810,7 +3810,7 @@ Vibração Quarteirão Município - Caixa livre;Caixa de donativos;Give-box + Caixa livre/de donativos;Give-box;Caixa livre;Caixa de donativos Seta: não Elevador Horário @@ -3917,8 +3917,8 @@ Posição de paragem da biblioteca itinerante Estado da pista: fechada Estado da pista: aberta - Vigiado: não - Vigiado: sim + Supervisionado: não + Supervisionado: sim Nome da pista Salto com esqui Passagem de vida selvagem From 1e2399351453b6fceb250b004996262d214fc64a Mon Sep 17 00:00:00 2001 From: max-klaus Date: Wed, 21 Apr 2021 09:22:49 +0300 Subject: [PATCH 86/86] Added backup test description --- .../plus/development/TestBackupActivity.java | 66 ++++++++++++++++--- 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java b/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java index 516e1cc581..a5d0daddf5 100644 --- a/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java +++ b/OsmAnd/src/net/osmand/plus/development/TestBackupActivity.java @@ -3,12 +3,14 @@ package net.osmand.plus.development; import android.app.Activity; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.util.Pair; import android.util.TypedValue; import android.view.View; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.Toolbar; @@ -23,6 +25,7 @@ import net.osmand.plus.backup.BackupHelper.BackupInfo; import net.osmand.plus.backup.BackupHelper.OnRegisterUserListener; import net.osmand.plus.backup.BackupTask; import net.osmand.plus.backup.BackupTask.OnBackupListener; +import net.osmand.plus.backup.GpxFileInfo; import net.osmand.plus.backup.PrepareBackupTask; import net.osmand.plus.backup.PrepareBackupTask.OnPrepareBackupListener; import net.osmand.plus.backup.UserFile; @@ -32,11 +35,15 @@ import net.osmand.util.Algorithms; import java.io.File; import java.lang.ref.WeakReference; +import java.text.DateFormat; +import java.util.Date; import java.util.Map; import java.util.Map.Entry; public class TestBackupActivity extends OsmandActionBarActivity { + private static final DateFormat DF = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM); + private OsmandApplication app; private OsmandSettings settings; private BackupHelper backupHelper; @@ -196,7 +203,7 @@ public class TestBackupActivity extends OsmandActionBarActivity { } else if (uploadErrors == null && downloadErrors == null) { description = "No data"; } else { - description = getBackupDescription(uploadErrors, downloadErrors, deleteErrors, error); + description = getBackupErrorsDescription(uploadErrors, downloadErrors, deleteErrors, error); } a.infoView.setText(description); a.infoView.requestFocus(); @@ -224,7 +231,7 @@ public class TestBackupActivity extends OsmandActionBarActivity { } else if (uploadErrors == null && downloadErrors == null) { description = "No data"; } else { - description = getBackupDescription(uploadErrors, downloadErrors, deleteErrors, error); + description = getBackupErrorsDescription(uploadErrors, downloadErrors, deleteErrors, error); } a.infoView.setText(description); a.infoView.requestFocus(); @@ -240,7 +247,7 @@ public class TestBackupActivity extends OsmandActionBarActivity { prepareBackup(); } - private String getBackupDescription(@Nullable Map uploadErrors, @Nullable Map downloadErrors, @Nullable Map deleteErrors, @Nullable String error) { + private String getBackupErrorsDescription(@Nullable Map uploadErrors, @Nullable Map downloadErrors, @Nullable Map deleteErrors, @Nullable String error) { StringBuilder sb = new StringBuilder(); if (!Algorithms.isEmpty(uploadErrors)) { sb.append("--- Upload errors ---").append("\n"); @@ -263,6 +270,48 @@ public class TestBackupActivity extends OsmandActionBarActivity { return sb.length() == 0 ? "OK" : sb.toString(); } + private String getBackupDescription(@NonNull BackupInfo backupInfo) { + StringBuilder sb = new StringBuilder(); + if (!Algorithms.isEmpty(backupInfo.filesToUpload)) { + sb.append("\n").append("--- Upload ---").append("\n"); + for (GpxFileInfo info : backupInfo.filesToUpload) { + sb.append(info.getFileName(true)) + .append(" L: ").append(DF.format(new Date(info.getFileDate()))) + .append(" U: ").append(DF.format(new Date(info.uploadTime))) + .append("\n"); + } + } + if (!Algorithms.isEmpty(backupInfo.filesToDownload)) { + sb.append("\n").append("--- Download ---").append("\n"); + for (UserFile userFile : backupInfo.filesToDownload) { + sb.append(userFile.getName()) + .append(" R: ").append(DF.format(new Date(userFile.getClienttimems()))) + .append("\n"); + } + } + if (!Algorithms.isEmpty(backupInfo.filesToDelete)) { + sb.append("\n").append("--- Delete ---").append("\n"); + for (UserFile userFile : backupInfo.filesToDelete) { + sb.append(userFile.getName()) + .append(" R: ").append(DF.format(new Date(userFile.getClienttimems()))) + .append("\n"); + } + } + if (!Algorithms.isEmpty(backupInfo.filesToMerge)) { + sb.append("\n").append("--- Conflicts ---").append("\n"); + for (Pair localRemote : backupInfo.filesToMerge) { + GpxFileInfo local = localRemote.first; + UserFile remote = localRemote.second; + sb.append(local.getFileName(true)) + .append(" L: ").append(DF.format(new Date(local.getFileDate()))) + .append(" U: ").append(DF.format(new Date(local.uploadTime))) + .append(" R: ").append(DF.format(new Date(remote.getClienttimems()))) + .append("\n"); + } + } + return sb.toString(); + } + private void prepareBackup() { final WeakReference activityRef = new WeakReference<>(this); PrepareBackupTask prepareBackupTask = new PrepareBackupTask(this, new OnPrepareBackupListener() { @@ -271,16 +320,17 @@ public class TestBackupActivity extends OsmandActionBarActivity { TestBackupActivity.this.backupInfo = backupInfo; TestBackupActivity a = activityRef.get(); if (AndroidUtils.isActivityNotDestroyed(a)) { - String description; + String description = "Last uploaded: " + DF.format(new Date(settings.BACKUP_LAST_UPLOADED_TIME.get())) + "\n\n"; if (error != null) { - description = error; + description += error; } else if (backupInfo == null) { - description = "No data"; + description += "No data"; } else { - description = "Files to upload: " + backupInfo.filesToUpload.size() + description += "Files to upload: " + backupInfo.filesToUpload.size() + "\nFiles to download: " + backupInfo.filesToDownload.size() + "\nFiles to delete: " + backupInfo.filesToDelete.size() - + "\nConflicts: " + backupInfo.filesToMerge.size(); + + "\nConflicts: " + backupInfo.filesToMerge.size() + + "\n" + getBackupDescription(backupInfo); } a.infoView.setText(description); a.infoView.requestFocus();