From 81c6a8f4374cd7d77b925c1f01976bef94049bf6 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Sun, 24 Jan 2021 02:01:32 +0200 Subject: [PATCH 1/3] Save track name inside gpx --- OsmAnd/src/net/osmand/FileUtils.java | 35 ++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/OsmAnd/src/net/osmand/FileUtils.java b/OsmAnd/src/net/osmand/FileUtils.java index 580450df60..13a5b18f4b 100644 --- a/OsmAnd/src/net/osmand/FileUtils.java +++ b/OsmAnd/src/net/osmand/FileUtils.java @@ -1,5 +1,6 @@ package net.osmand; +import android.os.AsyncTask; import android.widget.Toast; import androidx.annotation.NonNull; @@ -8,6 +9,8 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; +import net.osmand.GPXUtilities.GPXFile; +import net.osmand.GPXUtilities.Metadata; import net.osmand.plus.GpxSelectionHelper; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.OsmandApplication; @@ -109,6 +112,8 @@ public class FileUtils { selected.getGpxFile().path = dest.getAbsolutePath(); helper.updateSelectedGpxFile(selected); } + RenameGpxAsyncTask renameGpxAsyncTask = new RenameGpxAsyncTask(app, dest); + renameGpxAsyncTask.execute(); return dest; } return null; @@ -196,4 +201,34 @@ public class FileUtils { public interface RenameCallback { void renamedTo(File file); } + + private static class RenameGpxAsyncTask extends AsyncTask { + + private OsmandApplication app; + private File file; + + private RenameGpxAsyncTask(@NonNull OsmandApplication app, @NonNull File file) { + this.app = app; + this.file = file; + } + + @Override + protected Exception doInBackground(Void... voids) { + GpxSelectionHelper helper = app.getSelectedGpxHelper(); + SelectedGpxFile selected = helper.getSelectedFileByPath(file.getAbsolutePath()); + + GPXFile gpxFile; + if (selected != null && selected.getGpxFile() != null) { + gpxFile = selected.getGpxFile(); + } else { + gpxFile = GPXUtilities.loadGPXFile(file); + } + if (gpxFile.metadata == null) { + gpxFile.metadata = new Metadata(); + } + gpxFile.metadata.name = Algorithms.getFileNameWithoutExtension(file.getName()); + + return GPXUtilities.writeGpxFile(file, gpxFile); + } + } } From 60e2c5568c7ae32db2fee346f812992031869ccf Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Sun, 24 Jan 2021 02:09:25 +0200 Subject: [PATCH 2/3] Fix #10621 --- .../osmand/plus/dialogs/RenameFileBottomSheet.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/OsmAnd/src/net/osmand/plus/dialogs/RenameFileBottomSheet.java b/OsmAnd/src/net/osmand/plus/dialogs/RenameFileBottomSheet.java index c6057b56de..af26533607 100644 --- a/OsmAnd/src/net/osmand/plus/dialogs/RenameFileBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/dialogs/RenameFileBottomSheet.java @@ -62,6 +62,8 @@ public class RenameFileBottomSheet extends MenuBottomSheetDialogFragment { file = new File(path); } selectedFileName = savedInstanceState.getString(SELECTED_FILE_NAME_KEY); + } else { + selectedFileName = Algorithms.getFileNameWithoutExtension(file); } items.add(new TitleItem(getString(R.string.shared_string_rename))); @@ -74,7 +76,7 @@ public class RenameFileBottomSheet extends MenuBottomSheetDialogFragment { nameTextBox.setDefaultHintTextColor(colorStateList); editText = view.findViewById(R.id.name_edit_text); - editText.setText(selectedFileName != null ? selectedFileName : Algorithms.getFileNameWithoutExtension(file)); + editText.setText(selectedFileName); editText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { @@ -127,12 +129,13 @@ public class RenameFileBottomSheet extends MenuBottomSheetDialogFragment { File dest; int index = file.getName().lastIndexOf('.'); String ext = index == -1 ? "" : file.getName().substring(index); + String newName = Algorithms.getFileNameWithoutExtension(selectedFileName); if (SQLiteTileSource.EXT.equals(ext)) { - dest = renameSQLiteFile(app, file, selectedFileName + ext, null); + dest = renameSQLiteFile(app, file, newName + ext, null); } else if (IndexConstants.GPX_FILE_EXT.equals(ext)) { - dest = renameGpxFile(app, file, selectedFileName + ext, false, null); + dest = renameGpxFile(app, file, newName + ext, false, null); } else { - dest = renameFile(app, file, selectedFileName + ext, false, null); + dest = renameFile(app, file, newName + ext, false, null); } if (dest != null) { Fragment fragment = getTargetFragment(); From b949e0362b45b5a69b55372a02730818963708e3 Mon Sep 17 00:00:00 2001 From: Vitaliy Date: Mon, 25 Jan 2021 09:18:28 +0200 Subject: [PATCH 3/3] Add TrackPointsCard --- OsmAnd/res/layout/search_text_layout.xml | 1 + OsmAnd/res/layout/track_menu.xml | 81 ++- OsmAnd/res/layout/track_points_card.xml | 21 + OsmAnd/res/values/strings.xml | 3 + .../net/osmand/plus/GpxSelectionHelper.java | 49 +- .../plus/myplaces/DeletePointsTask.java | 4 +- .../EditTrackGroupDialogFragment.java | 275 ++++++-- .../plus/myplaces/TrackPointFragment.java | 2 +- .../BooleanPreferenceBottomSheet.java | 4 +- .../net/osmand/plus/track/SegmentsCard.java | 1 - .../plus/track/TrackAppearanceFragment.java | 6 + .../osmand/plus/track/TrackDisplayHelper.java | 26 - .../osmand/plus/track/TrackMenuFragment.java | 231 ++++++- .../osmand/plus/track/TrackPointsCard.java | 639 ++++++++++++++++++ .../views/AddGpxPointBottomSheetHelper.java | 18 +- .../plus/views/layers/ContextMenuLayer.java | 1 - .../osmand/plus/views/layers/GPXLayer.java | 13 +- 17 files changed, 1257 insertions(+), 118 deletions(-) create mode 100644 OsmAnd/res/layout/track_points_card.xml create mode 100644 OsmAnd/src/net/osmand/plus/track/TrackPointsCard.java diff --git a/OsmAnd/res/layout/search_text_layout.xml b/OsmAnd/res/layout/search_text_layout.xml index 3056aec3df..f9365d7f75 100644 --- a/OsmAnd/res/layout/search_text_layout.xml +++ b/OsmAnd/res/layout/search_text_layout.xml @@ -1,6 +1,7 @@ + android:paddingRight="@dimen/context_menu_padding_margin_default" + android:paddingBottom="@dimen/context_menu_direction_margin"> + tools:text="@string/amenity_type_finance" /> @@ -81,7 +81,7 @@ android:id="@+id/icon_view" android:layout_width="@dimen/map_widget_icon" android:layout_height="@dimen/map_widget_icon" - android:layout_marginTop="@dimen/context_menu_second_line_top_margin" + android:layout_marginTop="@dimen/context_menu_second_line_top_margin" android:tint="?attr/default_icon_color" osmand:srcCompat="@drawable/ic_action_polygom_dark" /> @@ -118,6 +118,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/OsmAnd/res/values/strings.xml b/OsmAnd/res/values/strings.xml index c2a9bb671e..79d3a211b4 100644 --- a/OsmAnd/res/values/strings.xml +++ b/OsmAnd/res/values/strings.xml @@ -12,6 +12,9 @@ --> + Copy to favorites + Copy to map markers + Delete waypoints Edit description Read full Delete this online routing engine? diff --git a/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java b/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java index 879dad90d1..f23320d091 100644 --- a/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java +++ b/OsmAnd/src/net/osmand/plus/GpxSelectionHelper.java @@ -45,10 +45,12 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.StringTokenizer; public class GpxSelectionHelper { @@ -60,6 +62,7 @@ public class GpxSelectionHelper { private static final String BACKUPMODIFIEDTIME = "backupTime"; private static final String COLOR = "color"; private static final String SELECTED_BY_USER = "selected_by_user"; + private static final String HIDDEN_GROUPS = "hidden_groups"; private OsmandApplication app; private SavingTrackHelper savingTrackHelper; @@ -577,7 +580,10 @@ public class GpxSelectionHelper { } else if (obj.has(BACKUP)) { selectedGpxFilesBackUp.put(gpx, gpx.modifiedTime); } else { - selectGpxFile(gpx, true, false, true, selectedByUser, false); + SelectedGpxFile file = selectGpxFile(gpx, true, false, true, selectedByUser, false); + if (obj.has(HIDDEN_GROUPS)) { + readHiddenGroups(file, obj.getString(HIDDEN_GROUPS)); + } } gpx.addGeneralTrack(); } else if (obj.has(CURRENT_TRACK)) { @@ -598,6 +604,33 @@ public class GpxSelectionHelper { } } + private String saveHiddenGroups(SelectedGpxFile selectedGpxFile) { + StringBuilder stringBuilder = new StringBuilder(); + Iterator it = selectedGpxFile.hiddenGroups.iterator(); + while (it.hasNext()) { + String name = it.next(); + stringBuilder.append(name != null ? name : " "); + if (it.hasNext()) { + stringBuilder.append(","); + } + } + return stringBuilder.toString(); + } + + public void readHiddenGroups(SelectedGpxFile selectedGpxFile, String text) { + StringTokenizer toks = new StringTokenizer(text, ","); + Set res = new HashSet<>(); + while (toks.hasMoreTokens()) { + String token = toks.nextToken(); + if (!Algorithms.isBlank(token)) { + res.add(token); + } else { + res.add(null); + } + } + selectedGpxFile.hiddenGroups = res; + } + private int parseColor(String color) { try { return Algorithms.isEmpty(color) ? 0 : Algorithms.parseColor(color); @@ -619,6 +652,7 @@ public class GpxSelectionHelper { if (s.gpxFile.getColor(0) != 0) { obj.put(COLOR, Algorithms.colorToString(s.gpxFile.getColor(0))); } + obj.put(HIDDEN_GROUPS, saveHiddenGroups(s)); } obj.put(SELECTED_BY_USER, s.selectedByUser); } catch (JSONException e) { @@ -765,6 +799,7 @@ public class GpxSelectionHelper { private GPXFile gpxFile; private GPXTrackAnalysis trackAnalysis; + private Set hiddenGroups = new HashSet<>(); private List processedPointsToDisplay = new ArrayList<>(); private List displayGroups; @@ -832,6 +867,18 @@ public class GpxSelectionHelper { return processedPointsToDisplay; } + public Set getHiddenGroups() { + return Collections.unmodifiableSet(hiddenGroups); + } + + public void addHiddenGroups(String group) { + hiddenGroups.add(group); + } + + public void removeHiddenGroups(String group) { + hiddenGroups.remove(group); + } + public GPXFile getGpxFile() { return gpxFile; } diff --git a/OsmAnd/src/net/osmand/plus/myplaces/DeletePointsTask.java b/OsmAnd/src/net/osmand/plus/myplaces/DeletePointsTask.java index e1d3dffea4..996d52cfec 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/DeletePointsTask.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/DeletePointsTask.java @@ -15,14 +15,14 @@ import java.io.File; import java.lang.ref.WeakReference; import java.util.Set; -class DeletePointsTask extends AsyncTask { +public class DeletePointsTask extends AsyncTask { private OsmandApplication app; private GPXFile gpx; private Set selectedItems; private WeakReference listenerRef; - DeletePointsTask(OsmandApplication app, GPXFile gpxFile, Set selectedItems, OnPointsDeleteListener listener) { + public DeletePointsTask(OsmandApplication app, GPXFile gpxFile, Set selectedItems, OnPointsDeleteListener listener) { this.app = app; this.gpx = gpxFile; this.selectedItems = selectedItems; diff --git a/OsmAnd/src/net/osmand/plus/myplaces/EditTrackGroupDialogFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/EditTrackGroupDialogFragment.java index f4fe05f68c..a38c9e993c 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/EditTrackGroupDialogFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/EditTrackGroupDialogFragment.java @@ -1,15 +1,16 @@ package net.osmand.plus.myplaces; -import android.app.Activity; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; +import android.graphics.Typeface; import android.os.AsyncTask; import android.os.Bundle; import android.text.TextUtils; import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup.LayoutParams; import android.widget.AdapterView; import android.widget.EditText; @@ -17,56 +18,127 @@ import android.widget.ImageView; import android.widget.LinearLayout; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.ListPopupWindow; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; import net.osmand.AndroidUtils; import net.osmand.GPXUtilities; import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.WptPt; +import net.osmand.data.FavouritePoint; +import net.osmand.plus.FavouritesDbHelper; import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; -import net.osmand.plus.mapmarkers.MapMarkersHelper; -import net.osmand.plus.mapmarkers.MapMarkersGroup; +import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType; +import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.EditFavoriteGroupDialogFragment.FavoriteColorAdapter; +import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.SavingTrackHelper; import net.osmand.plus.activities.TrackActivity; 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.SimpleBottomSheetItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.helpers.FontCache; +import net.osmand.plus.mapmarkers.MapMarkersGroup; +import net.osmand.plus.mapmarkers.MapMarkersHelper; +import net.osmand.plus.measurementtool.OptionsDividerItem; +import net.osmand.plus.myplaces.DeletePointsTask.OnPointsDeleteListener; +import net.osmand.plus.settings.backend.ApplicationMode; +import net.osmand.plus.track.TrackMenuFragment; import net.osmand.util.Algorithms; import java.io.File; import java.lang.ref.WeakReference; +import java.util.HashSet; import java.util.List; +import java.util.Set; + +import static net.osmand.plus.settings.bottomsheets.BooleanPreferenceBottomSheet.getCustomButtonView; +import static net.osmand.plus.settings.bottomsheets.BooleanPreferenceBottomSheet.updateCustomButtonView; + +public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment implements OnPointsDeleteListener { -public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment { public static final String TAG = EditTrackGroupDialogFragment.class.getSimpleName(); + private OsmandApplication app; + private GpxDisplayGroup group; @Override public void createMenuItems(Bundle savedInstanceState) { - final OsmandApplication app = getMyApplication(); + app = requiredMyApplication(); if (group == null) { return; } items.add(new TitleItem(getCategoryName(app, group.getName()))); - BaseBottomSheetItem editNameItem = new SimpleBottomSheetItem.Builder() - .setIcon(getContentIcon(R.drawable.ic_action_edit_dark)) - .setTitle(getString(R.string.edit_name)) - .setLayoutId(R.layout.bottom_sheet_item_simple) + SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(group.getGpx().path); + if (group.getType() == GpxDisplayItemType.TRACK_POINTS && selectedGpxFile != null) { + items.add(createShowOnMapItem(selectedGpxFile)); + } + items.add(createEditNameItem()); + items.add(new OptionsDividerItem(app)); + +// items.add(createCopyToMarkersItem()); + items.add(createCopyToFavoritesItem()); + items.add(new OptionsDividerItem(app)); + + items.add(createDeleteGroupItem()); + } + + private BaseBottomSheetItem createShowOnMapItem(final SelectedGpxFile selectedGpxFile) { + final String name = Algorithms.isEmpty(group.getName()) ? null : group.getName(); + boolean checked = !selectedGpxFile.getHiddenGroups().contains(name); + final ApplicationMode mode = app.getSettings().getApplicationMode(); + final BottomSheetItemWithCompoundButton[] showOnMapItem = new BottomSheetItemWithCompoundButton[1]; + showOnMapItem[0] = (BottomSheetItemWithCompoundButton) new BottomSheetItemWithCompoundButton.Builder() + .setCompoundButtonColorId(mode.getIconColorInfo().getColor(nightMode)) + .setChecked(checked) + .setTitle(getString(R.string.shared_string_show_on_map)) + .setCustomView(getCustomButtonView(app, mode, checked, nightMode)) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Activity activity = getActivity(); + boolean checked = !showOnMapItem[0].isChecked(); + if (checked) { + selectedGpxFile.removeHiddenGroups(name); + } else { + selectedGpxFile.addHiddenGroups(name); + } + app.getSelectedGpxHelper().updateSelectedGpxFile(selectedGpxFile); + + showOnMapItem[0].setChecked(checked); + updateCustomButtonView(app, mode, v, checked, nightMode); + + FragmentActivity activity = getActivity(); + if (activity instanceof MapActivity) { + ((MapActivity) activity).refreshMap(); + } + } + }) + .create(); + return showOnMapItem[0]; + } + + private BaseBottomSheetItem createEditNameItem() { + return new SimpleBottomSheetItem.Builder() + .setIcon(getContentIcon(R.drawable.ic_action_name_field)) + .setTitle(getString(R.string.shared_string_rename)) + .setLayoutId(R.layout.bottom_sheet_item_simple) + .setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final FragmentActivity activity = getActivity(); if (activity != null) { AlertDialog.Builder b = new AlertDialog.Builder(activity); b.setTitle(R.string.favorite_group_name); @@ -86,11 +158,8 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment String name = nameEditText.getText().toString(); boolean nameChanged = !Algorithms.objectEquals(group.getName(), name); if (nameChanged) { - TrackActivity trackActivity = getTrackActivity(); - if (trackActivity != null) { - new UpdateGpxCategoryTask(trackActivity, group, name) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } + new UpdateGpxCategoryTask(activity, group, name) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } dismiss(); } @@ -100,20 +169,118 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment } }) .create(); - items.add(editNameItem); + } + private BaseBottomSheetItem createCopyToMarkersItem() { + return new SimpleBottomSheetItem.Builder() + .setIcon(getContentIcon(R.drawable.ic_action_copy)) + .setTitle(getString(R.string.copy_to_map_markers)) + .setLayoutId(R.layout.bottom_sheet_item_simple) + .setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { +// MapMarkersHelper markersHelper = app.getMapMarkersHelper(); +// MapMarkersGroup markersGroup = markersHelper.getMarkersGroup(group); +// if (markersGroup != null) { +// markersHelper.removeMarkersGroup(markersGroup); +// } else { +// markersHelper.addOrEnableGroup(group); +// } + } + }) + .create(); + } + + private BaseBottomSheetItem createCopyToFavoritesItem() { + return new SimpleBottomSheetItem.Builder() + .setIcon(getContentIcon(R.drawable.ic_action_copy)) + .setTitle(getString(R.string.copy_to_map_favorites)) + .setLayoutId(R.layout.bottom_sheet_item_simple) + .setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp) + .setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + saveGroupToFavorites(); + } + }) + .create(); + } + + private void saveGroupToFavorites() { + FragmentActivity activity = getActivity(); + if (activity != null) { + AlertDialog.Builder b = new AlertDialog.Builder(activity); + final EditText editText = new EditText(activity); + String name = group.getModifiableList().iterator().next().group.getName(); + if (name.indexOf('\n') > 0) { + name = name.substring(0, name.indexOf('\n')); + } + editText.setText(name); + int leftMargin = AndroidUtils.dpToPx(activity, 16f); + int topMargin = AndroidUtils.dpToPx(activity, 8f); + editText.setPadding(leftMargin, topMargin, leftMargin, topMargin); + b.setTitle(R.string.save_as_favorites_points); + b.setView(editText); + b.setPositiveButton(R.string.shared_string_save, new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + String category = editText.getText().toString(); + FavouritesDbHelper favouritesDbHelper = app.getFavorites(); + for (GpxDisplayItem item : group.getModifiableList()) { + if (item.locationStart != null) { + FavouritePoint fp = FavouritePoint.fromWpt(item.locationStart, app, category); + if (!Algorithms.isEmpty(item.description)) { + fp.setDescription(item.description); + } + favouritesDbHelper.addFavourite(fp, false); + } + } + favouritesDbHelper.saveCurrentPointsIntoFile(); + dismiss(); + } + }); + b.setNegativeButton(R.string.shared_string_cancel, null); + b.show(); + } + } + + private BaseBottomSheetItem createDeleteGroupItem() { + String delete = app.getString(R.string.shared_string_delete); + Typeface typeface = FontCache.getRobotoMedium(app); + return new SimpleBottomSheetItem.Builder() + .setTitleColorId(R.color.color_osm_edit_delete) + .setIcon(getIcon(R.drawable.ic_action_delete_dark, R.color.color_osm_edit_delete)) + .setTitle(UiUtilities.createCustomFontSpannable(typeface, delete, delete)) + .setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp) + .setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + deleteGroupItems(); + } + }) + .create(); + } + + private void deleteGroupItems() { + Set items = new HashSet<>(group.getModifiableList()); + new DeletePointsTask(app, group.getGpx(), items, this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private BaseBottomSheetItem createChangeColorItem() { final int themeRes = nightMode ? R.style.OsmandDarkTheme : R.style.OsmandLightTheme; final View changeColorView = View.inflate(new ContextThemeWrapper(getContext(), themeRes), R.layout.change_fav_color, null); ((ImageView) changeColorView.findViewById(R.id.change_color_icon)) .setImageDrawable(getContentIcon(R.drawable.ic_action_appearance)); updateColorView((ImageView) changeColorView.findViewById(R.id.colorImage)); - BaseBottomSheetItem changeColorItem = new BaseBottomSheetItem.Builder() + return new BaseBottomSheetItem.Builder() .setCustomView(changeColorView) .setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - Activity activity = getActivity(); + final FragmentActivity activity = getActivity(); if (activity != null) { final ListPopupWindow popup = new ListPopupWindow(activity); popup.setAnchorView(v); @@ -135,11 +302,8 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment Integer color = colorAdapter.getItem(position); if (color != null) { if (color != group.getColor()) { - TrackActivity trackActivity = getTrackActivity(); - if (trackActivity != null) { - new UpdateGpxCategoryTask(trackActivity, group, color) - .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } + new UpdateGpxCategoryTask(activity, group, color) + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } popup.dismiss(); @@ -151,8 +315,6 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment } }) .create(); - items.add(changeColorItem); - } @Override @@ -163,15 +325,6 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment } } - @Nullable - private TrackActivity getTrackActivity() { - Activity activity = getActivity(); - if (activity != null && activity instanceof TrackActivity) { - return (TrackActivity) activity; - } - return null; - } - private static String getCategoryName(@NonNull Context ctx, String category) { return Algorithms.isEmpty(category) ? ctx.getString(R.string.shared_string_waypoints) : category; } @@ -181,24 +334,38 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment if (color == 0) { colorImageView.setImageDrawable(getContentIcon(R.drawable.ic_action_circle)); } else { - colorImageView.setImageDrawable(getMyApplication().getUIUtilities().getPaintedIcon(R.drawable.ic_action_circle, color)); + colorImageView.setImageDrawable(app.getUIUtilities().getPaintedIcon(R.drawable.ic_action_circle, color)); } } - public static void showInstance(FragmentManager fragmentManager, GpxDisplayGroup group) { - EditTrackGroupDialogFragment f = (EditTrackGroupDialogFragment) fragmentManager - .findFragmentByTag(EditTrackGroupDialogFragment.TAG); - if (f == null ) { - f = new EditTrackGroupDialogFragment(); - f.group = group; - f.show(fragmentManager, EditTrackGroupDialogFragment.TAG); + public static void showInstance(FragmentManager fragmentManager, GpxDisplayGroup group, Fragment target) { + if (!fragmentManager.isStateSaved() && fragmentManager.findFragmentByTag(TAG) == null) { + EditTrackGroupDialogFragment fragment = new EditTrackGroupDialogFragment(); + fragment.group = group; + fragment.setRetainInstance(true); + fragment.setTargetFragment(target, 0); + fragment.show(fragmentManager, TAG); } } + @Override + public void onPointsDeletionStarted() { + + } + + @Override + public void onPointsDeleted() { + Fragment fragment = getTargetFragment(); + if (fragment instanceof TrackMenuFragment) { + ((TrackMenuFragment) fragment).updateContent(); + } + dismiss(); + } + private static class UpdateGpxCategoryTask extends AsyncTask { private OsmandApplication app; - private WeakReference activityRef; + private WeakReference activityRef; private GpxDisplayGroup group; @@ -208,20 +375,20 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment private ProgressDialog progressDialog; private boolean wasUpdated = false; - private UpdateGpxCategoryTask(@NonNull TrackActivity activity, @NonNull GpxDisplayGroup group) { + private UpdateGpxCategoryTask(@NonNull FragmentActivity activity, @NonNull GpxDisplayGroup group) { this.app = (OsmandApplication) activity.getApplication(); activityRef = new WeakReference<>(activity); this.group = group; } - UpdateGpxCategoryTask(@NonNull TrackActivity activity, @NonNull GpxDisplayGroup group, + UpdateGpxCategoryTask(@NonNull FragmentActivity activity, @NonNull GpxDisplayGroup group, @NonNull String newCategory) { this(activity, group); this.newCategory = newCategory; } - UpdateGpxCategoryTask(@NonNull TrackActivity activity, @NonNull GpxDisplayGroup group, + UpdateGpxCategoryTask(@NonNull FragmentActivity activity, @NonNull GpxDisplayGroup group, @NonNull Integer newColor) { this(activity, group); this.newColor = newColor; @@ -229,7 +396,7 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment @Override protected void onPreExecute() { - TrackActivity activity = activityRef.get(); + FragmentActivity activity = activityRef.get(); if (activity != null) { progressDialog = new ProgressDialog(activity); progressDialog.setTitle(EditTrackGroupDialogFragment.getCategoryName(app, group.getName())); @@ -293,9 +460,15 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment progressDialog.dismiss(); } - TrackActivity activity = activityRef.get(); - if (activity != null) { - activity.loadGpx(); + FragmentActivity activity = activityRef.get(); + if (activity instanceof TrackActivity) { + ((TrackActivity) activity).loadGpx(); + } else if (activity instanceof MapActivity) { + MapActivity mapActivity = (MapActivity) activity; + TrackMenuFragment fragment = mapActivity.getTrackMenuFragment(); + if (fragment != null) { + fragment.updateContent(); + } } } diff --git a/OsmAnd/src/net/osmand/plus/myplaces/TrackPointFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/TrackPointFragment.java index 82fbfb02b6..59be8cc533 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/TrackPointFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/TrackPointFragment.java @@ -1007,7 +1007,7 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements public void onClick(View v) { FragmentActivity activity = getActivity(); if (activity != null) { - EditTrackGroupDialogFragment.showInstance(activity.getSupportFragmentManager(), group); + EditTrackGroupDialogFragment.showInstance(activity.getSupportFragmentManager(), group, null); } } }); diff --git a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java index 21cd184cd4..8b8b241b1b 100644 --- a/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/settings/bottomsheets/BooleanPreferenceBottomSheet.java @@ -122,14 +122,14 @@ public class BooleanPreferenceBottomSheet extends BasePreferenceBottomSheet { return R.string.shared_string_close; } - protected static View getCustomButtonView(OsmandApplication app, ApplicationMode mode, boolean checked, boolean nightMode) { + public static View getCustomButtonView(OsmandApplication app, ApplicationMode mode, boolean checked, boolean nightMode) { View customView = UiUtilities.getInflater(app, nightMode).inflate(R.layout.bottom_sheet_item_preference_switch, null); updateCustomButtonView(app, mode, customView, checked, nightMode); return customView; } - protected static void updateCustomButtonView(OsmandApplication app, ApplicationMode mode, View customView, boolean checked, boolean nightMode) { + public static void updateCustomButtonView(OsmandApplication app, ApplicationMode mode, View customView, boolean checked, boolean nightMode) { Context themedCtx = UiUtilities.getThemedContext(app, nightMode); View buttonView = customView.findViewById(R.id.button_container); diff --git a/OsmAnd/src/net/osmand/plus/track/SegmentsCard.java b/OsmAnd/src/net/osmand/plus/track/SegmentsCard.java index b3921a6889..ec000a8f08 100644 --- a/OsmAnd/src/net/osmand/plus/track/SegmentsCard.java +++ b/OsmAnd/src/net/osmand/plus/track/SegmentsCard.java @@ -22,7 +22,6 @@ public class SegmentsCard extends BaseCard { private TrackDisplayHelper displayHelper; private GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT}; - private SegmentGPXAdapter adapter; private SegmentActionsListener listener; public SegmentsCard(@NonNull MapActivity mapActivity, @NonNull TrackDisplayHelper displayHelper, diff --git a/OsmAnd/src/net/osmand/plus/track/TrackAppearanceFragment.java b/OsmAnd/src/net/osmand/plus/track/TrackAppearanceFragment.java index 9a89f67017..ddc102adc9 100644 --- a/OsmAnd/src/net/osmand/plus/track/TrackAppearanceFragment.java +++ b/OsmAnd/src/net/osmand/plus/track/TrackAppearanceFragment.java @@ -112,10 +112,16 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement return 0; } + @Override public float getMiddleStateKoef() { return 0.5f; } + @Override + public int getInitialMenuState() { + return MenuState.HALF_SCREEN; + } + public TrackDrawInfo getTrackDrawInfo() { return trackDrawInfo; } diff --git a/OsmAnd/src/net/osmand/plus/track/TrackDisplayHelper.java b/OsmAnd/src/net/osmand/plus/track/TrackDisplayHelper.java index 39ce84eedf..ade32e36a4 100644 --- a/OsmAnd/src/net/osmand/plus/track/TrackDisplayHelper.java +++ b/OsmAnd/src/net/osmand/plus/track/TrackDisplayHelper.java @@ -1,14 +1,9 @@ package net.osmand.plus.track; -import android.app.Activity; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import net.osmand.GPXUtilities.GPXFile; -import net.osmand.GPXUtilities.WptPt; -import net.osmand.data.LatLon; -import net.osmand.data.PointDescription; import net.osmand.data.QuadRect; import net.osmand.plus.GPXDatabase.GpxDataItem; import net.osmand.plus.GpxSelectionHelper; @@ -17,10 +12,6 @@ import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.R; -import net.osmand.plus.activities.MapActivity; -import net.osmand.plus.measurementtool.GpxData; -import net.osmand.plus.settings.backend.OsmandSettings; import java.io.File; import java.util.ArrayList; @@ -163,21 +154,4 @@ public class TrackDisplayHelper { } return list; } - - public void addNewGpxData(Activity activity) { - GPXFile gpxFile = getGpx(); - GpxData gpxData = new GpxData(gpxFile); - WptPt pointToShow = gpxFile != null ? gpxFile.findPointToShow() : null; - if (pointToShow != null) { - LatLon location = new LatLon(pointToShow.getLatitude(), pointToShow.getLongitude()); - final OsmandSettings settings = app.getSettings(); - settings.setMapLocationToShow(location.getLatitude(), location.getLongitude(), - settings.getLastKnownMapZoom(), - new PointDescription(PointDescription.POINT_TYPE_WPT, activity.getString(R.string.add_line)), - false, - gpxData - ); - MapActivity.launchMapActivityMoveToTop(activity); - } - } } diff --git a/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java b/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java index b62f481b22..1a0f0383c3 100644 --- a/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java +++ b/OsmAnd/src/net/osmand/plus/track/TrackMenuFragment.java @@ -1,21 +1,27 @@ package net.osmand.plus.track; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; import android.content.DialogInterface; import android.content.res.ColorStateList; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; +import android.view.View.OnClickListener; import android.view.ViewGroup; +import android.view.animation.DecelerateInterpolator; +import android.widget.EditText; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.TextView; -import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; @@ -33,6 +39,7 @@ import net.osmand.GPXUtilities.TrkSegment; import net.osmand.Location; import net.osmand.PlatformUtil; import net.osmand.data.LatLon; +import net.osmand.data.PointDescription; import net.osmand.data.QuadRect; import net.osmand.data.RotatedTileBox; import net.osmand.plus.GpxDbHelper; @@ -61,6 +68,7 @@ import net.osmand.plus.measurementtool.GpxData; import net.osmand.plus.measurementtool.MeasurementEditingContext; import net.osmand.plus.measurementtool.MeasurementToolFragment; import net.osmand.plus.myplaces.AvailableGPXFragment.GpxInfo; +import net.osmand.plus.myplaces.DeletePointsTask.OnPointsDeleteListener; import net.osmand.plus.myplaces.MoveGpxFileBottomSheet; import net.osmand.plus.myplaces.MoveGpxFileBottomSheet.OnTrackFileMoveListener; import net.osmand.plus.myplaces.SegmentActionsListener; @@ -70,6 +78,7 @@ import net.osmand.plus.osmedit.OsmEditingPlugin; import net.osmand.plus.routepreparationmenu.cards.BaseCard; import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener; import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener; +import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint; import net.osmand.plus.widgets.IconPopupMenu; import net.osmand.util.Algorithms; import net.osmand.util.MapUtils; @@ -94,9 +103,12 @@ import static net.osmand.plus.track.OptionsCard.RENAME_BUTTON_INDEX; import static net.osmand.plus.track.OptionsCard.SHARE_BUTTON_INDEX; import static net.osmand.plus.track.OptionsCard.SHOW_ON_MAP_BUTTON_INDEX; import static net.osmand.plus.track.OptionsCard.UPLOAD_OSM_BUTTON_INDEX; +import static net.osmand.plus.track.TrackPointsCard.ADD_WAYPOINT_INDEX; +import static net.osmand.plus.track.TrackPointsCard.DELETE_WAYPOINTS_INDEX; public class TrackMenuFragment extends ContextMenuScrollFragment implements CardListener, - SegmentActionsListener, RenameCallback, OnTrackFileMoveListener, OsmAndLocationListener, OsmAndCompassListener { + SegmentActionsListener, RenameCallback, OnTrackFileMoveListener, OnPointsDeleteListener, + OsmAndLocationListener, OsmAndCompassListener { public static final String TAG = TrackMenuFragment.class.getName(); private static final Log log = PlatformUtil.getLog(TrackMenuFragment.class); @@ -105,37 +117,46 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card private TrackDisplayHelper displayHelper; private SelectedGpxFile selectedGpxFile; - private View routeMenuTopShadowAll; - private TextView headerTitle; - private ImageView headerIcon; - private BottomNavigationView bottomNav; private TrackMenuType menuType = TrackMenuType.OVERVIEW; private SegmentsCard segmentsCard; private OptionsCard optionsCard; private DescriptionCard descriptionCard; private OverviewCard overviewCard; + private TrackPointsCard pointsCard; + private TextView headerTitle; + private ImageView headerIcon; + private View toolbarContainer; + private View searchContainer; + private ImageView searchButton; + private EditText searchEditText; + private TextView toolbarTextView; + private View routeMenuTopShadowAll; + private BottomNavigationView bottomNav; + + private String gpxTitle; private TrackChartPoints trackChartPoints; - private int menuTitleHeight; - private String gpxTitle; - private UpdateLocationViewCache updateLocationViewCache; - private Location lastLocation = null; private Float heading; + private Location lastLocation; + private UpdateLocationViewCache updateLocationViewCache; private boolean locationUpdateStarted; + private int menuTitleHeight; + private int toolbarHeightPx; + public enum TrackMenuType { OVERVIEW(R.id.action_overview, R.string.shared_string_overview), TRACK(R.id.action_track, R.string.shared_string_gpx_tracks), POINTS(R.id.action_points, R.string.shared_string_gpx_points), OPTIONS(R.id.action_options, R.string.shared_string_options); - TrackMenuType(@DrawableRes int iconId, @StringRes int titleId) { - this.iconId = iconId; + TrackMenuType(int menuItemId, @StringRes int titleId) { + this.menuItemId = menuItemId; this.titleId = titleId; } - public final int iconId; + public final int menuItemId; public final int titleId; } @@ -156,7 +177,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card @Override public int getToolbarHeight() { - return 0; + return toolbarHeightPx; } public float getMiddleStateKoef() { @@ -183,6 +204,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card app = requireMyApplication(); GpxDbHelper gpxDbHelper = app.getGpxDbHelper(); displayHelper = new TrackDisplayHelper(app); + updateLocationViewCache = app.getUIUtilities().getUpdateLocationViewCache(); Bundle arguments = getArguments(); if (arguments != null) { @@ -200,6 +222,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card String fileName = Algorithms.getFileWithoutDirs(getGpx().path); gpxTitle = GpxUiHelper.getGpxTitle(fileName); } + toolbarHeightPx = getResources().getDimensionPixelSize(R.dimen.dashboard_map_toolbar); } public GPXFile getGpx() { @@ -214,7 +237,10 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card routeMenuTopShadowAll = view.findViewById(R.id.route_menu_top_shadow_all); headerTitle = view.findViewById(R.id.title); headerIcon = view.findViewById(R.id.icon_view); - updateLocationViewCache = app.getUIUtilities().getUpdateLocationViewCache(); + toolbarContainer = view.findViewById(R.id.context_menu_toolbar_container); + toolbarTextView = view.findViewById(R.id.toolbar_title); + searchButton = view.findViewById(R.id.search_button); + searchContainer = view.findViewById(R.id.search_container); if (isPortrait()) { AndroidUiHelper.updateVisibility(getTopShadow(), true); @@ -226,6 +252,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card } setupCards(); + setupToolbar(); updateHeader(); setupButtons(view); enterTrackAppearanceMode(); @@ -261,6 +288,74 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card boolean isOptions = menuType == TrackMenuType.OPTIONS; setHeaderTitle(isOptions ? app.getString(menuType.titleId) : gpxTitle, !isOptions); } + if (menuType == TrackMenuType.POINTS) { + AndroidUiHelper.updateVisibility(searchButton, true); + } else { + AndroidUiHelper.updateVisibility(toolbarTextView, true); + AndroidUiHelper.updateVisibility(searchButton, false); + AndroidUiHelper.updateVisibility(searchContainer, false); + } + } + + private void setupToolbar() { + toolbarTextView.setText(gpxTitle); + + ImageView closeButton = toolbarContainer.findViewById(R.id.close_button); + closeButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (menuType == TrackMenuType.POINTS) { + AndroidUiHelper.updateVisibility(toolbarTextView, true); + AndroidUiHelper.updateVisibility(searchButton, true); + AndroidUiHelper.updateVisibility(searchContainer, false); + } + openMenuHeaderOnly(); + } + }); + closeButton.setImageResource(AndroidUtils.getNavigationIconResId(toolbarContainer.getContext())); + + searchButton.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + AndroidUiHelper.updateVisibility(searchContainer, true); + AndroidUiHelper.updateVisibility(searchButton, false); + AndroidUiHelper.updateVisibility(toolbarTextView, false); + } + }); + searchEditText = toolbarContainer.findViewById(R.id.searchEditText); + searchEditText.setHint(R.string.search_poi_filter); + searchEditText.addTextChangedListener( + new TextWatcher() { + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + if (pointsCard != null) { + pointsCard.filter(s.toString()); + } + } + } + ); + ImageView clearButton = toolbarContainer.findViewById(R.id.clearButton); + clearButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!Algorithms.isEmpty(searchEditText.getText())) { + searchEditText.setText(""); + searchEditText.setSelection(0); + } + if (pointsCard != null) { + pointsCard.updateContent(); + } + } + }); } private void setupCards() { @@ -296,13 +391,25 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card if (descriptionCard != null && descriptionCard.getView() != null) { ViewGroup parent = ((ViewGroup) descriptionCard.getView().getParent()); if (parent != null) { - cardsContainer.removeView(descriptionCard.getView()); + parent.removeView(descriptionCard.getView()); } cardsContainer.addView(descriptionCard.getView()); } else { descriptionCard = new DescriptionCard(getMapActivity(), displayHelper.getGpx()); cardsContainer.addView(descriptionCard.build(mapActivity)); } + } else if (menuType == TrackMenuType.POINTS) { + if (pointsCard != null && pointsCard.getView() != null) { + ViewGroup parent = (ViewGroup) pointsCard.getView().getParent(); + if (parent != null) { + parent.removeAllViews(); + } + cardsContainer.addView(pointsCard.getView()); + } else { + pointsCard = new TrackPointsCard(mapActivity, displayHelper); + pointsCard.setListener(this); + cardsContainer.addView(pointsCard.build(mapActivity)); + } } } } @@ -318,12 +425,14 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card protected void setViewY(int y, boolean animated, boolean adjustMapPos) { super.setViewY(y, animated, adjustMapPos); updateStatusBarColor(); + updateToolbar(y, animated); } @Override protected void updateMainViewLayout(int posY) { super.updateMainViewLayout(posY); updateStatusBarColor(); + updateToolbar(posY, true); } @Override @@ -485,7 +594,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card if (Build.VERSION.SDK_INT >= 23 && !nightMode) { view.setSystemUiVisibility(view.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); } - return nightMode ? R.color.divider_color_dark : R.color.divider_color_light; + return nightMode ? R.color.status_bar_color_dark : R.color.status_bar_color_light; } else { if (Build.VERSION.SDK_INT >= 23 && !nightMode) { view.setSystemUiVisibility(view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); @@ -523,8 +632,8 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card if (mapActivity == null) { return; } + final GPXFile gpxFile = getGpx(); if (card instanceof OptionsCard || card instanceof OverviewCard) { - final GPXFile gpxFile = getGpx(); if (buttonIndex == SHOW_ON_MAP_BUTTON_INDEX) { boolean gpxFileSelected = !isGpxFileSelected(app, gpxFile); app.getSelectedGpxHelper().selectGpxFile(gpxFile, gpxFileSelected, false); @@ -616,6 +725,25 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card }); builder.show(); } + } else if (card instanceof TrackPointsCard) { + if (buttonIndex == ADD_WAYPOINT_INDEX) { + PointDescription pointDescription = new PointDescription(PointDescription.POINT_TYPE_WPT, app.getString(R.string.add_waypoint)); + QuadRect rect = displayHelper.getRect(); + NewGpxPoint newGpxPoint = new NewGpxPoint(gpxFile, pointDescription, rect); + + mapActivity.getMapView().fitRectToMap(rect.left, rect.right, rect.top, rect.bottom, + (int) rect.width(), (int) rect.height(), 0); + mapActivity.getMapLayers().getContextMenuLayer().enterAddGpxPointMode(newGpxPoint); + + hide(); + } else if (buttonIndex == DELETE_WAYPOINTS_INDEX) { + TrackPointsCard pointsCard = (TrackPointsCard) card; + if (pointsCard.isSelectionMode()) { + pointsCard.deleteItemsAction(); + } else { + pointsCard.setSelectionMode(true); + } + } } } @@ -628,6 +756,34 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card return y; } + public void updateToolbar(int y, boolean animated) { + final MapActivity mapActivity = getMapActivity(); + if (mapActivity != null) { + if (toolbarContainer != null && isPortrait()) { + if (animated) { + final float toolbarAlpha = getToolbarAlpha(y); + if (toolbarAlpha > 0) { + updateVisibility(toolbarContainer, true); + } + toolbarContainer.animate().alpha(toolbarAlpha) + .setDuration(ContextMenuFragment.ANIMATION_DURATION) + .setInterpolator(new DecelerateInterpolator()) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + updateVisibility(toolbarContainer, toolbarAlpha); + mapActivity.updateStatusBarColor(); + } + }) + .start(); + } else { + updateToolbarVisibility(toolbarContainer, y); + mapActivity.updateStatusBarColor(); + } + } + } + } + @Override protected void onHeaderClick() { adjustMapPosition(getViewY()); @@ -665,7 +821,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card @Override public boolean onNavigationItemSelected(@NonNull MenuItem item) { for (TrackMenuType type : TrackMenuType.values()) { - if (type.iconId == item.getItemId()) { + if (type.menuItemId == item.getItemId()) { menuType = type; setupCards(); updateHeader(); @@ -688,6 +844,9 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card if (descriptionCard != null) { descriptionCard.updateContent(); } + if (pointsCard != null) { + pointsCard.updateContent(); + } setupCards(); } @@ -701,6 +860,18 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card } + @Override + public void onPointsDeletionStarted() { + + } + + @Override + public void onPointsDeleted() { + if (pointsCard != null) { + pointsCard.onPointsDeleted(); + } + } + @Override public void onPointSelected(TrkSegment segment, double lat, double lon) { if (trackChartPoints == null) { @@ -730,7 +901,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card TrackDetailsMenu trackDetailsMenu = getMapActivity().getTrackDetailsMenu(); trackDetailsMenu.setGpxItem(gpxItem); trackDetailsMenu.show(); - close(); + hide(); } @Override @@ -777,7 +948,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card private void editSegment(TrkSegment segment) { GPXFile gpxFile = getGpx(); openPlanRoute(new GpxData(gpxFile)); - close(); + hide(); } public void openPlanRoute(GpxData gpxData) { @@ -828,12 +999,24 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } - private void close() { + private void hide() { try { MapActivity mapActivity = getMapActivity(); if (mapActivity != null) { FragmentManager fragmentManager = mapActivity.getSupportFragmentManager(); - fragmentManager.beginTransaction().remove(this).commitAllowingStateLoss(); + fragmentManager.beginTransaction().hide(this).commit(); + } + } catch (Exception e) { + log.error(e); + } + } + + public void show() { + try { + MapActivity mapActivity = getMapActivity(); + if (mapActivity != null) { + FragmentManager fragmentManager = mapActivity.getSupportFragmentManager(); + fragmentManager.beginTransaction().show(this).commit(); } } catch (Exception e) { log.error(e); @@ -853,7 +1036,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card mapActivity.getSupportFragmentManager() .beginTransaction() - .replace(R.id.fragmentContainer, fragment, fragment.getFragmentTag()) + .replace(R.id.fragmentContainer, fragment, TAG) .addToBackStack(fragment.getFragmentTag()) .commitAllowingStateLoss(); return true; diff --git a/OsmAnd/src/net/osmand/plus/track/TrackPointsCard.java b/OsmAnd/src/net/osmand/plus/track/TrackPointsCard.java new file mode 100644 index 0000000000..ec3bc0446e --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/track/TrackPointsCard.java @@ -0,0 +1,639 @@ +package net.osmand.plus.track; + +import android.content.DialogInterface; +import android.os.AsyncTask; +import android.text.SpannableStringBuilder; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.ForegroundColorSpan; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.CheckBox; +import android.widget.ExpandableListView; +import android.widget.ExpandableListView.OnChildClickListener; +import android.widget.Filter; +import android.widget.Filterable; +import android.widget.ImageView; +import android.widget.TextView; + +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.core.content.ContextCompat; + +import net.osmand.AndroidUtils; +import net.osmand.Collator; +import net.osmand.GPXUtilities.WptPt; +import net.osmand.OsmAndCollator; +import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup; +import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; +import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType; +import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; +import net.osmand.plus.activities.MapActivity; +import net.osmand.plus.activities.OsmandBaseExpandableListAdapter; +import net.osmand.plus.base.PointImageDrawable; +import net.osmand.plus.helpers.AndroidUiHelper; +import net.osmand.plus.myplaces.DeletePointsTask; +import net.osmand.plus.myplaces.DeletePointsTask.OnPointsDeleteListener; +import net.osmand.plus.myplaces.EditTrackGroupDialogFragment; +import net.osmand.plus.routepreparationmenu.cards.BaseCard; +import net.osmand.util.Algorithms; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public class TrackPointsCard extends BaseCard implements OnChildClickListener, OnPointsDeleteListener { + + public static final int ADD_WAYPOINT_INDEX = 0; + public static final int DELETE_WAYPOINTS_INDEX = 1; + + private final TrackDisplayHelper displayHelper; + private final GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_POINTS, GpxDisplayItemType.TRACK_ROUTE_POINTS}; + + private final Set selectedGroups = new LinkedHashSet<>(); + private final LinkedHashMap> selectedItems = new LinkedHashMap<>(); + private boolean selectionMode; + + private final PointGPXAdapter adapter; + private ExpandableListView listView; + + public TrackPointsCard(@NonNull MapActivity mapActivity, @NonNull TrackDisplayHelper displayHelper) { + super(mapActivity); + this.displayHelper = displayHelper; + adapter = new PointGPXAdapter(); + } + + public boolean isSelectionMode() { + return selectionMode; + } + + public void setSelectionMode(boolean selectionMode) { + this.selectionMode = selectionMode; + adapter.notifyDataSetInvalidated(); + } + + @Override + public int getCardLayoutId() { + return R.layout.track_points_card; + } + + @Override + protected void updateContent() { + listView = view.findViewById(android.R.id.list); + listView.setOnChildClickListener(this); + + adapter.setFilterResults(null); + adapter.synchronizeGroups(getOriginalGroups()); + if (listView.getAdapter() == null) { + listView.setAdapter(adapter); + } + if (!adapter.isEmpty() && listView.getFooterViewsCount() == 0) { + LayoutInflater inflater = UiUtilities.getInflater(mapActivity, nightMode); + listView.addFooterView(inflater.inflate(R.layout.list_shadow_footer, listView, false)); + addActions(inflater); + } + expandAllGroups(); + } + + private void addActions(LayoutInflater inflater) { + View view = inflater.inflate(R.layout.preference_category_with_descr, listView, false); + TextView title = view.findViewById(android.R.id.title); + title.setText(R.string.shared_string_actions); + + AndroidUiHelper.updateVisibility(view.findViewById(android.R.id.icon), false); + AndroidUiHelper.updateVisibility(view.findViewById(android.R.id.summary), false); + listView.addFooterView(view); + + addWaypointAction(inflater); + deleteWaypointAction(inflater); + } + + private void addWaypointAction(LayoutInflater inflater) { + View view = inflater.inflate(R.layout.preference_button, listView, false); + TextView addWaypointTitle = view.findViewById(android.R.id.title); + ImageView addWaypointIcon = view.findViewById(android.R.id.icon); + + addWaypointTitle.setText(R.string.add_waypoint); + addWaypointIcon.setImageDrawable(getContentIcon(R.drawable.ic_action_name_field)); + + AndroidUiHelper.updateVisibility(view.findViewById(R.id.divider), true); + + view.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + CardListener listener = getListener(); + if (listener != null) { + listener.onCardButtonPressed(TrackPointsCard.this, ADD_WAYPOINT_INDEX); + } + } + }); + listView.addFooterView(view); + } + + private void deleteWaypointAction(LayoutInflater inflater) { + View view = inflater.inflate(R.layout.preference_button, listView, false); + TextView deleteWaypointsTitle = view.findViewById(android.R.id.title); + ImageView deleteWaypointsIcon = view.findViewById(android.R.id.icon); + + deleteWaypointsTitle.setText(R.string.delete_waypoints); + deleteWaypointsIcon.setImageDrawable(getColoredIcon(R.drawable.ic_action_delete_dark, R.color.color_osm_edit_delete)); + + view.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + CardListener listener = getListener(); + if (listener != null) { + listener.onCardButtonPressed(TrackPointsCard.this, DELETE_WAYPOINTS_INDEX); + } + } + }); + listView.addFooterView(view); + } + + private void expandAllGroups() { + for (int i = 0; i < adapter.getGroupCount(); i++) { + listView.expandGroup(i); + } + } + + private List getOriginalGroups() { + return displayHelper.getOriginalGroups(filterTypes); + } + + @Override + public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { + return true; + } + + public void deleteItemsAction() { + int size = getSelectedItemsCount(); + if (size > 0) { + AlertDialog.Builder b = new AlertDialog.Builder(mapActivity); + b.setMessage(app.getString(R.string.points_delete_multiple, size)); + b.setPositiveButton(R.string.shared_string_delete, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + deleteItems(); + setSelectionMode(false); + adapter.notifyDataSetInvalidated(); + } + }); + b.setNegativeButton(R.string.shared_string_cancel, null); + b.show(); + } + } + + private void deleteItems() { + new DeletePointsTask(app, displayHelper.getGpx(), getSelectedItems(), this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } + + private Set getSelectedItems() { + Set result = new LinkedHashSet<>(); + for (Set set : selectedItems.values()) { + if (set != null) { + result.addAll(set); + } + } + return result; + } + + private void updateSelectionMode() { + int size = getSelectedItemsCount(); + app.showToastMessage(size + " " + app.getString(R.string.shared_string_selected_lowercase)); + } + + private int getSelectedItemsCount() { + int count = 0; + for (Set set : selectedItems.values()) { + if (set != null) { + count += set.size(); + } + } + return count; + } + + @Override + public void onPointsDeletionStarted() { + + } + + @Override + public void onPointsDeleted() { + selectedItems.clear(); + selectedGroups.clear(); + adapter.synchronizeGroups(getOriginalGroups()); + } + + public void filter(String text) { + adapter.getFilter().filter(text); + } + + private class PointGPXAdapter extends OsmandBaseExpandableListAdapter implements Filterable { + + private final List groups = new ArrayList<>(); + private final Map> itemGroups = new LinkedHashMap<>(); + private final Comparator comparator; + private Filter pointsFilter; + private Set filteredItems; + + PointGPXAdapter() { + final Collator collator = OsmAndCollator.primaryCollator(); + comparator = new Comparator() { + @Override + public int compare(String s1, String s2) { + return collator.compare(s1, s2); + } + }; + } + + public void synchronizeGroups(@NonNull List displayGroups) { + groups.clear(); + itemGroups.clear(); + Set filtered = filteredItems; + Collections.sort(displayGroups, new Comparator() { + @Override + public int compare(GpxDisplayGroup g1, GpxDisplayGroup g2) { + int i1 = g1.getType().ordinal(); + int i2 = g2.getType().ordinal(); + return i1 < i2 ? -1 : (i1 == i2 ? 0 : 1); + } + }); + List trackPointsGroups = new ArrayList<>(); + List routePointsGroups = new ArrayList<>(); + for (GpxDisplayGroup group : displayGroups) { + if (group.getType() == GpxDisplayItemType.TRACK_POINTS) { + trackPointsGroups.add(group); + } else if (group.getType() == GpxDisplayItemType.TRACK_ROUTE_POINTS) { + routePointsGroups.add(group); + } + } + processDisplayGroups(trackPointsGroups, filtered); + processDisplayGroups(routePointsGroups, filtered); + notifyDataSetChanged(); + } + + private void processDisplayGroups(List displayGroups, Set filteredItems) { + for (int i = 0; i < displayGroups.size(); i++) { + GpxDisplayGroup group = displayGroups.get(i); + if (group.getModifiableList().isEmpty()) { + continue; + } + Map> itemsMap = collectItemsByCategory(group, i); + if (filteredItems != null) { + itemsMap = filterItems(itemsMap, filteredItems); + } + if (!Algorithms.isEmpty(itemsMap)) { + setCollectedItems(group, itemsMap); + } + } + } + + private Map> collectItemsByCategory(GpxDisplayGroup group, int index) { + Map> itemsMap = new HashMap<>(); + + for (GpxDisplayItem item : group.getModifiableList()) { + String category; + if (item.locationStart != null) { + if (group.getType() == GpxDisplayItemType.TRACK_POINTS) { + category = item.locationStart.category; + if (Algorithms.isEmpty(category)) { + category = ""; + } + } else { + category = app.getString(R.string.route_points) + " " + (index + 1); + } + } else { + category = ""; + } + List items = itemsMap.get(category); + if (items == null) { + items = new ArrayList<>(); + itemsMap.put(category, items); + } + items.add(item); + } + return itemsMap; + } + + private Map> filterItems(Map> itemsMap, Set filteredItems) { + Map> itemsMapFiltered = new HashMap<>(); + for (Entry> e : itemsMap.entrySet()) { + String category = e.getKey(); + List items = e.getValue(); + if (filteredItems.contains(category)) { + itemsMapFiltered.put(category, items); + } else { + for (GpxDisplayItem i : items) { + if (filteredItems.contains(i)) { + List itemsFiltered = itemsMapFiltered.get(category); + if (itemsFiltered == null) { + itemsFiltered = new ArrayList<>(); + itemsMapFiltered.put(category, itemsFiltered); + } + itemsFiltered.add(i); + } + } + } + } + return itemsMapFiltered; + } + + private void setCollectedItems(GpxDisplayGroup group, Map> itemsMap) { + List categories = new ArrayList<>(itemsMap.keySet()); + Collections.sort(categories, comparator); + for (String category : categories) { + List values = itemsMap.get(category); + GpxDisplayGroup headerGroup = group.cloneInstance(); + headerGroup.setName(category); + for (GpxDisplayItem i : values) { + if (i.locationStart != null && i.locationStart.getColor() != 0) { + headerGroup.setColor(i.locationStart.getColor(group.getColor())); + break; + } + } + List headerGroupItems = headerGroup.getModifiableList(); + headerGroupItems.clear(); + headerGroupItems.addAll(values); + itemGroups.put(headerGroup, values); + this.groups.add(headerGroup); + } + } + + @Override + public int getGroupCount() { + return groups.size(); + } + + @Override + public GpxDisplayGroup getGroup(int groupPosition) { + return groups.get(groupPosition); + } + + @Override + public long getGroupId(int groupPosition) { + return groupPosition; + } + + @Override + public int getChildrenCount(int groupPosition) { + return itemGroups.get(groups.get(groupPosition)).size(); + } + + @Override + public GpxDisplayItem getChild(int groupPosition, int childPosition) { + return itemGroups.get(groups.get(groupPosition)).get(childPosition); + } + + @Override + public long getChildId(int groupPosition, int childPosition) { + return groupPosition * 10000 + childPosition; + } + + @Override + public boolean isChildSelectable(int groupPosition, int childPosition) { + return true; + } + + @Override + public boolean hasStableIds() { + return false; + } + + @Override + public View getGroupView(final int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { + final GpxDisplayGroup group = getGroup(groupPosition); + View row = convertView; + if (row == null) { + LayoutInflater inflater = LayoutInflater.from(view.getContext()); + row = inflater.inflate(R.layout.wpt_list_item, parent, false); + } + + row.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (listView.isGroupExpanded(groupPosition)) { + listView.collapseGroup(groupPosition); + } else { + listView.expandGroup(groupPosition); + } + } + }); + + String categoryName = group.getName(); + if (TextUtils.isEmpty(categoryName)) { + categoryName = app.getString(R.string.shared_string_gpx_points); + } + SpannableStringBuilder text = new SpannableStringBuilder(categoryName) + .append(" – ") + .append(String.valueOf(getChildrenCount(groupPosition))); + text.setSpan(new ForegroundColorSpan(AndroidUtils.getColorFromAttr(view.getContext(), R.attr.wikivoyage_primary_text_color)), + 0, categoryName.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + text.setSpan(new ForegroundColorSpan(ContextCompat.getColor(app, R.color.wikivoyage_secondary_text)), + categoryName.length() + 1, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); + + TextView title = row.findViewById(R.id.label); + title.setText(text); + + ImageView icon = row.findViewById(R.id.icon); + icon.setImageDrawable(getContentIcon(R.drawable.ic_action_folder)); + + boolean expanded = listView.isGroupExpanded(groupPosition); + ImageView expandImage = row.findViewById(R.id.expand_image); + expandImage.setImageDrawable(getContentIcon(expanded ? R.drawable.ic_action_arrow_up : R.drawable.ic_action_arrow_down)); + + final CheckBox checkBox = (CheckBox) row.findViewById(R.id.toggle_item); + if (selectionMode) { + checkBox.setChecked(selectedGroups.contains(groupPosition)); + checkBox.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + List items = itemGroups.get(group); + setGroupSelection(items, groupPosition, checkBox.isChecked()); + adapter.notifyDataSetInvalidated(); + updateSelectionMode(); + } + }); + AndroidUiHelper.updateVisibility(checkBox, true); + } else { + AndroidUiHelper.updateVisibility(checkBox, false); + } + + ImageView options = (ImageView) row.findViewById(R.id.options); + options.setImageDrawable(getContentIcon(R.drawable.ic_overflow_menu_white)); + options.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + EditTrackGroupDialogFragment.showInstance(mapActivity.getSupportFragmentManager(), + group, mapActivity.getTrackMenuFragment()); + } + }); + + AndroidUiHelper.updateVisibility(expandImage, true); + AndroidUiHelper.updateVisibility(row.findViewById(R.id.divider), true); + AndroidUiHelper.updateVisibility(row.findViewById(R.id.description), false); + AndroidUiHelper.updateVisibility(row.findViewById(R.id.list_divider), false); + AndroidUiHelper.updateVisibility(row.findViewById(R.id.group_divider), true); + + return row; + } + + @Override + public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { + View row = convertView; + if (row == null) { + LayoutInflater inflater = LayoutInflater.from(view.getContext()); + row = inflater.inflate(R.layout.wpt_list_item, parent, false); + } + + final GpxDisplayGroup group = getGroup(groupPosition); + final GpxDisplayItem gpxItem = getChild(groupPosition, childPosition); + + TextView title = row.findViewById(R.id.label); + title.setText(gpxItem.name); + + TextView description = row.findViewById(R.id.description); + if (!Algorithms.isEmpty(gpxItem.description)) { + description.setText(gpxItem.description); + AndroidUiHelper.updateVisibility(description, true); + } else { + AndroidUiHelper.updateVisibility(description, false); + } + + final CheckBox checkBox = (CheckBox) row.findViewById(R.id.toggle_item); + if (selectionMode) { + checkBox.setVisibility(View.VISIBLE); + checkBox.setChecked(selectedItems.get(group.getType()) != null && selectedItems.get(group.getType()).contains(gpxItem)); + checkBox.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + if (checkBox.isChecked()) { + Set set = selectedItems.get(group.getType()); + if (set != null) { + set.add(gpxItem); + } else { + set = new LinkedHashSet<>(); + set.add(gpxItem); + selectedItems.put(group.getType(), set); + } + } else { + Set set = selectedItems.get(group.getType()); + if (set != null) { + set.remove(gpxItem); + } + } + updateSelectionMode(); + } + }); + AndroidUiHelper.updateVisibility(checkBox, true); + AndroidUiHelper.updateVisibility(row.findViewById(R.id.icon), false); + } else { + ImageView icon = row.findViewById(R.id.icon); + if (GpxDisplayItemType.TRACK_POINTS == group.getType()) { + WptPt wpt = gpxItem.locationStart; + int groupColor = wpt.getColor(group.getColor()); + if (groupColor == 0) { + groupColor = ContextCompat.getColor(app, R.color.gpx_color_point); + } + icon.setImageDrawable(PointImageDrawable.getFromWpt(app, groupColor, false, wpt)); + } else { + icon.setImageDrawable(getContentIcon(R.drawable.ic_action_marker_dark)); + } + AndroidUiHelper.updateVisibility(icon, true); + AndroidUiHelper.updateVisibility(checkBox, false); + } + + AndroidUiHelper.updateVisibility(row.findViewById(R.id.divider), false); + AndroidUiHelper.updateVisibility(row.findViewById(R.id.options), false); + AndroidUiHelper.updateVisibility(row.findViewById(R.id.list_divider), true); + + return row; + } + + private void setGroupSelection(List items, int groupPosition, boolean select) { + GpxDisplayGroup group = groups.get(groupPosition); + if (select) { + selectedGroups.add(groupPosition); + if (items != null) { + Set set = selectedItems.get(group.getType()); + if (set != null) { + set.addAll(items); + } else { + set = new LinkedHashSet<>(items); + selectedItems.put(group.getType(), set); + } + } + } else { + selectedGroups.remove(groupPosition); + selectedItems.remove(group.getType()); + } + } + + @Override + public Filter getFilter() { + if (pointsFilter == null) { + pointsFilter = new PointsFilter(); + } + return pointsFilter; + } + + public void setFilterResults(Set values) { + this.filteredItems = values; + } + } + + public class PointsFilter extends Filter { + + @Override + protected FilterResults performFiltering(CharSequence constraint) { + FilterResults results = new FilterResults(); + if (constraint == null || constraint.length() == 0) { + results.values = null; + results.count = 1; + } else { + Set filter = new HashSet<>(); + String cs = constraint.toString().toLowerCase(); + List groups = getOriginalGroups(); + if (groups != null) { + for (GpxDisplayGroup g : groups) { + for (GpxDisplayItem i : g.getModifiableList()) { + if (i.name.toLowerCase().contains(cs)) { + filter.add(i); + } else if (i.locationStart != null && !TextUtils.isEmpty(i.locationStart.category) + && i.locationStart.category.toLowerCase().contains(cs)) { + filter.add(i.locationStart.category); + } + } + } + } + results.values = filter; + results.count = filter.size(); + } + return results; + } + + @Override + protected void publishResults(CharSequence constraint, FilterResults results) { + synchronized (adapter) { + adapter.setFilterResults((Set) results.values); + List groups = getOriginalGroups(); + if (groups != null) { + adapter.synchronizeGroups(groups); + } + } + adapter.notifyDataSetChanged(); + expandAllGroups(); + } + } +} \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/views/AddGpxPointBottomSheetHelper.java b/OsmAnd/src/net/osmand/plus/views/AddGpxPointBottomSheetHelper.java index 9408ec5537..a317fece3f 100644 --- a/OsmAnd/src/net/osmand/plus/views/AddGpxPointBottomSheetHelper.java +++ b/OsmAnd/src/net/osmand/plus/views/AddGpxPointBottomSheetHelper.java @@ -6,19 +6,20 @@ import android.view.View; import android.widget.ImageView; import android.widget.TextView; +import net.osmand.GPXUtilities.GPXFile; import net.osmand.data.LatLon; import net.osmand.data.PointDescription; import net.osmand.data.QuadRect; import net.osmand.data.RotatedTileBox; -import net.osmand.GPXUtilities.GPXFile; -import net.osmand.plus.UiUtilities; import net.osmand.plus.R; +import net.osmand.plus.UiUtilities; import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.TrackActivity; import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.mapcontextmenu.editors.RtePtEditor; import net.osmand.plus.mapcontextmenu.editors.WptPtEditor; import net.osmand.plus.mapcontextmenu.editors.WptPtEditor.OnDismissListener; +import net.osmand.plus.track.TrackMenuFragment; import net.osmand.plus.views.layers.ContextMenuLayer; public class AddGpxPointBottomSheetHelper implements OnDismissListener { @@ -73,7 +74,7 @@ public class AddGpxPointBottomSheetHelper implements OnDismissListener { public void onClick(View v) { hide(); contextMenuLayer.cancelAddGpxPoint(); - openTrackActivity(); + onClose(); } }); } @@ -140,7 +141,16 @@ public class AddGpxPointBottomSheetHelper implements OnDismissListener { if (contextMenu.isVisible() && contextMenu.isClosable()) { contextMenu.close(); } - openTrackActivity(); + onClose(); + } + + private void onClose() { + TrackMenuFragment fragment = mapActivity.getTrackMenuFragment(); + if (fragment != null) { + fragment.show(); + } else { + openTrackActivity(); + } } private void openTrackActivity() { diff --git a/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java index 48dcc7fb6e..0fa8db69fb 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/ContextMenuLayer.java @@ -574,7 +574,6 @@ public class ContextMenuLayer extends OsmandMapLayer { public void cancelAddGpxPoint() { cancelApplyingNewMarkerPosition = true; quitAddGpxPoint(); - activity.getContextMenu().show(); applyingMarkerLatLon = null; } diff --git a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java index 688d114d03..339192e6b6 100644 --- a/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java +++ b/OsmAnd/src/net/osmand/plus/views/layers/GPXLayer.java @@ -516,7 +516,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM for (WptPt wpt : getListStarPoints(g)) { if (wpt.lat >= latLonBounds.bottom && wpt.lat <= latLonBounds.top && wpt.lon >= latLonBounds.left && wpt.lon <= latLonBounds.right - && wpt != contextMenuLayer.getMoveableObject()) { + && wpt != contextMenuLayer.getMoveableObject() && !isPointHidden(g, wpt)) { pointFileMap.put(wpt, g); MapMarker marker = null; if (synced) { @@ -777,6 +777,14 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM return g.getGpxFile().getPoints(); } + private boolean isPointHidden(SelectedGpxFile selectedGpxFile, WptPt point) { + if (!Algorithms.isEmpty(selectedGpxFile.getHiddenGroups())) { + return selectedGpxFile.getHiddenGroups().contains(point.category); + } else { + return false; + } + } + private boolean calculateBelongs(int ex, int ey, int objx, int objy, int radius) { return (Math.abs(objx - ex) <= radius && Math.abs(objy - ey) <= radius); } @@ -790,6 +798,9 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM List pts = getListStarPoints(g); // int fcolor = g.getColor() == 0 ? clr : g.getColor(); for (WptPt n : pts) { + if (isPointHidden(g, n)) { + continue; + } int x = (int) tb.getPixXFromLatLon(n.lat, n.lon); int y = (int) tb.getPixYFromLatLon(n.lat, n.lon); if (calculateBelongs(ex, ey, x, y, r)) {