Skip to content

Appearance for selected tracks #4389

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions OsmAnd.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1361,6 +1361,9 @@
C573544F2AAB5144008B3214 /* OAMapSettingsTerrainParametersViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = C573544D2AAB5144008B3214 /* OAMapSettingsTerrainParametersViewController.mm */; };
C57354502AAB5144008B3214 /* OAMapSettingsTerrainParametersViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = C573544E2AAB5144008B3214 /* OAMapSettingsTerrainParametersViewController.xib */; };
C5735AF62AFE9CB500638715 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = C5735AF42AFE9CB500638715 /* InfoPlist.strings */; };
C575798C2D64A0A30016957D /* TracksChangeAppearanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C575798B2D64A0A30016957D /* TracksChangeAppearanceViewController.swift */; };
C57693172D66159200E4F772 /* AppearanceData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C57693162D66159200E4F772 /* AppearanceData.swift */; };
C57693192D67148400E4F772 /* ChangeTracksAppearanceTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = C57693182D67148400E4F772 /* ChangeTracksAppearanceTask.swift */; };
C57E26F32D3D663F00A8A3D5 /* AttachRoadsBannerCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C57E26F12D3D663F00A8A3D5 /* AttachRoadsBannerCell.swift */; };
C57E26F42D3D663F00A8A3D5 /* AttachRoadsBannerCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = C57E26F22D3D663F00A8A3D5 /* AttachRoadsBannerCell.xib */; };
C5879A49299FB96300F53249 /* uk_tts.js in Resources */ = {isa = PBXBuildFile; fileRef = C5879A48299FB96300F53249 /* uk_tts.js */; };
Expand Down Expand Up @@ -4991,6 +4994,9 @@
C5735B452AFE9D8700638715 /* uz-Cyrl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "uz-Cyrl"; path = "Resources/Localizations/uz-Cyrl.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
C5735B462AFE9D8800638715 /* vi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = vi; path = Resources/Localizations/vi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C5735B472AFE9D8900638715 /* cy */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cy; path = Resources/Localizations/cy.lproj/InfoPlist.strings; sourceTree = "<group>"; };
C575798B2D64A0A30016957D /* TracksChangeAppearanceViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TracksChangeAppearanceViewController.swift; sourceTree = "<group>"; };
C57693162D66159200E4F772 /* AppearanceData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppearanceData.swift; sourceTree = "<group>"; };
C57693182D67148400E4F772 /* ChangeTracksAppearanceTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChangeTracksAppearanceTask.swift; sourceTree = "<group>"; };
C57E26F12D3D663F00A8A3D5 /* AttachRoadsBannerCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachRoadsBannerCell.swift; sourceTree = "<group>"; };
C57E26F22D3D663F00A8A3D5 /* AttachRoadsBannerCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AttachRoadsBannerCell.xib; sourceTree = "<group>"; };
C5879A48299FB96300F53249 /* uk_tts.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = uk_tts.js; path = ../resources/voice/uk/uk_tts.js; sourceTree = "<group>"; };
Expand Down Expand Up @@ -11973,6 +11979,9 @@
C50E32832CA57DDB00EEC41F /* TracksFilterDetailsViewController.swift */,
C5A887E32CAD7B8E00DA2979 /* TracksSearchFilter.swift */,
C57245D82CDF3B70004F1F19 /* TracksSortModeHelper.swift */,
C575798B2D64A0A30016957D /* TracksChangeAppearanceViewController.swift */,
C57693162D66159200E4F772 /* AppearanceData.swift */,
C57693182D67148400E4F772 /* ChangeTracksAppearanceTask.swift */,
);
path = MyPlaces;
sourceTree = "<group>";
Expand Down Expand Up @@ -16590,6 +16599,7 @@
DA842ECD2A0B998E0030866E /* WidgetInfoCreator.swift in Sources */,
460E9AC12B71111000411854 /* CloudTrashItemMenuViewController.swift in Sources */,
323F04C82C6577340058DD78 /* ShowHideWhitewaterSportsAction.swift in Sources */,
C57693172D66159200E4F772 /* AppearanceData.swift in Sources */,
32388EE226E6AF770021F829 /* OAQuickSearchResultTableViewCell.m in Sources */,
DA5F77B628742E85000A2BFF /* OABackupStatus.m in Sources */,
DAABE4EE2A1657AF00569F71 /* Color+INT.swift in Sources */,
Expand Down Expand Up @@ -16969,6 +16979,7 @@
DA5A845226C563A900F274C7 /* OAMapLayers.mm in Sources */,
DA4ABEEB2876DA5D00B996EF /* OASettingsCategoryItems.m in Sources */,
FA4195592AAB55E6007CBBAB /* ShadowTransporentView.swift in Sources */,
C57693192D67148400E4F772 /* ChangeTracksAppearanceTask.swift in Sources */,
DA5A81DE26C563A700F274C7 /* OAStreet.mm in Sources */,
325CFBEB2B5052E000090DF2 /* TracksViewController.swift in Sources */,
32DE2BE42B6D13730025F2B9 /* OATwoButtonsTableViewCell.swift in Sources */,
Expand Down Expand Up @@ -17140,6 +17151,7 @@
DA5A845026C563A900F274C7 /* OAPOILayer.mm in Sources */,
DA2EC47B29FBB90A00ECEB37 /* WidgetType.swift in Sources */,
4631377929AE6D8A000B0258 /* OACarPlayHistoryListController.mm in Sources */,
C575798C2D64A0A30016957D /* TracksChangeAppearanceViewController.swift in Sources */,
DA5A834026C563A800F274C7 /* OAFolderCardCollectionViewCell.m in Sources */,
DA5A849226C563A900F274C7 /* OAMeasurementEditingContext.mm in Sources */,
DA5A856226C563A900F274C7 /* OAWaypointHelper.mm in Sources */,
Expand Down
4 changes: 3 additions & 1 deletion Resources/Localizations/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -2363,9 +2363,11 @@
"shared_string_open" = "Open";
"time_closed" = "Closed";
"time_will_open" = "Will open in";

"shared_string_unchanged" = "Unchanged";
"osm_live_active" = "Active";
"menu_all_trips" = "All Tracks";
"each_favourite_point_own_icon" = "Each favourite point would keep its own icon.";
"unchanged_parameter_summary" = "Each track will retain its own parameters.";

"public_transport_ped_route_title" = "The route on foot is approximately %@, and may be faster than public transport";
"calc_pedestrian_route" = "Calculate pedestrian route";
Expand Down
75 changes: 75 additions & 0 deletions Sources/Controllers/MyPlaces/AppearanceData.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//
// AppearanceData.swift
// OsmAnd Maps
//
// Created by Dmitry Svetlichny on 19.02.2025.
// Copyright © 2025 OsmAnd. All rights reserved.
//

import UIKit

protocol AppearanceChangedDelegate: AnyObject {
func onAppearanceChanged()
}

struct AppearancePair {
let shouldReset: Bool
let value: Any?
}

final class AppearanceData: NSObject {
private var map: [GpxParameter: AppearancePair] = [:]
weak var delegate: AppearanceChangedDelegate?

override init() {
super.init()
}

init(data: AppearanceData) {
self.map = data.map
super.init()
}

private func notifyAppearanceModified() {
delegate?.onAppearanceChanged()
}

private func isValidValue(parameter: GpxParameter, value: Any?) -> Bool {
guard parameter.isAppearanceParameter() else { return false }
return true
}

func getParameter<T>(for parameter: GpxParameter) -> T? {
guard let pair = map[parameter] else { return nil }
return pair.value as? T
}

func setParameter(_ parameter: GpxParameter, value: Any?) {
if isValidValue(parameter: parameter, value: value) {
map[parameter] = AppearancePair(shouldReset: false, value: value)
notifyAppearanceModified()
}
}

func resetParameter(_ parameter: GpxParameter) {
guard parameter.isAppearanceParameter() else { return }
map[parameter] = AppearancePair(shouldReset: true, value: nil)
notifyAppearanceModified()
}

func shouldResetParameter(_ parameter: GpxParameter) -> Bool {
guard parameter.isAppearanceParameter() else { return false }
return map[parameter]?.shouldReset ?? false
}

func shouldResetAnything() -> Bool {
let params = GpxParameter.values()
for i in 0..<params.size {
if let param = params.get(index: i), shouldResetParameter(param) {
return true
}
}

return false
}
}
112 changes: 112 additions & 0 deletions Sources/Controllers/MyPlaces/ChangeTracksAppearanceTask.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//
// ChangeTracksAppearanceTask.swift
// OsmAnd Maps
//
// Created by Dmitry Svetlichny on 20.02.2025.
// Copyright © 2025 OsmAnd. All rights reserved.
//

import UIKit

final class ChangeTracksAppearanceTask: NSObject {
private let gpxDbHelper: GpxDbHelper?
private let data: AppearanceData
private let items: Set<TrackItem>
private let callback: (() -> Void)?

init(data: AppearanceData, items: Set<TrackItem>, callback: (() -> Void)? = nil) {
self.data = data
self.items = items
self.callback = callback
gpxDbHelper = GpxDbHelper.shared
}

private func doInBackground() {
let resetAnything = data.shouldResetAnything()
for track in items {
if let file = track.getFile() {
let gpxFile = resetAnything ? getGpxFile(for: file) : nil
updateTrackAppearance(file: file, gpxFile: gpxFile)
} else if track.isShowCurrentTrack {
updateCurrentTrackAppearance()
}
}
}

private func updateTrackAppearance(file: KFile, gpxFile: GpxFile?) {
let callback = getGpxDataItemCallback(gpxFile: gpxFile)
if let dataItem = gpxDbHelper?.getItem(file: file, callback: callback) {
updateTrackAppearance(item: dataItem, gpxFile: gpxFile)
}
}

private func getGpxFile(for file: KFile) -> GpxFile? {
let gpxFile = GpxUtilities.shared.loadGpxFile(file: file)
return gpxFile.error == nil ? gpxFile : nil
}

private func updateCurrentTrackAppearance() {
let settings = OAAppSettings.sharedManager()
if let color: Int = data.getParameter(for: GpxParameter.color) {
settings?.currentTrackColor.set(Int32(color))
}
if let coloringType: String = data.getParameter(for: GpxParameter.coloringType) {
let requiredValue = ColoringType.companion.requireValueOf(purpose: ColoringPurpose.track, name: coloringType)
settings?.currentTrackColoringType.set(Int32(requiredValue.ordinal))
let routeInfoAttribute = ColoringType.companion.getRouteInfoAttribute(name: coloringType)
settings?.routeInfoAttribute.set(routeInfoAttribute)
}
if let width: String = data.getParameter(for: GpxParameter.width) {
settings?.currentTrackWidth.set(width)
}
if let showArrows: Bool = data.getParameter(for: GpxParameter.showArrows) {
settings?.currentTrackShowArrows.set(showArrows)
}
if let showStartFinish: Bool = data.getParameter(for: GpxParameter.showStartFinish) {
settings?.currentTrackShowStartFinish.set(showStartFinish)
}
if let trackVisualizationType: String = data.getParameter(for: GpxParameter.trackVisualizationType),
let intValue = Int32(trackVisualizationType) {
settings?.currentTrackVisualization3dByType.set(intValue)
}
if let gradientPalette: String = data.getParameter(for: GpxParameter.colorPalette) {
settings?.gradientPalettes.set(gradientPalette)
}
}

private func getGpxDataItemCallback(gpxFile: GpxFile?) -> GpxDbHelperGpxDataItemCallback {
let handler = GpxDataItemHandler()
handler.onGpxDataItemReady = { [weak self] item in
self?.updateTrackAppearance(item: item, gpxFile: gpxFile)
}

return handler
}

private func updateTrackAppearance(item: GpxDataItem, gpxFile: GpxFile?) {
for parameter in GpxParameter.companion.getAppearanceParameters() {
if data.shouldResetParameter(parameter) {
if let gpxFile = gpxFile {
item.readGpxAppearanceParameter(gpxFile: gpxFile, parameter: parameter)
}
} else if let value: Any = data.getParameter(for: parameter) {
item.setParameter(parameter: parameter, value: value)
}
}

gpxDbHelper?.updateDataItem(item: item)
}

private func onPostExecute() {
callback?()
}

func execute() {
DispatchQueue.global(qos: .default).async {
self.doInBackground()
DispatchQueue.main.async {
self.onPostExecute()
}
}
}
}
Loading