Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--coding/file_container.cpp8
-rw-r--r--coding/file_container.hpp1
-rw-r--r--drape_frontend/backend_renderer.cpp18
-rw-r--r--drape_frontend/drape_engine.cpp18
-rw-r--r--drape_frontend/drape_engine.hpp4
-rw-r--r--drape_frontend/message.hpp3
-rw-r--r--drape_frontend/message_subclasses.hpp23
-rwxr-xr-xdrape_frontend/read_manager.cpp34
-rwxr-xr-xdrape_frontend/read_manager.hpp12
-rw-r--r--map/CMakeLists.txt2
-rw-r--r--map/framework.cpp21
-rw-r--r--map/framework.hpp8
-rw-r--r--map/hla_manager.cpp350
-rw-r--r--map/hla_manager.hpp81
-rw-r--r--map/map.pro2
16 files changed, 556 insertions, 30 deletions
diff --git a/.gitignore b/.gitignore
index d8f2b9c31a..bbd9c04cff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,6 +16,7 @@ data/*.mwm.osm2ft
data/*.mwm.routing
data/*.mwmmeta
data/[!W]*.mwm
+data/hla*.dat
# Compiled Python
*.pyc
diff --git a/coding/file_container.cpp b/coding/file_container.cpp
index 2598fa299a..69d4a698f7 100644
--- a/coding/file_container.cpp
+++ b/coding/file_container.cpp
@@ -440,7 +440,13 @@ void FilesContainerW::Write(ModelReaderPtr reader, Tag const & tag)
void FilesContainerW::Write(vector<char> const & buffer, Tag const & tag)
{
if (!buffer.empty())
- GetWriter(tag).Write(&buffer[0], buffer.size());
+ GetWriter(tag).Write(buffer.data(), buffer.size());
+}
+
+void FilesContainerW::Write(vector<uint8_t> const & buffer, Tag const & tag)
+{
+ if (!buffer.empty())
+ GetWriter(tag).Write(buffer.data(), buffer.size());
}
void FilesContainerW::Finish()
diff --git a/coding/file_container.hpp b/coding/file_container.hpp
index 01e3fd8c0e..1307e8e8cd 100644
--- a/coding/file_container.hpp
+++ b/coding/file_container.hpp
@@ -238,6 +238,7 @@ public:
void Write(string const & fPath, Tag const & tag);
void Write(ModelReaderPtr reader, Tag const & tag);
void Write(vector<char> const & buffer, Tag const & tag);
+ void Write(vector<uint8_t> const & buffer, Tag const & tag);
void Finish();
diff --git a/drape_frontend/backend_renderer.cpp b/drape_frontend/backend_renderer.cpp
index 51d8415a1c..3c5d02f877 100644
--- a/drape_frontend/backend_renderer.cpp
+++ b/drape_frontend/backend_renderer.cpp
@@ -428,9 +428,9 @@ void BackendRenderer::AcceptMessage(ref_ptr<Message> message)
break;
}
- case Message::SetCustomSymbols:
+ case Message::AddCustomSymbols:
{
- ref_ptr<SetCustomSymbolsMessage> msg = message;
+ ref_ptr<AddCustomSymbolsMessage> msg = message;
CustomSymbols customSymbols = msg->AcceptSymbols();
std::vector<FeatureID> features;
for (auto const & symbol : customSymbols)
@@ -442,6 +442,20 @@ void BackendRenderer::AcceptMessage(ref_ptr<Message> message)
break;
}
+ case Message::RemoveCustomSymbols:
+ {
+ ref_ptr<RemoveCustomSymbolsMessage> msg = message;
+ std::vector<FeatureID> leftoverIds;
+ if (msg->NeedRemoveAll())
+ m_readManager->RemoveAllCustomSymbols();
+ else
+ m_readManager->RemoveCustomSymbols(msg->GetMwmId(), leftoverIds);
+ m_commutator->PostMessage(ThreadsCommutator::RenderThread,
+ make_unique_dp<UpdateCustomSymbolsMessage>(std::move(leftoverIds)),
+ MessagePriority::Normal);
+ break;
+ }
+
default:
ASSERT(false, ());
break;
diff --git a/drape_frontend/drape_engine.cpp b/drape_frontend/drape_engine.cpp
index 8b02efd2b1..e359f731e8 100644
--- a/drape_frontend/drape_engine.cpp
+++ b/drape_frontend/drape_engine.cpp
@@ -580,10 +580,24 @@ void DrapeEngine::RunScenario(ScenarioManager::ScenarioData && scenarioData,
manager->RunScenario(move(scenarioData), onStartFn, onFinishFn);
}
-void DrapeEngine::SetCustomSymbols(CustomSymbols && symbols)
+void DrapeEngine::AddCustomSymbols(CustomSymbols && symbols)
{
m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
- make_unique_dp<SetCustomSymbolsMessage>(std::move(symbols)),
+ make_unique_dp<AddCustomSymbolsMessage>(std::move(symbols)),
+ MessagePriority::Normal);
+}
+
+void DrapeEngine::RemoveCustomSymbols(MwmSet::MwmId const & mwmId)
+{
+ m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
+ make_unique_dp<RemoveCustomSymbolsMessage>(mwmId),
+ MessagePriority::Normal);
+}
+
+void DrapeEngine::RemoveAllCustomSymbols()
+{
+ m_threadCommutator->PostMessage(ThreadsCommutator::ResourceUploadThread,
+ make_unique_dp<RemoveCustomSymbolsMessage>(),
MessagePriority::Normal);
}
diff --git a/drape_frontend/drape_engine.hpp b/drape_frontend/drape_engine.hpp
index 6e5075866b..f7e346f93b 100644
--- a/drape_frontend/drape_engine.hpp
+++ b/drape_frontend/drape_engine.hpp
@@ -192,7 +192,9 @@ public:
ScenarioManager::ScenarioCallback const & onStartFn,
ScenarioManager::ScenarioCallback const & onFinishFn);
- void SetCustomSymbols(CustomSymbols && symbols);
+ void AddCustomSymbols(CustomSymbols && symbols);
+ void RemoveCustomSymbols(MwmSet::MwmId const & mwmId);
+ void RemoveAllCustomSymbols();
private:
void AddUserEvent(drape_ptr<UserEvent> && e);
diff --git a/drape_frontend/message.hpp b/drape_frontend/message.hpp
index 7c615e1674..69ee6c4172 100644
--- a/drape_frontend/message.hpp
+++ b/drape_frontend/message.hpp
@@ -75,7 +75,8 @@ public:
DrapeApiAddLines,
DrapeApiRemove,
DrapeApiFlush,
- SetCustomSymbols,
+ AddCustomSymbols,
+ RemoveCustomSymbols,
UpdateCustomSymbols,
};
diff --git a/drape_frontend/message_subclasses.hpp b/drape_frontend/message_subclasses.hpp
index 1052847afd..ec2388fb1e 100644
--- a/drape_frontend/message_subclasses.hpp
+++ b/drape_frontend/message_subclasses.hpp
@@ -1151,14 +1151,14 @@ private:
TProperties m_properties;
};
-class SetCustomSymbolsMessage : public Message
+class AddCustomSymbolsMessage : public Message
{
public:
- explicit SetCustomSymbolsMessage(CustomSymbols && symbols)
+ explicit AddCustomSymbolsMessage(CustomSymbols && symbols)
: m_symbols(std::move(symbols))
{}
- Type GetType() const override { return Message::SetCustomSymbols; }
+ Type GetType() const override { return Message::AddCustomSymbols; }
CustomSymbols && AcceptSymbols() { return std::move(m_symbols); }
@@ -1166,6 +1166,23 @@ private:
CustomSymbols m_symbols;
};
+class RemoveCustomSymbolsMessage : public Message
+{
+public:
+ RemoveCustomSymbolsMessage() = default;
+ explicit RemoveCustomSymbolsMessage(MwmSet::MwmId const & mwmId)
+ : m_mwmId(mwmId), m_removeAll(false)
+ {}
+
+ Type GetType() const override { return Message::RemoveCustomSymbols; }
+ bool NeedRemoveAll() const { return m_removeAll; }
+ MwmSet::MwmId const & GetMwmId() const { return m_mwmId; }
+
+private:
+ MwmSet::MwmId m_mwmId;
+ bool m_removeAll = true;
+};
+
class UpdateCustomSymbolsMessage : public Message
{
public:
diff --git a/drape_frontend/read_manager.cpp b/drape_frontend/read_manager.cpp
index 3f4205edb6..a944a675a6 100755
--- a/drape_frontend/read_manager.cpp
+++ b/drape_frontend/read_manager.cpp
@@ -280,18 +280,36 @@ void ReadManager::SetTrafficEnabled(bool trafficEnabled)
}
}
-void ReadManager::UpdateCustomSymbols(CustomSymbols && symbols)
+void ReadManager::UpdateCustomSymbols(CustomSymbols const & symbols)
{
- auto customSymbolsContext = std::make_shared<CustomSymbolsContext>(std::move(symbols));
+ CustomSymbols currentSymbols = m_customSymbolsContext ? m_customSymbolsContext->m_symbols :
+ CustomSymbols();
+ for (auto const & s : symbols)
+ currentSymbols[s.first] = s.second;
+ m_customSymbolsContext = std::make_shared<CustomSymbolsContext>(std::move(currentSymbols));
+}
-#if !defined(OMIM_OS_LINUX)
- std::atomic_exchange(&m_customSymbolsContext, customSymbolsContext);
-#else
+void ReadManager::RemoveCustomSymbols(MwmSet::MwmId const & mwmId, std::vector<FeatureID> & leftoverIds)
+{
+ if (!m_customSymbolsContext)
+ return;
+
+ CustomSymbols currentSymbols;
+ leftoverIds.reserve(m_customSymbolsContext->m_symbols.size());
+ for (auto const & s : m_customSymbolsContext->m_symbols)
{
- lock_guard<mutex> guard(m_customSymbolsContextMutex);
- std::swap(m_customSymbolsContext, customSymbolsContext);
+ if (s.first.m_mwmId != mwmId)
+ {
+ currentSymbols.insert(std::make_pair(s.first, s.second));
+ leftoverIds.push_back(s.first);
+ }
}
-#endif
+ m_customSymbolsContext = std::make_shared<CustomSymbolsContext>(std::move(currentSymbols));
+}
+
+void ReadManager::RemoveAllCustomSymbols()
+{
+ m_customSymbolsContext = std::make_shared<CustomSymbolsContext>(CustomSymbols());
}
} // namespace df
diff --git a/drape_frontend/read_manager.hpp b/drape_frontend/read_manager.hpp
index e4d3903d8c..6660e72a23 100755
--- a/drape_frontend/read_manager.hpp
+++ b/drape_frontend/read_manager.hpp
@@ -43,7 +43,9 @@ public:
void SetTrafficEnabled(bool trafficEnabled);
- void UpdateCustomSymbols(CustomSymbols && symbols);
+ void UpdateCustomSymbols(CustomSymbols const & symbols);
+ void RemoveCustomSymbols(MwmSet::MwmId const & mwmId, std::vector<FeatureID> & leftoverIds);
+ void RemoveAllCustomSymbols();
static uint32_t ReadCount();
@@ -53,7 +55,6 @@ private:
void PushTaskBackForTileKey(TileKey const & tileKey, ref_ptr<dp::TextureManager> texMng);
-private:
ref_ptr<ThreadsCommutator> m_commutator;
MapDataProvider & m_model;
@@ -88,13 +89,6 @@ private:
CustomSymbolsContextPtr m_customSymbolsContext;
- // TODO (@y): unfortunately on Debian Jessie libstdc++ does not
- // support atomic_exchange for shared pointers, so mutex is used
- // instead. Get rid of this as soon as new libstdc++ is released.
-#if defined(OMIM_OS_LINUX)
- mutex m_customSymbolsContextMutex;
-#endif
-
void CancelTileInfo(shared_ptr<TileInfo> const & tileToCancel);
void ClearTileInfo(shared_ptr<TileInfo> const & tileToClear);
void IncreaseCounter(int value);
diff --git a/map/CMakeLists.txt b/map/CMakeLists.txt
index c62a9dccf9..cd8d7d8509 100644
--- a/map/CMakeLists.txt
+++ b/map/CMakeLists.txt
@@ -43,6 +43,8 @@ set(
gps_track.hpp
gps_tracker.cpp
gps_tracker.hpp
+ hla_manager.cpp
+ hla_manager.hpp
mwm_url.cpp
mwm_url.hpp
place_page_info.cpp
diff --git a/map/framework.cpp b/map/framework.cpp
index 1e86acd3db..49100905dd 100644
--- a/map/framework.cpp
+++ b/map/framework.cpp
@@ -276,6 +276,11 @@ TrafficManager & Framework::GetTrafficManager()
return m_trafficManager;
}
+HLAManager & Framework::GetHLAManager()
+{
+ return m_hlaManager;
+}
+
bool Framework::IsTrackingReporterEnabled() const
{
if (m_currentRouterType != routing::RouterType::Vehicle)
@@ -309,6 +314,7 @@ void Framework::OnViewportChanged(ScreenBase const & screen)
m_currentModelView = screen;
m_trafficManager.UpdateViewport(m_currentModelView);
+ m_hlaManager.UpdateViewport(m_currentModelView);
if (m_viewportChanged != nullptr)
m_viewportChanged(screen);
@@ -393,6 +399,7 @@ Framework::Framework()
, m_trafficManager(bind(&Framework::GetMwmsByRect, this, _1), kMaxTrafficCacheSizeBytes,
// Note. |m_routingSession| should be declared before |m_trafficManager|.
m_routingSession)
+ , m_hlaManager(bind(&Framework::GetMwmsByRect, this, _1), bind(&Framework::GetMwmIdByName, this, _1))
, m_displacementModeManager([this](bool show) {
int const mode = show ? dp::displacement::kHotelMode : dp::displacement::kDefaultMode;
CallDrapeFunction(bind(&df::DrapeEngine::SetDisplacementMode, _1, mode));
@@ -499,6 +506,7 @@ Framework::Framework()
Framework::~Framework()
{
m_trafficManager.Teardown();
+ m_hlaManager.Teardown();
DestroyDrapeEngine();
m_model.SetOnMapDeregisteredCallback(nullptr);
}
@@ -615,6 +623,7 @@ void Framework::OnCountryFileDownloaded(storage::TCountryId const & countryId, s
rect = id.GetInfo()->m_limitRect;
}
m_trafficManager.Invalidate();
+ m_hlaManager.OnDownloadCountry(countryId);
InvalidateRect(rect);
m_searchEngine->ClearCaches();
}
@@ -639,6 +648,7 @@ bool Framework::OnCountryFileDelete(storage::TCountryId const & countryId, stora
m_model.DeregisterMap(platform::CountryFile(countryId));
deferredDelete = true;
m_trafficManager.OnMwmDelete(mwmId);
+ m_hlaManager.OnDeleteCountry(countryId);
}
InvalidateRect(rect);
@@ -1785,6 +1795,7 @@ void Framework::CreateDrapeEngine(ref_ptr<dp::OGLContextFactory> contextFactory,
m_drapeApi.SetEngine(make_ref(m_drapeEngine));
m_trafficManager.SetDrapeEngine(make_ref(m_drapeEngine));
+ m_hlaManager.SetDrapeEngine(make_ref(m_drapeEngine));
benchmark::RunGraphicsBenchmark(this);
}
@@ -1821,6 +1832,7 @@ void Framework::DestroyDrapeEngine()
{
m_drapeApi.SetEngine(nullptr);
m_trafficManager.Teardown();
+ m_hlaManager.Teardown();
GpsTracker::Instance().Disconnect();
m_drapeEngine.reset();
}
@@ -3432,7 +3444,7 @@ void Framework::VisualizeRoadsInRect(m2::RectD const & rect)
}, kScale);
}
-vector<MwmSet::MwmId> Framework::GetMwmsByRect(m2::RectD const & rect)
+vector<MwmSet::MwmId> Framework::GetMwmsByRect(m2::RectD const & rect) const
{
vector<MwmSet::MwmId> result;
if (!m_infoGetter)
@@ -3440,7 +3452,12 @@ vector<MwmSet::MwmId> Framework::GetMwmsByRect(m2::RectD const & rect)
auto countryIds = m_infoGetter->GetRegionsCountryIdByRect(rect);
for (auto const & countryId : countryIds)
- result.push_back(m_model.GetIndex().GetMwmIdByCountryFile(platform::CountryFile(countryId)));
+ result.push_back(GetMwmIdByName(countryId));
return result;
}
+
+MwmSet::MwmId Framework::GetMwmIdByName(std::string const & name) const
+{
+ return m_model.GetIndex().GetMwmIdByCountryFile(platform::CountryFile(name));
+}
diff --git a/map/framework.hpp b/map/framework.hpp
index bda5373a16..8db53b50db 100644
--- a/map/framework.hpp
+++ b/map/framework.hpp
@@ -6,6 +6,7 @@
#include "map/city_finder.hpp"
#include "map/displacement_mode_manager.hpp"
#include "map/feature_vec_model.hpp"
+#include "map/hla_manager.hpp"
#include "map/mwm_url.hpp"
#include "map/place_page_info.hpp"
#include "map/track.hpp"
@@ -176,6 +177,8 @@ protected:
TrafficManager m_trafficManager;
+ HLAManager m_hlaManager;
+
/// This function will be called by m_storage when latest local files
/// is downloaded.
void OnCountryFileDownloaded(storage::TCountryId const & countryId, storage::Storage::TLocalFilePtr const localFile);
@@ -369,7 +372,8 @@ public:
/// Guarantees that listener is called in the main thread context.
void SetCurrentCountryChangedListener(TCurrentCountryChanged const & listener);
- vector<MwmSet::MwmId> GetMwmsByRect(m2::RectD const & rect);
+ vector<MwmSet::MwmId> GetMwmsByRect(m2::RectD const & rect) const;
+ MwmSet::MwmId GetMwmIdByName(std::string const & name) const;
private:
struct TapEvent
@@ -789,6 +793,8 @@ public:
TrafficManager & GetTrafficManager();
+ HLAManager & GetHLAManager();
+
bool LoadTrafficEnabled();
void SaveTrafficEnabled(bool trafficEnabled);
diff --git a/map/hla_manager.cpp b/map/hla_manager.cpp
new file mode 100644
index 0000000000..bd97566e90
--- /dev/null
+++ b/map/hla_manager.cpp
@@ -0,0 +1,350 @@
+#include "map/hla_manager.hpp"
+
+#include "drape_frontend/drape_engine.hpp"
+#include "drape_frontend/visual_params.hpp"
+
+#include "indexer/scales.hpp"
+
+#include "platform/http_client.hpp"
+#include "platform/platform.hpp"
+
+#include "coding/file_container.hpp"
+#include "coding/file_name_utils.hpp"
+
+namespace
+{
+std::string const kCampaignFile = "hla_campaigns.dat";
+std::string const kExpirationFile = "hla_expiration.dat";
+auto constexpr kWWanUpdateTimeout = std::chrono::hours(12);
+
+std::vector<uint8_t> SerializeTimestamp(std::chrono::steady_clock::time_point ts)
+{
+ long hours = std::chrono::duration_cast<std::chrono::hours>(ts.time_since_epoch()).count();
+
+ std::vector<uint8_t> result;
+ MemWriter<decltype(result)> writer(result);
+ writer.Write(&hours, sizeof(hours));
+
+ return result;
+}
+
+std::chrono::steady_clock::time_point DeserializeTimestamp(ModelReaderPtr const & reader)
+{
+ ASSERT_EQUAL(reader.Size(), sizeof(long), ());
+
+ std::vector<uint8_t> bytes(reader.Size());
+ reader.Read(0, bytes.data(), bytes.size());
+
+ MemReaderWithExceptions memReader(bytes.data(), bytes.size());
+ ReaderSource<decltype(memReader)> src(memReader);
+ long hours = ReadPrimitiveFromSource<long>(src);
+
+ return std::chrono::steady_clock::time_point(std::chrono::hours(hours));
+}
+
+std::string GetPath(std::string const & fileName)
+{
+ return my::JoinFoldersToPath(GetPlatform().WritableDir(), fileName);
+}
+} // namespace
+
+HLAManager::HLAManager(GetMwmsByRectFn const & getMwmsByRectFn,
+ GetMwmIdByName const & getMwmIdByName)
+ : m_getMwmsByRectFn(getMwmsByRectFn)
+ , m_getMwmIdByNameFn(getMwmIdByName)
+ , m_thread(&HLAManager::ThreadRoutine, this)
+{
+ CHECK(m_getMwmsByRectFn != nullptr, ());
+ CHECK(m_getMwmIdByNameFn != nullptr, ());
+}
+
+HLAManager::~HLAManager()
+{
+#ifdef DEBUG
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ ASSERT(!m_isRunning, ());
+ }
+#endif
+}
+
+void HLAManager::Teardown()
+{
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ if (!m_isRunning)
+ return;
+ m_isRunning = false;
+ }
+ m_condition.notify_one();
+ m_thread.join();
+}
+
+void HLAManager::SetDrapeEngine(ref_ptr<df::DrapeEngine> engine)
+{
+ m_drapeEngine = engine;
+ {
+ std::lock_guard<std::mutex> lock(m_symbolsCacheMutex);
+ if (!m_symbolsCache.empty())
+ m_drapeEngine->AddCustomSymbols(std::move(m_symbolsCache));
+ }
+}
+
+void HLAManager::UpdateViewport(ScreenBase const & screen)
+{
+ auto connectionStatus = GetPlatform().ConnectionStatus();
+ if (connectionStatus == Platform::EConnectionType::CONNECTION_NONE ||
+ df::GetZoomLevel(screen.GetScale()) <= scales::GetUpperWorldScale())
+ {
+ return;
+ }
+
+ std::vector<std::string> requestedCampaigns;
+ auto mwms = m_getMwmsByRectFn(screen.ClipRect());
+ if (mwms.empty())
+ return;
+
+ // Request HLA campaigns.
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+
+ for (auto const & mwm : mwms)
+ {
+ auto info = mwm.GetInfo();
+ if (!info)
+ continue;
+ std::string const & mwmName = info->GetCountryName();
+ auto campaignIt = m_campaigns.find(mwmName);
+ if (campaignIt == m_campaigns.end())
+ {
+ requestedCampaigns.push_back(mwmName);
+ continue;
+ }
+
+ // If a campaign has not been requested from server this session.
+ if (!campaignIt->second)
+ {
+ auto const it = m_expiration.find(mwmName);
+ bool needUpdateByTimeout = (connectionStatus == Platform::EConnectionType::CONNECTION_WIFI);
+ if (!needUpdateByTimeout && it != m_expiration.end())
+ {
+ auto const currentTime = std::chrono::steady_clock::now();
+ needUpdateByTimeout = (currentTime - it->second) > kWWanUpdateTimeout;
+ }
+
+ if (needUpdateByTimeout || it == m_expiration.end())
+ requestedCampaigns.push_back(mwmName);
+ }
+ }
+
+ if (!requestedCampaigns.empty())
+ {
+ m_requestedCampaigns.reserve(m_requestedCampaigns.size() + requestedCampaigns.size());
+ for (auto const & campaign : requestedCampaigns)
+ m_requestedCampaigns.push_back(std::make_pair(m_getMwmIdByNameFn(campaign), RequestType::Download));
+ m_condition.notify_one();
+ }
+ }
+}
+
+void HLAManager::ThreadRoutine()
+{
+ std::string const campaignFile = GetPath(kCampaignFile);
+ std::string const expirationFile = GetPath(kExpirationFile);
+
+ // Read persistence data (expiration file must be read the first).
+ ReadExpirationFile(expirationFile);
+ ReadCampaignFile(campaignFile);
+
+ std::vector<Request> campaignMwms;
+ while (WaitForRequest(campaignMwms))
+ {
+ try
+ {
+ FilesContainerW campaignsContainer(campaignFile, FileWriter::OP_WRITE_EXISTING);
+ FilesContainerW expirationContainer(expirationFile, FileWriter::OP_WRITE_EXISTING);
+ for (auto const & mwm : campaignMwms)
+ {
+ if (!mwm.first.IsAlive())
+ continue;
+
+ std::string const countryName = mwm.first.GetInfo()->GetCountryName();
+ if (mwm.second == RequestType::Download)
+ {
+ // Download campaign data from server.
+ std::vector<uint8_t> rawData = DownloadCampaign(mwm.first);
+ if (rawData.empty())
+ continue;
+
+ // Save data persistently.
+ campaignsContainer.Write(rawData, countryName);
+ auto ts = std::chrono::steady_clock::now();
+ expirationContainer.Write(SerializeTimestamp(ts), countryName);
+
+ // Update run-time data.
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_campaigns[countryName] = true;
+ m_expiration[countryName] = ts;
+ }
+
+ // Deserialize and send data to rendering.
+ auto symbols = DeserializeCampaign(std::move(rawData), ts);
+ if (symbols.empty())
+ DeleteSymbolsFromRendering(mwm.first);
+ else
+ SendSymbolsToRendering(std::move(symbols));
+ }
+ else if (mwm.second == RequestType::Delete)
+ {
+ campaignsContainer.DeleteSection(countryName);
+ expirationContainer.DeleteSection(countryName);
+ DeleteSymbolsFromRendering(mwm.first);
+ }
+ }
+ campaignsContainer.Finish();
+ expirationContainer.Finish();
+ }
+ catch (RootException const & ex)
+ {
+ LOG(LWARNING, (ex.Msg()));
+ }
+ campaignMwms.clear();
+ }
+}
+
+bool HLAManager::WaitForRequest(std::vector<Request> & campaignMwms)
+{
+ std::unique_lock<std::mutex> lock(m_mutex);
+
+ m_condition.wait(lock, [this] {return !m_isRunning || !m_requestedCampaigns.empty();});
+
+ if (!m_isRunning)
+ return false;
+
+ if (!m_requestedCampaigns.empty())
+ campaignMwms.swap(m_requestedCampaigns);
+
+ return true;
+}
+
+void HLAManager::OnDownloadCountry(std::string const & countryName)
+{
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_campaigns.erase(countryName);
+ m_expiration.erase(countryName);
+}
+
+void HLAManager::OnDeleteCountry(std::string const & countryName)
+{
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_campaigns.erase(countryName);
+ m_expiration.erase(countryName);
+ m_requestedCampaigns.push_back(std::make_pair(m_getMwmIdByNameFn(countryName), RequestType::Delete));
+ m_condition.notify_one();
+}
+
+string HLAManager::MakeRemoteURL(MwmSet::MwmId const &mwmId) const
+{
+ // TODO: build correct URL after server completion.
+
+ return "http://172.27.15.68/campaigns.data";
+}
+
+std::vector<uint8_t> HLAManager::DownloadCampaign(MwmSet::MwmId const & mwmId) const
+{
+ platform::HttpClient request(MakeRemoteURL(mwmId));
+ if (!request.RunHttpRequest() || request.ErrorCode() != 200)
+ return std::vector<uint8_t>();
+ string const & response = request.ServerResponse();
+ return std::vector<uint8_t>(response.cbegin(), response.cend());
+}
+
+df::CustomSymbols HLAManager::DeserializeCampaign(std::vector<uint8_t> && rawData,
+ std::chrono::steady_clock::time_point timestamp)
+{
+ df::CustomSymbols symbols;
+ // TODO: Deserialize campaign.
+ // TODO: Filter by timestamp.
+
+ // TEMP!
+ //auto mwmId = m_getMwmIdByNameFn("Russia_Moscow");
+ //symbols.insert(std::make_pair(FeatureID(mwmId, 371323), df::CustomSymbol("test-m", true)));
+ //symbols.insert(std::make_pair(FeatureID(mwmId, 371363), df::CustomSymbol("test-m", true)));
+ //symbols.insert(std::make_pair(FeatureID(mwmId, 373911), df::CustomSymbol("test-m", true)));
+
+ return symbols;
+}
+
+void HLAManager::ReadExpirationFile(std::string const & expirationFile)
+{
+ if (!GetPlatform().IsFileExistsByFullPath(expirationFile))
+ {
+ FilesContainerW f(expirationFile);
+ return;
+ }
+
+ try
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ FilesContainerR expirationContainer(expirationFile);
+ expirationContainer.ForEachTag([this, &expirationContainer](FilesContainerR::Tag const & tag)
+ {
+ m_expiration[tag] = DeserializeTimestamp(expirationContainer.GetReader(tag));
+ });
+ }
+ catch (Reader::Exception const & ex)
+ {
+ LOG(LWARNING, ("Error reading file:", expirationFile, ex.Msg()));
+ }
+}
+
+void HLAManager::ReadCampaignFile(std::string const & campaignFile)
+{
+ if (!GetPlatform().IsFileExistsByFullPath(campaignFile))
+ {
+ FilesContainerW f(campaignFile);
+ return;
+ }
+
+ try
+ {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ df::CustomSymbols allSymbols;
+ FilesContainerR campaignContainer(campaignFile);
+ campaignContainer.ForEachTag([this, &campaignContainer, &allSymbols](FilesContainerR::Tag const & tag)
+ {
+ auto const & reader = campaignContainer.GetReader(tag);
+ std::vector<uint8_t> rawData(reader.Size());
+ reader.Read(0, rawData.data(), rawData.size());
+ m_campaigns[tag] = false;
+
+ ASSERT(m_expiration.find(tag) != m_expiration.end(), ());
+ auto ts = m_expiration[tag];
+ auto symbols = DeserializeCampaign(std::move(rawData), ts);
+ allSymbols.insert(symbols.begin(), symbols.end());
+ });
+ SendSymbolsToRendering(std::move(allSymbols));
+ }
+ catch (Reader::Exception const & ex)
+ {
+ LOG(LWARNING, ("Error reading file:", campaignFile, ex.Msg()));
+ }
+}
+
+void HLAManager::SendSymbolsToRendering(df::CustomSymbols && symbols)
+{
+ if (m_drapeEngine == nullptr)
+ {
+ std::lock_guard<std::mutex> lock(m_symbolsCacheMutex);
+ m_symbolsCache.insert(symbols.begin(), symbols.end());
+ return;
+ }
+ m_drapeEngine->AddCustomSymbols(std::move(symbols));
+}
+
+void HLAManager::DeleteSymbolsFromRendering(MwmSet::MwmId const & mwmId)
+{
+ if (m_drapeEngine != nullptr)
+ m_drapeEngine->RemoveCustomSymbols(mwmId);
+}
diff --git a/map/hla_manager.hpp b/map/hla_manager.hpp
new file mode 100644
index 0000000000..fd4acd006b
--- /dev/null
+++ b/map/hla_manager.hpp
@@ -0,0 +1,81 @@
+#pragma once
+
+#include "drape_frontend/custom_symbol.hpp"
+
+#include "drape/pointers.hpp"
+
+#include "geometry/rect2d.hpp"
+#include "geometry/screenbase.hpp"
+
+#include "indexer/index.hpp"
+#include "indexer/mwm_set.hpp"
+
+#include "base/thread.hpp"
+
+#include <chrono>
+#include <map>
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace df
+{
+class DrapeEngine;
+} // namespace df
+
+// Hyper Local Ads (HLA) manager.
+class HLAManager final
+{
+public:
+ using GetMwmsByRectFn = function<std::vector<MwmSet::MwmId>(m2::RectD const &)>;
+ using GetMwmIdByName = function<MwmSet::MwmId(std::string const &)>;
+
+ HLAManager(GetMwmsByRectFn const & getMwmsByRectFn, GetMwmIdByName const & getMwmIdByName);
+ HLAManager(HLAManager && /* hlaManager */) = default;
+ ~HLAManager();
+
+ void Teardown();
+ void SetDrapeEngine(ref_ptr<df::DrapeEngine> engine);
+ void UpdateViewport(ScreenBase const & screen);
+
+ void OnDownloadCountry(std::string const & countryName);
+ void OnDeleteCountry(std::string const & countryName);
+
+private:
+ enum class RequestType
+ {
+ Download,
+ Delete
+ };
+ using Request = std::pair<MwmSet::MwmId, RequestType>;
+
+ void ThreadRoutine();
+ bool WaitForRequest(std::vector<Request> & campaignMwms);
+
+ std::string MakeRemoteURL(MwmSet::MwmId const & mwmId) const;
+ std::vector<uint8_t> DownloadCampaign(MwmSet::MwmId const & mwmId) const;
+ df::CustomSymbols DeserializeCampaign(std::vector<uint8_t> && rawData,
+ std::chrono::steady_clock::time_point timestamp);
+ void SendSymbolsToRendering(df::CustomSymbols && symbols);
+ void DeleteSymbolsFromRendering(MwmSet::MwmId const & mwmId);
+
+ void ReadExpirationFile(std::string const & expirationFile);
+ void ReadCampaignFile(std::string const & campaignFile);
+
+ GetMwmsByRectFn m_getMwmsByRectFn;
+ GetMwmIdByName m_getMwmIdByNameFn;
+
+ ref_ptr<df::DrapeEngine> m_drapeEngine;
+
+ std::map<std::string, bool> m_campaigns;
+ std::map<std::string, std::chrono::steady_clock::time_point> m_expiration;
+
+ df::CustomSymbols m_symbolsCache;
+ std::mutex m_symbolsCacheMutex;
+
+ bool m_isRunning = true;
+ std::condition_variable m_condition;
+ std::vector<Request> m_requestedCampaigns;
+ std::mutex m_mutex;
+ threads::SimpleThread m_thread;
+};
diff --git a/map/map.pro b/map/map.pro
index 0d6f9f60fb..d77cf3f23f 100644
--- a/map/map.pro
+++ b/map/map.pro
@@ -27,6 +27,7 @@ HEADERS += \
gps_track_filter.hpp \
gps_track_storage.hpp \
gps_tracker.hpp \
+ hla_manager.hpp \
mwm_url.hpp \
place_page_info.hpp \
reachable_by_taxi_checker.hpp \
@@ -53,6 +54,7 @@ SOURCES += \
gps_track_filter.cpp \
gps_track_storage.cpp \
gps_tracker.cpp \
+ hla_manager.cpp \
mwm_url.cpp \
place_page_info.cpp \
reachable_by_taxi_checker.cpp \