From 94f5eaa401c5858ef477c82fc7c113b488a7bfac Mon Sep 17 00:00:00 2001 From: vshcherb Date: Wed, 15 Jan 2025 16:21:46 +0200 Subject: [PATCH 1/3] 1)Add bundled.json 2) Fix progress bars 3) fix color palettes --- OsmAnd.xcodeproj/project.pbxproj | 8 + Sources/AppHost/OsmAndAppImpl.mm | 124 +---------- Sources/Backup/BackupUtils.swift | 66 +++--- .../Backup/LocalBackup/OASettingsHelper.mm | 19 +- Sources/Backup/OABackupExporter.m | 2 +- Sources/Backup/OABackupHelper.mm | 21 +- Sources/Backup/OABackupImporter.m | 14 +- Sources/Backup/OABackupListeners.h | 2 +- Sources/Backup/OAImportBackupTask.m | 2 +- Sources/Constants/OAIndexConstants.h | 1 - Sources/Data/OABundledAsetts.swift | 205 ++++++++++++++++++ Sources/Helpers/OAOperationLog.m | 2 +- 12 files changed, 293 insertions(+), 173 deletions(-) create mode 100644 Sources/Data/OABundledAsetts.swift diff --git a/OsmAnd.xcodeproj/project.pbxproj b/OsmAnd.xcodeproj/project.pbxproj index 4e98c8d6c3..6b057c0e71 100644 --- a/OsmAnd.xcodeproj/project.pbxproj +++ b/OsmAnd.xcodeproj/project.pbxproj @@ -828,6 +828,8 @@ 78AC698C23AB7BE1006C3DDE /* left_menu_configure_screen@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 78AC698823AB7BDF006C3DDE /* left_menu_configure_screen@3x.png */; }; 78AC698E23AB7BE1006C3DDE /* left_menu_configure_screen@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 78AC698923AB7BE0006C3DDE /* left_menu_configure_screen@2x.png */; }; 81AE516126298A3600BDF947 /* libGeographicLib_STATIC.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AE516026298A3600BDF947 /* libGeographicLib_STATIC.a */; }; + 81E0C9432D37DE5B00BC7D85 /* bundled_assets.json in Resources */ = {isa = PBXBuildFile; fileRef = 81E0C9422D37DE5A00BC7D85 /* bundled_assets.json */; }; + 81E0C9462D37DFCD00BC7D85 /* OABundledAsetts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 81E0C9452D37DFC700BC7D85 /* OABundledAsetts.swift */; }; 840C9FB61CF353C500F2F991 /* ic_coordinates_location@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84BA1DCF1CF338E400BF3D96 /* ic_coordinates_location@2x.png */; }; 840C9FB81CF353C800F2F991 /* ic_coordinates_location@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 84BA1DD01CF338E400BF3D96 /* ic_coordinates_location@3x.png */; }; 8410D068207F48200049DEB2 /* ic_tree_list_dark@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8410D062207F481E0049DEB2 /* ic_tree_list_dark@3x.png */; }; @@ -4248,6 +4250,8 @@ 78AC698823AB7BDF006C3DDE /* left_menu_configure_screen@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "left_menu_configure_screen@3x.png"; path = "Resources/Icons/left_menu_configure_screen@3x.png"; sourceTree = ""; }; 78AC698923AB7BE0006C3DDE /* left_menu_configure_screen@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "left_menu_configure_screen@2x.png"; path = "Resources/Icons/left_menu_configure_screen@2x.png"; sourceTree = ""; }; 81AE516026298A3600BDF947 /* libGeographicLib_STATIC.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libGeographicLib_STATIC.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 81E0C9422D37DE5A00BC7D85 /* bundled_assets.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; name = bundled_assets.json; path = ../resources/bundled_assets.json; sourceTree = ""; }; + 81E0C9452D37DFC700BC7D85 /* OABundledAsetts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OABundledAsetts.swift; sourceTree = ""; }; 8401FCF61EE46B7F00238915 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/Localizable.strings; sourceTree = ""; }; 8401FCF71EE46C2600238915 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; 8401FCF81EE46C3100238915 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; @@ -9913,6 +9917,7 @@ C51BE6FC2BBC3E7C00B63A1A /* PrivacyInfo.xcprivacy */, C5735AF42AFE9CB500638715 /* InfoPlist.strings */, BB451DF318D8F7D7005FBE09 /* Localizable.strings */, + 81E0C9422D37DE5A00BC7D85 /* bundled_assets.json */, ); name = Resources; sourceTree = ""; @@ -12874,6 +12879,7 @@ DA5A802F26C563A500F274C7 /* Data */ = { isa = PBXGroup; children = ( + 81E0C9452D37DFC700BC7D85 /* OABundledAsetts.swift */, DA5A803226C563A500F274C7 /* Coordinates */, DA5A803726C563A500F274C7 /* OAAppData.h */, DA5A803E26C563A500F274C7 /* OAAppData.m */, @@ -14115,6 +14121,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 81E0C9432D37DE5B00BC7D85 /* bundled_assets.json in Resources */, 32309A4426989180005751F5 /* ic_custom_map_zoom_in@3x.png in Resources */, 32B50ADF26BBE3C7002E6C92 /* ic_custom_map_location_follow@3x.png in Resources */, 84F3A9631B4ECF2700D16EBC /* ic_trip_visitedpoint@3x.png in Resources */, @@ -17155,6 +17162,7 @@ 46548DAA2BF7794F00B0D3C0 /* DefaultMapButtonsViewController.swift in Sources */, DA5A847B26C563A900F274C7 /* OATurnDrawable.mm in Sources */, FAF6C89F2AD014C900A3ED94 /* ThemeManager.swift in Sources */, + 81E0C9462D37DFCD00BC7D85 /* OABundledAsetts.swift in Sources */, DAD5B4CF288E80EC009CF611 /* OAOperationLog.m in Sources */, 46C8412D2C32F23A00E284B0 /* OAShowHideAirPressureAction.m in Sources */, DA5A853826C563A900F274C7 /* OAFavoritesHelper.mm in Sources */, diff --git a/Sources/AppHost/OsmAndAppImpl.mm b/Sources/AppHost/OsmAndAppImpl.mm index aef275565b..f27ee1a1c2 100644 --- a/Sources/AppHost/OsmAndAppImpl.mm +++ b/Sources/AppHost/OsmAndAppImpl.mm @@ -6,6 +6,7 @@ // Copyright (c) 2014 OsmAnd. All rights reserved. // +#import "OsmAnd_Maps-Swift.h" #import "OsmAndAppImpl.h" #import #import "OsmAndApp.h" @@ -297,27 +298,6 @@ - (void) runOpeningHoursParserTests OpeningHoursParser::runTestAmPmArabic(); } -- (void) migrateResourcesToDocumentsIfNeeded -{ - BOOL movedRes = [self moveContentsOfDirectory:[_dataPath stringByAppendingPathComponent:RESOURCES_DIR] - toDest:[_documentsPath stringByAppendingPathComponent:RESOURCES_DIR] - removeOriginalFile:YES]; - BOOL movedSqlite = [self moveContentsOfDirectory:[_dataPath stringByAppendingPathComponent:MAP_CREATOR_DIR] - toDest:[_documentsPath stringByAppendingPathComponent:MAP_CREATOR_DIR] - removeOriginalFile:YES]; - if (movedRes) - [self migrateMapNames:[_documentsPath stringByAppendingPathComponent:RESOURCES_DIR]]; - if (movedRes || movedSqlite) - _resourcesManager->rescanUnmanagedStoragePaths(true); - - [self moveContentsOfDirectory:[[NSBundle mainBundle] pathForResource:CLR_PALETTE_DIR ofType:nil] - toDest:_colorsPalettePath - removeOriginalFile:NO]; - [self moveContentsOfDirectory:[[NSBundle mainBundle].resourcePath stringByAppendingPathComponent:MODEL_3D_DIR] - toDest:[_documentsPath stringByAppendingPathComponent:MODEL_3D_DIR] - removeOriginalFile:NO]; -} - - (BOOL) initializeCore { @synchronized (self) @@ -585,7 +565,11 @@ - (BOOL) initializeImpl if (_terminating) return NO; - [self migrateResourcesToDocumentsIfNeeded]; + BOOL rescan = [BundledAssets.shared migrateResourcesToDocumentsIfNeededWithDataPath:_dataPath documentsPath:_documentsPath + versionChanged: currentVersion != prevVersion || _firstLaunch]; + if (rescan) { + _resourcesManager->rescanUnmanagedStoragePaths(true); + } // Copy regions.ocbf to Documents/Resources if needed NSString *ocbfPathBundle = [[NSBundle mainBundle] pathForResource:@"regions" ofType:@"ocbf"]; @@ -745,102 +729,6 @@ - (BOOL) initializeImpl return YES; } -- (BOOL) moveContentsOfDirectory:(NSString *)src - toDest:(NSString *)dest - removeOriginalFile:(BOOL)remove -{ - NSFileManager *fm = [NSFileManager defaultManager]; - if (![fm fileExistsAtPath:src]) - return NO; - if (![fm fileExistsAtPath:dest]) - [fm createDirectoryAtPath:dest withIntermediateDirectories:YES attributes:nil error:nil]; - - NSArray *files = [fm contentsOfDirectoryAtPath:src error:nil]; - BOOL tryAgain = NO; - for (NSString *file in files) - { - if ([fm fileExistsAtPath:[dest stringByAppendingPathComponent:file]]) - continue; - NSError *err = nil; - if (remove) - { - [fm moveItemAtPath:[src stringByAppendingPathComponent:file] - toPath:[dest stringByAppendingPathComponent:file] - error:&err]; - } - else - { - [fm copyItemAtPath:[src stringByAppendingPathComponent:file] - toPath:[dest stringByAppendingPathComponent:file] - error:&err]; - } - if (err) - tryAgain = YES; - } - if (remove && !tryAgain) - [fm removeItemAtPath:src error:nil]; - return YES; -} - -- (void) migrateMapNames:(NSString *)path -{ - NSFileManager *fm = [NSFileManager defaultManager]; - BOOL isDirectory = NO; - [fm fileExistsAtPath:path isDirectory:&isDirectory]; - if (!isDirectory) - return; - - NSArray *files = [fm contentsOfDirectoryAtPath:path error:nil]; - - for (NSString *file in files) - { - NSString *oldPath = [path stringByAppendingPathComponent:file]; - BOOL isDir = NO; - [fm fileExistsAtPath:oldPath isDirectory:&isDir]; - if (isDir) - [self migrateMapNames:oldPath]; - else - { - NSString *newPath = [path stringByAppendingPathComponent:[self generateCorrectFileName:file]]; - if (![newPath isEqualToString:oldPath]) - { - [fm moveItemAtPath:oldPath - toPath:newPath - error:nil]; - } - } - } -} - -- (NSString *) generateCorrectFileName:(NSString *)path -{ - NSString *fileName = path.lastPathComponent; - if ([fileName hasSuffix:@".map.obf"]) - { - fileName = [OAUtilities capitalizeFirstLetter:[fileName stringByReplacingOccurrencesOfString:@".map.obf" withString:@".obf"]]; - } - else if ([fileName.pathExtension isEqualToString:@"obf"]) - { - fileName = [OAUtilities capitalizeFirstLetter:fileName]; - } - else if ([fileName.pathExtension isEqualToString:@"sqlitedb"]) - { - if ([fileName hasSuffix:@".hillshade.sqlitedb"]) - { - fileName = [fileName stringByReplacingOccurrencesOfString:@".hillshade.sqlitedb" withString:@".sqlitedb"]; - fileName = [fileName stringByReplacingOccurrencesOfString:@"_" withString:@" "]; - fileName = [NSString stringWithFormat:@"Hillshade %@", [OAUtilities capitalizeFirstLetter:fileName]]; - } - else if ([fileName hasSuffix:@".slope.sqlitedb"]) - { - fileName = [fileName stringByReplacingOccurrencesOfString:@".slope.sqlitedb" withString:@".sqlitedb"]; - fileName = [fileName stringByReplacingOccurrencesOfString:@"_" withString:@" "]; - fileName = [NSString stringWithFormat:@"Slope %@", [OAUtilities capitalizeFirstLetter:fileName]]; - } - } - return [path.stringByDeletingLastPathComponent stringByAppendingPathComponent:fileName]; -} - - (NSString *) generateIndexesUrl { NSMutableString *res = [NSMutableString stringWithFormat:@"https://download.osmand.net/get_indexes?gzip&osmandver=%@", OAAppVersion.getVersionForUrl]; diff --git a/Sources/Backup/BackupUtils.swift b/Sources/Backup/BackupUtils.swift index bfaf5aa651..68cce330ff 100644 --- a/Sources/Backup/BackupUtils.swift +++ b/Sources/Backup/BackupUtils.swift @@ -63,23 +63,44 @@ final class BackupUtils: NSObject { infoFiles: Bool) -> [OARemoteFile: OASettingsItem] { var res = [OARemoteFile: OASettingsItem]() var files = remoteFiles + var settingsItemMap = [String: OASettingsItem]() + var subtypeFolders = [OAFileSettingsItem]() + let DELIMETER = "___" for item in items { - var processedFiles = [OARemoteFile]() - for file in files { - var name = file.name as NSString - if infoFiles && name.pathExtension == OABackupHelper.info_EXT() { - name = name.deletingPathExtension as NSString + let itemFileName = getItemFileName(item) + settingsItemMap[(OASettingsItemType.typeName(item.type) ?? "") + DELIMETER + itemFileName] = item + if let fileItem = item as? OAFileSettingsItem { + let subtypeFolder = OAFileSettingsItemFileSubtype.getFolderName(fileItem.subtype) + var isDir: ObjCBool = false + FileManager.default.fileExists(atPath: fileItem.filePath, isDirectory: &isDir) + if !subtypeFolder.isEmpty && isDir.boolValue { + subtypeFolders.append(fileItem) } - - if applyItem(item, type: file.type, name: name) { - if file.item == nil { - file.item = item + } + } + for file in files { + var name = file.name as NSString + if infoFiles && name.pathExtension == OABackupHelper.info_EXT() { + name = name.deletingPathExtension as NSString + } + + if let item = settingsItemMap[file.type + DELIMETER + (name as String)] { + res[file] = item + } else { + for fileItem in subtypeFolders { + let itemFileName = getItemFileName(fileItem) + var found = false + if !itemFileName.hasSuffix("/") { + found = name.hasPrefix(itemFileName + "/") + } else { + found = name.hasPrefix(itemFileName) + } + if found { + res[file] = fileItem + break } - res[file] = item - processedFiles.append(file) } } - files.removeAll { processedFiles.contains($0) } } return res } @@ -92,27 +113,6 @@ final class BackupUtils: NSObject { OACommonBoolean.withKey("\(versionHistoryPrefix)\(type.name)", defValue: true).makeGlobal().makeShared() } - static func applyItem(_ item: OASettingsItem, type: String, name: NSString) -> Bool { - let itemFileName = getItemFileName(item) - let itemTypeName = OASettingsItemType.typeName(item.type) - if itemTypeName == type { - if name.isEqual(to: itemFileName) { - return true - } else if let fileItem = item as? OAFileSettingsItem { - let subtypeFolder = OAFileSettingsItemFileSubtype.getFolderName(fileItem.subtype) - if name.hasPrefix(subtypeFolder) || subtypeFolder.isEmpty { - var isDir: ObjCBool = false - FileManager.default.fileExists(atPath: fileItem.filePath, isDirectory: &isDir) - if isDir.boolValue, !itemFileName.hasSuffix("/") { - return name.hasPrefix("\(itemFileName)/") - } else { - return name.hasPrefix(itemFileName) - } - } - } - } - return false - } static func getItemFileName(_ item: OASettingsItem) -> String { var fileName: String diff --git a/Sources/Backup/LocalBackup/OASettingsHelper.mm b/Sources/Backup/LocalBackup/OASettingsHelper.mm index 209037f22b..f18854e5f5 100644 --- a/Sources/Backup/LocalBackup/OASettingsHelper.mm +++ b/Sources/Backup/LocalBackup/OASettingsHelper.mm @@ -6,6 +6,8 @@ // Copyright © 2020 OsmAnd. All rights reserved. // +#import "OsmAnd_Maps-Swift.h" +#import "OAIndexConstants.h" #import "OASettingsHelper.h" #import "OASettingsItem.h" #import "OASettingsImporter.h" @@ -314,8 +316,23 @@ - (void) exportSettings:(NSString *)fileDir fileName:(NSString *)fileName settin NSMutableArray *items = [NSMutableArray array]; for (NSString *file in files) { - if ([file.pathExtension isEqualToString:@"txt"]) + if ([file.pathExtension isEqualToString:@"txt"]) { + NSString *key = [COLOR_PALETTE_DIR stringByAppendingPathComponent:file]; + NSDictionary *assetsMap = BundledAssets.shared.assets; // Access the assets map + Asset *asset = assetsMap[key]; + if(asset) { + NSString *filePath = [colorPaletteFolder stringByAppendingPathComponent:file]; + NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil]; + NSDate *lastModifiedDate = attributes[NSFileModificationDate]; + if (lastModifiedDate) { + NSTimeInterval lastModifiedTimestamp = [lastModifiedDate timeIntervalSince1970]; + if (lastModifiedTimestamp <= asset.version.doubleValue) { + continue; + } + } + } [items addObject:[colorPaletteFolder stringByAppendingPathComponent:file]]; + } } if (items.count > 0) diff --git a/Sources/Backup/OABackupExporter.m b/Sources/Backup/OABackupExporter.m index 96a025d612..2b6de18cc3 100644 --- a/Sources/Backup/OABackupExporter.m +++ b/Sources/Backup/OABackupExporter.m @@ -314,7 +314,7 @@ - (void)onItemUploadProgress:(nonnull OASettingsItem *)item fileName:(nonnull NS if (_listener) { [_listener updateItemProgress:[OASettingsItemType typeName:item.type] fileName:fileName progress:progress]; - [_listener updateGeneralProgress:_itemsProgress.countSync uploadedKb:p]; + [_listener updateGeneralProgress:_itemsProgress.countSync uploadedKb:p/1024]; } } diff --git a/Sources/Backup/OABackupHelper.mm b/Sources/Backup/OABackupHelper.mm index 8dcf8c86de..ac4f172c2d 100644 --- a/Sources/Backup/OABackupHelper.mm +++ b/Sources/Backup/OABackupHelper.mm @@ -7,6 +7,7 @@ // #import "OABackupHelper.h" +#import "OAExportBackupTask.h" #import "OsmAndApp.h" #import "OAExportSettingsType.h" #import "OASettingsItem.h" @@ -116,15 +117,6 @@ + (NSString *) CHECK_CODE_URL return CHECK_CODE_URL; } -+ (OASettingsItem *) getRestoreItem:(NSArray *)items remoteFile:(OARemoteFile *)remoteFile -{ - for (OASettingsItem *item in items) - { - if ([BackupUtils applyItem:item type:remoteFile.type name:remoteFile.name]) - return item; - } - return nil; -} + (OABackupHelper *)sharedInstance { @@ -466,7 +458,7 @@ - (void)sendCode:(NSString *)email action:(NSString *)action - (NSInteger)calculateFileSize:(OARemoteFile *)remoteFile { - NSInteger sz = remoteFile.filesize / 1024; + NSInteger sz = remoteFile.filesize; if (remoteFile.item.type == EOASettingsItemTypeFile) { OAFileSettingsItem *flItem = (OAFileSettingsItem *) remoteFile.item; @@ -475,10 +467,13 @@ - (NSInteger)calculateFileSize:(OARemoteFile *)remoteFile NSString *mapId = flItem.fileName.lowerCase; const auto res = _app.resourcesManager->getResourceInRepository(QString::fromNSString(mapId)); if (res) - sz = res->size / 1024; + sz = res->size; } } - return sz; + if (sz < APPROXIMATE_FILE_SIZE_BYTES) { + sz = APPROXIMATE_FILE_SIZE_BYTES; // take into account 100 KB overhead for small file + } + return sz / 1024; } - (NSString *)downloadFile:(NSString *)filePath @@ -558,7 +553,7 @@ - (NSString *)downloadFile:(NSString *)filePath // progress.startWork((int) (remoteFile.getFilesize() / 1024)); if (listener) - [listener onFileDownloadDone:type fileName:fileName error:error]; + [listener onFileDownloadDone:type fileName:fileName estSize:sz error:error]; [operationLog finishOperation]; return error; } diff --git a/Sources/Backup/OABackupImporter.m b/Sources/Backup/OABackupImporter.m index 124fd34f05..5dacc95dc8 100644 --- a/Sources/Backup/OABackupImporter.m +++ b/Sources/Backup/OABackupImporter.m @@ -146,6 +146,7 @@ @implementation OABackupImporter NSOperationQueue *_queue; OAAtomicInteger *_dataProgress; + OAAtomicInteger *_downloadedDataProgress; OAAtomicInteger *_itemsProgress; NSString *_tmpFilesDir; } @@ -162,6 +163,9 @@ - (instancetype) initWithListener:(id)listener _tmpFilesDir = NSTemporaryDirectory(); _tmpFilesDir = [_tmpFilesDir stringByAppendingPathComponent:@"backupTmp"]; + _dataProgress = [[OAAtomicInteger alloc] initWithInteger:0]; + _downloadedDataProgress = [[OAAtomicInteger alloc] initWithInteger:0]; + _itemsProgress = [[OAAtomicInteger alloc] initWithInteger:0]; } return self; } @@ -217,6 +221,7 @@ - (void) importItems:(NSArray *)items restoreDeleted:(BOOL)restoreDeleted { _dataProgress = [[OAAtomicInteger alloc] initWithInteger:0]; + _downloadedDataProgress = [[OAAtomicInteger alloc] initWithInteger:0]; _itemsProgress = [[OAAtomicInteger alloc] initWithInteger:0]; if (items.count == 0) @throw [NSException exceptionWithName:@"IllegalArgumentException" reason:@"No setting items" userInfo:nil]; @@ -688,19 +693,22 @@ - (BOOL)isDownloadCancelled return self.cancelled; } -- (void)onFileDownloadDone:(NSString *)type fileName:(NSString *)fileName error:(NSString *)error +- (void)onFileDownloadDone:(NSString *)type fileName:(NSString *)fileName estSize:(NSInteger) estSize error:(NSString *)error { [_itemsProgress addAndGet:1]; + [_downloadedDataProgress addAndGet:estSize]; + [_dataProgress set:0]; if (_listener) { [_listener itemExportDone:type fileName:fileName]; - [_listener updateGeneralProgress:_itemsProgress.get uploadedKb:_dataProgress.get]; + [_listener updateGeneralProgress:_itemsProgress.get uploadedKb: + _downloadedDataProgress.get]; } } - (void)onFileDownloadProgress:(NSString *)type fileName:(NSString *)fileName progress:(NSInteger)progress deltaWork:(NSInteger)deltaWork itemFileName:(NSString *)itemFileName { - NSInteger p = [_dataProgress addAndGet:(int) deltaWork]; + NSInteger p = [_dataProgress addAndGet:(int) deltaWork] + [_downloadedDataProgress get]; if (_listener) { [_listener updateItemProgress:type fileName:fileName progress:(int)progress]; diff --git a/Sources/Backup/OABackupListeners.h b/Sources/Backup/OABackupListeners.h index 49d571c5b4..3db1009b45 100644 --- a/Sources/Backup/OABackupListeners.h +++ b/Sources/Backup/OABackupListeners.h @@ -91,7 +91,7 @@ - (void) onFileDownloadProgress:(NSString *)type fileName:(NSString *)fileName progress:(NSInteger)progress deltaWork:(NSInteger)deltaWork itemFileName:(NSString *)itemFileName; -- (void) onFileDownloadDone:(NSString *)type fileName:(NSString *)fileName error:(NSString *)error; +- (void) onFileDownloadDone:(NSString *)type fileName:(NSString *)fileName estSize:(NSInteger) estSize error:(NSString *)error; - (BOOL) isDownloadCancelled; diff --git a/Sources/Backup/OAImportBackupTask.m b/Sources/Backup/OAImportBackupTask.m index f2e0483ef4..0b7c346a6e 100644 --- a/Sources/Backup/OAImportBackupTask.m +++ b/Sources/Backup/OAImportBackupTask.m @@ -133,7 +133,7 @@ + (NSInteger) calculateMaxProgress NSInteger maxProgress = 0; OABackupHelper *backupHelper = OABackupHelper.sharedInstance; OAPrepareBackupResult *backup = backupHelper.backup; - for (OARemoteFile *file in backup.backupInfo.filesToDownload) + for (OARemoteFile *file in backup.backupInfo.filteredFilesToDownload) { maxProgress += [backupHelper calculateFileSize:file]; } diff --git a/Sources/Constants/OAIndexConstants.h b/Sources/Constants/OAIndexConstants.h index 491e84df5b..b5cbddf84a 100644 --- a/Sources/Constants/OAIndexConstants.h +++ b/Sources/Constants/OAIndexConstants.h @@ -106,7 +106,6 @@ static NSString * const PLUGINS_DIR = @"Plugins"; static NSString * const FAVORITES_INDEX_DIR = @"favorites"; static NSString * const FAVORITES_BACKUP_DIR = @"favorites_backup"; static NSString * const COLOR_PALETTE_DIR = @"color-palette"; -static NSString * const CLR_PALETTE_DIR = @"color-palette"; static NSString * const MODEL_3D_DIR = @"models"; static NSString * const GEOTIFF_SQLITE_CACHE_DIR = @"geotiff_sqlite_cache"; diff --git a/Sources/Data/OABundledAsetts.swift b/Sources/Data/OABundledAsetts.swift new file mode 100644 index 0000000000..0c87203f72 --- /dev/null +++ b/Sources/Data/OABundledAsetts.swift @@ -0,0 +1,205 @@ +// +// OABundledAsetts.swift +// OsmAnd +// +// Created by Victor Shcherb on 15.01.2025. +// Copyright © 2025 OsmAnd. All rights reserved. +// +import Foundation + +@objc public class Asset: NSObject { + @objc public let source: String + @objc public let destination: String + @objc public let mode: String? + @objc public let version: NSNumber? // Use Int64 for timestamp + + init(source: String, destination: String, mode: String?, versionStr: String?) { + self.source = source + self.destination = destination + self.mode = mode + if let version = versionStr, let timestamp = Asset.parseDate(version) { + self.version = NSNumber(value: timestamp) // Wrap timestamp in NSNumber + } else { + self.version = nil + } + } + + private static func parseDate(_ dateString: String) -> Int64? { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = "dd.MM.yyyy" + if let date = dateFormatter.date(from: dateString) { + return Int64(date.timeIntervalSince1970) + } + return nil + } +} + +@objc public class BundledAssets: NSObject { + @objc public static let shared = BundledAssets() // Singleton instance + + @objc public private(set) var assets: [String: Asset]? + + private override init() { + super.init() + + if let filePath = Bundle.main.path(forResource: "bundled_assets", ofType: "json") { + assets = BundledAssets.parse(fromJSONFile: filePath) + } else { + print("Error: bundled_assets.json not found in the main bundle.") + } + } + + private static func parse(fromJSONFile filePath: String) -> [String: Asset]? { + guard let jsonData = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else { + print("Error loading JSON file.") + return nil + } + do { + let jsonDict = try JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] + guard let assetsArray = jsonDict?["assets"] as? [[String: Any]] else { + print("Invalid JSON format.") + return nil + } + var assetsMap = [String: Asset]() + for assetDict in assetsArray { + let iosEnabled = assetDict["ios"] as? Bool ?? true + if !iosEnabled { continue } // Skip ONLY if "ios" is explicitly + guard let source = assetDict["source"] as? String, + let destination = assetDict["destination"] as? String else { + print("Missing source or destination.") + continue + } + + let mode = assetDict["mode"] as? String + let version = assetDict["version"] as? String + + let asset = Asset(source: source, destination: destination, mode: mode, versionStr: version) + assetsMap[destination] = asset // Group by destination + } + return assetsMap + } catch { + print("Error parsing JSON: \(error)") + return nil + } + } + + @objc func migrateResourcesToDocumentsIfNeeded(dataPath:String, documentsPath:String, versionChanged: Bool) -> Bool { + let movedRes = moveContentsOfDirectory( + from: dataPath.appendingPathComponent(RESOURCES_DIR), + to: documentsPath.appendingPathComponent(RESOURCES_DIR), + folderName: RESOURCES_DIR, removeOriginalFile: true, versionChanged: versionChanged + ) + let movedSqlite = moveContentsOfDirectory( + from: dataPath.appendingPathComponent(MAP_CREATOR_DIR), + to: documentsPath.appendingPathComponent(MAP_CREATOR_DIR), + folderName: MAP_CREATOR_DIR, removeOriginalFile: true, versionChanged: versionChanged + ) + if movedRes { + migrateMapNames(at: documentsPath.appendingPathComponent(RESOURCES_DIR)) + } + + moveContentsOfDirectory( + from: Bundle.main.path(forResource: COLOR_PALETTE_DIR, ofType: nil)!, // Force unwrap since it's assumed to exist + to: documentsPath.appendingPathComponent(COLOR_PALETTE_DIR), + folderName: COLOR_PALETTE_DIR, removeOriginalFile: false, versionChanged: versionChanged + ) + moveContentsOfDirectory( + from: Bundle.main.path(forResource: MODEL_3D_DIR, ofType: nil)!, + to: documentsPath.appendingPathComponent(MODEL_3D_DIR), + folderName: MODEL_3D_DIR, removeOriginalFile: false, versionChanged: versionChanged + ) + if movedRes || movedSqlite { + return true + } + return false + } + + func migrateMapNames(at path: String) { + let fileManager = FileManager.default + var isDirectory: ObjCBool = false // Use ObjCBool for bridging with Objective-C + + guard fileManager.fileExists(atPath: path, isDirectory: &isDirectory), isDirectory.boolValue else { return } + + guard let files = try? fileManager.contentsOfDirectory(atPath: path) else { return } + + for file in files { + let oldPath = path.appendingPathComponent(file) + var isDir: ObjCBool = false + if fileManager.fileExists(atPath: oldPath, isDirectory: &isDir), isDir.boolValue { + migrateMapNames(at: oldPath) // Recursive call for subdirectories + } else { + let newPath = path.appendingPathComponent(generateCorrectFileName(file)) + if newPath != oldPath { + try? fileManager.moveItem(atPath: oldPath, toPath: newPath) + } + } + } + } + + @discardableResult + func moveContentsOfDirectory(from src: String, to dest: String, folderName: String, removeOriginalFile: Bool, versionChanged: Bool) -> Bool { + let fileManager = FileManager.default + guard fileManager.fileExists(atPath: src) else { return false } + + if !fileManager.fileExists(atPath: dest) { + try? fileManager.createDirectory(atPath: dest, withIntermediateDirectories: true, attributes: nil) + } + + guard let files = try? fileManager.contentsOfDirectory(atPath: src) else { return false } + + var tryAgain = false + for file in files { + let destinationPath = dest.appendingPathComponent(file) + if fileManager.fileExists(atPath: destinationPath) { + if !versionChanged { continue } + try? fileManager.removeItem(atPath: destinationPath) + } + do { + if removeOriginalFile { + try fileManager.moveItem(atPath: src.appendingPathComponent(file), toPath: destinationPath) + } else { + try fileManager.copyItem(atPath: src.appendingPathComponent(file), toPath: destinationPath) + } + if let assets = assets, let asset = assets["\(folderName)/\(file)"], let version = asset.version { + do { + let attributes = [FileAttributeKey.modificationDate: Date(timeIntervalSince1970: TimeInterval(version.intValue))] + try FileManager.default.setAttributes(attributes, ofItemAtPath: destinationPath) + } catch { + print("Error setting last modified date: \(error)") + } + } + } catch { + print("Error copying \(file): \(error)") + tryAgain = true + } + } + + if removeOriginalFile && !tryAgain { + try? fileManager.removeItem(atPath: src) + } + + return true + } + + func generateCorrectFileName(_ path: String) -> String { + var fileName = path.lastPathComponent() as String + + if fileName.hasSuffix(".map.obf") { + fileName = OAUtilities.capitalizeFirstLetter(fileName.replacingOccurrences(of: ".map.obf", with: ".obf")) + } else if fileName.hasSuffix(".obf") { + fileName = OAUtilities.capitalizeFirstLetter(fileName) + } else if fileName.hasSuffix(".sqlitedb") { + if fileName.hasSuffix(".hillshade.sqlitedb") { + fileName = fileName.replacingOccurrences(of: ".hillshade.sqlitedb", with: ".sqlitedb") + fileName = fileName.replacingOccurrences(of: "_", with: " ") + fileName = "Hillshade \(String(describing: OAUtilities.capitalizeFirstLetter(fileName)))" + } else if fileName.hasSuffix(".slope.sqlitedb") { + fileName = fileName.replacingOccurrences(of: ".slope.sqlitedb", with: ".sqlitedb") + fileName = fileName.replacingOccurrences(of: "_", with: " ") + fileName = "Slope \(String(describing: OAUtilities.capitalizeFirstLetter(fileName)))" + } + } + + return path.deletingLastPathComponent().appendingPathComponent(fileName) + } +} diff --git a/Sources/Helpers/OAOperationLog.m b/Sources/Helpers/OAOperationLog.m index e169bc9981..778f79b338 100644 --- a/Sources/Helpers/OAOperationLog.m +++ b/Sources/Helpers/OAOperationLog.m @@ -53,7 +53,7 @@ - (instancetype) initWithOperationName:(NSString *)operationName debug:(BOOL)deb - (void) commonInit { - _logThreshold = 0.1; // 100 ms by default + _logThreshold = 0.3; // 300 ms by default _startTime = NSDate.date.timeIntervalSince1970; } From b351358457b7eec08670440af6542190233d848d Mon Sep 17 00:00:00 2001 From: vshcherb Date: Wed, 15 Jan 2025 16:29:44 +0200 Subject: [PATCH 2/3] Update OABackupExporter.m --- Sources/Backup/OABackupExporter.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/Backup/OABackupExporter.m b/Sources/Backup/OABackupExporter.m index 2b6de18cc3..96a025d612 100644 --- a/Sources/Backup/OABackupExporter.m +++ b/Sources/Backup/OABackupExporter.m @@ -314,7 +314,7 @@ - (void)onItemUploadProgress:(nonnull OASettingsItem *)item fileName:(nonnull NS if (_listener) { [_listener updateItemProgress:[OASettingsItemType typeName:item.type] fileName:fileName progress:progress]; - [_listener updateGeneralProgress:_itemsProgress.countSync uploadedKb:p/1024]; + [_listener updateGeneralProgress:_itemsProgress.countSync uploadedKb:p]; } } From 2d6c7dcd9ad8b7e78c09fa47ff02a631c869aab8 Mon Sep 17 00:00:00 2001 From: Aleksandr Panchenko Date: Wed, 15 Jan 2025 17:24:26 +0200 Subject: [PATCH 3/3] grooming --- Sources/AppHost/OsmAndAppImpl.mm | 4 +- .../Backup/LocalBackup/OASettingsHelper.mm | 2 +- Sources/Data/OABundledAsetts.swift | 59 ++++++++++--------- 3 files changed, 33 insertions(+), 32 deletions(-) diff --git a/Sources/AppHost/OsmAndAppImpl.mm b/Sources/AppHost/OsmAndAppImpl.mm index f27ee1a1c2..713b34e24b 100644 --- a/Sources/AppHost/OsmAndAppImpl.mm +++ b/Sources/AppHost/OsmAndAppImpl.mm @@ -397,6 +397,7 @@ - (BOOL) initializeImpl _data = [OAAppData defaults]; [defaults setObject:[NSKeyedArchiver archivedDataWithRootObject:_data] forKey:kAppData]; + [defaults setBool:NO forKey:@"hide_all_gpx"]; [defaults setBool:NO forKey:@"reset_settings"]; [defaults setBool:NO forKey:@"reset_routing"]; @@ -567,9 +568,8 @@ - (BOOL) initializeImpl BOOL rescan = [BundledAssets.shared migrateResourcesToDocumentsIfNeededWithDataPath:_dataPath documentsPath:_documentsPath versionChanged: currentVersion != prevVersion || _firstLaunch]; - if (rescan) { + if (rescan) _resourcesManager->rescanUnmanagedStoragePaths(true); - } // Copy regions.ocbf to Documents/Resources if needed NSString *ocbfPathBundle = [[NSBundle mainBundle] pathForResource:@"regions" ofType:@"ocbf"]; diff --git a/Sources/Backup/LocalBackup/OASettingsHelper.mm b/Sources/Backup/LocalBackup/OASettingsHelper.mm index f18854e5f5..5304e90f6e 100644 --- a/Sources/Backup/LocalBackup/OASettingsHelper.mm +++ b/Sources/Backup/LocalBackup/OASettingsHelper.mm @@ -319,7 +319,7 @@ - (void) exportSettings:(NSString *)fileDir fileName:(NSString *)fileName settin if ([file.pathExtension isEqualToString:@"txt"]) { NSString *key = [COLOR_PALETTE_DIR stringByAppendingPathComponent:file]; NSDictionary *assetsMap = BundledAssets.shared.assets; // Access the assets map - Asset *asset = assetsMap[key]; + BundledAsset *asset = assetsMap[key]; if(asset) { NSString *filePath = [colorPaletteFolder stringByAppendingPathComponent:file]; NSDictionary *attributes = [fileManager attributesOfItemAtPath:filePath error:nil]; diff --git a/Sources/Data/OABundledAsetts.swift b/Sources/Data/OABundledAsetts.swift index 0c87203f72..6d5b084979 100644 --- a/Sources/Data/OABundledAsetts.swift +++ b/Sources/Data/OABundledAsetts.swift @@ -5,19 +5,19 @@ // Created by Victor Shcherb on 15.01.2025. // Copyright © 2025 OsmAnd. All rights reserved. // -import Foundation - -@objc public class Asset: NSObject { - @objc public let source: String - @objc public let destination: String - @objc public let mode: String? - @objc public let version: NSNumber? // Use Int64 for timestamp +@objcMembers +final class BundledAsset: NSObject { + let source: String + let destination: String + let mode: String? + let version: NSNumber? // Use Int64 for timestamp + init(source: String, destination: String, mode: String?, versionStr: String?) { self.source = source self.destination = destination self.mode = mode - if let version = versionStr, let timestamp = Asset.parseDate(version) { + if let version = versionStr, let timestamp = BundledAsset.parseDate(version) { self.version = NSNumber(value: timestamp) // Wrap timestamp in NSNumber } else { self.version = nil @@ -34,22 +34,23 @@ import Foundation } } -@objc public class BundledAssets: NSObject { - @objc public static let shared = BundledAssets() // Singleton instance - - @objc public private(set) var assets: [String: Asset]? - +@objcMembers +final class BundledAssets: NSObject { + static let shared = BundledAssets() + + private(set) var assets: [String: BundledAsset]? + private override init() { super.init() - + if let filePath = Bundle.main.path(forResource: "bundled_assets", ofType: "json") { assets = BundledAssets.parse(fromJSONFile: filePath) } else { print("Error: bundled_assets.json not found in the main bundle.") } } - - private static func parse(fromJSONFile filePath: String) -> [String: Asset]? { + + private static func parse(fromJSONFile filePath: String) -> [String: BundledAsset]? { guard let jsonData = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else { print("Error loading JSON file.") return nil @@ -60,7 +61,7 @@ import Foundation print("Invalid JSON format.") return nil } - var assetsMap = [String: Asset]() + var assetsMap = [String: BundledAsset]() for assetDict in assetsArray { let iosEnabled = assetDict["ios"] as? Bool ?? true if !iosEnabled { continue } // Skip ONLY if "ios" is explicitly @@ -73,7 +74,7 @@ import Foundation let mode = assetDict["mode"] as? String let version = assetDict["version"] as? String - let asset = Asset(source: source, destination: destination, mode: mode, versionStr: version) + let asset = BundledAsset(source: source, destination: destination, mode: mode, versionStr: version) assetsMap[destination] = asset // Group by destination } return assetsMap @@ -83,7 +84,7 @@ import Foundation } } - @objc func migrateResourcesToDocumentsIfNeeded(dataPath:String, documentsPath:String, versionChanged: Bool) -> Bool { + func migrateResourcesToDocumentsIfNeeded(dataPath:String, documentsPath:String, versionChanged: Bool) -> Bool { let movedRes = moveContentsOfDirectory( from: dataPath.appendingPathComponent(RESOURCES_DIR), to: documentsPath.appendingPathComponent(RESOURCES_DIR), @@ -117,11 +118,11 @@ import Foundation func migrateMapNames(at path: String) { let fileManager = FileManager.default var isDirectory: ObjCBool = false // Use ObjCBool for bridging with Objective-C - + guard fileManager.fileExists(atPath: path, isDirectory: &isDirectory), isDirectory.boolValue else { return } - + guard let files = try? fileManager.contentsOfDirectory(atPath: path) else { return } - + for file in files { let oldPath = path.appendingPathComponent(file) var isDir: ObjCBool = false @@ -140,13 +141,13 @@ import Foundation func moveContentsOfDirectory(from src: String, to dest: String, folderName: String, removeOriginalFile: Bool, versionChanged: Bool) -> Bool { let fileManager = FileManager.default guard fileManager.fileExists(atPath: src) else { return false } - + if !fileManager.fileExists(atPath: dest) { try? fileManager.createDirectory(atPath: dest, withIntermediateDirectories: true, attributes: nil) } - + guard let files = try? fileManager.contentsOfDirectory(atPath: src) else { return false } - + var tryAgain = false for file in files { let destinationPath = dest.appendingPathComponent(file) @@ -173,17 +174,17 @@ import Foundation tryAgain = true } } - + if removeOriginalFile && !tryAgain { try? fileManager.removeItem(atPath: src) } - + return true } func generateCorrectFileName(_ path: String) -> String { var fileName = path.lastPathComponent() as String - + if fileName.hasSuffix(".map.obf") { fileName = OAUtilities.capitalizeFirstLetter(fileName.replacingOccurrences(of: ".map.obf", with: ".obf")) } else if fileName.hasSuffix(".obf") { @@ -199,7 +200,7 @@ import Foundation fileName = "Slope \(String(describing: OAUtilities.capitalizeFirstLetter(fileName)))" } } - + return path.deletingLastPathComponent().appendingPathComponent(fileName) } }