Add TrackMenuFragment and refactor GPXItemPagerAdapter

This commit is contained in:
Vitaliy 2020-12-29 17:23:13 +02:00
parent 57a68d223f
commit 9ef0d4ae7e
18 changed files with 2188 additions and 1282 deletions

View file

@ -0,0 +1,127 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:osmand="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_transparent">
<LinearLayout
android:id="@+id/main_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="@layout/context_menu_top_shadow" />
<LinearLayout
android:id="@+id/route_menu_top_shadow_all"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/list_background_color"
android:orientation="vertical">
<View
android:layout_width="@dimen/content_padding"
android:layout_height="2dp"
android:layout_gravity="center"
android:layout_marginTop="@dimen/context_menu_padding_margin_tiny"
android:layout_marginBottom="@dimen/list_item_button_padding"
android:background="?attr/bg_dash_line" />
<LinearLayout
android:id="@+id/context_menu_top_shadow_all"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/context_menu_top_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="@dimen/context_menu_padding_margin_default"
android:paddingLeft="@dimen/context_menu_padding_margin_default"
android:paddingEnd="@dimen/context_menu_padding_margin_default"
android:paddingRight="@dimen/context_menu_padding_margin_default">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/context_menu_first_line_top_margin"
android:layout_marginEnd="@dimen/context_menu_padding_margin_default"
android:layout_marginRight="@dimen/context_menu_padding_margin_default"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/title"
style="@style/TextAppearance.ContextMenuTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="@string/search_address_building" />
<TextView
android:id="@+id/description"
style="@style/TextAppearance.ContextMenuSubtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
tools:text="@string/amenity_type_finance" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/context_menu_icon_view"
android:layout_width="@dimen/map_widget_icon"
android:layout_height="@dimen/map_widget_icon"
android:layout_marginTop="@dimen/context_menu_padding_margin_default"
android:tint="?attr/default_icon_color"
osmand:srcCompat="@drawable/ic_action_polygom_dark" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
<FrameLayout
android:id="@+id/bottom_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/activity_background_basic"
android:foreground="@drawable/bg_contextmenu_shadow"
android:foregroundGravity="top|fill_horizontal">
<net.osmand.plus.LockableScrollView
android:id="@+id/route_menu_bottom_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<LinearLayout
android:id="@+id/route_menu_cards_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/context_menu_action_buttons_height" />
</net.osmand.plus.LockableScrollView>
</FrameLayout>
</LinearLayout>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="@dimen/context_menu_action_buttons_height"
android:layout_gravity="bottom"
android:background="?attr/wikivoyage_card_bg_color"
osmand:itemBackground="?attr/wikivoyage_card_bg_color"
osmand:itemIconTint="@color/bottom_navigation_color_selector_light"
osmand:itemTextColor="@color/bottom_navigation_color_selector_light"
osmand:labelVisibilityMode="labeled"
osmand:menu="@menu/track_menu_bottom_navigation" />
</FrameLayout>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>

View file

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item <!-- <item-->
android:id="@+id/action_overview" <!-- android:id="@+id/action_overview"-->
android:icon="@drawable/ic_action_trail_overview" <!-- android:icon="@drawable/ic_action_trail_overview"-->
android:title="@string/shared_string_overview" /> <!-- android:title="@string/shared_string_overview" />-->
<item <item
android:id="@+id/action_track" android:id="@+id/action_track"
@ -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_white"-->
android:title="@string/shared_string_options" /> <!-- android:title="@string/shared_string_options" />-->
</menu> </menu>

View file

@ -43,6 +43,7 @@ import net.osmand.plus.myplaces.TrackPointFragment;
import net.osmand.plus.myplaces.TrackSegmentFragment; import net.osmand.plus.myplaces.TrackSegmentFragment;
import net.osmand.plus.settings.backend.OsmAndAppCustomization; import net.osmand.plus.settings.backend.OsmAndAppCustomization;
import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.backend.OsmandSettings;
import net.osmand.plus.track.TrackDisplayHelper;
import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint; import net.osmand.plus.views.AddGpxPointBottomSheetHelper.NewGpxPoint;
import java.io.File; import java.io.File;
@ -57,18 +58,14 @@ public class TrackActivity extends TabActivity {
public static final String OPEN_TRACKS_LIST = "OPEN_TRACKS_LIST"; public static final String OPEN_TRACKS_LIST = "OPEN_TRACKS_LIST";
public static final String CURRENT_RECORDING = "CURRENT_RECORDING"; public static final String CURRENT_RECORDING = "CURRENT_RECORDING";
public static final String SHOW_TEMPORARILY = "SHOW_TEMPORARILY"; public static final String SHOW_TEMPORARILY = "SHOW_TEMPORARILY";
protected List<WeakReference<Fragment>> fragList = new ArrayList<>();
private OsmandApplication app; private OsmandApplication app;
private TrackDisplayHelper displayHelper;
private TrackBitmapDrawer trackBitmapDrawer; private TrackBitmapDrawer trackBitmapDrawer;
private File file = null;
private GPXFile gpxFile;
private GpxDataItem gpxDataItem;
private LockableViewPager viewPager; private LockableViewPager viewPager;
private long modifiedTime = -1; private final List<WeakReference<Fragment>> fragList = new ArrayList<>();
private List<GpxDisplayGroup> displayGroups;
private List<GpxDisplayGroup> originalGroups = new ArrayList<>();
private boolean stopped = false; private boolean stopped = false;
private boolean openPointsTab = false; private boolean openPointsTab = false;
private boolean openTracksList = false; private boolean openTracksList = false;
@ -85,14 +82,15 @@ public class TrackActivity extends TabActivity {
finish(); finish();
return; return;
} }
displayHelper = new TrackDisplayHelper(app);
if (intent.hasExtra(TRACK_FILE_NAME)) { if (intent.hasExtra(TRACK_FILE_NAME)) {
file = new File(intent.getStringExtra(TRACK_FILE_NAME)); displayHelper.setFile(new File(intent.getStringExtra(TRACK_FILE_NAME)));
} }
ActionBar actionBar = getSupportActionBar(); ActionBar actionBar = getSupportActionBar();
if (actionBar != null) { if (actionBar != null) {
if (file != null) { if (getFile() != null) {
String fn = file.getName().replace(IndexConstants.GPX_FILE_EXT, "").replace("/", " ").replace("_", " "); String fn = getFile().getName().replace(IndexConstants.GPX_FILE_EXT, "").replace("/", " ").replace("_", " ");
actionBar.setTitle(fn); actionBar.setTitle(fn);
} else { } else {
actionBar.setTitle(getString(R.string.shared_string_currently_recording_track)); actionBar.setTitle(getString(R.string.shared_string_currently_recording_track));
@ -109,11 +107,19 @@ public class TrackActivity extends TabActivity {
setContentView(R.layout.track_content); setContentView(R.layout.track_content);
} }
public TrackDisplayHelper getDisplayHelper() {
return displayHelper;
}
@Nullable @Nullable
public TrackBitmapDrawer getTrackBitmapDrawer() { public TrackBitmapDrawer getTrackBitmapDrawer() {
return trackBitmapDrawer; return trackBitmapDrawer;
} }
public File getFile() {
return displayHelper.getFile();
}
public void addPoint(PointDescription pointDescription) { public void addPoint(PointDescription pointDescription) {
Intent currentIntent = getIntent(); Intent currentIntent = getIntent();
if (currentIntent != null) { if (currentIntent != null) {
@ -122,7 +128,7 @@ public class TrackActivity extends TabActivity {
final OsmandSettings settings = app.getSettings(); final OsmandSettings settings = app.getSettings();
GPXFile gpx = getGpx(); GPXFile gpx = getGpx();
LatLon location = settings.getLastKnownMapLocation(); LatLon location = settings.getLastKnownMapLocation();
QuadRect rect = getRect(); QuadRect rect = displayHelper.getRect();
NewGpxPoint newGpxPoint = new NewGpxPoint(gpx, pointDescription, rect); NewGpxPoint newGpxPoint = new NewGpxPoint(gpx, pointDescription, rect);
if (gpx != null && location != null) { if (gpx != null && location != null) {
settings.setMapLocationToShow(location.getLatitude(), location.getLongitude(), settings.setMapLocationToShow(location.getLatitude(), location.getLongitude(),
@ -152,53 +158,20 @@ public class TrackActivity extends TabActivity {
} }
} }
public QuadRect getRect() {
if (getGpx() != null) {
return getGpx().getRect();
} else {
return new QuadRect(0, 0, 0, 0);
}
}
protected void setGpxDataItem(GpxDataItem gpxDataItem) { protected void setGpxDataItem(GpxDataItem gpxDataItem) {
this.gpxDataItem = gpxDataItem; displayHelper.setGpxDataItem(gpxDataItem);
} }
protected void setGpx(GPXFile result) { protected void setGpx(GPXFile result) {
this.gpxFile = result; displayHelper.setGpx(result);
if (file == null) {
this.gpxFile = getMyApplication().getSavingTrackHelper().getCurrentGpx();
}
} }
public List<GpxDisplayGroup> getGpxFile(boolean useDisplayGroups) { public List<GpxDisplayGroup> getGpxFile(boolean useDisplayGroups) {
if (gpxFile == null) { return displayHelper.getGpxFile(useDisplayGroups);
return new ArrayList<>();
}
if (gpxFile.modifiedTime != modifiedTime) {
modifiedTime = gpxFile.modifiedTime;
GpxSelectionHelper selectedGpxHelper = ((OsmandApplication) getApplication()).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);
}
}
}
if (useDisplayGroups) {
return displayGroups;
} else {
return originalGroups;
}
} }
@Override @Override
public void onAttachFragment(Fragment fragment) { public void onAttachFragment(@NonNull Fragment fragment) {
fragList.add(new WeakReference<>(fragment)); fragList.add(new WeakReference<>(fragment));
if (trackBitmapDrawer != null && fragment instanceof TrackBitmapDrawerListener) { if (trackBitmapDrawer != null && fragment instanceof TrackBitmapDrawerListener) {
trackBitmapDrawer.addListener((TrackBitmapDrawerListener) fragment); trackBitmapDrawer.addListener((TrackBitmapDrawerListener) fragment);
@ -218,7 +191,7 @@ public class TrackActivity extends TabActivity {
} }
@Override @Override
protected void onSaveInstanceState(Bundle outState) { protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState); super.onSaveInstanceState(outState);
if (viewPager.getCurrentItem() == 1) { if (viewPager.getCurrentItem() == 1) {
outState.putBoolean(OPEN_POINTS_TAB, true); outState.putBoolean(OPEN_POINTS_TAB, true);
@ -314,23 +287,23 @@ public class TrackActivity extends TabActivity {
@Nullable @Nullable
public GPXFile getGpx() { public GPXFile getGpx() {
return gpxFile; return displayHelper.getGpx();
} }
@Nullable @Nullable
public GpxDataItem getGpxDataItem() { public GpxDataItem getGpxDataItem() {
return gpxDataItem; return displayHelper.getGpxDataItem();
} }
private void onGPXFileReady(@Nullable GPXFile gpxFile) { public void onGPXFileReady(@Nullable GPXFile gpxFile) {
setGpx(gpxFile); setGpx(gpxFile);
setGpxDataItem(file != null ? app.getGpxDbHelper().getItem(file) : null); setGpxDataItem(getFile() != null ? app.getGpxDbHelper().getItem(getFile()) : null);
WindowManager mgr = (WindowManager) getSystemService(Context.WINDOW_SERVICE); WindowManager mgr = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
if (gpxFile != null && mgr != null) { if (gpxFile != null && mgr != null) {
DisplayMetrics dm = new DisplayMetrics(); DisplayMetrics dm = new DisplayMetrics();
mgr.getDefaultDisplay().getMetrics(dm); mgr.getDefaultDisplay().getMetrics(dm);
trackBitmapDrawer = new TrackBitmapDrawer(app, gpxFile, getGpxDataItem(), getRect(), dm.density, dm.widthPixels, AndroidUtils.dpToPx(app, 152f)); trackBitmapDrawer = new TrackBitmapDrawer(app, gpxFile, getGpxDataItem(), displayHelper.getRect(), dm.density, dm.widthPixels, AndroidUtils.dpToPx(app, 152f));
} }
for (WeakReference<Fragment> f : fragList) { for (WeakReference<Fragment> f : fragList) {
@ -397,23 +370,6 @@ public class TrackActivity extends TabActivity {
} }
} }
public boolean setJoinSegments(boolean joinSegments) {
if (gpxDataItem != null) {
boolean updated = app.getGpxDbHelper().updateJoinSegments(gpxDataItem, joinSegments);
SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(gpxFile.path);
if (updated && selectedGpxFile != null) {
selectedGpxFile.setJoinSegments(joinSegments);
}
return updated;
}
return false;
}
public boolean isJoinSegments() {
return gpxDataItem != null && gpxDataItem.isJoinSegments();
}
private static class GPXFileLoaderTask extends AsyncTask<Void, Void, GPXFile> { private static class GPXFileLoaderTask extends AsyncTask<Void, Void, GPXFile> {
private OsmandApplication app; private OsmandApplication app;
@ -428,7 +384,7 @@ public class TrackActivity extends TabActivity {
GPXFileLoaderTask(@NonNull TrackActivity activity) { GPXFileLoaderTask(@NonNull TrackActivity activity) {
this.activityRef = new WeakReference<>(activity); this.activityRef = new WeakReference<>(activity);
app = activity.getMyApplication(); app = activity.getMyApplication();
file = activity.file; file = activity.getDisplayHelper().getFile();
} }
protected void onPreExecute() { protected void onPreExecute() {
@ -477,11 +433,11 @@ public class TrackActivity extends TabActivity {
if (activity != null) { if (activity != null) {
activity.setSupportProgressBarIndeterminateVisibility(false); activity.setSupportProgressBarIndeterminateVisibility(false);
if (result != null) { if (result != null) {
final GpxSelectionHelper helper = app.getSelectedGpxHelper(); GpxSelectionHelper helper = app.getSelectedGpxHelper();
if (showTemporarily) { if (showTemporarily) {
helper.selectGpxFile(result, false, false); helper.selectGpxFile(result, false, false);
} else { } else {
final SelectedGpxFile selectedGpx = helper.getSelectedFileByPath(result.path); SelectedGpxFile selectedGpx = helper.getSelectedFileByPath(result.path);
if (selectedGpx != null && result.error == null) { if (selectedGpx != null && result.error == null) {
selectedGpx.setGpxFile(result, app); selectedGpx.setGpxFile(result, app);
} }

View file

@ -70,7 +70,7 @@ public abstract class ContextMenuFragment extends BaseOsmAndFragment
private OnLayoutChangeListener containerLayoutListener; private OnLayoutChangeListener containerLayoutListener;
private View topShadow; private View topShadow;
private ViewGroup topView; private ViewGroup topView;
private View bottomScrollView; private ViewGroup bottomScrollView;
private LinearLayout cardsContainer; private LinearLayout cardsContainer;
private FrameLayout bottomContainer; private FrameLayout bottomContainer;
@ -259,7 +259,7 @@ public abstract class ContextMenuFragment extends BaseOsmAndFragment
return bottomContainer; return bottomContainer;
} }
public View getBottomScrollView() { public ViewGroup getBottomScrollView() {
return bottomScrollView; return bottomScrollView;
} }

View file

@ -14,6 +14,7 @@ import net.osmand.GPXUtilities.Route;
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.GPXUtilities.Metadata;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R; import net.osmand.plus.R;
import net.osmand.plus.Version; import net.osmand.plus.Version;
@ -91,6 +92,10 @@ class SaveGpxRouteAsyncTask extends AsyncTask<Void, Void, Exception> {
backupFile = FileUtils.backupFile(app, outFile); backupFile = FileUtils.backupFile(app, outFile);
String trackName = Algorithms.getFileNameWithoutExtension(outFile); String trackName = Algorithms.getFileNameWithoutExtension(outFile);
GPXFile gpx = generateGpxFile(measurementLayer, editingCtx, trackName, gpxFile); GPXFile gpx = generateGpxFile(measurementLayer, editingCtx, trackName, gpxFile);
if (gpxFile.metadata != null) {
gpx.metadata = new Metadata();
gpx.metadata.getExtensionsToWrite().putAll(gpxFile.metadata.getExtensionsToRead());
}
if (!gpx.showCurrentTrack) { if (!gpx.showCurrentTrack) {
res = GPXUtilities.writeGpxFile(outFile, gpx); res = GPXUtilities.writeGpxFile(outFile, gpx);
} }

View file

@ -0,0 +1,88 @@
package net.osmand.plus.myplaces;
import android.os.AsyncTask;
import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.activities.SavingTrackHelper;
import net.osmand.plus.mapmarkers.MapMarkersGroup;
import net.osmand.plus.mapmarkers.MapMarkersHelper;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.Set;
class DeletePointsTask extends AsyncTask<Void, Void, Void> {
private OsmandApplication app;
private GPXFile gpx;
private Set<GpxDisplayItem> selectedItems;
private WeakReference<OnPointsDeleteListener> listenerRef;
DeletePointsTask(OsmandApplication app, GPXFile gpxFile, Set<GpxDisplayItem> selectedItems, OnPointsDeleteListener listener) {
this.app = app;
this.gpx = gpxFile;
this.selectedItems = selectedItems;
this.listenerRef = new WeakReference<>(listener);
}
@Override
protected void onPreExecute() {
OnPointsDeleteListener listener = listenerRef.get();
if (listener != null) {
listener.onPointsDeletionStarted();
}
}
@Override
protected Void doInBackground(Void... params) {
SavingTrackHelper savingTrackHelper = app.getSavingTrackHelper();
if (gpx != null) {
for (GpxDisplayItem item : selectedItems) {
if (gpx.showCurrentTrack) {
savingTrackHelper.deletePointData(item.locationStart);
} else {
if (item.group.getType() == GpxDisplayItemType.TRACK_POINTS) {
gpx.deleteWptPt(item.locationStart);
} else if (item.group.getType() == GpxDisplayItemType.TRACK_ROUTE_POINTS) {
gpx.deleteRtePt(item.locationStart);
}
}
}
if (!gpx.showCurrentTrack) {
GPXUtilities.writeGpxFile(new File(gpx.path), gpx);
boolean selected = app.getSelectedGpxHelper().getSelectedFileByPath(gpx.path) != null;
if (selected) {
app.getSelectedGpxHelper().setGpxFileToDisplay(gpx);
}
}
syncGpx(gpx);
}
return null;
}
private void syncGpx(GPXFile gpxFile) {
MapMarkersHelper helper = app.getMapMarkersHelper();
MapMarkersGroup group = helper.getMarkersGroup(gpxFile);
if (group != null) {
helper.runSynchronization(group);
}
}
@Override
protected void onPostExecute(Void aVoid) {
OnPointsDeleteListener listener = listenerRef.get();
if (listener != null) {
listener.onPointsDeleted();
}
}
public interface OnPointsDeleteListener {
void onPointsDeletionStarted();
void onPointsDeleted();
}
}

View file

@ -0,0 +1,695 @@
package net.osmand.plus.myplaces;
import android.annotation.SuppressLint;
import android.graphics.Matrix;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.SwitchCompat;
import androidx.viewpager.widget.PagerAdapter;
import com.github.mikephil.charting.charts.LineChart;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.data.LineData;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.github.mikephil.charting.listener.ChartTouchListener.ChartGesture;
import com.github.mikephil.charting.listener.OnChartGestureListener;
import com.github.mikephil.charting.listener.OnChartValueSelectedListener;
import net.osmand.GPXUtilities;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.GPXTrackAnalysis;
import net.osmand.GPXUtilities.Track;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.plus.GPXDatabase.GpxDataItem;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.OsmAndFormatter;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.helpers.GpxUiHelper;
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetAxisType;
import net.osmand.plus.helpers.GpxUiHelper.LineGraphType;
import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet;
import net.osmand.plus.track.TrackDisplayHelper;
import net.osmand.plus.views.controls.PagerSlidingTabStrip;
import net.osmand.plus.views.controls.PagerSlidingTabStrip.CustomTabProvider;
import net.osmand.plus.views.controls.WrapContentHeightViewPager.ViewAtPositionInterface;
import net.osmand.util.Algorithms;
import net.osmand.util.MapUtils;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static net.osmand.plus.helpers.GpxUiHelper.LineGraphType.ALTITUDE;
import static net.osmand.plus.helpers.GpxUiHelper.LineGraphType.SLOPE;
import static net.osmand.plus.helpers.GpxUiHelper.LineGraphType.SPEED;
public class GPXItemPagerAdapter extends PagerAdapter implements CustomTabProvider, ViewAtPositionInterface {
private OsmandApplication app;
private UiUtilities iconsCache;
private TrackDisplayHelper displayHelper;
private Map<GPXTabItemType, List<ILineDataSet>> dataSetsMap = new HashMap<>();
private WptPt selectedWpt;
private TrkSegment segment;
private GpxDisplayItem gpxItem;
private GPXTabItemType[] tabTypes;
private PagerSlidingTabStrip tabs;
private SparseArray<View> views = new SparseArray<>();
private SegmentActionsListener actionsListener;
private boolean chartClicked;
public GPXItemPagerAdapter(@NonNull PagerSlidingTabStrip tabs,
@NonNull GpxDisplayItem gpxItem,
@NonNull TrackDisplayHelper displayHelper,
@NonNull SegmentActionsListener actionsListener) {
super();
this.tabs = tabs;
this.gpxItem = gpxItem;
this.displayHelper = displayHelper;
this.actionsListener = actionsListener;
app = (OsmandApplication) tabs.getContext().getApplicationContext();
iconsCache = app.getUIUtilities();
fetchTabTypes();
}
private void fetchTabTypes() {
List<GPXTabItemType> tabTypeList = new ArrayList<>();
tabTypeList.add(GPXTabItemType.GPX_TAB_ITEM_GENERAL);
if (gpxItem != null && gpxItem.analysis != null) {
if (gpxItem.analysis.hasElevationData) {
tabTypeList.add(GPXTabItemType.GPX_TAB_ITEM_ALTITUDE);
}
if (gpxItem.analysis.isSpeedSpecified()) {
tabTypeList.add(GPXTabItemType.GPX_TAB_ITEM_SPEED);
}
}
tabTypes = tabTypeList.toArray(new GPXTabItemType[0]);
}
private List<ILineDataSet> getDataSets(LineChart chart, GPXTabItemType tabType,
LineGraphType firstType, LineGraphType secondType) {
List<ILineDataSet> dataSets = dataSetsMap.get(tabType);
if (dataSets == null && chart != null) {
GPXTrackAnalysis analysis = gpxItem.analysis;
GpxDataItem gpxDataItem = displayHelper.getGpxDataItem();
boolean calcWithoutGaps = gpxItem.isGeneralTrack() && gpxDataItem != null && !gpxDataItem.isJoinSegments();
dataSets = GpxUiHelper.getDataSets(chart, app, analysis, firstType, secondType, calcWithoutGaps);
dataSetsMap.put(tabType, dataSets);
}
return dataSets;
}
private TrkSegment getTrackSegment(LineChart chart) {
if (segment == null) {
LineData lineData = chart.getLineData();
List<ILineDataSet> ds = lineData != null ? lineData.getDataSets() : null;
if (ds != null && ds.size() > 0) {
for (GPXUtilities.Track t : gpxItem.group.getGpx().tracks) {
for (TrkSegment s : t.segments) {
if (s.points.size() > 0 && s.points.get(0).equals(gpxItem.analysis.locationStart)) {
segment = s;
break;
}
}
if (segment != null) {
break;
}
}
}
}
return segment;
}
private WptPt getPoint(LineChart chart, float pos) {
WptPt wpt = null;
LineData lineData = chart.getLineData();
List<ILineDataSet> ds = lineData != null ? lineData.getDataSets() : null;
if (ds != null && ds.size() > 0) {
TrkSegment segment = getTrackSegment(chart);
OrderedLineDataSet dataSet = (OrderedLineDataSet) ds.get(0);
if (gpxItem.chartAxisType == GPXDataSetAxisType.TIME) {
float time = pos * 1000;
for (WptPt p : segment.points) {
if (p.time - gpxItem.analysis.startTime >= time) {
wpt = p;
break;
}
}
} else {
float distance = pos * dataSet.getDivX();
double totalDistance = 0;
for (int i = 0; i < segment.points.size(); i++) {
WptPt currentPoint = segment.points.get(i);
if (i != 0) {
WptPt previousPoint = segment.points.get(i - 1);
totalDistance += MapUtils.getDistance(previousPoint.lat, previousPoint.lon, currentPoint.lat, currentPoint.lon);
}
if (currentPoint.distance >= distance || Math.abs(totalDistance - distance) < 0.1) {
wpt = currentPoint;
break;
}
}
}
}
return wpt;
}
@Override
public int getCount() {
return tabTypes.length;
}
@Override
public CharSequence getPageTitle(int position) {
return tabTypes[position].toHumanString(app);
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
GPXTabItemType tabType = tabTypes[position];
View view = getViewForTab(container, tabType);
GPXFile gpxFile = displayHelper.getGpx();
if (gpxFile != null && gpxItem != null) {
GPXTrackAnalysis analysis = gpxItem.analysis;
LineChart chart = view.findViewById(R.id.chart);
setupChart(view, chart);
switch (tabType) {
case GPX_TAB_ITEM_GENERAL:
setupGeneralTab(view, chart, analysis, gpxFile, position);
break;
case GPX_TAB_ITEM_ALTITUDE:
setupAltitudeTab(view, chart, analysis, gpxFile, position);
break;
case GPX_TAB_ITEM_SPEED:
setupSpeedTab(view, chart, analysis, gpxFile, position);
break;
}
}
container.addView(view, 0);
views.put(position, view);
return view;
}
private View getViewForTab(@NonNull ViewGroup container, @NonNull GPXTabItemType tabType) {
LayoutInflater inflater = LayoutInflater.from(container.getContext());
switch (tabType) {
case GPX_TAB_ITEM_ALTITUDE:
return inflater.inflate(R.layout.gpx_item_altitude, container, false);
case GPX_TAB_ITEM_SPEED:
return inflater.inflate(R.layout.gpx_item_speed, container, false);
case GPX_TAB_ITEM_GENERAL:
default:
return inflater.inflate(R.layout.gpx_item_general, container, false);
}
}
private void setupSpeedTab(View view, LineChart chart, GPXTrackAnalysis analysis, GPXFile gpxFile, int position) {
if (analysis != null && analysis.isSpeedSpecified()) {
if (analysis.hasSpeedData) {
GpxUiHelper.setupGPXChart(app, chart, 4);
chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_SPEED, SPEED, null)));
updateChart(chart);
chart.setVisibility(View.VISIBLE);
} else {
chart.setVisibility(View.GONE);
}
((ImageView) view.findViewById(R.id.average_icon))
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_speed));
((ImageView) view.findViewById(R.id.max_icon))
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_max_speed));
((ImageView) view.findViewById(R.id.time_moving_icon))
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_time_span));
((ImageView) view.findViewById(R.id.distance_icon))
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_polygom_dark));
String avg = OsmAndFormatter.getFormattedSpeed(analysis.avgSpeed, app);
String max = OsmAndFormatter.getFormattedSpeed(analysis.maxSpeed, app);
((TextView) view.findViewById(R.id.average_text)).setText(avg);
((TextView) view.findViewById(R.id.max_text)).setText(max);
view.findViewById(R.id.gpx_join_gaps_container).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (displayHelper.setJoinSegments(!displayHelper.isJoinSegments())) {
for (int i = 0; i < getCount(); i++) {
View view = getViewAtPosition(i);
updateJoinGapsInfo(view, i);
}
}
}
});
} else {
chart.setVisibility(View.GONE);
view.findViewById(R.id.average_max).setVisibility(View.GONE);
view.findViewById(R.id.list_divider).setVisibility(View.GONE);
view.findViewById(R.id.time_distance).setVisibility(View.GONE);
}
updateJoinGapsInfo(view, position);
view.findViewById(R.id.analyze_on_map).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
openAnalyzeOnMap(GPXTabItemType.GPX_TAB_ITEM_SPEED);
}
});
if (gpxFile.showCurrentTrack) {
view.findViewById(R.id.split_interval).setVisibility(View.GONE);
} else {
view.findViewById(R.id.split_interval).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
openSplitIntervalScreen();
}
});
}
ImageView overflowMenu = view.findViewById(R.id.overflow_menu);
if (!gpxItem.group.getTrack().generalTrack) {
setupOptionsPopupMenu(overflowMenu, false);
} else {
overflowMenu.setVisibility(View.GONE);
}
}
private void setupOptionsPopupMenu(ImageView overflowMenu, final boolean confirmDeletion) {
overflowMenu.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_overflow_menu_white));
overflowMenu.setVisibility(View.VISIBLE);
overflowMenu.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
actionsListener.showOptionsPopupMenu(view, getTrkSegment(), confirmDeletion);
}
});
}
private void setupAltitudeTab(View view, LineChart chart, GPXTrackAnalysis analysis, GPXFile gpxFile, int position) {
if (analysis != null) {
if (analysis.hasElevationData) {
GpxUiHelper.setupGPXChart(app, chart, 4);
chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_ALTITUDE, ALTITUDE, SLOPE)));
updateChart(chart);
chart.setVisibility(View.VISIBLE);
} else {
chart.setVisibility(View.GONE);
}
((ImageView) view.findViewById(R.id.average_icon))
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_altitude_average));
((ImageView) view.findViewById(R.id.range_icon))
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_altitude_average));
((ImageView) view.findViewById(R.id.ascent_icon))
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_altitude_ascent));
((ImageView) view.findViewById(R.id.descent_icon))
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_altitude_descent));
String min = OsmAndFormatter.getFormattedAlt(analysis.minElevation, app);
String max = OsmAndFormatter.getFormattedAlt(analysis.maxElevation, app);
String asc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationUp, app);
String desc = OsmAndFormatter.getFormattedAlt(analysis.diffElevationDown, app);
((TextView) view.findViewById(R.id.average_text))
.setText(OsmAndFormatter.getFormattedAlt(analysis.avgElevation, app));
((TextView) view.findViewById(R.id.range_text)).setText(String.format("%s - %s", min, max));
((TextView) view.findViewById(R.id.ascent_text)).setText(asc);
((TextView) view.findViewById(R.id.descent_text)).setText(desc);
view.findViewById(R.id.gpx_join_gaps_container).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (displayHelper.setJoinSegments(!displayHelper.isJoinSegments())) {
for (int i = 0; i < getCount(); i++) {
View view = getViewAtPosition(i);
updateJoinGapsInfo(view, i);
}
}
}
});
} else {
chart.setVisibility(View.GONE);
view.findViewById(R.id.average_range).setVisibility(View.GONE);
view.findViewById(R.id.list_divider).setVisibility(View.GONE);
view.findViewById(R.id.ascent_descent).setVisibility(View.GONE);
}
updateJoinGapsInfo(view, position);
view.findViewById(R.id.analyze_on_map).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
openAnalyzeOnMap(GPXTabItemType.GPX_TAB_ITEM_ALTITUDE);
}
});
if (gpxFile.showCurrentTrack) {
view.findViewById(R.id.split_interval).setVisibility(View.GONE);
} else {
view.findViewById(R.id.split_interval).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
openSplitIntervalScreen();
}
});
}
ImageView overflowMenu = view.findViewById(R.id.overflow_menu);
if (!gpxItem.group.getTrack().generalTrack) {
setupOptionsPopupMenu(overflowMenu, false);
} else {
overflowMenu.setVisibility(View.GONE);
}
}
private void setupGeneralTab(View view, LineChart chart, GPXTrackAnalysis analysis, GPXFile gpxFile, int position) {
if (analysis != null) {
if (analysis.hasElevationData || analysis.hasSpeedData) {
GpxUiHelper.setupGPXChart(app, chart, 4);
chart.setData(new LineData(getDataSets(chart, GPXTabItemType.GPX_TAB_ITEM_GENERAL, ALTITUDE, SPEED)));
updateChart(chart);
chart.setVisibility(View.VISIBLE);
} else {
chart.setVisibility(View.GONE);
}
((ImageView) view.findViewById(R.id.distance_icon))
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_polygom_dark));
((ImageView) view.findViewById(R.id.duration_icon))
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_time_span));
((ImageView) view.findViewById(R.id.start_time_icon))
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_time_start));
((ImageView) view.findViewById(R.id.end_time_icon))
.setImageDrawable(iconsCache.getThemedIcon(R.drawable.ic_action_time_end));
view.findViewById(R.id.gpx_join_gaps_container).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (displayHelper.setJoinSegments(!displayHelper.isJoinSegments())) {
actionsListener.updateContent();
for (int i = 0; i < getCount(); i++) {
View view = getViewAtPosition(i);
updateJoinGapsInfo(view, i);
}
}
}
});
if (analysis.timeSpan > 0) {
DateFormat tf = SimpleDateFormat.getTimeInstance(DateFormat.SHORT);
DateFormat df = SimpleDateFormat.getDateInstance(DateFormat.MEDIUM);
Date start = new Date(analysis.startTime);
((TextView) view.findViewById(R.id.start_time_text)).setText(tf.format(start));
((TextView) view.findViewById(R.id.start_date_text)).setText(df.format(start));
Date end = new Date(analysis.endTime);
((TextView) view.findViewById(R.id.end_time_text)).setText(tf.format(end));
((TextView) view.findViewById(R.id.end_date_text)).setText(df.format(end));
} else {
view.findViewById(R.id.list_divider).setVisibility(View.GONE);
view.findViewById(R.id.start_end_time).setVisibility(View.GONE);
}
} else {
chart.setVisibility(View.GONE);
view.findViewById(R.id.distance_time_span).setVisibility(View.GONE);
view.findViewById(R.id.list_divider).setVisibility(View.GONE);
view.findViewById(R.id.start_end_time).setVisibility(View.GONE);
}
updateJoinGapsInfo(view, position);
view.findViewById(R.id.analyze_on_map).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
openAnalyzeOnMap(GPXTabItemType.GPX_TAB_ITEM_GENERAL);
}
});
if (gpxFile.showCurrentTrack) {
view.findViewById(R.id.split_interval).setVisibility(View.GONE);
} else {
view.findViewById(R.id.split_interval).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
openSplitIntervalScreen();
}
});
}
ImageView overflowMenu = view.findViewById(R.id.overflow_menu);
if (!gpxItem.group.getTrack().generalTrack) {
setupOptionsPopupMenu(overflowMenu, true);
} else {
overflowMenu.setVisibility(View.GONE);
}
}
private void setupChart(final View view, final LineChart chart) {
chart.setHighlightPerDragEnabled(chartClicked);
chart.setOnClickListener(new View.OnClickListener() {
@SuppressLint("ClickableViewAccessibility")
@Override
public void onClick(View view) {
if (!chartClicked) {
chartClicked = true;
if (selectedWpt != null) {
actionsListener.onPointSelected(selectedWpt.lat, selectedWpt.lon);
}
}
}
});
chart.setOnTouchListener(new View.OnTouchListener() {
private float listViewYPos;
@Override
public boolean onTouch(View v, MotionEvent event) {
if (chartClicked) {
actionsListener.onChartTouch();
if (!chart.isHighlightPerDragEnabled()) {
chart.setHighlightPerDragEnabled(true);
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
listViewYPos = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
actionsListener.scrollBy(Math.round(listViewYPos - event.getRawY()));
listViewYPos = event.getRawY();
break;
}
}
return false;
}
});
chart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() {
@Override
public void onValueSelected(Entry e, Highlight h) {
WptPt wpt = getPoint(chart, h.getX());
selectedWpt = wpt;
if (chartClicked && wpt != null) {
actionsListener.onPointSelected(wpt.lat, wpt.lon);
}
}
@Override
public void onNothingSelected() {
}
});
chart.setOnChartGestureListener(new OnChartGestureListener() {
float highlightDrawX = -1;
@Override
public void onChartGestureStart(MotionEvent me, ChartGesture lastPerformedGesture) {
if (chart.getHighlighted() != null && chart.getHighlighted().length > 0) {
highlightDrawX = chart.getHighlighted()[0].getDrawX();
} else {
highlightDrawX = -1;
}
}
@Override
public void onChartGestureEnd(MotionEvent me, ChartGesture lastPerformedGesture) {
gpxItem.chartMatrix = new Matrix(chart.getViewPortHandler().getMatrixTouch());
Highlight[] highlights = chart.getHighlighted();
if (highlights != null && highlights.length > 0) {
gpxItem.chartHighlightPos = highlights[0].getX();
} else {
gpxItem.chartHighlightPos = -1;
}
if (chartClicked) {
for (int i = 0; i < getCount(); i++) {
View v = getViewAtPosition(i);
if (v != view) {
updateChart(i);
}
}
}
}
@Override
public void onChartLongPressed(MotionEvent me) {
}
@Override
public void onChartDoubleTapped(MotionEvent me) {
}
@Override
public void onChartSingleTapped(MotionEvent me) {
}
@Override
public void onChartFling(MotionEvent me1, MotionEvent me2, float velocityX, float velocityY) {
}
@Override
public void onChartScale(MotionEvent me, float scaleX, float scaleY) {
}
@Override
public void onChartTranslate(MotionEvent me, float dX, float dY) {
if (chartClicked && highlightDrawX != -1) {
Highlight h = chart.getHighlightByTouchPoint(highlightDrawX, 0f);
if (h != null) {
chart.highlightValue(h);
WptPt wpt = getPoint(chart, h.getX());
if (wpt != null) {
actionsListener.onPointSelected(wpt.lat, wpt.lon);
}
}
}
}
});
}
@Override
public void destroyItem(@NonNull ViewGroup collection, int position, @NonNull Object view) {
views.remove(position);
collection.removeView((View) view);
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
return view == object;
}
@Override
public View getCustomTabView(@NonNull ViewGroup parent, int position) {
View tab = LayoutInflater.from(parent.getContext()).inflate(R.layout.gpx_tab, parent, false);
tab.setTag(tabTypes[position].name());
deselect(tab);
return tab;
}
@Override
public void select(View tab) {
GPXTabItemType tabType = GPXTabItemType.valueOf((String) tab.getTag());
ImageView img = tab.findViewById(R.id.tab_image);
switch (tabs.getTabSelectionType()) {
case ALPHA:
img.setAlpha(tabs.getTabTextSelectedAlpha());
break;
case SOLID_COLOR:
img.setImageDrawable(iconsCache.getPaintedIcon(tabType.getIconId(), tabs.getTextColor()));
break;
}
}
@Override
public void deselect(View tab) {
GPXTabItemType tabType = GPXTabItemType.valueOf((String) tab.getTag());
ImageView img = tab.findViewById(R.id.tab_image);
switch (tabs.getTabSelectionType()) {
case ALPHA:
img.setAlpha(tabs.getTabTextAlpha());
break;
case SOLID_COLOR:
img.setImageDrawable(iconsCache.getPaintedIcon(tabType.getIconId(), tabs.getTabInactiveTextColor()));
break;
}
}
@Override
public View getViewAtPosition(int position) {
return views.get(position);
}
void updateChart(int position) {
View view = getViewAtPosition(position);
if (view != null) {
updateChart((LineChart) view.findViewById(R.id.chart));
}
}
void updateJoinGapsInfo(View view, int position) {
if (view != null) {
GPXTrackAnalysis analysis = gpxItem.analysis;
GPXTabItemType tabType = tabTypes[position];
boolean visible = gpxItem.isGeneralTrack() && analysis != null && tabType.equals(GPXTabItemType.GPX_TAB_ITEM_GENERAL);
AndroidUiHelper.updateVisibility(view.findViewById(R.id.gpx_join_gaps_container), visible);
boolean joinSegments = displayHelper.isJoinSegments();
((SwitchCompat) view.findViewById(R.id.gpx_join_gaps_switch)).setChecked(joinSegments);
if (analysis != null) {
if (tabType.equals(GPXTabItemType.GPX_TAB_ITEM_GENERAL)) {
float totalDistance = !joinSegments && gpxItem.isGeneralTrack() ? analysis.totalDistanceWithoutGaps : analysis.totalDistance;
float timeSpan = !joinSegments && gpxItem.isGeneralTrack() ? analysis.timeSpanWithoutGaps : analysis.timeSpan;
((TextView) view.findViewById(R.id.distance_text)).setText(OsmAndFormatter.getFormattedDistance(totalDistance, app));
((TextView) view.findViewById(R.id.duration_text)).setText(Algorithms.formatDuration((int) (timeSpan / 1000), app.accessibilityEnabled()));
} else if (tabType.equals(GPXTabItemType.GPX_TAB_ITEM_SPEED)) {
long timeMoving = !joinSegments && gpxItem.isGeneralTrack() ? analysis.timeMovingWithoutGaps : analysis.timeMoving;
float totalDistanceMoving = !joinSegments && gpxItem.isGeneralTrack() ? analysis.totalDistanceMovingWithoutGaps : analysis.totalDistanceMoving;
((TextView) view.findViewById(R.id.time_moving_text)).setText(Algorithms.formatDuration((int) (timeMoving / 1000), app.accessibilityEnabled()));
((TextView) view.findViewById(R.id.distance_text)).setText(OsmAndFormatter.getFormattedDistance(totalDistanceMoving, app));
}
}
}
}
void updateChart(LineChart chart) {
if (chart != null && !chart.isEmpty()) {
if (gpxItem.chartMatrix != null) {
chart.getViewPortHandler().refresh(new Matrix(gpxItem.chartMatrix), chart, true);
}
if (gpxItem.chartHighlightPos != -1) {
chart.highlightValue(gpxItem.chartHighlightPos, 0);
} else {
chart.highlightValue(null);
}
}
}
private TrkSegment getTrkSegment() {
for (Track track : gpxItem.group.getGpx().tracks) {
if (!track.generalTrack && !gpxItem.isGeneralTrack() || track.generalTrack && gpxItem.isGeneralTrack()) {
for (TrkSegment segment : track.segments) {
if (segment.points.size() > 0 && segment.points.get(0).equals(gpxItem.analysis.locationStart)) {
return segment;
}
}
}
}
return null;
}
void openAnalyzeOnMap(GPXTabItemType tabType) {
List<ILineDataSet> ds = getDataSets(null, tabType, null, null);
actionsListener.openAnalyzeOnMap(gpxItem, ds, tabType);
}
private void openSplitIntervalScreen() {
actionsListener.openSplitInterval(gpxItem, getTrkSegment());
}
}

View file

@ -0,0 +1,32 @@
package net.osmand.plus.myplaces;
import android.content.Context;
import androidx.annotation.DrawableRes;
import androidx.annotation.StringRes;
import net.osmand.plus.R;
public enum GPXTabItemType {
GPX_TAB_ITEM_GENERAL(R.string.shared_string_overview, R.drawable.ic_action_polygom_dark),
GPX_TAB_ITEM_ALTITUDE(R.string.altitude, R.drawable.ic_action_altitude_average),
GPX_TAB_ITEM_SPEED(R.string.map_widget_speed, R.drawable.ic_action_speed);
private final int iconId;
private final int titleId;
GPXTabItemType(@StringRes int titleId, @DrawableRes int iconId) {
this.iconId = iconId;
this.titleId = titleId;
}
@DrawableRes
public int getIconId() {
return iconId;
}
public String toHumanString(Context ctx) {
return ctx.getString(titleId);
}
}

View file

@ -0,0 +1,27 @@
package net.osmand.plus.myplaces;
import android.view.View;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import java.util.List;
public interface SegmentActionsListener {
void updateContent();
void onChartTouch();
void scrollBy(int px);
void onPointSelected(double lat, double lon);
void openSplitInterval(GpxDisplayItem gpxItem, TrkSegment trkSegment);
void showOptionsPopupMenu(View view, TrkSegment trkSegment, boolean confirmDeletion);
void openAnalyzeOnMap(GpxDisplayItem gpxItem, List<ILineDataSet> dataSets, GPXTabItemType tabType);
}

View file

@ -0,0 +1,91 @@
package net.osmand.plus.myplaces;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import net.osmand.AndroidUtils;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.UiUtilities;
import net.osmand.plus.track.TrackDisplayHelper;
import net.osmand.plus.views.controls.PagerSlidingTabStrip;
import net.osmand.plus.views.controls.WrapContentHeightViewPager;
import java.util.List;
public class SegmentGPXAdapter extends ArrayAdapter<GpxDisplayItem> {
private OsmandApplication app;
private TrackDisplayHelper displayHelper;
private SegmentActionsListener listener;
private boolean nightMode;
public SegmentGPXAdapter(@NonNull Context context, @NonNull List<GpxDisplayItem> items,
@NonNull TrackDisplayHelper displayHelper,
@NonNull SegmentActionsListener listener,
boolean nightMode) {
super(context, R.layout.gpx_list_item_tab_content, items);
this.app = (OsmandApplication) context.getApplicationContext();
this.displayHelper = displayHelper;
this.listener = listener;
this.nightMode = nightMode;
}
@Override
public boolean isEmpty() {
return false;
}
@NonNull
@Override
public View getView(int position, View convertView, @NonNull ViewGroup parent) {
View row = convertView;
boolean create = false;
if (row == null) {
create = true;
row = createGpxTabsView(displayHelper, parent, listener, nightMode);
}
GpxDisplayItem item = getItem(position);
if (item != null) {
WrapContentHeightViewPager pager = row.findViewById(R.id.pager);
PagerSlidingTabStrip tabLayout = row.findViewById(R.id.sliding_tabs);
pager.setAdapter(new GPXItemPagerAdapter(tabLayout, item, displayHelper, listener));
if (create) {
tabLayout.setViewPager(pager);
} else {
tabLayout.notifyDataSetChanged(true);
}
}
return row;
}
public static View createGpxTabsView(TrackDisplayHelper displayHelper, ViewGroup root,
SegmentActionsListener listener, boolean nightMode) {
Context context = root.getContext();
View row = UiUtilities.getInflater(context, nightMode).inflate(R.layout.gpx_list_item_tab_content, root, false);
PagerSlidingTabStrip tabLayout = row.findViewById(R.id.sliding_tabs);
tabLayout.setTabBackground(R.color.color_transparent);
tabLayout.setIndicatorColorResource(nightMode ? R.color.active_color_primary_dark : R.color.active_color_primary_light);
tabLayout.setIndicatorBgColorResource(nightMode ? R.color.divider_color_dark : R.color.divider_color_light);
tabLayout.setIndicatorHeight(AndroidUtils.dpToPx(context, 1f));
if (!nightMode) {
tabLayout.setTextColor(tabLayout.getIndicatorColor());
tabLayout.setTabInactiveTextColor(ContextCompat.getColor(row.getContext(), R.color.text_color_secondary_light));
}
tabLayout.setTextSize(AndroidUtils.spToPx(context, 12f));
tabLayout.setShouldExpand(true);
WrapContentHeightViewPager pager = row.findViewById(R.id.pager);
pager.setSwipeable(false);
pager.setOffscreenPageLimit(2);
return row;
}
}

View file

@ -23,14 +23,18 @@ import android.widget.TextView;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.ListPopupWindow; import androidx.appcompat.widget.ListPopupWindow;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.DialogFragment; import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;
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.GPXTrackAnalysis; import net.osmand.GPXUtilities.GPXTrackAnalysis;
import net.osmand.GPXUtilities.TrkSegment;
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;
@ -39,8 +43,8 @@ import net.osmand.plus.OsmAndFormatter;
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.UiUtilities;
import net.osmand.plus.activities.TrackActivity;
import net.osmand.plus.helpers.FontCache; import net.osmand.plus.helpers.FontCache;
import net.osmand.plus.track.TrackDisplayHelper;
import net.osmand.util.Algorithms; import net.osmand.util.Algorithms;
import java.text.DateFormat; import java.text.DateFormat;
@ -54,7 +58,9 @@ import gnu.trove.list.array.TIntArrayList;
public class SplitSegmentDialogFragment extends DialogFragment { public class SplitSegmentDialogFragment extends DialogFragment {
public final static String TAG = "SPLIT_SEGMENT_DIALOG_FRAGMENT"; public final static String TAG = "SPLIT_SEGMENT_DIALOG_FRAGMENT";
private OsmandApplication app; private OsmandApplication app;
private TrackDisplayHelper displayHelper;
private SplitSegmentsAdapter adapter; private SplitSegmentsAdapter adapter;
private View headerView; private View headerView;
@ -78,8 +84,8 @@ public class SplitSegmentDialogFragment extends DialogFragment {
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
TrackActivity trackActivity = requireTrackActivity(); FragmentActivity activity = requireActivity();
app = trackActivity.getMyApplication(); app = (OsmandApplication) activity.getApplication();
ic = app.getUIUtilities(); ic = app.getUIUtilities();
boolean isLightTheme = app.getSettings().isLightContent(); boolean isLightTheme = app.getSettings().isLightContent();
int themeId = isLightTheme ? R.style.OsmandLightTheme : R.style.OsmandDarkTheme; int themeId = isLightTheme ? R.style.OsmandLightTheme : R.style.OsmandDarkTheme;
@ -89,9 +95,8 @@ public class SplitSegmentDialogFragment extends DialogFragment {
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
TrackActivity trackActivity = requireTrackActivity();
listView.setBackgroundColor(getResources().getColor( listView.setBackgroundColor(getResources().getColor(
trackActivity.getMyApplication().getSettings().isLightContent() ? R.color.activity_background_color_light app.getSettings().isLightContent() ? R.color.activity_background_color_light
: R.color.activity_background_color_dark)); : R.color.activity_background_color_dark));
} }
@ -103,7 +108,7 @@ public class SplitSegmentDialogFragment extends DialogFragment {
minMaxSpeedPaint.setStyle(Paint.Style.FILL); minMaxSpeedPaint.setStyle(Paint.Style.FILL);
minMaxSpeedTextBounds = new Rect(); minMaxSpeedTextBounds = new Rect();
TrackActivity trackActivity = requireTrackActivity(); AppCompatActivity trackActivity = (AppCompatActivity) getActivity();
final View view = trackActivity.getLayoutInflater().inflate(R.layout.split_segments_layout, container, false); final View view = trackActivity.getLayoutInflater().inflate(R.layout.split_segments_layout, container, false);
Toolbar toolbar = (Toolbar) view.findViewById(R.id.split_interval_toolbar); Toolbar toolbar = (Toolbar) view.findViewById(R.id.split_interval_toolbar);
@ -250,7 +255,7 @@ public class SplitSegmentDialogFragment extends DialogFragment {
} }
public void updateContent() { public void updateContent() {
if (getTrackActivity() != null) { if (getActivity() != null) {
adapter.clear(); adapter.clear();
adapter.setNotifyOnChange(false); adapter.setNotifyOnChange(false);
adapter.add(gpxItem); adapter.add(gpxItem);
@ -297,18 +302,7 @@ public class SplitSegmentDialogFragment extends DialogFragment {
@Nullable @Nullable
private GPXFile getGpx() { private GPXFile getGpx() {
TrackActivity trackActivity = getTrackActivity(); return displayHelper.getGpx();
return trackActivity != null ? trackActivity.getGpx() : null;
}
@Nullable
public TrackActivity getTrackActivity() {
return (TrackActivity) getActivity();
}
@NonNull
public TrackActivity requireTrackActivity() {
return (TrackActivity) requireActivity();
} }
private void prepareSplitIntervalAdapterData() { private void prepareSplitIntervalAdapterData() {
@ -346,7 +340,7 @@ public class SplitSegmentDialogFragment extends DialogFragment {
@NonNull @NonNull
private List<GpxDisplayGroup> getDisplayGroups() { private List<GpxDisplayGroup> getDisplayGroups() {
return filterGroups(true); return displayHelper.getDisplayGroups(filterTypes);
} }
private void addOptionSplit(int value, boolean distance, List<GpxDisplayGroup> model) { private void addOptionSplit(int value, boolean distance, List<GpxDisplayGroup> model) {
@ -374,32 +368,13 @@ public class SplitSegmentDialogFragment extends DialogFragment {
} }
} }
@NonNull
private List<GpxDisplayGroup> filterGroups(boolean useDisplayGroups) {
List<GpxDisplayGroup> groups = new ArrayList<>();
if (getTrackActivity() != null) {
List<GpxDisplayGroup> result = getTrackActivity().getGpxFile(useDisplayGroups);
for (GpxDisplayGroup group : result) {
boolean add = hasFilterType(group.getType());
if (add) {
groups.add(group);
}
}
}
return groups;
}
@NonNull @NonNull
private List<GpxDisplayItem> getSplitSegments() { private List<GpxDisplayItem> getSplitSegments() {
TrackActivity trackActivity = getTrackActivity();
List<GpxDisplayItem> splitSegments = new ArrayList<>(); List<GpxDisplayItem> splitSegments = new ArrayList<>();
if (trackActivity != null) { List<GpxDisplayGroup> result = displayHelper.getGpxFile(true);
List<GpxDisplayGroup> result = trackActivity.getGpxFile(true); if (result != null && result.size() > 0 && trkSegment.points.size() > 0) {
if (result != null && result.size() > 0 && trkSegment.points.size() > 0) { for (GpxDisplayGroup group : result) {
for (GpxDisplayGroup group : result) { splitSegments.addAll(collectDisplayItemsFromGroup(group));
splitSegments.addAll(collectDisplayItemsFromGroup(group));
}
} }
} }
return splitSegments; return splitSegments;
@ -445,7 +420,7 @@ public class SplitSegmentDialogFragment extends DialogFragment {
@Override @Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
GpxDisplayItem currentGpxDisplayItem = getItem(position); GpxDisplayItem currentGpxDisplayItem = getItem(position);
TrackActivity trackActivity = requireTrackActivity(); FragmentActivity trackActivity = requireActivity();
if (convertView == null) { if (convertView == null) {
convertView = trackActivity.getLayoutInflater().inflate(R.layout.gpx_split_segment_fragment, parent, false); convertView = trackActivity.getLayoutInflater().inflate(R.layout.gpx_split_segment_fragment, parent, false);
} }
@ -731,14 +706,15 @@ public class SplitSegmentDialogFragment extends DialogFragment {
} }
} }
public static boolean showInstance(@NonNull TrackActivity trackActivity, @NonNull GpxDisplayItem gpxItem, GPXUtilities.TrkSegment trkSegment) { public static boolean showInstance(@NonNull FragmentManager fragmentManager, @NonNull TrackDisplayHelper displayHelper,
@NonNull GpxDisplayItem gpxItem, @NonNull TrkSegment trkSegment) {
try { try {
SplitSegmentDialogFragment fragment = new SplitSegmentDialogFragment(); SplitSegmentDialogFragment fragment = new SplitSegmentDialogFragment();
fragment.setGpxItem(gpxItem); fragment.setGpxItem(gpxItem);
fragment.setTrkSegment(trkSegment); fragment.setTrkSegment(trkSegment);
fragment.setRetainInstance(true); fragment.setRetainInstance(true);
fragment.setJoinSegments(trackActivity.isJoinSegments()); fragment.displayHelper = displayHelper;
fragment.show(trackActivity.getSupportFragmentManager(), TAG); fragment.show(fragmentManager, TAG);
return true; return true;
} catch (RuntimeException e) { } catch (RuntimeException e) {
return false; return false;

View file

@ -39,10 +39,7 @@ import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.WptPt; import net.osmand.GPXUtilities.WptPt;
import net.osmand.PicassoUtils; import net.osmand.PicassoUtils;
import net.osmand.data.PointDescription; import net.osmand.data.PointDescription;
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.GpxDisplayGroup;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType; import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R; import net.osmand.plus.R;
@ -52,6 +49,7 @@ import net.osmand.plus.dialogs.GpxAppearanceAdapter;
import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener; import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener;
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;
import net.osmand.plus.track.TrackDisplayHelper;
import net.osmand.plus.widgets.tools.CropCircleTransformation; import net.osmand.plus.widgets.tools.CropCircleTransformation;
import net.osmand.plus.wikipedia.WikiArticleHelper; import net.osmand.plus.wikipedia.WikiArticleHelper;
import net.osmand.plus.wikivoyage.WikivoyageUtils; import net.osmand.plus.wikivoyage.WikivoyageUtils;
@ -59,8 +57,6 @@ import net.osmand.plus.wikivoyage.article.WikivoyageArticleDialogFragment;
import net.osmand.plus.wikivoyage.data.TravelArticle; import net.osmand.plus.wikivoyage.data.TravelArticle;
import net.osmand.render.RenderingRulesStorage; import net.osmand.render.RenderingRulesStorage;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_COLOR_ATTR; import static net.osmand.plus.dialogs.ConfigureMapMenu.CURRENT_TRACK_COLOR_ATTR;
@ -71,6 +67,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
private Fragment fragment; private Fragment fragment;
private ListView listView; private ListView listView;
private GpxDisplayItemType[] filterTypes; private GpxDisplayItemType[] filterTypes;
private TrackDisplayHelper displayHelper;
private boolean updateEnable; private boolean updateEnable;
private View headerView; private View headerView;
@ -99,11 +96,13 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
TrackActivityFragmentAdapter(@NonNull OsmandApplication app, TrackActivityFragmentAdapter(@NonNull OsmandApplication app,
@NonNull Fragment fragment, @NonNull Fragment fragment,
@NonNull ListView listView, @NonNull ListView listView,
@NonNull TrackDisplayHelper displayHelper,
@NonNull GpxDisplayItemType... filterTypes) { @NonNull GpxDisplayItemType... filterTypes) {
this.app = app; this.app = app;
this.fragment = fragment; this.fragment = fragment;
this.listView = listView; this.listView = listView;
this.filterTypes = filterTypes; this.filterTypes = filterTypes;
this.displayHelper = displayHelper;
} }
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
@ -191,14 +190,12 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
} }
private GPXFile getGpx() { private GPXFile getGpx() {
TrackActivity activity = getTrackActivity(); return displayHelper.getGpx();
return activity != null ? activity.getGpx() : null;
} }
@Nullable @Nullable
private GpxDataItem getGpxDataItem() { private GpxDataItem getGpxDataItem() {
TrackActivity activity = getTrackActivity(); return displayHelper.getGpxDataItem();
return activity != null ? activity.getGpxDataItem() : null;
} }
private void showTrackBitmapProgress() { private void showTrackBitmapProgress() {
@ -250,7 +247,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
imageView.setOnClickListener(new View.OnClickListener() { imageView.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
showTemporaryObjectOnMap(getRect()); showTemporaryObjectOnMap(displayHelper.getRect());
} }
}); });
final View appearanceView = headerView.findViewById(R.id.appearance_view); final View appearanceView = headerView.findViewById(R.id.appearance_view);
@ -258,7 +255,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
vis = (SwitchCompat) headerView.findViewById(R.id.showOnMapToggle); vis = (SwitchCompat) headerView.findViewById(R.id.showOnMapToggle);
final View bottomDivider = headerView.findViewById(R.id.bottom_divider); final View bottomDivider = headerView.findViewById(R.id.bottom_divider);
GPXFile gpxFile = getGpx(); GPXFile gpxFile = getGpx();
boolean gpxFileSelected = isGpxFileSelected(gpxFile); boolean gpxFileSelected = isGpxFileSelected(app, gpxFile);
boolean hasPath = gpxFile != null && (gpxFile.tracks.size() > 0 || gpxFile.routes.size() > 0); boolean hasPath = gpxFile != null && (gpxFile.tracks.size() > 0 || gpxFile.routes.size() > 0);
TrackActivity activity = getTrackActivity(); TrackActivity activity = getTrackActivity();
@ -318,7 +315,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
GPXFile gpx = getGpx(); GPXFile gpx = getGpx();
WptPt pointToShow = gpx != null ? gpx.findPointToShow() : null; WptPt pointToShow = gpx != null ? gpx.findPointToShow() : null;
if (activity != null && pointToShow != null) { if (activity != null && pointToShow != null) {
boolean gpxFileSelected = isGpxFileSelected(gpx); boolean gpxFileSelected = isGpxFileSelected(app, gpx);
if (!gpxFileSelected) { if (!gpxFileSelected) {
Intent intent = activity.getIntent(); Intent intent = activity.getIntent();
if (intent != null) { if (intent != null) {
@ -530,7 +527,7 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
} }
} }
public boolean isGpxFileSelected(GPXFile gpxFile) { public static boolean isGpxFileSelected(OsmandApplication app, GPXFile gpxFile) {
return gpxFile != null && return gpxFile != null &&
((gpxFile.showCurrentTrack && app.getSelectedGpxHelper().getSelectedCurrentRecordingTrack() != null) || ((gpxFile.showCurrentTrack && app.getSelectedGpxHelper().getSelectedCurrentRecordingTrack() != null) ||
(gpxFile.path != null && app.getSelectedGpxHelper().getSelectedFileByPath(gpxFile.path) != null)); (gpxFile.path != null && app.getSelectedGpxHelper().getSelectedFileByPath(gpxFile.path) != null));
@ -543,12 +540,6 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
} }
} }
@Nullable
private QuadRect getRect() {
TrackActivity activity = getTrackActivity();
return activity != null ? activity.getRect() : null;
}
public boolean isShowOnMap() { public boolean isShowOnMap() {
return vis != null && vis.isChecked(); return vis != null && vis.isChecked();
} }
@ -564,42 +555,6 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
menuFab.setVisibility(visible ? View.VISIBLE : View.GONE); menuFab.setVisibility(visible ? View.VISIBLE : View.GONE);
} }
@NonNull
public List<GpxDisplayGroup> getOriginalGroups() {
return filterGroups(false);
}
@NonNull
public List<GpxDisplayGroup> getDisplayGroups() {
return filterGroups(true);
}
private boolean hasFilterType(GpxDisplayItemType filterType) {
for (GpxDisplayItemType type : filterTypes) {
if (type == filterType) {
return true;
}
}
return false;
}
@NonNull
private List<GpxDisplayGroup> filterGroups(boolean useDisplayGroups) {
List<GpxDisplayGroup> groups = new ArrayList<>();
TrackActivity activity = getTrackActivity();
if (activity != null) {
List<GpxDisplayGroup> result = activity.getGpxFile(useDisplayGroups);
for (GpxDisplayGroup group : result) {
boolean add = hasFilterType(group.getType());
if (add) {
groups.add(group);
}
}
}
return groups;
}
private void updateTrackColor() { private void updateTrackColor() {
int color = getGpxDataItem() != null ? getGpxDataItem().getColor() : 0; int color = getGpxDataItem() != null ? getGpxDataItem().getColor() : 0;
GPXFile gpxFile = getGpx(); GPXFile gpxFile = getGpx();
@ -621,14 +576,6 @@ public class TrackActivityFragmentAdapter implements TrackBitmapDrawerListener {
} }
} }
public List<GpxSelectionHelper.GpxDisplayItem> flatten(List<GpxDisplayGroup> groups) {
ArrayList<GpxSelectionHelper.GpxDisplayItem> list = new ArrayList<>();
for (GpxDisplayGroup g : groups) {
list.addAll(g.getModifiableList());
}
return list;
}
public void hideTransparentOverlay() { public void hideTransparentOverlay() {
overlayView.setVisibility(View.GONE); overlayView.setVisibility(View.GONE);
} }

View file

@ -41,7 +41,6 @@ import com.google.android.material.snackbar.Snackbar;
import net.osmand.AndroidUtils; import net.osmand.AndroidUtils;
import net.osmand.Collator; import net.osmand.Collator;
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.OsmAndCollator; import net.osmand.OsmAndCollator;
@ -59,7 +58,6 @@ import net.osmand.plus.UiUtilities;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.activities.OsmandActionBarActivity; import net.osmand.plus.activities.OsmandActionBarActivity;
import net.osmand.plus.activities.OsmandBaseExpandableListAdapter; import net.osmand.plus.activities.OsmandBaseExpandableListAdapter;
import net.osmand.plus.activities.SavingTrackHelper;
import net.osmand.plus.activities.TrackActivity; import net.osmand.plus.activities.TrackActivity;
import net.osmand.plus.base.OsmandExpandableListFragment; import net.osmand.plus.base.OsmandExpandableListFragment;
import net.osmand.plus.base.PointImageDrawable; import net.osmand.plus.base.PointImageDrawable;
@ -67,14 +65,16 @@ import net.osmand.plus.helpers.GpxUiHelper;
import net.osmand.plus.mapmarkers.CoordinateInputDialogFragment; import net.osmand.plus.mapmarkers.CoordinateInputDialogFragment;
import net.osmand.plus.mapmarkers.MapMarkersGroup; import net.osmand.plus.mapmarkers.MapMarkersGroup;
import net.osmand.plus.mapmarkers.MapMarkersHelper; import net.osmand.plus.mapmarkers.MapMarkersHelper;
import net.osmand.plus.myplaces.DeletePointsTask.OnPointsDeleteListener;
import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener; import net.osmand.plus.myplaces.TrackBitmapDrawer.TrackBitmapDrawerListener;
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.SaveGpxAsyncTask.SaveGpxListener;
import net.osmand.plus.track.TrackDisplayHelper;
import net.osmand.plus.track.TrackMenuFragment;
import net.osmand.plus.widgets.TextViewEx; import net.osmand.plus.widgets.TextViewEx;
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.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -88,7 +88,7 @@ import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
public class TrackPointFragment extends OsmandExpandableListFragment implements TrackBitmapDrawerListener { public class TrackPointFragment extends OsmandExpandableListFragment implements TrackBitmapDrawerListener, OnPointsDeleteListener {
public static final int SEARCH_ID = -1; public static final int SEARCH_ID = -1;
public static final int DELETE_ID = 2; public static final int DELETE_ID = 2;
@ -105,6 +105,8 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
private OsmandApplication app; private OsmandApplication app;
private TrackActivityFragmentAdapter fragmentAdapter; private TrackActivityFragmentAdapter fragmentAdapter;
final private PointGPXAdapter adapter = new PointGPXAdapter(); final private PointGPXAdapter adapter = new PointGPXAdapter();
private GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_POINTS, GpxDisplayItemType.TRACK_ROUTE_POINTS};
private TrackDisplayHelper displayHelper;
private boolean selectionMode; private boolean selectionMode;
private LinkedHashMap<GpxDisplayItemType, Set<GpxDisplayItem>> selectedItems = new LinkedHashMap<>(); private LinkedHashMap<GpxDisplayItemType, Set<GpxDisplayItem>> selectedItems = new LinkedHashMap<>();
@ -120,6 +122,13 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
this.app = getMyApplication(); this.app = getMyApplication();
FragmentActivity activity = getActivity();
if (activity instanceof TrackActivity) {
displayHelper = ((TrackActivity) activity).getDisplayHelper();
} else if (getTargetFragment() instanceof TrackMenuFragment) {
displayHelper = ((TrackMenuFragment) getTargetFragment()).getDisplayHelper();
}
} }
@Override @Override
@ -135,8 +144,7 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
mainView = inflater.inflate(R.layout.track_points_tree, container, false); mainView = inflater.inflate(R.layout.track_points_tree, container, false);
ExpandableListView listView = (ExpandableListView) mainView.findViewById(android.R.id.list); ExpandableListView listView = (ExpandableListView) mainView.findViewById(android.R.id.list);
fragmentAdapter = new TrackActivityFragmentAdapter(app, this, listView, fragmentAdapter = new TrackActivityFragmentAdapter(app, this, listView, displayHelper, filterTypes);
GpxDisplayItemType.TRACK_POINTS, GpxDisplayItemType.TRACK_ROUTE_POINTS);
fragmentAdapter.setShowMapOnly(true); fragmentAdapter.setShowMapOnly(true);
fragmentAdapter.setTrackBitmapSelectionSupported(false); fragmentAdapter.setTrackBitmapSelectionSupported(false);
fragmentAdapter.setShowDescriptionCard(true); fragmentAdapter.setShowDescriptionCard(true);
@ -165,13 +173,8 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
if (optionsMenu != null) { if (optionsMenu != null) {
optionsMenu.close(); optionsMenu.close();
} }
if (fragmentAdapter != null) { if (fragmentAdapter != null && fragmentAdapter.colorListPopupWindow != null) {
if (fragmentAdapter.splitListPopupWindow != null) { fragmentAdapter.colorListPopupWindow.dismiss();
fragmentAdapter.splitListPopupWindow.dismiss();
}
if (fragmentAdapter.colorListPopupWindow != null) {
fragmentAdapter.colorListPopupWindow.dismiss();
}
} }
} }
@ -192,7 +195,6 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
} }
} }
private int getSelectedItemsCount() { private int getSelectedItemsCount() {
int count = 0; int count = 0;
for (Set<GpxDisplayItem> set : selectedItems.values()) { for (Set<GpxDisplayItem> set : selectedItems.values()) {
@ -213,21 +215,14 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
return result; return result;
} }
@Nullable
public TrackActivity getTrackActivity() {
return (TrackActivity) getActivity();
}
@Nullable @Nullable
private GPXFile getGpx() { private GPXFile getGpx() {
TrackActivity activity = getTrackActivity(); return displayHelper.getGpx();
return activity != null ? activity.getGpx() : null;
} }
@Nullable @Nullable
private GpxDataItem getGpxDataItem() { private GpxDataItem getGpxDataItem() {
TrackActivity activity = getTrackActivity(); return displayHelper.getGpxDataItem();
return activity != null ? activity.getGpxDataItem() : null;
} }
private void expandAllGroups() { private void expandAllGroups() {
@ -250,7 +245,7 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
@Nullable @Nullable
private List<GpxDisplayGroup> getOriginalGroups() { private List<GpxDisplayGroup> getOriginalGroups() {
return fragmentAdapter != null ? fragmentAdapter.getOriginalGroups() : null; return displayHelper.getOriginalGroups(filterTypes);
} }
public void setContent() { public void setContent() {
@ -348,7 +343,7 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
} }
}; };
} }
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.clear(); menu.clear();
@ -495,13 +490,13 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
} }
private void deleteItems() { private void deleteItems() {
new DeletePointsTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); new DeletePointsTask(app, getGpx(), getSelectedItems(), this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
private void addOrRemoveMapMarkersSyncGroup() { private void addOrRemoveMapMarkersSyncGroup() {
final MapMarkersHelper markersHelper = app.getMapMarkersHelper(); final MapMarkersHelper markersHelper = app.getMapMarkersHelper();
TrackActivity activity = getTrackActivity(); FragmentActivity activity = getActivity();
if (activity == null) { if (activity == null) {
return; return;
} }
@ -536,7 +531,7 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
.setAction(R.string.shared_string_undo, new View.OnClickListener() { .setAction(R.string.shared_string_undo, new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
TrackActivity trackActivity = getTrackActivity(); FragmentActivity trackActivity = getActivity();
if (trackActivity != null) { if (trackActivity != null) {
if (markersRemoved) { if (markersRemoved) {
if (gpxFile != null) { if (gpxFile != null) {
@ -606,7 +601,7 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
} }
private void selectFavoritesImpl() { private void selectFavoritesImpl() {
TrackActivity activity = getTrackActivity(); FragmentActivity activity = getActivity();
if (activity != null && getSelectedItemsCount() > 0) { if (activity != null && getSelectedItemsCount() > 0) {
AlertDialog.Builder b = new AlertDialog.Builder(activity); AlertDialog.Builder b = new AlertDialog.Builder(activity);
final EditText editText = new EditText(activity); final EditText editText = new EditText(activity);
@ -684,14 +679,11 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
} else { } else {
GPXFile gpx = item.group.getGpx(); GPXFile gpx = item.group.getGpx();
if (gpx != null) { if (gpx != null) {
TrackActivity trackActivity = getTrackActivity(); FragmentActivity activity = getActivity();
if (trackActivity != null && fragmentAdapter != null) { if (activity != null && !TrackActivityFragmentAdapter.isGpxFileSelected(app, gpx)) {
boolean gpxFileSelected = fragmentAdapter.isGpxFileSelected(gpx); Intent intent = activity.getIntent();
if (!gpxFileSelected) { if (intent != null) {
Intent intent = trackActivity.getIntent(); intent.putExtra(TrackActivity.SHOW_TEMPORARILY, true);
if (intent != null) {
intent.putExtra(TrackActivity.SHOW_TEMPORARILY, true);
}
} }
} }
app.getSelectedGpxHelper().setGpxFileToDisplay(gpx); app.getSelectedGpxHelper().setGpxFileToDisplay(gpx);
@ -741,6 +733,23 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
} }
} }
@Override
public void onPointsDeletionStarted() {
showProgressBar();
}
@Override
public void onPointsDeleted() {
selectedItems.clear();
selectedGroups.clear();
hideProgressBar();
List<GpxDisplayGroup> groups = getOriginalGroups();
if (groups != null) {
adapter.synchronizeGroups(groups);
}
}
class PointGPXAdapter extends OsmandBaseExpandableListAdapter implements Filterable { class PointGPXAdapter extends OsmandBaseExpandableListAdapter implements Filterable {
Map<GpxDisplayGroup, List<GpxDisplayItem>> itemGroups = new LinkedHashMap<>(); Map<GpxDisplayGroup, List<GpxDisplayItem>> itemGroups = new LinkedHashMap<>();
@ -1199,9 +1208,6 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
public class PointsFilter extends Filter { public class PointsFilter extends Filter {
PointsFilter() {
}
@Override @Override
protected FilterResults performFiltering(CharSequence constraint) { protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults(); FilterResults results = new FilterResults();
@ -1243,77 +1249,4 @@ public class TrackPointFragment extends OsmandExpandableListFragment implements
expandAllGroups(); expandAllGroups();
} }
} }
}
private static class DeletePointsTask extends AsyncTask<Void, Void, Void> {
private OsmandApplication app;
private WeakReference<TrackPointFragment> fragmentRef;
private GPXFile gpx;
private Set<GpxDisplayItem> selectedItems;
DeletePointsTask(TrackPointFragment fragment) {
this.app = fragment.getMyApplication();
this.fragmentRef = new WeakReference<>(fragment);
this.gpx = fragment.getGpx();
this.selectedItems = fragment.getSelectedItems();
}
@Override
protected void onPreExecute() {
TrackPointFragment fragment = fragmentRef.get();
if (fragment != null) {
fragment.showProgressBar();
}
}
@Override
protected Void doInBackground(Void... params) {
SavingTrackHelper savingTrackHelper = app.getSavingTrackHelper();
if (gpx != null) {
for (GpxDisplayItem item : selectedItems) {
if (gpx.showCurrentTrack) {
savingTrackHelper.deletePointData(item.locationStart);
} else {
if (item.group.getType() == GpxDisplayItemType.TRACK_POINTS) {
gpx.deleteWptPt(item.locationStart);
} else if (item.group.getType() == GpxDisplayItemType.TRACK_ROUTE_POINTS) {
gpx.deleteRtePt(item.locationStart);
}
}
}
if (!gpx.showCurrentTrack) {
GPXUtilities.writeGpxFile(new File(gpx.path), gpx);
boolean selected = app.getSelectedGpxHelper().getSelectedFileByPath(gpx.path) != null;
if (selected) {
app.getSelectedGpxHelper().setGpxFileToDisplay(gpx);
}
}
syncGpx(gpx);
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
TrackPointFragment fragment = fragmentRef.get();
if (fragment != null) {
fragment.selectedItems.clear();
fragment.selectedGroups.clear();
fragment.hideProgressBar();
List<GpxDisplayGroup> groups = fragment.getOriginalGroups();
if (groups != null) {
fragment.adapter.synchronizeGroups(groups);
}
}
}
private void syncGpx(GPXFile gpxFile) {
MapMarkersHelper helper = app.getMapMarkersHelper();
MapMarkersGroup group = helper.getMarkersGroup(gpxFile);
if (group != null) {
helper.runSynchronization(group);
}
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,57 @@
package net.osmand.plus.track;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.myplaces.GPXItemPagerAdapter;
import net.osmand.plus.myplaces.SegmentActionsListener;
import net.osmand.plus.myplaces.SegmentGPXAdapter;
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
import net.osmand.plus.views.controls.PagerSlidingTabStrip;
import net.osmand.plus.views.controls.WrapContentHeightViewPager;
import java.util.List;
public class SegmentsCard extends BaseCard {
private TrackDisplayHelper displayHelper;
private GpxDisplayItemType[] filterTypes = new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT};
private SegmentGPXAdapter adapter;
private SegmentActionsListener listener;
public SegmentsCard(@NonNull MapActivity mapActivity, @NonNull TrackDisplayHelper displayHelper,
@NonNull SegmentActionsListener listener) {
super(mapActivity);
this.displayHelper = displayHelper;
this.listener = listener;
}
@Override
public int getCardLayoutId() {
return R.layout.track_segments_container;
}
@Override
protected void updateContent() {
ViewGroup container = (ViewGroup) view;
container.removeAllViews();
List<GpxDisplayItem> items = TrackDisplayHelper.flatten(displayHelper.getOriginalGroups(filterTypes));
for (GpxDisplayItem displayItem : items) {
View segmentView = SegmentGPXAdapter.createGpxTabsView(displayHelper, container, listener, nightMode);
WrapContentHeightViewPager pager = segmentView.findViewById(R.id.pager);
PagerSlidingTabStrip tabLayout = segmentView.findViewById(R.id.sliding_tabs);
pager.setAdapter(new GPXItemPagerAdapter(tabLayout, displayItem, displayHelper, listener));
tabLayout.setViewPager(pager);
container.addView(segmentView);
}
}
}

View file

@ -0,0 +1,179 @@
package net.osmand.plus.track;
import android.app.Activity;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.data.LatLon;
import net.osmand.data.PointDescription;
import net.osmand.data.QuadRect;
import net.osmand.plus.GPXDatabase.GpxDataItem;
import net.osmand.plus.GpxSelectionHelper;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.measurementtool.GpxData;
import net.osmand.plus.settings.backend.OsmandSettings;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class TrackDisplayHelper {
private final OsmandApplication app;
private File file;
private GPXFile gpxFile;
private GpxDataItem gpxDataItem;
private long modifiedTime = -1;
private List<GpxDisplayGroup> displayGroups;
private List<GpxDisplayGroup> originalGroups = new ArrayList<>();
public TrackDisplayHelper(OsmandApplication app) {
this.app = app;
}
@Nullable
public GPXFile getGpx() {
return gpxFile;
}
@Nullable
public GpxDataItem getGpxDataItem() {
return gpxDataItem;
}
public File getFile() {
return file;
}
public void setFile(@Nullable File file) {
this.file = file;
}
public void setGpx(@NonNull GPXFile result) {
this.gpxFile = result;
if (file == null) {
this.gpxFile = app.getSavingTrackHelper().getCurrentGpx();
}
}
public void setGpxDataItem(GpxDataItem gpxDataItem) {
this.gpxDataItem = gpxDataItem;
}
public QuadRect getRect() {
if (getGpx() != null) {
return getGpx().getRect();
} else {
return new QuadRect(0, 0, 0, 0);
}
}
public boolean setJoinSegments(boolean joinSegments) {
if (gpxDataItem != null) {
boolean updated = app.getGpxDbHelper().updateJoinSegments(gpxDataItem, joinSegments);
SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().getSelectedFileByPath(gpxFile.path);
if (updated && selectedGpxFile != null) {
selectedGpxFile.setJoinSegments(joinSegments);
}
return updated;
}
return false;
}
public boolean isJoinSegments() {
return gpxDataItem != null && gpxDataItem.isJoinSegments();
}
public List<GpxDisplayGroup> getGpxFile(boolean useDisplayGroups) {
if (gpxFile == null) {
return new ArrayList<>();
}
if (gpxFile.modifiedTime != modifiedTime) {
modifiedTime = gpxFile.modifiedTime;
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) {
return displayGroups;
} else {
return originalGroups;
}
}
@NonNull
public List<GpxDisplayGroup> getOriginalGroups(GpxDisplayItemType[] filterTypes) {
return filterGroups(false, filterTypes);
}
@NonNull
public List<GpxDisplayGroup> getDisplayGroups(GpxDisplayItemType[] filterTypes) {
return filterGroups(true, filterTypes);
}
private boolean hasFilterType(GpxDisplayItemType filterType, GpxDisplayItemType[] filterTypes) {
for (GpxDisplayItemType type : filterTypes) {
if (type == filterType) {
return true;
}
}
return false;
}
@NonNull
private List<GpxDisplayGroup> filterGroups(boolean useDisplayGroups, GpxDisplayItemType[] filterTypes) {
List<GpxDisplayGroup> groups = new ArrayList<>();
for (GpxDisplayGroup group : getGpxFile(useDisplayGroups)) {
if (hasFilterType(group.getType(), filterTypes)) {
groups.add(group);
}
}
return groups;
}
public static List<GpxDisplayItem> flatten(List<GpxDisplayGroup> groups) {
ArrayList<GpxDisplayItem> list = new ArrayList<>();
for (GpxDisplayGroup g : groups) {
list.addAll(g.getModifiableList());
}
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

@ -0,0 +1,596 @@
package net.osmand.plus.track;
import android.content.DialogInterface;
import android.content.res.ColorStateList;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.TextView;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import net.osmand.AndroidUtils;
import net.osmand.GPXUtilities.GPXFile;
import net.osmand.GPXUtilities.Track;
import net.osmand.GPXUtilities.TrkSegment;
import net.osmand.GPXUtilities.WptPt;
import net.osmand.PlatformUtil;
import net.osmand.data.QuadRect;
import net.osmand.data.RotatedTileBox;
import net.osmand.plus.GPXDatabase.GpxDataItem;
import net.osmand.plus.GpxDbHelper;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayGroup;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItem;
import net.osmand.plus.GpxSelectionHelper.GpxDisplayItemType;
import net.osmand.plus.GpxSelectionHelper.SelectedGpxFile;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.ContextMenuFragment;
import net.osmand.plus.base.ContextMenuScrollFragment;
import net.osmand.plus.helpers.AndroidUiHelper;
import net.osmand.plus.helpers.GpxUiHelper;
import net.osmand.plus.helpers.GpxUiHelper.GPXDataSetType;
import net.osmand.plus.helpers.GpxUiHelper.OrderedLineDataSet;
import net.osmand.plus.mapcontextmenu.other.TrackDetailsMenu;
import net.osmand.plus.measurementtool.GpxData;
import net.osmand.plus.measurementtool.MeasurementEditingContext;
import net.osmand.plus.measurementtool.MeasurementToolFragment;
import net.osmand.plus.myplaces.GPXTabItemType;
import net.osmand.plus.myplaces.SegmentActionsListener;
import net.osmand.plus.myplaces.SplitSegmentDialogFragment;
import net.osmand.plus.myplaces.TrackActivityFragmentAdapter;
import net.osmand.plus.routepreparationmenu.cards.BaseCard;
import net.osmand.plus.routepreparationmenu.cards.BaseCard.CardListener;
import net.osmand.plus.track.SaveGpxAsyncTask.SaveGpxListener;
import net.osmand.plus.widgets.IconPopupMenu;
import net.osmand.util.Algorithms;
import org.apache.commons.logging.Log;
import java.io.File;
import java.util.List;
import static net.osmand.plus.activities.TrackActivity.CURRENT_RECORDING;
import static net.osmand.plus.activities.TrackActivity.TRACK_FILE_NAME;
public class TrackMenuFragment extends ContextMenuScrollFragment implements CardListener, SegmentActionsListener {
public static final String TAG = TrackMenuFragment.class.getName();
private static final Log log = PlatformUtil.getLog(TrackMenuFragment.class);
private OsmandApplication app;
private TrackDisplayHelper displayHelper;
private GpxDataItem gpxDataItem;
private SelectedGpxFile selectedGpxFile;
private View routeMenuTopShadowAll;
private BottomNavigationView bottomNav;
private TrackMenuType menuType = TrackMenuType.TRACK;
private SegmentsCard segmentsCard;
private int menuTitleHeight;
public enum TrackMenuType {
TRACK(R.id.action_track, R.string.shared_string_gpx_tracks),
POINTS(R.id.action_points, R.string.shared_string_gpx_points);
TrackMenuType(@DrawableRes int iconId, @StringRes int titleId) {
this.iconId = iconId;
this.titleId = titleId;
}
public final int iconId;
public final int titleId;
}
@Override
public int getMainLayoutId() {
return R.layout.track_menu;
}
@Override
public int getHeaderViewHeight() {
return menuTitleHeight;
}
@Override
public boolean isHeaderViewDetached() {
return false;
}
@Override
public int getToolbarHeight() {
return 0;
}
public float getMiddleStateKoef() {
return 0.5f;
}
@Override
public int getSupportedMenuStatesPortrait() {
return MenuState.HEADER_ONLY | MenuState.HALF_SCREEN | MenuState.FULL_SCREEN;
}
public TrackDisplayHelper getDisplayHelper() {
return displayHelper;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
app = requireMyApplication();
GpxDbHelper gpxDbHelper = app.getGpxDbHelper();
displayHelper = new TrackDisplayHelper(app);
String gpxFilePath = "";
boolean currentRecording = false;
Bundle arguments = getArguments();
if (savedInstanceState != null) {
gpxFilePath = savedInstanceState.getString(TRACK_FILE_NAME);
currentRecording = savedInstanceState.getBoolean(CURRENT_RECORDING, false);
} else if (arguments != null) {
gpxFilePath = arguments.getString(TRACK_FILE_NAME);
currentRecording = arguments.getBoolean(CURRENT_RECORDING, false);
}
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() {
return displayHelper.getGpx();
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = super.onCreateView(inflater, container, savedInstanceState);
if (view != null) {
bottomNav = view.findViewById(R.id.bottom_navigation);
routeMenuTopShadowAll = view.findViewById(R.id.route_menu_top_shadow_all);
TextView title = view.findViewById(R.id.title);
String fileName = Algorithms.getFileWithoutDirs(getGpx().path);
title.setText(GpxUiHelper.getGpxTitle(fileName));
if (isPortrait()) {
updateCardsLayout();
}
setupCards();
setupButtons(view);
enterTrackAppearanceMode();
runLayoutListener();
}
return view;
}
private void setupCards() {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
ViewGroup cardsContainer = getCardsContainer();
cardsContainer.removeAllViews();
if (menuType == TrackMenuType.TRACK) {
if (segmentsCard != null) {
cardsContainer.addView(segmentsCard.getView());
} else {
segmentsCard = new SegmentsCard(mapActivity, displayHelper, this);
cardsContainer.addView(segmentsCard.build(mapActivity));
}
}
}
}
@Override
protected void calculateLayout(View view, boolean initLayout) {
menuTitleHeight = routeMenuTopShadowAll.getHeight()
+ bottomNav.getHeight();
super.calculateLayout(view, initLayout);
}
@Override
protected void setViewY(int y, boolean animated, boolean adjustMapPos) {
super.setViewY(y, animated, adjustMapPos);
updateStatusBarColor();
}
@Override
protected void updateMainViewLayout(int posY) {
super.updateMainViewLayout(posY);
updateStatusBarColor();
}
@Override
public boolean shouldShowMapControls(int menuState) {
return menuState == MenuState.HEADER_ONLY || menuState == MenuState.HALF_SCREEN;
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
adjustMapPosition(getHeight());
}
@Override
public void onDestroyView() {
super.onDestroyView();
exitTrackAppearanceMode();
}
private void enterTrackAppearanceMode() {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
boolean portrait = AndroidUiHelper.isOrientationPortrait(mapActivity);
AndroidUiHelper.setVisibility(mapActivity, portrait ? View.INVISIBLE : View.GONE,
R.id.map_left_widgets_panel,
R.id.map_right_widgets_panel,
R.id.map_center_info);
}
}
private void exitTrackAppearanceMode() {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
AndroidUiHelper.setVisibility(mapActivity, View.VISIBLE,
R.id.map_left_widgets_panel,
R.id.map_right_widgets_panel,
R.id.map_center_info,
R.id.map_search_button);
}
}
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
outState.putString(TRACK_FILE_NAME, getGpx().path);
outState.putBoolean(CURRENT_RECORDING, selectedGpxFile.isShowCurrentTrack());
super.onSaveInstanceState(outState);
}
@Override
public int getStatusBarColorId() {
View view = getView();
if (view != null) {
boolean nightMode = isNightMode();
if (getViewY() <= getFullScreenTopPosY() || !isPortrait()) {
if (Build.VERSION.SDK_INT >= 23 && !nightMode) {
view.setSystemUiVisibility(view.getSystemUiVisibility() | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
return nightMode ? R.color.divider_color_dark : R.color.divider_color_light;
} else {
if (Build.VERSION.SDK_INT >= 23 && !nightMode) {
view.setSystemUiVisibility(view.getSystemUiVisibility() & ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
}
}
return -1;
}
private void updateStatusBarColor() {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
mapActivity.updateStatusBarColor();
}
}
@Override
protected String getThemeInfoProviderTag() {
return TAG;
}
@Override
public void onCardLayoutNeeded(@NonNull BaseCard card) {
}
@Override
public void onCardPressed(@NonNull BaseCard card) {
}
@Override
public void onCardButtonPressed(@NonNull BaseCard card, int buttonIndex) {
}
@Override
protected int applyPosY(int currentY, boolean needCloseMenu, boolean needMapAdjust, int previousMenuState, int newMenuState, int dZoom, boolean animated) {
int y = super.applyPosY(currentY, needCloseMenu, needMapAdjust, previousMenuState, newMenuState, dZoom, animated);
if (needMapAdjust) {
adjustMapPosition(y);
}
return y;
}
@Override
protected void onHeaderClick() {
adjustMapPosition(getViewY());
}
private void adjustMapPosition(int y) {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null && mapActivity.getMapView() != null) {
GPXFile gpxFile = getGpx();
QuadRect r = gpxFile.getRect();
RotatedTileBox tb = mapActivity.getMapView().getCurrentRotatedTileBox().copy();
int tileBoxWidthPx = 0;
int tileBoxHeightPx = 0;
if (!isPortrait()) {
tileBoxWidthPx = tb.getPixWidth() - getWidth();
} else {
int fHeight = getViewHeight() - y - AndroidUtils.getStatusBarHeight(mapActivity);
tileBoxHeightPx = tb.getPixHeight() - fHeight;
}
if (r.left != 0 && r.right != 0) {
mapActivity.getMapView().fitRectToMap(r.left, r.right, r.top, r.bottom, tileBoxWidthPx, tileBoxHeightPx, 0);
}
}
}
private void updateCardsLayout() {
View mainView = getMainView();
if (mainView != null) {
View topShadow = getTopShadow();
FrameLayout bottomContainer = getBottomContainer();
if (getCurrentMenuState() == MenuState.HEADER_ONLY) {
topShadow.setVisibility(View.INVISIBLE);
bottomContainer.setBackgroundDrawable(null);
} else {
topShadow.setVisibility(View.VISIBLE);
AndroidUtils.setBackground(mainView.getContext(), bottomContainer, isNightMode(), R.color.list_background_color_light, R.color.list_background_color_dark);
}
}
}
private void setupButtons(View view) {
ColorStateList navColorStateList = AndroidUtils.createBottomNavColorStateList(getContext(), isNightMode());
BottomNavigationView bottomNav = view.findViewById(R.id.bottom_navigation);
bottomNav.setItemIconTintList(navColorStateList);
bottomNav.setItemTextColor(navColorStateList);
bottomNav.setSelectedItemId(R.id.action_track);
bottomNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
for (TrackMenuType type : TrackMenuType.values()) {
if (type.iconId == item.getItemId()) {
menuType = type;
setupCards();
break;
}
}
return true;
}
});
}
@Override
public void updateContent() {
if (segmentsCard != null) {
segmentsCard.updateContent();
}
setupCards();
}
@Override
public void onChartTouch() {
getBottomScrollView().requestDisallowInterceptTouchEvent(true);
}
@Override
public void scrollBy(int px) {
}
@Override
public void onPointSelected(double lat, double lon) {
}
@Override
public void openSplitInterval(GpxDisplayItem gpxItem, TrkSegment trkSegment) {
FragmentManager fragmentManager = getFragmentManager();
if (fragmentManager != null) {
SplitSegmentDialogFragment.showInstance(fragmentManager, displayHelper, gpxItem, trkSegment);
}
}
@Override
public void openAnalyzeOnMap(GpxDisplayItem gpxItem, List<ILineDataSet> dataSets, GPXTabItemType tabType) {
WptPt wpt = null;
gpxItem.chartTypes = null;
if (dataSets != null && dataSets.size() > 0) {
gpxItem.chartTypes = new GPXDataSetType[dataSets.size()];
for (int i = 0; i < dataSets.size(); i++) {
OrderedLineDataSet orderedDataSet = (OrderedLineDataSet) dataSets.get(i);
gpxItem.chartTypes[i] = orderedDataSet.getDataSetType();
}
if (gpxItem.chartHighlightPos != -1) {
TrkSegment segment = null;
for (Track t : gpxItem.group.getGpx().tracks) {
for (TrkSegment s : t.segments) {
if (s.points.size() > 0 && s.points.get(0).equals(gpxItem.analysis.locationStart)) {
segment = s;
break;
}
}
if (segment != null) {
break;
}
}
if (segment != null) {
OrderedLineDataSet dataSet = (OrderedLineDataSet) dataSets.get(0);
float distance = gpxItem.chartHighlightPos * dataSet.getDivX();
for (WptPt p : segment.points) {
if (p.distance >= distance) {
wpt = p;
break;
}
}
}
}
}
if (wpt != null) {
gpxItem.locationOnMap = wpt;
} else {
gpxItem.locationOnMap = gpxItem.locationStart;
}
TrackDetailsMenu trackDetailsMenu = getMapActivity().getTrackDetailsMenu();
trackDetailsMenu.setGpxItem(gpxItem);
trackDetailsMenu.show();
close();
}
@Override
public void showOptionsPopupMenu(View view, final TrkSegment segment, final boolean confirmDeletion) {
FragmentActivity activity = getActivity();
if (activity != null) {
IconPopupMenu optionsPopupMenu = new IconPopupMenu(activity, view.findViewById(R.id.overflow_menu));
Menu menu = optionsPopupMenu.getMenu();
optionsPopupMenu.getMenuInflater().inflate(R.menu.track_segment_menu, menu);
menu.findItem(R.id.action_edit).setIcon(app.getUIUtilities().getThemedIcon(R.drawable.ic_action_edit_dark));
menu.findItem(R.id.action_delete).setIcon(app.getUIUtilities().getThemedIcon(R.drawable.ic_action_remove_dark));
optionsPopupMenu.setOnMenuItemClickListener(new IconPopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
int i = item.getItemId();
if (i == R.id.action_edit) {
editSegment(segment);
return true;
} else if (i == R.id.action_delete) {
FragmentActivity activity = getActivity();
if (!confirmDeletion) {
deleteAndSaveSegment(segment);
} else if (activity != null) {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
builder.setMessage(R.string.recording_delete_confirm);
builder.setPositiveButton(R.string.shared_string_yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
deleteAndSaveSegment(segment);
}
});
builder.setNegativeButton(R.string.shared_string_cancel, null);
builder.show();
}
return true;
}
return false;
}
});
optionsPopupMenu.show();
}
}
private void editSegment(TrkSegment segment) {
GPXFile gpxFile = getGpx();
openPlanRoute(new GpxData(gpxFile));
close();
}
public void openPlanRoute(GpxData gpxData) {
QuadRect qr = gpxData.getRect();
getMapActivity().getMapView().fitRectToMap(qr.left, qr.right, qr.top, qr.bottom, (int) qr.width(), (int) qr.height(), 0);
MeasurementEditingContext editingContext = new MeasurementEditingContext();
editingContext.setGpxData(gpxData);
MeasurementToolFragment.showInstance(getFragmentManager(), editingContext);
}
private void deleteAndSaveSegment(TrkSegment segment) {
if (deleteSegment(segment)) {
GPXFile gpx = displayHelper.getGpx();
if (gpx != null) {
boolean showOnMap = TrackActivityFragmentAdapter.isGpxFileSelected(app, gpx);
SelectedGpxFile selectedGpxFile = app.getSelectedGpxHelper().selectGpxFile(gpx, showOnMap, false);
saveGpx(showOnMap ? selectedGpxFile : null, gpx);
}
}
}
private boolean deleteSegment(TrkSegment segment) {
if (segment != null) {
GPXFile gpx = displayHelper.getGpx();
if (gpx != null) {
return gpx.removeTrkSegment(segment);
}
}
return false;
}
private void saveGpx(final SelectedGpxFile selectedGpxFile, GPXFile gpxFile) {
new SaveGpxAsyncTask(new File(gpxFile.path), gpxFile, new SaveGpxListener() {
@Override
public void gpxSavingStarted() {
}
@Override
public void gpxSavingFinished(Exception errorMessage) {
if (selectedGpxFile != null) {
List<GpxDisplayGroup> groups = displayHelper.getDisplayGroups(new GpxDisplayItemType[] {GpxDisplayItemType.TRACK_SEGMENT});
if (groups != null) {
selectedGpxFile.setDisplayGroups(groups, app);
selectedGpxFile.processPoints(app);
}
}
updateContent();
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private void close() {
try {
MapActivity mapActivity = getMapActivity();
if (mapActivity != null) {
FragmentManager fragmentManager = mapActivity.getSupportFragmentManager();
fragmentManager.beginTransaction().remove(this).commitAllowingStateLoss();
}
} catch (Exception e) {
log.error(e);
}
}
public static boolean showInstance(@NonNull MapActivity mapActivity, String path, boolean showCurrentTrack) {
try {
Bundle args = new Bundle();
args.putString(TRACK_FILE_NAME, path);
args.putBoolean(CURRENT_RECORDING, showCurrentTrack);
args.putInt(ContextMenuFragment.MENU_STATE_KEY, MenuState.HALF_SCREEN);
TrackMenuFragment fragment = new TrackMenuFragment();
fragment.setArguments(args);
mapActivity.getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragmentContainer, fragment, fragment.getFragmentTag())
.addToBackStack(fragment.getFragmentTag())
.commitAllowingStateLoss();
return true;
} catch (RuntimeException e) {
return false;
}
}
}