diff --git a/app/build.gradle b/app/build.gradle
index 431c45d..5b906fe 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -78,7 +78,7 @@ android {
}
lint {
- disable 'GradleDependency'
+ disable 'GradleDependency', 'AndroidGradlePluginVersion'
absolutePaths = false
abortOnError = true
ignoreWarnings = false
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5739b85..99947f6 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -16,9 +16,12 @@
android:supportsRtl="true"
android:theme="@style/AppTheme">
+
diff --git a/app/src/main/java/ru/dgis/sdk/demo/MainActivity.kt b/app/src/main/java/ru/dgis/sdk/demo/MainActivity.kt
index 6af9687..93fce1d 100644
--- a/app/src/main/java/ru/dgis/sdk/demo/MainActivity.kt
+++ b/app/src/main/java/ru/dgis/sdk/demo/MainActivity.kt
@@ -73,6 +73,10 @@ class MainActivity : AppCompatActivity() {
Page("Simulate navigation") {
val intent = Intent(this@MainActivity, SimulateNavigationActivity::class.java)
startActivity(intent)
+ },
+ Page("Map background") {
+ val intent = Intent(this@MainActivity, MapBackgroundActivity::class.java)
+ startActivity(intent)
}
)
diff --git a/app/src/main/java/ru/dgis/sdk/demo/MapBackgroundActivity.kt b/app/src/main/java/ru/dgis/sdk/demo/MapBackgroundActivity.kt
new file mode 100644
index 0000000..65cbb86
--- /dev/null
+++ b/app/src/main/java/ru/dgis/sdk/demo/MapBackgroundActivity.kt
@@ -0,0 +1,126 @@
+package ru.dgis.sdk.demo
+
+import android.os.Bundle
+import android.util.Log
+import android.view.Gravity
+import android.widget.LinearLayout
+import android.widget.PopupWindow
+import androidx.activity.enableEdgeToEdge
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.view.ViewCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.lifecycle.lifecycleScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import kotlinx.coroutines.withTimeoutOrNull
+import ru.dgis.sdk.Duration
+import ru.dgis.sdk.coordinates.GeoPoint
+import ru.dgis.sdk.demo.databinding.ActivityMapBackgroundBinding
+import ru.dgis.sdk.map.BearingSource
+import ru.dgis.sdk.map.CameraAnimatedMoveResult
+import ru.dgis.sdk.map.CameraPosition
+import ru.dgis.sdk.map.Map
+import ru.dgis.sdk.map.MapView
+import ru.dgis.sdk.map.MyLocationController
+import ru.dgis.sdk.map.MyLocationMapObjectSource
+import ru.dgis.sdk.map.Zoom
+import ru.dgis.sdk.map.toBitmap
+import ru.dgis.sdk.positioning.DesiredAccuracy
+
+/**
+ * Demonstrates how to create a snapshot of a map drawn offscreen. This is useful in scenarios
+ * where a visual representation of a map is required without displaying the MapView in the UI hierarchy.
+ *
+ * Prerequisites:
+ * - Location permissions must be granted at the start of the application.
+ * - For emulator testing, ensure that a realistic geographical location is simulated.
+ */
+class MapBackgroundActivity : AppCompatActivity() {
+ private val binding by lazy { ActivityMapBackgroundBinding.inflate(layoutInflater) }
+ private val locationSource by lazy {
+ MyLocationMapObjectSource(
+ application.sdkContext,
+ MyLocationController(BearingSource.AUTO)
+ )
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ enableEdgeToEdge()
+ setContentView(binding.root)
+ ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
+ val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
+ v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
+ insets
+ }
+
+ (application as Application).locationSource.setDesiredAccuracy(DesiredAccuracy.HIGH)
+
+ lifecycleScope.launch {
+ withContext(Dispatchers.Default) {
+ val screenHeight = resources.displayMetrics.heightPixels
+ val screenWidth = resources.displayMetrics.widthPixels
+
+ // Prepare an offscreen MapView inside a PopupWindow to capture the map snapshot.
+ val mapView = MapView(this@MapBackgroundActivity).apply {
+ layoutParams = LinearLayout.LayoutParams(
+ screenWidth,
+ (screenHeight * 0.25).toInt()
+ )
+ }
+ val popup = PopupWindow(
+ mapView,
+ screenWidth,
+ (screenHeight * 0.25).toInt()
+ ).also {
+ it.isClippingEnabled = false // this is crucial, allows overflow outside screen bounds
+ }
+
+ // Changing dispatcher here since it's mandatory to work with UI from main thread
+ withContext(Dispatchers.Main.immediate) {
+ popup.showAtLocation(binding.main, Gravity.NO_GRAVITY, -screenWidth, -screenHeight)
+ mapView.getMapAsync { map ->
+ map.addSource(locationSource)
+ lifecycleScope.launch {
+ withContext(Dispatchers.Default) {
+ takeSnapshotWithGeo(map, mapView, popup)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Takes a snapshot of the map centered on the last known location. If successful,
+ * the image is set to an ImageView and the PopupWindow is dismissed.
+ */
+ private suspend fun takeSnapshotWithGeo(map: Map, mapView: MapView, popup: PopupWindow) {
+ val timeoutMillis = 20000L
+ val snapshotResult = withTimeoutOrNull(timeoutMillis) {
+ var snapshotTaken = false
+ while (!snapshotTaken) {
+ application.locationService.lastLocation?.let { location ->
+ val cameraPosition = CameraPosition(GeoPoint(location.latitude, location.longitude), Zoom(16.2f))
+ map.camera.move(cameraPosition, Duration.ZERO).onResult { result ->
+ if (result == CameraAnimatedMoveResult.FINISHED) {
+ mapView.takeSnapshot().onResult { imgData ->
+ binding.mapSnapshotContainer.setImageBitmap(imgData.toBitmap())
+ popup.dismiss()
+ snapshotTaken = true
+ }
+ }
+ }
+ }
+ if (!snapshotTaken) delay(200)
+ }
+ }
+
+ if (snapshotResult == null) {
+ Log.w("MAP", "Failed to take a snapshot within the timeout period.")
+ }
+ }
+}
diff --git a/app/src/main/res/layout/activity_map_background.xml b/app/src/main/res/layout/activity_map_background.xml
new file mode 100644
index 0000000..815f56b
--- /dev/null
+++ b/app/src/main/res/layout/activity_map_background.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+