react-native-pointr 8.14.0 → 8.14.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,605 @@
1
+ package com.pointr
2
+
3
+ import android.annotation.SuppressLint
4
+ import android.util.Log
5
+ import android.view.Choreographer
6
+ import android.view.View
7
+ import android.widget.FrameLayout
8
+ import androidx.fragment.app.FragmentActivity
9
+ import com.facebook.react.bridge.ReactApplicationContext
10
+ import com.facebook.react.bridge.ReadableArray
11
+ import com.facebook.react.bridge.WritableMap
12
+ import com.facebook.react.bridge.WritableNativeMap
13
+ import com.facebook.react.uimanager.ThemedReactContext
14
+ import com.facebook.react.uimanager.ViewGroupManager
15
+ import com.facebook.react.uimanager.events.RCTEventEmitter
16
+ import com.pointrlabs.core.management.DataManager
17
+ import com.pointrlabs.core.management.Pointr
18
+ import com.pointrlabs.core.management.interfaces.PointrListener
19
+ import com.pointrlabs.core.management.models.ErrorMessage
20
+ import com.pointrlabs.core.management.models.Site
21
+ import com.pointrlabs.core.map.models.PTRError
22
+ import com.pointrlabs.core.map.models.PTRMapSymbolLayer
23
+ import com.pointrlabs.core.map.models.events_listeners.MapEventsListener
24
+ import com.pointrlabs.core.map.viewmodels.PTRMapWidgetConfiguration
25
+ import com.pointrlabs.core.map.views.PTRMapFragment
26
+ import com.pointrlabs.core.map.views.PTRMapWidgetFragment
27
+ import com.pointrlabs.core.nativecore.wrappers.Plog
28
+ import com.pointrlabs.core.pathfinding.PathManager
29
+ import com.pointrlabs.core.positioning.model.PositioningTypes
30
+ import com.pointrlabs.core.site.SiteManager
31
+ import java.util.concurrent.Semaphore
32
+
33
+
34
+ @SuppressLint("LogNotTimber")
35
+ class PTRMapWidgetManager(private val reactContext: ReactApplicationContext) :
36
+ ViewGroupManager<FrameLayout>(), MapEventsListener {
37
+
38
+ override fun getName() = REACT_CLASS
39
+
40
+ private lateinit var frameLayout: FrameLayout
41
+
42
+ private var _ptrMapWidgetFragment: PTRMapWidgetFragment? = null
43
+ private val ptrMapWidgetFragment: PTRMapWidgetFragment?
44
+ get() {
45
+ _ptrMapWidgetFragment?.let { return it }
46
+ Log.i("PTRMapWidgetManager", "Creating map widget fragment")
47
+ _ptrMapWidgetFragment = createMapWidgetFragment()
48
+ Choreographer.getInstance().postFrameCallback(frameCallback)
49
+ _ptrMapWidgetFragment?.addListener(this)
50
+ return _ptrMapWidgetFragment
51
+ }
52
+
53
+ private fun createMapWidgetFragment(): PTRMapWidgetFragment {
54
+ waitForPointrToRun()
55
+ // TODO: wait for configuration to be ready
56
+ val configuration = PointrMapWidgetActivity.mapWidgetConfiguration
57
+ configuration.isExitButtonShown = false
58
+ @Suppress("DEPRECATION")
59
+ val widget = PTRMapWidgetFragment.newInstance(configuration).show(
60
+ (reactContext.currentActivity as FragmentActivity).supportFragmentManager,
61
+ frameLayout.id
62
+ )
63
+ return widget
64
+ }
65
+
66
+ /**
67
+ * Return a FrameLayout which will later hold the Fragment
68
+ */
69
+ override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
70
+ frameLayout = FrameLayout(reactContext)
71
+ return frameLayout
72
+ }
73
+
74
+ /**
75
+ * Handle "create" command (called from JS) and call createFragment method
76
+ */
77
+ override fun receiveCommand(
78
+ root: FrameLayout, command: String, args: ReadableArray?
79
+ ) {
80
+ super.receiveCommand(root, command, args)
81
+ val args1 = args ?: return
82
+ val ptrMapWidgetFragment = this.ptrMapWidgetFragment ?: run {
83
+ Log.e(
84
+ "PTRMapWidgetManager",
85
+ "PTRMapWidgetFragment is null"
86
+ )
87
+ return
88
+ }
89
+ clearPath(ptrMapWidgetFragment)
90
+ executeCommand(ptrMapWidgetFragment, command, args1)
91
+ }
92
+
93
+ private fun clearPath(ptrMapWidgetFragment: PTRMapWidgetFragment) {
94
+ ptrMapWidgetFragment.mapFragment?.removeAllFeatures { isSuccess, message ->
95
+ if (!isSuccess) {
96
+ Log.e(
97
+ "PTRMapWidgetManager",
98
+ "Error removing features: $message"
99
+ )
100
+ }
101
+ ptrMapWidgetFragment.mapFragment?.removeLayer(LAYER_STATIC_PATH) { b, e ->
102
+ if (!b) {
103
+ Log.e(
104
+ "PTRMapWidgetManager",
105
+ "Error removing layer: $e"
106
+ )
107
+ }
108
+ ptrMapWidgetFragment.mapFragment?.currentPath = null
109
+ ptrMapWidgetFragment.mapFragment?.currentPathSession?.abort()
110
+ }
111
+ }
112
+ }
113
+
114
+ private fun executeCommand(
115
+ ptrMapWidgetFragment: PTRMapWidgetFragment,
116
+ command: String,
117
+ args1: ReadableArray
118
+ ) {
119
+ when (command) {
120
+ PTRMapWidgetCommandType.SITE -> showSite(ptrMapWidgetFragment, args1.getString(0))
121
+ PTRMapWidgetCommandType.BUILDING -> showBuilding(
122
+ ptrMapWidgetFragment,
123
+ args1.getString(0),
124
+ args1.getString(1)
125
+ )
126
+
127
+ PTRMapWidgetCommandType.LEVEL -> showLevel(
128
+ ptrMapWidgetFragment,
129
+ args1.getString(0),
130
+ args1.getString(1),
131
+ args1.getInt(2)
132
+ )
133
+
134
+ PTRMapWidgetCommandType.POI -> showPoi(
135
+ ptrMapWidgetFragment,
136
+ args1.getString(0),
137
+ args1.getString(1)
138
+ )
139
+
140
+ PTRMapWidgetCommandType.PATH -> showPath(
141
+ ptrMapWidgetFragment,
142
+ args1.getString(0),
143
+ args1.getString(1)
144
+ )
145
+
146
+ PTRMapWidgetCommandType.STATIC_PATH -> showStaticPath(
147
+ ptrMapWidgetFragment,
148
+ args1.getString(0),
149
+ args1.getString(1),
150
+ args1.getString(2)
151
+ )
152
+
153
+ else -> return
154
+ }
155
+ }
156
+
157
+ private fun mapWidgetDidEndLoading(
158
+ @PTRMapWidgetCommandType command: String,
159
+ vararg args: Any
160
+ ) {
161
+ val event: WritableMap = WritableNativeMap()
162
+ event.putString("command", command)
163
+ when (command) {
164
+ PTRMapWidgetCommandType.SITE -> {
165
+ event.putString("siteExternalIdentifier", args[0] as String)
166
+ event.putString("error", args[1] as String)
167
+ }
168
+
169
+ PTRMapWidgetCommandType.BUILDING -> {
170
+ event.putString("siteExternalIdentifier", args[0] as String)
171
+ event.putString("buildingExternalIdentifier", args[1] as String)
172
+ event.putString("error", args[2] as String)
173
+ }
174
+
175
+ PTRMapWidgetCommandType.LEVEL -> {
176
+ event.putString("siteExternalIdentifier", args[0] as String)
177
+ event.putString("buildingExternalIdentifier", args[1] as String)
178
+ event.putInt("levelIndex", args[2] as Int)
179
+ event.putString("error", args[3] as String)
180
+ }
181
+
182
+ PTRMapWidgetCommandType.POI -> {
183
+ event.putString("siteExternalIdentifier", args[0] as String)
184
+ event.putString("poiExternalIdentifier", args[1] as String)
185
+ event.putString("error", args[2] as String)
186
+ }
187
+
188
+ PTRMapWidgetCommandType.PATH -> {
189
+ event.putString("siteExternalIdentifier", args[0] as String)
190
+ event.putString("poiExternalIdentifier", args[1] as String)
191
+ event.putString("error", args[2] as String)
192
+ }
193
+
194
+ PTRMapWidgetCommandType.STATIC_PATH -> {
195
+ event.putString("siteExternalIdentifier", args[0] as String)
196
+ event.putString("fromPoiExternalIdentifier", args[1] as String)
197
+ event.putString("toPoiExternalIdentifier", args[2] as String)
198
+ event.putString("error", args[3] as String)
199
+ }
200
+
201
+ else -> {
202
+ return
203
+ }
204
+ }
205
+ reactContext.getJSModule(RCTEventEmitter::class.java).receiveEvent(
206
+ frameLayout.id,
207
+ "onMapWidgetDidEndLoading",
208
+ event
209
+ )
210
+ }
211
+
212
+ override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any> {
213
+ return mapOf(
214
+ "onMapWidgetDidEndLoading" to mapOf(
215
+ "phasedRegistrationNames" to mapOf(
216
+ "bubbled" to "onMapWidgetDidEndLoading"
217
+ )
218
+ )
219
+ )
220
+ }
221
+
222
+
223
+ private fun showStaticPath(
224
+ ptrMapWidgetFragment: PTRMapWidgetFragment,
225
+ site: String,
226
+ fromPoi: String,
227
+ toPoi: String
228
+ ) {
229
+ val siteObject = getSite(site) ?: run {
230
+ mapWidgetDidEndLoading(
231
+ PTRMapWidgetCommandType.STATIC_PATH,
232
+ site,
233
+ fromPoi,
234
+ toPoi,
235
+ "Site not found"
236
+ )
237
+ return
238
+ }
239
+ if (!waitForSiteData(siteObject)) {
240
+ mapWidgetDidEndLoading(
241
+ PTRMapWidgetCommandType.STATIC_PATH,
242
+ site,
243
+ fromPoi,
244
+ toPoi,
245
+ "Site data not found"
246
+ )
247
+ return
248
+ }
249
+ val source = Pointr.getPointr()?.poiManager?.getPoiByExternalIdentifier(siteObject, fromPoi)
250
+ ?: run {
251
+ mapWidgetDidEndLoading(
252
+ PTRMapWidgetCommandType.STATIC_PATH,
253
+ site,
254
+ fromPoi,
255
+ toPoi,
256
+ "Source POI not found"
257
+ )
258
+ return
259
+ }
260
+ val target =
261
+ Pointr.getPointr()?.poiManager?.getPoiByExternalIdentifier(siteObject, toPoi) ?: run {
262
+ mapWidgetDidEndLoading(
263
+ PTRMapWidgetCommandType.STATIC_PATH,
264
+ site,
265
+ fromPoi,
266
+ toPoi,
267
+ "Target POI not found"
268
+ )
269
+ return
270
+ }
271
+ if (!waitForPathManager(siteObject)) {
272
+ mapWidgetDidEndLoading(
273
+ PTRMapWidgetCommandType.STATIC_PATH,
274
+ site,
275
+ fromPoi,
276
+ toPoi,
277
+ "Path manager not ready"
278
+ )
279
+ return
280
+ }
281
+ val pathManager = Pointr.getPointr()?.pathManager ?: run {
282
+ mapWidgetDidEndLoading(
283
+ PTRMapWidgetCommandType.STATIC_PATH,
284
+ site,
285
+ fromPoi,
286
+ toPoi,
287
+ "Path manager not found"
288
+ )
289
+ return
290
+ }
291
+ val path = pathManager.calculatePath(source, listOf(target)) ?: run {
292
+ mapWidgetDidEndLoading(
293
+ PTRMapWidgetCommandType.STATIC_PATH,
294
+ site,
295
+ fromPoi,
296
+ toPoi,
297
+ "Path not found"
298
+ )
299
+ return
300
+ }
301
+ val onCompleted = { error: String? ->
302
+ error?.let {
303
+ mapWidgetDidEndLoading(
304
+ PTRMapWidgetCommandType.STATIC_PATH,
305
+ site,
306
+ fromPoi,
307
+ toPoi,
308
+ "Error loading the map: $it"
309
+ )
310
+ Plog.e("Error showing level: $it")
311
+ return@let
312
+ }
313
+ ptrMapWidgetFragment.focusOnCoordinate(
314
+ source.location.site?.internalIdentifier ?: PositioningTypes.INVALID_INTEGER,
315
+ source.location.building?.internalIdentifier ?: PositioningTypes.INVALID_INTEGER,
316
+ source.location.level?.index ?: PositioningTypes.INVALID_INTEGER,
317
+ source.location.lat,
318
+ source.location.lon
319
+ ) {
320
+ val layer = PTRMapSymbolLayer(LAYER_STATIC_PATH)
321
+ val mapFragment = ptrMapWidgetFragment.mapFragment ?: return@focusOnCoordinate
322
+ mapFragment.addLayer(layer) { b1, e1 ->
323
+ if (!b1) {
324
+ mapWidgetDidEndLoading(
325
+ PTRMapWidgetCommandType.STATIC_PATH,
326
+ site,
327
+ fromPoi,
328
+ toPoi,
329
+ "Error adding layer: $e1"
330
+ )
331
+ Plog.e("Error adding layer: $e1")
332
+ return@addLayer
333
+ }
334
+ mapFragment.addFeatures(
335
+ listOf(source, target), layer.identifier
336
+ ) { b2, e2 ->
337
+ if (!b2) {
338
+ mapWidgetDidEndLoading(
339
+ PTRMapWidgetCommandType.STATIC_PATH,
340
+ site,
341
+ fromPoi,
342
+ toPoi,
343
+ "Error adding features: $e2"
344
+ )
345
+ Plog.e("Error adding features: $e2")
346
+ return@addFeatures
347
+ }
348
+ mapFragment.currentPath = path
349
+ mapWidgetDidEndLoading(
350
+ PTRMapWidgetCommandType.STATIC_PATH,
351
+ site,
352
+ fromPoi,
353
+ toPoi,
354
+ ""
355
+ )
356
+ Plog.i("Path shown")
357
+ }
358
+ }
359
+ }
360
+ }
361
+ source.level?.let {
362
+ ptrMapWidgetFragment.showLevel(it, onComplete = onCompleted)
363
+ } ?: ptrMapWidgetFragment.showSite(
364
+ source.location.site?.internalIdentifier ?: 0, onComplete = onCompleted
365
+ )
366
+
367
+ }
368
+
369
+ private fun showPath(ptrMapWidgetFragment: PTRMapWidgetFragment, site: String, toPoi: String) {
370
+ ptrMapWidgetFragment.showPathFinding(site, toPoi) { error ->
371
+ this@PTRMapWidgetManager.mapWidgetDidEndLoading(
372
+ PTRMapWidgetCommandType.PATH,
373
+ site,
374
+ toPoi,
375
+ error ?: ""
376
+ )
377
+ error?.let {
378
+ Plog.e("Error showing path: $it")
379
+ return@showPathFinding
380
+ }
381
+ Plog.i("Path shown")
382
+ }
383
+ }
384
+
385
+ private fun showPoi(ptrMapWidgetFragment: PTRMapWidgetFragment, site: String, poi: String) {
386
+ ptrMapWidgetFragment.showPoiDetails(site, poi) { error ->
387
+ this@PTRMapWidgetManager.mapWidgetDidEndLoading(
388
+ PTRMapWidgetCommandType.POI,
389
+ site,
390
+ poi,
391
+ error ?: ""
392
+ )
393
+ error?.let {
394
+ Plog.e("Error showing poi: $it")
395
+ return@showPoiDetails
396
+ }
397
+ Plog.i("Poi shown")
398
+ }
399
+ }
400
+
401
+ private fun showLevel(
402
+ ptrMapWidgetFragment: PTRMapWidgetFragment,
403
+ site: String,
404
+ building: String,
405
+ level: Int
406
+ ) {
407
+ ptrMapWidgetFragment.showLevel(site, building, level) showLevelInternal@{ error ->
408
+ this@PTRMapWidgetManager.mapWidgetDidEndLoading(
409
+ PTRMapWidgetCommandType.LEVEL,
410
+ site,
411
+ building,
412
+ level,
413
+ error ?: ""
414
+ )
415
+ error?.let {
416
+ Plog.e("Error showing level: $it")
417
+ return@showLevelInternal
418
+ }
419
+ Plog.i("Level shown")
420
+ }
421
+ }
422
+
423
+ private fun showBuilding(
424
+ ptrMapWidgetFragment: PTRMapWidgetFragment,
425
+ site: String,
426
+ building: String
427
+ ) {
428
+ ptrMapWidgetFragment.showBuilding(site, building) showBuildingInternal@{ error ->
429
+ this@PTRMapWidgetManager.mapWidgetDidEndLoading(
430
+ PTRMapWidgetCommandType.BUILDING,
431
+ site,
432
+ building,
433
+ error ?: ""
434
+ )
435
+ error?.let {
436
+ Plog.e("Error showing building: $it")
437
+ return@showBuildingInternal
438
+ }
439
+ Plog.i("Building shown")
440
+ }
441
+ }
442
+
443
+ private fun showSite(ptrMapWidgetFragment: PTRMapWidgetFragment, site: String) {
444
+ ptrMapWidgetFragment.showSite(site) showSiteInternal@{ error ->
445
+ this@PTRMapWidgetManager.mapWidgetDidEndLoading(
446
+ PTRMapWidgetCommandType.SITE,
447
+ site,
448
+ error ?: ""
449
+ )
450
+ error?.let {
451
+ Plog.e("Error showing site: $it")
452
+ return@showSiteInternal
453
+ }
454
+ Plog.i("Site shown")
455
+ }
456
+ }
457
+
458
+ // region Show path
459
+
460
+ private fun waitForPathManager(theSite: Site): Boolean {
461
+ val pathManager = Pointr.getPointr()?.pathManager ?: return false
462
+ val semaphore = Semaphore(0)
463
+ val listener = object : PathManager.Listener {
464
+ override fun onPathManagerReadyForSite(site: Site) {
465
+ if (site != theSite) return
466
+ semaphore.release()
467
+ }
468
+ }
469
+ pathManager.addListener(listener)
470
+ if (!pathManager.isReadyForSite(theSite)) {
471
+ semaphore.tryAcquire(15, java.util.concurrent.TimeUnit.SECONDS)
472
+ }
473
+ pathManager.removeListener(listener)
474
+ return pathManager.isReadyForSite(theSite)
475
+ }
476
+
477
+ private fun waitForSiteData(theSite: Site): Boolean {
478
+ val dataManager = Pointr.getPointr()?.dataManager ?: return false
479
+ val semaphore = Semaphore(0)
480
+ val listener = object : DataManager.Listener {
481
+ override fun onDataManagerCompleteAllForSite(
482
+ site: Site,
483
+ isSuccessful: Boolean,
484
+ isOnlineData: Boolean,
485
+ errors: List<ErrorMessage?>?
486
+ ) {
487
+ if (site != theSite) return
488
+ if (!isOnlineData) return
489
+ semaphore.release()
490
+ }
491
+ }
492
+ dataManager.addListener(listener)
493
+ if (Pointr.getPointr()?.poiManager?.hasContentForSite(theSite) != true) {
494
+ dataManager.loadDataForSite(theSite)
495
+ semaphore.tryAcquire(15, java.util.concurrent.TimeUnit.SECONDS)
496
+ }
497
+ dataManager.removeListener(listener)
498
+ return Pointr.getPointr()?.poiManager?.hasContentForSite(theSite) == true
499
+ }
500
+
501
+ private fun getSite(site: String): Site? {
502
+ val siteManager = Pointr.getPointr()?.siteManager ?: return null
503
+ val semaphore = Semaphore(0)
504
+ val listener = object : SiteManager.Listener {
505
+ override fun onSiteManagerDataChanged() {
506
+ semaphore.release()
507
+ }
508
+ }
509
+ siteManager.addListener(listener)
510
+ if (siteManager.getSites().isEmpty()) {
511
+ semaphore.tryAcquire(10, java.util.concurrent.TimeUnit.SECONDS)
512
+ }
513
+ siteManager.removeListener(listener)
514
+ return siteManager.getSite(site)
515
+ }
516
+
517
+ // endregion
518
+
519
+ private fun waitForPointrToRun(): Boolean {
520
+ Log.e(
521
+ "PTRMapWidgetManager",
522
+ "Waiting for Pointr to run"
523
+ )
524
+ val semaphore = Semaphore(0)
525
+ val listener = object : PointrListener {
526
+ override fun onStateUpdated(p0: Pointr.State?) {
527
+ Log.e(
528
+ "PTRMapWidgetManager",
529
+ "Pointr state updated to: $p0"
530
+ )
531
+ val state = p0 ?: return
532
+ if (state.`val` < Pointr.State.OFF.`val` || state == Pointr.State.RUNNING) {
533
+ semaphore.release()
534
+ return
535
+ }
536
+ }
537
+ }
538
+ Log.e(
539
+ "PTRMapWidgetManager",
540
+ "Adding listener to Pointr"
541
+ )
542
+ Pointr.getPointr()?.addListener(listener)
543
+ Log.e(
544
+ "PTRMapWidgetManager",
545
+ "Pointr state: ${Pointr.getPointr()?.state}"
546
+ )
547
+ if (Pointr.getPointr()?.state == Pointr.State.RUNNING) {
548
+ Log.e(
549
+ "PTRMapWidgetManager",
550
+ "Already running Pointr"
551
+ )
552
+ Pointr.getPointr()?.removeListener(listener)
553
+ return true
554
+ }
555
+ Log.e(
556
+ "PTRMapWidgetManager",
557
+ "Starting Pointr"
558
+ )
559
+ Pointr.getPointr()?.start()
560
+ Log.e(
561
+ "PTRMapWidgetManager",
562
+ "Pointr started"
563
+ )
564
+ semaphore.tryAcquire(10, java.util.concurrent.TimeUnit.SECONDS)
565
+ Log.e(
566
+ "PTRMapWidgetManager",
567
+ "Final Pointr state: ${Pointr.getPointr()?.state}"
568
+ )
569
+ Pointr.getPointr()?.removeListener(listener)
570
+ return Pointr.getPointr()?.state == Pointr.State.RUNNING
571
+ }
572
+
573
+ override fun mapDidEndLoading(mapFragment: PTRMapFragment) {
574
+ super.mapDidEndLoading(mapFragment)
575
+ Log.i("PTRMapWidgetManager", "Map loaded")
576
+ }
577
+
578
+ override fun mapDidFailToLoad(mapFragment: PTRMapFragment, ptrError: PTRError) {
579
+ super.mapDidFailToLoad(mapFragment, ptrError)
580
+ Log.e("PTRMapWidgetManager", "Map failed to load: $ptrError")
581
+ }
582
+
583
+ private val frameCallback = object : Choreographer.FrameCallback {
584
+ override fun doFrame(frameTimeNanos: Long) {
585
+ frameLayout.measure(
586
+ View.MeasureSpec.makeMeasureSpec(frameLayout.width, View.MeasureSpec.EXACTLY),
587
+ View.MeasureSpec.makeMeasureSpec(frameLayout.height, View.MeasureSpec.EXACTLY)
588
+ )
589
+ frameLayout.layout(
590
+ frameLayout.left,
591
+ frameLayout.top,
592
+ frameLayout.right,
593
+ frameLayout.bottom
594
+ )
595
+ frameLayout.viewTreeObserver.dispatchOnGlobalLayout()
596
+ Choreographer.getInstance().postFrameCallback(this)
597
+ }
598
+ }
599
+
600
+ companion object {
601
+ const val REACT_CLASS = "PTRMapWidget"
602
+ const val LAYER_STATIC_PATH = "sym-static-path"
603
+ }
604
+
605
+ }