Merge pull request #10655 from osmandapp/points_menu_gpx

Points GPX context menu
This commit is contained in:
vshcherb 2021-01-25 09:17:57 +01:00 committed by GitHub
commit d224fdecaa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1299 additions and 122 deletions

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto" xmlns:osmand="http://schemas.android.com/apk/res-auto"
android:id="@+id/search_container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="56dp" android:layout_height="56dp"
android:focusableInTouchMode="true" android:focusableInTouchMode="true"

View file

@ -118,6 +118,79 @@
</LinearLayout> </LinearLayout>
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/context_menu_toolbar_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="0">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/actionModeBackground"
android:minHeight="@dimen/toolbar_height"
android:padding="0dp"
osmand:contentInsetEnd="0dp"
osmand:contentInsetLeft="0dp"
osmand:contentInsetRight="0dp"
osmand:contentInsetStart="0dp"
osmand:theme="@style/ThemeOverlay.AppCompat.ActionBar">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:minHeight="@dimen/toolbar_height">
<ImageButton
android:id="@+id/close_button"
style="@style/Widget.AppCompat.Toolbar.Button.Navigation"
android:layout_width="@dimen/toolbar_height"
android:layout_height="@dimen/toolbar_height"
android:contentDescription="@string/access_shared_string_navigate_up"
osmand:srcCompat="@drawable/ic_arrow_back" />
<net.osmand.plus.widgets.TextViewEx
android:id="@+id/toolbar_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/content_padding"
android:layout_marginLeft="@dimen/content_padding"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:layout_weight="1"
android:ellipsize="end"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:maxLines="2"
android:textColor="?attr/app_bar_primary_item_color"
android:textSize="@dimen/dialog_header_text_size"
osmand:typeface="@string/font_roboto_medium"
tools:text="@string/routing_settings_2" />
<include
layout="@layout/search_text_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1"
android:visibility="gone" />
<ImageButton
android:id="@+id/search_button"
style="@style/Widget.AppCompat.Toolbar.Button.Navigation"
android:layout_width="@dimen/toolbar_height"
android:layout_height="@dimen/toolbar_height"
android:contentDescription="@string/access_shared_string_navigate_up"
android:visibility="gone"
osmand:srcCompat="@drawable/ic_action_search_dark" />
</LinearLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView <com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation" android:id="@+id/bottom_navigation"
android:layout_width="match_parent" android:layout_width="match_parent"

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ExpandableListView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="0dp"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:layout_marginEnd="0dp"
android:layout_marginRight="0dp"
android:divider="@null"
android:dividerHeight="0dp"
android:drawSelectorOnTop="false"
android:groupIndicator="@android:color/transparent" />
</LinearLayout>

View file

@ -12,6 +12,9 @@
--> -->
<string name="copy_to_map_favorites">Copy to favorites</string>
<string name="copy_to_map_markers">Copy to map markers</string>
<string name="delete_waypoints">Delete waypoints</string>
<string name="context_menu_edit_descr">Edit description</string> <string name="context_menu_edit_descr">Edit description</string>
<string name="context_menu_read_full">Read full</string> <string name="context_menu_read_full">Read full</string>
<string name="delete_online_routing_engine">Delete this online routing engine?</string> <string name="delete_online_routing_engine">Delete this online routing engine?</string>

View file

@ -1,5 +1,6 @@
package net.osmand; package net.osmand;
import android.os.AsyncTask;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -8,6 +9,8 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager; 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;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
@ -109,6 +112,8 @@ public class FileUtils {
selected.getGpxFile().path = dest.getAbsolutePath(); selected.getGpxFile().path = dest.getAbsolutePath();
helper.updateSelectedGpxFile(selected); helper.updateSelectedGpxFile(selected);
} }
RenameGpxAsyncTask renameGpxAsyncTask = new RenameGpxAsyncTask(app, dest);
renameGpxAsyncTask.execute();
return dest; return dest;
} }
return null; return null;
@ -196,4 +201,34 @@ public class FileUtils {
public interface RenameCallback { public interface RenameCallback {
void renamedTo(File file); void renamedTo(File file);
} }
private static class RenameGpxAsyncTask extends AsyncTask<Void, Void, Exception> {
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);
}
}
} }

View file

@ -45,10 +45,12 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer;
public class GpxSelectionHelper { public class GpxSelectionHelper {
@ -60,6 +62,7 @@ public class GpxSelectionHelper {
private static final String BACKUPMODIFIEDTIME = "backupTime"; private static final String BACKUPMODIFIEDTIME = "backupTime";
private static final String COLOR = "color"; private static final String COLOR = "color";
private static final String SELECTED_BY_USER = "selected_by_user"; private static final String SELECTED_BY_USER = "selected_by_user";
private static final String HIDDEN_GROUPS = "hidden_groups";
private OsmandApplication app; private OsmandApplication app;
private SavingTrackHelper savingTrackHelper; private SavingTrackHelper savingTrackHelper;
@ -577,7 +580,10 @@ public class GpxSelectionHelper {
} else if (obj.has(BACKUP)) { } else if (obj.has(BACKUP)) {
selectedGpxFilesBackUp.put(gpx, gpx.modifiedTime); selectedGpxFilesBackUp.put(gpx, gpx.modifiedTime);
} else { } 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(); gpx.addGeneralTrack();
} else if (obj.has(CURRENT_TRACK)) { } else if (obj.has(CURRENT_TRACK)) {
@ -598,6 +604,33 @@ public class GpxSelectionHelper {
} }
} }
private String saveHiddenGroups(SelectedGpxFile selectedGpxFile) {
StringBuilder stringBuilder = new StringBuilder();
Iterator<String> 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<String> 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) { private int parseColor(String color) {
try { try {
return Algorithms.isEmpty(color) ? 0 : Algorithms.parseColor(color); return Algorithms.isEmpty(color) ? 0 : Algorithms.parseColor(color);
@ -619,6 +652,7 @@ public class GpxSelectionHelper {
if (s.gpxFile.getColor(0) != 0) { if (s.gpxFile.getColor(0) != 0) {
obj.put(COLOR, Algorithms.colorToString(s.gpxFile.getColor(0))); obj.put(COLOR, Algorithms.colorToString(s.gpxFile.getColor(0)));
} }
obj.put(HIDDEN_GROUPS, saveHiddenGroups(s));
} }
obj.put(SELECTED_BY_USER, s.selectedByUser); obj.put(SELECTED_BY_USER, s.selectedByUser);
} catch (JSONException e) { } catch (JSONException e) {
@ -765,6 +799,7 @@ public class GpxSelectionHelper {
private GPXFile gpxFile; private GPXFile gpxFile;
private GPXTrackAnalysis trackAnalysis; private GPXTrackAnalysis trackAnalysis;
private Set<String> hiddenGroups = new HashSet<>();
private List<TrkSegment> processedPointsToDisplay = new ArrayList<>(); private List<TrkSegment> processedPointsToDisplay = new ArrayList<>();
private List<GpxDisplayGroup> displayGroups; private List<GpxDisplayGroup> displayGroups;
@ -832,6 +867,18 @@ public class GpxSelectionHelper {
return processedPointsToDisplay; return processedPointsToDisplay;
} }
public Set<String> getHiddenGroups() {
return Collections.unmodifiableSet(hiddenGroups);
}
public void addHiddenGroups(String group) {
hiddenGroups.add(group);
}
public void removeHiddenGroups(String group) {
hiddenGroups.remove(group);
}
public GPXFile getGpxFile() { public GPXFile getGpxFile() {
return gpxFile; return gpxFile;
} }

View file

@ -62,6 +62,8 @@ public class RenameFileBottomSheet extends MenuBottomSheetDialogFragment {
file = new File(path); file = new File(path);
} }
selectedFileName = savedInstanceState.getString(SELECTED_FILE_NAME_KEY); selectedFileName = savedInstanceState.getString(SELECTED_FILE_NAME_KEY);
} else {
selectedFileName = Algorithms.getFileNameWithoutExtension(file);
} }
items.add(new TitleItem(getString(R.string.shared_string_rename))); items.add(new TitleItem(getString(R.string.shared_string_rename)));
@ -74,7 +76,7 @@ public class RenameFileBottomSheet extends MenuBottomSheetDialogFragment {
nameTextBox.setDefaultHintTextColor(colorStateList); nameTextBox.setDefaultHintTextColor(colorStateList);
editText = view.findViewById(R.id.name_edit_text); editText = view.findViewById(R.id.name_edit_text);
editText.setText(selectedFileName != null ? selectedFileName : Algorithms.getFileNameWithoutExtension(file)); editText.setText(selectedFileName);
editText.addTextChangedListener(new TextWatcher() { editText.addTextChangedListener(new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@ -127,12 +129,13 @@ public class RenameFileBottomSheet extends MenuBottomSheetDialogFragment {
File dest; File dest;
int index = file.getName().lastIndexOf('.'); int index = file.getName().lastIndexOf('.');
String ext = index == -1 ? "" : file.getName().substring(index); String ext = index == -1 ? "" : file.getName().substring(index);
String newName = Algorithms.getFileNameWithoutExtension(selectedFileName);
if (SQLiteTileSource.EXT.equals(ext)) { 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)) { } 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 { } else {
dest = renameFile(app, file, selectedFileName + ext, false, null); dest = renameFile(app, file, newName + ext, false, null);
} }
if (dest != null) { if (dest != null) {
Fragment fragment = getTargetFragment(); Fragment fragment = getTargetFragment();

View file

@ -15,14 +15,14 @@ import java.io.File;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.Set; import java.util.Set;
class DeletePointsTask extends AsyncTask<Void, Void, Void> { public class DeletePointsTask extends AsyncTask<Void, Void, Void> {
private OsmandApplication app; private OsmandApplication app;
private GPXFile gpx; private GPXFile gpx;
private Set<GpxDisplayItem> selectedItems; private Set<GpxDisplayItem> selectedItems;
private WeakReference<OnPointsDeleteListener> listenerRef; private WeakReference<OnPointsDeleteListener> listenerRef;
DeletePointsTask(OsmandApplication app, GPXFile gpxFile, Set<GpxDisplayItem> selectedItems, OnPointsDeleteListener listener) { public DeletePointsTask(OsmandApplication app, GPXFile gpxFile, Set<GpxDisplayItem> selectedItems, OnPointsDeleteListener listener) {
this.app = app; this.app = app;
this.gpx = gpxFile; this.gpx = gpxFile;
this.selectedItems = selectedItems; this.selectedItems = selectedItems;

View file

@ -1,15 +1,16 @@
package net.osmand.plus.myplaces; package net.osmand.plus.myplaces;
import android.app.Activity;
import android.app.ProgressDialog; import android.app.ProgressDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.graphics.Typeface;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Bundle; import android.os.Bundle;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.ContextThemeWrapper; import android.view.ContextThemeWrapper;
import android.view.Gravity; import android.view.Gravity;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.EditText; import android.widget.EditText;
@ -17,56 +18,127 @@ import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.ListPopupWindow; import androidx.appcompat.widget.ListPopupWindow;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
import net.osmand.GPXUtilities; import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.WptPt; 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.GpxDisplayGroup;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.mapmarkers.MapMarkersHelper; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.EditFavoriteGroupDialogFragment.FavoriteColorAdapter; import net.osmand.plus.activities.EditFavoriteGroupDialogFragment.FavoriteColorAdapter;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.activities.SavingTrackHelper; import net.osmand.plus.activities.SavingTrackHelper;
import net.osmand.plus.activities.TrackActivity; import net.osmand.plus.activities.TrackActivity;
import net.osmand.plus.base.MenuBottomSheetDialogFragment; import net.osmand.plus.base.MenuBottomSheetDialogFragment;
import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem; 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.SimpleBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem; import net.osmand.plus.base.bottomsheetmenu.simpleitems.TitleItem;
import net.osmand.plus.helpers.AndroidUiHelper; 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 net.osmand.util.Algorithms;
import java.io.File; import java.io.File;
import java.lang.ref.WeakReference; import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.List; 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(); public static final String TAG = EditTrackGroupDialogFragment.class.getSimpleName();
private OsmandApplication app;
private GpxDisplayGroup group; private GpxDisplayGroup group;
@Override @Override
public void createMenuItems(Bundle savedInstanceState) { public void createMenuItems(Bundle savedInstanceState) {
final OsmandApplication app = getMyApplication(); app = requiredMyApplication();
if (group == null) { if (group == null) {
return; return;
} }
items.add(new TitleItem(getCategoryName(app, group.getName()))); items.add(new TitleItem(getCategoryName(app, group.getName())));
BaseBottomSheetItem editNameItem = new SimpleBottomSheetItem.Builder() SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(group.getGpx().path);
.setIcon(getContentIcon(R.drawable.ic_action_edit_dark)) if (group.getType() == GpxDisplayItemType.TRACK_POINTS && selectedGpxFile != null) {
.setTitle(getString(R.string.edit_name)) items.add(createShowOnMapItem(selectedGpxFile));
.setLayoutId(R.layout.bottom_sheet_item_simple) }
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() { .setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { 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) { if (activity != null) {
AlertDialog.Builder b = new AlertDialog.Builder(activity); AlertDialog.Builder b = new AlertDialog.Builder(activity);
b.setTitle(R.string.favorite_group_name); b.setTitle(R.string.favorite_group_name);
@ -86,12 +158,9 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment
String name = nameEditText.getText().toString(); String name = nameEditText.getText().toString();
boolean nameChanged = !Algorithms.objectEquals(group.getName(), name); boolean nameChanged = !Algorithms.objectEquals(group.getName(), name);
if (nameChanged) { if (nameChanged) {
TrackActivity trackActivity = getTrackActivity(); new UpdateGpxCategoryTask(activity, group, name)
if (trackActivity != null) {
new UpdateGpxCategoryTask(trackActivity, group, name)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
}
dismiss(); dismiss();
} }
}); });
@ -100,20 +169,118 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment
} }
}) })
.create(); .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<GpxDisplayItem> 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 int themeRes = nightMode ? R.style.OsmandDarkTheme : R.style.OsmandLightTheme;
final View changeColorView = View.inflate(new ContextThemeWrapper(getContext(), themeRes), final View changeColorView = View.inflate(new ContextThemeWrapper(getContext(), themeRes),
R.layout.change_fav_color, null); R.layout.change_fav_color, null);
((ImageView) changeColorView.findViewById(R.id.change_color_icon)) ((ImageView) changeColorView.findViewById(R.id.change_color_icon))
.setImageDrawable(getContentIcon(R.drawable.ic_action_appearance)); .setImageDrawable(getContentIcon(R.drawable.ic_action_appearance));
updateColorView((ImageView) changeColorView.findViewById(R.id.colorImage)); updateColorView((ImageView) changeColorView.findViewById(R.id.colorImage));
BaseBottomSheetItem changeColorItem = new BaseBottomSheetItem.Builder() return new BaseBottomSheetItem.Builder()
.setCustomView(changeColorView) .setCustomView(changeColorView)
.setOnClickListener(new View.OnClickListener() { .setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
Activity activity = getActivity(); final FragmentActivity activity = getActivity();
if (activity != null) { if (activity != null) {
final ListPopupWindow popup = new ListPopupWindow(activity); final ListPopupWindow popup = new ListPopupWindow(activity);
popup.setAnchorView(v); popup.setAnchorView(v);
@ -135,13 +302,10 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment
Integer color = colorAdapter.getItem(position); Integer color = colorAdapter.getItem(position);
if (color != null) { if (color != null) {
if (color != group.getColor()) { if (color != group.getColor()) {
TrackActivity trackActivity = getTrackActivity(); new UpdateGpxCategoryTask(activity, group, color)
if (trackActivity != null) {
new UpdateGpxCategoryTask(trackActivity, group, color)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
} }
}
popup.dismiss(); popup.dismiss();
dismiss(); dismiss();
} }
@ -151,8 +315,6 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment
} }
}) })
.create(); .create();
items.add(changeColorItem);
} }
@Override @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) { private static String getCategoryName(@NonNull Context ctx, String category) {
return Algorithms.isEmpty(category) ? ctx.getString(R.string.shared_string_waypoints) : 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) { if (color == 0) {
colorImageView.setImageDrawable(getContentIcon(R.drawable.ic_action_circle)); colorImageView.setImageDrawable(getContentIcon(R.drawable.ic_action_circle));
} else { } 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) { public static void showInstance(FragmentManager fragmentManager, GpxDisplayGroup group, Fragment target) {
EditTrackGroupDialogFragment f = (EditTrackGroupDialogFragment) fragmentManager if (!fragmentManager.isStateSaved() && fragmentManager.findFragmentByTag(TAG) == null) {
.findFragmentByTag(EditTrackGroupDialogFragment.TAG); EditTrackGroupDialogFragment fragment = new EditTrackGroupDialogFragment();
if (f == null ) { fragment.group = group;
f = new EditTrackGroupDialogFragment(); fragment.setRetainInstance(true);
f.group = group; fragment.setTargetFragment(target, 0);
f.show(fragmentManager, EditTrackGroupDialogFragment.TAG); 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<Void, Void, Void> { private static class UpdateGpxCategoryTask extends AsyncTask<Void, Void, Void> {
private OsmandApplication app; private OsmandApplication app;
private WeakReference<TrackActivity> activityRef; private WeakReference<FragmentActivity> activityRef;
private GpxDisplayGroup group; private GpxDisplayGroup group;
@ -208,20 +375,20 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment
private ProgressDialog progressDialog; private ProgressDialog progressDialog;
private boolean wasUpdated = false; 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(); this.app = (OsmandApplication) activity.getApplication();
activityRef = new WeakReference<>(activity); activityRef = new WeakReference<>(activity);
this.group = group; this.group = group;
} }
UpdateGpxCategoryTask(@NonNull TrackActivity activity, @NonNull GpxDisplayGroup group, UpdateGpxCategoryTask(@NonNull FragmentActivity activity, @NonNull GpxDisplayGroup group,
@NonNull String newCategory) { @NonNull String newCategory) {
this(activity, group); this(activity, group);
this.newCategory = newCategory; this.newCategory = newCategory;
} }
UpdateGpxCategoryTask(@NonNull TrackActivity activity, @NonNull GpxDisplayGroup group, UpdateGpxCategoryTask(@NonNull FragmentActivity activity, @NonNull GpxDisplayGroup group,
@NonNull Integer newColor) { @NonNull Integer newColor) {
this(activity, group); this(activity, group);
this.newColor = newColor; this.newColor = newColor;
@ -229,7 +396,7 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment
@Override @Override
protected void onPreExecute() { protected void onPreExecute() {
TrackActivity activity = activityRef.get(); FragmentActivity activity = activityRef.get();
if (activity != null) { if (activity != null) {
progressDialog = new ProgressDialog(activity); progressDialog = new ProgressDialog(activity);
progressDialog.setTitle(EditTrackGroupDialogFragment.getCategoryName(app, group.getName())); progressDialog.setTitle(EditTrackGroupDialogFragment.getCategoryName(app, group.getName()));
@ -293,9 +460,15 @@ public class EditTrackGroupDialogFragment extends MenuBottomSheetDialogFragment
progressDialog.dismiss(); progressDialog.dismiss();
} }
TrackActivity activity = activityRef.get(); FragmentActivity activity = activityRef.get();
if (activity != null) { if (activity instanceof TrackActivity) {
activity.loadGpx(); ((TrackActivity) activity).loadGpx();
} else if (activity instanceof MapActivity) {
MapActivity mapActivity = (MapActivity) activity;
TrackMenuFragment fragment = mapActivity.getTrackMenuFragment();
if (fragment != null) {
fragment.updateContent();
}
} }
} }

View file

@ -1007,7 +1007,7 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
public void onClick(View v) { public void onClick(View v) {
FragmentActivity activity = getActivity(); FragmentActivity activity = getActivity();
if (activity != null) { if (activity != null) {
EditTrackGroupDialogFragment.showInstance(activity.getSupportFragmentManager(), group); EditTrackGroupDialogFragment.showInstance(activity.getSupportFragmentManager(), group, null);
} }
} }
}); });

View file

@ -122,14 +122,14 @@ public class BooleanPreferenceBottomSheet extends BasePreferenceBottomSheet {
return R.string.shared_string_close; 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); View customView = UiUtilities.getInflater(app, nightMode).inflate(R.layout.bottom_sheet_item_preference_switch, null);
updateCustomButtonView(app, mode, customView, checked, nightMode); updateCustomButtonView(app, mode, customView, checked, nightMode);
return customView; 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); Context themedCtx = UiUtilities.getThemedContext(app, nightMode);
View buttonView = customView.findViewById(R.id.button_container); View buttonView = customView.findViewById(R.id.button_container);

View file

@ -22,7 +22,6 @@ public class SegmentsCard extends BaseCard {
private TrackDisplayHelper displayHelper; private TrackDisplayHelper displayHelper;
private GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT}; private GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT};
private SegmentGPXAdapter adapter;
private SegmentActionsListener listener; private SegmentActionsListener listener;
public SegmentsCard(@NonNull MapActivity mapActivity, @NonNull TrackDisplayHelper displayHelper, public SegmentsCard(@NonNull MapActivity mapActivity, @NonNull TrackDisplayHelper displayHelper,

View file

@ -112,10 +112,16 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement
return 0; return 0;
} }
@Override
public float getMiddleStateKoef() { public float getMiddleStateKoef() {
return 0.5f; return 0.5f;
} }
@Override
public int getInitialMenuState() {
return MenuState.HALF_SCREEN;
}
public TrackDrawInfo getTrackDrawInfo() { public TrackDrawInfo getTrackDrawInfo() {
return trackDrawInfo; return trackDrawInfo;
} }

View file

@ -1,14 +1,9 @@
package net.osmand.plus.track; package net.osmand.plus.track;
import android.app.Activity;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import net.osmand.GPXUtilities.GPXFile; 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.data.QuadRect;
import net.osmand.plus.GPXDatabase.GpxDataItem; import net.osmand.plus.GPXDatabase.GpxDataItem;
import net.osmand.plus.GpxSelectionHelper; 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.GpxDisplayItemType;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.OsmandApplication; 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.io.File;
import java.util.ArrayList; import java.util.ArrayList;
@ -163,21 +154,4 @@ public class TrackDisplayHelper {
} }
return list; 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);
}
}
} }

View file

@ -1,21 +1,27 @@
package net.osmand.plus.track; package net.osmand.plus.track;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.os.AsyncTask; import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.EditText;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
@ -33,6 +39,7 @@ import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.Location; import net.osmand.Location;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.data.PointDescription;
import net.osmand.data.QuadRect; import net.osmand.data.QuadRect;
import net.osmand.data.RotatedTileBox; import net.osmand.data.RotatedTileBox;
import net.osmand.plus.GpxDbHelper; 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.MeasurementEditingContext;
import net.osmand.plus.measurementtool.MeasurementToolFragment; import net.osmand.plus.measurementtool.MeasurementToolFragment;
import net.osmand.plus.myplaces.AvailableGPXFragment.GpxInfo; 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;
import net.osmand.plus.myplaces.MoveGpxFileBottomSheet.OnTrackFileMoveListener; import net.osmand.plus.myplaces.MoveGpxFileBottomSheet.OnTrackFileMoveListener;
import net.osmand.plus.myplaces.SegmentActionsListener; 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;
import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener; import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener;
import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener; import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener;
import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint;
import net.osmand.plus.widgets.IconPopupMenu; import net.osmand.plus.widgets.IconPopupMenu;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils; 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.SHARE_BUTTON_INDEX;
import static net.osmand.plus.track.OptionsCard.SHOW_ON_MAP_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.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, 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(); public static final String TAG = TrackMenuFragment.class.getName();
private static final Log log = PlatformUtil.getLog(TrackMenuFragment.class); private static final Log log = PlatformUtil.getLog(TrackMenuFragment.class);
@ -105,37 +117,46 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
private TrackDisplayHelper displayHelper; private TrackDisplayHelper displayHelper;
private SelectedGpxFile selectedGpxFile; private SelectedGpxFile selectedGpxFile;
private View routeMenuTopShadowAll;
private TextView headerTitle;
private ImageView headerIcon;
private BottomNavigationView bottomNav;
private TrackMenuType menuType = TrackMenuType.OVERVIEW; private TrackMenuType menuType = TrackMenuType.OVERVIEW;
private SegmentsCard segmentsCard; private SegmentsCard segmentsCard;
private OptionsCard optionsCard; private OptionsCard optionsCard;
private DescriptionCard descriptionCard; private DescriptionCard descriptionCard;
private OverviewCard overviewCard; 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 TrackChartPoints trackChartPoints;
private int menuTitleHeight;
private String gpxTitle;
private UpdateLocationViewCache updateLocationViewCache;
private Location lastLocation = null;
private Float heading; private Float heading;
private Location lastLocation;
private UpdateLocationViewCache updateLocationViewCache;
private boolean locationUpdateStarted; private boolean locationUpdateStarted;
private int menuTitleHeight;
private int toolbarHeightPx;
public enum TrackMenuType { public enum TrackMenuType {
OVERVIEW(R.id.action_overview, R.string.shared_string_overview), OVERVIEW(R.id.action_overview, R.string.shared_string_overview),
TRACK(R.id.action_track, R.string.shared_string_gpx_tracks), TRACK(R.id.action_track, R.string.shared_string_gpx_tracks),
POINTS(R.id.action_points, R.string.shared_string_gpx_points), POINTS(R.id.action_points, R.string.shared_string_gpx_points),
OPTIONS(R.id.action_options, R.string.shared_string_options); OPTIONS(R.id.action_options, R.string.shared_string_options);
TrackMenuType(@DrawableRes int iconId, @StringRes int titleId) { TrackMenuType(int menuItemId, @StringRes int titleId) {
this.iconId = iconId; this.menuItemId = menuItemId;
this.titleId = titleId; this.titleId = titleId;
} }
public final int iconId; public final int menuItemId;
public final int titleId; public final int titleId;
} }
@ -156,7 +177,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
@Override @Override
public int getToolbarHeight() { public int getToolbarHeight() {
return 0; return toolbarHeightPx;
} }
public float getMiddleStateKoef() { public float getMiddleStateKoef() {
@ -183,6 +204,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
app = requireMyApplication(); app = requireMyApplication();
GpxDbHelper gpxDbHelper = app.getGpxDbHelper(); GpxDbHelper gpxDbHelper = app.getGpxDbHelper();
displayHelper = new TrackDisplayHelper(app); displayHelper = new TrackDisplayHelper(app);
updateLocationViewCache = app.getUIUtilities().getUpdateLocationViewCache();
Bundle arguments = getArguments(); Bundle arguments = getArguments();
if (arguments != null) { if (arguments != null) {
@ -200,6 +222,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
String fileName = Algorithms.getFileWithoutDirs(getGpx().path); String fileName = Algorithms.getFileWithoutDirs(getGpx().path);
gpxTitle = GpxUiHelper.getGpxTitle(fileName); gpxTitle = GpxUiHelper.getGpxTitle(fileName);
} }
toolbarHeightPx = getResources().getDimensionPixelSize(R.dimen.dashboard_map_toolbar);
} }
public GPXFile getGpx() { public GPXFile getGpx() {
@ -214,7 +237,10 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
routeMenuTopShadowAll = view.findViewById(R.id.route_menu_top_shadow_all); routeMenuTopShadowAll = view.findViewById(R.id.route_menu_top_shadow_all);
headerTitle = view.findViewById(R.id.title); headerTitle = view.findViewById(R.id.title);
headerIcon = view.findViewById(R.id.icon_view); 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()) { if (isPortrait()) {
AndroidUiHelper.updateVisibility(getTopShadow(), true); AndroidUiHelper.updateVisibility(getTopShadow(), true);
@ -226,6 +252,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
} }
setupCards(); setupCards();
setupToolbar();
updateHeader(); updateHeader();
setupButtons(view); setupButtons(view);
enterTrackAppearanceMode(); enterTrackAppearanceMode();
@ -261,6 +288,74 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
boolean isOptions = menuType == TrackMenuType.OPTIONS; boolean isOptions = menuType == TrackMenuType.OPTIONS;
setHeaderTitle(isOptions ? app.getString(menuType.titleId) : gpxTitle, !isOptions); 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() { private void setupCards() {
@ -296,13 +391,25 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
if (descriptionCard != null && descriptionCard.getView() != null) { if (descriptionCard != null && descriptionCard.getView() != null) {
ViewGroup parent = ((ViewGroup) descriptionCard.getView().getParent()); ViewGroup parent = ((ViewGroup) descriptionCard.getView().getParent());
if (parent != null) { if (parent != null) {
cardsContainer.removeView(descriptionCard.getView()); parent.removeView(descriptionCard.getView());
} }
cardsContainer.addView(descriptionCard.getView()); cardsContainer.addView(descriptionCard.getView());
} else { } else {
descriptionCard = new DescriptionCard(getMapActivity(), displayHelper.getGpx()); descriptionCard = new DescriptionCard(getMapActivity(), displayHelper.getGpx());
cardsContainer.addView(descriptionCard.build(mapActivity)); 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) { protected void setViewY(int y, boolean animated, boolean adjustMapPos) {
super.setViewY(y, animated, adjustMapPos); super.setViewY(y, animated, adjustMapPos);
updateStatusBarColor(); updateStatusBarColor();
updateToolbar(y, animated);
} }
@Override @Override
protected void updateMainViewLayout(int posY) { protected void updateMainViewLayout(int posY) {
super.updateMainViewLayout(posY); super.updateMainViewLayout(posY);
updateStatusBarColor(); updateStatusBarColor();
updateToolbar(posY, true);
} }
@Override @Override
@ -484,7 +593,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
if (Build.VERSION.SDK_INT >= 23 && !nightMode) { if (Build.VERSION.SDK_INT >= 23 && !nightMode) {
view.setSystemUiVisibility(view.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); 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 { } else {
if (Build.VERSION.SDK_INT >= 23 && !nightMode) { if (Build.VERSION.SDK_INT >= 23 && !nightMode) {
view.setSystemUiVisibility(view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR); view.setSystemUiVisibility(view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
@ -522,8 +631,8 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
if (mapActivity == null) { if (mapActivity == null) {
return; return;
} }
if (card instanceof OptionsCard || card instanceof OverviewCard) {
final GPXFile gpxFile = getGpx(); final GPXFile gpxFile = getGpx();
if (card instanceof OptionsCard || card instanceof OverviewCard) {
if (buttonIndex == SHOW_ON_MAP_BUTTON_INDEX) { if (buttonIndex == SHOW_ON_MAP_BUTTON_INDEX) {
boolean gpxFileSelected = !isGpxFileSelected(app, gpxFile); boolean gpxFileSelected = !isGpxFileSelected(app, gpxFile);
app.getSelectedGpxHelper().selectGpxFile(gpxFile, gpxFileSelected, false); app.getSelectedGpxHelper().selectGpxFile(gpxFile, gpxFileSelected, false);
@ -615,6 +724,25 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
}); });
builder.show(); 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);
}
}
} }
} }
@ -627,6 +755,34 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
return y; 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 @Override
protected void onHeaderClick() { protected void onHeaderClick() {
adjustMapPosition(getViewY()); adjustMapPosition(getViewY());
@ -664,7 +820,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
@Override @Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) { public boolean onNavigationItemSelected(@NonNull MenuItem item) {
for (TrackMenuType type : TrackMenuType.values()) { for (TrackMenuType type : TrackMenuType.values()) {
if (type.iconId == item.getItemId()) { if (type.menuItemId == item.getItemId()) {
menuType = type; menuType = type;
setupCards(); setupCards();
updateHeader(); updateHeader();
@ -687,6 +843,9 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
if (descriptionCard != null) { if (descriptionCard != null) {
descriptionCard.updateContent(); descriptionCard.updateContent();
} }
if (pointsCard != null) {
pointsCard.updateContent();
}
setupCards(); setupCards();
} }
@ -700,6 +859,18 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
} }
@Override
public void onPointsDeletionStarted() {
}
@Override
public void onPointsDeleted() {
if (pointsCard != null) {
pointsCard.onPointsDeleted();
}
}
@Override @Override
public void onPointSelected(TrkSegment segment, double lat, double lon) { public void onPointSelected(TrkSegment segment, double lat, double lon) {
if (trackChartPoints == null) { if (trackChartPoints == null) {
@ -729,7 +900,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
TrackDetailsMenu trackDetailsMenu = getMapActivity().getTrackDetailsMenu(); TrackDetailsMenu trackDetailsMenu = getMapActivity().getTrackDetailsMenu();
trackDetailsMenu.setGpxItem(gpxItem); trackDetailsMenu.setGpxItem(gpxItem);
trackDetailsMenu.show(); trackDetailsMenu.show();
close(); hide();
} }
@Override @Override
@ -783,7 +954,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
private void editSegment(TrkSegment segment) { private void editSegment(TrkSegment segment) {
GPXFile gpxFile = getGpx(); GPXFile gpxFile = getGpx();
openPlanRoute(new GpxData(gpxFile)); openPlanRoute(new GpxData(gpxFile));
close(); hide();
} }
public void openPlanRoute(GpxData gpxData) { public void openPlanRoute(GpxData gpxData) {
@ -834,12 +1005,24 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
private void close() { private void hide() {
try { try {
MapActivity mapActivity = getMapActivity(); MapActivity mapActivity = getMapActivity();
if (mapActivity != null) { if (mapActivity != null) {
FragmentManager fragmentManager = mapActivity.getSupportFragmentManager(); 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) { } catch (Exception e) {
log.error(e); log.error(e);
@ -859,7 +1042,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
mapActivity.getSupportFragmentManager() mapActivity.getSupportFragmentManager()
.beginTransaction() .beginTransaction()
.replace(R.id.fragmentContainer, fragment, fragment.getFragmentTag()) .replace(R.id.fragmentContainer, fragment, TAG)
.addToBackStack(fragment.getFragmentTag()) .addToBackStack(fragment.getFragmentTag())
.commitAllowingStateLoss(); .commitAllowingStateLoss();
return true; return true;

View file

@ -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<Integer> selectedGroups = new LinkedHashSet<>();
private final LinkedHashMap<GpxDisplayItemType, Set<GpxDisplayItem>> 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<GpxDisplayGroup> 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<GpxDisplayItem> getSelectedItems() {
Set<GpxDisplayItem> result = new LinkedHashSet<>();
for (Set<GpxDisplayItem> 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<GpxDisplayItem> 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<GpxDisplayGroup> groups = new ArrayList<>();
private final Map<GpxDisplayGroup, List<GpxDisplayItem>> itemGroups = new LinkedHashMap<>();
private final Comparator<String> comparator;
private Filter pointsFilter;
private Set<?> filteredItems;
PointGPXAdapter() {
final Collator collator = OsmAndCollator.primaryCollator();
comparator = new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return collator.compare(s1, s2);
}
};
}
public void synchronizeGroups(@NonNull List<GpxDisplayGroup> displayGroups) {
groups.clear();
itemGroups.clear();
Set<?> filtered = filteredItems;
Collections.sort(displayGroups, new Comparator<GpxDisplayGroup>() {
@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<GpxDisplayGroup> trackPointsGroups = new ArrayList<>();
List<GpxDisplayGroup> 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<GpxDisplayGroup> displayGroups, Set<?> filteredItems) {
for (int i = 0; i < displayGroups.size(); i++) {
GpxDisplayGroup group = displayGroups.get(i);
if (group.getModifiableList().isEmpty()) {
continue;
}
Map<String, List<GpxDisplayItem>> itemsMap = collectItemsByCategory(group, i);
if (filteredItems != null) {
itemsMap = filterItems(itemsMap, filteredItems);
}
if (!Algorithms.isEmpty(itemsMap)) {
setCollectedItems(group, itemsMap);
}
}
}
private Map<String, List<GpxDisplayItem>> collectItemsByCategory(GpxDisplayGroup group, int index) {
Map<String, List<GpxDisplayItem>> 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<GpxDisplayItem> items = itemsMap.get(category);
if (items == null) {
items = new ArrayList<>();
itemsMap.put(category, items);
}
items.add(item);
}
return itemsMap;
}
private Map<String, List<GpxDisplayItem>> filterItems(Map<String, List<GpxDisplayItem>> itemsMap, Set<?> filteredItems) {
Map<String, List<GpxDisplayItem>> itemsMapFiltered = new HashMap<>();
for (Entry<String, List<GpxDisplayItem>> e : itemsMap.entrySet()) {
String category = e.getKey();
List<GpxDisplayItem> items = e.getValue();
if (filteredItems.contains(category)) {
itemsMapFiltered.put(category, items);
} else {
for (GpxDisplayItem i : items) {
if (filteredItems.contains(i)) {
List<GpxDisplayItem> 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<String, List<GpxDisplayItem>> itemsMap) {
List<String> categories = new ArrayList<>(itemsMap.keySet());
Collections.sort(categories, comparator);
for (String category : categories) {
List<GpxDisplayItem> 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<GpxDisplayItem> 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<GpxDisplayItem> 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<GpxDisplayItem> 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<GpxDisplayItem> 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<GpxDisplayItem> items, int groupPosition, boolean select) {
GpxDisplayGroup group = groups.get(groupPosition);
if (select) {
selectedGroups.add(groupPosition);
if (items != null) {
Set<GpxDisplayItem> 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<Object> filter = new HashSet<>();
String cs = constraint.toString().toLowerCase();
List<GpxDisplayGroup> 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<GpxDisplayGroup> groups = getOriginalGroups();
if (groups != null) {
adapter.synchronizeGroups(groups);
}
}
adapter.notifyDataSetChanged();
expandAllGroups();
}
}
}

View file

@ -6,19 +6,20 @@ import android.view.View;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.data.LatLon; import net.osmand.data.LatLon;
import net.osmand.data.PointDescription; import net.osmand.data.PointDescription;
import net.osmand.data.QuadRect; import net.osmand.data.QuadRect;
import net.osmand.data.RotatedTileBox; import net.osmand.data.RotatedTileBox;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.activities.TrackActivity; import net.osmand.plus.activities.TrackActivity;
import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.mapcontextmenu.MapContextMenu;
import net.osmand.plus.mapcontextmenu.editors.RtePtEditor; import net.osmand.plus.mapcontextmenu.editors.RtePtEditor;
import net.osmand.plus.mapcontextmenu.editors.WptPtEditor; import net.osmand.plus.mapcontextmenu.editors.WptPtEditor;
import net.osmand.plus.mapcontextmenu.editors.WptPtEditor.OnDismissListener; import net.osmand.plus.mapcontextmenu.editors.WptPtEditor.OnDismissListener;
import net.osmand.plus.track.TrackMenuFragment;
import net.osmand.plus.views.layers.ContextMenuLayer; import net.osmand.plus.views.layers.ContextMenuLayer;
public class AddGpxPointBottomSheetHelper implements OnDismissListener { public class AddGpxPointBottomSheetHelper implements OnDismissListener {
@ -73,7 +74,7 @@ public class AddGpxPointBottomSheetHelper implements OnDismissListener {
public void onClick(View v) { public void onClick(View v) {
hide(); hide();
contextMenuLayer.cancelAddGpxPoint(); contextMenuLayer.cancelAddGpxPoint();
openTrackActivity(); onClose();
} }
}); });
} }
@ -140,8 +141,17 @@ public class AddGpxPointBottomSheetHelper implements OnDismissListener {
if (contextMenu.isVisible() && contextMenu.isClosable()) { if (contextMenu.isVisible() && contextMenu.isClosable()) {
contextMenu.close(); contextMenu.close();
} }
onClose();
}
private void onClose() {
TrackMenuFragment fragment = mapActivity.getTrackMenuFragment();
if (fragment != null) {
fragment.show();
} else {
openTrackActivity(); openTrackActivity();
} }
}
private void openTrackActivity() { private void openTrackActivity() {
Intent newIntent = new Intent(mapActivity, mapActivity.getMyApplication().getAppCustomization().getTrackActivity()); Intent newIntent = new Intent(mapActivity, mapActivity.getMyApplication().getAppCustomization().getTrackActivity());

View file

@ -574,7 +574,6 @@ public class ContextMenuLayer extends OsmandMapLayer {
public void cancelAddGpxPoint() { public void cancelAddGpxPoint() {
cancelApplyingNewMarkerPosition = true; cancelApplyingNewMarkerPosition = true;
quitAddGpxPoint(); quitAddGpxPoint();
activity.getContextMenu().show();
applyingMarkerLatLon = null; applyingMarkerLatLon = null;
} }

View file

@ -516,7 +516,7 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
for (WptPt wpt : getListStarPoints(g)) { for (WptPt wpt : getListStarPoints(g)) {
if (wpt.lat >= latLonBounds.bottom && wpt.lat <= latLonBounds.top if (wpt.lat >= latLonBounds.bottom && wpt.lat <= latLonBounds.top
&& wpt.lon >= latLonBounds.left && wpt.lon <= latLonBounds.right && wpt.lon >= latLonBounds.left && wpt.lon <= latLonBounds.right
&& wpt != contextMenuLayer.getMoveableObject()) { && wpt != contextMenuLayer.getMoveableObject() && !isPointHidden(g, wpt)) {
pointFileMap.put(wpt, g); pointFileMap.put(wpt, g);
MapMarker marker = null; MapMarker marker = null;
if (synced) { if (synced) {
@ -777,6 +777,14 @@ public class GPXLayer extends OsmandMapLayer implements IContextMenuProvider, IM
return g.getGpxFile().getPoints(); 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) { private boolean calculateBelongs(int ex, int ey, int objx, int objy, int radius) {
return (Math.abs(objx - ex) <= radius && Math.abs(objy - ey) <= 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<WptPt> pts = getListStarPoints(g); List<WptPt> pts = getListStarPoints(g);
// int fcolor = g.getColor() == 0 ? clr : g.getColor(); // int fcolor = g.getColor() == 0 ? clr : g.getColor();
for (WptPt n : pts) { for (WptPt n : pts) {
if (isPointHidden(g, n)) {
continue;
}
int x = (int) tb.getPixXFromLatLon(n.lat, n.lon); int x = (int) tb.getPixXFromLatLon(n.lat, n.lon);
int y = (int) tb.getPixYFromLatLon(n.lat, n.lon); int y = (int) tb.getPixYFromLatLon(n.lat, n.lon);
if (calculateBelongs(ex, ey, x, y, r)) { if (calculateBelongs(ex, ey, x, y, r)) {