11package com.mapbox.maps.testapp.examples
22
33import android.annotation.SuppressLint
4+ import android.content.Context
5+ import android.graphics.BlurMaskFilter
6+ import android.graphics.Canvas
7+ import android.graphics.Color
8+ import android.graphics.CornerPathEffect
9+ import android.graphics.Paint
10+ import android.graphics.Path
11+ import android.graphics.PorterDuff
12+ import android.graphics.PorterDuffXfermode
413import android.os.Bundle
514import android.view.Menu
615import android.view.MenuItem
16+ import android.view.View
17+ import android.view.ViewGroup
718import android.widget.TextView
819import androidx.appcompat.app.AppCompatActivity
920import com.google.android.material.snackbar.Snackbar
@@ -13,6 +24,7 @@ import com.mapbox.maps.MapboxMap
1324import com.mapbox.maps.PerformanceSamplerOptions
1425import com.mapbox.maps.PerformanceStatisticsOptions
1526import com.mapbox.maps.Style
27+ import com.mapbox.maps.Vec2
1628import com.mapbox.maps.debugoptions.MapViewDebugOptions
1729import com.mapbox.maps.logE
1830import com.mapbox.maps.logI
@@ -58,10 +70,40 @@ class DebugModeActivity : AppCompatActivity() {
5870
5971 mapboxMap.loadStyle(Style .STANDARD )
6072 setupPerformanceStatisticsCollection()
73+ setupScreenShapeButton()
6174 binding.mapView.compass.opacity = 0.5f
6275 binding.mapView.debugOptions = debugOptions
6376 registerListeners(mapboxMap)
6477 }
78+ private var overlayView: HexSpotlightView ? = null
79+
80+ private fun setupScreenShapeButton () {
81+ binding.screenShapeButton.setOnClickListener {
82+ when (binding.screenShapeButton.text) {
83+ SCREEN_SHAPE_RECT -> {
84+ binding.screenShapeButton.text = SCREEN_SHAPE_CUSTOM
85+
86+ mapboxMap.setScreenCullingShape(emptyList())
87+ (overlayView?.parent as ? ViewGroup )?.removeView(overlayView)
88+ }
89+ SCREEN_SHAPE_CUSTOM -> {
90+ val shape = listOf (
91+ Vec2 (0.35 , 0.34 ),
92+ Vec2 (0.65 , 0.34 ),
93+ Vec2 (0.85 , 0.50 ),
94+ Vec2 (0.65 , 0.66 ),
95+ Vec2 (0.35 , 0.66 ),
96+ Vec2 (0.15 , 0.50 )
97+ )
98+
99+ overlayView = HexSpotlightView (this , shape)
100+ addContentView(overlayView, ViewGroup .LayoutParams (ViewGroup .LayoutParams .MATCH_PARENT , ViewGroup .LayoutParams .MATCH_PARENT ))
101+ mapboxMap.setScreenCullingShape(shape)
102+ binding.screenShapeButton.text = SCREEN_SHAPE_RECT
103+ }
104+ }
105+ }
106+ }
65107
66108 private fun setupPerformanceStatisticsCollection () {
67109 binding.perfStatButton.setOnClickListener {
@@ -229,5 +271,71 @@ class DebugModeActivity : AppCompatActivity() {
229271 private const val TAG_PERFORMANCE_STATISTICS = " PerformanceStatistics"
230272 private const val PERF_STAT_STOP_COLLECT_BUTTON = " Stop collecting"
231273 private const val PERF_STAT_START_COLLECT_BUTTON = " Collect Perf Stats"
274+ private const val SCREEN_SHAPE_CUSTOM = " Hex screen"
275+ private const val SCREEN_SHAPE_RECT = " Rect screen"
276+ }
277+ }
278+
279+ @SuppressLint(" ViewConstructor" )
280+ class HexSpotlightView (context : Context , private val shape : List <Vec2 >) : View(context) {
281+
282+ private val overlayPaint = Paint (Paint .ANTI_ALIAS_FLAG ).apply {
283+ color = Color .parseColor(" #80000000" ) // dimmed background
284+ }
285+
286+ private val clearPaint = Paint (Paint .ANTI_ALIAS_FLAG ).apply {
287+ xfermode = PorterDuffXfermode (PorterDuff .Mode .CLEAR )
288+ }
289+
290+ private val strokePaint = Paint (Paint .ANTI_ALIAS_FLAG ).apply {
291+ color = Color .WHITE
292+ style = Paint .Style .STROKE
293+ strokeWidth = 4F * context.resources.displayMetrics.density
294+ strokeJoin = Paint .Join .ROUND
295+ pathEffect = CornerPathEffect (12F * context.resources.displayMetrics.density)
296+ }
297+ private var hexPath: Path = createPath(width.toFloat(), height.toFloat())
298+
299+ private val glowPaint = Paint (Paint .ANTI_ALIAS_FLAG ).apply {
300+ color = Color .WHITE
301+ style = Paint .Style .STROKE
302+ strokeWidth = 4F * context.resources.displayMetrics.density
303+ maskFilter = BlurMaskFilter (32F , BlurMaskFilter .Blur .OUTER )
304+ strokeJoin = Paint .Join .ROUND
305+ pathEffect = CornerPathEffect (12F * context.resources.displayMetrics.density)
306+ }
307+
308+ override fun onLayout (changed : Boolean , left : Int , top : Int , right : Int , bottom : Int ) {
309+ super .onLayout(changed, left, top, right, bottom)
310+
311+ hexPath = createPath(width.toFloat(), height.toFloat())
312+ }
313+
314+ override fun onDraw (canvas : Canvas ) {
315+ super .onDraw(canvas)
316+
317+ val layer = canvas.saveLayer(null , null )
318+
319+ // Draw full dim overlay
320+ canvas.drawRect(0f , 0f , width.toFloat(), height.toFloat(), overlayPaint)
321+
322+ // Draw hexagon cutout
323+ canvas.drawPath(hexPath, clearPaint)
324+ canvas.drawPath(hexPath, glowPaint)
325+ canvas.drawPath(hexPath, glowPaint)
326+ canvas.drawPath(hexPath, strokePaint)
327+
328+ canvas.restoreToCount(layer)
329+ }
330+
331+ private fun createPath (w : Float , h : Float ): Path {
332+ val path = Path ()
333+ shape.forEachIndexed { index, pt ->
334+ val x = pt.x * w
335+ val y = pt.y * h
336+ if (index == 0 ) path.moveTo(x.toFloat(), y.toFloat()) else path.lineTo(x.toFloat(), y.toFloat())
337+ }
338+ path.close()
339+ return path
232340 }
233341}
0 commit comments