From 3aad4f4185d0069977ca1a1f497cdd9d430b0b6e Mon Sep 17 00:00:00 2001 From: "r.kuznetsov" Date: Thu, 26 Apr 2018 15:33:58 +0300 Subject: [PATCH] Added bookmarks catalog support --- .../maps/bookmarks/data/BookmarkManager.cpp | 56 ++++- .../maps/bookmarks/data/BookmarkManager.java | 71 ++++++ map/CMakeLists.txt | 2 + map/bookmark.cpp | 29 ++- map/bookmark.hpp | 8 +- map/bookmark_catalog.cpp | 79 +++++++ map/bookmark_catalog.hpp | 28 +++ map/bookmark_helpers.cpp | 10 + map/bookmark_helpers.hpp | 3 + map/bookmark_manager.cpp | 203 +++++++++++++++--- map/bookmark_manager.hpp | 26 ++- map/cloud.cpp | 33 +-- platform/CMakeLists.txt | 2 + platform/platform_linux.cpp | 2 + platform/platform_mac.mm | 1 + platform/remote_file.cpp | 74 +++++++ platform/remote_file.hpp | 50 +++++ xcode/map/map.xcodeproj/project.pbxproj | 22 +- .../platform.xcodeproj/project.pbxproj | 8 + 19 files changed, 647 insertions(+), 60 deletions(-) create mode 100644 map/bookmark_catalog.cpp create mode 100644 map/bookmark_catalog.hpp create mode 100644 platform/remote_file.cpp create mode 100644 platform/remote_file.hpp diff --git a/android/jni/com/mapswithme/maps/bookmarks/data/BookmarkManager.cpp b/android/jni/com/mapswithme/maps/bookmarks/data/BookmarkManager.cpp index 3f54f4cf72..8059c7ee3f 100644 --- a/android/jni/com/mapswithme/maps/bookmarks/data/BookmarkManager.cpp +++ b/android/jni/com/mapswithme/maps/bookmarks/data/BookmarkManager.cpp @@ -26,6 +26,8 @@ jmethodID g_onSynchronizationStartedMethod; jmethodID g_onSynchronizationFinishedMethod; jmethodID g_onRestoreRequestedMethod; jmethodID g_onRestoredFilesPreparedMethod; +jmethodID g_onImportStartedMethod; +jmethodID g_onImportFinishedMethod; void PrepareClassRefs(JNIEnv * env) { @@ -60,6 +62,10 @@ void PrepareClassRefs(JNIEnv * env) jni::GetMethodID(env, bookmarkManagerInstance, "onRestoreRequested", "(IJ)V"); g_onRestoredFilesPreparedMethod = jni::GetMethodID(env, bookmarkManagerInstance, "onRestoredFilesPrepared", "()V"); + g_onImportStartedMethod = + jni::GetMethodID(env, bookmarkManagerInstance, "onImportStarted", "(Ljava/lang/String;)V"); + g_onImportFinishedMethod = + jni::GetMethodID(env, bookmarkManagerInstance, "onImportFinished", "(Ljava/lang/String;Z)V"); } void OnAsyncLoadingStarted(JNIEnv * env) @@ -178,6 +184,26 @@ void OnRestoredFilesPrepared(JNIEnv * env) env->CallVoidMethod(bookmarkManagerInstance, g_onRestoredFilesPreparedMethod); jni::HandleJavaException(env); } + +void OnImportStarted(JNIEnv * env, std::string const & serverId) +{ + ASSERT(g_bookmarkManagerClass, ()); + jobject bookmarkManagerInstance = env->GetStaticObjectField(g_bookmarkManagerClass, + g_bookmarkManagerInstanceField); + env->CallVoidMethod(bookmarkManagerInstance, g_onImportStartedMethod, + jni::ToJavaString(env, serverId)); + jni::HandleJavaException(env); +} + +void OnImportFinished(JNIEnv * env, std::string const & serverId, bool successful) +{ + ASSERT(g_bookmarkManagerClass, ()); + jobject bookmarkManagerInstance = env->GetStaticObjectField(g_bookmarkManagerClass, + g_bookmarkManagerInstanceField); + env->CallVoidMethod(bookmarkManagerInstance, g_onImportFinishedMethod, + jni::ToJavaString(env, serverId), static_cast(successful)); + jni::HandleJavaException(env); +} } // namespace extern "C" @@ -208,11 +234,15 @@ Java_com_mapswithme_maps_bookmarks_data_BookmarkManager_nativeLoadBookmarks(JNIE std::bind(&OnRestoreRequested, env, _1, _2), std::bind(&OnRestoredFilesPrepared, env)); + frm()->GetBookmarkManager().SetCatalogHandlers(nullptr, nullptr, + std::bind(&OnImportStarted, env, _1), + std::bind(&OnImportFinished, env, _1, _2)); frm()->LoadBookmarks(); } JNIEXPORT jint JNICALL -Java_com_mapswithme_maps_bookmarks_data_BookmarkManager_nativeGetCategoriesCount(JNIEnv * env, jobject thiz) +Java_com_mapswithme_maps_bookmarks_data_BookmarkManager_nativeGetCategoriesCount( + JNIEnv * env, jobject thiz) { return frm()->GetBookmarkManager().GetBmGroupsIdList().size(); } @@ -546,4 +576,28 @@ Java_com_mapswithme_maps_bookmarks_data_BookmarkManager_nativeAreNotificationsEn { return static_cast(frm()->GetBookmarkManager().AreNotificationsEnabled()); } + +JNIEXPORT void JNICALL +Java_com_mapswithme_maps_bookmarks_data_BookmarkManager_nativeImportFromCatalog( + JNIEnv * env, jobject thiz, jstring serverId, jstring filePath) +{ + auto & bm = frm()->GetBookmarkManager(); + bm.ImportDownloadedFromCatalog(ToNativeString(env, serverId), ToNativeString(env, filePath)); +} + +JNIEXPORT jstring JNICALL +Java_com_mapswithme_maps_bookmarks_data_BookmarkManager_nativeGetCatalogDeeplink( + JNIEnv * env, jobject thiz, jlong catId) +{ + auto & bm = frm()->GetBookmarkManager(); + return ToJavaString(env, bm.GetCategoryCatalogDeeplink(static_cast(catId))); +} + +JNIEXPORT jboolean JNICALL +Java_com_mapswithme_maps_bookmarks_data_BookmarkManager_nativeIsCategoryFromCatalog( + JNIEnv * env, jobject thiz, jlong catId) +{ + auto & bm = frm()->GetBookmarkManager(); + return static_cast(bm.IsCategoryFromCatalog(static_cast(catId))); +} } // extern "C" diff --git a/android/src/com/mapswithme/maps/bookmarks/data/BookmarkManager.java b/android/src/com/mapswithme/maps/bookmarks/data/BookmarkManager.java index 21b58b9ac3..00002b414f 100644 --- a/android/src/com/mapswithme/maps/bookmarks/data/BookmarkManager.java +++ b/android/src/com/mapswithme/maps/bookmarks/data/BookmarkManager.java @@ -58,6 +58,9 @@ public enum BookmarkManager @NonNull private List mCloudListeners = new ArrayList<>(); + + @NonNull + private List mCatalogListeners = new ArrayList<>(); static { @@ -136,6 +139,16 @@ public enum BookmarkManager mCloudListeners.remove(listener); } + public void addCatalogListener(@NonNull BookmarksCatalogListener listener) + { + mCatalogListeners.add(listener); + } + + public void removeCatalogListener(@NonNull BookmarksCatalogListener listener) + { + mCatalogListeners.remove(listener); + } + // Called from JNI. @MainThread public void onBookmarksLoadingStarted() @@ -220,6 +233,22 @@ public enum BookmarkManager listener.onRestoredFilesPrepared(); } + // Called from JNI. + @MainThread + public void onImportStarted(@NonNull String id) + { + for (BookmarksCatalogListener listener : mCatalogListeners) + listener.onImportStarted(id); + } + + // Called from JNI. + @MainThread + public void onImportFinished(@NonNull String id, boolean successful) + { + for (BookmarksCatalogListener listener : mCatalogListeners) + listener.onImportFinished(id, successful); + } + public boolean isVisible(long catId) { return nativeIsVisible(catId); @@ -405,6 +434,22 @@ public enum BookmarkManager return nativeAreNotificationsEnabled(); } + public void importFromCatalog(@NonNull String serverId, @NonNull String filePath) + { + nativeImportFromCatalog(serverId, filePath); + } + + @NonNull + public String getCatalogDeeplink(long catId) + { + return nativeGetCatalogDeeplink(catId); + } + + public boolean isCategoryFromCatalog(long catId) + { + return nativeIsCategoryFromCatalog(catId); + } + private native int nativeGetCategoriesCount(); private native int nativeGetCategoryPositionById(long catId); @@ -493,6 +538,14 @@ public enum BookmarkManager private static native boolean nativeAreNotificationsEnabled(); + private static native void nativeImportFromCatalog(@NonNull String serverId, + @NonNull String filePath); + + @NonNull + private static native String nativeGetCatalogDeeplink(long catId); + + private static native boolean nativeIsCategoryFromCatalog(long catId); + public interface BookmarksLoadingListener { void onBookmarksLoadingStarted(); @@ -545,4 +598,22 @@ public enum BookmarkManager */ void onRestoredFilesPrepared(); } + + public interface BookmarksCatalogListener + { + /** + * The method is called when the importing of a file from the catalog is started. + * + * @param serverId is server identifier of the file. + */ + void onImportStarted(@NonNull String serverId); + + /** + * The method is called when the importing of a file from the catalog is finished. + * + * @param serverId is server identifier of the file. + * @param successful is result of the importing. + */ + void onImportFinished(@NonNull String serverId, boolean successful); + } } diff --git a/map/CMakeLists.txt b/map/CMakeLists.txt index c421ed8833..e8b12a6998 100644 --- a/map/CMakeLists.txt +++ b/map/CMakeLists.txt @@ -22,6 +22,8 @@ set( booking_filter_cache.hpp bookmark.cpp bookmark.hpp + bookmark_catalog.cpp + bookmark_catalog.hpp bookmark_helpers.cpp bookmark_helpers.hpp bookmark_manager.cpp diff --git a/map/bookmark.cpp b/map/bookmark.cpp index 5424722ca3..cd99cc3ec8 100644 --- a/map/bookmark.cpp +++ b/map/bookmark.cpp @@ -1,6 +1,10 @@ #include "map/bookmark.hpp" #include "map/bookmark_helpers.hpp" +#include "coding/url_encode.hpp" + +#include + namespace { std::string GetBookmarkIconType(kml::BookmarkIcon const & icon) @@ -222,10 +226,6 @@ BookmarkCategory::BookmarkCategory(kml::CategoryData && data, bool autoSave) Base::SetIsVisible(m_data.m_visible); } -BookmarkCategory::~BookmarkCategory() -{ -} - void BookmarkCategory::SetIsVisible(bool isVisible) { Base::SetIsVisible(isVisible); @@ -238,11 +238,32 @@ void BookmarkCategory::SetName(std::string const & name) kml::SetDefaultStr(m_data.m_name, name); } +void BookmarkCategory::SetServerId(std::string const & serverId) +{ + if (m_serverId == serverId) + return; + + SetDirty(); + m_serverId = serverId; +} + std::string BookmarkCategory::GetName() const { return kml::GetDefaultStr(m_data.m_name); } +bool BookmarkCategory::IsCategoryFromCatalog() const +{ + return FromCatalog(m_data, m_serverId); +} + +std::string BookmarkCategory::GetCatalogDeeplink() const +{ + std::ostringstream ss; + ss << "mapsme://dlink.maps.me/catalogue?id=" << m_serverId << "&name=" << UrlEncode(GetName()); + return ss.str(); +} + // static kml::PredefinedColor BookmarkCategory::GetDefaultColor() { diff --git a/map/bookmark.hpp b/map/bookmark.hpp index fd14b3dae3..b265257d25 100644 --- a/map/bookmark.hpp +++ b/map/bookmark.hpp @@ -66,7 +66,6 @@ class BookmarkCategory : public UserMarkLayer public: BookmarkCategory(std::string const & name, kml::MarkGroupId groupId, bool autoSave); BookmarkCategory(kml::CategoryData && data, bool autoSave); - ~BookmarkCategory() override; static kml::PredefinedColor GetDefaultColor(); @@ -83,6 +82,12 @@ public: kml::CategoryData const & GetCategoryData() const { return m_data; } + void SetServerId(std::string const & serverId); + std::string const & GetServerId() const { return m_serverId; } + + bool IsCategoryFromCatalog() const; + std::string GetCatalogDeeplink() const; + private: void SetDirty() override; @@ -90,4 +95,5 @@ private: std::string m_file; bool m_autoSave = true; kml::CategoryData m_data; + std::string m_serverId; }; diff --git a/map/bookmark_catalog.cpp b/map/bookmark_catalog.cpp new file mode 100644 index 0000000000..a5cedef320 --- /dev/null +++ b/map/bookmark_catalog.cpp @@ -0,0 +1,79 @@ +#include "map/bookmark_catalog.hpp" + +#include "platform/platform.hpp" + +#include "coding/file_name_utils.hpp" + +#include "base/string_utils.hpp" + +#include + +namespace +{ +std::string BuildCatalogUrl(std::string const & serverId) +{ + //TODO: This code is temporary. + return "https://bookcat.demo.mapsme1.devmail.ru/static/" + serverId + "/" + serverId; +} +} // namespace + +BookmarkCatalog::BookmarkCatalog(std::string const & catalogDir) +{ + Platform::FilesList files; + Platform::GetFilesRecursively(catalogDir, files); + for (auto const & f : files) + m_downloadedIds.insert(my::GetNameFromFullPathWithoutExt(f)); +} + +void BookmarkCatalog::RegisterDownloadedId(std::string const & id) +{ + if (id.empty()) + return; + + m_downloadedIds.insert(id); + m_downloadingIds.erase(id); +} + +void BookmarkCatalog::UnregisterDownloadedId(std::string const & id) +{ + if (id.empty()) + return; + + m_downloadedIds.erase(id); +} + +std::vector BookmarkCatalog::GetDownloadingNames() const +{ + std::vector names; + names.reserve(m_downloadingIds.size()); + for (auto const & p : m_downloadingIds) + names.push_back(p.second); + return names; +} + +void BookmarkCatalog::Download(std::string const & id, std::string const & name, + std::function && startHandler, + platform::RemoteFile::ResultHandler && finishHandler) +{ + if (m_downloadingIds.find(id) != m_downloadingIds.cend() || + m_downloadedIds.find(id) != m_downloadedIds.cend()) + { + return; + } + + m_downloadingIds.insert(std::make_pair(id, name)); + + if (startHandler) + startHandler(); + + static uint32_t counter = 0; + auto const path = my::JoinPath(GetPlatform().TmpDir(), "file" + strings::to_string(++counter)); + + platform::RemoteFile remoteFile(BuildCatalogUrl(id)); + remoteFile.DownloadAsync(path, [finishHandler = std::move(finishHandler)] + (platform::RemoteFile::Result && result, std::string const & filePath) mutable + { + if (finishHandler) + finishHandler(std::move(result), filePath); + }); +} diff --git a/map/bookmark_catalog.hpp b/map/bookmark_catalog.hpp new file mode 100644 index 0000000000..2c6b3b22c5 --- /dev/null +++ b/map/bookmark_catalog.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "platform/remote_file.hpp" + +#include +#include +#include +#include +#include + +class BookmarkCatalog +{ +public: + explicit BookmarkCatalog(std::string const & catalogDir); + + void RegisterDownloadedId(std::string const & id); + void UnregisterDownloadedId(std::string const & id); + + void Download(std::string const & id, std::string const & name, + std::function && startHandler, + platform::RemoteFile::ResultHandler && finishHandler); + size_t GetDownloadingCount() const { return m_downloadingIds.size(); } + std::vector GetDownloadingNames() const; + +private: + std::map m_downloadingIds; + std::set m_downloadedIds; +}; diff --git a/map/bookmark_helpers.cpp b/map/bookmark_helpers.cpp index 2d1cf2088f..32c699a275 100644 --- a/map/bookmark_helpers.cpp +++ b/map/bookmark_helpers.cpp @@ -427,3 +427,13 @@ std::string GetPreferredBookmarkName(kml::BookmarkData const & bmData) name = GetLocalizedBookmarkType(bmData.m_featureTypes); return name; } + +bool FromCatalog(kml::FileData const & kmlData) +{ + return FromCatalog(kmlData.m_categoryData, kmlData.m_serverId); +} + +bool FromCatalog(kml::CategoryData const & categoryData, std::string const & serverId) +{ + return !serverId.empty() && categoryData.m_accessRules == kml::AccessRules::Public; +} diff --git a/map/bookmark_helpers.hpp b/map/bookmark_helpers.hpp index c3775ada5a..8b477065c7 100644 --- a/map/bookmark_helpers.hpp +++ b/map/bookmark_helpers.hpp @@ -24,3 +24,6 @@ std::string GetPreferredBookmarkName(kml::BookmarkData const & bmData); std::string GetPreferredBookmarkStr(kml::LocalizableString const & name); std::string GetPreferredBookmarkStr(kml::LocalizableString const & name, feature::RegionData const & regionData); std::string GetLocalizedBookmarkType(std::vector const & types); + +bool FromCatalog(kml::FileData const & kmlData); +bool FromCatalog(kml::CategoryData const & categoryData, std::string const & serverId); diff --git a/map/bookmark_manager.cpp b/map/bookmark_manager.cpp index 0a8c26812e..3e85e62d86 100644 --- a/map/bookmark_manager.cpp +++ b/map/bookmark_manager.cpp @@ -74,6 +74,11 @@ std::string GetBookmarksDirectory() return my::JoinPath(GetPlatform().SettingsDir(), "bookmarks"); } +std::string GetPrivateBookmarksDirectory() +{ + return my::JoinPath(GetPlatform().PrivateDir(), "bookmarks_private"); +} + bool IsBadCharForPath(strings::UniChar const & c) { static strings::UniChar const illegalChars[] = {':', '/', '\\', '<', '>', '\"', '|', '?', '*'}; @@ -167,14 +172,14 @@ Cloud::ConvertionResult ConvertBeforeUploading(std::string const & filePath, return result; } -Cloud::ConvertionResult ConvertAfterDownloading(std::string const & filePath, - std::string const & convertedFilePath) +std::unique_ptr ImportAfterDownloading(std::string const & filePath, + std::string & hash) { ZipFileReader::FileListT files; ZipFileReader::FilesList(filePath, files); if (files.empty()) return {}; - + auto fileName = files.front().first; for (auto const & file : files) { @@ -188,14 +193,22 @@ Cloud::ConvertionResult ConvertAfterDownloading(std::string const & filePath, MY_SCOPE_GUARD(fileGuard, bind(&FileWriter::DeleteFileX, unarchievedPath)); ZipFileReader::UnzipFile(filePath, fileName, unarchievedPath); if (!GetPlatform().IsFileExistsByFullPath(unarchievedPath)) - return {}; + return nullptr; + + hash = coding::SHA1::CalculateBase64(unarchievedPath); + return LoadKmlFile(unarchievedPath, false /* binary */); +} - auto kmlData = LoadKmlFile(unarchievedPath, false /* binary */); +Cloud::ConvertionResult ConvertAfterDownloading(std::string const & filePath, + std::string const & convertedFilePath) +{ + std::string hash; + auto kmlData = ImportAfterDownloading(filePath, hash); if (kmlData == nullptr) return {}; - + Cloud::ConvertionResult result; - result.m_hash = coding::SHA1::CalculateBase64(unarchievedPath); + result.m_hash = hash; result.m_isSuccessful = SaveKmlFile(*kmlData, convertedFilePath, true /* binary */); return result; } @@ -502,6 +515,7 @@ BookmarkManager::BookmarkManager(Callbacks && callbacks) GetBookmarksDirectory(), std::string(kKmbExtension), std::bind(&ConvertBeforeUploading, _1, _2), std::bind(&ConvertAfterDownloading, _1, _2))) + , m_bookmarkCatalog(GetPrivateBookmarksDirectory()) { ASSERT(m_callbacks.m_getStringsBundle != nullptr, ()); @@ -793,6 +807,7 @@ void BookmarkManager::ClearGroup(kml::MarkGroupId groupId) m_tracks.erase(trackId); } group->Clear(); + m_changesTracker.OnDeleteGroup(groupId); } std::string BookmarkManager::GetCategoryName(kml::MarkGroupId categoryId) const @@ -924,10 +939,7 @@ void BookmarkManager::ClearCategories() { CHECK_THREAD_CHECKER(m_threadChecker, ()); for (auto groupId : m_bmGroupsIdList) - { ClearGroup(groupId); - m_changesTracker.OnDeleteGroup(groupId); - } m_categories.clear(); UpdateBmGroupIdList(); @@ -936,8 +948,9 @@ void BookmarkManager::ClearCategories() m_tracks.clear(); } -BookmarkManager::KMLDataCollectionPtr BookmarkManager::LoadBookmarks(std::string const & dir, std::string const & ext, - bool binary, std::vector & filePaths) +BookmarkManager::KMLDataCollectionPtr BookmarkManager::LoadBookmarks( + std::string const & dir, std::string const & ext, bool binary, + std::vector & filePaths) { Platform::FilesList files; Platform::GetFilesByExt(dir, ext, files); @@ -978,6 +991,14 @@ void BookmarkManager::LoadBookmarks() auto collection = LoadBookmarks(dir, filesExt, migrated, filePaths); migration::FixUpHotelPlacemarks(collection, isMigrationCompleted); + // Load files downloaded from catalog. + std::vector unusedFilePaths; + auto catalogCollection = LoadBookmarks(GetPrivateBookmarksDirectory(), kKmbExtension, + true /* binary */, unusedFilePaths); + collection->reserve(collection->size() + catalogCollection->size()); + for (auto & p : *catalogCollection) + collection->emplace_back(p.first, std::move(p.second)); + if (m_needTeardown) return; NotifyAboutFinishAsyncLoading(std::move(collection)); @@ -1022,7 +1043,7 @@ void BookmarkManager::LoadBookmarkRoutine(std::string const & filePath, bool isT { auto fileSavePath = savePath.get(); auto kmlData = LoadKmlFile(fileSavePath, false /* useBinary */); - if (kmlData != nullptr) + if (kmlData && !FromCatalog(*kmlData)) { if (m_needTeardown) return; @@ -1361,9 +1382,9 @@ bool BookmarkManager::DeleteBmCategory(kml::MarkGroupId groupId) return false; ClearGroup(groupId); - m_changesTracker.OnDeleteGroup(groupId); FileWriter::DeleteFileX(it->second->GetFileName()); + m_bookmarkCatalog.UnregisterDownloadedId(it->second->GetServerId()); m_categories.erase(it); UpdateBmGroupIdList(); @@ -1375,7 +1396,8 @@ namespace class BestUserMarkFinder { public: - explicit BestUserMarkFinder(BookmarkManager::TTouchRectHolder const & rectHolder, BookmarkManager const * manager) + explicit BestUserMarkFinder(BookmarkManager::TTouchRectHolder const & rectHolder, + BookmarkManager const * manager) : m_rectHolder(rectHolder) , m_d(numeric_limits::max()) , m_mark(nullptr) @@ -1487,6 +1509,7 @@ void BookmarkManager::CreateCategories(KMLDataCollection && dataCollection, bool loadedGroups.insert(groupId); auto * group = GetBmCategory(groupId); group->SetFileName(fileName); + group->SetServerId(fileData.m_serverId); for (auto & bmData : fileData.m_bookmarksData) { @@ -1541,12 +1564,13 @@ std::unique_ptr BookmarkManager::CollectBmGroupKMLData(BookmarkCa { auto kmlData = std::make_unique(); kmlData->m_deviceId = GetPlatform().UniqueClientId(); + kmlData->m_serverId = group->GetServerId(); kmlData->m_categoryData = group->GetCategoryData(); auto const & markIds = group->GetUserMarks(); kmlData->m_bookmarksData.reserve(markIds.size()); for (auto it = markIds.rbegin(); it != markIds.rend(); ++it) { - Bookmark const *bm = GetBookmark(*it); + Bookmark const * bm = GetBookmark(*it); kmlData->m_bookmarksData.emplace_back(bm->GetData()); } auto const & lineIds = group->GetUserLines(); @@ -1567,7 +1591,7 @@ bool BookmarkManager::SaveBookmarkCategory(kml::MarkGroupId groupId) return false; auto const & file = collection->front().first; auto & kmlData = *collection->front().second; - return SaveKmlFileSafe(kmlData, file, migration::IsMigrationCompleted()); + return SaveKmlFileSafe(kmlData, file); } bool BookmarkManager::SaveBookmarkCategory(kml::MarkGroupId groupId, Writer & writer, bool useBinary) const @@ -1582,7 +1606,6 @@ BookmarkManager::KMLDataCollectionPtr BookmarkManager::PrepareToSaveBookmarks( kml::GroupIdCollection const & groupIdCollection) { bool migrated = migration::IsMigrationCompleted(); - std::string const fileDir = migrated ? GetBookmarksDirectory() : GetPlatform().SettingsDir(); std::string const fileExt = migrated ? kKmbExtension : kKmlExtension; @@ -1590,11 +1613,24 @@ BookmarkManager::KMLDataCollectionPtr BookmarkManager::PrepareToSaveBookmarks( return nullptr; auto collection = std::make_shared(); - for (auto const groupId : groupIdCollection) { auto * group = GetBmCategory(groupId); + if (group->IsCategoryFromCatalog()) + { + std::string const privateFileDir = GetPrivateBookmarksDirectory(); + if (!GetPlatform().IsFileExistsByFullPath(privateFileDir) && + !GetPlatform().MkDirChecked(privateFileDir)) + { + return nullptr; + } + auto const fn = my::JoinPath(privateFileDir, group->GetServerId() + kKmbExtension); + group->SetFileName(fn); + collection->emplace_back(fn, CollectBmGroupKMLData(group)); + continue; + } + // Get valid file name from category name std::string const name = RemoveInvalidSymbols(group->GetName(), kDefaultBookmarksFileName); std::string file = group->GetFileName(); @@ -1610,10 +1646,11 @@ BookmarkManager::KMLDataCollectionPtr BookmarkManager::PrepareToSaveBookmarks( return collection; } -bool BookmarkManager::SaveKmlFileSafe(kml::FileData & kmlData, std::string const & file, bool useBinary) +bool BookmarkManager::SaveKmlFileSafe(kml::FileData & kmlData, std::string const & file) { + auto const ext = my::GetFileExtension(file); auto const fileTmp = file + ".tmp"; - if (SaveKmlFile(kmlData, fileTmp, useBinary)) + if (SaveKmlFile(kmlData, fileTmp, ext == kKmbExtension)) { // Only after successful save we replace original file. my::DeleteFileX(file); @@ -1635,20 +1672,19 @@ void BookmarkManager::SaveBookmarks(kml::GroupIdCollection const & groupIdCollec if (!kmlDataCollection) return; - bool const migrated = migration::IsMigrationCompleted(); - if (m_testModeEnabled) { // Save bookmarks synchronously. for (auto const & kmlItem : *kmlDataCollection) - SaveKmlFileSafe(*kmlItem.second, kmlItem.first, migrated); + SaveKmlFileSafe(*kmlItem.second, kmlItem.first); return; } - GetPlatform().RunTask(Platform::Thread::File, [this, migrated, kmlDataCollection = std::move(kmlDataCollection)]() + GetPlatform().RunTask(Platform::Thread::File, + [this, kmlDataCollection = std::move(kmlDataCollection)]() { for (auto const & kmlItem : *kmlDataCollection) - SaveKmlFileSafe(*kmlItem.second, kmlItem.first, migrated); + SaveKmlFileSafe(*kmlItem.second, kmlItem.first); }); } @@ -1813,19 +1849,19 @@ void BookmarkManager::ConvertAllKmlFiles(ConversionHandler && handler) bool allConverted = true; for (auto const & f : files) { + std::unique_ptr kmlData = LoadKmlFile(f, false /* binary */); + if (kmlData == nullptr || FromCatalog(*kmlData)) + { + allConverted = false; + continue; + } + std::string fileName = my::GetNameFromFullPathWithoutExt(f); auto kmbPath = my::JoinPath(newDir, fileName + kKmbExtension); size_t counter = 1; while (Platform::IsFileExistsByFullPath(kmbPath)) kmbPath = my::JoinPath(newDir, fileName + strings::to_string(counter++) + kKmbExtension); - std::unique_ptr kmlData = LoadKmlFile(f, false /* binary */); - if (kmlData == nullptr) - { - allConverted = false; - continue; - } - if (!SaveKmlFile(*kmlData, kmbPath, true /* binary */)) { allConverted = false; @@ -1959,6 +1995,107 @@ bool BookmarkManager::AreNotificationsEnabled() const return m_notificationsEnabled; } +void BookmarkManager::SetCatalogHandlers(OnCatalogDownloadStartedHandler && onCatalogDownloadStarted, + OnCatalogDownloadFinishedHandler && onCatalogDownloadFinished, + OnCatalogImportStartedHandler && onCatalogImportStarted, + OnCatalogImportFinishedHandler && onCatalogImportFinished) +{ + m_onCatalogDownloadStarted = std::move(onCatalogDownloadStarted); + m_onCatalogDownloadFinished = std::move(onCatalogDownloadFinished); + m_onCatalogImportStarted = std::move(onCatalogImportStarted); + m_onCatalogImportFinished = std::move(onCatalogImportFinished); +} + +void BookmarkManager::DownloadFromCatalogAndImport(std::string const & id, std::string const & name) +{ + m_bookmarkCatalog.Download(id, name, [this, id]() + { + if (m_onCatalogDownloadStarted) + m_onCatalogDownloadStarted(id); + }, + [this, id](platform::RemoteFile::Result && result, std::string const & filePath) + { + if (m_onCatalogDownloadFinished) + m_onCatalogDownloadFinished(id, result); + + if (result.m_status == platform::RemoteFile::Status::Ok) + ImportDownloadedFromCatalog(id, filePath); + }); +} + +void BookmarkManager::ImportDownloadedFromCatalog(std::string const & id, std::string const & filePath) +{ + if (m_onCatalogImportStarted) + m_onCatalogImportStarted(id); + + GetPlatform().RunTask(Platform::Thread::File, [this, id, filePath]() + { + MY_SCOPE_GUARD(fileGuard, std::bind(&FileWriter::DeleteFileX, filePath)); + std::string hash; + auto kmlData = ImportAfterDownloading(filePath, hash); + if (kmlData && FromCatalog(*kmlData) && kmlData->m_serverId == id) + { + auto const p = my::JoinPath(GetPrivateBookmarksDirectory(), id, kKmbExtension); + auto collection = std::make_shared(); + collection->emplace_back(p, std::move(kmlData)); + + GetPlatform().RunTask(Platform::Thread::Gui, [this, id, collection]() + { + std::vector idsToDelete; + for (auto const & group : m_categories) + { + if (id == group.second->GetServerId()) + { + ClearGroup(group.first); + idsToDelete.push_back(group.first); + } + } + for (auto const & categoryId : idsToDelete) + m_categories.erase(categoryId); + + CreateCategories(std::move(*collection)); + + m_bookmarkCatalog.RegisterDownloadedId(id); + + if (m_onCatalogImportFinished) + m_onCatalogImportFinished(id, true /* successful */); + }); + } + else + { + if (m_onCatalogImportFinished) + m_onCatalogImportFinished(id, false /* successful */); + } + }); +} + +size_t BookmarkManager::GetDownloadingFromCatalogCount() const +{ + return m_bookmarkCatalog.GetDownloadingCount(); +} + +std::vector BookmarkManager::GetDownloadingFromCatalogNames() const +{ + return m_bookmarkCatalog.GetDownloadingNames(); +} + +bool BookmarkManager::IsCategoryFromCatalog(kml::MarkGroupId categoryId) const +{ + auto cat = GetBmCategory(categoryId); + if (cat == nullptr) + return false; + + return cat->IsCategoryFromCatalog(); +} + +std::string BookmarkManager::GetCategoryCatalogDeeplink(kml::MarkGroupId categoryId) const +{ + auto cat = GetBmCategory(categoryId); + if (cat == nullptr) + return {}; + return cat->GetCatalogDeeplink(); +} + void BookmarkManager::EnableTestMode(bool enable) { UserMarkIdStorage::Instance().EnableSaving(!enable); diff --git a/map/bookmark_manager.hpp b/map/bookmark_manager.hpp index f85c9f1af1..49c057875d 100644 --- a/map/bookmark_manager.hpp +++ b/map/bookmark_manager.hpp @@ -1,6 +1,7 @@ #pragma once #include "map/bookmark.hpp" +#include "map/bookmark_catalog.hpp" #include "map/cloud.hpp" #include "map/track.hpp" #include "map/user_mark_layer.hpp" @@ -268,6 +269,23 @@ public: void SetNotificationsEnabled(bool enabled); bool AreNotificationsEnabled() const; + using OnCatalogDownloadStartedHandler = platform::SafeCallback; + using OnCatalogDownloadFinishedHandler = platform::SafeCallback; + using OnCatalogImportStartedHandler = platform::SafeCallback; + using OnCatalogImportFinishedHandler = platform::SafeCallback; + void SetCatalogHandlers(OnCatalogDownloadStartedHandler && onCatalogDownloadStarted, + OnCatalogDownloadFinishedHandler && onCatalogDownloadFinished, + OnCatalogImportStartedHandler && onCatalogImportStarted, + OnCatalogImportFinishedHandler && onCatalogImportFinished); + void DownloadFromCatalogAndImport(std::string const & id, std::string const & name); + void ImportDownloadedFromCatalog(std::string const & id, std::string const & filePath); + size_t GetDownloadingFromCatalogCount() const; + std::vector GetDownloadingFromCatalogNames() const; + bool IsCategoryFromCatalog(kml::MarkGroupId categoryId) const; + std::string GetCategoryCatalogDeeplink(kml::MarkGroupId categoryId) const; + /// These functions are public for unit tests only. You shouldn't call them from client code. void EnableTestMode(bool enable); bool SaveBookmarkCategory(kml::MarkGroupId groupId); @@ -432,7 +450,7 @@ private: std::unique_ptr CollectBmGroupKMLData(BookmarkCategory const * group) const; KMLDataCollectionPtr PrepareToSaveBookmarks(kml::GroupIdCollection const & groupIdCollection); - bool SaveKmlFileSafe(kml::FileData & kmlData, std::string const & file, bool useBinary); + bool SaveKmlFileSafe(kml::FileData & kmlData, std::string const & file); void OnSynchronizationStarted(Cloud::SynchronizationType type); void OnSynchronizationFinished(Cloud::SynchronizationType type, Cloud::SynchronizationResult result, @@ -495,6 +513,12 @@ private: Cloud::RestoreRequestedHandler m_onRestoreRequested; Cloud::RestoredFilesPreparedHandler m_onRestoredFilesPrepared; + BookmarkCatalog m_bookmarkCatalog; + OnCatalogDownloadStartedHandler m_onCatalogDownloadStarted; + OnCatalogDownloadFinishedHandler m_onCatalogDownloadFinished; + OnCatalogImportStartedHandler m_onCatalogImportStarted; + OnCatalogImportFinishedHandler m_onCatalogImportFinished; + bool m_testModeEnabled = false; DISALLOW_COPY_AND_MOVE(BookmarkManager); diff --git a/map/cloud.cpp b/map/cloud.cpp index c73146c8bb..eb3acfed19 100644 --- a/map/cloud.cpp +++ b/map/cloud.cpp @@ -10,6 +10,7 @@ #include "platform/http_client.hpp" #include "platform/platform.hpp" #include "platform/preferred_languages.hpp" +#include "platform/remote_file.hpp" #include "platform/settings.hpp" #include "platform/http_uploader.hpp" @@ -329,7 +330,7 @@ Cloud::SnapshotResponseData ReadSnapshotFile(std::string const & filename) return {}; } -Cloud::RequestResult DownloadFile(std::string const & url, std::string const & fileName, +/*Cloud::RequestResult DownloadFile(std::string const & url, std::string const & fileName, bool & successfullyWritten) { successfullyWritten = true; @@ -361,7 +362,7 @@ Cloud::RequestResult DownloadFile(std::string const & url, std::string const & f } } return {Cloud::RequestStatus::NetworkError, request.ServerResponse()}; -} +}*/ bool CheckAndGetFileSize(std::string const & filePath, uint64_t & fileSize) { @@ -1423,25 +1424,31 @@ void Cloud::DownloadingTask(std::string const & dirPath, bool useFallbackUrl, FinishRestoring(SynchronizationResult::NetworkError, "Fallback url is absent"); return; } - bool successfullyWritten = true; - auto const downloadResult = DownloadFile(useFallbackUrl ? result.m_response.m_fallbackUrl - : result.m_response.m_url, - filePath, successfullyWritten); - if (!successfullyWritten) - { - FinishRestoring(SynchronizationResult::DiskError, "Could not create downloaded file"); - } - else if (downloadResult.m_status == RequestStatus::Ok) + + platform::RemoteFile remoteFile(useFallbackUrl ? result.m_response.m_fallbackUrl + : result.m_response.m_url, + false /* allowRedirection */); + auto const downloadResult = remoteFile.Download(filePath); + + if (downloadResult.m_status == platform::RemoteFile::Status::Ok) { // Download next file. DownloadingTask(dirPath, false /* useFallbackUrl */, std::move(files)); } + else if (downloadResult.m_status == platform::RemoteFile::Status::DiskError) + { + FinishRestoring(SynchronizationResult::DiskError, downloadResult.m_description); + } + else if (downloadResult.m_status == platform::RemoteFile::Status::Forbidden) + { + FinishRestoring(SynchronizationResult::AuthError, downloadResult.m_description); + } else { alohalytics::TStringMap details{ {"service", m_params.m_serverPathName}, {"type", useFallbackUrl ? "fallback_server" : "download_server"}, - {"error", downloadResult.m_error}}; + {"error", downloadResult.m_description}}; alohalytics::Stats::Instance().LogEvent("Cloud_Restore_error", details); if (!useFallbackUrl) @@ -1452,7 +1459,7 @@ void Cloud::DownloadingTask(std::string const & dirPath, bool useFallbackUrl, } else { - FinishRestoring(SynchronizationResult::NetworkError, "File downloader error"); + FinishRestoring(SynchronizationResult::NetworkError, downloadResult.m_description); } } } diff --git a/platform/CMakeLists.txt b/platform/CMakeLists.txt index cfaf56bdf6..b275c0b280 100644 --- a/platform/CMakeLists.txt +++ b/platform/CMakeLists.txt @@ -41,6 +41,8 @@ set( platform.hpp preferred_languages.cpp preferred_languages.hpp + remote_file.cpp + remote_file.hpp secure_storage.hpp servers_list.cpp servers_list.hpp diff --git a/platform/platform_linux.cpp b/platform/platform_linux.cpp index 296fc7761a..5eb42689f9 100644 --- a/platform/platform_linux.cpp +++ b/platform/platform_linux.cpp @@ -187,6 +187,8 @@ Platform::Platform() m_tmpDir = "/tmp"; m_tmpDir += '/'; + m_privateDir = m_settingsDir; + m_guiThread = make_unique(); LOG(LDEBUG, ("Resources directory:", m_resourcesDir)); diff --git a/platform/platform_mac.mm b/platform/platform_mac.mm index 544795cd8f..8f3c8d0eca 100644 --- a/platform/platform_mac.mm +++ b/platform/platform_mac.mm @@ -96,6 +96,7 @@ Platform::Platform() m_writableDir = my::AddSlashIfNeeded(m_writableDir); m_settingsDir = m_writableDir; + m_privateDir = m_writableDir; NSString * tempDir = NSTemporaryDirectory(); if (tempDir == nil) diff --git a/platform/remote_file.cpp b/platform/remote_file.cpp new file mode 100644 index 0000000000..bbe03bb3e0 --- /dev/null +++ b/platform/remote_file.cpp @@ -0,0 +1,74 @@ +#include "platform/remote_file.hpp" + +#include "platform/http_client.hpp" +#include "platform/platform.hpp" + +#include "coding/file_writer.hpp" + +#include "base/logging.hpp" + +namespace platform +{ +namespace +{ +double constexpr kRequestTimeoutInSec = 5.0; +} // namespace + +RemoteFile::RemoteFile(std::string url, bool allowRedirection) + : m_url(std::move(url)) + , m_allowRedirection(allowRedirection) +{} + +RemoteFile::Result RemoteFile::Download(std::string const & filePath) const +{ + platform::HttpClient request(m_url); + request.SetTimeout(kRequestTimeoutInSec); + if (request.RunHttpRequest()) + { + if (!m_allowRedirection && request.WasRedirected()) + return {m_url, Status::NetworkError, "Unexpected redirection"}; + + auto const & response = request.ServerResponse(); + int const resultCode = request.ErrorCode(); + if (resultCode == 403) + { + LOG(LWARNING, ("Access denied for", m_url, "response:", response)); + return {m_url, Status::Forbidden, resultCode, response}; + } + else if (resultCode == 404) + { + LOG(LWARNING, ("File not found at", m_url, "response:", response)); + return {m_url, Status::NotFound, resultCode, response}; + } + + if (resultCode >= 200 && resultCode < 300) + { + try + { + FileWriter w(filePath); + w.Write(response.data(), response.length()); + } + catch (FileWriter::Exception const & exception) + { + LOG(LWARNING, ("Exception while writing file:", filePath, "reason:", exception.what())); + return {m_url, Status::DiskError, resultCode, exception.what()}; + } + return {m_url, Status::Ok, resultCode, ""}; + } + return {m_url, Status::NetworkError, resultCode, response}; + } + return {m_url, Status::NetworkError, "Unspecified network error"}; +} + +void RemoteFile::DownloadAsync(std::string const & filePath, ResultHandler && handler) const +{ + RemoteFile remoteFile = *this; + GetPlatform().RunTask(Platform::Thread::Network, + [filePath, remoteFile = std::move(remoteFile), handler = std::move(handler)]() + { + auto result = remoteFile.Download(filePath); + if (handler) + handler(std::move(result), filePath); + }); +} +} // namespace platform diff --git a/platform/remote_file.hpp b/platform/remote_file.hpp new file mode 100644 index 0000000000..0f7b80c34c --- /dev/null +++ b/platform/remote_file.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include + +namespace platform +{ +class RemoteFile +{ +public: + enum class Status + { + Ok, + Forbidden, + NotFound, + NetworkError, + DiskError + }; + + struct Result + { + std::string const m_url; + Status const m_status; + int const m_httpCode; + std::string const m_description; + + Result(std::string url, Status status, int httpCode, std::string description) + : m_url(std::move(url)) + , m_status(status) + , m_httpCode(httpCode) + , m_description(std::move(description)) + {} + Result(std::string url, Status status, std::string description) + : Result(std::move(url), status, 0, std::move(description)) + {} + }; + + explicit RemoteFile(std::string url, bool allowRedirection = true); + + Result Download(std::string const & filePath) const; + + using ResultHandler = std::function; + void DownloadAsync(std::string const & filePath, ResultHandler && handler) const; + +private: + std::string const m_url; + bool const m_allowRedirection; +}; +} // namespace platform diff --git a/xcode/map/map.xcodeproj/project.pbxproj b/xcode/map/map.xcodeproj/project.pbxproj index b98c61aa87..e7d9d2f5ed 100644 --- a/xcode/map/map.xcodeproj/project.pbxproj +++ b/xcode/map/map.xcodeproj/project.pbxproj @@ -44,6 +44,8 @@ 454649F21F2728CE00EF4064 /* local_ads_mark.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 454649F01F2728CE00EF4064 /* local_ads_mark.hpp */; }; 45580ABE1E2CBD5E00CD535D /* benchmark_tools.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45580ABC1E2CBD5E00CD535D /* benchmark_tools.cpp */; }; 45580ABF1E2CBD5E00CD535D /* benchmark_tools.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 45580ABD1E2CBD5E00CD535D /* benchmark_tools.hpp */; }; + 4564FA82209497A70043CCFB /* bookmark_catalog.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4564FA80209497A60043CCFB /* bookmark_catalog.hpp */; }; + 4564FA83209497A70043CCFB /* bookmark_catalog.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4564FA81209497A70043CCFB /* bookmark_catalog.cpp */; }; 456E1B3A1F9A3C2A009C32E1 /* search_mark.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 456E1B381F9A3C29009C32E1 /* search_mark.cpp */; }; 456E1B3B1F9A3C2A009C32E1 /* search_mark.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 456E1B391F9A3C2A009C32E1 /* search_mark.hpp */; }; 45A2D9D51F7556EB003310A0 /* user.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 45A2D9D31F7556EB003310A0 /* user.cpp */; }; @@ -211,6 +213,8 @@ 454649F01F2728CE00EF4064 /* local_ads_mark.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = local_ads_mark.hpp; sourceTree = ""; }; 45580ABC1E2CBD5E00CD535D /* benchmark_tools.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = benchmark_tools.cpp; sourceTree = ""; }; 45580ABD1E2CBD5E00CD535D /* benchmark_tools.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = benchmark_tools.hpp; sourceTree = ""; }; + 4564FA80209497A60043CCFB /* bookmark_catalog.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = bookmark_catalog.hpp; sourceTree = ""; }; + 4564FA81209497A70043CCFB /* bookmark_catalog.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = bookmark_catalog.cpp; sourceTree = ""; }; 456E1B381F9A3C29009C32E1 /* search_mark.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = search_mark.cpp; sourceTree = ""; }; 456E1B391F9A3C2A009C32E1 /* search_mark.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = search_mark.hpp; sourceTree = ""; }; 45A2D9D31F7556EB003310A0 /* user.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = user.cpp; sourceTree = ""; }; @@ -526,9 +530,6 @@ 675345BD1A4054AD00A0A8C3 /* map */ = { isa = PBXGroup; children = ( - F6D67CE22063F4980032FD38 /* framework_light.hpp */, - BBFC7E38202D29BF00531BE7 /* user_mark_layer.cpp */, - BBFC7E39202D29BF00531BE7 /* user_mark_layer.hpp */, 675345CB1A4054E800A0A8C3 /* address_finder.cpp */, 45201E921CE4AC90008A4842 /* api_mark_point.cpp */, 34921F611BFA0A6900737D6E /* api_mark_point.hpp */, @@ -539,12 +540,14 @@ 3D4E999E1FB4A6400025B48C /* booking_filter_cache.hpp */, 3D4E99A01FB4A6410025B48C /* booking_filter.cpp */, 3D4E99A11FB4A6410025B48C /* booking_filter.hpp */, - 675345DB1A4054E800A0A8C3 /* bookmark.cpp */, - 675345DC1A4054E800A0A8C3 /* bookmark.hpp */, + 4564FA81209497A70043CCFB /* bookmark_catalog.cpp */, + 4564FA80209497A60043CCFB /* bookmark_catalog.hpp */, BBA014AB2073C783007402E4 /* bookmark_helpers.cpp */, BBA014AC2073C784007402E4 /* bookmark_helpers.hpp */, 675345D91A4054E800A0A8C3 /* bookmark_manager.cpp */, 675345DA1A4054E800A0A8C3 /* bookmark_manager.hpp */, + 675345DB1A4054E800A0A8C3 /* bookmark.cpp */, + 675345DC1A4054E800A0A8C3 /* bookmark.hpp */, 0831F23B200E53600034C365 /* bookmarks_search_params.hpp */, 348AB57A1D7EE0C6009F8301 /* chart_generator.cpp */, 348AB57B1D7EE0C6009F8301 /* chart_generator.hpp */, @@ -558,6 +561,7 @@ 3D4E99801FB462B60025B48C /* everywhere_search_params.hpp */, 675345F31A4054E800A0A8C3 /* feature_vec_model.cpp */, 675345F41A4054E800A0A8C3 /* feature_vec_model.hpp */, + F6D67CE22063F4980032FD38 /* framework_light.hpp */, 675345F51A4054E800A0A8C3 /* framework.cpp */, 675345F61A4054E800A0A8C3 /* framework.hpp */, 675345F71A4054E800A0A8C3 /* ge0_parser.cpp */, @@ -602,10 +606,12 @@ 347B60741DD9926D0050FA24 /* traffic_manager.cpp */, 347B60751DD9926D0050FA24 /* traffic_manager.hpp */, BB4E5F201FCC663700A77250 /* transit */, - 674C385F1BFF3095000D603B /* user_mark.cpp */, - 675346331A4054E800A0A8C3 /* user_mark.hpp */, BBA014B020754996007402E4 /* user_mark_id_storage.cpp */, BBA014AF20754996007402E4 /* user_mark_id_storage.hpp */, + BBFC7E38202D29BF00531BE7 /* user_mark_layer.cpp */, + BBFC7E39202D29BF00531BE7 /* user_mark_layer.hpp */, + 674C385F1BFF3095000D603B /* user_mark.cpp */, + 675346331A4054E800A0A8C3 /* user_mark.hpp */, 45A2D9D31F7556EB003310A0 /* user.cpp */, 45A2D9D41F7556EB003310A0 /* user.hpp */, 3D4E99811FB462B60025B48C /* viewport_search_params.hpp */, @@ -660,6 +666,7 @@ 3D4E99821FB462B60025B48C /* everywhere_search_params.hpp in Headers */, 675346751A4054E800A0A8C3 /* mwm_url.hpp in Headers */, 6753464B1A4054E800A0A8C3 /* bookmark.hpp in Headers */, + 4564FA82209497A70043CCFB /* bookmark_catalog.hpp in Headers */, 3D47B2941F054BC5000828D2 /* taxi_delegate.hpp in Headers */, 3D47B2C81F20EF06000828D2 /* displayed_categories_modifiers.hpp in Headers */, 454523AA202A0068009275C1 /* cloud.hpp in Headers */, @@ -794,6 +801,7 @@ buildActionMask = 2147483647; files = ( 456E1B3A1F9A3C2A009C32E1 /* search_mark.cpp in Sources */, + 4564FA83209497A70043CCFB /* bookmark_catalog.cpp in Sources */, F6B283051C1B03320081957A /* gps_track_filter.cpp in Sources */, 675346481A4054E800A0A8C3 /* bookmark_manager.cpp in Sources */, 45F6EE9F1FB1C77600019892 /* search_api.cpp in Sources */, diff --git a/xcode/platform/platform.xcodeproj/project.pbxproj b/xcode/platform/platform.xcodeproj/project.pbxproj index 530e72cc9f..275b3d1fe9 100644 --- a/xcode/platform/platform.xcodeproj/project.pbxproj +++ b/xcode/platform/platform.xcodeproj/project.pbxproj @@ -25,6 +25,8 @@ 451E32A01F73A8B000964C9F /* secure_storage_ios.mm in Sources */ = {isa = PBXBuildFile; fileRef = 451E329D1F73A8B000964C9F /* secure_storage_ios.mm */; }; 451E32A11F73A8B000964C9F /* secure_storage_qt.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 451E329E1F73A8B000964C9F /* secure_storage_qt.cpp */; }; 451E32A21F73A8B000964C9F /* secure_storage.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 451E329F1F73A8B000964C9F /* secure_storage.hpp */; }; + 4564FA7E2094978D0043CCFB /* remote_file.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 4564FA7C2094978C0043CCFB /* remote_file.hpp */; }; + 4564FA7F2094978D0043CCFB /* remote_file.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4564FA7D2094978D0043CCFB /* remote_file.cpp */; }; 56EB1EDC1C6B6E6C0022D831 /* file_logging.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56EB1ED81C6B6E6C0022D831 /* file_logging.cpp */; }; 56EB1EDD1C6B6E6C0022D831 /* file_logging.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 56EB1ED91C6B6E6C0022D831 /* file_logging.hpp */; }; 56EB1EDE1C6B6E6C0022D831 /* mwm_traits.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 56EB1EDA1C6B6E6C0022D831 /* mwm_traits.cpp */; }; @@ -137,6 +139,8 @@ 451E329D1F73A8B000964C9F /* secure_storage_ios.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = secure_storage_ios.mm; sourceTree = ""; }; 451E329E1F73A8B000964C9F /* secure_storage_qt.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = secure_storage_qt.cpp; sourceTree = ""; }; 451E329F1F73A8B000964C9F /* secure_storage.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = secure_storage.hpp; sourceTree = ""; }; + 4564FA7C2094978C0043CCFB /* remote_file.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = remote_file.hpp; sourceTree = ""; }; + 4564FA7D2094978D0043CCFB /* remote_file.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = remote_file.cpp; sourceTree = ""; }; 56EB1ED81C6B6E6C0022D831 /* file_logging.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_logging.cpp; sourceTree = ""; }; 56EB1ED91C6B6E6C0022D831 /* file_logging.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = file_logging.hpp; sourceTree = ""; }; 56EB1EDA1C6B6E6C0022D831 /* mwm_traits.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = mwm_traits.cpp; sourceTree = ""; }; @@ -404,6 +408,8 @@ 675343A21A3F5D5A00A0A8C3 /* platform.hpp */, 675343A31A3F5D5A00A0A8C3 /* preferred_languages.cpp */, 675343A41A3F5D5A00A0A8C3 /* preferred_languages.hpp */, + 4564FA7D2094978D0043CCFB /* remote_file.cpp */, + 4564FA7C2094978C0043CCFB /* remote_file.hpp */, 3D78157C1F3D8A0A0068B6AC /* safe_callback.hpp */, 451E329D1F73A8B000964C9F /* secure_storage_ios.mm */, 451E329E1F73A8B000964C9F /* secure_storage_qt.cpp */, @@ -483,6 +489,7 @@ 675343B41A3F5D5A00A0A8C3 /* chunks_download_strategy.hpp in Headers */, 67247FFE1C60BD6500EDE56A /* writable_dir_changer.hpp in Headers */, 3D78157D1F3D8A0A0068B6AC /* safe_callback.hpp in Headers */, + 4564FA7E2094978D0043CCFB /* remote_file.hpp in Headers */, 3D78156F1F3A14090068B6AC /* gui_thread.hpp in Headers */, 451E32A21F73A8B000964C9F /* secure_storage.hpp in Headers */, 674125091B4C00CC00A3E828 /* country_defines.hpp in Headers */, @@ -632,6 +639,7 @@ 67AB92DC1B7B3D7300AB5194 /* mwm_version.cpp in Sources */, 67A2526B1BB40E520063F8A8 /* platform_mac.mm in Sources */, 6741250A1B4C00CC00A3E828 /* country_file.cpp in Sources */, + 4564FA7F2094978D0043CCFB /* remote_file.cpp in Sources */, 674125081B4C00CC00A3E828 /* country_defines.cpp in Sources */, EB60B4DC204C130300E4953B /* network_policy_ios.mm in Sources */, 6741250E1B4C00CC00A3E828 /* local_country_file.cpp in Sources */,