react-native-pointr 8.14.0 → 8.15.0

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.
@@ -1,5 +1,6 @@
1
1
  package com.pointr
2
2
 
3
+ import android.Manifest
3
4
  import android.annotation.SuppressLint
4
5
  import android.content.Intent
5
6
  import android.os.Build
@@ -9,6 +10,7 @@ import android.util.Log
9
10
  import android.view.WindowManager
10
11
  import android.widget.TextView
11
12
  import android.widget.Toast
13
+ import androidx.annotation.WorkerThread
12
14
  import androidx.appcompat.app.AppCompatActivity
13
15
  import androidx.constraintlayout.widget.ConstraintLayout
14
16
  import com.pointrlabs.core.management.Pointr
@@ -21,10 +23,17 @@ import com.pointrlabs.core.positioning.model.PositioningTypes.INVALID_INTEGER
21
23
  import com.facebook.react.bridge.Callback
22
24
  import com.pointr.PointrModule.Companion.mapEventsListenerKey
23
25
  import com.pointrlabs.core.geometry.GeoPoint
26
+ import com.pointrlabs.core.management.DataManager
27
+ import com.pointrlabs.core.management.models.ErrorMessage
28
+ import com.pointrlabs.core.management.models.Site
24
29
  import com.pointrlabs.core.map.handlers.ExitButtonEventsHandler
25
30
  import com.pointrlabs.core.map.models.PTRMapSymbolLayer
26
31
  import com.pointrlabs.core.map.models.events_listeners.MapEventsListener
32
+ import com.pointrlabs.core.site.SiteManager
27
33
  import java.lang.IllegalArgumentException
34
+ import java.util.concurrent.Semaphore
35
+ import java.util.concurrent.TimeUnit
36
+ import kotlin.random.Random
28
37
 
29
38
 
30
39
  /**
@@ -54,6 +63,8 @@ class PointrMapWidgetActivity : AppCompatActivity() {
54
63
 
55
64
  var pendingAction: () -> Unit = {}
56
65
 
66
+ private val timeoutInSeconds = 15L
67
+
57
68
  private var ptrMapWidgetFragment: PTRMapWidgetFragment? = null
58
69
 
59
70
  private var mapEventsListener: MapEventsListener? = null
@@ -65,22 +76,25 @@ class PointrMapWidgetActivity : AppCompatActivity() {
65
76
 
66
77
  private val pointrListener = object : PointrListener {
67
78
  override fun onStateUpdated(p0: Pointr.State?) {
68
- Plog.v("Pointr state updated: ${p0?.name}")
69
- if (p0 == Pointr.State.RUNNING) {
79
+ val state = p0 ?: return
80
+ Plog.v("Pointr state updated: ${state.name}")
81
+ if (state == Pointr.State.RUNNING) {
70
82
  Plog.v("Pointr is Running")
71
83
  pointr?.removeListener(this)
84
+ if (PointrModule.shouldRequestPermissionsAtStartup) {
85
+ requestPermissions()
86
+ } else {
87
+ pointr?.permissionManager?.shouldRequestLocationPermissionForWhileInUse = false
88
+ pointr?.permissionManager?.shouldRequestBluetoothPermission = false
89
+ pointr?.permissionManager?.shouldRequestLocationPermissionForAlways = false
90
+ }
72
91
  runOnUiThread {
73
92
  pendingAction.invoke()
74
93
  pendingAction = {}
75
94
  }
76
- } else if (p0 == Pointr.State.FAILED_VALIDATION
77
- || p0 == Pointr.State.FAILED_REGISTRATION
78
- || p0 == Pointr.State.FAILED_INVALID_DEEPLINK_URL
79
- || p0 == Pointr.State.NO_INTERNET
80
- || p0 == Pointr.State.OFF
81
- ) {
82
- Plog.e("Pointr could not start. Current state: ${p0.name}")
83
- callback?.invoke("Starting Pointr resulted in ${p0.name}")
95
+ } else if (state.`val` <= Pointr.State.OFF.`val`) {
96
+ Plog.e("Pointr could not start. Current state: ${state.name}")
97
+ callback?.invoke("Starting Pointr resulted in ${state.name}")
84
98
  callback = null
85
99
  pointr?.removeListener(this)
86
100
  runOnUiThread {
@@ -93,6 +107,23 @@ class PointrMapWidgetActivity : AppCompatActivity() {
93
107
  }
94
108
  }
95
109
 
110
+ private fun requestPermissions() {
111
+ val permissionManager = Pointr.getPointr()?.permissionManager ?: run {
112
+ Log.e("Pointr", "Permission manager is not ready")
113
+ return
114
+ }
115
+ val permissionList = arrayListOf<String>()
116
+ if (!permissionManager.hasLocationPermissionAlwaysOrWhileInUse)
117
+ permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION)
118
+
119
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !permissionManager.hasBluetoothPermission) {
120
+ permissionList.add(Manifest.permission.BLUETOOTH_SCAN)
121
+ permissionList.add(Manifest.permission.BLUETOOTH_CONNECT)
122
+ }
123
+ if (permissionList.isEmpty()) return
124
+ requestPermissions(permissionList.toTypedArray(), Random.nextInt(1024, 65_536))
125
+ }
126
+
96
127
  override fun onCreate(savedInstanceState: Bundle?) {
97
128
  super.onCreate(savedInstanceState)
98
129
  setContentView(R.layout.pointr_map_widget_activity_layout)
@@ -138,8 +169,8 @@ class PointrMapWidgetActivity : AppCompatActivity() {
138
169
 
139
170
  private fun showMapWidgetBasedOnAction() {
140
171
 
141
- PTRMapWidgetFragment.newInstance(configuration = mapWidgetConfiguration) {
142
- ptrMapWidgetFragment = it.show(supportFragmentManager, R.id.fragment_container)
172
+ PTRMapWidgetFragment.newInstance(configuration = mapWidgetConfiguration) { widget->
173
+ ptrMapWidgetFragment = widget.show(supportFragmentManager, R.id.fragment_container)
143
174
  ptrMapWidgetFragment?.exitButtonEventsHandler = object : ExitButtonEventsHandler() {
144
175
  override fun onExitClicked(mapWidget: PTRMapWidgetFragment, exitButton: TextView) {
145
176
  this@PointrMapWidgetActivity.finish()
@@ -151,6 +182,7 @@ class PointrMapWidgetActivity : AppCompatActivity() {
151
182
  PointrModule.CustomMapEventsListener::class.java
152
183
  )
153
184
  } else {
185
+ @Suppress("DEPRECATION")
154
186
  intent.getParcelableExtra(mapEventsListenerKey)
155
187
  }
156
188
  mapEventsListener?.let { ptrMapWidgetFragment?.addListener(it) }
@@ -160,7 +192,7 @@ class PointrMapWidgetActivity : AppCompatActivity() {
160
192
  if (siteExternalId != null) {
161
193
  val animationTypeInt = intent.getIntExtra(animationTypeKey, 0)
162
194
  val animationType = try {
163
- PTRMapAnimationType.entries[animationTypeInt]
195
+ PTRMapAnimationType.values()[animationTypeInt]
164
196
  } catch (ex: IllegalArgumentException) {
165
197
  PTRMapAnimationType.flyOver
166
198
  }
@@ -262,6 +294,7 @@ class PointrMapWidgetActivity : AppCompatActivity() {
262
294
  } catch (ex: IllegalArgumentException) {
263
295
  PTRMapAnimationType.flyOver
264
296
  }
297
+
265
298
  ptrMapWidgetFragment?.showPathFinding(
266
299
  siteExternalId,
267
300
  poiExternalId,
@@ -310,9 +343,9 @@ class PointrMapWidgetActivity : AppCompatActivity() {
310
343
  } catch (ex: IllegalArgumentException) {
311
344
  PTRMapAnimationType.flyOver
312
345
  }
313
- val siteManager = pointr?.siteManager ?: run {
346
+ val dataManager = pointr?.dataManager ?: run {
314
347
  this@PointrMapWidgetActivity.finish()
315
- callback?.invoke("Site Manager is not ready")
348
+ callback?.invoke("Data Manager is not ready")
316
349
  callback = null
317
350
  return
318
351
  }
@@ -328,22 +361,53 @@ class PointrMapWidgetActivity : AppCompatActivity() {
328
361
  callback = null
329
362
  return
330
363
  }
331
- val site = siteManager.getSite(siteExternalId) ?: run {
364
+ val theSite = getSite(siteExternalId) ?: run {
332
365
  this@PointrMapWidgetActivity.finish()
333
366
  callback?.invoke("Site `$siteExternalId` does not exist.")
334
367
  callback = null
335
368
  return
336
369
  }
337
- val sourcePoi = poiManager.getPoiByExternalIdentifier(site, sourcePoiExternalId) ?: run {
370
+ val dataManagementSemaphore = Semaphore(0)
371
+ val dataManagerListener = object : DataManager.Listener {
372
+ override fun onDataManagerCompleteAllForSite(
373
+ site: Site,
374
+ isSuccessful: Boolean,
375
+ isOnlineData: Boolean,
376
+ errors: List<ErrorMessage?>?
377
+ ) {
378
+ if (theSite == site && isOnlineData) {
379
+ dataManagementSemaphore.release()
380
+ }
381
+ }
382
+ }
383
+ dataManager.addListener(dataManagerListener)
384
+ if (!dataManager.isSiteContentReady(theSite)) {
385
+ Plog.i("Data for site: ${theSite.title} is not ready. Will start and wait for completion")
386
+ dataManager.loadDataForSite(theSite)
387
+ val didAcquire = dataManagementSemaphore.tryAcquire(timeoutInSeconds, TimeUnit.SECONDS)
388
+ if (!didAcquire) {
389
+ Plog.e("Data management for site: ${theSite.title} did not complete in time")
390
+ dataManager.removeListener(dataManagerListener)
391
+ dataManagementSemaphore.release()
392
+ this@PointrMapWidgetActivity.finish()
393
+ callback?.invoke("Data management for site: ${theSite.title} did not complete in time")
394
+ callback = null
395
+ return
396
+ }
397
+ } else {
398
+ Plog.v("Data for site: ${theSite.title} is ready. Moving on.")
399
+ dataManager.removeListener(dataManagerListener)
400
+ }
401
+ val sourcePoi = poiManager.getPoiByExternalIdentifier(theSite, sourcePoiExternalId) ?: run {
338
402
  this@PointrMapWidgetActivity.finish()
339
- callback?.invoke("Source poi `$sourcePoiExternalId` does not exist within site `${site.title}`.")
403
+ callback?.invoke("Source poi `$sourcePoiExternalId` does not exist within site `${theSite.title}`.")
340
404
  callback = null
341
405
  return
342
406
  }
343
407
  val destinationPoi =
344
- poiManager.getPoiByExternalIdentifier(site, destinationPoiExternalId) ?: run {
408
+ poiManager.getPoiByExternalIdentifier(theSite, destinationPoiExternalId) ?: run {
345
409
  this@PointrMapWidgetActivity.finish()
346
- callback?.invoke("Destination poi `$destinationPoiExternalId` does not exist within site ${site.title}.")
410
+ callback?.invoke("Destination poi `$destinationPoiExternalId` does not exist within site ${theSite.title}.")
347
411
  callback = null
348
412
  return
349
413
  }
@@ -400,4 +464,22 @@ class PointrMapWidgetActivity : AppCompatActivity() {
400
464
  }
401
465
  }
402
466
  }
467
+
468
+ @WorkerThread
469
+ private fun getSite(siteExternalId: String): Site? {
470
+ val siteManager = pointr?.siteManager ?: return null
471
+ if (siteManager.getSites().isEmpty()) {
472
+ val semaphore = Semaphore(0)
473
+ val listener = object : SiteManager.Listener {
474
+ override fun onSiteManagerDataChanged() {
475
+ semaphore.release()
476
+ }
477
+ }
478
+ siteManager.addListener(listener)
479
+ semaphore.tryAcquire(timeoutInSeconds, TimeUnit.SECONDS)
480
+ siteManager.removeListener(listener)
481
+ semaphore.release()
482
+ }
483
+ return siteManager.getSite(siteExternalId)
484
+ }
403
485
  }
@@ -1,8 +1,9 @@
1
1
  package com.pointr
2
2
 
3
+ import android.Manifest
3
4
  import android.annotation.SuppressLint
4
- import android.content.ComponentName
5
5
  import android.content.Intent
6
+ import android.os.Build
6
7
  import android.os.Parcel
7
8
  import android.os.Parcelable
8
9
  import android.util.Log
@@ -21,36 +22,85 @@ import com.pointrlabs.core.management.models.Level
21
22
  import com.pointrlabs.core.management.models.PTRParams
22
23
  import com.pointrlabs.core.management.models.PointrEnvironment
23
24
  import com.pointrlabs.core.management.models.Site
25
+ import com.pointrlabs.core.map.models.PTRError
24
26
  import com.pointrlabs.core.map.models.events_listeners.MapEventsListener
25
27
  import com.pointrlabs.core.map.views.PTRMapFragment
26
- import com.pointrlabs.core.nativecore.wrappers.Plog.LogLevel
27
- import com.pointrlabs.core.nativecore.wrappers.Plog.v
28
+ import com.pointrlabs.core.map.views.helper_views.PTRDialog
29
+ import com.pointrlabs.core.nativecore.wrappers.Plog
28
30
  import com.pointrlabs.core.positioning.model.CalculatedLocation
29
31
  import com.pointrlabs.core.positioning.model.PositioningTypes
30
32
  import org.json.JSONObject
33
+ import kotlin.random.Random
31
34
  import kotlin.reflect.KMutableProperty1
32
35
 
33
36
  @SuppressLint("LogNotTimber")
34
37
  class PointrModule(reactContext: ReactApplicationContext) :
35
38
  ReactContextBaseJavaModule(reactContext) {
36
39
 
40
+ private val positionManagerListener: PositionManager.Listener
41
+ private var listenerCount = 0
42
+
37
43
  init {
44
+ positionManagerListener = object : PositionManager.Listener {
45
+ override fun onPositionManagerCalculatedLocation(p0: CalculatedLocation?) {
46
+ val calculatedLocation = p0 ?: return
47
+ val params = applyParamsForCalculatedLocation(calculatedLocation)
48
+ sendEventToJs(reactContext, OnPositionManagerCalculatedLocation, params)
49
+ }
50
+
51
+ override fun onPositionManagerDetectedPositionLevelChange(p0: Level) {}
52
+ override fun onPositionManagerPositionIsFading() {}
53
+ override fun onPositionManagerPositionIsLost() {}
54
+ override fun onPositionManagerPositioningServiceStateChangedTo(p0: PositioningTypes.PositioningServiceState?) {}
55
+ }
38
56
  setPointrListeners()
39
57
  }
40
58
 
41
- private var listenerCount = 0
42
59
 
43
60
  override fun getName(): String {
44
61
  return NAME
45
62
  }
46
63
 
64
+ @ReactMethod
65
+ fun shouldRequestPermissionsAtStartup(shouldRequestPermissions: Boolean) {
66
+ shouldRequestPermissionsAtStartup = shouldRequestPermissions
67
+ Pointr.getPointr()?.permissionManager?.shouldRequestLocationPermissionForWhileInUse =
68
+ shouldRequestPermissionsAtStartup
69
+ Pointr.getPointr()?.permissionManager?.shouldRequestBluetoothPermission =
70
+ shouldRequestPermissionsAtStartup
71
+ Pointr.getPointr()?.permissionManager?.shouldRequestLocationPermissionForAlways =
72
+ shouldRequestPermissionsAtStartup
73
+ }
74
+
75
+ @ReactMethod
76
+ fun requestPermissions() {
77
+ val permissionManager = Pointr.getPointr()?.permissionManager ?: run {
78
+ Log.e("Pointr", "Permission manager is not ready")
79
+ return
80
+ }
81
+ val activity = currentActivity ?: run {
82
+ Log.e("Pointr", "Could not get current activity")
83
+ return
84
+ }
85
+ val permissionList = arrayListOf<String>()
86
+ if (!permissionManager.hasLocationPermissionAlwaysOrWhileInUse)
87
+ permissionList.add(Manifest.permission.ACCESS_FINE_LOCATION)
88
+
89
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && !permissionManager.hasBluetoothPermission) {
90
+ permissionList.add(Manifest.permission.BLUETOOTH_SCAN)
91
+ permissionList.add(Manifest.permission.BLUETOOTH_CONNECT)
92
+ }
93
+ if (permissionList.isEmpty()) return
94
+ activity.requestPermissions(permissionList.toTypedArray(), Random.nextInt(1024, 65_536))
95
+ }
96
+
47
97
  @ReactMethod
48
98
  fun addListener(eventName: String) {
49
99
  // TODO as per documentation, this should get invoked when addListener is called
50
100
  // on the JS side, but it does not get invoked
51
101
  if (listenerCount == 0) {
52
102
  // Set up any upstream listeners or background tasks as necessary
53
- start { args -> v("Pointr started and listeners added") }
103
+ start { args -> Plog.v("Pointr started and listeners added") }
54
104
  }
55
105
  listenerCount += 1
56
106
  }
@@ -104,6 +154,14 @@ class PointrModule(reactContext: ReactApplicationContext) :
104
154
  Pointr.getPointr()?.stop()
105
155
  }
106
156
 
157
+ @ReactMethod
158
+ fun isMyCarMarked(callback: Callback) {
159
+ val pointr = Pointr.getPointr() ?: return callback.invoke("Pointr is not initialized")
160
+ if (pointr.state != Pointr.State.RUNNING) return callback.invoke("Pointr is not running")
161
+ if (pointr.myCarFeature == null) return callback.invoke("No saved location")
162
+ callback.invoke(null)
163
+ }
164
+
107
165
  @ReactMethod
108
166
  fun getCurrentLocation(callback: Callback) {
109
167
  val pointr = Pointr.getPointr() ?: return callback.invoke(null)
@@ -265,7 +323,7 @@ class PointrModule(reactContext: ReactApplicationContext) :
265
323
  const val OnBuildingClicked = "OnBuildingClicked"
266
324
  const val OnSiteClicked = "OnSiteClicked"
267
325
 
268
-
326
+ var shouldRequestPermissionsAtStartup = true
269
327
 
270
328
  fun sendEventToJs(
271
329
  reactContext: ReactApplicationContext,
@@ -296,19 +354,6 @@ class PointrModule(reactContext: ReactApplicationContext) :
296
354
  * which listens to this event.
297
355
  */
298
356
 
299
- private val positionManagerListener = object : PositionManager.Listener {
300
- override fun onPositionManagerCalculatedLocation(p0: CalculatedLocation?) {
301
- val calculatedLocation = p0 ?: return
302
- val params = applyParamsForCalculatedLocation(calculatedLocation)
303
- sendEventToJs(reactContext, OnPositionManagerCalculatedLocation, params)
304
- }
305
-
306
- override fun onPositionManagerDetectedPositionLevelChange(p0: Level) {}
307
- override fun onPositionManagerPositionIsFading() {}
308
- override fun onPositionManagerPositionIsLost() {}
309
- override fun onPositionManagerPositioningServiceStateChangedTo(p0: PositioningTypes.PositioningServiceState?) {}
310
- }
311
-
312
357
  open class CustomMapEventsListener() : MapEventsListener, Parcelable {
313
358
 
314
359
  private var reactContext: ReactApplicationContext? = null
@@ -366,6 +411,34 @@ class PointrModule(reactContext: ReactApplicationContext) :
366
411
  sendEventToJs(it, OnSiteClicked, params)
367
412
  }
368
413
  }
414
+
415
+ override fun mapDidFailToLoad(mapFragment: PTRMapFragment, ptrError: PTRError) {
416
+ Plog.e("Pointr - Map failed to load: ${ptrError.ptrUIError?.code}.${ptrError.ptrUIError?.message}")
417
+ // take reference to the activity and dismiss the fragment
418
+ // we need to keep the activity to show the dialog
419
+ val activity = mapFragment.activity ?: return
420
+ val ptrMapWidgetFragment = mapFragment.parentFragment ?: return
421
+ ptrMapWidgetFragment.parentFragmentManager.beginTransaction()
422
+ .remove(ptrMapWidgetFragment).commit()
423
+ val ptrDialogModel = PTRDialog.PtrDialogModel(
424
+ iconDrawableResId = com.pointrlabs.core.R.drawable.error_generic,
425
+ bigTextString = activity.getString(com.pointrlabs.core.R.string.ptr_error_title_map_loading_generic_error),
426
+ smallTextString = activity.getString(com.pointrlabs.core.R.string.no_internet),
427
+ primaryButton = PTRDialog.PtrDialogButtonModel(
428
+ activity.getString(com.pointrlabs.core.R.string.ok)
429
+ ) { dialog, _ ->
430
+ dialog.dismiss()
431
+ // finish the activity after we are done with the dialog
432
+ activity.finish()
433
+ },
434
+ secondaryButton = null,
435
+ errorCode = ptrError.ptrUIError?.errorCode
436
+ )
437
+ val dialog = PTRDialog(activity, ptrDialogModel)
438
+ dialog.setCancelable(false)
439
+ dialog.setCanceledOnTouchOutside(false)
440
+ dialog.show()
441
+ }
369
442
  // endregion [com.pointrlabs.core.map.models.events_listeners.CustomMapEventsListener]
370
443
  }
371
444
 
@@ -415,7 +488,7 @@ class PointrModule(reactContext: ReactApplicationContext) :
415
488
  Log.e(name, "Could not set environment of PTRParams for env: $env")
416
489
  }
417
490
  try {
418
- val ptrLogLevel = LogLevel.values()[logLevel]
491
+ val ptrLogLevel = Plog.LogLevel.entries[logLevel]
419
492
  ptrParams.logLevel = ptrLogLevel
420
493
  } catch (ex: IllegalArgumentException) {
421
494
  Log.e(name, "Could not set log level of PTRParams for logLevel: $logLevel")
@@ -430,9 +503,9 @@ class PointrModule(reactContext: ReactApplicationContext) :
430
503
  Log.v(name, "Pointr state updated: ${p0?.name}}")
431
504
  val state = p0 ?: return
432
505
  if (state.`val` > Pointr.State.OFF.`val` && state.`val` < Pointr.State.RUNNING.`val`) return
506
+ pointr.removeListener(this)
433
507
  setPointrListeners()
434
508
  action.invoke(state.name)
435
- pointr.removeListener(this)
436
509
  }
437
510
  })
438
511
  }
@@ -447,6 +520,13 @@ class PointrModule(reactContext: ReactApplicationContext) :
447
520
  val pointr = Pointr.getPointr() ?: return
448
521
  if (pointr.state != Pointr.State.RUNNING) return
449
522
  pointr.positionManager?.addListener(positionManagerListener)
523
+ if (shouldRequestPermissionsAtStartup) {
524
+ requestPermissions()
525
+ } else {
526
+ pointr.permissionManager?.shouldRequestLocationPermissionForWhileInUse = false
527
+ pointr.permissionManager?.shouldRequestBluetoothPermission = false
528
+ pointr.permissionManager?.shouldRequestLocationPermissionForAlways = false
529
+ }
450
530
  }
451
531
 
452
532
  fun setPointrMapWidgetConfiguration(jsonObject: JSONObject) {
@@ -12,6 +12,6 @@ class PointrPackage : ReactPackage {
12
12
  }
13
13
 
14
14
  override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
15
- return emptyList()
15
+ return listOf(PTRMapWidgetManager(reactContext))
16
16
  }
17
17
  }
@@ -0,0 +1,19 @@
1
+ #import <React/RCTComponent.h>
2
+ #import <PointrKit/PointrKit.h>
3
+
4
+ extern NSString * const kLayerStaticPath;
5
+
6
+ @interface PTRMapWidgetContainerView: UIView<PTRConfigurationManagerDelegate, PTRSiteManagerDelegate, PTRDataManagerDelegate, PTRPathManagerDelegate>
7
+
8
+ @property (nonatomic, copy) RCTBubblingEventBlock onMapWidgetDidEndLoading;
9
+ @property(readonly, atomic, strong) PTRMapWidgetViewController *mapWidget;
10
+ @property (nonatomic, strong) dispatch_semaphore_t semaphore;
11
+ @property (nonatomic, strong) PTRSite *site;
12
+
13
+
14
+ - (void)ptrMapWidgetDidEndLoadingWithParameters:(NSDictionary *)parameters;
15
+ -(PTRMapWidgetViewController *) getMapWidget;
16
+ -(void) present:(PTRMapWidgetViewController *) mapWidget;
17
+ -(void) showStaticPath:(NSString *) siteExternalIdentifier fromPoiExternalIdentifier:(NSString *) fromPoiExternalIdentifier toPoiExternalIdentifier:(NSString *) toPoiExternalIdentifier;
18
+
19
+ @end