Merge pull request #10576 from osmandapp/gpx_extra_actions

Gpx options first part
This commit is contained in:
alex-osm 2021-01-14 17:18:10 +03:00 committed by GitHub
commit 1a82db982f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 740 additions and 124 deletions

View file

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:minHeight="@dimen/bottom_sheet_list_item_height"
android:paddingStart="@dimen/content_padding"
android:paddingLeft="@dimen/content_padding"
android:paddingEnd="@dimen/content_padding"
android:paddingRight="@dimen/content_padding">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/standard_icon_size"
android:layout_height="@dimen/standard_icon_size"
android:layout_marginEnd="@dimen/bottom_sheet_icon_margin_large"
android:layout_marginRight="@dimen/bottom_sheet_icon_margin_large"
tools:src="@drawable/ic_action_coordinates_latitude" />
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/content_padding"
android:layout_marginRight="@dimen/content_padding"
android:layout_weight="1"
android:ellipsize="end"
android:maxLines="4"
android:textAppearance="@style/TextAppearance.ListItemTitle"
tools:text="Some Title" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/compound_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:background="@null"
android:clickable="false"
android:focusable="false"
android:focusableInTouchMode="false"
android:saveEnabled="false" />
</LinearLayout>

View file

@ -54,11 +54,15 @@
android:layout_weight="1" android:layout_weight="1"
android:orientation="vertical"> android:orientation="vertical">
<TextView <net.osmand.plus.widgets.TextViewEx
android:id="@+id/title" android:id="@+id/title"
style="@style/TextAppearance.ContextMenuTitle"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:letterSpacing="@dimen/text_button_letter_spacing"
android:minHeight="@dimen/default_title_line_height"
android:textSize="@dimen/default_list_text_size"
osmand:lineHeight="@dimen/default_title_line_height"
osmand:typeface="@string/font_roboto_medium"
tools:text="@string/search_address_building" /> tools:text="@string/search_address_building" />
<TextView <TextView
@ -72,7 +76,7 @@
</LinearLayout> </LinearLayout>
<androidx.appcompat.widget.AppCompatImageView <androidx.appcompat.widget.AppCompatImageView
android:id="@+id/context_menu_icon_view" android:id="@+id/icon_view"
android:layout_width="@dimen/map_widget_icon" android:layout_width="@dimen/map_widget_icon"
android:layout_height="@dimen/map_widget_icon" android:layout_height="@dimen/map_widget_icon"
android:layout_marginTop="@dimen/context_menu_padding_margin_default" android:layout_marginTop="@dimen/context_menu_padding_margin_default"

View file

@ -14,8 +14,8 @@
android:icon="@drawable/ic_action_waypoint" android:icon="@drawable/ic_action_waypoint"
android:title="@string/shared_string_gpx_points" /> android:title="@string/shared_string_gpx_points" />
<!-- <item--> <item
<!-- android:id="@+id/action_options"--> android:id="@+id/action_options"
<!-- android:icon="@drawable/ic_overflow_menu_white"--> android:icon="@drawable/ic_overflow_menu_with_background"
<!-- android:title="@string/shared_string_options" />--> android:title="@string/shared_string_options" />
</menu> </menu>

View file

@ -12,6 +12,11 @@
--> -->
<string name="change_folder">Change folder</string>
<string name="rename_track">Rename track</string>
<string name="edit_track">Edit track</string>
<string name="upload_to_openstreetmap">Upload to OpenStreetMap</string>
<string name="analyze_by_intervals">Analyze by intervals (split interval)</string>
<string name="shared_string_empty">Empty</string> <string name="shared_string_empty">Empty</string>
<string name="select_folder_descr">Select folder or add new one</string> <string name="select_folder_descr">Select folder or add new one</string>
<string name="select_folder">Select folder</string> <string name="select_folder">Select folder</string>

View file

@ -8,8 +8,6 @@ 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;
@ -107,6 +105,7 @@ public class FileUtils {
SelectedGpxFile selected = helper.getSelectedFileByPath(src.getAbsolutePath()); SelectedGpxFile selected = helper.getSelectedFileByPath(src.getAbsolutePath());
app.getGpxDbHelper().rename(src, dest); app.getGpxDbHelper().rename(src, dest);
if (selected != null && selected.getGpxFile() != null) { if (selected != null && selected.getGpxFile() != null) {
selected.resetSplitProcessed();
selected.getGpxFile().path = dest.getAbsolutePath(); selected.getGpxFile().path = dest.getAbsolutePath();
helper.updateSelectedGpxFile(selected); helper.updateSelectedGpxFile(selected);
} }

View file

@ -465,7 +465,7 @@ public class GPXDatabase {
return false; return false;
} }
public boolean rename(File currentFile, File newFile) { public boolean rename(@Nullable GpxDataItem item, File currentFile, File newFile) {
SQLiteConnection db = openConnection(false); SQLiteConnection db = openConnection(false);
if (db != null){ if (db != null){
try { try {
@ -478,6 +478,9 @@ public class GPXDatabase {
GPX_COL_DIR + " = ? " + GPX_COL_DIR + " = ? " +
" WHERE " + GPX_COL_NAME + " = ? AND " + GPX_COL_DIR + " = ?", " WHERE " + GPX_COL_NAME + " = ? AND " + GPX_COL_DIR + " = ?",
new Object[] { newFileName, newFileDir, fileName, fileDir }); new Object[] { newFileName, newFileDir, fileName, fileDir });
if (item != null) {
item.file = newFile;
}
} finally { } finally {
db.close(); db.close();
} }

View file

@ -68,9 +68,8 @@ public class GpxDbHelper {
} }
public boolean rename(File currentFile, File newFile) { public boolean rename(File currentFile, File newFile) {
boolean res = db.rename(currentFile, newFile); GpxDataItem item = itemsCache.get(currentFile);
itemsCache.remove(currentFile); return db.rename(item, currentFile, newFile);
return res;
} }
public boolean updateColor(GpxDataItem item, int color) { public boolean updateColor(GpxDataItem item, int color) {

View file

@ -861,6 +861,10 @@ public class GpxSelectionHelper {
return color; return color;
} }
public void resetSplitProcessed() {
splitProcessed = false;
}
public List<GpxDisplayGroup> getDisplayGroups(OsmandApplication app) { public List<GpxDisplayGroup> getDisplayGroups(OsmandApplication app) {
if (modifiedTime != gpxFile.modifiedTime || !splitProcessed) { if (modifiedTime != gpxFile.modifiedTime || !splitProcessed) {
update(app); update(app);

View file

@ -67,8 +67,7 @@ import net.osmand.plus.AppInitializer;
import net.osmand.plus.AppInitializer.AppInitializeListener; import net.osmand.plus.AppInitializer.AppInitializeListener;
import net.osmand.plus.AppInitializer.InitEvents; import net.osmand.plus.AppInitializer.InitEvents;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.mapmarkers.MapMarker; import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.mapmarkers.MapMarkersHelper.MapMarkerChangedListener;
import net.osmand.plus.OnDismissDialogFragmentListener; import net.osmand.plus.OnDismissDialogFragmentListener;
import net.osmand.plus.OsmAndConstants; import net.osmand.plus.OsmAndConstants;
import net.osmand.plus.OsmAndLocationSimulation; import net.osmand.plus.OsmAndLocationSimulation;
@ -80,7 +79,6 @@ import net.osmand.plus.TargetPointsHelper.TargetPoint;
import net.osmand.plus.Version; import net.osmand.plus.Version;
import net.osmand.plus.activities.search.SearchActivity; import net.osmand.plus.activities.search.SearchActivity;
import net.osmand.plus.base.BaseOsmAndFragment; import net.osmand.plus.base.BaseOsmAndFragment;
import net.osmand.plus.base.ContextMenuFragment;
import net.osmand.plus.base.FailSafeFuntions; import net.osmand.plus.base.FailSafeFuntions;
import net.osmand.plus.base.MapViewTrackingUtilities; import net.osmand.plus.base.MapViewTrackingUtilities;
import net.osmand.plus.chooseplan.OsmLiveGoneDialog; import net.osmand.plus.chooseplan.OsmLiveGoneDialog;
@ -108,10 +106,11 @@ import net.osmand.plus.helpers.ScrollHelper.OnScrollEventListener;
import net.osmand.plus.importfiles.ImportHelper; import net.osmand.plus.importfiles.ImportHelper;
import net.osmand.plus.mapcontextmenu.AdditionalActionsBottomSheetDialogFragment; import net.osmand.plus.mapcontextmenu.AdditionalActionsBottomSheetDialogFragment;
import net.osmand.plus.mapcontextmenu.MapContextMenu; import net.osmand.plus.mapcontextmenu.MapContextMenu;
import net.osmand.plus.mapcontextmenu.MenuController;
import net.osmand.plus.mapcontextmenu.builders.cards.dialogs.ContextMenuCardDialogFragment; import net.osmand.plus.mapcontextmenu.builders.cards.dialogs.ContextMenuCardDialogFragment;
import net.osmand.plus.mapcontextmenu.other.DestinationReachedMenu; import net.osmand.plus.mapcontextmenu.other.DestinationReachedMenu;
import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu; import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu;
import net.osmand.plus.mapmarkers.MapMarker;
import net.osmand.plus.mapmarkers.MapMarkersHelper.MapMarkerChangedListener;
import net.osmand.plus.mapmarkers.PlanRouteFragment; import net.osmand.plus.mapmarkers.PlanRouteFragment;
import net.osmand.plus.measurementtool.GpxApproximationFragment; import net.osmand.plus.measurementtool.GpxApproximationFragment;
import net.osmand.plus.measurementtool.GpxData; import net.osmand.plus.measurementtool.GpxData;
@ -125,8 +124,8 @@ import net.osmand.plus.routepreparationmenu.ChooseRouteFragment;
import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu; import net.osmand.plus.routepreparationmenu.MapRouteInfoMenu;
import net.osmand.plus.routepreparationmenu.MapRouteInfoMenuFragment; import net.osmand.plus.routepreparationmenu.MapRouteInfoMenuFragment;
import net.osmand.plus.routing.IRouteInformationListener; import net.osmand.plus.routing.IRouteInformationListener;
import net.osmand.plus.routing.RoutingHelper;
import net.osmand.plus.routing.RouteCalculationProgressCallback; import net.osmand.plus.routing.RouteCalculationProgressCallback;
import net.osmand.plus.routing.RoutingHelper;
import net.osmand.plus.routing.TransportRoutingHelper.TransportRouteCalculationProgressCallback; import net.osmand.plus.routing.TransportRoutingHelper.TransportRouteCalculationProgressCallback;
import net.osmand.plus.search.QuickSearchDialogFragment; import net.osmand.plus.search.QuickSearchDialogFragment;
import net.osmand.plus.search.QuickSearchDialogFragment.QuickSearchTab; import net.osmand.plus.search.QuickSearchDialogFragment.QuickSearchTab;
@ -169,8 +168,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_SETTINGS_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_SETTINGS_ID;
import static net.osmand.plus.activities.TrackActivity.CURRENT_RECORDING;
import static net.osmand.plus.activities.TrackActivity.TRACK_FILE_NAME;
public class MapActivity extends OsmandActionBarActivity implements DownloadEvents, public class MapActivity extends OsmandActionBarActivity implements DownloadEvents,
OnRequestPermissionsResultCallback, IRouteInformationListener, AMapPointUpdateListener, OnRequestPermissionsResultCallback, IRouteInformationListener, AMapPointUpdateListener,
@ -1177,15 +1174,15 @@ public class MapActivity extends OsmandActionBarActivity implements DownloadEven
MapRouteInfoMenu.showLocationOnMap(this, latLonToShow.getLatitude(), latLonToShow.getLongitude()); MapRouteInfoMenu.showLocationOnMap(this, latLonToShow.getLatitude(), latLonToShow.getLongitude());
} else if (toShow instanceof GPXFile) { } else if (toShow instanceof GPXFile) {
hideContextAndRouteInfoMenues(); hideContextAndRouteInfoMenues();
GPXFile gpxFile = (GPXFile) toShow;
SelectedGpxFile selectedGpxFile;
if (gpxFile.showCurrentTrack) {
selectedGpxFile = app.getSavingTrackHelper().getCurrentTrack();
} else {
selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(gpxFile.path);
}
Bundle args = new Bundle(); TrackAppearanceFragment.showInstance(this, selectedGpxFile);
args.putString(TRACK_FILE_NAME, ((GPXFile) toShow).path);
args.putBoolean(CURRENT_RECORDING, ((GPXFile) toShow).showCurrentTrack);
args.putInt(ContextMenuFragment.MENU_STATE_KEY, MenuController.MenuState.HALF_SCREEN);
TrackAppearanceFragment fragment = new TrackAppearanceFragment();
fragment.setArguments(args);
TrackAppearanceFragment.showInstance(this, fragment);
} else if (toShow instanceof QuadRect) { } else if (toShow instanceof QuadRect) {
QuadRect qr = (QuadRect) toShow; QuadRect qr = (QuadRect) toShow;
mapView.fitRectToMap(qr.left, qr.right, qr.top, qr.bottom, (int) qr.width(), (int) qr.height(), 0); mapView.fitRectToMap(qr.left, qr.right, qr.top, qr.bottom, (int) qr.width(), (int) qr.height(), 0);

View file

@ -99,6 +99,7 @@ import net.osmand.plus.dialogs.GpxAppearanceAdapter.AppearanceListItem;
import net.osmand.plus.helpers.enums.MetricsConstants; import net.osmand.plus.helpers.enums.MetricsConstants;
import net.osmand.plus.helpers.enums.SpeedConstants; import net.osmand.plus.helpers.enums.SpeedConstants;
import net.osmand.plus.monitoring.OsmandMonitoringPlugin; import net.osmand.plus.monitoring.OsmandMonitoringPlugin;
import net.osmand.plus.myplaces.SaveCurrentTrackTask;
import net.osmand.plus.routing.RouteCalculationResult; import net.osmand.plus.routing.RouteCalculationResult;
import net.osmand.plus.settings.backend.CommonPreference; import net.osmand.plus.settings.backend.CommonPreference;
import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.backend.OsmandSettings;
@ -2192,6 +2193,23 @@ public class GpxUiHelper {
new SaveGpxAsyncTask(file, gpxFile, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new SaveGpxAsyncTask(file, gpxFile, listener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
public static void saveAndShareCurrentGpx(@NonNull final OsmandApplication app, @NonNull final GPXFile gpxFile) {
SaveGpxListener saveGpxListener = new SaveGpxListener() {
@Override
public void gpxSavingStarted() {
}
@Override
public void gpxSavingFinished(Exception errorMessage) {
if (errorMessage == null) {
GpxUiHelper.shareGpx(app, new File(gpxFile.path));
}
}
};
new SaveCurrentTrackTask(app, gpxFile, saveGpxListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public static void saveAndShareGpxWithAppearance(@NonNull final Context context, @NonNull final GPXFile gpxFile) { public static void saveAndShareGpxWithAppearance(@NonNull final Context context, @NonNull final GPXFile gpxFile) {
OsmandApplication app = (OsmandApplication) context.getApplicationContext(); OsmandApplication app = (OsmandApplication) context.getApplicationContext();
GpxDataItem dataItem = getDataItem(app, gpxFile); GpxDataItem dataItem = getDataItem(app, gpxFile);

View file

@ -23,9 +23,7 @@ import net.osmand.plus.activities.TrackActivity;
import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.GpxUiHelper;
import net.osmand.plus.mapcontextmenu.MenuController; import net.osmand.plus.mapcontextmenu.MenuController;
import net.osmand.plus.mapcontextmenu.builders.SelectedGpxMenuBuilder; import net.osmand.plus.mapcontextmenu.builders.SelectedGpxMenuBuilder;
import net.osmand.plus.myplaces.SaveCurrentTrackTask;
import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener;
import net.osmand.plus.track.TrackMenuFragment; import net.osmand.plus.track.TrackMenuFragment;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
@ -77,7 +75,7 @@ public class SelectedGpxMenuController extends MenuController {
rightTitleButtonController.startIconId = R.drawable.ic_action_analyze_intervals; rightTitleButtonController.startIconId = R.drawable.ic_action_analyze_intervals;
} }
private static class OpenGpxDetailsTask extends AsyncTask<Void, Void, GpxSelectionHelper.GpxDisplayItem> { public static class OpenGpxDetailsTask extends AsyncTask<Void, Void, GpxSelectionHelper.GpxDisplayItem> {
private OsmandApplication app; private OsmandApplication app;
@ -87,7 +85,7 @@ public class SelectedGpxMenuController extends MenuController {
private ProgressDialog progressDialog; private ProgressDialog progressDialog;
private WeakReference<MapActivity> activityRef; private WeakReference<MapActivity> activityRef;
OpenGpxDetailsTask(SelectedGpxFile selectedGpxFile, WptPt selectedPoint, MapActivity mapActivity) { public OpenGpxDetailsTask(SelectedGpxFile selectedGpxFile, WptPt selectedPoint, MapActivity mapActivity) {
app = mapActivity.getMyApplication(); app = mapActivity.getMyApplication();
this.activityRef = new WeakReference<>(mapActivity); this.activityRef = new WeakReference<>(mapActivity);
this.selectedGpxFile = selectedGpxFile; this.selectedGpxFile = selectedGpxFile;
@ -217,7 +215,7 @@ public class SelectedGpxMenuController extends MenuController {
if (gpxFile != null) { if (gpxFile != null) {
OsmandApplication app = mapActivity.getMyApplication(); OsmandApplication app = mapActivity.getMyApplication();
if (Algorithms.isEmpty(gpxFile.path)) { if (Algorithms.isEmpty(gpxFile.path)) {
saveAndShareCurrentGpx(app, gpxFile); GpxUiHelper.saveAndShareCurrentGpx(app, gpxFile);
} else { } else {
GpxUiHelper.saveAndShareGpxWithAppearance(app, gpxFile); GpxUiHelper.saveAndShareGpxWithAppearance(app, gpxFile);
} }
@ -227,23 +225,6 @@ public class SelectedGpxMenuController extends MenuController {
} }
} }
public void saveAndShareCurrentGpx(@NonNull final OsmandApplication app, @NonNull final GPXFile gpxFile) {
SaveGpxListener saveGpxListener = new SaveGpxListener() {
@Override
public void gpxSavingStarted() {
}
@Override
public void gpxSavingFinished(Exception errorMessage) {
if (errorMessage == null) {
GpxUiHelper.shareGpx(app, new File(gpxFile.path));
}
}
};
new SaveCurrentTrackTask(app, gpxFile, saveGpxListener).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
public static class SelectedGpxPoint { public static class SelectedGpxPoint {
private final WptPt selectedPoint; private final WptPt selectedPoint;

View file

@ -254,6 +254,7 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (displayHelper.setJoinSegments(!displayHelper.isJoinSegments())) { if (displayHelper.setJoinSegments(!displayHelper.isJoinSegments())) {
actionsListener.updateContent();
for (int i = 0; i < getCount(); i++) { for (int i = 0; i < getCount(); i++) {
View view = getViewAtPosition(i); View view = getViewAtPosition(i);
updateJoinGapsInfo(view, i); updateJoinGapsInfo(view, i);
@ -337,6 +338,7 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid
@Override @Override
public void onClick(View v) { public void onClick(View v) {
if (displayHelper.setJoinSegments(!displayHelper.isJoinSegments())) { if (displayHelper.setJoinSegments(!displayHelper.isJoinSegments())) {
actionsListener.updateContent();
for (int i = 0; i < getCount(); i++) { for (int i = 0; i < getCount(); i++) {
View view = getViewAtPosition(i); View view = getViewAtPosition(i);
updateJoinGapsInfo(view, i); updateJoinGapsInfo(view, i);
@ -461,7 +463,7 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid
if (!chartClicked) { if (!chartClicked) {
chartClicked = true; chartClicked = true;
if (selectedWpt != null) { if (selectedWpt != null) {
actionsListener.onPointSelected(selectedWpt.lat, selectedWpt.lon); actionsListener.onPointSelected(segment, selectedWpt.lat, selectedWpt.lon);
} }
} }
} }
@ -496,7 +498,7 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid
WptPt wpt = getPoint(chart, h.getX()); WptPt wpt = getPoint(chart, h.getX());
selectedWpt = wpt; selectedWpt = wpt;
if (chartClicked && wpt != null) { if (chartClicked && wpt != null) {
actionsListener.onPointSelected(wpt.lat, wpt.lon); actionsListener.onPointSelected(segment, wpt.lat, wpt.lon);
} }
} }
@ -565,7 +567,7 @@ public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvid
chart.highlightValue(h); chart.highlightValue(h);
WptPt wpt = getPoint(chart, h.getX()); WptPt wpt = getPoint(chart, h.getX());
if (wpt != null) { if (wpt != null) {
actionsListener.onPointSelected(wpt.lat, wpt.lon); actionsListener.onPointSelected(segment, wpt.lat, wpt.lon);
} }
} }
} }

View file

@ -17,7 +17,7 @@ public interface SegmentActionsListener {
void scrollBy(int px); void scrollBy(int px);
void onPointSelected(double lat, double lon); void onPointSelected(TrkSegment segment, double lat, double lon);
void openSplitInterval(GpxDisplayItem gpxItem, TrkSegment trkSegment); void openSplitInterval(GpxDisplayItem gpxItem, TrkSegment trkSegment);

View file

@ -284,7 +284,7 @@ public class TrackSegmentFragment extends OsmAndListFragment implements TrackBit
} }
@Override @Override
public void onPointSelected(double lat, double lon) { public void onPointSelected(TrkSegment segment, double lat, double lon) {
if (fragmentAdapter != null) { if (fragmentAdapter != null) {
fragmentAdapter.updateSelectedPoint(lat, lon); fragmentAdapter.updateSelectedPoint(lat, lon);
} }

View file

@ -456,7 +456,7 @@ public class OsmEditingPlugin extends OsmandPlugin {
} }
} }
public boolean sendGPXFiles(final FragmentActivity activity, AvailableGPXFragment fragment, final GpxInfo... info) { public boolean sendGPXFiles(final FragmentActivity activity, Fragment fragment, final GpxInfo... info) {
String name = settings.USER_NAME.get(); String name = settings.USER_NAME.get();
String pwd = settings.USER_PASSWORD.get(); String pwd = settings.USER_PASSWORD.get();
String authToken = settings.USER_ACCESS_TOKEN.get(); String authToken = settings.USER_ACCESS_TOKEN.get();

View file

@ -0,0 +1,355 @@
package net.osmand.plus.track;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import net.osmand.AndroidUtils;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.plus.OsmandPlugin;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.bottomsheetmenu.BaseBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithCompoundButton;
import net.osmand.plus.base.bottomsheetmenu.BottomSheetItemWithDescriptionDifHeight;
import net.osmand.plus.base.bottomsheetmenu.SimpleBottomSheetItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerItem;
import net.osmand.plus.base.bottomsheetmenu.simpleitems.DividerSpaceItem;
import net.osmand.plus.helpers.FontCache;
import net.osmand.plus.osmedit.OsmEditingPlugin;
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import static net.osmand.plus.myplaces.TrackActivityFragmentAdapter.isGpxFileSelected;
import static net.osmand.util.Algorithms.capitalizeFirstLetter;
public class OptionsCard extends BaseCard {
public static final int SHOW_ON_MAP_BUTTON_INDEX = 0;
public static final int APPEARANCE_BUTTON_INDEX = 1;
public static final int DIRECTIONS_BUTTON_INDEX = 2;
public static final int JOIN_GAPS_BUTTON_INDEX = 3;
public static final int ANALYZE_ON_MAP_BUTTON_INDEX = 4;
public static final int ANALYZE_BY_INTERVALS_BUTTON_INDEX = 5;
public static final int SHARE_BUTTON_INDEX = 6;
public static final int UPLOAD_OSM_BUTTON_INDEX = 7;
public static final int EDIT_BUTTON_INDEX = 8;
public static final int RENAME_BUTTON_INDEX = 9;
public static final int CHANGE_FOLDER_BUTTON_INDEX = 10;
public static final int DELETE_BUTTON_INDEX = 11;
private TrackDisplayHelper displayHelper;
private GPXFile gpxFile;
private List<BaseBottomSheetItem> items = new ArrayList<>();
public OptionsCard(@NonNull MapActivity mapActivity, TrackDisplayHelper displayHelper) {
super(mapActivity);
this.displayHelper = displayHelper;
this.gpxFile = displayHelper.getGpx();
}
@Override
public int getCardLayoutId() {
return R.layout.card_container;
}
@Override
protected void updateContent() {
ViewGroup itemsContainer = (ViewGroup) view;
itemsContainer.removeAllViews();
items.clear();
boolean fileAvailable = gpxFile.path != null && !gpxFile.showCurrentTrack;
items.add(createShowOnMapItem());
items.add(createAppearanceItem());
if (fileAvailable) {
items.add(createDirectionsItem());
}
items.add(createDividerItem());
items.add(createJoinGapsItem());
items.add(createAnalyzeOnMapItem());
items.add(createAnalyzeByIntervalsItem());
items.add(createDividerItem());
items.add(createShareItem());
if (fileAvailable) {
items.add(createUploadOsmItem());
items.add(createDividerItem());
items.add(createEditItem());
items.add(createRenameItem());
items.add(createChangeFolderItem());
items.add(createDividerItem());
items.add(createDeleteItem());
}
items.add(new DividerSpaceItem(mapActivity, AndroidUtils.dpToPx(app, 6)));
inflateItems();
}
private BaseBottomSheetItem createDividerItem() {
DividerItem dividerItem = new DividerItem(mapActivity);
int start = app.getResources().getDimensionPixelSize(R.dimen.measurement_tool_options_divider_margin_start);
int verticalMargin = AndroidUtils.dpToPx(app, 6);
dividerItem.setMargins(start, verticalMargin, 0, verticalMargin);
return dividerItem;
}
private BaseBottomSheetItem createShowOnMapItem() {
final Drawable showIcon = getActiveIcon(R.drawable.ic_action_view);
final Drawable hideIcon = getContentIcon(R.drawable.ic_action_hide);
boolean gpxFileSelected = isGpxFileSelected(app, gpxFile);
final BottomSheetItemWithCompoundButton[] showOnMapItem = new BottomSheetItemWithCompoundButton[1];
showOnMapItem[0] = (BottomSheetItemWithCompoundButton) new BottomSheetItemWithCompoundButton.Builder()
.setChecked(gpxFileSelected)
.setIcon(gpxFileSelected ? showIcon : hideIcon)
.setTitle(app.getString(R.string.shared_string_show_on_map))
.setLayoutId(R.layout.bottom_sheet_item_with_switch_pad_32)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean checked = !showOnMapItem[0].isChecked();
showOnMapItem[0].setChecked(checked);
showOnMapItem[0].setIcon(checked ? showIcon : hideIcon);
CardListener listener = getListener();
if (listener != null) {
listener.onCardButtonPressed(OptionsCard.this, SHOW_ON_MAP_BUTTON_INDEX);
}
}
})
.create();
return showOnMapItem[0];
}
private BaseBottomSheetItem createAppearanceItem() {
return new SimpleBottomSheetItem.Builder()
.setIcon(getActiveIcon(R.drawable.ic_action_appearance))
.setTitle(app.getString(R.string.shared_string_appearance))
.setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp)
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
CardListener listener = getListener();
if (listener != null) {
listener.onCardButtonPressed(OptionsCard.this, APPEARANCE_BUTTON_INDEX);
}
}
})
.create();
}
private BaseBottomSheetItem createDirectionsItem() {
return new SimpleBottomSheetItem.Builder()
.setIcon(getActiveIcon(R.drawable.ic_action_gdirections_dark))
.setTitle(app.getString(R.string.get_directions))
.setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CardListener listener = getListener();
if (listener != null) {
listener.onCardButtonPressed(OptionsCard.this, DIRECTIONS_BUTTON_INDEX);
}
}
})
.create();
}
private BaseBottomSheetItem createJoinGapsItem() {
final Drawable joinGapsEnabledIcon = getActiveIcon(R.drawable.ic_action_join_segments);
final Drawable joinGapsDisabledIcon = getContentIcon(R.drawable.ic_action_join_segments);
boolean joinSegments = displayHelper.isJoinSegments();
final BottomSheetItemWithCompoundButton[] joinGapsItem = new BottomSheetItemWithCompoundButton[1];
joinGapsItem[0] = (BottomSheetItemWithCompoundButton) new BottomSheetItemWithCompoundButton.Builder()
.setChecked(joinSegments)
.setIcon(joinSegments ? joinGapsEnabledIcon : joinGapsDisabledIcon)
.setTitle(app.getString(R.string.gpx_join_gaps))
.setLayoutId(R.layout.bottom_sheet_item_with_switch_pad_32)
.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
boolean checked = !joinGapsItem[0].isChecked();
joinGapsItem[0].setChecked(checked);
joinGapsItem[0].setIcon(checked ? joinGapsEnabledIcon : joinGapsDisabledIcon);
CardListener listener = getListener();
if (listener != null) {
listener.onCardButtonPressed(OptionsCard.this, JOIN_GAPS_BUTTON_INDEX);
}
}
})
.create();
return joinGapsItem[0];
}
private BaseBottomSheetItem createAnalyzeOnMapItem() {
return new SimpleBottomSheetItem.Builder()
.setIcon(getActiveIcon(R.drawable.ic_action_analyze_intervals))
.setTitle(app.getString(R.string.analyze_on_map))
.setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp)
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
CardListener listener = getListener();
if (listener != null) {
listener.onCardButtonPressed(OptionsCard.this, ANALYZE_ON_MAP_BUTTON_INDEX);
}
}
})
.create();
}
private BaseBottomSheetItem createAnalyzeByIntervalsItem() {
return new SimpleBottomSheetItem.Builder()
.setIcon(getActiveIcon(R.drawable.ic_action_analyze_intervals))
.setTitle(app.getString(R.string.analyze_by_intervals))
.setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp)
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
CardListener listener = getListener();
if (listener != null) {
listener.onCardButtonPressed(OptionsCard.this, ANALYZE_BY_INTERVALS_BUTTON_INDEX);
}
}
})
.create();
}
private BaseBottomSheetItem createShareItem() {
Drawable shareIcon = getActiveIcon(R.drawable.ic_action_gshare_dark);
return new SimpleBottomSheetItem.Builder()
.setIcon(AndroidUtils.getDrawableForDirection(app, shareIcon))
.setTitle(app.getString(R.string.shared_string_share))
.setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp)
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
CardListener listener = getListener();
if (listener != null) {
listener.onCardButtonPressed(OptionsCard.this, SHARE_BUTTON_INDEX);
}
}
})
.create();
}
private BaseBottomSheetItem createUploadOsmItem() {
OsmEditingPlugin osmEditingPlugin = OsmandPlugin.getEnabledPlugin(OsmEditingPlugin.class);
if (osmEditingPlugin != null) {
return new SimpleBottomSheetItem.Builder()
.setIcon(getActiveIcon(R.drawable.ic_action_export))
.setTitle(app.getString(R.string.upload_to_openstreetmap))
.setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp)
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
CardListener listener = getListener();
if (listener != null) {
listener.onCardButtonPressed(OptionsCard.this, UPLOAD_OSM_BUTTON_INDEX);
}
}
})
.create();
}
return null;
}
private BaseBottomSheetItem createEditItem() {
Drawable editIcon = getActiveIcon(R.drawable.ic_action_edit_dark);
return new SimpleBottomSheetItem.Builder()
.setIcon(AndroidUtils.getDrawableForDirection(app, editIcon))
.setTitle(app.getString(R.string.edit_track))
.setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp)
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
CardListener listener = getListener();
if (listener != null) {
listener.onCardButtonPressed(OptionsCard.this, EDIT_BUTTON_INDEX);
}
}
})
.create();
}
private BaseBottomSheetItem createRenameItem() {
Drawable renameIcon = getActiveIcon(R.drawable.ic_action_name_field);
return new SimpleBottomSheetItem.Builder()
.setIcon(AndroidUtils.getDrawableForDirection(app, renameIcon))
.setTitle(app.getString(R.string.rename_track))
.setLayoutId(R.layout.bottom_sheet_item_simple_pad_32dp)
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
CardListener listener = getListener();
if (listener != null) {
listener.onCardButtonPressed(OptionsCard.this, RENAME_BUTTON_INDEX);
}
}
})
.create();
}
private BaseBottomSheetItem createChangeFolderItem() {
String folder = new File(gpxFile.path).getParentFile().getName();
Drawable changeFolderIcon = getActiveIcon(R.drawable.ic_action_folder_move);
return new BottomSheetItemWithDescriptionDifHeight.Builder()
.setMinHeight(app.getResources().getDimensionPixelSize(R.dimen.setting_list_item_group_height))
.setDescriptionColorId(nightMode ? R.color.text_color_secondary_dark : R.color.text_color_secondary_light)
.setDescription(capitalizeFirstLetter(folder))
.setIcon(AndroidUtils.getDrawableForDirection(app, changeFolderIcon))
.setTitle(app.getString(R.string.change_folder))
.setLayoutId(R.layout.bottom_sheet_item_with_descr_pad_32dp)
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
CardListener listener = getListener();
if (listener != null) {
listener.onCardButtonPressed(OptionsCard.this, CHANGE_FOLDER_BUTTON_INDEX);
}
}
})
.create();
}
private BaseBottomSheetItem createDeleteItem() {
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(getColoredIcon(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) {
CardListener listener = getListener();
if (listener != null) {
listener.onCardButtonPressed(OptionsCard.this, DELETE_BUTTON_INDEX);
}
}
})
.create();
}
private void inflateItems() {
for (BaseBottomSheetItem item : items) {
item.inflate(mapActivity, (ViewGroup) view, nightMode);
}
}
}

View file

@ -34,7 +34,7 @@ public class SegmentsCard extends BaseCard {
@Override @Override
public int getCardLayoutId() { public int getCardLayoutId() {
return R.layout.track_segments_container; return R.layout.card_container;
} }
@Override @Override

View file

@ -56,8 +56,6 @@ import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import static net.osmand.plus.activities.TrackActivity.CURRENT_RECORDING;
import static net.osmand.plus.activities.TrackActivity.TRACK_FILE_NAME;
import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_COLOR_ATTR; import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_COLOR_ATTR;
import static net.osmand.plus.dialogs.GpxAppearanceAdapter.TRACK_WIDTH_BOLD; import static net.osmand.plus.dialogs.GpxAppearanceAdapter.TRACK_WIDTH_BOLD;
import static net.osmand.plus.dialogs.GpxAppearanceAdapter.TRACK_WIDTH_MEDIUM; import static net.osmand.plus.dialogs.GpxAppearanceAdapter.TRACK_WIDTH_MEDIUM;
@ -121,6 +119,10 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement
return trackDrawInfo; return trackDrawInfo;
} }
public void setSelectedGpxFile(SelectedGpxFile selectedGpxFile) {
this.selectedGpxFile = selectedGpxFile;
}
@Override @Override
public int getSupportedMenuStatesPortrait() { public int getSupportedMenuStatesPortrait() {
return MenuState.HEADER_ONLY | MenuState.HALF_SCREEN | MenuState.FULL_SCREEN; return MenuState.HEADER_ONLY | MenuState.HALF_SCREEN | MenuState.FULL_SCREEN;
@ -132,40 +134,25 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement
app = requireMyApplication(); app = requireMyApplication();
gpxDbHelper = app.getGpxDbHelper(); gpxDbHelper = app.getGpxDbHelper();
Bundle arguments = getArguments();
if (savedInstanceState != null) { if (savedInstanceState != null) {
trackDrawInfo = new TrackDrawInfo(savedInstanceState); trackDrawInfo = new TrackDrawInfo(savedInstanceState);
if (trackDrawInfo.isCurrentRecording()) {
selectedGpxFile = app.getSavingTrackHelper().getCurrentTrack();
} else {
selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(trackDrawInfo.getFilePath());
}
if (!selectedGpxFile.isShowCurrentTrack()) { if (!selectedGpxFile.isShowCurrentTrack()) {
gpxDataItem = gpxDbHelper.getItem(new File(trackDrawInfo.getFilePath())); gpxDataItem = gpxDbHelper.getItem(new File(trackDrawInfo.getFilePath()));
} }
showStartFinishIconsInitialValue = savedInstanceState.getBoolean(SHOW_START_FINISH_ICONS_INITIAL_VALUE_KEY, showStartFinishIconsInitialValue = savedInstanceState.getBoolean(SHOW_START_FINISH_ICONS_INITIAL_VALUE_KEY,
app.getSettings().SHOW_START_FINISH_ICONS.get()); app.getSettings().SHOW_START_FINISH_ICONS.get());
} else if (arguments != null) { } else {
String gpxFilePath = arguments.getString(TRACK_FILE_NAME);
boolean currentRecording = arguments.getBoolean(CURRENT_RECORDING, false);
showStartFinishIconsInitialValue = app.getSettings().SHOW_START_FINISH_ICONS.get(); showStartFinishIconsInitialValue = app.getSettings().SHOW_START_FINISH_ICONS.get();
if (gpxFilePath == null && !currentRecording) { if (selectedGpxFile.isShowCurrentTrack()) {
log.error("Required extra '" + TRACK_FILE_NAME + "' is missing");
dismiss();
return;
}
if (currentRecording) {
trackDrawInfo = new TrackDrawInfo(true); trackDrawInfo = new TrackDrawInfo(true);
trackDrawInfo.setColor(app.getSettings().CURRENT_TRACK_COLOR.get()); trackDrawInfo.setColor(app.getSettings().CURRENT_TRACK_COLOR.get());
trackDrawInfo.setWidth(app.getSettings().CURRENT_TRACK_WIDTH.get()); trackDrawInfo.setWidth(app.getSettings().CURRENT_TRACK_WIDTH.get());
trackDrawInfo.setShowArrows(app.getSettings().CURRENT_TRACK_SHOW_ARROWS.get()); trackDrawInfo.setShowArrows(app.getSettings().CURRENT_TRACK_SHOW_ARROWS.get());
trackDrawInfo.setShowStartFinish(app.getSettings().CURRENT_TRACK_SHOW_START_FINISH.get()); trackDrawInfo.setShowStartFinish(app.getSettings().CURRENT_TRACK_SHOW_START_FINISH.get());
selectedGpxFile = app.getSavingTrackHelper().getCurrentTrack();
} else { } else {
gpxDataItem = gpxDbHelper.getItem(new File(gpxFilePath)); gpxDataItem = gpxDbHelper.getItem(new File(selectedGpxFile.getGpxFile().path));
trackDrawInfo = new TrackDrawInfo(app, gpxDataItem, false); trackDrawInfo = new TrackDrawInfo(app, gpxDataItem, false);
selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(gpxFilePath);
} }
updateTrackColor(); updateTrackColor();
} }
@ -726,8 +713,12 @@ public class TrackAppearanceFragment extends ContextMenuScrollFragment implement
return totalScreenHeight - frameTotalHeight; return totalScreenHeight - frameTotalHeight;
} }
public static boolean showInstance(@NonNull MapActivity mapActivity, TrackAppearanceFragment fragment) { public static boolean showInstance(@NonNull MapActivity mapActivity, @NonNull SelectedGpxFile selectedGpxFile) {
try { try {
TrackAppearanceFragment fragment = new TrackAppearanceFragment();
fragment.setSelectedGpxFile(selectedGpxFile);
fragment.setRetainInstance(true);
mapActivity.getSupportFragmentManager() mapActivity.getSupportFragmentManager()
.beginTransaction() .beginTransaction()
.replace(R.id.fragmentContainer, fragment, fragment.getFragmentTag()) .replace(R.id.fragmentContainer, fragment, fragment.getFragmentTag())

View file

@ -101,19 +101,7 @@ public class TrackDisplayHelper {
return new ArrayList<>(); return new ArrayList<>();
} }
if (gpxFile.modifiedTime != modifiedTime) { if (gpxFile.modifiedTime != modifiedTime) {
modifiedTime = gpxFile.modifiedTime; updateDisplayGroups();
GpxSelectionHelper selectedGpxHelper = app.getSelectedGpxHelper();
displayGroups = selectedGpxHelper.collectDisplayGroups(gpxFile);
originalGroups.clear();
for (GpxSelectionHelper.GpxDisplayGroup g : displayGroups) {
originalGroups.add(g.cloneInstance());
}
if (file != null) {
SelectedGpxFile sf = selectedGpxHelper.getSelectedFileByPath(gpxFile.path);
if (sf != null && file != null && sf.getDisplayGroups(app) != null) {
displayGroups = sf.getDisplayGroups(app);
}
}
} }
if (useDisplayGroups) { if (useDisplayGroups) {
return displayGroups; return displayGroups;
@ -122,6 +110,22 @@ public class TrackDisplayHelper {
} }
} }
public void updateDisplayGroups() {
modifiedTime = gpxFile.modifiedTime;
GpxSelectionHelper selectedGpxHelper = app.getSelectedGpxHelper();
displayGroups = selectedGpxHelper.collectDisplayGroups(gpxFile);
originalGroups.clear();
for (GpxDisplayGroup g : displayGroups) {
originalGroups.add(g.cloneInstance());
}
if (file != null) {
SelectedGpxFile sf = selectedGpxHelper.getSelectedFileByPath(gpxFile.path);
if (sf != null && file != null && sf.getDisplayGroups(app) != null) {
displayGroups = sf.getDisplayGroups(app);
}
}
}
@NonNull @NonNull
public List<GpxDisplayGroup> getOriginalGroups(GpxDisplayItemType[] filterTypes) { public List<GpxDisplayGroup> getOriginalGroups(GpxDisplayItemType[] filterTypes) {
return filterGroups(false, filterTypes); return filterGroups(false, filterTypes);

View file

@ -5,12 +5,14 @@ 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.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.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.DrawableRes; import androidx.annotation.DrawableRes;
@ -25,36 +27,47 @@ import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.BottomNavigationView;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
import net.osmand.FileUtils;
import net.osmand.FileUtils.RenameCallback;
import net.osmand.GPXUtilities.GPXFile; import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.Track; import net.osmand.GPXUtilities.Track;
import net.osmand.GPXUtilities.TrkSegment; import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.PlatformUtil; import net.osmand.PlatformUtil;
import net.osmand.data.LatLon;
import net.osmand.data.QuadRect; import net.osmand.data.QuadRect;
import net.osmand.data.RotatedTileBox; import net.osmand.data.RotatedTileBox;
import net.osmand.plus.GPXDatabase.GpxDataItem;
import net.osmand.plus.GpxDbHelper; import net.osmand.plus.GpxDbHelper;
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.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.OsmandPlugin;
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.MapActivityActions;
import net.osmand.plus.base.ContextMenuFragment; import net.osmand.plus.base.ContextMenuFragment;
import net.osmand.plus.base.ContextMenuScrollFragment; import net.osmand.plus.base.ContextMenuScrollFragment;
import net.osmand.plus.helpers.AndroidUiHelper; import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.helpers.GpxUiHelper; import net.osmand.plus.helpers.GpxUiHelper;
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType; import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType;
import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet; import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet;
import net.osmand.plus.mapcontextmenu.controllers.SelectedGpxMenuController.OpenGpxDetailsTask;
import net.osmand.plus.mapcontextmenu.other.TrackChartPoints;
import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu; import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu;
import net.osmand.plus.measurementtool.GpxData; 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.GPXTabItemType; import net.osmand.plus.myplaces.GPXTabItemType;
import net.osmand.plus.myplaces.MoveGpxFileBottomSheet;
import net.osmand.plus.myplaces.MoveGpxFileBottomSheet.OnTrackFileMoveListener;
import net.osmand.plus.myplaces.SegmentActionsListener; import net.osmand.plus.myplaces.SegmentActionsListener;
import net.osmand.plus.myplaces.SplitSegmentDialogFragment; import net.osmand.plus.myplaces.SplitSegmentDialogFragment;
import net.osmand.plus.myplaces.TrackActivityFragmentAdapter; import net.osmand.plus.myplaces.TrackActivityFragmentAdapter;
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;
@ -68,28 +81,46 @@ import java.util.List;
import static net.osmand.plus.activities.TrackActivity.CURRENT_RECORDING; import static net.osmand.plus.activities.TrackActivity.CURRENT_RECORDING;
import static net.osmand.plus.activities.TrackActivity.TRACK_FILE_NAME; import static net.osmand.plus.activities.TrackActivity.TRACK_FILE_NAME;
import static net.osmand.plus.myplaces.TrackActivityFragmentAdapter.isGpxFileSelected;
import static net.osmand.plus.track.OptionsCard.ANALYZE_BY_INTERVALS_BUTTON_INDEX;
import static net.osmand.plus.track.OptionsCard.ANALYZE_ON_MAP_BUTTON_INDEX;
import static net.osmand.plus.track.OptionsCard.APPEARANCE_BUTTON_INDEX;
import static net.osmand.plus.track.OptionsCard.CHANGE_FOLDER_BUTTON_INDEX;
import static net.osmand.plus.track.OptionsCard.DELETE_BUTTON_INDEX;
import static net.osmand.plus.track.OptionsCard.DIRECTIONS_BUTTON_INDEX;
import static net.osmand.plus.track.OptionsCard.EDIT_BUTTON_INDEX;
import static net.osmand.plus.track.OptionsCard.JOIN_GAPS_BUTTON_INDEX;
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;
public class TrackMenuFragment extends ContextMenuScrollFragment implements CardListener, SegmentActionsListener { public class TrackMenuFragment extends ContextMenuScrollFragment implements CardListener,
SegmentActionsListener, RenameCallback, OnTrackFileMoveListener {
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);
private OsmandApplication app; private OsmandApplication app;
private TrackDisplayHelper displayHelper; private TrackDisplayHelper displayHelper;
private GpxDataItem gpxDataItem;
private SelectedGpxFile selectedGpxFile; private SelectedGpxFile selectedGpxFile;
private View routeMenuTopShadowAll; private View routeMenuTopShadowAll;
private TextView headerTitle;
private ImageView headerIcon;
private BottomNavigationView bottomNav; private BottomNavigationView bottomNav;
private TrackMenuType menuType = TrackMenuType.TRACK; private TrackMenuType menuType = TrackMenuType.TRACK;
private SegmentsCard segmentsCard; private SegmentsCard segmentsCard;
private OptionsCard optionsCard;
private TrackChartPoints trackChartPoints;
private int menuTitleHeight; private int menuTitleHeight;
public enum TrackMenuType { public enum TrackMenuType {
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);
TrackMenuType(@DrawableRes int iconId, @StringRes int titleId) { TrackMenuType(@DrawableRes int iconId, @StringRes int titleId) {
this.iconId = iconId; this.iconId = iconId;
@ -140,26 +171,20 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
GpxDbHelper gpxDbHelper = app.getGpxDbHelper(); GpxDbHelper gpxDbHelper = app.getGpxDbHelper();
displayHelper = new TrackDisplayHelper(app); displayHelper = new TrackDisplayHelper(app);
String gpxFilePath = "";
boolean currentRecording = false;
Bundle arguments = getArguments(); Bundle arguments = getArguments();
if (savedInstanceState != null) { if (arguments != null) {
gpxFilePath = savedInstanceState.getString(TRACK_FILE_NAME); String gpxFilePath = arguments.getString(TRACK_FILE_NAME);
currentRecording = savedInstanceState.getBoolean(CURRENT_RECORDING, false); boolean currentRecording = arguments.getBoolean(CURRENT_RECORDING, false);
} else if (arguments != null) { if (currentRecording) {
gpxFilePath = arguments.getString(TRACK_FILE_NAME); selectedGpxFile = app.getSavingTrackHelper().getCurrentTrack();
currentRecording = arguments.getBoolean(CURRENT_RECORDING, false); } else {
File file = new File(gpxFilePath);
displayHelper.setFile(file);
displayHelper.setGpxDataItem(gpxDbHelper.getItem(file));
selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(gpxFilePath);
}
displayHelper.setGpx(selectedGpxFile.getGpxFile());
} }
if (currentRecording) {
selectedGpxFile = app.getSavingTrackHelper().getCurrentTrack();
} else {
File file = new File(gpxFilePath);
displayHelper.setFile(file);
gpxDataItem = gpxDbHelper.getItem(file);
selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(gpxFilePath);
}
displayHelper.setGpxDataItem(gpxDataItem);
displayHelper.setGpx(selectedGpxFile.getGpxFile());
} }
public GPXFile getGpx() { public GPXFile getGpx() {
@ -172,14 +197,19 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
if (view != null) { if (view != null) {
bottomNav = view.findViewById(R.id.bottom_navigation); bottomNav = view.findViewById(R.id.bottom_navigation);
routeMenuTopShadowAll = view.findViewById(R.id.route_menu_top_shadow_all); routeMenuTopShadowAll = view.findViewById(R.id.route_menu_top_shadow_all);
TextView title = view.findViewById(R.id.title); headerTitle = view.findViewById(R.id.title);
String fileName = Algorithms.getFileWithoutDirs(getGpx().path); headerIcon = view.findViewById(R.id.icon_view);
title.setText(GpxUiHelper.getGpxTitle(fileName));
if (isPortrait()) { if (isPortrait()) {
updateCardsLayout(); updateCardsLayout();
} else {
int widthNoShadow = getLandscapeNoShadowWidth();
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(widthNoShadow, ViewGroup.LayoutParams.WRAP_CONTENT);
params.gravity = Gravity.BOTTOM | Gravity.START;
bottomNav.setLayoutParams(params);
} }
setupCards(); setupCards();
updateHeader();
setupButtons(view); setupButtons(view);
enterTrackAppearanceMode(); enterTrackAppearanceMode();
runLayoutListener(); runLayoutListener();
@ -187,18 +217,46 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
return view; return view;
} }
private void updateHeader() {
if (menuType == TrackMenuType.OPTIONS) {
headerTitle.setText(menuType.titleId);
AndroidUiHelper.updateVisibility(headerIcon, false);
} else {
String fileName = Algorithms.getFileWithoutDirs(getGpx().path);
headerTitle.setText(GpxUiHelper.getGpxTitle(fileName));
AndroidUiHelper.updateVisibility(headerIcon, true);
}
}
private void setupCards() { private void setupCards() {
MapActivity mapActivity = getMapActivity(); MapActivity mapActivity = getMapActivity();
if (mapActivity != null) { if (mapActivity != null) {
ViewGroup cardsContainer = getCardsContainer(); ViewGroup cardsContainer = getCardsContainer();
cardsContainer.removeAllViews(); cardsContainer.removeAllViews();
if (menuType == TrackMenuType.TRACK) { if (menuType == TrackMenuType.TRACK) {
if (segmentsCard != null) { if (segmentsCard != null && segmentsCard.getView() != null) {
ViewGroup parent = (ViewGroup) segmentsCard.getView().getParent();
if (parent != null) {
parent.removeAllViews();
}
cardsContainer.addView(segmentsCard.getView()); cardsContainer.addView(segmentsCard.getView());
} else { } else {
segmentsCard = new SegmentsCard(mapActivity, displayHelper, this); segmentsCard = new SegmentsCard(mapActivity, displayHelper, this);
segmentsCard.setListener(this);
cardsContainer.addView(segmentsCard.build(mapActivity)); cardsContainer.addView(segmentsCard.build(mapActivity));
} }
} else if (menuType == TrackMenuType.OPTIONS) {
if (optionsCard != null && optionsCard.getView() != null) {
ViewGroup parent = (ViewGroup) optionsCard.getView().getParent();
if (parent != null) {
parent.removeAllViews();
}
cardsContainer.addView(optionsCard.getView());
} else {
optionsCard = new OptionsCard(mapActivity, displayHelper);
optionsCard.setListener(this);
cardsContainer.addView(optionsCard.build(mapActivity));
}
} }
} }
} }
@ -239,6 +297,46 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
exitTrackAppearanceMode(); exitTrackAppearanceMode();
} }
@Override
public void onResume() {
super.onResume();
MapActivity mapActivity = getMapActivity();
if (mapActivity != null && trackChartPoints != null) {
mapActivity.getMapLayers().getGpxLayer().setTrackChartPoints(trackChartPoints);
}
}
@Override
public void onPause() {
super.onPause();
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
mapActivity.getMapLayers().getGpxLayer().setTrackChartPoints(null);
}
}
@Override
public void renamedTo(File file) {
updateFile(file);
}
@Override
public void onFileMove(@NonNull File src, @NonNull File dest) {
File file = FileUtils.renameGpxFile(app, src, dest);
if (file != null) {
updateFile(file);
} else {
app.showToastMessage(R.string.file_can_not_be_renamed);
}
}
private void updateFile(File file) {
displayHelper.setFile(file);
displayHelper.updateDisplayGroups();
updateHeader();
updateContent();
}
private void enterTrackAppearanceMode() { private void enterTrackAppearanceMode() {
MapActivity mapActivity = getMapActivity(); MapActivity mapActivity = getMapActivity();
if (mapActivity != null) { if (mapActivity != null) {
@ -311,7 +409,104 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
@Override @Override
public void onCardButtonPressed(@NonNull BaseCard card, int buttonIndex) { public void onCardButtonPressed(@NonNull BaseCard card, int buttonIndex) {
MapActivity mapActivity = getMapActivity();
if (mapActivity == null) {
return;
}
if (card instanceof OptionsCard) {
final GPXFile gpxFile = getGpx();
if (buttonIndex == SHOW_ON_MAP_BUTTON_INDEX) {
boolean gpxFileSelected = !isGpxFileSelected(app, gpxFile);
app.getSelectedGpxHelper().selectGpxFile(gpxFile, gpxFileSelected, false);
mapActivity.refreshMap();
} else if (buttonIndex == APPEARANCE_BUTTON_INDEX) {
TrackAppearanceFragment.showInstance(mapActivity, selectedGpxFile);
} else if (buttonIndex == DIRECTIONS_BUTTON_INDEX) {
MapActivityActions mapActions = mapActivity.getMapActions();
if (app.getRoutingHelper().isFollowingMode()) {
mapActions.stopNavigationActionConfirm(null, new Runnable() {
@Override
public void run() {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
mapActivity.getMapActions().enterRoutePlanningModeGivenGpx(gpxFile, null,
null, null, true, true, MenuState.HEADER_ONLY);
}
}
});
} else {
mapActions.stopNavigationWithoutConfirm();
mapActions.enterRoutePlanningModeGivenGpx(gpxFile, null, null,
null, true, true, MenuState.HEADER_ONLY);
}
dismiss();
}
if (buttonIndex == JOIN_GAPS_BUTTON_INDEX) {
displayHelper.setJoinSegments(!displayHelper.isJoinSegments());
mapActivity.refreshMap();
if (segmentsCard != null) {
segmentsCard.updateContent();
}
} else if (buttonIndex == ANALYZE_ON_MAP_BUTTON_INDEX) {
new OpenGpxDetailsTask(selectedGpxFile, null, mapActivity).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
dismiss();
} else if (buttonIndex == ANALYZE_BY_INTERVALS_BUTTON_INDEX) {
FragmentManager fragmentManager = mapActivity.getSupportFragmentManager();
TrkSegment segment = gpxFile.getGeneralSegment();
if (segment == null) {
List<TrkSegment> segments = gpxFile.getNonEmptyTrkSegments(false);
if (!Algorithms.isEmpty(segments)) {
segment = segments.get(0);
}
}
GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT};
List<GpxDisplayItem> items = TrackDisplayHelper.flatten(displayHelper.getOriginalGroups(filterTypes));
if (segment != null && !Algorithms.isEmpty(items)) {
SplitSegmentDialogFragment.showInstance(fragmentManager, displayHelper, items.get(0), segment);
}
} else if (buttonIndex == SHARE_BUTTON_INDEX) {
OsmandApplication app = mapActivity.getMyApplication();
if (gpxFile.showCurrentTrack) {
GpxUiHelper.saveAndShareCurrentGpx(app, gpxFile);
} else if (!Algorithms.isEmpty(gpxFile.path)) {
GpxUiHelper.saveAndShareGpxWithAppearance(app, gpxFile);
}
} else if (buttonIndex == UPLOAD_OSM_BUTTON_INDEX) {
OsmEditingPlugin osmEditingPlugin = OsmandPlugin.getEnabledPlugin(OsmEditingPlugin.class);
if (osmEditingPlugin != null) {
GpxInfo gpxInfo = new GpxInfo();
gpxInfo.gpx = gpxFile;
gpxInfo.file = new File(gpxFile.path);
osmEditingPlugin.sendGPXFiles(mapActivity, this, gpxInfo);
}
} else if (buttonIndex == EDIT_BUTTON_INDEX) {
String fileName = Algorithms.getFileWithoutDirs(gpxFile.path);
MeasurementToolFragment.showInstance(mapActivity.getSupportFragmentManager(), fileName);
dismiss();
} else if (buttonIndex == RENAME_BUTTON_INDEX) {
FileUtils.renameFile(mapActivity, new File(gpxFile.path), this, true);
} else if (buttonIndex == CHANGE_FOLDER_BUTTON_INDEX) {
FragmentManager fragmentManager = mapActivity.getSupportFragmentManager();
MoveGpxFileBottomSheet.showInstance(fragmentManager, this, gpxFile.path, true);
} else if (buttonIndex == DELETE_BUTTON_INDEX) {
String fileName = Algorithms.getFileWithoutDirs(gpxFile.path);
AlertDialog.Builder builder = new AlertDialog.Builder(UiUtilities.getThemedContext(mapActivity, isNightMode()));
builder.setTitle(getString(R.string.delete_confirmation_msg, fileName));
builder.setMessage(R.string.are_you_sure);
builder.setNegativeButton(R.string.shared_string_cancel, null).setPositiveButton(
R.string.shared_string_ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (FileUtils.removeGpxFile(app, new File((gpxFile.path)))) {
dismiss();
}
}
});
builder.show();
}
}
} }
@Override @Override
@ -378,6 +573,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
if (type.iconId == item.getItemId()) { if (type.iconId == item.getItemId()) {
menuType = type; menuType = type;
setupCards(); setupCards();
updateHeader();
break; break;
} }
} }
@ -391,6 +587,9 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
if (segmentsCard != null) { if (segmentsCard != null) {
segmentsCard.updateContent(); segmentsCard.updateContent();
} }
if (optionsCard != null) {
optionsCard.updateContent();
}
setupCards(); setupCards();
} }
@ -405,8 +604,19 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
} }
@Override @Override
public void onPointSelected(double lat, double lon) { public void onPointSelected(TrkSegment segment, double lat, double lon) {
if (trackChartPoints == null) {
trackChartPoints = new TrackChartPoints();
trackChartPoints.setGpx(getGpx());
}
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
int segmentColor = segment != null ? segment.getColor(0) : 0;
trackChartPoints.setSegmentColor(segmentColor);
trackChartPoints.setHighlightedPoint(new LatLon(lat, lon));
mapActivity.getMapLayers().getGpxLayer().setTrackChartPoints(trackChartPoints);
mapActivity.refreshMap();
}
} }
@Override @Override
@ -551,10 +761,8 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
public void gpxSavingFinished(Exception errorMessage) { public void gpxSavingFinished(Exception errorMessage) {
if (selectedGpxFile != null) { if (selectedGpxFile != null) {
List<GpxDisplayGroup> groups = displayHelper.getDisplayGroups(new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT}); List<GpxDisplayGroup> groups = displayHelper.getDisplayGroups(new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT});
if (groups != null) { selectedGpxFile.setDisplayGroups(groups, app);
selectedGpxFile.setDisplayGroups(groups, app); selectedGpxFile.processPoints(app);
selectedGpxFile.processPoints(app);
}
} }
updateContent(); updateContent();
} }
@ -582,6 +790,7 @@ public class TrackMenuFragment extends ContextMenuScrollFragment implements Card
TrackMenuFragment fragment = new TrackMenuFragment(); TrackMenuFragment fragment = new TrackMenuFragment();
fragment.setArguments(args); fragment.setArguments(args);
fragment.setRetainInstance(true);
mapActivity.getSupportFragmentManager() mapActivity.getSupportFragmentManager()
.beginTransaction() .beginTransaction()