From 47fd4d21691b85fdf209a194511c5796d19c2972 Mon Sep 17 00:00:00 2001 From: Arsentiy Milchakov Date: Tue, 28 Apr 2020 17:25:14 +0300 Subject: [PATCH] [guides on map] request guides by viewport is implemented. --- map/CMakeLists.txt | 2 + map/framework.cpp | 5 +- map/guides_manager.cpp | 271 +++++++++++++----------- map/guides_manager.hpp | 35 ++- map/guides_manager_delegate.cpp | 11 + map/guides_manager_delegate.hpp | 15 ++ partners_api/guides_on_map_api.cpp | 5 +- partners_api/guides_on_map_api.hpp | 3 + partners_api/utm.hpp | 5 + xcode/map/map.xcodeproj/project.pbxproj | 8 + 10 files changed, 229 insertions(+), 131 deletions(-) create mode 100644 map/guides_manager_delegate.cpp create mode 100644 map/guides_manager_delegate.hpp diff --git a/map/CMakeLists.txt b/map/CMakeLists.txt index f88b9b7dce..36dc49ff8f 100644 --- a/map/CMakeLists.txt +++ b/map/CMakeLists.txt @@ -75,6 +75,8 @@ set( gps_tracker.hpp guides_manager.cpp guides_manager.hpp + guides_manager_delegate.cpp + guides_manager_delegate.hpp guides_on_map_delegate.cpp guides_on_map_delegate.hpp isolines_manager.cpp diff --git a/map/framework.cpp b/map/framework.cpp index 437d5540a9..d1c3d3021a 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -6,6 +6,7 @@ #include "map/download_on_map_ads_delegate.hpp" #include "map/everywhere_search_params.hpp" #include "map/gps_tracker.hpp" +#include "map/guides_manager_delegate.hpp" #include "map/guides_on_map_delegate.hpp" #include "map/notifications/notification_manager_delegate.hpp" #include "map/notifications/notification_queue.hpp" @@ -525,6 +526,8 @@ Framework::Framework(FrameworkParams const & params) m_isolinesManager.SetEnabled(LoadIsolinesEnabled()); + m_guidesManager.SetDelegate(make_unique(*this)); + m_guidesManager.SetApiDelegate(make_unique(catalogHeadersProvider)); m_guidesManager.SetEnabled(LoadGuidesEnabled()); m_adsEngine = make_unique( @@ -553,8 +556,6 @@ Framework::Framework(FrameworkParams const & params) *m_cityFinder, catalogHeadersProvider)); eye::Eye::Instance().Subscribe(m_promoApi.get()); - m_guidesManager.SetApiDelegate(make_unique(catalogHeadersProvider)); - // Clean the no longer used key from old devices. // Remove this line after April 2020 (assuming the majority of devices // will have updated by then). diff --git a/map/guides_manager.cpp b/map/guides_manager.cpp index 4d1449e308..1ace29ec4d 100644 --- a/map/guides_manager.cpp +++ b/map/guides_manager.cpp @@ -1,18 +1,46 @@ #include "map/guides_manager.hpp" +#include "map/bookmark_catalog.hpp" + +#include "partners_api/utm.hpp" + +#include "drape_frontend/visual_params.hpp" + +#include "platform/preferred_languages.hpp" + +#include "private.h" + +#include + +namespace +{ +auto constexpr kRequestAttemptsCount = 3; +} // namespace + GuidesManager::GuidesState GuidesManager::GetState() const { return m_state; } -void GuidesManager::SetStateListener(GuidesStateChangedFn const & onStateChangedFn) +void GuidesManager::SetStateListener(GuidesStateChangedFn const & onStateChanged) { - m_onStateChangedFn = onStateChangedFn; + m_onStateChanged = onStateChanged; } void GuidesManager::UpdateViewport(ScreenBase const & screen) { - // TODO: Implement. + auto const zoom = df::GetDrawTileScale(screen); + // TODO(a): to implement correct way to filter out same rects. + if (m_currentRect.EqualDxDy(screen.GlobalRect(), 1e-4) && m_zoom == zoom) + return; + + m_currentRect = screen.GlobalRect(); + m_zoom = zoom; + + if (m_state == GuidesState::Disabled || m_state == GuidesState::FatalNetworkError) + return; + + RequestGuides(m_currentRect, m_zoom); } void GuidesManager::Invalidate() @@ -20,9 +48,24 @@ void GuidesManager::Invalidate() // TODO: Implement. } +void GuidesManager::Reconnect() +{ + if (m_state != GuidesState::FatalNetworkError) + return; + + ChangeState(GuidesState::Enabled); + RequestGuides(m_currentRect, m_zoom); +} + void GuidesManager::SetEnabled(bool enabled) { - ChangeState(enabled ? GuidesState::Enabled : GuidesState::Disabled); + auto const newState = enabled ? GuidesState::Enabled : GuidesState::Disabled; + if (newState == m_state) + return; + + Clear(); + ChangeState(newState); + RequestGuides(m_currentRect, m_zoom); } bool GuidesManager::IsEnabled() const @@ -35,13 +78,59 @@ void GuidesManager::ChangeState(GuidesState newState) if (m_state == newState) return; m_state = newState; - if (m_onStateChangedFn != nullptr) - m_onStateChangedFn(newState); + if (m_onStateChanged != nullptr) + m_onStateChanged(newState); +} + +void GuidesManager::RequestGuides(m2::AnyRectD const & rect, int zoom) +{ + auto const requestNumber = ++m_requestCounter; + m_api.GetGuidesOnMap( + rect, zoom, + [this](guides_on_map::GuidesOnMap const & guides) { + if (m_state == GuidesState::Disabled) + return; + + m_guides = guides; + m_errorRequestsCount = 0; + + if (!m_guides.empty()) + ChangeState(GuidesState::Enabled); + else + ChangeState(GuidesState::NoData); + + m_onGalleryChanged(true); + }, + [this, requestNumber, zoom]() mutable { + if (m_state == GuidesState::Disabled || m_state == GuidesState::FatalNetworkError) + return; + + if (++m_errorRequestsCount >= kRequestAttemptsCount) + { + Clear(); + ChangeState(GuidesState::FatalNetworkError); + } + else + { + ChangeState(GuidesState::NetworkError); + } + + // Re-request only when no additional requests enqueued. + if (requestNumber == m_requestCounter) + RequestGuides(m_currentRect, zoom); + }); +} + +void GuidesManager::Clear() +{ + m_activeGuide.clear(); + m_guides.clear(); + m_errorRequestsCount = 0; } GuidesManager::GuidesGallery GuidesManager::GetGallery() const { - // Dummy gallery for debug only. + // Dummy gallery hardcode for debug only. GuidesGallery gallery; { GuidesGallery::Item item; @@ -56,7 +145,7 @@ GuidesManager::GuidesGallery GuidesManager::GetGallery() const item.m_cityParams.m_bookmarksCount = 32; item.m_cityParams.m_trackIsAvailable = false; - gallery.m_items.push_back(item); + gallery.m_items.emplace_back(std::move(item)); } { @@ -72,7 +161,7 @@ GuidesManager::GuidesGallery GuidesManager::GetGallery() const item.m_cityParams.m_bookmarksCount = 31; item.m_cityParams.m_trackIsAvailable = true; - gallery.m_items.push_back(item); + gallery.m_items.emplace_back(std::move(item)); } { @@ -88,140 +177,76 @@ GuidesManager::GuidesGallery GuidesManager::GetGallery() const item.m_outdoorsParams.m_distance = 24100; item.m_outdoorsParams.m_duration = 749246; - gallery.m_items.push_back(item); + gallery.m_items.emplace_back(std::move(item)); } + for (auto const & guide : m_guides) { - GuidesGallery::Item item; - item.m_guideId = "048f4c49-ee80-463f-8513-e57ade2303ee"; - item.m_url = "https://routes.maps.me/en/v3/mobilefront/route/048f4c49-ee80-463f-8513-e57ade2303ee"; - item.m_imageUrl = "https://storage.maps.me/bookmarks_catalogue/" - "002dc2ae-7b5c-4d3c-88bc-7c7ba109d0e8.jpg?t=1584470956.009026"; - item.m_title = "Moscow by The Village"; - item.m_subTitle = "awesome city guide"; - item.m_type = GuidesGallery::Item::Type::City; - item.m_downloaded = false; - item.m_cityParams.m_bookmarksCount = 32; - item.m_cityParams.m_trackIsAvailable = false; + if (guide.m_outdoorCount + guide.m_sightsCount != 1) + continue; - gallery.m_items.push_back(item); + auto const & info = guide.m_guideInfo; + + GuidesGallery::Item item; + item.m_guideId = info.m_id; + + auto url = url::Join(BOOKMARKS_CATALOG_FRONT_URL, languages::GetCurrentNorm(), "v3/mobilefront", + info.m_id); + InjectUTM(url, UTM::GuidesOnMapGallery); + item.m_url = std::move(url); + item.m_imageUrl = info.m_imageUrl; + item.m_title = info.m_name; + item.m_downloaded = m_delegate->IsGuideDownloaded(info.m_id); + + if (guide.m_sightsCount == 1) + { + item.m_type = GuidesGallery::Item::Type::City; + item.m_cityParams.m_bookmarksCount = guide.m_guideInfo.m_bookmarksCount; + item.m_cityParams.m_trackIsAvailable = guide.m_guideInfo.m_hasTrack; + item.m_subTitle = "TODO(a): to add correct value"; + } + else + { + item.m_type = GuidesGallery::Item::Type::Outdoor; + item.m_outdoorsParams.m_duration = guide.m_guideInfo.m_tourDuration; + item.m_outdoorsParams.m_distance = guide.m_guideInfo.m_tracksLength; + item.m_outdoorsParams.m_ascent = guide.m_guideInfo.m_ascent; + item.m_subTitle = guide.m_guideInfo.m_tag; + } + + gallery.m_items.emplace_back(std::move(item)); } + // Dummy, for debug only. + while (gallery.m_items.size() < 10) { - GuidesGallery::Item item; - item.m_guideId = "e2d448eb-7fa4-4fab-93e7-ef0fea91cfff"; - item.m_url = "https://routes.maps.me/en/v3/mobilefront/route/e2d448eb-7fa4-4fab-93e7-ef0fea91cfff"; - item.m_imageUrl = "https://storage.maps.me/bookmarks_catalogue/" - "002dc2ae-7b5c-4d3c-88bc-7c7ba109d0e8.jpg?t=1584470956.009026"; - item.m_title = "Riga City Tour"; - item.m_subTitle = "awesome city guide"; - item.m_type = GuidesGallery::Item::Type::City; - item.m_downloaded = false; - item.m_cityParams.m_bookmarksCount = 31; - item.m_cityParams.m_trackIsAvailable = true; - - gallery.m_items.push_back(item); - } - - { - GuidesGallery::Item item; - item.m_guideId = "d26a6662-20a3-432c-a357-c9cb3cce6d57"; - item.m_url = "https://routes.maps.me/en/v3/mobilefront/route/d26a6662-20a3-432c-a357-c9cb3cce6d57"; - item.m_imageUrl = "https://img.oastatic.com/img2/1966324/834x417s/t.jpg"; - item.m_title = "Klassik trifft Romantik"; - item.m_subTitle = "Hiking / Trekking"; - item.m_type = GuidesGallery::Item::Type::Outdoor; - item.m_downloaded = true; - item.m_outdoorsParams.m_ascent = 400; - item.m_outdoorsParams.m_distance = 24100; - item.m_outdoorsParams.m_duration = 749246; - - gallery.m_items.push_back(item); - } - { - GuidesGallery::Item item; - item.m_guideId = "048f4c49-ee80-463f-8513-e57ade2303ee"; - item.m_url = "https://routes.maps.me/en/v3/mobilefront/route/048f4c49-ee80-463f-8513-e57ade2303ee"; - item.m_imageUrl = "https://storage.maps.me/bookmarks_catalogue/" - "002dc2ae-7b5c-4d3c-88bc-7c7ba109d0e8.jpg?t=1584470956.009026"; - item.m_title = "Moscow by The Village"; - item.m_subTitle = "awesome city guide"; - item.m_type = GuidesGallery::Item::Type::City; - item.m_downloaded = false; - item.m_cityParams.m_bookmarksCount = 32; - item.m_cityParams.m_trackIsAvailable = false; - - gallery.m_items.push_back(item); - } - - { - GuidesGallery::Item item; - item.m_guideId = "e2d448eb-7fa4-4fab-93e7-ef0fea91cfff"; - item.m_url = "https://routes.maps.me/en/v3/mobilefront/route/e2d448eb-7fa4-4fab-93e7-ef0fea91cfff"; - item.m_imageUrl = "https://storage.maps.me/bookmarks_catalogue/" - "002dc2ae-7b5c-4d3c-88bc-7c7ba109d0e8.jpg?t=1584470956.009026"; - item.m_title = "Riga City Tour"; - item.m_subTitle = "awesome city guide"; - item.m_type = GuidesGallery::Item::Type::City; - item.m_downloaded = false; - item.m_cityParams.m_bookmarksCount = 31; - item.m_cityParams.m_trackIsAvailable = true; - - gallery.m_items.push_back(item); - } - - { - GuidesGallery::Item item; - item.m_guideId = "d26a6662-20a3-432c-a357-c9cb3cce6d57"; - item.m_url = "https://routes.maps.me/en/v3/mobilefront/route/d26a6662-20a3-432c-a357-c9cb3cce6d57"; - item.m_imageUrl = "https://img.oastatic.com/img2/1966324/834x417s/t.jpg"; - item.m_title = "Klassik trifft Romantik"; - item.m_subTitle = "Hiking / Trekking"; - item.m_type = GuidesGallery::Item::Type::Outdoor; - item.m_downloaded = false; - item.m_outdoorsParams.m_ascent = 400; - item.m_outdoorsParams.m_distance = 24100; - item.m_outdoorsParams.m_duration = 749246; - - gallery.m_items.push_back(item); - } - - { - GuidesGallery::Item item; - item.m_guideId = "d26a6662-20a3-432c-a357-c9cb3cce6d57"; - item.m_url = "https://routes.maps.me/en/v3/mobilefront/route/d26a6662-20a3-432c-a357-c9cb3cce6d57"; - item.m_imageUrl = "https://img.oastatic.com/img2/1966324/834x417s/t.jpg"; - item.m_title = "Klassik trifft Romantik"; - item.m_subTitle = "Hiking / Trekking"; - item.m_type = GuidesGallery::Item::Type::Outdoor; - item.m_downloaded = false; - item.m_outdoorsParams.m_ascent = 400; - item.m_outdoorsParams.m_distance = 24100; - item.m_outdoorsParams.m_duration = 749246; - - gallery.m_items.push_back(item); + std::copy(gallery.m_items.begin(), gallery.m_items.end(), std::back_inserter(gallery.m_items)); } return gallery; } -std::string GuidesManager::GetActiveGuide() const -{ - // Dummy active guide for debug only. - return "048f4c49-ee80-463f-8513-e57ade2303ee"; -} +std::string GuidesManager::GetActiveGuide() const { return m_activeGuide; } void GuidesManager::SetActiveGuide(std::string const & guideId) { - // TODO: Implement. + if (m_activeGuide == guideId) + return; + + m_activeGuide = guideId; } -void GuidesManager::SetGalleryListener(GuidesGalleryChangedFn const & onGalleryChangedFn) +void GuidesManager::SetGalleryListener(GuidesGalleryChangedFn const & onGalleryChanged) { - m_onGalleryChangedFn = onGalleryChangedFn; + m_onGalleryChanged = onGalleryChanged; } -void GuidesManager::SetApiDelegate(std::unique_ptr apiDelegate) +void GuidesManager::SetDelegate(std::unique_ptr delegate) +{ + m_delegate = std::move(delegate); +} + +void GuidesManager::SetApiDelegate(std::unique_ptr apiDelegate) { m_api.SetDelegate(std::move(apiDelegate)); } diff --git a/map/guides_manager.hpp b/map/guides_manager.hpp index aaf20aad2d..d79c13c8f7 100644 --- a/map/guides_manager.hpp +++ b/map/guides_manager.hpp @@ -18,10 +18,22 @@ class GuidesManager final public: enum class GuidesState { + // Layer is disabled. Disabled, + // Layer is enabled and no errors after last guide request are found. Enabled, + // No data into requested rect. NoData, - NetworkError + // Attempt to request guides is failed. + NetworkError, + // Several attempts to request guides are failed. + FatalNetworkError, + }; + + class Delegate + { + public: + virtual bool IsGuideDownloaded(std::string const & guideId) const = 0; }; struct GuidesGallery @@ -71,6 +83,7 @@ public: void UpdateViewport(ScreenBase const & screen); void Invalidate(); + void Reconnect(); void SetEnabled(bool enabled); bool IsEnabled() const; @@ -82,16 +95,30 @@ public: using GuidesGalleryChangedFn = std::function; void SetGalleryListener(GuidesGalleryChangedFn const & onGalleryChangedFn); - void SetApiDelegate(std::unique_ptr apiDelegate); + void SetDelegate(std::unique_ptr delegate); + void SetApiDelegate(std::unique_ptr apiDelegate); private: void ChangeState(GuidesState newState); + void RequestGuides(m2::AnyRectD const & rect, int zoom); + void Clear(); GuidesState m_state = GuidesState::Disabled; - GuidesStateChangedFn m_onStateChangedFn; - GuidesGalleryChangedFn m_onGalleryChangedFn; + GuidesStateChangedFn m_onStateChanged; + GuidesGalleryChangedFn m_onGalleryChanged; + + int m_zoom = 0; + m2::AnyRectD m_currentRect; + + uint64_t m_requestCounter = 0; + uint8_t m_errorRequestsCount = 0; guides_on_map::Api m_api; + guides_on_map::GuidesOnMap m_guides; + // Initial value is dummy for debug only. + std::string m_activeGuide = "048f4c49-ee80-463f-8513-e57ade2303ee"; + + std::unique_ptr m_delegate; }; std::string DebugPrint(GuidesManager::GuidesState state); diff --git a/map/guides_manager_delegate.cpp b/map/guides_manager_delegate.cpp new file mode 100644 index 0000000000..f99001b8cc --- /dev/null +++ b/map/guides_manager_delegate.cpp @@ -0,0 +1,11 @@ +#include "map/guides_manager_delegate.hpp" + +GuidesManagerDelegate::GuidesManagerDelegate(Framework const & framework) + : m_framework(framework) +{ +} + +bool GuidesManagerDelegate::IsGuideDownloaded(std::string const & guideId) const +{ + return m_framework.GetBookmarkManager().GetCatalog().HasDownloaded(guideId); +} diff --git a/map/guides_manager_delegate.hpp b/map/guides_manager_delegate.hpp new file mode 100644 index 0000000000..0d2887ceb0 --- /dev/null +++ b/map/guides_manager_delegate.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "map/framework.hpp" +#include "map/guides_manager.hpp" + +class GuidesManagerDelegate : public GuidesManager::Delegate +{ +public: + explicit GuidesManagerDelegate(Framework const & framework); + + bool IsGuideDownloaded(std::string const & guideId) const override; + +private: + Framework const & m_framework; +}; diff --git a/partners_api/guides_on_map_api.cpp b/partners_api/guides_on_map_api.cpp index 1513d4f9c8..daa1966088 100644 --- a/partners_api/guides_on_map_api.cpp +++ b/partners_api/guides_on_map_api.cpp @@ -57,6 +57,8 @@ void ParseGallery(std::string const & src, guides_on_map::GuidesOnMap & result) FromJSONObject(extraObj, "has_track", info.m_hasTrack); FromJSONObjectOptionalField(extraObj, "tracks_length", info.m_tracksLength); FromJSONObjectOptionalField(extraObj, "tour_duration", info.m_tourDuration); + // Server returns duration in minutes, so convert value to small units. + info.m_tourDuration *= 60; FromJSONObjectOptionalField(extraObj, "ascent", info.m_ascent); } @@ -144,8 +146,7 @@ void Api::GetGuidesOnMap(m2::AnyRectD const & viewport, uint8_t zoomLevel, catch (Json::Exception const & e) { LOG(LERROR, (e.Msg(), httpResult)); - onError(); - return; + result.clear(); } onSuccess(result); diff --git a/partners_api/guides_on_map_api.hpp b/partners_api/guides_on_map_api.hpp index d7de7b6a26..2d10a12c0c 100644 --- a/partners_api/guides_on_map_api.hpp +++ b/partners_api/guides_on_map_api.hpp @@ -25,8 +25,11 @@ struct GuidesNode std::string m_tag; uint32_t m_bookmarksCount = 0; bool m_hasTrack = false; + // Length in meters. double m_tracksLength = 0.0; + // Duration in seconds. double m_tourDuration = 0.0; + // Ascent in meters. int32_t m_ascent = 0; }; diff --git a/partners_api/utm.hpp b/partners_api/utm.hpp index 5656b8c263..1ad8db9347 100644 --- a/partners_api/utm.hpp +++ b/partners_api/utm.hpp @@ -19,6 +19,7 @@ enum class UTM : uint8_t DiscoverCatalogOnboarding, FreeSamplesOnboading, OutdoorPlacepageGallery, + GuidesOnMapGallery, }; enum class UTMContent : uint8_t @@ -82,6 +83,10 @@ inline std::string InjectUTM(std::string const & url, UTM utm) params.emplace_back("utm_medium", "gallery"); params.emplace_back("utm_campaign", "outdoor_placepage_gallery"); break; + case UTM::GuidesOnMapGallery: + params.emplace_back("utm_medium", "gallery"); + params.emplace_back("utm_campaign", "map"); + break; case UTM::None: return url; } diff --git a/xcode/map/map.xcodeproj/project.pbxproj b/xcode/map/map.xcodeproj/project.pbxproj index fb27c7a39b..971d25009e 100644 --- a/xcode/map/map.xcodeproj/project.pbxproj +++ b/xcode/map/map.xcodeproj/project.pbxproj @@ -26,6 +26,8 @@ 39E3C60423312BA800FB0C37 /* features_fetcher.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 39E3C60223312BA800FB0C37 /* features_fetcher.cpp */; }; 3D035CA82451960400C21B57 /* guides_on_map_delegate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D035CA62451960400C21B57 /* guides_on_map_delegate.cpp */; }; 3D035CA92451960400C21B57 /* guides_on_map_delegate.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D035CA72451960400C21B57 /* guides_on_map_delegate.hpp */; }; + 3D035CAC24575A5E00C21B57 /* guides_manager_delegate.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3D035CAA24575A5E00C21B57 /* guides_manager_delegate.cpp */; }; + 3D035CAD24575A5E00C21B57 /* guides_manager_delegate.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 3D035CAB24575A5E00C21B57 /* guides_manager_delegate.hpp */; }; 3D0AEAFC1FBB0FF400AD042B /* libgenerator_tests_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D0AEAFF1FBB0FF400AD042B /* libgenerator_tests_support.a */; }; 3D0AEAFE1FBB0FF400AD042B /* libsearch_tests_support.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D0AEB011FBB0FF400AD042B /* libsearch_tests_support.a */; }; 3D0BBAE523F3EEEF00A50354 /* libweb_api.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D0BBAE423F3EEEF00A50354 /* libweb_api.a */; }; @@ -321,6 +323,8 @@ 39E3C60223312BA800FB0C37 /* features_fetcher.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = features_fetcher.cpp; sourceTree = ""; }; 3D035CA62451960400C21B57 /* guides_on_map_delegate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = guides_on_map_delegate.cpp; sourceTree = ""; }; 3D035CA72451960400C21B57 /* guides_on_map_delegate.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = guides_on_map_delegate.hpp; sourceTree = ""; }; + 3D035CAA24575A5E00C21B57 /* guides_manager_delegate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = guides_manager_delegate.cpp; sourceTree = ""; }; + 3D035CAB24575A5E00C21B57 /* guides_manager_delegate.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = guides_manager_delegate.hpp; sourceTree = ""; }; 3D0AEAFF1FBB0FF400AD042B /* libgenerator_tests_support.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libgenerator_tests_support.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3D0AEB001FBB0FF400AD042B /* libindexer_tests_support.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libindexer_tests_support.a; sourceTree = BUILT_PRODUCTS_DIR; }; 3D0AEB011FBB0FF400AD042B /* libsearch_tests_support.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libsearch_tests_support.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -904,6 +908,8 @@ 675345BD1A4054AD00A0A8C3 /* map */ = { isa = PBXGroup; children = ( + 3D035CAA24575A5E00C21B57 /* guides_manager_delegate.cpp */, + 3D035CAB24575A5E00C21B57 /* guides_manager_delegate.hpp */, 3D035CA62451960400C21B57 /* guides_on_map_delegate.cpp */, 3D035CA72451960400C21B57 /* guides_on_map_delegate.hpp */, BBBB5ED024460BF9000CBFF4 /* guides_manager.cpp */, @@ -1103,6 +1109,7 @@ 3DF528D8237DC82E000ED0D5 /* position_provider.hpp in Headers */, BBFC7E3B202D29C000531BE7 /* user_mark_layer.hpp in Headers */, 3D4E99A51FB4A6410025B48C /* booking_filter.hpp in Headers */, + 3D035CAD24575A5E00C21B57 /* guides_manager_delegate.hpp in Headers */, 56C116612090E5670068BBC0 /* extrapolator.hpp in Headers */, 3DD692B3220AD240001C3C62 /* caching_address_getter.hpp in Headers */, 3D62CBDA20FF6C8B00E7BB6E /* discovery_search.hpp in Headers */, @@ -1322,6 +1329,7 @@ 3DA5723320C195ED007BDE27 /* viewport_search_callback.cpp in Sources */, 3D035CA82451960400C21B57 /* guides_on_map_delegate.cpp in Sources */, 675346641A4054E800A0A8C3 /* framework.cpp in Sources */, + 3D035CAC24575A5E00C21B57 /* guides_manager_delegate.cpp in Sources */, BBBB5ED224460BFA000CBFF4 /* guides_manager.cpp in Sources */, 40ACC79623191C2600238E21 /* countries_names_tests.cpp in Sources */, 3DBD7B92240D523400ED9FE8 /* elevation_info.cpp in Sources */,