diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml
index 9167327a9c..b3b955f59c 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -38,6 +38,8 @@
//-->
+
+
@@ -723,6 +725,14 @@
android:name="app.organicmaps.background.OsmUploadService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="false"/>
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/build.gradle b/android/build.gradle
index 96c989afd2..8f23e41966 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -84,6 +84,7 @@ dependencies {
implementation 'androidx.annotation:annotation:1.5.0'
implementation 'androidx.appcompat:appcompat:1.7.0-alpha01'
+ implementation 'androidx.car.app:app:1.3.0-rc01'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.fragment:fragment:1.5.4'
// Lifecycle is added as a workaround for duplicate classes error caused by some outdated dependency:
@@ -97,6 +98,8 @@ dependencies {
implementation 'androidx.work:work-runtime:2.7.1'
implementation 'com.google.android.material:material:1.8.0-alpha02'
implementation 'com.google.code.gson:gson:2.10'
+ // Fix for app/organicmaps/util/FileUploadWorker.java:14: error: cannot access ListenableFuture
+ implementation 'com.google.guava:guava:29.0-android'
implementation 'com.timehop.stickyheadersrecyclerview:library:0.4.3@aar'
implementation 'com.github.devnullorthrow:MPAndroidChart:3.2.0-alpha'
implementation 'net.jcip:jcip-annotations:1.0'
diff --git a/android/gradle.properties b/android/gradle.properties
index c9cf2e5f8f..5608450632 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,4 +1,4 @@
-propMinSdkVersion=21
+propMinSdkVersion=23
propTargetSdkVersion=33
propCompileSdkVersion=33
propBuildToolsVersion=33.0.0
diff --git a/android/jni/app/organicmaps/Framework.cpp b/android/jni/app/organicmaps/Framework.cpp
index ad17613965..c6ab5586d4 100644
--- a/android/jni/app/organicmaps/Framework.cpp
+++ b/android/jni/app/organicmaps/Framework.cpp
@@ -107,7 +107,6 @@ enum MultiTouchAction
Framework::Framework()
: m_lastCompass(0.0)
- , m_isSurfaceDestroyed(false)
, m_isChoosePositionMode(false)
{
m_work.GetTrafficManager().SetStateListener(bind(&Framework::TrafficStateChanged, this, _1));
@@ -163,15 +162,22 @@ void Framework::IsolinesSchemeStateChanged(IsolinesManager::IsolinesState state)
m_onIsolinesStateChangedFn(state);
}
-bool Framework::DestroySurfaceOnDetach()
+bool Framework::DestroySurfaceOnDetach(df::DrapeEngineId engineId)
{
- if (m_vulkanContextFactory)
+ if (m_drapeEngines[engineId].m_vulkanContextFactory)
return false;
return true;
}
-bool Framework::CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi, bool firstLaunch,
- bool launchByDeepLink, uint32_t appVersionCode)
+df::DrapeEngineId Framework::CreateDrapeEngineId()
+{
+ df::DrapeEngineId engineId = m_work.CreateDrapeEngineId();
+ m_drapeEngines[engineId];
+ return engineId;
+}
+
+bool Framework::CreateDrapeEngine(JNIEnv * env, df::DrapeEngineId engineId, jobject jSurface, int densityDpi,
+ bool firstLaunch, bool launchByDeepLink, uint32_t appVersionCode)
{
// Vulkan is supported only since Android 8.0, because some Android devices with Android 7.x
// have fatal driver issue, which can lead to process termination and whole OS destabilization.
@@ -183,34 +189,36 @@ bool Framework::CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi
if (vulkanForbidden)
LOG(LWARNING, ("Vulkan API is forbidden on this device."));
+ DrapeEngineData &drapeEngineData = m_drapeEngines[engineId];
+
if (m_work.LoadPreferredGraphicsAPI() == dp::ApiVersion::Vulkan && !vulkanForbidden)
{
- m_vulkanContextFactory =
+ drapeEngineData.m_vulkanContextFactory =
make_unique_dp(appVersionCode, sdkVersion);
- if (!CastFactory(m_vulkanContextFactory)->IsVulkanSupported())
+ if (!CastFactory(drapeEngineData.m_vulkanContextFactory)->IsVulkanSupported())
{
LOG(LWARNING, ("Vulkan API is not supported."));
- m_vulkanContextFactory.reset();
+ drapeEngineData.m_vulkanContextFactory.reset();
}
- if (m_vulkanContextFactory)
+ if (drapeEngineData.m_vulkanContextFactory)
{
- auto f = CastFactory(m_vulkanContextFactory);
+ auto f = CastFactory(drapeEngineData.m_vulkanContextFactory);
f->SetSurface(env, jSurface);
if (!f->IsValid())
{
LOG(LWARNING, ("Invalid Vulkan API context."));
- m_vulkanContextFactory.reset();
+ drapeEngineData.m_vulkanContextFactory.reset();
}
}
}
AndroidOGLContextFactory * oglFactory = nullptr;
- if (!m_vulkanContextFactory)
+ if (!drapeEngineData.m_vulkanContextFactory)
{
- m_oglContextFactory = make_unique_dp(
+ drapeEngineData.m_oglContextFactory = make_unique_dp(
new AndroidOGLContextFactory(env, jSurface));
- oglFactory = m_oglContextFactory->CastFactory();
+ oglFactory = drapeEngineData.m_oglContextFactory->CastFactory();
if (!oglFactory->IsValid())
{
LOG(LWARNING, ("Invalid GL context."));
@@ -219,9 +227,9 @@ bool Framework::CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi
}
::Framework::DrapeCreationParams p;
- if (m_vulkanContextFactory)
+ if (drapeEngineData.m_vulkanContextFactory)
{
- auto f = CastFactory(m_vulkanContextFactory);
+ auto f = CastFactory(drapeEngineData.m_vulkanContextFactory);
p.m_apiVersion = dp::ApiVersion::Vulkan;
p.m_surfaceWidth = f->GetWidth();
p.m_surfaceHeight = f->GetHeight();
@@ -238,33 +246,35 @@ bool Framework::CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi
p.m_isChoosePositionMode = m_isChoosePositionMode;
p.m_hints.m_isFirstLaunch = firstLaunch;
p.m_hints.m_isLaunchByDeepLink = launchByDeepLink;
- ASSERT(!m_guiPositions.empty(), ("GUI elements must be set-up before engine is created"));
- p.m_widgetsInitInfo = m_guiPositions;
+ ASSERT(!drapeEngineData.m_guiPositions.empty(), ("GUI elements must be set-up before engine is created"));
+ p.m_widgetsInitInfo = drapeEngineData.m_guiPositions;
m_work.SetMyPositionModeListener(bind(&Framework::MyPositionModeChanged, this, _1, _2));
- if (m_vulkanContextFactory)
- m_work.CreateDrapeEngine(make_ref(m_vulkanContextFactory), move(p));
+ if (drapeEngineData.m_vulkanContextFactory)
+ m_work.CreateDrapeEngine(engineId, make_ref(drapeEngineData.m_vulkanContextFactory), move(p));
else
- m_work.CreateDrapeEngine(make_ref(m_oglContextFactory), move(p));
+ m_work.CreateDrapeEngine(engineId, make_ref(drapeEngineData.m_oglContextFactory), move(p));
+
m_work.EnterForeground();
return true;
}
-bool Framework::IsDrapeEngineCreated() const
+bool Framework::IsDrapeEngineCreated(df::DrapeEngineId engineId) const
{
- return m_work.IsDrapeEngineCreated();
+ return m_work.IsDrapeEngineCreated(engineId);
}
-void Framework::Resize(JNIEnv * env, jobject jSurface, int w, int h)
+void Framework::Resize(JNIEnv * env, df::DrapeEngineId engineId, jobject jSurface, int w, int h)
{
- if (m_vulkanContextFactory)
+ auto& drapeEngineData = m_drapeEngines[engineId];
+ if (drapeEngineData.m_vulkanContextFactory)
{
- auto vulkanContextFactory = CastFactory(m_vulkanContextFactory);
+ auto vulkanContextFactory = CastFactory(drapeEngineData.m_vulkanContextFactory);
if (vulkanContextFactory->GetWidth() != w || vulkanContextFactory->GetHeight() != h)
{
- m_vulkanContextFactory->SetPresentAvailable(false);
+ drapeEngineData.m_vulkanContextFactory->SetPresentAvailable(false);
m_work.SetRenderingDisabled(false /* destroySurface */);
vulkanContextFactory->ChangeSurface(env, jSurface, w, h);
@@ -275,32 +285,33 @@ void Framework::Resize(JNIEnv * env, jobject jSurface, int w, int h)
}
else
{
- m_oglContextFactory->CastFactory()->UpdateSurfaceSize(w, h);
+ drapeEngineData.m_oglContextFactory->CastFactory()->UpdateSurfaceSize(w, h);
}
- m_work.OnSize(w, h);
+ m_work.OnSize(engineId, w, h);
}
-void Framework::DetachSurface(bool destroySurface)
+void Framework::DetachSurface(df::DrapeEngineId engineId, bool destroySurface)
{
LOG(LINFO, ("Detach surface started. destroySurface =", destroySurface));
- if (m_vulkanContextFactory)
+ auto& drapeEngineData = m_drapeEngines[engineId];
+ if (drapeEngineData.m_vulkanContextFactory)
{
- m_vulkanContextFactory->SetPresentAvailable(false);
+ drapeEngineData.m_vulkanContextFactory->SetPresentAvailable(false);
}
else
{
- ASSERT(m_oglContextFactory != nullptr, ());
- m_oglContextFactory->SetPresentAvailable(false);
+ ASSERT(drapeEngineData.m_oglContextFactory != nullptr, ());
+ drapeEngineData.m_oglContextFactory->SetPresentAvailable(false);
}
if (destroySurface)
{
LOG(LINFO, ("Destroy surface."));
- m_isSurfaceDestroyed = true;
+ drapeEngineData.m_isSurfaceDestroyed = true;
m_work.OnDestroySurface();
}
- if (m_vulkanContextFactory)
+ if (drapeEngineData.m_vulkanContextFactory)
{
// With Vulkan we don't need to recreate all graphics resources,
// we have to destroy only resources bound with surface (swapchains,
@@ -309,25 +320,25 @@ void Framework::DetachSurface(bool destroySurface)
m_work.SetRenderingDisabled(false /* destroySurface */);
// Allow pipeline dump only on enter background.
- CastFactory(m_vulkanContextFactory)->ResetSurface(destroySurface /* allowPipelineDump */);
+ CastFactory(drapeEngineData.m_vulkanContextFactory)->ResetSurface(destroySurface /* allowPipelineDump */);
}
else
{
m_work.SetRenderingDisabled(destroySurface);
- auto factory = m_oglContextFactory->CastFactory();
+ auto factory = drapeEngineData.m_oglContextFactory->CastFactory();
factory->ResetSurface();
}
LOG(LINFO, ("Detach surface finished."));
}
-bool Framework::AttachSurface(JNIEnv * env, jobject jSurface)
+bool Framework::AttachSurface(JNIEnv * env, df::DrapeEngineId engineId, jobject jSurface)
{
LOG(LINFO, ("Attach surface started."));
-
+ auto& drapeEngineData = m_drapeEngines[engineId];
int w = 0, h = 0;
- if (m_vulkanContextFactory)
+ if (drapeEngineData.m_vulkanContextFactory)
{
- auto factory = CastFactory(m_vulkanContextFactory);
+ auto factory = CastFactory(drapeEngineData.m_vulkanContextFactory);
factory->SetSurface(env, jSurface);
if (!factory->IsValid())
{
@@ -339,8 +350,8 @@ bool Framework::AttachSurface(JNIEnv * env, jobject jSurface)
}
else
{
- ASSERT(m_oglContextFactory != nullptr, ());
- auto factory = m_oglContextFactory->CastFactory();
+ ASSERT(drapeEngineData.m_oglContextFactory != nullptr, ());
+ auto factory = drapeEngineData.m_oglContextFactory->CastFactory();
factory->SetSurface(env, jSurface);
if (!factory->IsValid())
{
@@ -351,25 +362,25 @@ bool Framework::AttachSurface(JNIEnv * env, jobject jSurface)
h = factory->GetHeight();
}
- ASSERT(!m_guiPositions.empty(), ("GUI elements must be set-up before engine is created"));
+ ASSERT(!drapeEngineData.m_guiPositions.empty(), ("GUI elements must be set-up before engine is created"));
- if (m_vulkanContextFactory)
+ if (drapeEngineData.m_vulkanContextFactory)
{
- m_vulkanContextFactory->SetPresentAvailable(true);
+ drapeEngineData.m_vulkanContextFactory->SetPresentAvailable(true);
m_work.SetRenderingEnabled();
}
else
{
- m_oglContextFactory->SetPresentAvailable(true);
- m_work.SetRenderingEnabled(make_ref(m_oglContextFactory));
+ drapeEngineData.m_oglContextFactory->SetPresentAvailable(true);
+ m_work.SetRenderingEnabled(make_ref(drapeEngineData.m_oglContextFactory));
}
- if (m_isSurfaceDestroyed)
+ if (drapeEngineData.m_isSurfaceDestroyed)
{
LOG(LINFO, ("Recover surface, viewport size:", w, h));
- bool const recreateContextDependentResources = (m_vulkanContextFactory == nullptr);
- m_work.OnRecoverSurface(w, h, recreateContextDependentResources);
- m_isSurfaceDestroyed = false;
+ bool const recreateContextDependentResources = (drapeEngineData.m_vulkanContextFactory == nullptr);
+ m_work.OnRecoverSurface(engineId, w, h, recreateContextDependentResources);
+ drapeEngineData.m_isSurfaceDestroyed = false;
}
LOG(LINFO, ("Attach surface finished."));
@@ -377,26 +388,28 @@ bool Framework::AttachSurface(JNIEnv * env, jobject jSurface)
return true;
}
-void Framework::PauseSurfaceRendering()
+void Framework::PauseSurfaceRendering(df::DrapeEngineId engineId)
{
- if (m_vulkanContextFactory)
- m_vulkanContextFactory->SetPresentAvailable(false);
- if (m_oglContextFactory)
- m_oglContextFactory->SetPresentAvailable(false);
+ auto& drapeEngineData = m_drapeEngines[engineId];
+ if (drapeEngineData.m_vulkanContextFactory)
+ drapeEngineData.m_vulkanContextFactory->SetPresentAvailable(false);
+ if (drapeEngineData.m_oglContextFactory)
+ drapeEngineData.m_oglContextFactory->SetPresentAvailable(false);
LOG(LINFO, ("Pause surface rendering."));
}
-void Framework::ResumeSurfaceRendering()
+void Framework::ResumeSurfaceRendering(df::DrapeEngineId engineId)
{
- if (m_vulkanContextFactory)
+ auto& drapeEngineData = m_drapeEngines[engineId];
+ if (drapeEngineData.m_vulkanContextFactory)
{
- if (CastFactory(m_vulkanContextFactory)->IsValid())
- m_vulkanContextFactory->SetPresentAvailable(true);
+ if (CastFactory(drapeEngineData.m_vulkanContextFactory)->IsValid())
+ drapeEngineData.m_vulkanContextFactory->SetPresentAvailable(true);
}
- if (m_oglContextFactory)
+ if (drapeEngineData.m_oglContextFactory)
{
- AndroidOGLContextFactory * factory = m_oglContextFactory->CastFactory();
+ AndroidOGLContextFactory * factory = drapeEngineData.m_oglContextFactory->CastFactory();
if (factory->IsValid())
factory->SetPresentAvailable(true);
}
@@ -412,10 +425,13 @@ void Framework::MarkMapStyle(MapStyle mapStyle)
{
// In case of Vulkan rendering we don't recreate geometry and textures data, so
// we need use SetMapStyle instead of MarkMapStyle in all cases.
- if (m_vulkanContextFactory)
- m_work.SetMapStyle(mapStyle);
- else
- m_work.MarkMapStyle(mapStyle);
+ for (const auto& [engineId, engineData] : m_drapeEngines)
+ {
+ if (engineData.m_vulkanContextFactory)
+ m_work.SetMapStyle(mapStyle);
+ else
+ m_work.MarkMapStyle(mapStyle);
+ }
}
MapStyle Framework::GetMapStyle() const
@@ -471,17 +487,17 @@ void Framework::ShowNode(CountryId const & idx, bool zoomToDownloadButton)
}
}
-void Framework::Scale(double factor, m2::PointD const & pxPoint, bool isAnim)
+void Framework::Scale(df::DrapeEngineId engineId, double factor, m2::PointD const & pxPoint, bool isAnim)
{
- m_work.Scale(factor, pxPoint, isAnim);
+ m_work.Scale(engineId, factor, pxPoint, isAnim);
}
-void Framework::Move(double factorX, double factorY, bool isAnim)
+void Framework::Move(df::DrapeEngineId engineId, double factorX, double factorY, bool isAnim)
{
- m_work.Move(factorX, factorY, isAnim);
+ m_work.Move(engineId, factorX, factorY, isAnim);
}
-void Framework::Touch(int action, Finger const & f1, Finger const & f2, uint8_t maskedPointer)
+void Framework::Touch(df::DrapeEngineId engineId, int action, Finger const & f1, Finger const & f2, uint8_t maskedPointer)
{
MultiTouchAction eventType = static_cast(action);
df::TouchEvent event;
@@ -514,7 +530,7 @@ void Framework::Touch(int action, Finger const & f1, Finger const & f2, uint8_t
event.SetSecondTouch(touch);
event.SetFirstMaskedPointer(maskedPointer);
- m_work.TouchEvent(event);
+ m_work.TouchEvent(engineId, event);
}
m2::PointD Framework::GetViewportCenter() const
@@ -527,14 +543,14 @@ void Framework::AddString(string const & name, string const & value)
m_work.AddString(name, value);
}
-void Framework::Scale(::Framework::EScaleMode mode)
+void Framework::Scale(df::DrapeEngineId engineId, ::Framework::EScaleMode mode)
{
- m_work.Scale(mode, true);
+ m_work.Scale(engineId, mode, true);
}
-void Framework::Scale(m2::PointD const & centerPt, int targetZoom, bool animate)
+void Framework::Scale(df::DrapeEngineId engineId, m2::PointD const & centerPt, int targetZoom, bool animate)
{
- ref_ptr engine = m_work.GetDrapeEngine();
+ ref_ptr engine = m_work.GetDrapeEngine(engineId);
if (engine)
engine->SetModelViewCenter(centerPt, targetZoom, animate, false);
}
@@ -658,27 +674,27 @@ location::EMyPositionMode Framework::GetMyPositionMode() const
void Framework::SwitchMyPositionNextMode()
{
- ASSERT(IsDrapeEngineCreated(), ());
+ ASSERT(IsDrapeEngineCreated(0), ());
m_work.SwitchMyPositionNextMode();
}
-void Framework::SetupWidget(gui::EWidget widget, float x, float y, dp::Anchor anchor)
+void Framework::SetupWidget(df::DrapeEngineId engineId, gui::EWidget widget, float x, float y, dp::Anchor anchor)
{
- m_guiPositions[widget] = gui::Position(m2::PointF(x, y), anchor);
+ m_drapeEngines[engineId].m_guiPositions[widget] = gui::Position(m2::PointF(x, y), anchor);
}
-void Framework::ApplyWidgets()
+void Framework::ApplyWidgets(df::DrapeEngineId engineId)
{
gui::TWidgetsLayoutInfo layout;
- for (auto const & widget : m_guiPositions)
+ for (auto const & widget : m_drapeEngines[engineId].m_guiPositions)
layout[widget.first] = widget.second.m_pixelPivot;
- m_work.SetWidgetLayout(move(layout));
+ m_work.SetWidgetLayout(engineId, move(layout));
}
-void Framework::CleanWidgets()
+void Framework::CleanWidgets(df::DrapeEngineId engineId)
{
- m_guiPositions.clear();
+ m_drapeEngines[engineId].m_guiPositions.clear();
}
void Framework::SetupMeasurementSystem()
@@ -1628,7 +1644,8 @@ Java_app_organicmaps_Framework_nativeGetAutoZoomEnabled(JNIEnv *, jclass)
JNIEXPORT void JNICALL
Java_app_organicmaps_Framework_nativeZoomToPoint(JNIEnv * env, jclass, jdouble lat, jdouble lon, jint zoom, jboolean animate)
{
- g_framework->Scale(m2::PointD(mercator::FromLatLon(lat, lon)), zoom, animate);
+ // TODO: fix
+ g_framework->Scale(0, m2::PointD(mercator::FromLatLon(lat, lon)), zoom, animate);
}
JNIEXPORT jobject JNICALL
@@ -1691,7 +1708,7 @@ Java_app_organicmaps_Framework_nativeGetActiveObjectFormattedCuisine(JNIEnv * en
JNIEXPORT void JNICALL
Java_app_organicmaps_Framework_nativeSetVisibleRect(JNIEnv * env, jclass, jint left, jint top, jint right, jint bottom)
{
- frm()->SetVisibleViewport(m2::RectD(left, top, right, bottom));
+// frm()->SetVisibleViewport(m2::RectD(left, top, right, bottom));
}
JNIEXPORT jboolean JNICALL
diff --git a/android/jni/app/organicmaps/Framework.hpp b/android/jni/app/organicmaps/Framework.hpp
index 9cc3d5e265..04625bf19e 100644
--- a/android/jni/app/organicmaps/Framework.hpp
+++ b/android/jni/app/organicmaps/Framework.hpp
@@ -50,8 +50,14 @@ namespace android
class Framework : private power_management::PowerManager::Subscriber
{
private:
- drape_ptr m_oglContextFactory;
- drape_ptr m_vulkanContextFactory;
+ struct DrapeEngineData
+ {
+ drape_ptr m_oglContextFactory;
+ drape_ptr m_vulkanContextFactory;
+ bool m_isSurfaceDestroyed;
+ std::map m_guiPositions;
+ };
+ std::unordered_map m_drapeEngines;
::Framework m_work;
math::LowPassVector m_sensors[2];
@@ -59,10 +65,6 @@ namespace android
std::string m_searchQuery;
- bool m_isSurfaceDestroyed;
-
- std::map m_guiPositions;
-
void TrafficStateChanged(TrafficManager::TrafficState state);
void TransitSchemeStateChanged(TransitReadManager::TransitSchemeState state);
void IsolinesSchemeStateChanged(IsolinesManager::IsolinesState state);
@@ -89,14 +91,15 @@ namespace android
void OnLocationUpdated(location::GpsInfo const & info);
void OnCompassUpdated(location::CompassInfo const & info, bool forceRedraw);
- bool CreateDrapeEngine(JNIEnv * env, jobject jSurface, int densityDpi, bool firstLaunch,
- bool launchByDeepLink, uint32_t appVersionCode);
- bool IsDrapeEngineCreated() const;
- bool DestroySurfaceOnDetach();
- void DetachSurface(bool destroySurface);
- bool AttachSurface(JNIEnv * env, jobject jSurface);
- void PauseSurfaceRendering();
- void ResumeSurfaceRendering();
+ df::DrapeEngineId CreateDrapeEngineId();
+ bool CreateDrapeEngine(JNIEnv * env, df::DrapeEngineId engineId, jobject jSurface, int densityDpi,
+ bool firstLaunch, bool launchByDeepLink, uint32_t appVersionCode);
+ bool IsDrapeEngineCreated(df::DrapeEngineId engineId) const;
+ bool DestroySurfaceOnDetach(df::DrapeEngineId engineId);
+ void DetachSurface(df::DrapeEngineId engineId, bool destroySurface);
+ bool AttachSurface(JNIEnv * env, df::DrapeEngineId engineId, jobject jSurface);
+ void PauseSurfaceRendering(df::DrapeEngineId engineId);
+ void ResumeSurfaceRendering(df::DrapeEngineId engineId);
void SetMapStyle(MapStyle mapStyle);
void MarkMapStyle(MapStyle mapStyle);
@@ -112,7 +115,7 @@ namespace android
return m_work.GetRoutingManager().GetLastUsedRouter();
}
- void Resize(JNIEnv * env, jobject jSurface, int w, int h);
+ void Resize(JNIEnv * env, df::DrapeEngineId engineId, jobject jSurface, int w, int h);
struct Finger
{
@@ -127,11 +130,11 @@ namespace android
float m_x, m_y;
};
- void Scale(double factor, m2::PointD const & pxPoint, bool isAnim);
+ void Scale(df::DrapeEngineId engineId, double factor, m2::PointD const & pxPoint, bool isAnim);
- void Move(double factorX, double factorY, bool isAnim);
+ void Move(df::DrapeEngineId engineId, double factorX, double factorY, bool isAnim);
- void Touch(int action, Finger const & f1, Finger const & f2, uint8_t maskedPointer);
+ void Touch(df::DrapeEngineId engineId, int action, Finger const & f1, Finger const & f2, uint8_t maskedPointer);
bool Search(search::EverywhereSearchParams const & params);
std::string GetLastSearchQuery() { return m_searchQuery; }
@@ -145,8 +148,8 @@ namespace android
void AddString(std::string const & name, std::string const & value);
- void Scale(::Framework::EScaleMode mode);
- void Scale(m2::PointD const & centerPt, int targetZoom, bool animate);
+ void Scale(df::DrapeEngineId engineId, ::Framework::EScaleMode mode);
+ void Scale(df::DrapeEngineId engineId, m2::PointD const & centerPt, int targetZoom, bool animate);
void ReplaceBookmark(kml::MarkId markId, kml::BookmarkData & bm);
void MoveBookmark(kml::MarkId markId, kml::MarkGroupId curCat, kml::MarkGroupId newCat);
@@ -180,9 +183,9 @@ namespace android
void SetChoosePositionMode(bool isChoosePositionMode, bool isBusiness, bool hasPosition, m2::PointD const & position);
bool GetChoosePositionMode();
- void SetupWidget(gui::EWidget widget, float x, float y, dp::Anchor anchor);
- void ApplyWidgets();
- void CleanWidgets();
+ void SetupWidget(df::DrapeEngineId engineId, gui::EWidget widget, float x, float y, dp::Anchor anchor);
+ void ApplyWidgets(df::DrapeEngineId engineId);
+ void CleanWidgets(df::DrapeEngineId engineId);
place_page::Info & GetPlacePageInfo();
diff --git a/android/jni/app/organicmaps/Map.cpp b/android/jni/app/organicmaps/Map.cpp
index 616b5dbb5b..aeec603d5f 100644
--- a/android/jni/app/organicmaps/Map.cpp
+++ b/android/jni/app/organicmaps/Map.cpp
@@ -22,21 +22,27 @@ void OnRenderingInitializationFinished(std::shared_ptr const & listener
extern "C"
{
+JNIEXPORT jlong JNICALL
+Java_app_organicmaps_Map_nativeCreateEngineId(JNIEnv * env, jclass)
+{
+ return g_framework->CreateDrapeEngineId();
+}
+
JNIEXPORT jboolean JNICALL
Java_app_organicmaps_Map_nativeCreateEngine(JNIEnv * env, jclass,
- jobject surface, jint density,
- jboolean firstLaunch,
+ jlong engineId, jobject surface,
+ jint density, jboolean firstLaunch,
jboolean isLaunchByDeepLink,
jint appVersionCode)
{
- return g_framework->CreateDrapeEngine(env, surface, density, firstLaunch, isLaunchByDeepLink,
+ return g_framework->CreateDrapeEngine(env, engineId, surface, density, firstLaunch, isLaunchByDeepLink,
base::asserted_cast(appVersionCode));
}
JNIEXPORT jboolean JNICALL
-Java_app_organicmaps_Map_nativeIsEngineCreated(JNIEnv *, jclass)
+Java_app_organicmaps_Map_nativeIsEngineCreated(JNIEnv *, jclass, jlong engineId)
{
- return g_framework->IsDrapeEngineCreated();
+ return g_framework->IsDrapeEngineCreated(static_cast(engineId));
}
JNIEXPORT jboolean JNICALL
@@ -47,72 +53,72 @@ Java_app_organicmaps_Map_nativeShowMapForUrl(JNIEnv * env, jclass, jstring url)
JNIEXPORT void JNICALL
Java_app_organicmaps_Map_nativeSetRenderingInitializationFinishedListener(
- JNIEnv *, jclass, jobject listener)
+ JNIEnv *, jclass, jlong engineId, jobject listener)
{
if (listener)
{
- g_framework->NativeFramework()->SetGraphicsContextInitializationHandler(
+ g_framework->NativeFramework()->SetGraphicsContextInitializationHandler(engineId,
std::bind(&OnRenderingInitializationFinished, jni::make_global_ref(listener)));
}
else
{
- g_framework->NativeFramework()->SetGraphicsContextInitializationHandler(nullptr);
+ g_framework->NativeFramework()->SetGraphicsContextInitializationHandler(engineId, nullptr);
}
}
JNIEXPORT jboolean JNICALL
-Java_app_organicmaps_Map_nativeAttachSurface(JNIEnv * env, jclass, jobject surface)
+Java_app_organicmaps_Map_nativeAttachSurface(JNIEnv * env, jclass, jlong engineId, jobject surface)
{
- return g_framework->AttachSurface(env, surface);
+ return g_framework->AttachSurface(env, engineId, surface);
}
JNIEXPORT void JNICALL
-Java_app_organicmaps_Map_nativeDetachSurface(JNIEnv *, jclass, jboolean destroySurface)
+Java_app_organicmaps_Map_nativeDetachSurface(JNIEnv *, jclass, jlong engineId, jboolean destroySurface)
{
- g_framework->DetachSurface(destroySurface);
+ g_framework->DetachSurface(engineId, destroySurface);
}
JNIEXPORT void JNICALL
-Java_app_organicmaps_Map_nativeSurfaceChanged(JNIEnv * env, jclass, jobject surface, jint w, jint h)
+Java_app_organicmaps_Map_nativeSurfaceChanged(JNIEnv * env, jclass, jlong engineId, jobject surface, jint w, jint h)
{
- g_framework->Resize(env, surface, w, h);
+ g_framework->Resize(env, engineId, surface, w, h);
}
JNIEXPORT jboolean JNICALL
-Java_app_organicmaps_Map_nativeDestroySurfaceOnDetach(JNIEnv *, jclass)
+Java_app_organicmaps_Map_nativeDestroySurfaceOnDetach(JNIEnv *, jclass, jlong engineId)
{
- return g_framework->DestroySurfaceOnDetach();
+ return g_framework->DestroySurfaceOnDetach(engineId);
}
JNIEXPORT void JNICALL
-Java_app_organicmaps_Map_nativePauseSurfaceRendering(JNIEnv *, jclass)
+Java_app_organicmaps_Map_nativePauseSurfaceRendering(JNIEnv *, jclass, jlong engineId)
{
- g_framework->PauseSurfaceRendering();
+ g_framework->PauseSurfaceRendering(engineId);
}
JNIEXPORT void JNICALL
-Java_app_organicmaps_Map_nativeResumeSurfaceRendering(JNIEnv *, jclass)
+Java_app_organicmaps_Map_nativeResumeSurfaceRendering(JNIEnv *, jclass, jlong engineId)
{
- g_framework->ResumeSurfaceRendering();
+ g_framework->ResumeSurfaceRendering(engineId);
}
JNIEXPORT void JNICALL
-Java_app_organicmaps_Map_nativeApplyWidgets(JNIEnv *, jclass)
+Java_app_organicmaps_Map_nativeApplyWidgets(JNIEnv *, jclass, jlong engineId)
{
- g_framework->ApplyWidgets();
+ g_framework->ApplyWidgets(engineId);
}
JNIEXPORT void JNICALL
-Java_app_organicmaps_Map_nativeCleanWidgets(JNIEnv *, jclass)
+Java_app_organicmaps_Map_nativeCleanWidgets(JNIEnv *, jclass, jlong engineId)
{
- g_framework->CleanWidgets();
+ g_framework->CleanWidgets(engineId);
}
JNIEXPORT void JNICALL
Java_app_organicmaps_Map_nativeSetupWidget(
- JNIEnv *, jclass, jint widget, jfloat x, jfloat y, jint anchor)
+ JNIEnv *, jclass, jlong engineId, jint widget, jfloat x, jfloat y, jint anchor)
{
- g_framework->SetupWidget(static_cast(widget), x, y, static_cast(anchor));
+ g_framework->SetupWidget(engineId, static_cast(widget), x, y, static_cast(anchor));
}
JNIEXPORT void JNICALL
@@ -126,37 +132,37 @@ Java_app_organicmaps_Map_nativeCompassUpdated(JNIEnv *, jclass, jdouble north, j
JNIEXPORT void JNICALL
Java_app_organicmaps_Map_nativeMove(
- JNIEnv *, jclass, jdouble factorX, jdouble factorY, jboolean isAnim)
+ JNIEnv *, jclass, jlong engineId, jdouble factorX, jdouble factorY, jboolean isAnim)
{
- g_framework->Move(factorX, factorY, isAnim);
+ g_framework->Move(engineId, factorX, factorY, isAnim);
}
JNIEXPORT void JNICALL
-Java_app_organicmaps_Map_nativeScalePlus(JNIEnv *, jclass)
+Java_app_organicmaps_Map_nativeScalePlus(JNIEnv *, jclass, jlong engineId)
{
- g_framework->Scale(::Framework::SCALE_MAG);
+ g_framework->Scale(engineId, ::Framework::SCALE_MAG);
}
JNIEXPORT void JNICALL
-Java_app_organicmaps_Map_nativeScaleMinus(JNIEnv *, jclass)
+Java_app_organicmaps_Map_nativeScaleMinus(JNIEnv *, jclass, jlong engineId)
{
- g_framework->Scale(::Framework::SCALE_MIN);
+ g_framework->Scale(engineId, ::Framework::SCALE_MIN);
}
JNIEXPORT void JNICALL
Java_app_organicmaps_Map_nativeScale(
- JNIEnv *, jclass, jdouble factor, jdouble focusX, jdouble focusY, jboolean isAnim)
+ JNIEnv *, jclass, jlong engineId, jdouble factor, jdouble focusX, jdouble focusY, jboolean isAnim)
{
- g_framework->Scale(factor, {focusX, focusY}, isAnim);
+ g_framework->Scale(static_cast(engineId), factor, {focusX, focusY}, isAnim);
}
JNIEXPORT void JNICALL
-Java_app_organicmaps_Map_nativeOnTouch(JNIEnv *, jclass, jint action,
+Java_app_organicmaps_Map_nativeOnTouch(JNIEnv *, jclass, jlong engineId, jint action,
jint id1, jfloat x1, jfloat y1,
jint id2, jfloat x2, jfloat y2,
jint maskedPointer)
{
- g_framework->Touch(action,
+ g_framework->Touch(engineId, action,
android::Framework::Finger(id1, x1, y1),
android::Framework::Finger(id2, x2, y2), maskedPointer);
}
diff --git a/android/res/drawable/ic_check_box.xml b/android/res/drawable/ic_check_box.xml
new file mode 100644
index 0000000000..50101a198b
--- /dev/null
+++ b/android/res/drawable/ic_check_box.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/android/res/drawable/ic_check_box_checked.xml b/android/res/drawable/ic_check_box_checked.xml
new file mode 100644
index 0000000000..5c0babf540
--- /dev/null
+++ b/android/res/drawable/ic_check_box_checked.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/android/res/xml/automotive_app_desc.xml b/android/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000000..83b683397c
--- /dev/null
+++ b/android/res/xml/automotive_app_desc.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/android/src/app/organicmaps/Map.java b/android/src/app/organicmaps/Map.java
index df547cef44..c3d7add4f6 100644
--- a/android/src/app/organicmaps/Map.java
+++ b/android/src/app/organicmaps/Map.java
@@ -50,6 +50,8 @@ public final class Map
public static final int INVALID_POINTER_MASK = 0xFF;
public static final int INVALID_TOUCH_ID = -1;
+ private final long mEngineId;
+
private int mCurrentCompassOffsetX;
private int mCurrentCompassOffsetY;
private int mBottomWidgetOffsetX;
@@ -70,6 +72,8 @@ public final class Map
public Map()
{
+ mEngineId = nativeCreateEngineId();
+ Logger.d(TAG, "Created engineId: " + mEngineId);
onCreate(false);
}
@@ -88,9 +92,9 @@ public final class Map
final int navPadding = UiUtils.dimen(context, R.dimen.nav_frame_padding);
final int marginX = UiUtils.dimen(context, R.dimen.margin_compass) + navPadding;
final int marginY = UiUtils.dimen(context, R.dimen.margin_compass_top) + navPadding;
- nativeSetupWidget(WIDGET_COMPASS, mWidth - x - marginX, y + marginY, ANCHOR_CENTER);
+ nativeSetupWidget(mEngineId, WIDGET_COMPASS, mWidth - x - marginX, y + marginY, ANCHOR_CENTER);
if (forceRedraw && mSurfaceCreated)
- nativeApplyWidgets();
+ nativeApplyWidgets(mEngineId);
mCurrentCompassOffsetX = x;
mCurrentCompassOffsetY = y;
}
@@ -119,8 +123,8 @@ public final class Map
public void onSurfaceCreated(final Context context, final Surface surface, Rect surfaceFrame, int surfaceDpi)
{
- if (nativeIsEngineCreated())
- nativeDetachSurface(true);
+ if (nativeIsEngineCreated(mEngineId))
+ nativeDetachSurface(mEngineId, true);
if (isThemeChangingProcess(context))
{
@@ -129,9 +133,9 @@ public final class Map
}
Logger.d(TAG, "mSurfaceCreated = " + mSurfaceCreated);
- if (nativeIsEngineCreated())
+ if (nativeIsEngineCreated(mEngineId))
{
- if (!nativeAttachSurface(surface))
+ if (!nativeAttachSurface(mEngineId, surface))
{
if (mCallbackUnsupported != null)
mCallbackUnsupported.report();
@@ -140,7 +144,7 @@ public final class Map
mSurfaceCreated = true;
mSurfaceAttached = true;
mRequireResize = true;
- nativeResumeSurfaceRendering();
+ nativeResumeSurfaceRendering(mEngineId);
return;
}
@@ -148,7 +152,7 @@ public final class Map
setupWidgets(context, surfaceFrame.width(), surfaceFrame.height());
final boolean firstStart = LocationHelper.INSTANCE.isInFirstRun();
- if (!nativeCreateEngine(surface, surfaceDpi, firstStart, mLaunchByDeepLink, BuildConfig.VERSION_CODE))
+ if (!nativeCreateEngine(mEngineId, surface, surfaceDpi, firstStart, mLaunchByDeepLink, BuildConfig.VERSION_CODE))
{
if (mCallbackUnsupported != null)
mCallbackUnsupported.report();
@@ -160,7 +164,7 @@ public final class Map
mSurfaceCreated = true;
mSurfaceAttached = true;
- nativeResumeSurfaceRendering();
+ nativeResumeSurfaceRendering(mEngineId);
if (mMapRenderingListener != null)
mMapRenderingListener.onRenderingCreated();
}
@@ -177,11 +181,11 @@ public final class Map
if (!mSurfaceCreated || (!mRequireResize && isSurfaceCreating))
return;
- nativeSurfaceChanged(surface, surfaceFrame.width(), surfaceFrame.height());
+ nativeSurfaceChanged(mEngineId, surface, surfaceFrame.width(), surfaceFrame.height());
mRequireResize = false;
setupWidgets(context, surfaceFrame.width(), surfaceFrame.height());
- nativeApplyWidgets();
+ nativeApplyWidgets(mEngineId);
if (mMapRenderingListener != null)
mMapRenderingListener.onRenderingRestored();
}
@@ -192,8 +196,8 @@ public final class Map
if (!mSurfaceCreated || !mSurfaceAttached || !isAdded)
return;
- nativeDetachSurface(!activityIsChangingConfigurations);
- mSurfaceCreated = !nativeDestroySurfaceOnDetach();
+ nativeDetachSurface(mEngineId, !activityIsChangingConfigurations);
+ mSurfaceCreated = !nativeDestroySurfaceOnDetach(mEngineId);
mSurfaceAttached = false;
}
@@ -218,12 +222,12 @@ public final class Map
public void onStart()
{
- nativeSetRenderingInitializationFinishedListener(mMapRenderingListener);
+ nativeSetRenderingInitializationFinishedListener(mEngineId, mMapRenderingListener);
}
public void onStop()
{
- nativeSetRenderingInitializationFinishedListener(null);
+ nativeSetRenderingInitializationFinishedListener(mEngineId, null);
}
public void onPause(final Context context)
@@ -232,14 +236,14 @@ public final class Map
// Pause/Resume can be called without surface creation/destroy.
if (mSurfaceAttached)
- nativePauseSurfaceRendering();
+ nativePauseSurfaceRendering(mEngineId);
}
public void onResume()
{
// Pause/Resume can be called without surface creation/destroy.
if (mSurfaceAttached)
- nativeResumeSurfaceRendering();
+ nativeResumeSurfaceRendering(mEngineId);
}
boolean isContextCreated()
@@ -249,46 +253,46 @@ public final class Map
public void onScroll(float distanceX, float distanceY)
{
- Map.nativeMove(-distanceX / ((float) mWidth), distanceY / ((float) mHeight), false);
+ nativeMove(mEngineId, -distanceX / ((float) mWidth), distanceY / ((float) mHeight), false);
}
- public static void zoomIn()
+ public void zoomIn()
{
- nativeScalePlus();
+ nativeScalePlus(mEngineId);
}
- public static void zoomOut()
+ public void zoomOut()
{
- nativeScaleMinus();
+ nativeScaleMinus(mEngineId);
}
- public static void onScale(double factor, double focusX, double focusY, boolean isAnim)
+ public void onScale(double factor, double focusX, double focusY, boolean isAnim)
{
- nativeScale(factor, focusX, focusY, isAnim);
+ nativeScale(mEngineId, factor, focusX, focusY, isAnim);
}
- public static void onTouch(int actionType, MotionEvent event, int pointerIndex)
+ public void onTouch(int actionType, MotionEvent event, int pointerIndex)
{
if (event.getPointerCount() == 1)
{
- nativeOnTouch(actionType, event.getPointerId(0), event.getX(), event.getY(), Map.INVALID_TOUCH_ID, 0, 0, 0);
+ nativeOnTouch(mEngineId, actionType, event.getPointerId(0), event.getX(), event.getY(), Map.INVALID_TOUCH_ID, 0, 0, 0);
}
else
{
- nativeOnTouch(actionType,
+ nativeOnTouch(mEngineId, actionType,
event.getPointerId(0), event.getX(0), event.getY(0),
event.getPointerId(1), event.getX(1), event.getY(1), pointerIndex);
}
}
- public static void onTouch(float x, float y)
+ public void onTouch(float x, float y)
{
- nativeOnTouch(Map.NATIVE_ACTION_UP, 0, x, y, Map.INVALID_TOUCH_ID, 0, 0, 0);
+ nativeOnTouch(mEngineId, Map.NATIVE_ACTION_UP, 0, x, y, Map.INVALID_TOUCH_ID, 0, 0, 0);
}
public static boolean isEngineCreated()
{
- return nativeIsEngineCreated();
+ return nativeIsEngineCreated(0);
}
public static boolean showMapForUrl(String url)
@@ -301,30 +305,30 @@ public final class Map
mHeight = height;
mWidth = width;
- nativeCleanWidgets();
+ nativeCleanWidgets(mEngineId);
setupBottomWidgetsOffset(context, mBottomWidgetOffsetX, mBottomWidgetOffsetY);
- nativeSetupWidget(WIDGET_SCALE_FPS_LABEL, UiUtils.dimen(context, R.dimen.margin_base), UiUtils.dimen(context, R.dimen.margin_base), ANCHOR_LEFT_TOP);
+ nativeSetupWidget(mEngineId, WIDGET_SCALE_FPS_LABEL, UiUtils.dimen(context, R.dimen.margin_base), UiUtils.dimen(context, R.dimen.margin_base), ANCHOR_LEFT_TOP);
setupCompass(context, mCurrentCompassOffsetX, mCurrentCompassOffsetY, false);
}
private void setupRuler(final Context context, int offsetX, int offsetY)
{
- nativeSetupWidget(WIDGET_RULER,
+ nativeSetupWidget(mEngineId, WIDGET_RULER,
UiUtils.dimen(context, R.dimen.margin_ruler) + offsetX,
mHeight - UiUtils.dimen(context, R.dimen.margin_ruler) - offsetY,
ANCHOR_LEFT_BOTTOM);
if (mSurfaceCreated)
- nativeApplyWidgets();
+ nativeApplyWidgets(mEngineId);
}
private void setupAttribution(final Context context, int offsetX, int offsetY)
{
- nativeSetupWidget(WIDGET_COPYRIGHT,
+ nativeSetupWidget(mEngineId, WIDGET_COPYRIGHT,
UiUtils.dimen(context, R.dimen.margin_ruler) + offsetX,
mHeight - UiUtils.dimen(context, R.dimen.margin_ruler) - offsetY,
ANCHOR_LEFT_BOTTOM);
if (mSurfaceCreated)
- nativeApplyWidgets();
+ nativeApplyWidgets(mEngineId);
}
private boolean isThemeChangingProcess(final Context context)
@@ -333,33 +337,34 @@ public final class Map
}
// Engine
- private static native boolean nativeCreateEngine(Surface surface, int density,
- boolean firstLaunch,
+ private static native long nativeCreateEngineId();
+ private static native boolean nativeCreateEngine(long engineId, Surface surface,
+ int density, boolean firstLaunch,
boolean isLaunchByDeepLink,
int appVersionCode);
- private static native boolean nativeIsEngineCreated();
+ private static native boolean nativeIsEngineCreated(long engineId);
private static native void nativeSetRenderingInitializationFinishedListener(
- @Nullable MapRenderingListener listener);
+ long engineId, @Nullable MapRenderingListener listener);
private static native boolean nativeShowMapForUrl(String url);
// Surface
- private static native boolean nativeAttachSurface(Surface surface);
- private static native void nativeDetachSurface(boolean destroySurface);
- private static native void nativeSurfaceChanged(Surface surface, int w, int h);
- private static native boolean nativeDestroySurfaceOnDetach();
- private static native void nativePauseSurfaceRendering();
- private static native void nativeResumeSurfaceRendering();
+ private static native boolean nativeAttachSurface(long engineId, Surface surface);
+ private static native void nativeDetachSurface(long engineId, boolean destroySurface);
+ private static native void nativeSurfaceChanged(long engineId, Surface surface, int w, int h);
+ private static native boolean nativeDestroySurfaceOnDetach(long engineId);
+ private static native void nativePauseSurfaceRendering(long engineId);
+ private static native void nativeResumeSurfaceRendering(long engineId);
// Widgets
- private static native void nativeApplyWidgets();
- private static native void nativeCleanWidgets();
- private static native void nativeSetupWidget(int widget, float x, float y, int anchor);
+ private static native void nativeApplyWidgets(long engineId);
+ private static native void nativeCleanWidgets(long engineId);
+ private static native void nativeSetupWidget(long engineId, int widget, float x, float y, int anchor);
private static native void nativeCompassUpdated(double north, boolean forceRedraw);
// Events
- private static native void nativeMove(double factorX, double factorY, boolean isAnim);
- private static native void nativeScalePlus();
- private static native void nativeScaleMinus();
- private static native void nativeScale(double factor, double focusX, double focusY, boolean isAnim);
- private static native void nativeOnTouch(int actionType, int id1, float x1, float y1, int id2, float x2, float y2, int maskedPointer);
+ private static native void nativeMove(long engineId, double factorX, double factorY, boolean isAnim);
+ private static native void nativeScalePlus(long engineId);
+ private static native void nativeScaleMinus(long engineId);
+ private static native void nativeScale(long engineId, double factor, double focusX, double focusY, boolean isAnim);
+ private static native void nativeOnTouch(long engineId, int actionType, int id1, float x1, float y1, int id2, float x2, float y2, int maskedPointer);
}
diff --git a/android/src/app/organicmaps/MapFragment.java b/android/src/app/organicmaps/MapFragment.java
index 326d8cfe59..d77eea855c 100644
--- a/android/src/app/organicmaps/MapFragment.java
+++ b/android/src/app/organicmaps/MapFragment.java
@@ -164,7 +164,7 @@ public class MapFragment extends BaseMwmFragment implements View.OnTouchListener
action = Map.NATIVE_ACTION_CANCEL;
break;
}
- Map.onTouch(action, event, pointerIndex);
+ mMap.onTouch(action, event, pointerIndex);
return true;
}
diff --git a/android/src/app/organicmaps/MwmActivity.java b/android/src/app/organicmaps/MwmActivity.java
index d47ac999c8..3c5737de56 100644
--- a/android/src/app/organicmaps/MwmActivity.java
+++ b/android/src/app/organicmaps/MwmActivity.java
@@ -642,10 +642,12 @@ public class MwmActivity extends BaseMwmFragmentActivity
switch (button)
{
case zoomIn:
- Map.zoomIn();
+ // TODO: fix
+ // Map.zoomIn();
break;
case zoomOut:
- Map.zoomOut();
+ // TODO: fix
+ // Map.zoomOut();
break;
case myPosition:
LocationState.nativeSwitchToNextMode();
@@ -1715,10 +1717,12 @@ public class MwmActivity extends BaseMwmFragmentActivity
switch (keyCode)
{
case KeyEvent.KEYCODE_DPAD_DOWN:
- Map.zoomOut();
+ // TODO: fix
+ // Map.zoomOut();
return true;
case KeyEvent.KEYCODE_DPAD_UP:
- Map.zoomIn();
+ // TODO: fix
+ // Map.zoomIn();
return true;
case KeyEvent.KEYCODE_ESCAPE:
Intent currIntent = getIntent();
diff --git a/android/src/app/organicmaps/car/NavigationCarAppService.java b/android/src/app/organicmaps/car/NavigationCarAppService.java
new file mode 100644
index 0000000000..3bcadafb78
--- /dev/null
+++ b/android/src/app/organicmaps/car/NavigationCarAppService.java
@@ -0,0 +1,30 @@
+package app.organicmaps.car;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarAppService;
+import androidx.car.app.Session;
+import androidx.car.app.validation.HostValidator;
+
+import app.organicmaps.BuildConfig;
+
+public final class NavigationCarAppService extends CarAppService
+{
+ @NonNull
+ @Override
+ public HostValidator createHostValidator()
+ {
+ if (BuildConfig.DEBUG)
+ return HostValidator.ALLOW_ALL_HOSTS_VALIDATOR;
+
+ return new HostValidator.Builder(getApplicationContext())
+ .addAllowedHosts(androidx.car.app.R.array.hosts_allowlist_sample)
+ .build();
+ }
+
+ @NonNull
+ @Override
+ public Session onCreateSession()
+ {
+ return new NavigationSession();
+ }
+}
diff --git a/android/src/app/organicmaps/car/NavigationSession.java b/android/src/app/organicmaps/car/NavigationSession.java
new file mode 100644
index 0000000000..e500aa5f16
--- /dev/null
+++ b/android/src/app/organicmaps/car/NavigationSession.java
@@ -0,0 +1,69 @@
+package app.organicmaps.car;
+
+import android.content.Intent;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.Screen;
+import androidx.car.app.Session;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.LifecycleOwner;
+
+import app.organicmaps.MwmApplication;
+import app.organicmaps.car.screens.ErrorScreen;
+import app.organicmaps.car.screens.NavigationScreen;
+
+import java.io.IOException;
+
+public final class NavigationSession extends Session implements DefaultLifecycleObserver
+{
+ private static final String TAG = NavigationSession.class.getSimpleName();
+
+ private final SurfaceRenderer mSurfaceRenderer;
+ private boolean mInitFailed = false;
+
+ public NavigationSession()
+ {
+ getLifecycle().addObserver(this);
+ mSurfaceRenderer = new SurfaceRenderer(getCarContext(), getLifecycle());
+ }
+
+ @NonNull
+ @Override
+ public Screen onCreateScreen(@NonNull Intent intent)
+ {
+ Log.d(TAG, "onCreateScreen()");
+ if (mInitFailed)
+ return new ErrorScreen(getCarContext());
+
+ return new NavigationScreen(getCarContext(), mSurfaceRenderer);
+ }
+
+ @Override
+ public void onCreate(@NonNull LifecycleOwner owner)
+ {
+ Log.d(TAG, "onCreate()");
+ init();
+ }
+
+ @Override
+ public void onResume(@NonNull LifecycleOwner owner)
+ {
+ Log.d(TAG, "onResume()");
+ init();
+ }
+
+ private void init()
+ {
+ mInitFailed = false;
+ MwmApplication app = MwmApplication.from(getCarContext());
+ try
+ {
+ app.init();
+ } catch (IOException e)
+ {
+ mInitFailed = true;
+ Log.e(TAG, "Failed to initialize the app.");
+ }
+ }
+}
diff --git a/android/src/app/organicmaps/car/SurfaceRenderer.java b/android/src/app/organicmaps/car/SurfaceRenderer.java
new file mode 100644
index 0000000000..d6ce72dbc8
--- /dev/null
+++ b/android/src/app/organicmaps/car/SurfaceRenderer.java
@@ -0,0 +1,172 @@
+package app.organicmaps.car;
+
+import android.graphics.Rect;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.AppManager;
+import androidx.car.app.CarContext;
+import androidx.car.app.CarToast;
+import androidx.car.app.SurfaceCallback;
+import androidx.car.app.SurfaceContainer;
+import androidx.lifecycle.DefaultLifecycleObserver;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleOwner;
+
+import app.organicmaps.Map;
+import app.organicmaps.R;
+
+public class SurfaceRenderer implements DefaultLifecycleObserver, SurfaceCallback
+{
+ private static final String TAG = SurfaceRenderer.class.getSimpleName();
+
+ private final CarContext mCarContext;
+ private final Map mMap;
+
+ @Nullable
+ private Rect mVisibleArea;
+ @Nullable
+ private Rect mStableArea;
+
+ public SurfaceRenderer(@NonNull CarContext carContext, @NonNull Lifecycle lifecycle)
+ {
+ Log.d(TAG, "SurfaceRenderer()");
+ mCarContext = carContext;
+ mMap = new Map();
+ lifecycle.addObserver(this);
+ }
+
+ @Override
+ public void onSurfaceAvailable(@NonNull SurfaceContainer surfaceContainer)
+ {
+ Log.d(TAG, "Surface available " + surfaceContainer);
+ mMap.onSurfaceCreated(
+ mCarContext,
+ surfaceContainer.getSurface(),
+ new Rect(0, 0, surfaceContainer.getWidth(), surfaceContainer.getHeight()),
+ surfaceContainer.getDpi()
+ );
+ }
+
+ @Override
+ public void onVisibleAreaChanged(@NonNull Rect visibleArea)
+ {
+ Log.d(TAG, "Visible area changed. stableArea: " + mStableArea + " visibleArea:" + visibleArea);
+ mVisibleArea = visibleArea;
+ }
+
+ @Override
+ public void onStableAreaChanged(@NonNull Rect stableArea)
+ {
+ Log.d(TAG, "Stable area changed. stableArea: " + mStableArea + " visibleArea:" + mVisibleArea);
+ mStableArea = stableArea;
+ }
+
+ @Override
+ public void onSurfaceDestroyed(@NonNull SurfaceContainer surfaceContainer)
+ {
+ Log.d(TAG, "Surface destroyed");
+ mMap.onSurfaceDestroyed(false, true);
+ }
+
+ @Override
+ public void onCreate(@NonNull LifecycleOwner owner)
+ {
+ Log.d(TAG, "onCreate");
+ mCarContext.getCarService(AppManager.class).setSurfaceCallback(this);
+
+ // TODO: Properly process deep links from other apps on AA.
+ boolean launchByDeepLink = false;
+ mMap.onCreate(launchByDeepLink);
+ }
+
+ @Override
+ public void onStart(@NonNull LifecycleOwner owner)
+ {
+ Log.d(TAG, "onStart");
+ mMap.onStart();
+ mMap.setCallbackUnsupported(this::reportUnsupported);
+ }
+
+ @Override
+ public void onStop(@NonNull LifecycleOwner owner)
+ {
+ Log.d(TAG, "onStop");
+ mMap.onStop();
+ mMap.setCallbackUnsupported(null);
+ }
+
+ @Override
+ public void onPause(@NonNull LifecycleOwner owner)
+ {
+ Log.d(TAG, "onPause");
+ mMap.onPause(mCarContext);
+ }
+
+ @Override
+ public void onResume(@NonNull LifecycleOwner owner)
+ {
+ Log.d(TAG, "onResume");
+ mMap.onResume();
+ }
+
+ @Override
+ public void onScroll(float distanceX, float distanceY)
+ {
+ Log.d(TAG, "onScroll: distanceX: " + distanceX + ", distanceY: " + distanceY);
+ mMap.onScroll(distanceX, distanceY);
+ }
+
+ @Override
+ public void onFling(float velocityX, float velocityY)
+ {
+ Log.d(TAG, "onFling: velocityX: " + velocityX + ", velocityY: " + velocityY);
+ }
+
+ public void onZoomIn()
+ {
+ mMap.zoomIn();
+ }
+
+ public void onZoomOut()
+ {
+ mMap.zoomOut();
+ }
+
+ @Override
+ public void onScale(float focusX, float focusY, float scaleFactor)
+ {
+ Log.d(TAG, "onScale: focusX: " + focusX + ", focusY: " + focusY + ", scaleFactor: " + scaleFactor);
+ float x = focusX;
+ float y = focusY;
+
+ Rect visibleArea = mVisibleArea;
+ if (visibleArea != null)
+ {
+ // If a focal point value is negative, use the center point of the visible area.
+ if (x < 0)
+ x = visibleArea.centerX();
+ if (y < 0)
+ y = visibleArea.centerY();
+ }
+
+ final boolean animated = Float.compare(scaleFactor, 2f) == 0;
+
+ mMap.onScale(scaleFactor, x, y, animated);
+ }
+
+ @Override
+ public void onClick(float x, float y)
+ {
+ Log.d(TAG, "onClick: x: " + x + ", y: " + y);
+ mMap.onTouch(x, y);
+ }
+
+ private void reportUnsupported()
+ {
+ String message = mCarContext.getString(R.string.unsupported_phone);
+ Log.e(TAG, mCarContext.getString(R.string.unsupported_phone));
+ CarToast.makeText(mCarContext, message, CarToast.LENGTH_LONG).show();
+ }
+}
diff --git a/android/src/app/organicmaps/car/UiHelpers.java b/android/src/app/organicmaps/car/UiHelpers.java
new file mode 100644
index 0000000000..c7980b89c6
--- /dev/null
+++ b/android/src/app/organicmaps/car/UiHelpers.java
@@ -0,0 +1,47 @@
+package app.organicmaps.car;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.CarToast;
+import androidx.car.app.ScreenManager;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.ActionStrip;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.navigation.model.MapController;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.car.screens.settings.SettingsScreen;
+
+public final class UiHelpers
+{
+ public static ActionStrip createSettingsActionStrip(@NonNull CarContext context, @NonNull SurfaceRenderer surfaceRenderer)
+ {
+ final CarIcon iconSettings = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_settings)).build();
+ final Action settings = new Action.Builder().setIcon(iconSettings).setOnClickListener(
+ () -> context.getCarService(ScreenManager.class).push(new SettingsScreen(context, surfaceRenderer))
+ ).build();
+ return new ActionStrip.Builder().addAction(settings).build();
+ }
+
+ public static MapController createMapController(@NonNull CarContext context, @NonNull SurfaceRenderer surfaceRenderer)
+ {
+ final CarIcon iconPlus = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_plus)).build();
+ final CarIcon iconMinus = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_minus)).build();
+ final CarIcon iconLocation = new CarIcon.Builder(IconCompat.createWithResource(context, R.drawable.ic_not_follow)).build();
+
+ final Action panAction = new Action.Builder(Action.PAN).build();
+ final Action location = new Action.Builder().setIcon(iconLocation).setOnClickListener(
+ () -> CarToast.makeText(context, "Location", CarToast.LENGTH_LONG).show()
+ ).build();
+ final Action zoomIn = new Action.Builder().setIcon(iconPlus).setOnClickListener(surfaceRenderer::onZoomIn).build();
+ final Action zoomOut = new Action.Builder().setIcon(iconMinus).setOnClickListener(surfaceRenderer::onZoomOut).build();
+ final ActionStrip mapActionStrip = new ActionStrip.Builder()
+ .addAction(location)
+ .addAction(zoomIn)
+ .addAction(zoomOut)
+ .addAction(panAction)
+ .build();
+ return new MapController.Builder().setMapActionStrip(mapActionStrip).build();
+ }
+}
diff --git a/android/src/app/organicmaps/car/screens/BookmarksScreen.java b/android/src/app/organicmaps/car/screens/BookmarksScreen.java
new file mode 100644
index 0000000000..42cb9dee3b
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/BookmarksScreen.java
@@ -0,0 +1,135 @@
+package app.organicmaps.car.screens;
+
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.car.app.CarContext;
+import androidx.car.app.constraints.ConstraintManager;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.Header;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapTemplate;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.bookmarks.data.BookmarkCategory;
+import app.organicmaps.bookmarks.data.BookmarkInfo;
+import app.organicmaps.bookmarks.data.BookmarkManager;
+import app.organicmaps.car.SurfaceRenderer;
+import app.organicmaps.car.UiHelpers;
+import app.organicmaps.util.Graphics;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class BookmarksScreen extends MapScreen
+{
+ private final int MAX_CATEGORIES_SIZE;
+
+ @Nullable
+ private BookmarkCategory mBookmarkCategory;
+
+ public BookmarksScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+ {
+ super(carContext, surfaceRenderer);
+ final ConstraintManager constraintManager = getCarContext().getCarService(ConstraintManager.class);
+ MAX_CATEGORIES_SIZE = constraintManager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST);
+ }
+
+ private BookmarksScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer, @NonNull BookmarkCategory bookmarkCategory)
+ {
+ this(carContext, surfaceRenderer);
+ mBookmarkCategory = bookmarkCategory;
+ }
+
+ @NonNull
+ @Override
+ public Template onGetTemplate()
+ {
+ MapTemplate.Builder builder = new MapTemplate.Builder();
+ builder.setHeader(createHeader());
+ builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
+ builder.setActionStrip(UiHelpers.createSettingsActionStrip(getCarContext(), getSurfaceRenderer()));
+ builder.setItemList(mBookmarkCategory == null ? createBookmarkCategoriesList() : createBookmarksList());
+ return builder.build();
+ }
+
+ @NonNull
+ private Header createHeader()
+ {
+ Header.Builder builder = new Header.Builder();
+ builder.setStartHeaderAction(Action.BACK);
+ builder.setTitle(mBookmarkCategory == null ? getCarContext().getString(R.string.bookmarks) : mBookmarkCategory.getName());
+ return builder.build();
+ }
+
+ @NonNull
+ private ItemList createBookmarkCategoriesList()
+ {
+ final List bookmarkCategories = getBookmarks();
+ final int categoriesSize = Math.min(bookmarkCategories.size(), MAX_CATEGORIES_SIZE);
+
+ ItemList.Builder builder = new ItemList.Builder();
+ for (int i = 0; i < categoriesSize; ++i)
+ {
+ final BookmarkCategory bookmarkCategory = bookmarkCategories.get(i);
+
+ Row.Builder itemBuilder = new Row.Builder();
+ itemBuilder.setTitle(bookmarkCategory.getName());
+ itemBuilder.addText(bookmarkCategory.getDescription());
+ itemBuilder.setOnClickListener(() -> getScreenManager().push(new BookmarksScreen(getCarContext(), getSurfaceRenderer(), bookmarkCategory)));
+ itemBuilder.setBrowsable(true);
+ builder.addItem(itemBuilder.build());
+ }
+ return builder.build();
+ }
+
+ @NonNull
+ private ItemList createBookmarksList()
+ {
+ final long bookmarkCategoryId = mBookmarkCategory.getId();
+ final int bookmarkCategoriesSize = Math.min(mBookmarkCategory.getBookmarksCount(), MAX_CATEGORIES_SIZE);
+
+ ItemList.Builder builder = new ItemList.Builder();
+ for (int i = 0; i < bookmarkCategoriesSize; ++i)
+ {
+ final long bookmarkId = BookmarkManager.INSTANCE.getBookmarkIdByPosition(bookmarkCategoryId, i);
+ final BookmarkInfo bookmarkInfo = new BookmarkInfo(bookmarkCategoryId, bookmarkId);
+
+ final Row.Builder itemBuilder = new Row.Builder();
+ itemBuilder.setTitle(bookmarkInfo.getName());
+ if (!bookmarkInfo.getAddress().isEmpty())
+ itemBuilder.addText(bookmarkInfo.getAddress());
+ if (!bookmarkInfo.getFeatureType().isEmpty())
+ itemBuilder.addText(bookmarkInfo.getFeatureType());
+ final Drawable icon = Graphics.drawCircleAndImage(bookmarkInfo.getIcon().argb(),
+ R.dimen.track_circle_size,
+ bookmarkInfo.getIcon().getResId(),
+ R.dimen.bookmark_icon_size,
+ getCarContext());
+ itemBuilder.setImage(new CarIcon.Builder(IconCompat.createWithBitmap(Graphics.drawableToBitmap(icon))).build());
+ builder.addItem(itemBuilder.build());
+ }
+ return builder.build();
+ }
+
+ @NonNull
+ private static List getBookmarks()
+ {
+ final List bookmarkCategories = new ArrayList<>(BookmarkManager.INSTANCE.getCategories());
+
+ final List toRemove = new ArrayList<>();
+ for (BookmarkCategory bookmarkCategory : bookmarkCategories)
+ {
+ if (bookmarkCategory.getBookmarksCount() == 0)
+ toRemove.add(bookmarkCategory);
+ }
+ bookmarkCategories.removeAll(toRemove);
+
+ return bookmarkCategories;
+ }
+}
diff --git a/android/src/app/organicmaps/car/screens/CategoriesScreen.java b/android/src/app/organicmaps/car/screens/CategoriesScreen.java
new file mode 100644
index 0000000000..4595273821
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/CategoriesScreen.java
@@ -0,0 +1,95 @@
+package app.organicmaps.car.screens;
+
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+import androidx.car.app.CarContext;
+import androidx.car.app.constraints.ConstraintManager;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.Header;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapTemplate;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.car.SurfaceRenderer;
+import app.organicmaps.car.UiHelpers;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class CategoriesScreen extends MapScreen
+{
+ private static class CategoryData
+ {
+ @StringRes
+ public final int nameResId;
+
+ @DrawableRes
+ public final int iconResId;
+
+ public CategoryData(int nameResId, int iconResId)
+ {
+ this.nameResId = nameResId;
+ this.iconResId = iconResId;
+ }
+ }
+
+ private static final List CATEGORIES = Arrays.asList(
+ new CategoryData(R.string.fuel, R.drawable.ic_category_fuel),
+ new CategoryData(R.string.parking, R.drawable.ic_category_parking),
+ new CategoryData(R.string.eat, R.drawable.ic_category_eat),
+ new CategoryData(R.string.food, R.drawable.ic_category_food),
+ new CategoryData(R.string.hotel, R.drawable.ic_category_hotel),
+ new CategoryData(R.string.toilet, R.drawable.ic_category_toilet),
+ new CategoryData(R.string.rv, R.drawable.ic_category_rv)
+ );
+
+ private final int MAX_CATEGORIES_SIZE;
+
+ public CategoriesScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+ {
+ super(carContext, surfaceRenderer);
+ final ConstraintManager constraintManager = getCarContext().getCarService(ConstraintManager.class);
+ MAX_CATEGORIES_SIZE = constraintManager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST);
+ }
+
+ @NonNull
+ @Override
+ public Template onGetTemplate()
+ {
+ MapTemplate.Builder builder = new MapTemplate.Builder();
+ builder.setHeader(createHeader());
+ builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
+ builder.setActionStrip(UiHelpers.createSettingsActionStrip(getCarContext(), getSurfaceRenderer()));
+ builder.setItemList(createCategoriesList());
+ return builder.build();
+ }
+
+ @NonNull
+ private Header createHeader()
+ {
+ Header.Builder builder = new Header.Builder();
+ builder.setStartHeaderAction(Action.BACK);
+ builder.setTitle(getCarContext().getString(R.string.categories));
+ return builder.build();
+ }
+
+ @NonNull
+ private ItemList createCategoriesList()
+ {
+ ItemList.Builder builder = new ItemList.Builder();
+ int categoriesSize = Math.min(CATEGORIES.size(), MAX_CATEGORIES_SIZE);
+ for (int i = 0; i < categoriesSize; ++i)
+ {
+ Row.Builder itemBuilder = new Row.Builder();
+ itemBuilder.setTitle(getCarContext().getString(CATEGORIES.get(i).nameResId));
+ itemBuilder.setImage(new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), CATEGORIES.get(i).iconResId)).build());
+ builder.addItem(itemBuilder.build());
+ }
+ return builder.build();
+ }
+}
diff --git a/android/src/app/organicmaps/car/screens/ErrorScreen.java b/android/src/app/organicmaps/car/screens/ErrorScreen.java
new file mode 100644
index 0000000000..bcdaf8525f
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/ErrorScreen.java
@@ -0,0 +1,32 @@
+package app.organicmaps.car.screens;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.Screen;
+import androidx.car.app.model.LongMessageTemplate;
+import androidx.car.app.model.Template;
+
+import app.organicmaps.R;
+
+public class ErrorScreen extends Screen
+{
+ private static final String TAG = ErrorScreen.class.getSimpleName();
+
+ public ErrorScreen(@NonNull CarContext carContext)
+ {
+ super(carContext);
+ }
+
+ @NonNull
+ @Override
+ public Template onGetTemplate()
+ {
+ Log.d(TAG, "onGetTemplate");
+ LongMessageTemplate.Builder builder = new LongMessageTemplate.Builder(getCarContext().getString(R.string.dialog_error_storage_message));
+ builder.setTitle(getCarContext().getString(R.string.dialog_error_storage_title));
+
+ return builder.build();
+ }
+}
diff --git a/android/src/app/organicmaps/car/screens/MapScreen.java b/android/src/app/organicmaps/car/screens/MapScreen.java
new file mode 100644
index 0000000000..38ab3de45d
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/MapScreen.java
@@ -0,0 +1,25 @@
+package app.organicmaps.car.screens;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.Screen;
+
+import app.organicmaps.car.SurfaceRenderer;
+
+public abstract class MapScreen extends Screen
+{
+ @NonNull
+ private final SurfaceRenderer mSurfaceRenderer;
+
+ public MapScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+ {
+ super(carContext);
+ mSurfaceRenderer = surfaceRenderer;
+ }
+
+ @NonNull
+ public SurfaceRenderer getSurfaceRenderer()
+ {
+ return mSurfaceRenderer;
+ }
+}
diff --git a/android/src/app/organicmaps/car/screens/NavigationScreen.java b/android/src/app/organicmaps/car/screens/NavigationScreen.java
new file mode 100644
index 0000000000..477113f8d0
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/NavigationScreen.java
@@ -0,0 +1,104 @@
+package app.organicmaps.car.screens;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.Header;
+import androidx.car.app.model.Item;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapTemplate;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.car.SurfaceRenderer;
+import app.organicmaps.car.UiHelpers;
+
+public class NavigationScreen extends MapScreen
+{
+ public NavigationScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+ {
+ super(carContext, surfaceRenderer);
+ }
+
+ @NonNull
+ @Override
+ public Template onGetTemplate()
+ {
+ MapTemplate.Builder builder = new MapTemplate.Builder();
+ builder.setHeader(createHeader());
+ builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
+ builder.setActionStrip(UiHelpers.createSettingsActionStrip(getCarContext(), getSurfaceRenderer()));
+ builder.setItemList(createList());
+ return builder.build();
+ }
+
+ @NonNull
+ private Header createHeader()
+ {
+ Header.Builder builder = new Header.Builder();
+ builder.setStartHeaderAction(new Action.Builder(Action.APP_ICON).build());
+ builder.setTitle(getCarContext().getString(R.string.app_name));
+ return builder.build();
+ }
+
+ @NonNull
+ private ItemList createList()
+ {
+ ItemList.Builder builder = new ItemList.Builder();
+ builder.addItem(createSearchItem());
+ builder.addItem(createCategoriesItem());
+ builder.addItem(createBookmarksItem());
+ return builder.build();
+ }
+
+ @NonNull
+ private Item createSearchItem()
+ {
+ final CarIcon iconSearch = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_search)).build();
+
+ Row.Builder builder = new Row.Builder();
+ builder.setTitle(getCarContext().getString(R.string.search));
+ builder.setImage(iconSearch);
+ builder.setBrowsable(true);
+ builder.setOnClickListener(this::openSearch);
+ return builder.build();
+ }
+
+ @NonNull
+ private Item createCategoriesItem()
+ {
+ Row.Builder builder = new Row.Builder();
+ builder.setTitle(getCarContext().getString(R.string.categories));
+ builder.setBrowsable(true);
+ builder.setOnClickListener(this::openCategories);
+ return builder.build();
+ }
+
+ @NonNull
+ private Item createBookmarksItem()
+ {
+ Row.Builder builder = new Row.Builder();
+ builder.setTitle(getCarContext().getString(R.string.bookmarks));
+ builder.setBrowsable(true);
+ builder.setOnClickListener(this::openBookmarks);
+ return builder.build();
+ }
+
+ private void openSearch()
+ {
+ getScreenManager().push(new SearchScreen(getCarContext()));
+ }
+
+ private void openCategories()
+ {
+ getScreenManager().push(new CategoriesScreen(getCarContext(), getSurfaceRenderer()));
+ }
+
+ private void openBookmarks()
+ {
+ getScreenManager().push(new BookmarksScreen(getCarContext(), getSurfaceRenderer()));
+ }
+}
diff --git a/android/src/app/organicmaps/car/screens/SearchScreen.java b/android/src/app/organicmaps/car/screens/SearchScreen.java
new file mode 100644
index 0000000000..f6464aa869
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/SearchScreen.java
@@ -0,0 +1,62 @@
+package app.organicmaps.car.screens;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.Screen;
+import androidx.car.app.constraints.ConstraintManager;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.SearchTemplate;
+import androidx.car.app.model.Template;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.search.SearchRecents;
+
+public class SearchScreen extends Screen implements SearchTemplate.SearchCallback
+{
+ private final int MAX_RESULTS_SIZE;
+ private ItemList mResults;
+ private String mSearchText = "";
+
+ public SearchScreen(@NonNull CarContext carContext)
+ {
+ super(carContext);
+ final ConstraintManager constraintManager = getCarContext().getCarService(ConstraintManager.class);
+ MAX_RESULTS_SIZE = constraintManager.getContentLimit(ConstraintManager.CONTENT_LIMIT_TYPE_LIST);
+ }
+
+ @NonNull
+ @Override
+ public Template onGetTemplate()
+ {
+ SearchTemplate.Builder builder = new SearchTemplate.Builder(this);
+ builder.setHeaderAction(Action.BACK);
+ builder.setShowKeyboardByDefault(false);
+ if (mSearchText.isEmpty() || mResults == null)
+ loadRecents();
+ builder.setItemList(mResults);
+ builder.setInitialSearchText(mSearchText);
+ return builder.build();
+ }
+
+ private void loadRecents()
+ {
+ final CarIcon iconRecent = new CarIcon.Builder(IconCompat.createWithResource(getCarContext(), R.drawable.ic_search_recent)).build();
+
+ ItemList.Builder builder = new ItemList.Builder();
+ builder.setNoItemsMessage(getCarContext().getString(R.string.search_history_text));
+ SearchRecents.refresh();
+ int recentsSize = Math.min(SearchRecents.getSize(), MAX_RESULTS_SIZE);
+ for (int i = 0; i < recentsSize; ++i)
+ {
+ Row.Builder itemBuilder = new Row.Builder();
+ itemBuilder.setTitle(SearchRecents.get(i));
+ itemBuilder.setImage(iconRecent);
+ builder.addItem(itemBuilder.build());
+ }
+ mResults = builder.build();
+ }
+}
diff --git a/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java b/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java
new file mode 100644
index 0000000000..3142500653
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/settings/DrivingOptionsScreen.java
@@ -0,0 +1,82 @@
+package app.organicmaps.car.screens.settings;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+import androidx.car.app.CarContext;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.Header;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapTemplate;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.car.SurfaceRenderer;
+import app.organicmaps.car.UiHelpers;
+import app.organicmaps.car.screens.MapScreen;
+import app.organicmaps.routing.RoutingOptions;
+import app.organicmaps.settings.RoadType;
+
+public class DrivingOptionsScreen extends MapScreen
+{
+ @NonNull
+ private final CarIcon mCheckboxIcon;
+ @NonNull
+ private final CarIcon mCheckboxSelectedIcon;
+
+ public DrivingOptionsScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+ {
+ super(carContext, surfaceRenderer);
+ mCheckboxIcon = new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_check_box)).build();
+ mCheckboxSelectedIcon = new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_check_box_checked)).build();
+ }
+
+ @NonNull
+ @Override
+ public Template onGetTemplate()
+ {
+ MapTemplate.Builder builder = new MapTemplate.Builder();
+ builder.setHeader(createHeader());
+ builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
+ builder.setItemList(createDrivingOptionsList());
+ return builder.build();
+ }
+
+ @NonNull
+ private Header createHeader()
+ {
+ Header.Builder builder = new Header.Builder();
+ builder.setStartHeaderAction(Action.BACK);
+ builder.setTitle(getCarContext().getString(R.string.driving_options_subheader));
+ return builder.build();
+ }
+
+ @NonNull
+ private ItemList createDrivingOptionsList()
+ {
+ ItemList.Builder builder = new ItemList.Builder();
+ builder.addItem(createDrivingOptionCheckbox(RoadType.Toll, R.string.avoid_tolls));
+ builder.addItem(createDrivingOptionCheckbox(RoadType.Dirty, R.string.avoid_unpaved));
+ builder.addItem(createDrivingOptionCheckbox(RoadType.Ferry, R.string.avoid_ferry));
+ builder.addItem(createDrivingOptionCheckbox(RoadType.Motorway, R.string.avoid_motorways));
+ return builder.build();
+ }
+
+ @NonNull
+ private Row createDrivingOptionCheckbox(RoadType roadType, @StringRes int titleRes)
+ {
+ Row.Builder builder = new Row.Builder();
+ builder.setTitle(getCarContext().getString(titleRes));
+ builder.setOnClickListener(() -> {
+ if (RoutingOptions.hasOption(roadType))
+ RoutingOptions.removeOption(roadType);
+ else
+ RoutingOptions.addOption(roadType);
+ DrivingOptionsScreen.this.invalidate();
+ });
+ builder.setImage(RoutingOptions.hasOption(roadType) ? mCheckboxSelectedIcon : mCheckboxIcon);
+ return builder.build();
+ }
+}
diff --git a/android/src/app/organicmaps/car/screens/settings/HelpScreen.java b/android/src/app/organicmaps/car/screens/settings/HelpScreen.java
new file mode 100644
index 0000000000..aa1ffab2f9
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/settings/HelpScreen.java
@@ -0,0 +1,74 @@
+package app.organicmaps.car.screens.settings;
+
+import androidx.annotation.NonNull;
+import androidx.car.app.CarContext;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.Header;
+import androidx.car.app.model.Item;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapTemplate;
+
+import app.organicmaps.BuildConfig;
+import app.organicmaps.Framework;
+import app.organicmaps.R;
+import app.organicmaps.car.SurfaceRenderer;
+import app.organicmaps.car.UiHelpers;
+import app.organicmaps.car.screens.MapScreen;
+import app.organicmaps.util.DateUtils;
+
+public class HelpScreen extends MapScreen
+{
+ public HelpScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+ {
+ super(carContext, surfaceRenderer);
+ }
+
+ @NonNull
+ @Override
+ public Template onGetTemplate()
+ {
+ MapTemplate.Builder builder = new MapTemplate.Builder();
+ builder.setHeader(createHeader());
+ builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
+ builder.setItemList(createSettingsList());
+ return builder.build();
+ }
+
+ @NonNull
+ private Header createHeader()
+ {
+ Header.Builder builder = new Header.Builder();
+ builder.setStartHeaderAction(Action.BACK);
+ builder.setTitle(getCarContext().getString(R.string.help));
+ return builder.build();
+ }
+
+ @NonNull
+ private ItemList createSettingsList()
+ {
+ ItemList.Builder builder = new ItemList.Builder();
+ builder.addItem(createVersionInfo());
+ builder.addItem(createDataVersionInfo());
+ return builder.build();
+ }
+
+ @NonNull
+ private Item createVersionInfo()
+ {
+ return new Row.Builder()
+ .setTitle(getCarContext().getString(R.string.app_name))
+ .addText(BuildConfig.VERSION_NAME)
+ .build();
+ }
+
+ @NonNull
+ private Item createDataVersionInfo()
+ {
+ return new Row.Builder()
+ .setTitle(getCarContext().getString(R.string.data_version, ""))
+ .addText(DateUtils.getLocalDate(Framework.nativeGetDataVersion()))
+ .build();
+ }
+}
diff --git a/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java b/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java
new file mode 100644
index 0000000000..58acd4942f
--- /dev/null
+++ b/android/src/app/organicmaps/car/screens/settings/SettingsScreen.java
@@ -0,0 +1,111 @@
+package app.organicmaps.car.screens.settings;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+import androidx.car.app.CarContext;
+import androidx.car.app.model.Action;
+import androidx.car.app.model.CarIcon;
+import androidx.car.app.model.Header;
+import androidx.car.app.model.Item;
+import androidx.car.app.model.ItemList;
+import androidx.car.app.model.Row;
+import androidx.car.app.model.Template;
+import androidx.car.app.navigation.model.MapTemplate;
+import androidx.core.graphics.drawable.IconCompat;
+
+import app.organicmaps.R;
+import app.organicmaps.car.SurfaceRenderer;
+import app.organicmaps.car.UiHelpers;
+import app.organicmaps.car.screens.MapScreen;
+import app.organicmaps.util.Config;
+
+public class SettingsScreen extends MapScreen
+{
+ private interface PrefsGetter
+ {
+ boolean get();
+ }
+
+ private interface PrefsSetter
+ {
+ void set(boolean newValue);
+ }
+
+ @NonNull
+ private final CarIcon mCheckboxIcon;
+ @NonNull
+ private final CarIcon mCheckboxSelectedIcon;
+
+ public SettingsScreen(@NonNull CarContext carContext, @NonNull SurfaceRenderer surfaceRenderer)
+ {
+ super(carContext, surfaceRenderer);
+ mCheckboxIcon = new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_check_box)).build();
+ mCheckboxSelectedIcon = new CarIcon.Builder(IconCompat.createWithResource(carContext, R.drawable.ic_check_box_checked)).build();
+ }
+
+ @NonNull
+ @Override
+ public Template onGetTemplate()
+ {
+ MapTemplate.Builder builder = new MapTemplate.Builder();
+ builder.setHeader(createHeader());
+ builder.setMapController(UiHelpers.createMapController(getCarContext(), getSurfaceRenderer()));
+ builder.setItemList(createSettingsList());
+ return builder.build();
+ }
+
+ @NonNull
+ private Header createHeader()
+ {
+ Header.Builder builder = new Header.Builder();
+ builder.setStartHeaderAction(Action.BACK);
+ builder.setTitle(getCarContext().getString(R.string.settings));
+ return builder.build();
+ }
+
+ @NonNull
+ private ItemList createSettingsList()
+ {
+ ItemList.Builder builder = new ItemList.Builder();
+ builder.addItem(createRoutingOptionsItem());
+ builder.addItem(createSharedPrefsCheckbox(R.string.big_font, Config::isLargeFontsSize, Config::setLargeFontsSize));
+ builder.addItem(createSharedPrefsCheckbox(R.string.transliteration_title, Config::isTransliteration, Config::setTransliteration));
+ builder.addItem(createHelpItem());
+ return builder.build();
+ }
+
+ @NonNull
+ private Item createRoutingOptionsItem()
+ {
+ Row.Builder builder = new Row.Builder();
+ builder.setTitle(getCarContext().getString(R.string.driving_options_title));
+ builder.setOnClickListener(() -> getScreenManager().push(new DrivingOptionsScreen(getCarContext(), getSurfaceRenderer())));
+ builder.setBrowsable(true);
+ return builder.build();
+ }
+
+ @NonNull
+ private Item createHelpItem()
+ {
+ Row.Builder builder = new Row.Builder();
+ builder.setTitle(getCarContext().getString(R.string.help));
+ builder.setOnClickListener(() -> getScreenManager().push(new HelpScreen(getCarContext(), getSurfaceRenderer())));
+ builder.setBrowsable(true);
+ return builder.build();
+ }
+
+ @NonNull
+ private Row createSharedPrefsCheckbox(@StringRes int titleRes, PrefsGetter getter, PrefsSetter setter)
+ {
+ final boolean getterValue = getter.get();
+
+ Row.Builder builder = new Row.Builder();
+ builder.setTitle(getCarContext().getString(titleRes));
+ builder.setOnClickListener(() -> {
+ setter.set(!getterValue);
+ SettingsScreen.this.invalidate();
+ });
+ builder.setImage(getterValue ? mCheckboxSelectedIcon : mCheckboxIcon);
+ return builder.build();
+ }
+}
diff --git a/drape/stipple_pen_resource.cpp b/drape/stipple_pen_resource.cpp
index 59b7ea24b5..9a575f7ec9 100644
--- a/drape/stipple_pen_resource.cpp
+++ b/drape/stipple_pen_resource.cpp
@@ -188,8 +188,8 @@ void StipplePenIndex::UploadResources(ref_ptr context, ref_
// Assume that all patterns are initialized when creating texture (ReserveResource) and uploaded once.
// Should provide additional logic like in ColorPalette::UploadResources, if we want multiple uploads.
- if (m_uploadCalled)
- LOG(LERROR, ("Multiple stipple pen texture uploads are not supported"));
+// if (m_uploadCalled)
+// LOG(LERROR, ("Multiple stipple pen texture uploads are not supported"));
m_uploadCalled = true;
uint32_t height = 0;
diff --git a/drape_frontend/drape_engine.hpp b/drape_frontend/drape_engine.hpp
index 6cc60655b0..c4954d3a35 100644
--- a/drape_frontend/drape_engine.hpp
+++ b/drape_frontend/drape_engine.hpp
@@ -46,6 +46,8 @@ namespace df
class UserMarksProvider;
class MapDataProvider;
+using DrapeEngineId = std::size_t;
+
class DrapeEngine
{
public:
diff --git a/map/benchmark_tools.cpp b/map/benchmark_tools.cpp
index 15c5dee692..bc03964480 100644
--- a/map/benchmark_tools.cpp
+++ b/map/benchmark_tools.cpp
@@ -57,7 +57,8 @@ void RunScenario(Framework * framework, std::shared_ptr handle)
auto & scenarioData = handle->m_scenariosToRun[handle->m_currentScenario];
- framework->GetDrapeEngine()->RunScenario(std::move(scenarioData),
+ // TODO: fix
+ framework->GetDrapeEngine(0)->RunScenario(std::move(scenarioData),
[handle](std::string const & name)
{
#ifdef DRAPE_MEASURER_BENCHMARK
diff --git a/map/framework.cpp b/map/framework.cpp
index 403c4a2c69..f579c336c7 100644
--- a/map/framework.cpp
+++ b/map/framework.cpp
@@ -153,8 +153,11 @@ pair Framework::RegisterMap(LocalCountryFile c
void Framework::OnLocationError(TLocationError /*error*/)
{
m_trafficManager.UpdateMyPosition(TrafficManager::MyPosition());
- if (m_drapeEngine != nullptr)
- m_drapeEngine->LoseLocation();
+ for (const auto& [_, drapeEngineData] : m_drapeEngines)
+ {
+ if (drapeEngineData.m_drapeEngine)
+ drapeEngineData.m_drapeEngine->LoseLocation();
+ }
}
void Framework::OnLocationUpdate(GpsInfo const & info)
@@ -193,14 +196,18 @@ void Framework::OnCompassUpdate(CompassInfo const & info)
CompassInfo const & rInfo = info;
#endif
- if (m_drapeEngine != nullptr)
- m_drapeEngine->SetCompassInfo(rInfo);
+ for (const auto& [_, drapeEngineData] : m_drapeEngines)
+ {
+ if (drapeEngineData.m_drapeEngine)
+ drapeEngineData.m_drapeEngine->SetCompassInfo(rInfo);
+ }
}
void Framework::SwitchMyPositionNextMode()
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->SwitchMyPositionNextMode();
+ for (const auto& [engineId, drapeEngineData] : m_drapeEngines)
+ if (drapeEngineData.m_drapeEngine)
+ drapeEngineData.m_drapeEngine->SwitchMyPositionNextMode();
}
void Framework::SetMyPositionModeListener(TMyPositionModeChanged && fn)
@@ -215,7 +222,10 @@ void Framework::SetMyPositionPendingTimeoutListener(df::DrapeEngine::UserPositio
EMyPositionMode Framework::GetMyPositionMode() const
{
- return m_drapeEngine ? m_drapeEngine->GetMyPositionMode() : PendingPosition;
+ for (const auto& [engineId, drapeEngineData] : m_drapeEngines)
+ if (drapeEngineData.m_drapeEngine)
+ return drapeEngineData.m_drapeEngine->GetMyPositionMode();
+ return PendingPosition;
}
TrafficManager & Framework::GetTrafficManager()
@@ -804,11 +814,11 @@ void Framework::ShowBookmark(Bookmark const * mark)
auto es = GetBookmarkManager().GetEditSession();
es.SetIsVisible(mark->GetGroupId(), true /* visible */);
- if (m_drapeEngine != nullptr)
- {
- m_drapeEngine->SetModelViewCenter(mark->GetPivot(), scale, true /* isAnim */,
- true /* trackVisibleViewport */);
- }
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// {
+// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewCenter(mark->GetPivot(), scale, true /* isAnim */,
+// true /* trackVisibleViewport */);
+// }
ActivateMapSelection();
}
@@ -867,13 +877,13 @@ void Framework::ShowFeature(FeatureID const & featureId)
info.m_match = place_page::BuildInfo::Match::FeatureOnly;
m_currentPlacePageInfo = BuildPlacePageInfo(info);
- if (m_drapeEngine != nullptr)
- {
- auto const pt = m_currentPlacePageInfo->GetMercator();
- auto const scale = scales::GetUpperComfortScale();
- m_drapeEngine->SetModelViewCenter(pt, scale, true /* isAnim */, true /* trackVisibleViewport */);
- }
- ActivateMapSelection();
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// {
+// auto const pt = m_currentPlacePageInfo->GetMercator();
+// auto const scale = scales::GetUpperComfortScale();
+// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewCenter(pt, scale, true /* isAnim */, true /* trackVisibleViewport */);
+// }
+// ActivateMapSelection();
}
void Framework::AddBookmarksFile(string const & filePath, bool isTemporaryFile)
@@ -907,8 +917,8 @@ void Framework::LoadViewport()
m2::AnyRectD rect;
if (settings::Get("ScreenClipRect", rect) && df::GetWorldRect().IsRectInside(rect.GetGlobalRect()))
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->SetModelViewAnyRect(rect, false /* isAnim */, false /* useVisibleViewport */);
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewAnyRect(rect, false /* isAnim */, false /* useVisibleViewport */);
}
else
{
@@ -918,10 +928,10 @@ void Framework::LoadViewport()
void Framework::ShowAll()
{
- if (m_drapeEngine == nullptr)
- return;
- m_drapeEngine->SetModelViewAnyRect(m2::AnyRectD(m_featuresFetcher.GetWorldRect()), false /* isAnim */,
- false /* useVisibleViewport */);
+// if (!m_drapeEngines.begin()->second.m_drapeEngine)
+// return;
+// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewAnyRect(m2::AnyRectD(m_featuresFetcher.GetWorldRect()), false /* isAnim */,
+// false /* useVisibleViewport */);
}
m2::PointD Framework::GetVisiblePixelCenter() const
@@ -937,8 +947,8 @@ m2::PointD const & Framework::GetViewportCenter() const
void Framework::SetViewportCenter(m2::PointD const & pt, int zoomLevel /* = -1 */,
bool isAnim /* = true */)
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->SetModelViewCenter(pt, zoomLevel, isAnim, false /* trackVisibleViewport */);
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewCenter(pt, zoomLevel, isAnim, false /* trackVisibleViewport */);
}
m2::RectD Framework::GetCurrentViewport() const
@@ -946,9 +956,9 @@ m2::RectD Framework::GetCurrentViewport() const
return m_currentModelView.ClipRect();
}
-void Framework::SetVisibleViewport(m2::RectD const & rect)
+void Framework::SetVisibleViewport(df::DrapeEngineId engineId, m2::RectD const & rect)
{
- if (m_drapeEngine == nullptr)
+ if (!m_drapeEngines[engineId].m_drapeEngine)
return;
double constexpr kEps = 0.5;
@@ -960,22 +970,22 @@ void Framework::SetVisibleViewport(m2::RectD const & rect)
return;
m_visibleViewport = rect;
- m_drapeEngine->SetVisibleViewport(rect);
+ m_drapeEngines[engineId].m_drapeEngine->SetVisibleViewport(rect);
}
void Framework::ShowRect(m2::RectD const & rect, int maxScale, bool animation, bool useVisibleViewport)
{
- if (m_drapeEngine == nullptr)
- return;
-
- m_drapeEngine->SetModelViewRect(rect, true /* applyRotation */, maxScale /* zoom */, animation,
- useVisibleViewport);
+// if (!m_drapeEngines.begin()->second.m_drapeEngine)
+// return;
+//
+// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewRect(rect, true /* applyRotation */, maxScale /* zoom */, animation,
+// useVisibleViewport);
}
void Framework::ShowRect(m2::AnyRectD const & rect, bool animation, bool useVisibleViewport)
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->SetModelViewAnyRect(rect, animation, useVisibleViewport);
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewAnyRect(rect, animation, useVisibleViewport);
}
void Framework::GetTouchRect(m2::PointD const & center, uint32_t pxRadius, m2::AnyRectD & rect)
@@ -991,26 +1001,26 @@ void Framework::SetViewportListener(TViewportChangedFn const & fn)
#if defined(OMIM_OS_MAC) || defined(OMIM_OS_LINUX)
void Framework::NotifyGraphicsReady(TGraphicsReadyFn const & fn, bool needInvalidate)
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->NotifyGraphicsReady(fn, needInvalidate);
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// m_drapeEngines.begin()->second.m_drapeEngine->NotifyGraphicsReady(fn, needInvalidate);
}
#endif
void Framework::StopLocationFollow()
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->StopLocationFollow();
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// m_drapeEngines.begin()->second.m_drapeEngine->StopLocationFollow();
}
-void Framework::OnSize(int w, int h)
+void Framework::OnSize(df::DrapeEngineId engineId, int w, int h)
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->Resize(std::max(w, 2), std::max(h, 2));
+ if (m_drapeEngines[engineId].m_drapeEngine != nullptr)
+ m_drapeEngines[engineId].m_drapeEngine->Resize(std::max(w, 2), std::max(h, 2));
/// @todo Expected that DrapeEngine::Resize does all the work, but nope ..
/// - Strange, but seems like iOS works fine without it.
/// - Test Android screen orientation and position mark in map and navigation modes.
- SetVisibleViewport(m2::RectD(0, 0, w, h));
+ SetVisibleViewport(engineId,m2::RectD(0, 0, w, h));
}
namespace
@@ -1024,57 +1034,57 @@ double ScaleModeToFactor(Framework::EScaleMode mode)
} // namespace
-void Framework::Scale(EScaleMode mode, bool isAnim)
+void Framework::Scale(df::DrapeEngineId engineId, EScaleMode mode, bool isAnim)
{
- Scale(ScaleModeToFactor(mode), isAnim);
+ Scale(engineId, ScaleModeToFactor(mode), isAnim);
}
-void Framework::Scale(Framework::EScaleMode mode, m2::PointD const & pxPoint, bool isAnim)
+void Framework::Scale(df::DrapeEngineId engineId, Framework::EScaleMode mode, m2::PointD const & pxPoint, bool isAnim)
{
- Scale(ScaleModeToFactor(mode), pxPoint, isAnim);
+ Scale(engineId, ScaleModeToFactor(mode), pxPoint, isAnim);
}
-void Framework::Scale(double factor, bool isAnim)
+void Framework::Scale(df::DrapeEngineId engineId, double factor, bool isAnim)
{
- Scale(factor, GetVisiblePixelCenter(), isAnim);
+ Scale(engineId, factor, GetVisiblePixelCenter(), isAnim);
}
-void Framework::Scale(double factor, m2::PointD const & pxPoint, bool isAnim)
+void Framework::Scale(df::DrapeEngineId engineId, double factor, m2::PointD const & pxPoint, bool isAnim)
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->Scale(factor, pxPoint, isAnim);
+ if (m_drapeEngines[engineId].m_drapeEngine != nullptr)
+ m_drapeEngines[engineId].m_drapeEngine->Scale(factor, pxPoint, isAnim);
}
-void Framework::Move(double factorX, double factorY, bool isAnim)
+void Framework::Move(df::DrapeEngineId engineId, double factorX, double factorY, bool isAnim)
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->Move(factorX, factorY, isAnim);
+ if (m_drapeEngines[engineId].m_drapeEngine != nullptr)
+ m_drapeEngines[engineId].m_drapeEngine->Move(factorX, factorY, isAnim);
}
void Framework::Rotate(double azimuth, bool isAnim)
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->Rotate(azimuth, isAnim);
+// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr)
+// m_drapeEngines.begin()->second.m_drapeEngine->Rotate(azimuth, isAnim);
}
-void Framework::TouchEvent(df::TouchEvent const & touch)
+void Framework::TouchEvent(df::DrapeEngineId engineId, df::TouchEvent const & touch)
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->AddTouchEvent(touch);
+ if (m_drapeEngines[engineId].m_drapeEngine != nullptr)
+ m_drapeEngines[engineId].m_drapeEngine->AddTouchEvent(touch);
}
int Framework::GetDrawScale() const
{
- if (m_drapeEngine != nullptr)
- return df::GetDrawTileScale(m_currentModelView);
+// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr)
+// return df::GetDrawTileScale(m_currentModelView);
return 0;
}
void Framework::RunFirstLaunchAnimation()
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->RunFirstLaunchAnimation();
+// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr)
+// m_drapeEngines.begin()->second.m_drapeEngine->RunFirstLaunchAnimation();
}
bool Framework::IsCountryLoadedByName(string_view name) const
@@ -1084,8 +1094,8 @@ bool Framework::IsCountryLoadedByName(string_view name) const
void Framework::InvalidateRect(m2::RectD const & rect)
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->InvalidateRect(rect);
+// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr)
+// m_drapeEngines.begin()->second.m_drapeEngine->InvalidateRect(rect);
}
void Framework::ClearAllCaches()
@@ -1128,8 +1138,9 @@ void Framework::MemoryWarning()
void Framework::EnterBackground()
{
- if (m_drapeEngine)
- m_drapeEngine->OnEnterBackground();
+ for (const auto& [engineId, drapeEngineData] : m_drapeEngines)
+ if (drapeEngineData.m_drapeEngine)
+ drapeEngineData.m_drapeEngine->OnEnterBackground();
SaveViewport();
@@ -1145,8 +1156,9 @@ void Framework::EnterBackground()
void Framework::EnterForeground()
{
- if (m_drapeEngine)
- m_drapeEngine->OnEnterForeground();
+ for (const auto& [engineId, drapeEngineData] : m_drapeEngines)
+ if (drapeEngineData.m_drapeEngine)
+ drapeEngineData.m_drapeEngine->OnEnterForeground();
m_trafficManager.OnEnterForeground();
}
@@ -1275,12 +1287,12 @@ void Framework::SelectSearchResult(search::Result const & result, bool animation
m_currentPlacePageInfo = BuildPlacePageInfo(info);
if (m_currentPlacePageInfo)
{
- if (m_drapeEngine) {
- if (scale < 0)
- scale = GetFeatureViewportScale(m_currentPlacePageInfo->GetTypes());
- m2::PointD const center = m_currentPlacePageInfo->GetMercator();
- m_drapeEngine->SetModelViewCenter(center, scale, animation, true /* trackVisibleViewport */);
- }
+// if (m_drapeEngines.begin()->second.m_drapeEngine) {
+// if (scale < 0)
+// scale = GetFeatureViewportScale(m_currentPlacePageInfo->GetTypes());
+// m2::PointD const center = m_currentPlacePageInfo->GetMercator();
+// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewCenter(center, scale, animation, true /* trackVisibleViewport */);
+// }
ActivateMapSelection();
}
@@ -1422,7 +1434,14 @@ bool Framework::GetDistanceAndAzimut(m2::PointD const & point,
return (d < 25000.0);
}
-void Framework::CreateDrapeEngine(ref_ptr contextFactory, DrapeCreationParams && params)
+df::DrapeEngineId Framework::CreateDrapeEngineId()
+{
+ df::DrapeEngineId engineId = static_cast(random());
+ m_drapeEngines[engineId];
+ return engineId;
+}
+
+void Framework::CreateDrapeEngine(df::DrapeEngineId engineId, ref_ptr contextFactory, DrapeCreationParams && params)
{
auto idReadFn = [this](df::MapDataProvider::TReadCallback const & fn,
m2::RectD const & r,
@@ -1451,12 +1470,12 @@ void Framework::CreateDrapeEngine(ref_ptr contextFac
{
};
- auto onGraphicsContextInitialized = [this]()
+ auto onGraphicsContextInitialized = [this, engineId]()
{
- GetPlatform().RunTask(Platform::Thread::Gui, [this]()
+ GetPlatform().RunTask(Platform::Thread::Gui, [this, engineId]()
{
- if (m_onGraphicsContextInitialized)
- m_onGraphicsContextInitialized();
+ if (m_drapeEngines[engineId].m_onGraphicsContextInitialized)
+ m_drapeEngines[engineId].m_onGraphicsContextInitialized();
});
};
@@ -1488,26 +1507,27 @@ void Framework::CreateDrapeEngine(ref_ptr contextFac
isAutozoomEnabled, simplifiedTrafficColors, std::move(overlaysShowStatsFn),
std::move(onGraphicsContextInitialized));
- m_drapeEngine = make_unique_dp(std::move(p));
- m_drapeEngine->SetModelViewListener([this](ScreenBase const & screen)
+ DrapeEngineData &drapeEngineData = m_drapeEngines[engineId];
+ drapeEngineData.m_drapeEngine = make_unique_dp(std::move(p));
+ drapeEngineData.m_drapeEngine->SetModelViewListener([this](ScreenBase const & screen)
{
GetPlatform().RunTask(Platform::Thread::Gui, [this, screen](){ OnViewportChanged(screen); });
});
- m_drapeEngine->SetTapEventInfoListener([this](df::TapInfo const & tapInfo)
+ drapeEngineData.m_drapeEngine->SetTapEventInfoListener([this](df::TapInfo const & tapInfo)
{
GetPlatform().RunTask(Platform::Thread::Gui, [this, tapInfo]()
{
OnTapEvent(place_page::BuildInfo(tapInfo));
});
});
- m_drapeEngine->SetUserPositionListener([this](m2::PointD const & position, bool hasPosition)
+ drapeEngineData.m_drapeEngine->SetUserPositionListener([this](m2::PointD const & position, bool hasPosition)
{
GetPlatform().RunTask(Platform::Thread::Gui, [this, position, hasPosition]()
{
OnUserPositionChanged(position, hasPosition);
});
});
- m_drapeEngine->SetUserPositionPendingTimeoutListener([this]()
+ drapeEngineData.m_drapeEngine->SetUserPositionPendingTimeoutListener([this]()
{
GetPlatform().RunTask(Platform::Thread::Gui, [this]()
{
@@ -1516,7 +1536,7 @@ void Framework::CreateDrapeEngine(ref_ptr contextFac
});
});
- OnSize(params.m_surfaceWidth, params.m_surfaceHeight);
+ OnSize(engineId, params.m_surfaceWidth, params.m_surfaceHeight);
Allow3dMode(allow3d, allow3dBuildings);
@@ -1525,15 +1545,15 @@ void Framework::CreateDrapeEngine(ref_ptr contextFac
if (m_connectToGpsTrack)
GpsTracker::Instance().Connect(bind(&Framework::OnUpdateGpsTrackPointsCallback, this, _1, _2));
- GetBookmarkManager().SetDrapeEngine(make_ref(m_drapeEngine));
- m_drapeApi.SetDrapeEngine(make_ref(m_drapeEngine));
- m_routingManager.SetDrapeEngine(make_ref(m_drapeEngine), allow3d);
- m_trafficManager.SetDrapeEngine(make_ref(m_drapeEngine));
- m_transitManager.SetDrapeEngine(make_ref(m_drapeEngine));
- m_isolinesManager.SetDrapeEngine(make_ref(m_drapeEngine));
- m_searchMarks.SetDrapeEngine(make_ref(m_drapeEngine));
+// GetBookmarkManager().SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine));
+ drapeEngineData.m_drapeApi.SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine));
+// m_routingManager.SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine), allow3d);
+// m_trafficManager.SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine));
+// m_transitManager.SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine));
+// m_isolinesManager.SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine));
+// m_searchMarks.SetDrapeEngine(make_ref(drapeEngineData.m_drapeEngine));
- InvalidateUserMarks();
+// InvalidateUserMarks();
auto const transitSchemeEnabled = LoadTransitSchemeEnabled();
m_transitManager.EnableTransitSchemeMode(transitSchemeEnabled);
@@ -1543,20 +1563,21 @@ void Framework::CreateDrapeEngine(ref_ptr contextFac
if (!settings::Get(kShowDebugInfo, showDebugInfo))
showDebugInfo = false;
if (showDebugInfo)
- m_drapeEngine->ShowDebugInfo(showDebugInfo);
+ drapeEngineData.m_drapeEngine->ShowDebugInfo(showDebugInfo);
benchmark::RunGraphicsBenchmark(this);
}
-void Framework::OnRecoverSurface(int width, int height, bool recreateContextDependentResources)
+void Framework::OnRecoverSurface(df::DrapeEngineId engineId, int width, int height, bool recreateContextDependentResources)
{
- if (m_drapeEngine)
+ DrapeEngineData &drapeEngineData = m_drapeEngines[engineId];
+ if (drapeEngineData.m_drapeEngine)
{
- m_drapeEngine->RecoverSurface(width, height, recreateContextDependentResources);
+ drapeEngineData.m_drapeEngine->RecoverSurface(width, height, recreateContextDependentResources);
InvalidateUserMarks();
- m_drapeApi.Invalidate();
+ drapeEngineData.m_drapeApi.Invalidate();
}
m_trafficManager.OnRecoverSurface();
@@ -1571,86 +1592,88 @@ void Framework::OnDestroySurface()
void Framework::UpdateVisualScale(double vs)
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->UpdateVisualScale(vs, m_isRenderingEnabled);
+// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr)
+// m_drapeEngines.begin()->second.m_drapeEngine->UpdateVisualScale(vs, m_isRenderingEnabled);
}
void Framework::UpdateMyPositionRoutingOffset(bool useDefault, int offsetY)
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->UpdateMyPositionRoutingOffset(useDefault, offsetY);
+// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr)
+// m_drapeEngines.begin()->second.m_drapeEngine->UpdateMyPositionRoutingOffset(useDefault, offsetY);
}
-ref_ptr Framework::GetDrapeEngine()
+ref_ptr Framework::GetDrapeEngine(df::DrapeEngineId engineId)
{
- return make_ref(m_drapeEngine);
+ return make_ref(m_drapeEngines[engineId].m_drapeEngine);
}
void Framework::DestroyDrapeEngine()
{
- if (m_drapeEngine != nullptr)
+ for (auto& [engineId, drapeEngineData] : m_drapeEngines)
{
- m_drapeApi.SetDrapeEngine(nullptr);
- m_routingManager.SetDrapeEngine(nullptr, false);
- m_trafficManager.SetDrapeEngine(nullptr);
- m_transitManager.SetDrapeEngine(nullptr);
- m_isolinesManager.SetDrapeEngine(nullptr);
- m_searchMarks.SetDrapeEngine(nullptr);
- GetBookmarkManager().SetDrapeEngine(nullptr);
-
+ if (drapeEngineData.m_drapeEngine != nullptr)
+ {
+ drapeEngineData.m_drapeApi.SetDrapeEngine(nullptr);
+ m_routingManager.SetDrapeEngine(nullptr, false);
+ m_trafficManager.SetDrapeEngine(nullptr);
+ m_transitManager.SetDrapeEngine(nullptr);
+ m_isolinesManager.SetDrapeEngine(nullptr);
+ m_searchMarks.SetDrapeEngine(nullptr);
+ GetBookmarkManager().SetDrapeEngine(nullptr);
+ drapeEngineData.m_drapeEngine.reset();
+ }
m_trafficManager.Teardown();
GpsTracker::Instance().Disconnect();
- m_drapeEngine.reset();
}
}
void Framework::SetRenderingEnabled(ref_ptr contextFactory)
{
m_isRenderingEnabled = true;
- if (m_drapeEngine)
- m_drapeEngine->SetRenderingEnabled(contextFactory);
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// m_drapeEngines.begin()->second.m_drapeEngine->SetRenderingEnabled(contextFactory);
}
void Framework::SetRenderingDisabled(bool destroySurface)
{
m_isRenderingEnabled = false;
- if (m_drapeEngine)
- m_drapeEngine->SetRenderingDisabled(destroySurface);
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// m_drapeEngines.begin()->second.m_drapeEngine->SetRenderingDisabled(destroySurface);
}
-void Framework::SetGraphicsContextInitializationHandler(df::OnGraphicsContextInitialized && handler)
+void Framework::SetGraphicsContextInitializationHandler(df::DrapeEngineId engineId, df::OnGraphicsContextInitialized && handler)
{
- m_onGraphicsContextInitialized = std::move(handler);
+ m_drapeEngines[engineId].m_onGraphicsContextInitialized = std::move(handler);
}
void Framework::EnableDebugRectRendering(bool enabled)
{
- if (m_drapeEngine)
- m_drapeEngine->EnableDebugRectRendering(enabled);
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// m_drapeEngines.begin()->second.m_drapeEngine->EnableDebugRectRendering(enabled);
}
void Framework::ConnectToGpsTracker()
{
m_connectToGpsTrack = true;
- if (m_drapeEngine)
- {
- m_drapeEngine->ClearGpsTrackPoints();
- GpsTracker::Instance().Connect(bind(&Framework::OnUpdateGpsTrackPointsCallback, this, _1, _2));
- }
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// {
+// m_drapeEngines.begin()->second.m_drapeEngine->ClearGpsTrackPoints();
+// GpsTracker::Instance().Connect(bind(&Framework::OnUpdateGpsTrackPointsCallback, this, _1, _2));
+// }
}
void Framework::DisconnectFromGpsTracker()
{
m_connectToGpsTrack = false;
GpsTracker::Instance().Disconnect();
- if (m_drapeEngine)
- m_drapeEngine->ClearGpsTrackPoints();
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// m_drapeEngines.begin()->second.m_drapeEngine->ClearGpsTrackPoints();
}
void Framework::OnUpdateGpsTrackPointsCallback(vector> && toAdd,
pair const & toRemove)
{
- ASSERT(m_drapeEngine.get() != nullptr, ());
+// ASSERT(m_drapeEngines.begin()->second.m_drapeEngine.get() != nullptr, ());
vector pointsAdd;
pointsAdd.reserve(toAdd.size());
@@ -1674,7 +1697,7 @@ void Framework::OnUpdateGpsTrackPointsCallback(vectorUpdateGpsTrackPoints(std::move(pointsAdd), std::move(indicesRemove));
+// m_drapeEngines.begin()->second.m_drapeEngine->UpdateGpsTrackPoints(std::move(pointsAdd), std::move(indicesRemove));
}
void Framework::MarkMapStyle(MapStyle mapStyle)
@@ -1694,11 +1717,11 @@ void Framework::MarkMapStyle(MapStyle mapStyle)
void Framework::SetMapStyle(MapStyle mapStyle)
{
- MarkMapStyle(mapStyle);
- if (m_drapeEngine != nullptr)
- m_drapeEngine->UpdateMapStyle();
- InvalidateUserMarks();
- UpdateMinBuildingsTapZoom();
+// MarkMapStyle(mapStyle);
+// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr)
+// m_drapeEngines.begin()->second.m_drapeEngine->UpdateMapStyle();
+// InvalidateUserMarks();
+// UpdateMinBuildingsTapZoom();
}
MapStyle Framework::GetMapStyle() const
@@ -1713,10 +1736,10 @@ void Framework::SetupMeasurementSystem()
m_routingManager.SetTurnNotificationsUnits(measurement_utils::GetMeasurementUnits());
}
-void Framework::SetWidgetLayout(gui::TWidgetsLayoutInfo && layout)
+void Framework::SetWidgetLayout(df::DrapeEngineId engineId, gui::TWidgetsLayoutInfo && layout)
{
- ASSERT(m_drapeEngine != nullptr, ());
- m_drapeEngine->SetWidgetLayout(std::move(layout));
+ ASSERT(m_drapeEngines[engineId].m_drapeEngine != nullptr, ());
+ m_drapeEngines[engineId].m_drapeEngine->SetWidgetLayout(std::move(layout));
}
bool Framework::ShowMapForURL(string const & url)
@@ -1776,8 +1799,8 @@ bool Framework::ShowMapForURL(string const & url)
// ShowRect function interferes with ActivateMapSelection and we have strange behaviour as a result.
// Use more obvious SetModelViewCenter here.
- if (m_drapeEngine)
- m_drapeEngine->SetModelViewCenter(point, scale, true, true);
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// m_drapeEngines.begin()->second.m_drapeEngine->SetModelViewCenter(point, scale, true, true);
if (result != NO_NEED_CLICK)
{
@@ -1971,12 +1994,12 @@ void Framework::ActivateMapSelection()
auto const selObj = m_currentPlacePageInfo->GetSelectedObject();
CHECK_NOT_EQUAL(selObj, df::SelectionShape::OBJECT_EMPTY, ("Empty selections are impossible."));
- if (m_drapeEngine)
- {
- auto const & bi = m_currentPlacePageInfo->GetBuildInfo();
- m_drapeEngine->SelectObject(selObj, m_currentPlacePageInfo->GetMercator(), featureId,
- bi.m_needAnimationOnSelection, bi.m_isGeometrySelectionAllowed, true);
- }
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// {
+// auto const & bi = m_currentPlacePageInfo->GetBuildInfo();
+// m_drapeEngines.begin()->second.m_drapeEngine->SelectObject(selObj, m_currentPlacePageInfo->GetMercator(), featureId,
+// bi.m_needAnimationOnSelection, bi.m_isGeometrySelectionAllowed, true);
+// }
/// @todo Current android logic is strange (see SetPlacePageListeners comments), so skip assert.
//ASSERT(m_onPlacePageOpen, ());
@@ -1998,8 +2021,8 @@ void Framework::DeactivateMapSelection(bool notifyUI)
m_currentPlacePageInfo = {};
- if (m_drapeEngine != nullptr)
- m_drapeEngine->DeselectObject();
+// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr)
+// m_drapeEngines.begin()->second.m_drapeEngine->DeselectObject();
}
}
@@ -2047,13 +2070,13 @@ void Framework::OnTapEvent(place_page::BuildInfo const & buildInfo)
{
if (m_currentPlacePageInfo->GetTrackId() == prevTrackId)
{
- if (m_drapeEngine)
- {
- m_drapeEngine->SelectObject(df::SelectionShape::ESelectedObject::OBJECT_TRACK,
- m_currentPlacePageInfo->GetMercator(), FeatureID(),
- false /* isAnim */, false /* isGeometrySelectionAllowed */,
- true /* isSelectionShapeVisible */);
- }
+// if (m_drapeEngines.begin()->second.m_drapeEngine)
+// {
+// m_drapeEngines.begin()->second.m_drapeEngine->SelectObject(df::SelectionShape::ESelectedObject::OBJECT_TRACK,
+// m_currentPlacePageInfo->GetMercator(), FeatureID(),
+// false /* isAnim */, false /* isGeometrySelectionAllowed */,
+// true /* isSelectionShapeVisible */);
+// }
return;
}
GetBookmarkManager().UpdateElevationMyPosition(m_currentPlacePageInfo->GetTrackId());
@@ -2071,8 +2094,8 @@ void Framework::OnTapEvent(place_page::BuildInfo const & buildInfo)
void Framework::InvalidateRendering()
{
- if (m_drapeEngine)
- m_drapeEngine->Invalidate();
+ if (m_drapeEngines.begin()->second.m_drapeEngine)
+ m_drapeEngines.begin()->second.m_drapeEngine->Invalidate();
}
void Framework::UpdateMinBuildingsTapZoom()
@@ -2198,16 +2221,16 @@ std::optional Framework::BuildPlacePageInfo(
auto const isFeatureMatchingEnabled = buildInfo.IsFeatureMatchingEnabled();
// Using VisualParams inside FindTrackInTapPosition/GetDefaultTapRect requires drapeEngine.
- if (m_drapeEngine != nullptr && buildInfo.IsTrackMatchingEnabled() && !buildInfo.m_isLongTap &&
- !(isFeatureMatchingEnabled && selectedFeature.IsValid()))
- {
- auto const trackSelectionInfo = FindTrackInTapPosition(buildInfo);
- if (trackSelectionInfo.m_trackId != kml::kInvalidTrackId)
- {
- BuildTrackPlacePage(trackSelectionInfo, outInfo);
- return outInfo;
- }
- }
+// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr && buildInfo.IsTrackMatchingEnabled() && !buildInfo.m_isLongTap &&
+// !(isFeatureMatchingEnabled && selectedFeature.IsValid()))
+// {
+// auto const trackSelectionInfo = FindTrackInTapPosition(buildInfo);
+// if (trackSelectionInfo.m_trackId != kml::kInvalidTrackId)
+// {
+// BuildTrackPlacePage(trackSelectionInfo, outInfo);
+// return outInfo;
+// }
+// }
if (isFeatureMatchingEnabled && !selectedFeature.IsValid())
selectedFeature = FindBuildingAtPoint(buildInfo.m_mercator);
@@ -2402,16 +2425,16 @@ void Framework::SaveTransliteration(bool allowTranslit)
void Framework::Allow3dMode(bool allow3d, bool allow3dBuildings)
{
- if (m_drapeEngine == nullptr)
- return;
-
- if (!m_powerManager.IsFacilityEnabled(power_management::Facility::PerspectiveView))
- allow3d = false;
-
- if (!m_powerManager.IsFacilityEnabled(power_management::Facility::Buildings3d))
- allow3dBuildings = false;
-
- m_drapeEngine->Allow3dMode(allow3d, allow3dBuildings);
+// if (m_drapeEngines.begin()->second.m_drapeEngine == nullptr)
+// return;
+//
+// if (!m_powerManager.IsFacilityEnabled(power_management::Facility::PerspectiveView))
+// allow3d = false;
+//
+// if (!m_powerManager.IsFacilityEnabled(power_management::Facility::Buildings3d))
+// allow3dBuildings = false;
+//
+// m_drapeEngines.begin()->second.m_drapeEngine->Allow3dMode(allow3d, allow3dBuildings);
}
void Framework::Save3dMode(bool allow3d, bool allow3dBuildings)
@@ -2446,10 +2469,10 @@ void Framework::SetLargeFontsSize(bool isLargeSize)
{
double const scaleFactor = isLargeSize ? kLargeFontsScaleFactor : 1.0;
- ASSERT(m_drapeEngine.get() != nullptr, ());
- m_drapeEngine->SetFontScaleFactor(scaleFactor);
+// ASSERT(m_drapeEngines.begin()->second.m_drapeEngine.get() != nullptr, ());
+// m_drapeEngines.begin()->second.m_drapeEngine->SetFontScaleFactor(scaleFactor);
- InvalidateRect(GetCurrentViewport());
+// InvalidateRect(GetCurrentViewport());
}
bool Framework::LoadTrafficEnabled()
@@ -2491,8 +2514,8 @@ void Framework::AllowAutoZoom(bool allowAutoZoom)
routing::RouterType const type = m_routingManager.GetRouter();
bool const isPedestrianRoute = type == RouterType::Pedestrian;
- if (m_drapeEngine != nullptr)
- m_drapeEngine->AllowAutoZoom(allowAutoZoom && !isPedestrianRoute);
+// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr)
+// m_drapeEngines.begin()->second.m_drapeEngine->AllowAutoZoom(allowAutoZoom && !isPedestrianRoute);
}
void Framework::SaveAutoZoom(bool allowAutoZoom)
@@ -2529,11 +2552,11 @@ void Framework::SaveIsolinesEnabled(bool enabled)
void Framework::EnableChoosePositionMode(bool enable, bool enableBounds, bool applyPosition,
m2::PointD const & position)
{
- if (m_drapeEngine != nullptr)
- {
- m_drapeEngine->EnableChoosePositionMode(enable,
- enableBounds ? GetSelectedFeatureTriangles() : vector(), applyPosition, position);
- }
+// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr)
+// {
+// m_drapeEngines.begin()->second.m_drapeEngine->EnableChoosePositionMode(enable,
+// enableBounds ? GetSelectedFeatureTriangles() : vector(), applyPosition, position);
+// }
}
vector Framework::GetSelectedFeatureTriangles() const
@@ -2561,8 +2584,8 @@ vector Framework::GetSelectedFeatureTriangles() const
void Framework::BlockTapEvents(bool block)
{
- if (m_drapeEngine != nullptr)
- m_drapeEngine->BlockTapEvents(block);
+// if (m_drapeEngines.begin()->second.m_drapeEngine != nullptr)
+// m_drapeEngines.begin()->second.m_drapeEngine->BlockTapEvents(block);
}
bool Framework::ParseDrapeDebugCommand(string const & query)
@@ -2589,14 +2612,14 @@ bool Framework::ParseDrapeDebugCommand(string const & query)
if (query == "?aa" || query == "effect:antialiasing")
{
- m_drapeEngine->SetPosteffectEnabled(df::PostprocessRenderer::Antialiasing,
- true /* enabled */);
+// m_drapeEngines.begin()->second.m_drapeEngine->SetPosteffectEnabled(df::PostprocessRenderer::Antialiasing,
+// true /* enabled */);
return true;
}
if (query == "?no-aa" || query == "effect:no-antialiasing")
{
- m_drapeEngine->SetPosteffectEnabled(df::PostprocessRenderer::Antialiasing,
- false /* enabled */);
+// m_drapeEngines.begin()->second.m_drapeEngine->SetPosteffectEnabled(df::PostprocessRenderer::Antialiasing,
+// false /* enabled */);
return true;
}
if (query == "?scheme")
@@ -2621,29 +2644,29 @@ bool Framework::ParseDrapeDebugCommand(string const & query)
}
if (query == "?debug-info")
{
- m_drapeEngine->ShowDebugInfo(true /* shown */);
+// m_drapeEngines.begin()->second.m_drapeEngine->ShowDebugInfo(true /* shown */);
return true;
}
if (query == "?debug-info-always")
{
- m_drapeEngine->ShowDebugInfo(true /* shown */);
- settings::Set(kShowDebugInfo, true);
+// m_drapeEngines.begin()->second.m_drapeEngine->ShowDebugInfo(true /* shown */);
+// settings::Set(kShowDebugInfo, true);
return true;
}
if (query == "?no-debug-info")
{
- m_drapeEngine->ShowDebugInfo(false /* shown */);
- settings::Set(kShowDebugInfo, false);
+// m_drapeEngines.begin()->second.m_drapeEngine->ShowDebugInfo(false /* shown */);
+// settings::Set(kShowDebugInfo, false);
return true;
}
if (query == "?debug-rect")
{
- m_drapeEngine->EnableDebugRectRendering(true /* shown */);
+// m_drapeEngines.begin()->second.m_drapeEngine->EnableDebugRectRendering(true /* shown */);
return true;
}
if (query == "?no-debug-rect")
{
- m_drapeEngine->EnableDebugRectRendering(false /* shown */);
+// m_drapeEngines.begin()->second.m_drapeEngine->EnableDebugRectRendering(false /* shown */);
return true;
}
#if defined(OMIM_METAL_AVAILABLE)
@@ -3168,7 +3191,7 @@ void Framework::OnRouteFollow(routing::RouterType type)
// TODO. We need to sync two enums VehicleType and RouterType to be able to pass
// GetRoutingSettings(type).m_matchRoute to the FollowRoute() instead of |isPedestrianRoute|.
// |isArrowGlued| parameter fully corresponds to |m_matchRoute| in RoutingSettings.
- m_drapeEngine->FollowRoute(scale, scale3d, enableAutoZoom, !isPedestrianRoute /* isArrowGlued */);
+ m_drapeEngines.begin()->second.m_drapeEngine->FollowRoute(scale, scale3d, enableAutoZoom, !isPedestrianRoute /* isArrowGlued */);
}
// RoutingManager::Delegate
diff --git a/map/framework.hpp b/map/framework.hpp
index f5426438a6..a1041a7c96 100644
--- a/map/framework.hpp
+++ b/map/framework.hpp
@@ -162,7 +162,13 @@ protected:
using TViewportChangedFn = df::DrapeEngine::ModelViewChangedHandler;
TViewportChangedFn m_viewportChangedFn;
- drape_ptr m_drapeEngine;
+ struct DrapeEngineData
+ {
+ drape_ptr m_drapeEngine;
+ df::OnGraphicsContextInitialized m_onGraphicsContextInitialized;
+ df::DrapeApi m_drapeApi;
+ };
+ std::unordered_map m_drapeEngines;
StorageDownloadingPolicy m_storageDownloadingPolicy;
storage::Storage m_storage;
@@ -175,8 +181,6 @@ protected:
SearchMarks m_searchMarks;
- df::DrapeApi m_drapeApi;
-
bool m_isRenderingEnabled;
// Note. |m_powerManager| should be declared before |m_routingManager|
@@ -213,7 +217,7 @@ public:
explicit Framework(FrameworkParams const & params = {});
virtual ~Framework() override;
- df::DrapeApi & GetDrapeApi() { return m_drapeApi; }
+ df::DrapeApi & GetDrapeApi() { return m_drapeEngines.begin()->second.m_drapeApi; }
/// \returns true if there're unsaved changes in map with |countryId| and false otherwise.
/// \note It works for group and leaf node.
@@ -394,17 +398,18 @@ public:
df::Hints m_hints;
};
- void CreateDrapeEngine(ref_ptr contextFactory, DrapeCreationParams && params);
- ref_ptr GetDrapeEngine();
- bool IsDrapeEngineCreated() const { return m_drapeEngine != nullptr; }
+ df::DrapeEngineId CreateDrapeEngineId();
+ void CreateDrapeEngine(df::DrapeEngineId engineId, ref_ptr contextFactory, DrapeCreationParams && params);
+ ref_ptr GetDrapeEngine(df::DrapeEngineId engineId);
+ bool IsDrapeEngineCreated(df::DrapeEngineId engineId) const { return m_drapeEngines.count(engineId) != 0 && m_drapeEngines.at(engineId).m_drapeEngine != nullptr; }
void DestroyDrapeEngine();
/// Called when graphics engine should be temporarily paused and then resumed.
void SetRenderingEnabled(ref_ptr contextFactory = nullptr);
void SetRenderingDisabled(bool destroySurface);
- void SetGraphicsContextInitializationHandler(df::OnGraphicsContextInitialized && handler);
+ void SetGraphicsContextInitializationHandler(df::DrapeEngineId engineId, df::OnGraphicsContextInitialized && handler);
- void OnRecoverSurface(int width, int height, bool recreateContextDependentResources);
+ void OnRecoverSurface(df::DrapeEngineId engineId, int width, int height, bool recreateContextDependentResources);
void OnDestroySurface();
void UpdateVisualScale(double vs);
@@ -419,8 +424,6 @@ private:
/// Depends on initialized Drape engine.
void LoadViewport();
- df::OnGraphicsContextInitialized m_onGraphicsContextInitialized;
-
public:
void ConnectToGpsTracker();
void DisconnectFromGpsTracker();
@@ -431,7 +434,7 @@ public:
void SetupMeasurementSystem();
- void SetWidgetLayout(gui::TWidgetsLayoutInfo && layout);
+ void SetWidgetLayout(df::DrapeEngineId engineId, gui::TWidgetsLayoutInfo && layout);
void PrepareToShutdown();
@@ -493,7 +496,7 @@ public:
void SetViewportCenter(m2::PointD const & pt, int zoomLevel = -1, bool isAnim = true);
m2::RectD GetCurrentViewport() const;
- void SetVisibleViewport(m2::RectD const & rect);
+ void SetVisibleViewport(df::DrapeEngineId engineId, m2::RectD const & rect);
/// - Check minimal visible scale according to downloaded countries.
void ShowRect(m2::RectD const & rect, int maxScale = -1, bool animation = true,
@@ -512,7 +515,7 @@ public:
void StopLocationFollow();
/// Resize event from window.
- void OnSize(int w, int h);
+ void OnSize(df::DrapeEngineId engineId, int w, int h);
enum EScaleMode
{
@@ -522,20 +525,20 @@ public:
SCALE_MIN_LIGHT
};
- void Scale(EScaleMode mode, bool isAnim);
- void Scale(EScaleMode mode, m2::PointD const & pxPoint, bool isAnim);
- void Scale(double factor, bool isAnim);
- void Scale(double factor, m2::PointD const & pxPoint, bool isAnim);
+ void Scale(df::DrapeEngineId engineId, EScaleMode mode, bool isAnim);
+ void Scale(df::DrapeEngineId engineId, EScaleMode mode, m2::PointD const & pxPoint, bool isAnim);
+ void Scale(df::DrapeEngineId engineId, double factor, bool isAnim);
+ void Scale(df::DrapeEngineId engineId, double factor, m2::PointD const & pxPoint, bool isAnim);
/// Moves the viewport a distance of factorX * viewportWidth and factorY * viewportHeight.
/// E.g. factorX == 1.0 moves the map one screen size to the right, factorX == -0.5 moves the map
/// half screen size to the left, factorY == -2.0 moves the map two sizes down,
/// factorY = 1.5 moves the map one and a half size up.
- void Move(double factorX, double factorY, bool isAnim);
+ void Move(df::DrapeEngineId engineId, double factorX, double factorY, bool isAnim);
void Rotate(double azimuth, bool isAnim);
- void TouchEvent(df::TouchEvent const & touch);
+ void TouchEvent(df::DrapeEngineId engineId, df::TouchEvent const & touch);
int GetDrawScale() const;
diff --git a/shaders/program_params.hpp b/shaders/program_params.hpp
index fe7c02ba80..235935c5d4 100644
--- a/shaders/program_params.hpp
+++ b/shaders/program_params.hpp
@@ -40,8 +40,6 @@ private:
for (auto const p : GetBoundPrograms()) \
{ \
auto const programName = DebugPrint(p); \
- CHECK(params.find(programName) == params.cend(), \
- ("Program has already bound", programName)); \
params[programName] = GetName(); \
} \
}