diff --git a/OsmAnd/res/animator/appbar_always_elevated.xml b/OsmAnd/res/animator/appbar_always_elevated.xml new file mode 100644 index 0000000000..c8378c1c29 --- /dev/null +++ b/OsmAnd/res/animator/appbar_always_elevated.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/layout/bottom_sheet_item_with_descr_and_checkbox_56dp.xml b/OsmAnd/res/layout/bottom_sheet_item_with_descr_and_checkbox_56dp.xml index 34e329d8a3..65fc03610c 100644 --- a/OsmAnd/res/layout/bottom_sheet_item_with_descr_and_checkbox_56dp.xml +++ b/OsmAnd/res/layout/bottom_sheet_item_with_descr_and_checkbox_56dp.xml @@ -33,7 +33,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:ellipsize="end" - android:maxLines="1" + android:maxLines="2" android:textAppearance="@style/TextAppearance.ListItemTitle" tools:text="Some title"/> diff --git a/OsmAnd/res/layout/enough_space_warning_card.xml b/OsmAnd/res/layout/enough_space_warning_card.xml index a39076eb99..d572d20366 100644 --- a/OsmAnd/res/layout/enough_space_warning_card.xml +++ b/OsmAnd/res/layout/enough_space_warning_card.xml @@ -36,6 +36,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:letterSpacing="@dimen/text_button_letter_spacing" + android:paddingBottom="@dimen/measurement_tool_menu_title_padding_bottom" android:text="@string/export_not_enough_space" android:textColor="?android:textColorPrimary" android:textSize="@dimen/default_list_text_size" diff --git a/OsmAnd/res/layout/fragment_import.xml b/OsmAnd/res/layout/fragment_import.xml index 30f518cbfc..eb2cbc7f79 100644 --- a/OsmAnd/res/layout/fragment_import.xml +++ b/OsmAnd/res/layout/fragment_import.xml @@ -23,16 +23,22 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="bottom" - android:background="?attr/bg_color" - android:gravity="center" - android:minHeight="@dimen/bottom_sheet_title_height" android:orientation="vertical"> + + + android:background="@android:color/transparent" + android:stateListAnimator="@animator/appbar_always_elevated"> diff --git a/OsmAnd/res/values/sizes.xml b/OsmAnd/res/values/sizes.xml index c1addc1349..f91581adf6 100644 --- a/OsmAnd/res/values/sizes.xml +++ b/OsmAnd/res/values/sizes.xml @@ -397,6 +397,7 @@ 42dp 128dp 164dp + 68dp 6dp 12dp diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index 72fb10e393..9da2ac6d86 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -11,6 +11,7 @@ Thx - Hardy --> + %1$s * %2$s OsmAnd shows photos from several sources:\nOpenPlaceReviews - POI photos;\nMapillary - street-level imagery;\nWeb / Wikimedia - POI photos specified in OpenStreetMap data. Use dev.openstreetmap.org Switch to use "dev.openstreetmap.org" instead of "openstreetmap.org" to testing uploading OSM Note / POI / GPX. diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java b/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java index bf86c42ee6..13135c0821 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/ExportSettingsType.java @@ -10,7 +10,6 @@ public enum ExportSettingsType { GLOBAL(R.string.general_settings_2, R.drawable.ic_action_settings), QUICK_ACTIONS(R.string.configure_screen_quick_action, R.drawable.ic_quick_action), POI_TYPES(R.string.poi_dialog_poi_type, R.drawable.ic_action_info_dark), - SEARCH_HISTORY(R.string.shared_string_search_history, R.drawable.ic_action_history), AVOID_ROADS(R.string.avoid_road, R.drawable.ic_action_alert), FAVORITES(R.string.shared_string_favorites, R.drawable.ic_action_favorite), TRACKS(R.string.shared_string_tracks, R.drawable.ic_action_route_distance), @@ -19,6 +18,7 @@ public enum ExportSettingsType { MULTIMEDIA_NOTES(R.string.audionotes_plugin_name, R.drawable.ic_grouped_by_type), ACTIVE_MARKERS(R.string.map_markers, R.drawable.ic_action_flag), HISTORY_MARKERS(R.string.markers_history, R.drawable.ic_action_flag), + SEARCH_HISTORY(R.string.shared_string_search_history, R.drawable.ic_action_history), CUSTOM_RENDER_STYLE(R.string.shared_string_rendering_style, R.drawable.ic_action_map_style), CUSTOM_ROUTING(R.string.shared_string_routing, R.drawable.ic_action_route_distance), MAP_SOURCES(R.string.quick_action_map_source_title, R.drawable.ic_map), @@ -48,12 +48,13 @@ public enum ExportSettingsType { public boolean isSettingsCategory() { return this == PROFILE || this == GLOBAL || this == QUICK_ACTIONS || this == POI_TYPES - || this == SEARCH_HISTORY || this == AVOID_ROADS; + || this == AVOID_ROADS; } public boolean isMyPlacesCategory() { return this == FAVORITES || this == TRACKS || this == OSM_EDITS || this == OSM_NOTES - || this == MULTIMEDIA_NOTES || this == ACTIVE_MARKERS || this == HISTORY_MARKERS; + || this == MULTIMEDIA_NOTES || this == ACTIVE_MARKERS || this == HISTORY_MARKERS + || this == SEARCH_HISTORY; } public boolean isResourcesCategory() { 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 563a04fc75..493e49fa07 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/FileSettingsItem.java @@ -19,6 +19,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class FileSettingsItem extends StreamSettingsItem { @@ -131,6 +132,7 @@ public class FileSettingsItem extends StreamSettingsItem { private final File appPath; protected FileSubtype subtype; private long size; + private long lastModified; public FileSettingsItem(@NonNull OsmandApplication app, @NonNull File file) throws IllegalArgumentException { super(app, file.getPath().replace(app.getAppPath(null).getPath(), "")); @@ -222,6 +224,14 @@ public class FileSettingsItem extends StreamSettingsItem { this.size = size; } + public long getLastModified() { + return lastModified; + } + + public void setLastModified(long lastModified) { + this.lastModified = lastModified; + } + @NonNull public File getFile() { return file; @@ -292,6 +302,9 @@ public class FileSettingsItem extends StreamSettingsItem { } finally { Algorithms.closeStream(output); } + if (lastModified != -1) { + dest.setLastModified(lastModified); + } } }; } @@ -306,7 +319,14 @@ public class FileSettingsItem extends StreamSettingsItem { warnings.add(app.getString(R.string.settings_item_read_error, file.getName())); SettingsHelper.LOG.error("Failed to set input stream from file: " + file.getName(), e); } - return super.getWriter(); + return new StreamSettingsItemWriter(this) { + @Override + public ZipEntry createNewEntry(String fileName) { + ZipEntry entry = super.createNewEntry(fileName); + entry.setTime(file.lastModified()); + return entry; + } + }; } else { return new StreamSettingsItemWriter(this) { diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsHelper.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsHelper.java index 4553cf805f..babb91a140 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsHelper.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsHelper.java @@ -539,10 +539,6 @@ public class SettingsHelper { if (!poiList.isEmpty()) { settingsItems.put(ExportSettingsType.POI_TYPES, poiList); } - List historyEntries = SearchHistoryHelper.getInstance(app).getHistoryEntries(false); - if (!historyEntries.isEmpty()) { - settingsItems.put(ExportSettingsType.SEARCH_HISTORY, historyEntries); - } Map impassableRoads = app.getAvoidSpecificRoads().getImpassableRoads(); if (!impassableRoads.isEmpty()) { settingsItems.put(ExportSettingsType.AVOID_ROADS, new ArrayList<>(impassableRoads.values())); @@ -611,6 +607,10 @@ public class SettingsHelper { markersGroup.setMarkers(markersHistory); myPlacesItems.put(ExportSettingsType.HISTORY_MARKERS, Collections.singletonList(markersGroup)); } + List historyEntries = SearchHistoryHelper.getInstance(app).getHistoryEntries(false); + if (!historyEntries.isEmpty()) { + myPlacesItems.put(ExportSettingsType.SEARCH_HISTORY, historyEntries); + } return myPlacesItems; } @@ -829,10 +829,10 @@ public class SettingsHelper { List routingFilesList = new ArrayList<>(); List renderFilesList = new ArrayList<>(); List multimediaFilesList = new ArrayList<>(); - List tracksFilesList = new ArrayList<>(); List ttsVoiceFilesList = new ArrayList<>(); List voiceFilesList = new ArrayList<>(); List mapFilesList = new ArrayList<>(); + List tracksFilesList = new ArrayList<>(); List avoidRoads = new ArrayList<>(); List globalSettingsItems = new ArrayList<>(); List notesPointList = new ArrayList<>(); @@ -856,7 +856,7 @@ public class SettingsHelper { } else if (fileItem.getSubtype() == FileSubtype.MULTIMEDIA_NOTES) { multimediaFilesList.add(fileItem.getFile()); } else if (fileItem.getSubtype() == FileSubtype.GPX) { - tracksFilesList.add(fileItem.getFile()); + tracksFilesList.add(fileItem); } else if (fileItem.getSubtype().isMap()) { mapFilesList.add(fileItem); } else if (fileItem.getSubtype() == FileSubtype.TTS_VOICE) { diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsImporter.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsImporter.java index 7f72761066..66c40d03cc 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsImporter.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsImporter.java @@ -60,7 +60,7 @@ class SettingsImporter { try { SettingsItemsFactory itemsFactory = new SettingsItemsFactory(app, itemsJson); List settingsItemList = itemsFactory.getItems(); - getFilesSize(file, settingsItemList); + updateFilesInfo(file, settingsItemList); items.addAll(settingsItemList); } catch (IllegalArgumentException e) { SettingsHelper.LOG.error("Error parsing items: " + itemsJson, e); @@ -81,7 +81,7 @@ class SettingsImporter { return items; } - private void getFilesSize(@NonNull File file, List settingsItemList) throws IOException { + private void updateFilesInfo(@NonNull File file, List settingsItemList) throws IOException { ZipFile zipfile = new ZipFile(file.getPath()); Enumeration zipEnum = zipfile.entries(); while (zipEnum.hasMoreElements()) { @@ -90,7 +90,9 @@ class SettingsImporter { for (SettingsItem settingsItem : settingsItemList) { if (settingsItem instanceof FileSettingsItem && zipEntry.getName().equals(settingsItem.getFileName())) { - ((FileSettingsItem) settingsItem).setSize(size); + FileSettingsItem fileSettingsItem = (FileSettingsItem) settingsItem; + fileSettingsItem.setSize(size); + fileSettingsItem.setLastModified(zipEntry.getTime()); break; } } diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemWriter.java b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemWriter.java index 090767a493..65863f0e90 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemWriter.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/backup/SettingsItemWriter.java @@ -22,9 +22,13 @@ public abstract class SettingsItemWriter { public abstract boolean writeToStream(@NonNull OutputStream outputStream) throws IOException; public void writeEntry(String fileName, @NonNull ZipOutputStream zos) throws IOException { - ZipEntry entry = new ZipEntry(fileName); + ZipEntry entry = createNewEntry(fileName); zos.putNextEntry(entry); writeToStream(zos); zos.closeEntry(); } + + public ZipEntry createNewEntry(String fileName) { + return new ZipEntry(fileName); + } } diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java index d95b6cffea..86b7389f6e 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/BaseSettingsListFragment.java @@ -2,15 +2,14 @@ package net.osmand.plus.settings.fragments; import android.content.Context; import android.content.DialogInterface; -import android.os.Build; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.widget.ExpandableListView; import android.widget.LinearLayout; +import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -31,6 +30,7 @@ import net.osmand.plus.settings.backend.ExportSettingsCategory; import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.plus.settings.fragments.ExportSettingsAdapter.OnItemSelectedListener; import net.osmand.plus.widgets.TextViewEx; +import net.osmand.util.Algorithms; import java.io.File; import java.util.ArrayList; @@ -60,6 +60,7 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem protected ExpandableListView expandableList; protected ExportSettingsAdapter adapter; + protected boolean exportMode; protected boolean nightMode; private boolean wasDrawerDisabled; @@ -73,6 +74,17 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem super.onCreate(savedInstanceState); app = requireMyApplication(); nightMode = !app.getSettings().isLightContent(); + + requireActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { + @Override + public void handleOnBackPressed() { + if (hasSelectedData()) { + showExitDialog(); + } else { + dismissFragment(); + } + } + }); } @Nullable @@ -101,26 +113,15 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem continueBtn = root.findViewById(R.id.continue_button); UiUtilities.setupDialogButton(nightMode, continueBtn, DialogButtonType.PRIMARY, getString(R.string.shared_string_continue)); - continueBtn.setOnClickListener(new View.OnClickListener() { + root.findViewById(R.id.continue_button_container).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - onContinueButtonClickAction(); - } - }); - - ViewTreeObserver treeObserver = buttonsContainer.getViewTreeObserver(); - treeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - if (buttonsContainer != null) { - ViewTreeObserver vts = buttonsContainer.getViewTreeObserver(); - int height = buttonsContainer.getMeasuredHeight(); - expandableList.setPadding(0, 0, 0, height); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - vts.removeOnGlobalLayoutListener(this); - } else { - vts.removeGlobalOnLayoutListener(this); + if (expandableList.getHeaderViewsCount() <= 1) { + if (hasSelectedData()) { + onContinueButtonClickAction(); } + } else { + expandableList.smoothScrollToPosition(0); } } }); @@ -185,7 +186,11 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - showExitDialog(); + if (hasSelectedData()) { + showExitDialog(); + } else { + dismissFragment(); + } } }); } @@ -204,16 +209,25 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem continueBtn.setEnabled(false); } else { updateWarningHeaderVisibility(false); - continueBtn.setEnabled(adapter.hasSelectedData()); + continueBtn.setEnabled(hasSelectedData()); } itemsSizeContainer.setVisibility(View.VISIBLE); } else { updateWarningHeaderVisibility(false); itemsSizeContainer.setVisibility(View.INVISIBLE); - continueBtn.setEnabled(adapter.hasSelectedData()); + continueBtn.setEnabled(hasSelectedData()); } } + public boolean hasSelectedData() { + for (List items : selectedItemsMap.values()) { + if (!Algorithms.isEmpty(items)) { + return true; + } + } + return false; + } + private void updateWarningHeaderVisibility(boolean visible) { if (visible) { if (expandableList.getHeaderViewsCount() < 2) { @@ -255,13 +269,24 @@ public abstract class BaseSettingsListFragment extends BaseOsmAndFragment implem updateAvailableSpace(); } + protected List getItemsForType(ExportSettingsType type) { + for (SettingsCategoryItems categoryItems : dataList.values()) { + if (categoryItems.getTypes().contains(type)) { + return (List) categoryItems.getItemsForType(type); + } + } + return null; + } + + protected List getSelectedItemsForType(ExportSettingsType type) { + return (List) selectedItemsMap.get(type); + } + @Override - public void onTypeClicked(ExportSettingsCategory category, ExportSettingsType type) { + public void onTypeClicked(ExportSettingsType type) { FragmentManager fragmentManager = getFragmentManager(); if (fragmentManager != null && type != ExportSettingsType.GLOBAL && type != ExportSettingsType.SEARCH_HISTORY) { - List items = (List) dataList.get(category).getItemsForType(type); - List selectedItems = (List) selectedItemsMap.get(type); - ExportItemsBottomSheet.showInstance(type, selectedItems, items, fragmentManager, this); + ExportItemsBottomSheet.showInstance(fragmentManager, type, this, exportMode); } } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java index 8c62e04ccf..0a310e5c16 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportItemsBottomSheet.java @@ -14,11 +14,15 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import net.osmand.AndroidUtils; +import net.osmand.GPXUtilities.GPXTrackAnalysis; import net.osmand.IndexConstants; import net.osmand.PlatformUtil; import net.osmand.map.ITileSource; import net.osmand.map.TileSourceManager.TileSourceTemplate; import net.osmand.plus.FavouritesDbHelper.FavoriteGroup; +import net.osmand.plus.GPXDatabase.GpxDataItem; +import net.osmand.plus.GpxDbHelper.GpxDataItemCallback; +import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; import net.osmand.plus.SQLiteTileSource; @@ -28,6 +32,7 @@ import net.osmand.plus.base.MenuBottomSheetDialogFragment; 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.helpers.AvoidSpecificRoads.AvoidRoadInfo; @@ -47,6 +52,7 @@ import net.osmand.plus.settings.backend.ApplicationMode; import net.osmand.plus.settings.backend.ApplicationMode.ApplicationModeBean; import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.plus.settings.backend.backup.FileSettingsItem; +import net.osmand.plus.settings.backend.backup.FileSettingsItem.FileSubtype; import net.osmand.plus.settings.backend.backup.GlobalSettingsItem; import net.osmand.plus.settings.fragments.ExportSettingsAdapter.OnItemSelectedListener; import net.osmand.util.Algorithms; @@ -67,18 +73,43 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { public static final String TAG = ExportItemsBottomSheet.class.getSimpleName(); private static final Log LOG = PlatformUtil.getLog(ExportItemsBottomSheet.class); + private static final String SETTINGS_TYPE_KEY = "settings_type_key"; + private static final String EXPORT_MODE_KEY = "export_mode_key"; + private OsmandApplication app; private UiUtilities uiUtilities; private ExportSettingsType type; - private List allItems; - private List selectedItems = new ArrayList<>(); + private final List allItems = new ArrayList<>(); + private final List selectedItems = new ArrayList<>(); private TextView selectedSize; private ThreeStateCheckbox checkBox; private int activeColorRes; private int secondaryColorRes; + private boolean exportMode; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (savedInstanceState != null) { + exportMode = savedInstanceState.getBoolean(EXPORT_MODE_KEY); + type = ExportSettingsType.valueOf(savedInstanceState.getString(SETTINGS_TYPE_KEY)); + } + Fragment target = getTargetFragment(); + if (target instanceof BaseSettingsListFragment) { + BaseSettingsListFragment fragment = (BaseSettingsListFragment) target; + List items = fragment.getItemsForType(type); + if (items != null) { + allItems.addAll(items); + } + List selectedItemsForType = fragment.getSelectedItemsForType(type); + if (selectedItemsForType != null) { + selectedItems.addAll(selectedItemsForType); + } + } + } @Override public void createMenuItems(Bundle savedInstanceState) { @@ -111,11 +142,18 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { }) .setTag(object); setupBottomSheetItem(builder, object); - item[0] = (BottomSheetItemWithCompoundButton) builder.create(); + item[0] = builder.create(); items.add(item[0]); } } + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putBoolean(EXPORT_MODE_KEY, exportMode); + outState.putString(SETTINGS_TYPE_KEY, type.name()); + } + private BaseBottomSheetItem createTitleItem() { LayoutInflater themedInflater = UiUtilities.getInflater(requireContext(), nightMode); View view = themedInflater.inflate(R.layout.settings_group_title, null); @@ -153,7 +191,30 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { int checkBoxColor = checkBox.getState() == UNCHECKED ? secondaryColorRes : activeColorRes; CompoundButtonCompat.setButtonTintList(checkBox, ColorStateList.valueOf(ContextCompat.getColor(app, checkBoxColor))); - selectedSize.setText(getString(R.string.ltr_or_rtl_combine_via_slash, selectedItems.size(), allItems.size())); + String description; + if (type == ExportSettingsType.OFFLINE_MAPS && !selectedItems.isEmpty()) { + String size = AndroidUtils.formatSize(app, calculateSelectedItemsSize()); + String selected = getString(R.string.ltr_or_rtl_combine_via_slash, selectedItems.size(), allItems.size()); + description = getString(R.string.ltr_or_rtl_combine_via_comma, selected, size); + } else { + description = getString(R.string.ltr_or_rtl_combine_via_slash, selectedItems.size(), allItems.size()); + } + selectedSize.setText(description); + } + + private long calculateSelectedItemsSize() { + long itemsSize = 0; + for (int i = 0; i < allItems.size(); i++) { + Object object = allItems.get(i); + if (selectedItems.contains(object)) { + if (object instanceof FileSettingsItem) { + itemsSize += ((FileSettingsItem) object).getSize(); + } else if (object instanceof File) { + itemsSize += ((File) object).length(); + } + } + } + return itemsSize; } private void updateItems(boolean checked) { @@ -184,15 +245,13 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { dismiss(); } - public static void showInstance(@NonNull ExportSettingsType type, List selectedItems, List allItems, @NonNull FragmentManager fm, @Nullable Fragment target) { + public static void showInstance(@NonNull FragmentManager fm, @NonNull ExportSettingsType type, + @NonNull BaseSettingsListFragment target, boolean exportMode) { try { if (!fm.isStateSaved() && fm.findFragmentByTag(TAG) == null) { ExportItemsBottomSheet fragment = new ExportItemsBottomSheet(); fragment.type = type; - fragment.allItems = (List) allItems; - if (selectedItems != null) { - fragment.selectedItems.addAll(selectedItems); - } + fragment.exportMode = exportMode; fragment.setTargetFragment(target, 0); fragment.show(fm, TAG); } @@ -211,7 +270,7 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { return null; } - private void setupBottomSheetItem(BottomSheetItemWithCompoundButton.Builder builder, Object object) { + private void setupBottomSheetItem(Builder builder, Object object) { if (object instanceof ApplicationModeBean) { ApplicationModeBean modeBean = (ApplicationModeBean) object; String profileName = modeBean.userProfileName; @@ -254,10 +313,11 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { builder.setTitle(tileSource.getName()); builder.setIcon(uiUtilities.getIcon(R.drawable.ic_map, activeColorRes)); } else if (object instanceof File) { - setupBottomSheetItemForFile(builder, (File) object); + File file = (File) object; + setupBottomSheetItemForFile(builder, file, file.lastModified(), file.length()); } else if (object instanceof FileSettingsItem) { - FileSettingsItem fileSettingsItem = (FileSettingsItem) object; - setupBottomSheetItemForFile(builder, fileSettingsItem.getFile()); + FileSettingsItem item = (FileSettingsItem) object; + setupBottomSheetItemForFile(builder, item.getFile(), item.getLastModified(), item.getSize()); } else if (object instanceof AvoidRoadInfo) { AvoidRoadInfo avoidRoadInfo = (AvoidRoadInfo) object; builder.setTitle(avoidRoadInfo.name); @@ -275,6 +335,9 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { builder.setTitle(group.getDisplayName(app)); int color = group.getColor() == 0 ? ContextCompat.getColor(app, R.color.color_favorite) : group.getColor(); builder.setIcon(uiUtilities.getPaintedIcon(R.drawable.ic_action_folder, color)); + int points = group.getPoints().size(); + String itemsDescr = getString(R.string.shared_string_gpx_points); + builder.setDescription(getString(R.string.ltr_or_rtl_combine_via_colon, itemsDescr, points)); } else if (object instanceof GlobalSettingsItem) { GlobalSettingsItem globalSettingsItem = (GlobalSettingsItem) object; builder.setTitle(globalSettingsItem.getPublicName(app)); @@ -285,9 +348,12 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { builder.setTitle(getString(R.string.map_markers)); builder.setIcon(uiUtilities.getIcon(R.drawable.ic_action_flag, activeColorRes)); } else if (ExportSettingsType.HISTORY_MARKERS.name().equals(markersGroup.getId())) { - builder.setTitle(getString(R.string.map_markers)); + builder.setTitle(getString(R.string.markers_history)); builder.setIcon(uiUtilities.getIcon(R.drawable.ic_action_history, activeColorRes)); } + int selectedMarkers = markersGroup.getMarkers().size(); + String itemsDescr = getString(R.string.shared_string_items); + builder.setDescription(getString(R.string.ltr_or_rtl_combine_via_colon, itemsDescr, selectedMarkers)); } else if (object instanceof HistoryEntry) { HistoryEntry historyEntry = (HistoryEntry) object; builder.setTitle(historyEntry.getName().getName()); @@ -295,8 +361,8 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { } } - private void setupBottomSheetItemForFile(Builder builder, File file) { - FileSettingsItem.FileSubtype fileSubtype = FileSettingsItem.FileSubtype.getSubtypeByPath(app, file.getPath()); + private void setupBottomSheetItemForFile(Builder builder, File file, long lastModified, long size) { + FileSubtype fileSubtype = FileSubtype.getSubtypeByPath(app, file.getPath()); builder.setTitle(file.getName()); if (file.getAbsolutePath().contains(IndexConstants.RENDERERS_DIR)) { builder.setIcon(uiUtilities.getIcon(R.drawable.ic_action_map_style, activeColorRes)); @@ -304,6 +370,8 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { builder.setIcon(uiUtilities.getIcon(R.drawable.ic_action_route_distance, activeColorRes)); } else if (file.getAbsolutePath().contains(IndexConstants.GPX_INDEX_DIR)) { builder.setTitle(GpxUiHelper.getGpxTitle(file.getName())); + builder.setTag(file); + builder.setDescription(getTrackDescr(file, lastModified, size)); builder.setIcon(uiUtilities.getIcon(R.drawable.ic_action_route_distance, activeColorRes)); } else if (file.getAbsolutePath().contains(IndexConstants.AV_INDEX_DIR)) { int iconId = AudioVideoNotesPlugin.getIconIdForRecordingFile(file); @@ -316,6 +384,85 @@ public class ExportItemsBottomSheet extends MenuBottomSheetDialogFragment { || fileSubtype == FileSettingsItem.FileSubtype.VOICE) { builder.setTitle(FileNameTranslationHelper.getFileNameWithRegion(app, file.getName())); builder.setIcon(uiUtilities.getIcon(fileSubtype.getIconId(), activeColorRes)); + + if (fileSubtype.isMap()) { + String mapDescription = getMapDescription(file); + String formattedSize = AndroidUtils.formatSize(app, size); + if (mapDescription != null) { + builder.setDescription(getString(R.string.ltr_or_rtl_combine_via_star, mapDescription, formattedSize)); + } else { + builder.setDescription(formattedSize); + } + } } } + + private final GpxDataItemCallback gpxDataItemCallback = new GpxDataItemCallback() { + @Override + public boolean isCancelled() { + return false; + } + + @Override + public void onGpxDataItemReady(GpxDataItem item) { + for (BaseBottomSheetItem bottomSheetItem : items) { + if (Algorithms.objectEquals(item.getFile(), bottomSheetItem.getTag())) { + ((BottomSheetItemWithDescription) bottomSheetItem).setDescription(getTrackDescrForDataItem(item)); + break; + } + } + } + }; + + private String getTrackDescr(@NonNull File file, long lastModified, long size) { + String folder = ""; + File parent = file.getParentFile(); + if (parent != null) { + folder = Algorithms.capitalizeFirstLetter(parent.getName()); + } + if (exportMode) { + GpxDataItem dataItem = getDataItem(file, gpxDataItemCallback); + if (dataItem != null) { + return getTrackDescrForDataItem(dataItem); + } + } else { + String date = OsmAndFormatter.getFormattedDate(app, lastModified); + String formattedSize = AndroidUtils.formatSize(app, size); + String descr = getString(R.string.ltr_or_rtl_combine_via_bold_point, folder, date); + return getString(R.string.ltr_or_rtl_combine_via_comma, descr, formattedSize); + } + return null; + } + + private String getTrackDescrForDataItem(@NonNull GpxDataItem dataItem) { + GPXTrackAnalysis analysis = dataItem.getAnalysis(); + if (analysis != null) { + File parent = dataItem.getFile().getParentFile(); + String folder = Algorithms.capitalizeFirstLetter(parent.getName()); + String dist = OsmAndFormatter.getFormattedDistance(analysis.totalDistance, app); + String points = analysis.wptPoints + " " + getString(R.string.shared_string_gpx_points).toLowerCase(); + String descr = getString(R.string.ltr_or_rtl_combine_via_bold_point, folder, dist); + return getString(R.string.ltr_or_rtl_combine_via_comma, descr, points); + } + return null; + } + + private GpxDataItem getDataItem(File file, @Nullable GpxDataItemCallback callback) { + return app.getGpxDbHelper().getItem(file, callback); + } + + private String getMapDescription(File file) { + if (file.isDirectory() || file.getName().endsWith(IndexConstants.BINARY_WIKIVOYAGE_MAP_INDEX_EXT)) { + return getString(R.string.online_map); + } else if (file.getName().endsWith(IndexConstants.BINARY_ROAD_MAP_INDEX_EXT)) { + 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)) { + return getString(R.string.download_srtm_maps); + } else if (file.getName().endsWith(IndexConstants.BINARY_MAP_INDEX_EXT)) { + return getString(R.string.download_regular_maps); + } + return null; + } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsAdapter.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsAdapter.java index d479b78a7c..1eec036171 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsAdapter.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsAdapter.java @@ -18,6 +18,7 @@ import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.OsmandBaseExpandableListAdapter; import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.FontCache; +import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.settings.backend.ExportSettingsCategory; import net.osmand.plus.settings.backend.ExportSettingsType; import net.osmand.plus.settings.backend.backup.FileSettingsItem; @@ -54,6 +55,9 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { private final boolean nightMode; private final int activeColorRes; private final int secondaryColorRes; + private final int groupViewHeight; + private final int childViewHeight; + private final int listBottomPadding; ExportSettingsAdapter(OsmandApplication app, OnItemSelectedListener listener, boolean nightMode) { this.app = app; @@ -63,6 +67,9 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { themedInflater = UiUtilities.getInflater(app, 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; + groupViewHeight = app.getResources().getDimensionPixelSize(R.dimen.setting_list_item_group_height); + childViewHeight = app.getResources().getDimensionPixelSize(R.dimen.setting_list_item_large_height); + listBottomPadding = app.getResources().getDimensionPixelSize(R.dimen.fab_recycler_view_padding_bottom); } @Override @@ -70,6 +77,7 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { View group = convertView; if (group == null) { group = themedInflater.inflate(R.layout.profile_data_list_item_group, parent, false); + group.findViewById(R.id.item_container).setMinimumHeight(groupViewHeight); } final ExportSettingsCategory category = itemsTypes.get(groupPosition); final SettingsCategoryItems items = itemsMap.get(category); @@ -108,6 +116,9 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { } }); + boolean addPadding = !isExpanded && groupPosition == getGroupCount() - 1; + group.setPadding(0, 0, 0, addPadding ? listBottomPadding : 0); + adjustIndicator(app, groupPosition, isExpanded, group, nightMode); AndroidUiHelper.updateVisibility(group.findViewById(R.id.divider), isExpanded); AndroidUiHelper.updateVisibility(group.findViewById(R.id.card_top_divider), true); @@ -122,6 +133,7 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { View child = convertView; if (child == null) { child = themedInflater.inflate(R.layout.profile_data_list_item_group, parent, false); + child.findViewById(R.id.item_container).setMinimumHeight(childViewHeight); } final ExportSettingsCategory category = itemsTypes.get(groupPosition); final SettingsCategoryItems categoryItems = itemsMap.get(category); @@ -157,7 +169,7 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { @Override public void onClick(View v) { if (listener != null) { - listener.onTypeClicked(category, type); + listener.onTypeClicked(type); } } }); @@ -174,7 +186,8 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { notifyDataSetChanged(); } }); - + boolean addPadding = isLastChild && groupPosition == getGroupCount() - 1; + child.setPadding(0, 0, 0, addPadding ? listBottomPadding : 0); AndroidUiHelper.updateVisibility(child.findViewById(R.id.card_bottom_divider), isLastChild); return child; @@ -247,10 +260,6 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { notifyDataSetChanged(); } - public boolean hasSelectedData() { - return !selectedItemsMap.isEmpty(); - } - public List getData() { List selectedItems = new ArrayList<>(); for (List items : selectedItemsMap.values()) { @@ -307,6 +316,10 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { itemsSize += ((FileSettingsItem) object).getSize(); } else if (object instanceof File) { itemsSize += ((File) object).length(); + } else if (object instanceof MapMarkersGroup) { + int selectedMarkers = ((MapMarkersGroup) object).getMarkers().size(); + String itemsDescr = app.getString(R.string.shared_string_items); + return app.getString(R.string.ltr_or_rtl_combine_via_colon, itemsDescr, selectedMarkers); } } } @@ -332,7 +345,7 @@ public class ExportSettingsAdapter extends OsmandBaseExpandableListAdapter { void onCategorySelected(ExportSettingsCategory type, boolean selected); - void onTypeClicked(ExportSettingsCategory category, ExportSettingsType type); + void onTypeClicked(ExportSettingsType type); } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsFragment.java index f831fd8fdd..af954db7c5 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ExportSettingsFragment.java @@ -11,7 +11,6 @@ import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; -import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; @@ -76,14 +75,8 @@ public class ExportSettingsFragment extends BaseSettingsListFragment { progressMax = savedInstanceState.getInt(PROGRESS_MAX_KEY); progressValue = savedInstanceState.getInt(PROGRESS_VALUE_KEY); } + exportMode = true; dataList = app.getSettingsHelper().getAdditionalData(globalExport); - - requireActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { - @Override - public void handleOnBackPressed() { - showExitDialog(); - } - }); } @Nullable diff --git a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java index bb629308b9..18e4f3cec5 100644 --- a/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java +++ b/OsmAnd/src/net/osmand/plus/settings/fragments/ImportSettingsFragment.java @@ -12,7 +12,6 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; -import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentManager; @@ -101,13 +100,8 @@ public class ImportSettingsFragment extends BaseSettingsListFragment { if (savedInstanceState != null) { duplicateStartTime = savedInstanceState.getLong(DUPLICATES_START_TIME_KEY); } + exportMode = false; settingsHelper = app.getSettingsHelper(); - requireActivity().getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) { - @Override - public void handleOnBackPressed() { - showExitDialog(); - } - }); ImportAsyncTask importTask = settingsHelper.getImportTask(); if (importTask != null) {