diff --git a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/TrackFolderLoaderTask.kt b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/TrackFolderLoaderTask.kt index 5b1db67aa09..b4222663461 100644 --- a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/TrackFolderLoaderTask.kt +++ b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/TrackFolderLoaderTask.kt @@ -51,7 +51,7 @@ class TrackFolderLoaderTask( if (!shouldLoadFolder(cachedRootFolder)) return cachedRootFolder!! val start = currentTimeMillis() - log.info("Start loading tracks in ${folder.getDirName()}") + log.info("Start loading tracks in ${folder.getDirName(true)}") folder.clearData() loadingTime = currentTimeMillis() diff --git a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/ComparableTracksGroup.kt b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/ComparableTracksGroup.kt index 62573f09aea..e726ad3ef0c 100644 --- a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/ComparableTracksGroup.kt +++ b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/ComparableTracksGroup.kt @@ -4,6 +4,7 @@ import net.osmand.shared.gpx.filters.TrackFolderAnalysis interface ComparableTracksGroup { fun getFolderAnalysis(): TrackFolderAnalysis - fun getDirName(): String + fun getDirName(useExtendedName: Boolean): String fun lastModified(): Long + fun getDefaultOrder(): Int = -1 } \ No newline at end of file diff --git a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/SmartFolder.kt b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/SmartFolder.kt index baf2640e457..263d81d97fe 100644 --- a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/SmartFolder.kt +++ b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/SmartFolder.kt @@ -10,6 +10,9 @@ import net.osmand.shared.util.KCollectionUtils @Serializable class SmartFolder(@Serializable var folderName: String) : TracksGroup, ComparableTracksGroup { + companion object { + const val ID_PREFIX = "SMART_FOLDER___" + } @Transient private var trackItems: List? = null @@ -25,6 +28,10 @@ class SmartFolder(@Serializable var folderName: String) : TracksGroup, Comparabl @Transient private var folderAnalysis: TrackFolderAnalysis? = null + override fun getId(): String { + return ID_PREFIX + folderName + } + override fun getName() = folderName override fun getTrackItems(): List { @@ -52,7 +59,7 @@ class SmartFolder(@Serializable var folderName: String) : TracksGroup, Comparabl return analysis } - override fun getDirName() = folderName + override fun getDirName(useExtendedName: Boolean) = folderName override fun lastModified() = creationTime diff --git a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/TrackFolder.kt b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/TrackFolder.kt index 429754ac594..5b09bf61dfc 100644 --- a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/TrackFolder.kt +++ b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/TrackFolder.kt @@ -5,7 +5,6 @@ import net.osmand.shared.gpx.TrackItem import net.osmand.shared.gpx.filters.TrackFolderAnalysis import net.osmand.shared.io.KFile import net.osmand.shared.util.KAlgorithms -import net.osmand.shared.util.KCollectionUtils import kotlin.math.max class TrackFolder(dirFile: KFile, parentFolder: TrackFolder?) : @@ -37,6 +36,8 @@ class TrackFolder(dirFile: KFile, parentFolder: TrackFolder?) : lastModified = folder.lastModified } + override fun getId() = relativePath + override fun getName(): String { return GpxHelper.getFolderName(dirFile, false) } @@ -50,15 +51,21 @@ class TrackFolder(dirFile: KFile, parentFolder: TrackFolder?) : } val relativePath: String - get() { - val dirName = getDirName() - val parentFolder = getParentFolder() - return if (parentFolder != null && !parentFolder.isRootFolder) parentFolder.relativePath + "/" + dirName else dirName - } + get() = + if (!isRootFolder) { + val dirName = dirFile.name() + val parent = getParentFolder() + if (parent?.isRootFolder == false) parent.relativePath + "/" + dirName else dirName + } else { + "" + } + val isRootFolder: Boolean get() = getParentFolder() == null + fun getRootFolder(): TrackFolder = getParentFolder()?.getRootFolder() ?: this + fun getParentFolder(): TrackFolder? { return parentFolder } @@ -144,8 +151,8 @@ class TrackFolder(dirFile: KFile, parentFolder: TrackFolder?) : return analysis } - override fun getDirName(): String { - return dirFile.name() + override fun getDirName(useExtendedName: Boolean): String { + return if (useExtendedName) relativePath else dirFile.name() } fun getLastModified(): Long { diff --git a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/TracksGroup.kt b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/TracksGroup.kt index d8973bb6ddf..f47c21da226 100644 --- a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/TracksGroup.kt +++ b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/data/TracksGroup.kt @@ -3,6 +3,8 @@ package net.osmand.shared.gpx.data import net.osmand.shared.gpx.TrackItem interface TracksGroup { + fun getId(): String + fun getName(): String fun getTrackItems(): List diff --git a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/filters/TrackFolderAnalysis.kt b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/filters/TrackFolderAnalysis.kt index ec4aa8ee39c..3cfc13033a2 100644 --- a/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/filters/TrackFolderAnalysis.kt +++ b/OsmAnd-shared/src/commonMain/kotlin/net/osmand/shared/gpx/filters/TrackFolderAnalysis.kt @@ -52,7 +52,7 @@ class TrackFolderAnalysis(folder: TracksGroup) { timeSpan = timeSpanSum.toInt() tracksCount = items.size - log.info(">>>> ${folder.getName()} = (tracks: $tracksCount, totalDistance: ${"%.2f".format(totalDistance)}, " + + log.info(">>>> ${folder.getId()} = (tracks: $tracksCount, totalDistance: ${"%.2f".format(totalDistance)}, " + "timeSpan: $timeSpan, fileSize: $fileSize, diffElevationUp: ${"%.2f".format(diffElevationUp)}, diffElevationDown: ${"%.2f".format(diffElevationDown)}") } } diff --git a/OsmAnd/src/net/osmand/plus/auto/screens/TracksFoldersScreen.kt b/OsmAnd/src/net/osmand/plus/auto/screens/TracksFoldersScreen.kt index 10ccde8c56d..526542d7aa1 100644 --- a/OsmAnd/src/net/osmand/plus/auto/screens/TracksFoldersScreen.kt +++ b/OsmAnd/src/net/osmand/plus/auto/screens/TracksFoldersScreen.kt @@ -81,7 +81,7 @@ class TracksFoldersScreen( .setTitle(app.getString(R.string.sort_last_modified)) .setImage(iconLastModified) .setBrowsable(true) - .setOnClickListener { onClickTabFolder(trackTabsHelper.trackTabs[TrackTabType.ALL.name]!!) } + .setOnClickListener { onClickTabFolder(trackTabsHelper.getTrackTab(TrackTabType.ALL.name)!!) } .build()) if (trackTabsHelper.trackTabs.isEmpty()) { @@ -93,14 +93,14 @@ class TracksFoldersScreen( } templateBuilder.setLoading(false) var itemsCount = 1 - for (trackTab in trackTabsHelper.trackTabs.values) { + for (trackTab in trackTabsHelper.getSortedTrackTabs(false)) { if (trackTab.type != TrackTabType.FOLDER) { continue } if (itemsCount == contentLimit) { break } - val title = trackTab.getName(app) + val title = trackTab.getName() val iconColorId = ColorUtilities.getDefaultIconColorId(app.daynightHelper.isNightMode) val iconDrawable = app.uiUtilities.getIcon(trackTab.type.iconId, iconColorId) val icon = CarIcon.Builder( @@ -131,7 +131,7 @@ class TracksFoldersScreen( } override fun loadTracksFinished(folder: TrackFolder) { - trackTabsHelper.updateTrackItems(folder.getFlattenedTrackItems()) + trackTabsHelper.updateTrackItems(folder) invalidate() } diff --git a/OsmAnd/src/net/osmand/plus/auto/screens/TracksScreen.kt b/OsmAnd/src/net/osmand/plus/auto/screens/TracksScreen.kt index 60cc38ee25f..3b189bb87eb 100644 --- a/OsmAnd/src/net/osmand/plus/auto/screens/TracksScreen.kt +++ b/OsmAnd/src/net/osmand/plus/auto/screens/TracksScreen.kt @@ -83,7 +83,7 @@ class TracksScreen( val title = if (trackTab.type == TrackTabType.ALL) { app.getString(R.string.sort_last_modified) } else { - trackTab.getName(app) + trackTab.getName() } val isLoading = loadTracksTask.status != AsyncTask.Status.FINISHED templateBuilder.setLoading(isLoading) diff --git a/OsmAnd/src/net/osmand/plus/configmap/tracks/PreselectedTabParams.java b/OsmAnd/src/net/osmand/plus/configmap/tracks/PreselectedTabParams.java index b4ab9117e06..d1d7b446253 100644 --- a/OsmAnd/src/net/osmand/plus/configmap/tracks/PreselectedTabParams.java +++ b/OsmAnd/src/net/osmand/plus/configmap/tracks/PreselectedTabParams.java @@ -1,33 +1,19 @@ package net.osmand.plus.configmap.tracks; -import static net.osmand.plus.configmap.tracks.TrackTabType.SMART_FOLDER; - -import android.content.Context; - import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import net.osmand.util.Algorithms; - -import java.util.Collections; -import java.util.List; public class PreselectedTabParams { - public static final String PRESELECTED_TRACKS_TAB_NAME = "preselected_tab_name"; - public static final String PRESELECTED_TRACKS_TAB_TYPE = "preselected_tab_type"; + public static final String PRESELECTED_TRACKS_TAB_ID = "preselected_tab_id"; public static final String SELECT_ALL_ITEMS_ON_TAB = "select_all_items_on_tab"; public static final String CALLING_FRAGMENT_TAG = "calling_fragment_tag"; @NonNull - private final String name; - @NonNull - private final TrackTabType type; + private final String id; private final boolean selectAll; - public PreselectedTabParams(@NonNull String name, @NonNull TrackTabType type, boolean selectAll) { - this.name = name; - this.type = type; + public PreselectedTabParams(@NonNull String id, boolean selectAll) { + this.id = id; this.selectAll = selectAll; } @@ -36,16 +22,7 @@ public boolean shouldSelectAll() { } @NonNull - public String getPreselectedTabName(@NonNull Context context, @NonNull List trackTabs) { - if (type == SMART_FOLDER) { - for (TrackTab tab : trackTabs) { - String tabName = tab.getName(context); - if (tab.type == SMART_FOLDER && Algorithms.stringsEqual(tabName, name)) { - return tab.getTypeName(); - } - } - return ""; - } - return name; + public String getPreselectedTabId() { + return id; } } \ No newline at end of file diff --git a/OsmAnd/src/net/osmand/plus/configmap/tracks/SelectTrackTabsHelper.java b/OsmAnd/src/net/osmand/plus/configmap/tracks/SelectTrackTabsHelper.java new file mode 100644 index 00000000000..27e707fb17d --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/configmap/tracks/SelectTrackTabsHelper.java @@ -0,0 +1,21 @@ +package net.osmand.plus.configmap.tracks; + +import androidx.annotation.NonNull; + +import net.osmand.plus.OsmandApplication; +import net.osmand.shared.gpx.data.TrackFolder; + +public class SelectTrackTabsHelper extends TrackTabsHelper { + + public SelectTrackTabsHelper(@NonNull OsmandApplication app) { + super(app); + } + + @Override + protected void updateTrackTabs(@NonNull TrackFolder rootFolder) { + trackTabs.clear(); + trackTabs.put(TrackTabType.ON_MAP.name(), getTracksOnMapTab()); + trackTabs.put(TrackTabType.ALL.name(), getAllTracksTab()); + trackTabs.put(TrackTabType.FOLDERS.name(), getFoldersTab(rootFolder)); + } +} diff --git a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackFolderLoaderTask.java b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackFolderLoaderTask.java index b592fa8d017..3cd70776fa6 100644 --- a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackFolderLoaderTask.java +++ b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackFolderLoaderTask.java @@ -66,7 +66,7 @@ protected void onProgressUpdate(TrackItem... items) { @Override protected Void doInBackground(Void... voids) { long start = System.currentTimeMillis(); - LOG.info("Start loading tracks in " + folder.getDirName()); + LOG.info("Start loading tracks in " + folder.getDirName(true)); folder.clearData(); loadingTime = System.currentTimeMillis(); diff --git a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackGroupsBottomSheet.java b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackGroupsBottomSheet.java index 3874b86d0b8..ae007df96ee 100644 --- a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackGroupsBottomSheet.java +++ b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackGroupsBottomSheet.java @@ -42,9 +42,8 @@ public void onCreate(@Nullable Bundle savedInstanceState) { nightMode = isNightMode(true); Fragment target = getTargetFragment(); - if (target instanceof TracksTabsFragment) { - TracksTabsFragment fragment = (TracksTabsFragment) target; - trackTabs = fragment.getTrackTabs(); + if (target instanceof TracksTabsFragment fragment) { + trackTabs = fragment.getSortedTrackTabs(true); selectedTab = fragment.getSelectedTab(); } } @@ -87,7 +86,7 @@ public TrackGroupViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int vi public void onBindViewHolder(@NonNull TrackGroupViewHolder holder, int position) { TrackTab trackTab = trackTabs.get(position); - holder.title.setText(trackTab.getName(app, true)); + holder.title.setText(trackTab.getDirName(true)); boolean selected = trackTab == selectedTab; int colorId = selected ? activeColorId : defaultColorId; @@ -101,7 +100,7 @@ public void onBindViewHolder(@NonNull TrackGroupViewHolder holder, int position) int adapterPosition = holder.getAdapterPosition(); if (adapterPosition != RecyclerView.NO_POSITION && target instanceof TracksTabsFragment) { TrackTab tab = trackTabs.get(adapterPosition); - ((TracksTabsFragment) target).setSelectedTab(tab.getTypeName()); + ((TracksTabsFragment) target).setSelectedTab(tab.getId()); } dismiss(); }); diff --git a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackItemsFragment.java b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackItemsFragment.java index da2bbb77b3c..35d28591c2d 100644 --- a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackItemsFragment.java +++ b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackItemsFragment.java @@ -27,9 +27,9 @@ public class TrackItemsFragment extends BaseOsmAndFragment implements OsmAndComp public static final String TAG = TrackItemsFragment.class.getSimpleName(); - private static final String TRACK_TAB_NAME_KEY = "track_tab_name_key"; + private static final String TRACK_TAB_ID_KEY = "track_tab_id_key"; - private String trackTabName; + private String trackTabId; private TracksAdapter adapter; private RecyclerView recyclerView; @@ -79,11 +79,11 @@ private void setupAdapter(@NonNull TrackTab trackTab) { @Nullable public TrackTab getTrackTab() { BaseTracksTabsFragment fragment = (BaseTracksTabsFragment) requireParentFragment(); - return fragment.getTab(trackTabName); + return fragment.getTab(trackTabId); } public void setTrackTab(@NonNull TrackTab trackTab) { - this.trackTabName = trackTab.getTypeName(); + this.trackTabId = trackTab.getId(); } @Override @@ -118,7 +118,7 @@ public void onPause() { @Override public void onSaveInstanceState(@NonNull Bundle outState) { super.onSaveInstanceState(outState); - outState.putString(TRACK_TAB_NAME_KEY, trackTabName); + outState.putString(TRACK_TAB_ID_KEY, trackTabId); } @Override diff --git a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackSortModesCollection.java b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackSortModesCollection.java new file mode 100644 index 00000000000..1f772e3158f --- /dev/null +++ b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackSortModesCollection.java @@ -0,0 +1,173 @@ +package net.osmand.plus.configmap.tracks; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +import net.osmand.IndexConstants; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.settings.backend.OsmandSettings; +import net.osmand.plus.settings.backend.preferences.ListStringPreference; +import net.osmand.plus.settings.enums.TracksSortMode; +import net.osmand.shared.gpx.data.SmartFolder; +import net.osmand.shared.gpx.data.TrackFolder; +import net.osmand.util.Algorithms; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; + +public class TrackSortModesCollection { + + private static final String ROOT_FOLDER = IndexConstants.GPX_INDEX_DIR; + private static final String SEPARATOR = ",,"; + + private final Map cachedSortModes = new ConcurrentHashMap<>(); + private final ListStringPreference preference; + private boolean keysUpgraded = false; + + public TrackSortModesCollection(@NonNull OsmandSettings settings) { + this.preference = settings.TRACKS_TABS_SORT_MODES; + loadFromPreference(); + } + + @NonNull + public TracksSortMode getRootSortMode() { + return requireSortMode(""); + } + + @NonNull + public TracksSortMode requireSortMode(@Nullable String id) { + TracksSortMode sortMode = getSortMode(id); + return sortMode != null ? sortMode : TracksSortMode.getDefaultSortMode(); + } + + @Nullable + public TracksSortMode getSortMode(@Nullable String id) { + id = removeExtraFileSeparator(id); + TracksSortMode sortMode = cachedSortModes.get(id); + if (sortMode == null) { + String idV1 = getFolderIdV1(id); + return idV1 != null ? cachedSortModes.get(idV1) : null; + } + return sortMode; + } + + public void setSortMode(@NonNull String id, @NonNull TracksSortMode sortMode) { + id = removeExtraFileSeparator(id); + cachedSortModes.put(id, sortMode); + } + + public void askUpgradeKeysWithSync(@NonNull OsmandApplication app, @Nullable TrackFolder folder) { + if (folder != null && !keysUpgraded) { + if (askUpgradeCachedKeys(app, folder)) { + keysUpgraded = true; + syncSettings(); + } + } + } + + /** + * Removes surplus keys and upgrades outdated ones. + * Upgraded keys will use folder relative path as a key (V2) instead of folder name (V1). + */ + private boolean askUpgradeCachedKeys(@NonNull OsmandApplication app, @NonNull TrackFolder trackFolder) { + TrackFolder rootFolder = trackFolder.getRootFolder(); + if (!Objects.equals(rootFolder.getDirFile(), app.getAppPathKt(ROOT_FOLDER))) { + // Execute only from tracks root folder to not lose valid entries + return false; + } + Map upgradedCache = new HashMap<>(); + putUpgradedKey(upgradedCache, TrackTabType.ON_MAP.name()); + putUpgradedKey(upgradedCache, TrackTabType.ALL.name()); + putUpgradedKey(upgradedCache, TrackTabType.FOLDERS.name()); + putUpgradedKey(upgradedCache, rootFolder.getId()); + for (TrackFolder folder : rootFolder.getFlattenedSubFolders()) { + putUpgradedKey(upgradedCache, folder.getId()); + } + for (SmartFolder folder : app.getSmartFolderHelper().getSmartFolders()) { + putUpgradedKey(upgradedCache, folder.getId()); + } + cachedSortModes.clear(); + cachedSortModes.putAll(upgradedCache); + return true; + } + + private void putUpgradedKey(@NonNull Map map, @NonNull String id) { + id = removeExtraFileSeparator(id); + TracksSortMode sortMode = getSortMode(id); + if (sortMode != null) { + map.put(id, sortMode); + } + } + + public void updateAfterMoveTrackFolder(@NonNull TrackFolder trackFolder, @NonNull File oldDir) { + String previousId = getFolderId(oldDir.getAbsolutePath()); + TracksSortMode sortMode = getSortMode(previousId); + if (sortMode != null) { + setSortMode(trackFolder.getId(), sortMode); + syncSettings(); + } + } + + public void updateAfterDeleteTrackFolder(@NonNull TrackFolder trackFolder) { + cachedSortModes.remove(trackFolder.getId()); + syncSettings(); + } + + public void syncSettings() { + saveToPreference(); + } + + private void loadFromPreference() { + List tokens = preference.getStringsList(); + if (!Algorithms.isEmpty(tokens)) { + for (String token : tokens) { + String[] tokenParts = token.split(SEPARATOR); + if (tokenParts.length == 2) { + cachedSortModes.put(tokenParts[0], TracksSortMode.getByValue(tokenParts[1])); + } + } + } + } + + private void saveToPreference() { + List tokens = new ArrayList<>(); + for (Entry entry : cachedSortModes.entrySet()) { + tokens.add(entry.getKey() + SEPARATOR + entry.getValue().name()); + } + preference.setStringsList(tokens); + } + + @NonNull + public static String getFolderId(@NonNull String absolutePath) { + int index = absolutePath.indexOf(ROOT_FOLDER); + if (index > 0) { + index += ROOT_FOLDER.length(); + } + return index > 0 ? absolutePath.substring(index) : absolutePath; + } + + @Nullable + private static String getFolderIdV1(@Nullable String id) { + if (id != null && id.isEmpty()) { + return removeExtraFileSeparator(ROOT_FOLDER); + } + int index = id != null ? id.lastIndexOf(File.separator) : -1; + return index > 0 ? id.substring(index + 1) : null; + } + + @Nullable + private static String removeExtraFileSeparator(@Nullable String id) { + // Ensure consistency by removing trailing File.separator from relative paths + // before querying or saving to settings to avoid key mismatches. + if (id != null && id.endsWith(File.separator)) { + return id.substring(0, id.length() - 1); + } + return id; + } +} diff --git a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackTab.java b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackTab.java index 2d268606f58..49e39acab6a 100644 --- a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackTab.java +++ b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackTab.java @@ -1,52 +1,95 @@ package net.osmand.plus.configmap.tracks; +import static net.osmand.plus.configmap.tracks.TrackTabType.FOLDER; +import static net.osmand.plus.configmap.tracks.TrackTabType.SMART_FOLDER; + import android.content.Context; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import net.osmand.plus.settings.enums.TracksSortMode; +import net.osmand.shared.gpx.data.ComparableTracksGroup; import net.osmand.shared.gpx.data.SmartFolder; import net.osmand.shared.gpx.data.TrackFolder; import net.osmand.plus.track.helpers.GpxUiHelper; import net.osmand.shared.gpx.TrackItem; +import net.osmand.shared.gpx.data.TracksGroup; +import net.osmand.shared.gpx.filters.TrackFolderAnalysis; import java.io.File; import java.util.ArrayList; import java.util.List; -public class TrackTab { - public static final String SMART_FOLDER_TAB_NAME_PREFIX = "SMART_FOLDER___"; +public class TrackTab implements TracksGroup, ComparableTracksGroup { + @NonNull public final TrackTabType type; public final List items = new ArrayList<>(); @Nullable public final File directory; - @Nullable - private final String name; - private String typeName = null; + public final SmartFolder smartFolder; + public final String initialName; private TracksSortMode sortMode = TracksSortMode.getDefaultSortMode(); + private TrackFolderAnalysis analysis = null; - public TrackTab(@NonNull File directory) { - this.directory = directory; - this.name = null; - this.type = TrackTabType.FOLDER; + public TrackTab(@NonNull Context context, @NonNull File directory) { + this(context, directory, null, FOLDER); + } + + public TrackTab(@NonNull Context context, @NonNull SmartFolder smartFolder) { + this(context, null, smartFolder, SMART_FOLDER); } - public TrackTab(@NonNull TrackTabType type) { - this.directory = null; - this.name = null; + public TrackTab(@NonNull Context context, @NonNull TrackTabType type) { + this(context, null, null, type); + } + + private TrackTab(@NonNull Context context, @Nullable File directory, + @Nullable SmartFolder smartFolder, @NonNull TrackTabType type) { + this.directory = directory; + this.smartFolder = smartFolder; this.type = type; + this.initialName = createInitialName(context); + } + + @NonNull + private String createInitialName(@NonNull Context context) { + if (type.titleId != -1) { + return context.getString(type.titleId); + } + if (directory != null) { + return GpxUiHelper.getFolderName(context, directory); + } + if (smartFolder != null) { + return smartFolder.getFolderName(); + } + return ""; + } + + @NonNull + @Override + public String getId() { + return switch (type) { + case FOLDER -> directory != null ? TrackSortModesCollection.getFolderId(directory.getAbsolutePath()) : ""; + case SMART_FOLDER -> smartFolder != null ? smartFolder.getId() : ""; + default -> type.name(); + }; } - public TrackTab(@NonNull SmartFolder smartFolder) { - this.directory = null; - this.name = smartFolder.getFolderName(); - this.type = TrackTabType.SMART_FOLDER; - typeName = SMART_FOLDER_TAB_NAME_PREFIX + name; + @NonNull + @Override + public TrackFolderAnalysis getFolderAnalysis() { + // Note: To avoid excessive calculations that could slow down the UI, + // analysis is not recalculated when folder or track parameters change. + // For example after file deletion or moving to another directory. + if (analysis == null) { + analysis = smartFolder != null ? smartFolder.getFolderAnalysis() : new TrackFolderAnalysis(this); + } + return analysis; } @NonNull @@ -59,34 +102,22 @@ public void setSortMode(@NonNull TracksSortMode sortMode) { } @NonNull - public String getName(@NonNull Context context) { - return getName(context, false); + @Override + public String getName() { + return getDirName(false); } @NonNull - public String getName(@NonNull Context context, boolean includeParentDir) { - if (type.titleId != -1) { - return context.getString(type.titleId); - } - if (directory != null) { - return GpxUiHelper.getFolderName(context, directory, includeParentDir); - } - if (name != null) { - return name; - } - return ""; + @Override + public String getDirName(boolean useExtendedName) { + return directory != null && useExtendedName + ? GpxUiHelper.getExtendedFolderName(directory, initialName) + : initialName; } - @NonNull - public String getTypeName() { - switch (type) { - case FOLDER: - return directory != null ? directory.getName() : ""; - case SMART_FOLDER: - return typeName != null ? typeName : ""; - default: - return type.name(); - } + @Override + public int getDefaultOrder() { + return type.ordinal(); } @NonNull @@ -111,9 +142,20 @@ public List getTrackFolders() { return trackFolders; } + @Override + public long lastModified() { + if (directory != null) { + return directory.lastModified(); + } + if (smartFolder != null) { + return smartFolder.lastModified(); + } + return 0; + } + @NonNull @Override public String toString() { - return "TrackTab{name=" + getTypeName() + "}"; + return "TrackTab{name=" + getId() + "}"; } } diff --git a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackTabType.java b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackTabType.java index 63b4ed63c5c..cdfda0dd3d0 100644 --- a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackTabType.java +++ b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackTabType.java @@ -9,8 +9,8 @@ public enum TrackTabType { ON_MAP(R.string.shared_string_on_map, R.drawable.ic_show_on_map), ALL(R.string.shared_string_all, R.drawable.ic_action_list_header), - FOLDER(-1, R.drawable.ic_action_folder), SMART_FOLDER(-1, R.drawable.ic_action_folder_smart), + FOLDER(-1, R.drawable.ic_action_folder), FOLDERS(R.string.shared_string_folders, R.drawable.ic_action_folder); diff --git a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackTabsHelper.java b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackTabsHelper.java index 2c334f04c72..7c56832c71f 100644 --- a/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackTabsHelper.java +++ b/OsmAnd/src/net/osmand/plus/configmap/tracks/TrackTabsHelper.java @@ -8,14 +8,10 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import net.osmand.Collator; -import net.osmand.IndexConstants; -import net.osmand.OsmAndCollator; import net.osmand.plus.shared.SharedUtil; import net.osmand.data.LatLon; import net.osmand.shared.gpx.GpxFile; import net.osmand.plus.OsmandApplication; -import net.osmand.plus.R; import net.osmand.plus.myplaces.tracks.ItemsSelectionHelper; import net.osmand.plus.plugins.PluginsHelper; import net.osmand.plus.plugins.monitoring.OsmandMonitoringPlugin; @@ -30,6 +26,7 @@ import net.osmand.util.Algorithms; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashMap; @@ -46,8 +43,7 @@ public class TrackTabsHelper { private final ItemsSelectionHelper itemsSelectionHelper; private final Set recentlyVisibleTrackItem = new HashSet<>(); - private final Map trackTabs = new LinkedHashMap<>(); - private final Collator collator = OsmAndCollator.primaryCollator(); + protected final Map trackTabs = new LinkedHashMap<>(); public TrackTabsHelper(@NonNull OsmandApplication app) { this.app = app; @@ -62,90 +58,73 @@ public ItemsSelectionHelper getItemsSelectionHelper() { } @NonNull - public Map getTrackTabs() { - return trackTabs; + public List getSortedTrackTabs(boolean useExtendedName) { + List result = getTrackTabs(); + result.sort(new TracksComparator(getRootSortMode(), getDefaultLocation(), useExtendedName)); + return result; } @NonNull - public Set getRecentlyVisibleTracks() { - return new HashSet<>(recentlyVisibleTrackItem); + public List getTrackTabs() { + return new ArrayList<>(trackTabs.values()); } - public void updateTrackItems(@NonNull List trackItems) { - List allTrackItems = new ArrayList<>(trackItems); - if (settings.SAVE_GLOBAL_TRACK_TO_GPX.get() || gpxSelectionHelper.getSelectedCurrentRecordingTrack() != null) { - SelectedGpxFile selectedGpxFile = app.getSavingTrackHelper().getCurrentTrack(); - TrackItem trackItem = new TrackItem(selectedGpxFile.getGpxFile()); - allTrackItems.add(trackItem); - } - itemsSelectionHelper.setAllItems(allTrackItems); - Map trackTabs = new LinkedHashMap<>(); - for (TrackItem item : trackItems) { - addTrackItem(trackTabs, item); - } - updateTrackTabs(trackTabs); + @Nullable + public TrackTab getTrackTab(@NonNull String id) { + return trackTabs.get(id); } - public void updateItems(@NonNull TrackFolder folder){ - List allTrackItems = new ArrayList<>(folder.getFlattenedTrackItems()); - if (settings.SAVE_GLOBAL_TRACK_TO_GPX.get() || gpxSelectionHelper.getSelectedCurrentRecordingTrack() != null) { - SelectedGpxFile selectedGpxFile = app.getSavingTrackHelper().getCurrentTrack(); - TrackItem trackItem = new TrackItem(selectedGpxFile.getGpxFile()); - allTrackItems.add(trackItem); - } - itemsSelectionHelper.setAllItems(allTrackItems); - updateSelectTrackTabs(folder); + @NonNull + public Set getRecentlyVisibleTracks() { + return new HashSet<>(recentlyVisibleTrackItem); } - private void updateTrackTabs(@NonNull Map folderTabs) { + public void updateTrackItems(@NonNull TrackFolder rootFolder) { + List allTrackItems = new ArrayList<>(rootFolder.getFlattenedTrackItems()); + addCurrentTrackItemIfPresent(allTrackItems); + itemsSelectionHelper.setAllItems(allTrackItems); + processVisibleTracks(); processRecentlyVisibleTracks(); + + updateTrackTabs(rootFolder); + loadTabsSortModes(rootFolder); + sortTrackTabsContent(); + } + + protected void updateTrackTabs(@NonNull TrackFolder rootFolder) { trackTabs.clear(); trackTabs.put(TrackTabType.ON_MAP.name(), getTracksOnMapTab()); trackTabs.put(TrackTabType.ALL.name(), getAllTracksTab()); - for (TrackTab tab : sortTrackTabs(getAllSmartFoldersTabs())) { - trackTabs.put(tab.getTypeName(), tab); + for (TrackTab tab : getAllSmartFoldersTabs()) { + trackTabs.put(tab.getId(), tab); } - for (TrackTab tab : sortTrackTabs(folderTabs)) { - trackTabs.put(tab.getTypeName(), tab); + for (TrackTab tab : getAllTrackFoldersTabs(rootFolder)) { + trackTabs.put(tab.getId(), tab); } - loadTabsSortModes(); - sortTrackTabs(); } - private List sortTrackTabs(Map folderTabs) { - List sortedTabs = new ArrayList<>(folderTabs.values()); - Map tabsSortModes = settings.getTrackSortModes(); - TracksSortMode sortMode = TracksSortMode.getDefaultSortMode(); - String trackRootDir = app.getAppPath(IndexConstants.GPX_INDEX_DIR).getName(); - String sortModeKey = tabsSortModes.get(trackRootDir); - if (sortModeKey != null) { - sortMode = TracksSortMode.getByValue(sortModeKey); + private void addCurrentTrackItemIfPresent(@NonNull List trackItems) { + if (settings.SAVE_GLOBAL_TRACK_TO_GPX.get() || gpxSelectionHelper.getSelectedCurrentRecordingTrack() != null) { + SelectedGpxFile selectedGpxFile = app.getSavingTrackHelper().getCurrentTrack(); + TrackItem trackItem = new TrackItem(selectedGpxFile.getGpxFile()); + trackItems.add(trackItem); } - int sign = sortMode == TracksSortMode.NAME_DESCENDING ? -1 : 1; - // PRELIMINARY: Should ultimately use TracksComparator for trackTabs, similar to trackFolders - Collections.sort(sortedTabs, (tab1, tab2) -> { - String name1 = tab1.getTypeName(); - String name2 = tab2.getTypeName(); - return sign * collator.compare(name1, name2); - }); - return sortedTabs; } - private void updateSelectTrackTabs(@NonNull TrackFolder folder) { - processVisibleTracks(); - processRecentlyVisibleTracks(); - trackTabs.clear(); - trackTabs.put(app.getString(R.string.shared_string_visible), getTracksOnMapTab()); - trackTabs.put(app.getString(R.string.shared_string_all_tracks), getAllTracksTab()); - trackTabs.put(app.getString(R.string.shared_string_folders), getFoldersTab(folder)); - loadTabsSortModes(); - sortTrackTabs(); + @NonNull + private Collection getAllTrackFoldersTabs(@NonNull TrackFolder rootFolder) { + List trackItems = rootFolder.getFlattenedTrackItems(); + Map trackFolderTabs = new LinkedHashMap<>(); + for (TrackItem item : trackItems) { + addTrackItem(trackFolderTabs, item); + } + return trackFolderTabs.values(); } @NonNull - private TrackTab getTracksOnMapTab() { - TrackTab trackTab = new TrackTab(TrackTabType.ON_MAP); + protected TrackTab getTracksOnMapTab() { + TrackTab trackTab = new TrackTab(app, TrackTabType.ON_MAP); trackTab.items.addAll(getOnMapTabItems()); return trackTab; } @@ -157,8 +136,8 @@ public void updateTracksOnMap() { } @NonNull - private TrackTab getAllTracksTab() { - TrackTab trackTab = new TrackTab(TrackTabType.ALL); + protected TrackTab getAllTracksTab() { + TrackTab trackTab = new TrackTab(app, TrackTabType.ALL); trackTab.items.addAll(getAllTabItems()); return trackTab; } @@ -187,8 +166,8 @@ private List getAllTabItems() { } @NonNull - private TrackTab getFoldersTab(@NonNull TrackFolder folder) { - TrackTab trackTab = new TrackTab(TrackTabType.FOLDERS); + protected TrackTab getFoldersTab(@NonNull TrackFolder folder) { + TrackTab trackTab = new TrackTab(app, TrackTabType.FOLDERS); trackTab.items.add(TYPE_SORT_TRACKS); trackTab.items.addAll(folder.getSubFolders()); trackTab.items.addAll(folder.getTrackItems()); @@ -245,13 +224,13 @@ public void processVisibleTracks() { } @NonNull - private Map getAllSmartFoldersTabs() { - Map smartFoldersTabs = new LinkedHashMap<>(); + private List getAllSmartFoldersTabs() { + List smartFoldersTabs = new ArrayList<>(); for (SmartFolder folder : app.getSmartFolderHelper().getSmartFolders()) { - TrackTab folderTab = new TrackTab(folder); + TrackTab folderTab = new TrackTab(app, folder); folderTab.items.add(TYPE_SORT_TRACKS); folderTab.items.addAll(folder.getTrackItems()); - smartFoldersTabs.put(folderTab.getTypeName(), folderTab); + smartFoldersTabs.add(folderTab); } return smartFoldersTabs; } @@ -261,12 +240,13 @@ private TrackTab addTrackItem(@NonNull Map trackTabs, @NonNull KFile file = item.getFile(); if (file != null && file.getParentFile() != null) { KFile dir = file.getParentFile(); - if(dir != null) { - TrackTab trackTab = trackTabs.get(dir.name()); + if (dir != null) { + String folderId = TrackSortModesCollection.getFolderId(dir.absolutePath()); + TrackTab trackTab = trackTabs.get(folderId); if (trackTab == null) { - trackTab = new TrackTab(SharedUtil.jFile(dir)); + trackTab = new TrackTab(app, SharedUtil.jFile(dir)); trackTab.items.add(TYPE_SORT_TRACKS); - trackTabs.put(trackTab.getTypeName(), trackTab); + trackTabs.put(folderId, trackTab); } trackTab.items.add(item); return trackTab; @@ -300,14 +280,14 @@ public void saveTracksVisibility() { gpxSelectionHelper.saveTracksVisibility(itemsSelectionHelper.getSelectedItems()); } - private void sortTrackTabs() { + private void sortTrackTabsContent() { for (TrackTab trackTab : trackTabs.values()) { sortTrackTab(trackTab); } } public void sortTrackTab(@NonNull TrackTab trackTab) { - LatLon latLon = app.getMapViewTrackingUtilities().getDefaultLocation(); + LatLon latLon = getDefaultLocation(); if (trackTab.type == TrackTabType.ON_MAP) { List visibleItems = getVisibleItems(); List recentlyVisibleItems = getRecentlyVisibleItems(); @@ -325,25 +305,32 @@ public void sortTrackTab(@NonNull TrackTab trackTab) { } } - public void loadTabsSortModes() { - Map tabsSortModes = settings.getTrackSortModes(); - if (!Algorithms.isEmpty(tabsSortModes)) { - for (Entry entry : tabsSortModes.entrySet()) { - TrackTab trackTab = trackTabs.get(entry.getKey()); - if (trackTab != null) { - trackTab.setSortMode(TracksSortMode.getByValue(entry.getValue())); - } + public void loadTabsSortModes(@NonNull TrackFolder folder) { + TrackSortModesCollection sortModes = settings.getTrackSortModes(folder); + for (Entry entry : trackTabs.entrySet()) { + TracksSortMode sortMode = sortModes.getSortMode(entry.getKey()); + if (sortMode != null) { + TrackTab trackTab = entry.getValue(); + trackTab.setSortMode(sortMode); } } } public void saveTabsSortModes() { - Map tabsSortModes = settings.getTrackSortModes(); + TrackSortModesCollection sortModes = settings.getTrackSortModes(); for (TrackTab trackTab : trackTabs.values()) { - String name = trackTab.getTypeName(); - String sortType = trackTab.getSortMode().name(); - tabsSortModes.put(name, sortType); + sortModes.setSortMode(trackTab.getId(), trackTab.getSortMode()); } - settings.saveTabsSortModes(tabsSortModes); + sortModes.syncSettings(); + } + + @NonNull + private TracksSortMode getRootSortMode() { + return settings.getTrackSortModes().getRootSortMode(); + } + + @NonNull + private LatLon getDefaultLocation() { + return app.getMapViewTrackingUtilities().getDefaultLocation(); } } diff --git a/OsmAnd/src/net/osmand/plus/configmap/tracks/TracksComparator.java b/OsmAnd/src/net/osmand/plus/configmap/tracks/TracksComparator.java index 2121fa93041..e44bb2d2092 100644 --- a/OsmAnd/src/net/osmand/plus/configmap/tracks/TracksComparator.java +++ b/OsmAnd/src/net/osmand/plus/configmap/tracks/TracksComparator.java @@ -1,6 +1,9 @@ package net.osmand.plus.configmap.tracks; import static com.jwetherell.openmap.common.LatLonPoint.EQUIVALENT_TOLERANCE; +import static net.osmand.plus.settings.enums.TracksSortMode.DATE_DESCENDING; +import static net.osmand.plus.settings.enums.TracksSortMode.DISTANCE_ASCENDING; +import static net.osmand.plus.settings.enums.TracksSortMode.DURATION_ASCENDING; import static net.osmand.plus.settings.enums.TracksSortMode.LAST_MODIFIED; import static net.osmand.plus.settings.enums.TracksSortMode.NAME_ASCENDING; import static net.osmand.plus.settings.enums.TracksSortMode.NAME_DESCENDING; @@ -16,7 +19,6 @@ import net.osmand.plus.myplaces.tracks.VisibleTracksGroup; import net.osmand.plus.settings.enums.TracksSortMode; import net.osmand.shared.gpx.data.ComparableTracksGroup; -import net.osmand.shared.gpx.filters.TrackFolderAnalysis; import net.osmand.shared.data.KLatLon; import net.osmand.shared.gpx.GpxDataItem; import net.osmand.shared.gpx.GpxTrackAnalysis; @@ -33,6 +35,7 @@ public class TracksComparator implements Comparator { public final TrackTab trackTab; public final TracksSortMode sortMode; public final Collator collator = OsmAndCollator.primaryCollator(); + private boolean useExtendedName = false; public TracksComparator(@NonNull TrackTab trackTab, @NonNull LatLon latLon) { this.trackTab = trackTab; @@ -40,8 +43,14 @@ public TracksComparator(@NonNull TrackTab trackTab, @NonNull LatLon latLon) { this.latLon = SharedUtil.kLatLon(latLon); } + public TracksComparator(@NonNull TracksSortMode sortMode, + @NonNull LatLon latLon, boolean useExtendedName) { + this(sortMode, latLon); + this.useExtendedName = useExtendedName; + } + public TracksComparator(@NonNull TracksSortMode sortMode, @NonNull LatLon latLon) { - trackTab = null; + this.trackTab = null; this.sortMode = sortMode; this.latLon = SharedUtil.kLatLon(latLon); } @@ -66,8 +75,16 @@ public int compare(Object o1, Object o2) { if (o2 instanceof VisibleTracksGroup) { return 1; } - if (o1 instanceof ComparableTracksGroup) { - return o2 instanceof ComparableTracksGroup ? compareTrackFolders((ComparableTracksGroup) o1, (ComparableTracksGroup) o2) : -1; + if (o1 instanceof ComparableTracksGroup folder1) { + if (o2 instanceof ComparableTracksGroup folder2) { + int predefinedOrder1 = folder1.getDefaultOrder(); + int predefinedOrder2 = folder2.getDefaultOrder(); + if (predefinedOrder1 != predefinedOrder2) { + return Integer.compare(predefinedOrder1, predefinedOrder2); + } + return compareTrackFolders(folder1, folder2); + } + return -1; } if (o2 instanceof ComparableTracksGroup) { return 1; @@ -79,43 +96,35 @@ public int compare(Object o1, Object o2) { } private int compareTrackFolders(@NonNull ComparableTracksGroup folder1, @NonNull ComparableTracksGroup folder2) { - TrackFolderAnalysis folderAnalysis1; - TrackFolderAnalysis folderAnalysis2; + int multiplier; switch (sortMode) { - case NAME_ASCENDING: - return compareTrackFolderNames(folder1, folder2); - case NAME_DESCENDING: - return -compareTrackFolderNames(folder1, folder2); - case DATE_ASCENDING: - return compareFolderFilesByLastModified(folder1, folder2); - case DATE_DESCENDING: - return -compareFolderFilesByLastModified(folder1, folder2); - case LAST_MODIFIED: - return compareFolderFilesByLastModified(folder1, folder2); - case DISTANCE_DESCENDING: - folderAnalysis1 = folder1.getFolderAnalysis(); - folderAnalysis2 = folder2.getFolderAnalysis(); - if (Math.abs(folderAnalysis1.getTotalDistance() - folderAnalysis2.getTotalDistance()) >= EQUIVALENT_TOLERANCE) { - return -Float.compare(folderAnalysis1.getTotalDistance(), folderAnalysis2.getTotalDistance()); - } - case DISTANCE_ASCENDING: - folderAnalysis1 = folder1.getFolderAnalysis(); - folderAnalysis2 = folder2.getFolderAnalysis(); - if (Math.abs(folderAnalysis1.getTotalDistance() - folderAnalysis2.getTotalDistance()) >= EQUIVALENT_TOLERANCE) { - return Float.compare(folderAnalysis1.getTotalDistance(), folderAnalysis2.getTotalDistance()); - } - case DURATION_DESCENDING: - folderAnalysis1 = folder1.getFolderAnalysis(); - folderAnalysis2 = folder2.getFolderAnalysis(); - if (folderAnalysis1.getTimeSpan() != folderAnalysis2.getTimeSpan()) { - return -Long.compare(folderAnalysis1.getTimeSpan(), folderAnalysis2.getTimeSpan()); + case NAME_ASCENDING, NAME_DESCENDING: { + multiplier = sortMode == NAME_ASCENDING ? 1 : -1; + return multiplier * compareTrackFolderNames(folder1, folder2); + } + + case LAST_MODIFIED, DATE_ASCENDING, DATE_DESCENDING: { + multiplier = sortMode == DATE_DESCENDING ? -1 : 1; + return multiplier * compareFolderFilesByLastModified(folder1, folder2); + } + + case DISTANCE_ASCENDING, DISTANCE_DESCENDING: { + float dist1 = folder1.getFolderAnalysis().getTotalDistance(); + float dist2 = folder2.getFolderAnalysis().getTotalDistance(); + if (Math.abs(dist1 - dist2) >= EQUIVALENT_TOLERANCE) { + multiplier = sortMode == DISTANCE_ASCENDING ? 1 : -1; + return multiplier * Float.compare(dist1, dist2); } - case DURATION_ASCENDING: - folderAnalysis1 = folder1.getFolderAnalysis(); - folderAnalysis2 = folder2.getFolderAnalysis(); - if (folderAnalysis1.getTimeSpan() != folderAnalysis2.getTimeSpan()) { - return Long.compare(folderAnalysis1.getTimeSpan(), folderAnalysis2.getTimeSpan()); + } + + case DURATION_ASCENDING, DURATION_DESCENDING: { + int timeSpan1 = folder1.getFolderAnalysis().getTimeSpan(); + int timeSpan2 = folder2.getFolderAnalysis().getTimeSpan(); + if (timeSpan1 != timeSpan2) { + multiplier = sortMode == DURATION_ASCENDING ? 1 : -1; + return multiplier * Long.compare(timeSpan1, timeSpan2); } + } } return compareTrackFolderNames(folder1, folder2); } @@ -292,8 +301,9 @@ private int compareTrackItemNames(@NonNull TrackItem item1, @NonNull TrackItem i return compareNames(item1.getName(), item2.getName()); } - private int compareTrackFolderNames(@NonNull ComparableTracksGroup folder1, @NonNull ComparableTracksGroup folder2) { - return compareNames(folder1.getDirName(), folder2.getDirName()); + private int compareTrackFolderNames(@NonNull ComparableTracksGroup folder1, + @NonNull ComparableTracksGroup folder2) { + return compareNames(folder1.getDirName(useExtendedName), folder2.getDirName(useExtendedName)); } private int compareNames(@NonNull String item1, @NonNull String item2) { diff --git a/OsmAnd/src/net/osmand/plus/configmap/tracks/TracksTabAdapter.java b/OsmAnd/src/net/osmand/plus/configmap/tracks/TracksTabAdapter.java index 63a83a0fcc9..8ac7fd76113 100644 --- a/OsmAnd/src/net/osmand/plus/configmap/tracks/TracksTabAdapter.java +++ b/OsmAnd/src/net/osmand/plus/configmap/tracks/TracksTabAdapter.java @@ -15,25 +15,22 @@ public class TracksTabAdapter extends FragmentStatePagerAdapter { - private final OsmandApplication app; private final List trackTabs = new ArrayList<>(); - public TracksTabAdapter(@NonNull OsmandApplication app, @NonNull FragmentManager manager, @NonNull List tabs) { + public TracksTabAdapter(@NonNull FragmentManager manager, @NonNull List tabs) { super(manager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); - this.app = app; trackTabs.addAll(tabs); } - public void setTrackTabs(@NonNull Map tabs) { + public void setTrackTabs(@NonNull List tabs) { trackTabs.clear(); - trackTabs.addAll(tabs.values()); + trackTabs.addAll(tabs); notifyDataSetChanged(); } @Override public int getItemPosition(@NonNull Object object) { - if (object instanceof TrackItemsFragment) { - TrackItemsFragment fragment = (TrackItemsFragment) object; + if (object instanceof TrackItemsFragment fragment) { int index = trackTabs.indexOf(fragment.getTrackTab()); return index >= 0 ? index : POSITION_NONE; } @@ -56,7 +53,7 @@ public Fragment getItem(int position) { @Override public CharSequence getPageTitle(int position) { - return trackTabs.get(position).getName(app); + return trackTabs.get(position).getName(); } @Override diff --git a/OsmAnd/src/net/osmand/plus/configmap/tracks/TracksTabsFragment.java b/OsmAnd/src/net/osmand/plus/configmap/tracks/TracksTabsFragment.java index 12174dc0de6..98fcf8d0d6c 100644 --- a/OsmAnd/src/net/osmand/plus/configmap/tracks/TracksTabsFragment.java +++ b/OsmAnd/src/net/osmand/plus/configmap/tracks/TracksTabsFragment.java @@ -25,6 +25,7 @@ import androidx.fragment.app.FragmentManager; import androidx.viewpager.widget.ViewPager.SimpleOnPageChangeListener; +import net.osmand.IndexConstants; import net.osmand.plus.shared.SharedUtil; import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; @@ -228,7 +229,7 @@ private void updateButtonsState() { selectionButton.setEnabled(!Algorithms.isEmpty(itemsSelectionHelper.getSelectedItems()) || notAllSelected); } applyButton.setEnabled(itemsSelectionHelper.hasItemsToApply()); - TrackTab allTracksTab = trackTabsHelper.getTrackTabs().get(TrackTabType.ALL.name()); + TrackTab allTracksTab = trackTabsHelper.getTrackTab(TrackTabType.ALL.name()); searchButton.setVisibility(allTracksTab == null ? View.GONE : View.VISIBLE); } } @@ -262,7 +263,7 @@ public void tracksLoaded(@NonNull TrackFolder folder) { @Override public void loadTracksFinished(@NonNull TrackFolder folder) { - trackTabsHelper.updateTrackItems(folder.getFlattenedTrackItems()); + trackTabsHelper.updateTrackItems(folder); AndroidUiHelper.updateVisibility(progressBar, false); updateTrackTabs(); applyPreselectedParams(); @@ -277,10 +278,10 @@ public void deferredLoadTracksFinished(@NonNull TrackFolder folder) { private void applyPreselectedParams() { if (preselectedTabParams != null) { - String tabName = preselectedTabParams.getPreselectedTabName(app, getTrackTabs()); - TrackTab trackTab = getTab(tabName); + String tabId = preselectedTabParams.getPreselectedTabId(); + TrackTab trackTab = getTab(tabId); if (trackTab != null) { - setSelectedTab(tabName); + setSelectedTab(tabId); if (preselectedTabParams.shouldSelectAll()) { itemsSelectionHelper.onItemsSelected(trackTab.getTrackItems(), true); diff --git a/OsmAnd/src/net/osmand/plus/helpers/IntentHelper.java b/OsmAnd/src/net/osmand/plus/helpers/IntentHelper.java index 25b8e943bfe..b7c97af043d 100644 --- a/OsmAnd/src/net/osmand/plus/helpers/IntentHelper.java +++ b/OsmAnd/src/net/osmand/plus/helpers/IntentHelper.java @@ -2,8 +2,7 @@ import static net.osmand.plus.backup.BackupListeners.OnRegisterDeviceListener; import static net.osmand.plus.configmap.tracks.PreselectedTabParams.CALLING_FRAGMENT_TAG; -import static net.osmand.plus.configmap.tracks.PreselectedTabParams.PRESELECTED_TRACKS_TAB_NAME; -import static net.osmand.plus.configmap.tracks.PreselectedTabParams.PRESELECTED_TRACKS_TAB_TYPE; +import static net.osmand.plus.configmap.tracks.PreselectedTabParams.PRESELECTED_TRACKS_TAB_ID; import static net.osmand.plus.configmap.tracks.PreselectedTabParams.SELECT_ALL_ITEMS_ON_TAB; import static net.osmand.plus.helpers.MapFragmentsHelper.CLOSE_ALL_FRAGMENTS; import static net.osmand.plus.settings.fragments.ExportSettingsFragment.SELECTED_TYPES; @@ -560,13 +559,12 @@ public void parseContentIntent() { clearIntent(intent); } Bundle extras = intent.getExtras(); - if (extras != null && intent.hasExtra(PRESELECTED_TRACKS_TAB_NAME) && intent.hasExtra(PRESELECTED_TRACKS_TAB_TYPE)) { - String name = extras.getString(PRESELECTED_TRACKS_TAB_NAME, TrackTabType.ALL.name()); + if (extras != null && intent.hasExtra(PRESELECTED_TRACKS_TAB_ID)) { + String id = extras.getString(PRESELECTED_TRACKS_TAB_ID, TrackTabType.ALL.name()); String callingFragmentTag = extras.getString(CALLING_FRAGMENT_TAG, null); - TrackTabType type = AndroidUtils.getSerializable(extras, PRESELECTED_TRACKS_TAB_TYPE, TrackTabType.class); boolean selectAllItems = intent.getBooleanExtra(SELECT_ALL_ITEMS_ON_TAB, false); - PreselectedTabParams params = new PreselectedTabParams(name, type != null ? type : TrackTabType.ALL, selectAllItems); + PreselectedTabParams params = new PreselectedTabParams(id, selectAllItems); TracksTabsFragment.showInstance(mapActivity.getSupportFragmentManager(), params, callingFragmentTag); clearIntent(intent); } diff --git a/OsmAnd/src/net/osmand/plus/myplaces/tracks/VisibleTracksGroup.java b/OsmAnd/src/net/osmand/plus/myplaces/tracks/VisibleTracksGroup.java index 004350c6c8f..8e555593a17 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/tracks/VisibleTracksGroup.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/tracks/VisibleTracksGroup.java @@ -4,6 +4,7 @@ import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; +import net.osmand.plus.configmap.tracks.TrackTabType; import net.osmand.shared.gpx.TrackItem; import net.osmand.shared.gpx.data.TracksGroup; import net.osmand.plus.track.helpers.GpxSelectionHelper; @@ -22,6 +23,12 @@ public VisibleTracksGroup(@NonNull OsmandApplication app) { this.selectedGpxHelper = app.getSelectedGpxHelper(); } + @NonNull + @Override + public String getId() { + return TrackTabType.ON_MAP.name(); + } + @NonNull @Override public List getTrackItems() { diff --git a/OsmAnd/src/net/osmand/plus/myplaces/tracks/controller/TrackFolderOptionsController.java b/OsmAnd/src/net/osmand/plus/myplaces/tracks/controller/TrackFolderOptionsController.java index b9b1133a2ad..2cdb74686ab 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/tracks/controller/TrackFolderOptionsController.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/tracks/controller/TrackFolderOptionsController.java @@ -20,7 +20,6 @@ import net.osmand.plus.base.dialog.data.DisplayItem; import net.osmand.plus.base.dialog.interfaces.controller.IDialogItemClicked; import net.osmand.plus.base.dialog.interfaces.controller.IDisplayDataProvider; -import net.osmand.shared.gpx.TrackItem; import net.osmand.plus.myplaces.tracks.TrackFoldersHelper; import net.osmand.plus.settings.bottomsheets.CustomizableOptionsBottomSheet; import net.osmand.shared.gpx.data.TrackFolder; @@ -37,11 +36,9 @@ import org.apache.commons.logging.Log; import java.io.File; -import java.util.ArrayList; -import java.util.List; -public class TrackFolderOptionsController extends BaseDialogController implements IDisplayDataProvider, - IDialogItemClicked, TrackFolderOptionsListener { +public class TrackFolderOptionsController extends BaseDialogController + implements IDisplayDataProvider, IDialogItemClicked, TrackFolderOptionsListener { private final static Log LOG = PlatformUtil.getLog(TrackFolderOptionsController.class); @@ -165,7 +162,7 @@ private void showRenameDialog() { } }); String caption = activity.getString(R.string.enter_new_name); - CustomAlert.showInput(dialogData, activity, trackFolder.getDirName(), caption); + CustomAlert.showInput(dialogData, activity, trackFolder.getDirName(false), caption); } } @@ -175,15 +172,7 @@ private void renameFolder(@NonNull String newName) { if (oldDir.renameTo(newDir)) { trackFolder.setDirFile(SharedUtil.kFile(newDir)); trackFolder.resetCachedData(); - - List files = new ArrayList<>(); - for (TrackItem trackItem : trackFolder.getFlattenedTrackItems()) { - KFile file = trackItem.getFile(); - if (file != null) { - files.add(SharedUtil.jFile(file)); - } - } - FileUtils.updateMovedGpxFiles(app, files, oldDir, newDir); + FileUtils.updateMovedTrackFolder(app, trackFolder, oldDir, newDir); dialogManager.askRefreshDialogCompletely(PROCESS_ID); @@ -234,6 +223,8 @@ private void showDeleteDialog() { @Override public void onFolderDeleted() { + FileUtils.updateAfterDeleteTrackFolder(app, trackFolder); + // Close options dialog after folder deleted dialogManager.askDismissDialog(PROCESS_ID); diff --git a/OsmAnd/src/net/osmand/plus/myplaces/tracks/dialogs/BaseTrackFolderFragment.java b/OsmAnd/src/net/osmand/plus/myplaces/tracks/dialogs/BaseTrackFolderFragment.java index 86ca7e61dfd..20dd7136fe7 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/tracks/dialogs/BaseTrackFolderFragment.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/tracks/dialogs/BaseTrackFolderFragment.java @@ -1,10 +1,8 @@ package net.osmand.plus.myplaces.tracks.dialogs; import static net.osmand.plus.configmap.tracks.PreselectedTabParams.CALLING_FRAGMENT_TAG; -import static net.osmand.plus.configmap.tracks.PreselectedTabParams.PRESELECTED_TRACKS_TAB_NAME; -import static net.osmand.plus.configmap.tracks.PreselectedTabParams.PRESELECTED_TRACKS_TAB_TYPE; +import static net.osmand.plus.configmap.tracks.PreselectedTabParams.PRESELECTED_TRACKS_TAB_ID; import static net.osmand.plus.configmap.tracks.PreselectedTabParams.SELECT_ALL_ITEMS_ON_TAB; -import static net.osmand.plus.configmap.tracks.TrackTab.SMART_FOLDER_TAB_NAME_PREFIX; import static net.osmand.plus.configmap.tracks.TrackTabType.FOLDER; import static net.osmand.plus.configmap.tracks.TrackTabType.SMART_FOLDER; import static net.osmand.plus.importfiles.ImportHelper.IMPORT_FILE_REQUEST; @@ -33,7 +31,7 @@ import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView.ViewHolder; -import net.osmand.IndexConstants; +import net.osmand.plus.configmap.tracks.TrackSortModesCollection; import net.osmand.plus.myplaces.tracks.DialogClosedListener; import net.osmand.plus.shared.SharedUtil; import net.osmand.data.LatLon; @@ -63,7 +61,6 @@ import net.osmand.plus.myplaces.tracks.dialogs.MoveGpxFileBottomSheet.OnTrackFileMoveListener; import net.osmand.plus.myplaces.tracks.dialogs.viewholders.TracksGroupViewHolder.TrackGroupsListener; import net.osmand.plus.plugins.osmedit.oauth.OsmOAuthHelper.OsmAuthorizationListener; -import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.settings.enums.TracksSortMode; import net.osmand.plus.track.fragments.TrackMenuFragment; import net.osmand.plus.track.helpers.GpxSelectionHelper; @@ -84,10 +81,7 @@ import java.io.File; import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; import java.util.Set; public abstract class BaseTrackFolderFragment extends BaseOsmAndFragment implements FragmentStateHolder, @@ -333,23 +327,14 @@ public void showSortByDialog() { @NonNull @Override public TracksSortMode getTracksSortMode() { - Map tabsSortModes = settings.getTrackSortModes(); - for (Entry entry : tabsSortModes.entrySet()) { - if (KAlgorithms.INSTANCE.stringsEqual(entry.getKey(), getSortEntryName())) { - return TracksSortMode.getByValue(entry.getValue()); - } - } - return TracksSortMode.getDefaultSortMode(); + TrackSortModesCollection sortModes = settings.getTrackSortModes(rootFolder); + return sortModes.requireSortMode(getSortEntryId()); } - protected String getSortEntryName() { - if (selectedFolder != null) { - return selectedFolder.getDirName(); - } - if (smartFolder != null) { - return SMART_FOLDER_TAB_NAME_PREFIX + smartFolder.getName(); - } - return null; + @Nullable + protected String getSortEntryId() { + TracksGroup folder = selectedFolder != null ? selectedFolder : smartFolder; + return folder != null ? folder.getId() : null; } @Override @@ -357,53 +342,28 @@ public void setTracksSortMode(@NonNull TracksSortMode sortMode, boolean sortSubF if (sortSubFolders) { sortSubFolder(sortMode); } else { - Map tabsSortModes = settings.getTrackSortModes(); - if (smartFolder != null) { - tabsSortModes.put(SMART_FOLDER_TAB_NAME_PREFIX + smartFolder.getFolderName(), sortMode.name()); - } else { - tabsSortModes.put(selectedFolder.getDirName(), sortMode.name()); - } - settings.saveTabsSortModes(tabsSortModes); - + TracksGroup folder = smartFolder != null ? smartFolder : selectedFolder; + TrackSortModesCollection sortModes = settings.getTrackSortModes(rootFolder); + sortModes.setSortMode(folder.getId(), sortMode); + sortModes.syncSettings(); updateContent(); } } - private void sortSubFolder(TracksSortMode sortMode) { - OsmandSettings settings = app.getSettings(); - Map tabsSortModes = settings.getTrackSortModes(); - sortFolders(selectedFolder, tabsSortModes, sortMode); - settings.saveTabsSortModes(tabsSortModes); + private void sortSubFolder(@NonNull TracksSortMode sortMode) { + TrackSortModesCollection sortModes = settings.getTrackSortModes(rootFolder); + sortFolders(selectedFolder, sortModes, sortMode); + sortModes.syncSettings(); app.showToastMessage(app.getString(R.string.sorted_sufolders_toast, selectedFolder.getName(), app.getString(sortMode.getNameId()))); } - private void sortFolders(TrackFolder trackFolder, Map tabsSortModes, TracksSortMode sortMode) { + private void sortFolders(@NonNull TrackFolder trackFolder, + @NonNull TrackSortModesCollection sortModes, + @NonNull TracksSortMode sortMode) { for (TrackFolder folder : trackFolder.getFlattenedSubFolders()) { - tabsSortModes.put(folder.getDirName(), sortMode.name()); - } - } - - private void removeSurplusTabsSortModes() { - if (!rootFolder.getDirFile().equals(app.getAppPathKt(IndexConstants.GPX_INDEX_DIR))) { - // Execute only from tracks root folder to not lose valid entries - return; - } - OsmandSettings settings = app.getSettings(); - Map oldTabsSortModes = settings.getTrackSortModes(); - Map tabsSortModes = new HashMap<>(); - - tabsSortModes.put(TrackTabType.ON_MAP.name(), oldTabsSortModes.get(TrackTabType.ON_MAP.name())); - tabsSortModes.put(TrackTabType.ALL.name(), oldTabsSortModes.get(TrackTabType.ALL.name())); - tabsSortModes.put(rootFolder.getDirName(), oldTabsSortModes.get(rootFolder.getDirName())); - for (TrackFolder folder : rootFolder.getFlattenedSubFolders()) { - tabsSortModes.put(folder.getDirName(), oldTabsSortModes.get(folder.getDirName())); - } - for (SmartFolder folder : app.getSmartFolderHelper().getSmartFolders()) { - String key = SMART_FOLDER_TAB_NAME_PREFIX + folder.getFolderName(); - tabsSortModes.put(key, oldTabsSortModes.get(key)); + sortModes.setSortMode(folder.getId(), sortMode); } - settings.saveTabsSortModes(tabsSortModes); } @Override @@ -517,31 +477,28 @@ public ScreenPositionData getFirstSuitableItemScreenPosition() { @Override public void onFolderRenamed(@NonNull File newDir) { updateContent(); - removeSurplusTabsSortModes(); } @Override public void onFolderDeleted() { reloadTracks(); - removeSurplusTabsSortModes(); } @Override public void showFolderTracksOnMap(@NonNull TrackFolder folder) { - showTracksVisibilityDialog(folder.getDirName(), FOLDER, true); + showTracksVisibilityDialog(folder.getId(), FOLDER, true); } @Override public void showSmartFolderTracksOnMap(@NonNull SmartFolder smartFolder) { - showTracksVisibilityDialog(smartFolder.getFolderName(), SMART_FOLDER, true); + showTracksVisibilityDialog(smartFolder.getId(), SMART_FOLDER, true); } - protected void showTracksVisibilityDialog(@NonNull String name, @NonNull TrackTabType type, boolean selectAll) { + protected void showTracksVisibilityDialog(@NonNull String id, @NonNull TrackTabType type, boolean selectAll) { FragmentActivity activity = getActivity(); if (activity != null) { Bundle bundle = new Bundle(); - bundle.putString(PRESELECTED_TRACKS_TAB_NAME, name); - bundle.putSerializable(PRESELECTED_TRACKS_TAB_TYPE, type); + bundle.putString(PRESELECTED_TRACKS_TAB_ID, id); bundle.putBoolean(SELECT_ALL_ITEMS_ON_TAB, selectAll); bundle.putString(CALLING_FRAGMENT_TAG, TAG); MapActivity.launchMapActivityMoveToTop(activity, storeState(), null, bundle); diff --git a/OsmAnd/src/net/osmand/plus/myplaces/tracks/tasks/MoveTrackFoldersTask.java b/OsmAnd/src/net/osmand/plus/myplaces/tracks/tasks/MoveTrackFoldersTask.java index 10ee98784a0..52626052551 100644 --- a/OsmAnd/src/net/osmand/plus/myplaces/tracks/tasks/MoveTrackFoldersTask.java +++ b/OsmAnd/src/net/osmand/plus/myplaces/tracks/tasks/MoveTrackFoldersTask.java @@ -15,10 +15,8 @@ import net.osmand.util.Algorithms; import java.io.File; -import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; -import java.util.List; import java.util.Set; public class MoveTrackFoldersTask extends BaseLoadAsyncTask { @@ -68,15 +66,7 @@ private void moveTrackFolder(@NonNull TrackFolder trackFolder) { File dest = new File(destinationFolder, src.name()); if (src.renameTo(dest.getAbsolutePath())) { dest.setLastModified(System.currentTimeMillis()); - - List files = new ArrayList<>(); - for (TrackItem trackItem : trackFolder.getFlattenedTrackItems()) { - KFile file = trackItem.getFile(); - if (file != null) { - files.add(SharedUtil.jFile(file)); - } - } - FileUtils.updateMovedGpxFiles(app, files, SharedUtil.jFile(src), dest); + FileUtils.updateMovedTrackFolder(app, trackFolder, SharedUtil.jFile(src), dest); } } } diff --git a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java index 812b7b47904..8dea33efc71 100644 --- a/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java +++ b/OsmAnd/src/net/osmand/plus/settings/backend/OsmandSettings.java @@ -65,6 +65,7 @@ import net.osmand.plus.charts.GPXDataSetAxisType; import net.osmand.plus.charts.GPXDataSetType; import net.osmand.plus.configmap.routes.MtbClassification; +import net.osmand.plus.configmap.tracks.TrackSortModesCollection; import net.osmand.plus.download.IndexItem; import net.osmand.plus.feedback.RateUsState; import net.osmand.plus.helpers.OsmandBackupAgent; @@ -96,6 +97,7 @@ import net.osmand.plus.wikipedia.WikiArticleShowImages; import net.osmand.render.RenderingRulesStorage; import net.osmand.shared.gpx.ColoringPurpose; +import net.osmand.shared.gpx.data.TrackFolder; import net.osmand.shared.obd.OBDDataComputer; import net.osmand.shared.routing.ColoringType; import net.osmand.shared.settings.enums.MetricsConstants; @@ -109,7 +111,6 @@ import java.io.File; import java.io.IOException; import java.util.*; -import java.util.Map.Entry; public class OsmandSettings { @@ -2054,37 +2055,20 @@ private String getPagedWidgetIds(@NonNull List pages) { public final CommonPreference SEARCH_TRACKS_SORT_MODE = new EnumStringPreference<>(this, "search_tracks_sort_mode", TracksSortMode.getDefaultSortMode(), TracksSortMode.values()); public final ListStringPreference TRACKS_TABS_SORT_MODES = (ListStringPreference) new ListStringPreference(this, "tracks_tabs_sort_modes", null, ";;").makeGlobal().makeShared().cache(); - @NonNull - public Map getTrackSortModes() { - return getTrackSortModes(TRACKS_TABS_SORT_MODES.getStringsList()); - } - - public void saveTabsSortModes(@NonNull Map tabsSortModes) { - List sortModes = getPlainSortModes(tabsSortModes); - TRACKS_TABS_SORT_MODES.setStringsList(sortModes); - } + private TrackSortModesCollection trackSortModes = null; @NonNull - private Map getTrackSortModes(@Nullable List modes) { - Map sortModes = new HashMap<>(); - if (!Algorithms.isEmpty(modes)) { - for (String sortMode : modes) { - String[] tabSortMode = sortMode.split(",,"); - if (tabSortMode.length == 2) { - sortModes.put(tabSortMode[0], tabSortMode[1]); - } - } - } - return sortModes; + public TrackSortModesCollection getTrackSortModes() { + return getTrackSortModes(null); } @NonNull - private List getPlainSortModes(@NonNull Map tabsSortModes) { - List sortTypes = new ArrayList<>(); - for (Entry entry : tabsSortModes.entrySet()) { - sortTypes.add(entry.getKey() + ",," + entry.getValue()); + public TrackSortModesCollection getTrackSortModes(@Nullable TrackFolder trackFolder) { + if (trackSortModes == null) { + trackSortModes = new TrackSortModesCollection(this); } - return sortTypes; + trackSortModes.askUpgradeKeysWithSync(ctx, trackFolder); + return trackSortModes; } public final OsmandPreference ANIMATE_MY_LOCATION = new BooleanPreference(this, "animate_my_location", true).makeProfile().cache(); diff --git a/OsmAnd/src/net/osmand/plus/track/BaseTracksTabsFragment.java b/OsmAnd/src/net/osmand/plus/track/BaseTracksTabsFragment.java index fa30aa525e9..15a5a5ae799 100644 --- a/OsmAnd/src/net/osmand/plus/track/BaseTracksTabsFragment.java +++ b/OsmAnd/src/net/osmand/plus/track/BaseTracksTabsFragment.java @@ -22,6 +22,7 @@ import androidx.fragment.app.FragmentManager; import androidx.viewpager.widget.ViewPager; +import net.osmand.plus.OsmandApplication; import net.osmand.plus.shared.SharedUtil; import net.osmand.plus.R; import net.osmand.plus.base.BaseOsmAndDialogFragment; @@ -61,7 +62,6 @@ import net.osmand.util.Algorithms; import java.io.File; -import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; @@ -108,20 +108,20 @@ public ItemsSelectionHelper getSelectionHelper() { public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); importHelper = app.getImportHelper(); - trackTabsHelper = new TrackTabsHelper(app); + trackTabsHelper = createTrackTabsHelper(app); gpxSelectionHelper = app.getSelectedGpxHelper(); itemsSelectionHelper = trackTabsHelper.getItemsSelectionHelper(); } protected void setupTabLayout(@NonNull View view) { viewPager = view.findViewById(R.id.view_pager); - List tabs = getTrackTabs(); + List tabs = getSortedTrackTabs(); tabLayout = view.findViewById(R.id.sliding_tabs); tabLayout.setTabBackground(nightMode ? R.color.app_bar_main_dark : R.color.card_and_list_background_light); tabLayout.setCustomTabProvider(new PagerSlidingTabStrip.CustomTabProvider() { @Override public View getCustomTabView(@NonNull ViewGroup parent, int position) { - TrackTab trackTab = getTrackTabs().get(position); + TrackTab trackTab = getSortedTrackTabs().get(position); int activeColor = ColorUtilities.getActiveColor(app, nightMode); int textColor = ColorUtilities.getPrimaryTextColor(app, nightMode); @@ -132,7 +132,7 @@ public View getCustomTabView(@NonNull ViewGroup parent, int position) { TextView textView = customView.findViewById(android.R.id.text1); textView.setPadding(sidePadding, textView.getPaddingTop(), sidePadding, textView.getPaddingBottom()); textView.setTextColor(AndroidUtils.createColorStateList(android.R.attr.state_selected, activeColor, textColor)); - textView.setText(trackTab.getName(app)); + textView.setText(trackTab.getName()); return customView; } @@ -154,27 +154,42 @@ public void tabStylesUpdated(View tabsContainer, int currentPosition) { setTabs(tabs); } + @NonNull + protected TrackTabsHelper createTrackTabsHelper(@NonNull OsmandApplication app) { + return new TrackTabsHelper(app); + } + @NonNull public List getTrackTabs() { - return new ArrayList<>(trackTabsHelper.getTrackTabs().values()); + return trackTabsHelper.getTrackTabs(); + } + + @NonNull + public List getSortedTrackTabs() { + return getSortedTrackTabs(false); + } + + @NonNull + public List getSortedTrackTabs(boolean useExtendedName) { + return trackTabsHelper.getSortedTrackTabs(useExtendedName); } protected void setViewPagerAdapter(@NonNull ViewPager pager, List items) { - adapter = new TracksTabAdapter(app, getChildFragmentManager(), items); + adapter = new TracksTabAdapter(getChildFragmentManager(), items); pager.setAdapter(adapter); } @Nullable public TrackTab getSelectedTab() { - List trackTabs = getTrackTabs(); + List trackTabs = getSortedTrackTabs(); return trackTabs.isEmpty() ? null : trackTabs.get(viewPager.getCurrentItem()); } - public void setSelectedTab(@NonNull String name) { - List trackTabs = getTrackTabs(); + public void setSelectedTab(@NonNull String id) { + List trackTabs = getSortedTrackTabs(); for (int i = 0; i < trackTabs.size(); i++) { TrackTab tab = trackTabs.get(i); - if (Algorithms.stringsEqual(tab.getTypeName(), name)) { + if (Algorithms.stringsEqual(tab.getId(), id)) { viewPager.setCurrentItem(i); break; } @@ -182,9 +197,9 @@ public void setSelectedTab(@NonNull String name) { } @Nullable - public TrackTab getTab(@NonNull String name) { + public TrackTab getTab(@NonNull String id) { for (TrackTab trackTab : getTrackTabs()) { - if (Algorithms.stringsEqual(name, trackTab.getTypeName())) { + if (Algorithms.stringsEqual(id, trackTab.getId())) { return trackTab; } } @@ -209,7 +224,7 @@ public TracksSortMode getTracksSortMode() { @Override public void onResume() { super.onResume(); - List tabs = getTrackTabs(); + List tabs = getSortedTrackTabs(); if (tabs.size() != tabSize) { setTabs(tabs); } @@ -238,7 +253,7 @@ public void loadTracksStarted() { } protected void updateTrackTabs() { - adapter.setTrackTabs(trackTabsHelper.getTrackTabs()); + adapter.setTrackTabs(trackTabsHelper.getSortedTrackTabs(false)); } @Override diff --git a/OsmAnd/src/net/osmand/plus/track/SelectTrackFolderFragment.java b/OsmAnd/src/net/osmand/plus/track/SelectTrackFolderFragment.java index 3d562f2dc9e..52e6b57cd47 100644 --- a/OsmAnd/src/net/osmand/plus/track/SelectTrackFolderFragment.java +++ b/OsmAnd/src/net/osmand/plus/track/SelectTrackFolderFragment.java @@ -181,7 +181,7 @@ private void updateAdapter() { } private TrackTab getUpdatedTrackTab() { - TrackTab trackTab = new TrackTab(TrackTabType.FOLDERS); + TrackTab trackTab = new TrackTab(app, TrackTabType.FOLDERS); List subFolders = currentTrackFolder.getSubFolders(); List trackItems = currentTrackFolder.getTrackItems(); if (Algorithms.isEmpty(subFolders) && Algorithms.isEmpty(trackItems)) { diff --git a/OsmAnd/src/net/osmand/plus/track/SelectTrackTabsFragment.java b/OsmAnd/src/net/osmand/plus/track/SelectTrackTabsFragment.java index 1108cacaee3..c956cac4345 100644 --- a/OsmAnd/src/net/osmand/plus/track/SelectTrackTabsFragment.java +++ b/OsmAnd/src/net/osmand/plus/track/SelectTrackTabsFragment.java @@ -19,6 +19,10 @@ import androidx.fragment.app.FragmentManager; import net.osmand.CallbackWithObject; +import net.osmand.IndexConstants; +import net.osmand.plus.OsmandApplication; +import net.osmand.plus.configmap.tracks.SelectTrackTabsHelper; +import net.osmand.plus.configmap.tracks.TrackTabsHelper; import net.osmand.plus.shared.SharedUtil; import net.osmand.shared.gpx.GpxFile; import net.osmand.plus.R; @@ -94,6 +98,12 @@ protected void setTabs(@NonNull List tabs) { viewPager.setCurrentItem(0); } + @NonNull + @Override + protected TrackTabsHelper createTrackTabsHelper(@NonNull OsmandApplication app) { + return new SelectTrackTabsHelper(app); + } + @Override public void loadTracksProgress(@NonNull TrackItem... items) { } @@ -104,7 +114,7 @@ public void tracksLoaded(@NonNull TrackFolder folder) { @Override public void loadTracksFinished(@NonNull TrackFolder folder) { - trackTabsHelper.updateItems(folder); + trackTabsHelper.updateTrackItems(folder); AndroidUiHelper.updateVisibility(progressBar, false); updateTrackTabs(); updateTabsContent(); @@ -158,9 +168,10 @@ public void onTrackItemsSelected(@NonNull Set trackItems, boolean sel } @Nullable - public TrackTab getTab(@NonNull String name) { + @Override + public TrackTab getTab(@NonNull String id) { for (TrackTab trackTab : getTrackTabs()) { - if (Algorithms.stringsEqual(name, trackTab.getTypeName())) { + if (Algorithms.stringsEqual(id, trackTab.getId())) { updateTrackItemsVisibility(trackTab); return trackTab; } diff --git a/OsmAnd/src/net/osmand/plus/track/helpers/GpxUiHelper.java b/OsmAnd/src/net/osmand/plus/track/helpers/GpxUiHelper.java index 4cacb0fc1a2..fbe27285add 100644 --- a/OsmAnd/src/net/osmand/plus/track/helpers/GpxUiHelper.java +++ b/OsmAnd/src/net/osmand/plus/track/helpers/GpxUiHelper.java @@ -67,6 +67,7 @@ import net.osmand.shared.gpx.primitives.WptPt; import net.osmand.shared.io.KFile; import net.osmand.util.Algorithms; +import net.osmand.util.CollectionUtils; import net.osmand.util.MapUtils; import org.apache.commons.logging.Log; @@ -189,26 +190,29 @@ public static void selectSingleGPXFile(FragmentActivity activity, boolean showCu } @NonNull - public static String getFolderName(@NonNull Context context, @NonNull File dir, boolean includeParentDir) { - String name = dir.getName(); + public static String getFolderName(@NonNull Context context, @NonNull File directory) { + String name = directory.getName(); if (GPX_INDEX_DIR.equals(name + File.separator)) { return context.getString(R.string.shared_string_tracks); } - String dirPath = dir.getPath() + File.separator; + String dirPath = directory.getPath() + File.separator; if (dirPath.endsWith(GPX_IMPORT_DIR) || dirPath.endsWith(GPX_RECORDED_INDEX_DIR)) { return Algorithms.capitalizeFirstLetter(name); } - if (includeParentDir) { - File parent = dir.getParentFile(); - String parentName = parent != null ? parent.getName() : ""; - if (!Algorithms.isEmpty(parentName) && !GPX_INDEX_DIR.equals(parentName + File.separator)) { - name = parentName + File.separator + name; - } - return name; - } return name; } + @NonNull + public static String getExtendedFolderName(@NonNull File directory, @NonNull String initialName) { + String name = directory.getName() + File.separator; + File parent = directory.getParentFile(); + String parentName = parent != null ? parent.getName() + File.separator : ""; + if (!CollectionUtils.equalsToAny(GPX_INDEX_DIR, name, parentName)) { + return parentName + initialName; + } + return initialName; + } + @NonNull public static String getFolderDescription(@NonNull OsmandApplication app, @NonNull TrackFolder folder) { long lastModified = folder.getLastModified(); diff --git a/OsmAnd/src/net/osmand/plus/utils/FileUtils.java b/OsmAnd/src/net/osmand/plus/utils/FileUtils.java index 12d5553aa48..683262b8f94 100644 --- a/OsmAnd/src/net/osmand/plus/utils/FileUtils.java +++ b/OsmAnd/src/net/osmand/plus/utils/FileUtils.java @@ -12,6 +12,8 @@ import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentManager; +import net.osmand.plus.configmap.tracks.TrackSortModesCollection; +import net.osmand.plus.settings.backend.OsmandSettings; import net.osmand.plus.shared.SharedUtil; import net.osmand.plus.OsmandApplication; import net.osmand.plus.R; @@ -24,7 +26,10 @@ import net.osmand.plus.track.helpers.SelectedGpxFile; import net.osmand.plus.track.helpers.save.SaveGpxHelper; import net.osmand.shared.gpx.GpxFile; +import net.osmand.shared.gpx.TrackItem; +import net.osmand.shared.gpx.data.TrackFolder; import net.osmand.shared.gpx.primitives.Metadata; +import net.osmand.shared.io.KFile; import net.osmand.util.Algorithms; import net.osmand.util.CollectionUtils; @@ -174,8 +179,30 @@ private static void updateGpxMetadata(@NonNull GpxFile gpxFile, @NonNull String } } - public static void updateMovedGpxFiles(@NonNull OsmandApplication app, @NonNull List files, - @NonNull File srcDir, @NonNull File destDir) { + public static void updateMovedTrackFolder(@NonNull OsmandApplication app, @NonNull TrackFolder trackFolder, + @NonNull File srcDir, @NonNull File destDir) { + List files = new ArrayList<>(); + for (TrackItem trackItem : trackFolder.getFlattenedTrackItems()) { + KFile file = trackItem.getFile(); + if (file != null) { + files.add(SharedUtil.jFile(file)); + } + } + updateMovedGpxFiles(app, files, srcDir, destDir); + + OsmandSettings settings = app.getSettings(); + TrackSortModesCollection sortModes = settings.getTrackSortModes(trackFolder); + sortModes.updateAfterMoveTrackFolder(trackFolder, srcDir); + } + + public static void updateAfterDeleteTrackFolder(@NonNull OsmandApplication app, @NonNull TrackFolder trackFolder) { + OsmandSettings settings = app.getSettings(); + TrackSortModesCollection sortModes = settings.getTrackSortModes(trackFolder); + sortModes.updateAfterDeleteTrackFolder(trackFolder); + } + + private static void updateMovedGpxFiles(@NonNull OsmandApplication app, @NonNull List files, + @NonNull File srcDir, @NonNull File destDir) { for (File srcFile : files) { String path = srcFile.getAbsolutePath(); String newPath = path.replace(srcDir.getAbsolutePath(), destDir.getAbsolutePath());