@maplibre/maplibre-react-native 11.0.0-alpha.31 → 11.0.0-alpha.33

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.
Files changed (99) hide show
  1. package/README.md +1 -1
  2. package/android/src/main/java/org/maplibre/reactnative/MLRNPackage.kt +10 -12
  3. package/android/src/main/java/org/maplibre/reactnative/components/mapview/MLRNMapView.kt +9 -0
  4. package/android/src/main/java/org/maplibre/reactnative/components/mapview/MLRNMapViewManager.kt +5 -1
  5. package/android/src/main/java/org/maplibre/reactnative/modules/MLRNModule.java +6 -24
  6. package/android/src/main/java/org/maplibre/reactnative/modules/MLRNOfflineModule.kt +561 -0
  7. package/ios/components/map-view/MLRNMapView.h +1 -0
  8. package/ios/components/map-view/MLRNMapView.m +5 -0
  9. package/ios/components/map-view/MLRNMapViewComponentView.mm +4 -0
  10. package/ios/modules/mlrn/MLRNModule.m +0 -13
  11. package/ios/modules/offline/MLRNOfflineModule.h +4 -7
  12. package/ios/modules/offline/MLRNOfflineModule.mm +693 -0
  13. package/lib/commonjs/MLRNModule.js +1 -3
  14. package/lib/commonjs/MLRNModule.js.map +1 -1
  15. package/lib/commonjs/components/map-view/AndroidTextureMapViewNativeComponent.ts +1 -0
  16. package/lib/commonjs/components/map-view/MapView.js.map +1 -1
  17. package/lib/commonjs/components/map-view/MapViewNativeComponent.ts +1 -0
  18. package/lib/commonjs/index.js +0 -8
  19. package/lib/commonjs/index.js.map +1 -1
  20. package/lib/commonjs/modules/offline/NativeOfflineModule.js +9 -0
  21. package/lib/commonjs/modules/offline/NativeOfflineModule.js.map +1 -0
  22. package/lib/commonjs/modules/offline/OfflineManager.js +124 -164
  23. package/lib/commonjs/modules/offline/OfflineManager.js.map +1 -1
  24. package/lib/commonjs/modules/offline/OfflinePack.js +15 -25
  25. package/lib/commonjs/modules/offline/OfflinePack.js.map +1 -1
  26. package/lib/module/MLRNModule.js +0 -1
  27. package/lib/module/MLRNModule.js.map +1 -1
  28. package/lib/module/components/map-view/AndroidTextureMapViewNativeComponent.ts +1 -0
  29. package/lib/module/components/map-view/MapView.js.map +1 -1
  30. package/lib/module/components/map-view/MapViewNativeComponent.ts +1 -0
  31. package/lib/module/index.js +0 -1
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/module/modules/offline/NativeOfflineModule.js +5 -0
  34. package/lib/module/modules/offline/NativeOfflineModule.js.map +1 -0
  35. package/lib/module/modules/offline/OfflineManager.js +123 -163
  36. package/lib/module/modules/offline/OfflineManager.js.map +1 -1
  37. package/lib/module/modules/offline/OfflinePack.js +14 -25
  38. package/lib/module/modules/offline/OfflinePack.js.map +1 -1
  39. package/lib/typescript/commonjs/src/MLRNModule.d.ts +1 -6
  40. package/lib/typescript/commonjs/src/MLRNModule.d.ts.map +1 -1
  41. package/lib/typescript/commonjs/src/components/map-view/AndroidTextureMapViewNativeComponent.d.ts +1 -0
  42. package/lib/typescript/commonjs/src/components/map-view/AndroidTextureMapViewNativeComponent.d.ts.map +1 -1
  43. package/lib/typescript/commonjs/src/components/map-view/MapView.d.ts +6 -0
  44. package/lib/typescript/commonjs/src/components/map-view/MapView.d.ts.map +1 -1
  45. package/lib/typescript/commonjs/src/components/map-view/MapViewNativeComponent.d.ts +1 -0
  46. package/lib/typescript/commonjs/src/components/map-view/MapViewNativeComponent.d.ts.map +1 -1
  47. package/lib/typescript/commonjs/src/index.d.ts +2 -5
  48. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  49. package/lib/typescript/commonjs/src/modules/offline/NativeOfflineModule.d.ts +50 -0
  50. package/lib/typescript/commonjs/src/modules/offline/NativeOfflineModule.d.ts.map +1 -0
  51. package/lib/typescript/commonjs/src/modules/offline/OfflineManager.d.ts +66 -69
  52. package/lib/typescript/commonjs/src/modules/offline/OfflineManager.d.ts.map +1 -1
  53. package/lib/typescript/commonjs/src/modules/offline/OfflinePack.d.ts +11 -9
  54. package/lib/typescript/commonjs/src/modules/offline/OfflinePack.d.ts.map +1 -1
  55. package/lib/typescript/module/src/MLRNModule.d.ts +1 -6
  56. package/lib/typescript/module/src/MLRNModule.d.ts.map +1 -1
  57. package/lib/typescript/module/src/components/map-view/AndroidTextureMapViewNativeComponent.d.ts +1 -0
  58. package/lib/typescript/module/src/components/map-view/AndroidTextureMapViewNativeComponent.d.ts.map +1 -1
  59. package/lib/typescript/module/src/components/map-view/MapView.d.ts +6 -0
  60. package/lib/typescript/module/src/components/map-view/MapView.d.ts.map +1 -1
  61. package/lib/typescript/module/src/components/map-view/MapViewNativeComponent.d.ts +1 -0
  62. package/lib/typescript/module/src/components/map-view/MapViewNativeComponent.d.ts.map +1 -1
  63. package/lib/typescript/module/src/index.d.ts +2 -5
  64. package/lib/typescript/module/src/index.d.ts.map +1 -1
  65. package/lib/typescript/module/src/modules/offline/NativeOfflineModule.d.ts +50 -0
  66. package/lib/typescript/module/src/modules/offline/NativeOfflineModule.d.ts.map +1 -0
  67. package/lib/typescript/module/src/modules/offline/OfflineManager.d.ts +66 -69
  68. package/lib/typescript/module/src/modules/offline/OfflineManager.d.ts.map +1 -1
  69. package/lib/typescript/module/src/modules/offline/OfflinePack.d.ts +11 -9
  70. package/lib/typescript/module/src/modules/offline/OfflinePack.d.ts.map +1 -1
  71. package/package.json +7 -3
  72. package/src/MLRNModule.ts +0 -8
  73. package/src/components/map-view/AndroidTextureMapViewNativeComponent.ts +1 -0
  74. package/src/components/map-view/MapView.tsx +7 -0
  75. package/src/components/map-view/MapViewNativeComponent.ts +1 -0
  76. package/src/index.ts +13 -5
  77. package/src/modules/offline/NativeOfflineModule.ts +63 -0
  78. package/src/modules/offline/OfflineManager.ts +174 -210
  79. package/src/modules/offline/OfflinePack.ts +22 -32
  80. package/android/src/main/java/org/maplibre/reactnative/modules/MLRNOfflineModule.java +0 -586
  81. package/ios/modules/offline/MLRNOfflineModule.m +0 -524
  82. package/lib/commonjs/modules/offline/OfflineCreatePackOptions.js +0 -37
  83. package/lib/commonjs/modules/offline/OfflineCreatePackOptions.js.map +0 -1
  84. package/lib/commonjs/utils/makeNativeBounds.js +0 -11
  85. package/lib/commonjs/utils/makeNativeBounds.js.map +0 -1
  86. package/lib/module/modules/offline/OfflineCreatePackOptions.js +0 -32
  87. package/lib/module/modules/offline/OfflineCreatePackOptions.js.map +0 -1
  88. package/lib/module/utils/makeNativeBounds.js +0 -7
  89. package/lib/module/utils/makeNativeBounds.js.map +0 -1
  90. package/lib/typescript/commonjs/src/modules/offline/OfflineCreatePackOptions.d.ts +0 -20
  91. package/lib/typescript/commonjs/src/modules/offline/OfflineCreatePackOptions.d.ts.map +0 -1
  92. package/lib/typescript/commonjs/src/utils/makeNativeBounds.d.ts +0 -2
  93. package/lib/typescript/commonjs/src/utils/makeNativeBounds.d.ts.map +0 -1
  94. package/lib/typescript/module/src/modules/offline/OfflineCreatePackOptions.d.ts +0 -20
  95. package/lib/typescript/module/src/modules/offline/OfflineCreatePackOptions.d.ts.map +0 -1
  96. package/lib/typescript/module/src/utils/makeNativeBounds.d.ts +0 -2
  97. package/lib/typescript/module/src/utils/makeNativeBounds.d.ts.map +0 -1
  98. package/src/modules/offline/OfflineCreatePackOptions.ts +0 -53
  99. package/src/utils/makeNativeBounds.ts +0 -5
@@ -0,0 +1,561 @@
1
+ package org.maplibre.reactnative.modules
2
+
3
+ import android.content.Context
4
+ import android.os.Handler
5
+ import android.os.Looper
6
+ import android.util.Log
7
+ import com.facebook.react.bridge.Arguments
8
+ import com.facebook.react.bridge.Promise
9
+ import com.facebook.react.bridge.ReactApplicationContext
10
+ import com.facebook.react.bridge.ReadableMap
11
+ import com.facebook.react.bridge.WritableMap
12
+ import com.facebook.react.bridge.WritableNativeMap
13
+ import org.json.JSONException
14
+ import org.json.JSONObject
15
+ import org.maplibre.android.geometry.LatLngBounds
16
+ import org.maplibre.android.offline.OfflineManager
17
+ import org.maplibre.android.offline.OfflineRegion
18
+ import org.maplibre.android.offline.OfflineRegionError
19
+ import org.maplibre.android.offline.OfflineRegionStatus
20
+ import org.maplibre.android.offline.OfflineTilePyramidRegionDefinition
21
+ import org.maplibre.android.storage.FileSource
22
+ import org.maplibre.reactnative.NativeOfflineModuleSpec
23
+ import org.maplibre.reactnative.utils.GeoJSONUtils
24
+ import java.util.UUID
25
+
26
+ class MLRNOfflineModule(reactContext: ReactApplicationContext) :
27
+ NativeOfflineModuleSpec(reactContext) {
28
+
29
+ companion object {
30
+ const val NAME = "MLRNOfflineModule"
31
+
32
+ const val MIGRATION_KEY = "migrationVersion"
33
+ const val MIGRATION_VERSION = 1
34
+ }
35
+
36
+ private val context: Context = reactContext.applicationContext
37
+ private var progressEventThrottle = 300.0
38
+
39
+ override fun initialize() {
40
+ Handler(Looper.getMainLooper()).post {
41
+ runMigrations()
42
+ }
43
+ }
44
+
45
+ override fun getName(): String = NAME
46
+
47
+ override fun createPack(options: ReadableMap, promise: Promise) {
48
+ val offlineManager = OfflineManager.getInstance(context)
49
+
50
+ val packId = UUID.randomUUID().toString()
51
+ val latLngBounds = getBoundsFromOptions(options)
52
+ val definition = makeDefinition(latLngBounds, options)
53
+
54
+ val metadataJson = JSONObject()
55
+ metadataJson.put(MIGRATION_KEY, MIGRATION_VERSION)
56
+ metadataJson.put("id", packId)
57
+
58
+ val metadataString = options.getString("metadata")
59
+ if (metadataString != null && metadataString.isNotEmpty()) {
60
+ metadataJson.put("metadata", metadataString)
61
+ } else {
62
+ metadataJson.put("metadata", "{}")
63
+ }
64
+
65
+ val metadataBytes = metadataJson.toString().toByteArray(Charsets.UTF_8)
66
+
67
+ offlineManager.createOfflineRegion(
68
+ definition, metadataBytes, object : OfflineManager.CreateOfflineRegionCallback {
69
+ override fun onCreate(offlineRegion: OfflineRegion) {
70
+ val pack = fromOfflineRegion(offlineRegion)
71
+ promise.resolve(pack)
72
+ setOfflineRegionObserver(packId, offlineRegion)
73
+ }
74
+
75
+ override fun onError(error: String) {
76
+ emitOnError(makeErrorPayload(packId, error))
77
+ Log.e(NAME, "createPack error: $error")
78
+ }
79
+ })
80
+ }
81
+
82
+ override fun getPacks(promise: Promise) {
83
+ activateFileSource()
84
+
85
+ val offlineManager = OfflineManager.getInstance(context)
86
+
87
+ offlineManager.listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
88
+ override fun onList(offlineRegions: Array<OfflineRegion>?) {
89
+ val payload = Arguments.createArray()
90
+ offlineRegions?.forEach { region ->
91
+ payload.pushMap(fromOfflineRegion(region))
92
+ }
93
+ promise.resolve(payload)
94
+ }
95
+
96
+ override fun onError(error: String) {
97
+ promise.reject("getRegions", error)
98
+ }
99
+ })
100
+ }
101
+
102
+ override fun invalidateAmbientCache(promise: Promise) {
103
+ activateFileSource()
104
+ val offlineManager = OfflineManager.getInstance(context)
105
+ offlineManager.invalidateAmbientCache(object : OfflineManager.FileSourceCallback {
106
+ override fun onSuccess() {
107
+ promise.resolve(null)
108
+ }
109
+
110
+ override fun onError(message: String) {
111
+ promise.reject("invalidateAmbientCache", message)
112
+ }
113
+ })
114
+ }
115
+
116
+ override fun clearAmbientCache(promise: Promise) {
117
+ activateFileSource()
118
+ val offlineManager = OfflineManager.getInstance(context)
119
+ offlineManager.clearAmbientCache(object : OfflineManager.FileSourceCallback {
120
+ override fun onSuccess() {
121
+ promise.resolve(null)
122
+ }
123
+
124
+ override fun onError(message: String) {
125
+ promise.reject("clearAmbientCache", message)
126
+ }
127
+ })
128
+ }
129
+
130
+ override fun setMaximumAmbientCacheSize(size: Double, promise: Promise) {
131
+ activateFileSource()
132
+ val offlineManager = OfflineManager.getInstance(context)
133
+ offlineManager.setMaximumAmbientCacheSize(
134
+ size.toLong(), object : OfflineManager.FileSourceCallback {
135
+ override fun onSuccess() {
136
+ promise.resolve(null)
137
+ }
138
+
139
+ override fun onError(message: String) {
140
+ promise.reject("setMaximumAmbientCacheSize", message)
141
+ }
142
+ })
143
+ }
144
+
145
+ override fun resetDatabase(promise: Promise) {
146
+ activateFileSource()
147
+ val offlineManager = OfflineManager.getInstance(context)
148
+ offlineManager.resetDatabase(object : OfflineManager.FileSourceCallback {
149
+ override fun onSuccess() {
150
+ promise.resolve(null)
151
+ }
152
+
153
+ override fun onError(message: String) {
154
+ promise.reject("resetDatabase", message)
155
+ }
156
+ })
157
+ }
158
+
159
+ override fun getPackStatus(id: String, promise: Promise) {
160
+ activateFileSource()
161
+ val offlineManager = OfflineManager.getInstance(context)
162
+
163
+ offlineManager.listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
164
+ override fun onList(offlineRegions: Array<OfflineRegion>?) {
165
+ val region = getRegionById(id, offlineRegions)
166
+
167
+ if (region == null) {
168
+ promise.resolve(null)
169
+ Log.w(NAME, "getPackStatus - Unknown offline region")
170
+ return
171
+ }
172
+
173
+ region.getStatus(object : OfflineRegion.OfflineRegionStatusCallback {
174
+ override fun onStatus(status: OfflineRegionStatus?) {
175
+ if (status != null) {
176
+ promise.resolve(makeRegionStatus(id, status))
177
+ } else {
178
+ promise.reject("getPackStatus", "Status is null")
179
+ }
180
+ }
181
+
182
+ override fun onError(error: String?) {
183
+ promise.reject("getPackStatus", error ?: "Unknown error")
184
+ }
185
+ })
186
+ }
187
+
188
+ override fun onError(error: String) {
189
+ promise.reject("getPackStatus", error)
190
+ }
191
+ })
192
+ }
193
+
194
+ override fun setPackObserver(id: String, promise: Promise) {
195
+ activateFileSource()
196
+ val offlineManager = OfflineManager.getInstance(context)
197
+
198
+ offlineManager.listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
199
+ override fun onList(offlineRegions: Array<OfflineRegion>?) {
200
+ val region = getRegionById(id, offlineRegions)
201
+ val hasRegion = region != null
202
+
203
+ if (hasRegion) {
204
+ setOfflineRegionObserver(id, region)
205
+ }
206
+
207
+ promise.resolve(hasRegion)
208
+ }
209
+
210
+ override fun onError(error: String) {
211
+ promise.reject("setPackObserver", error)
212
+ }
213
+ })
214
+ }
215
+
216
+ override fun invalidatePack(id: String, promise: Promise) {
217
+ activateFileSource()
218
+ val offlineManager = OfflineManager.getInstance(context)
219
+
220
+ offlineManager.listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
221
+ override fun onList(offlineRegions: Array<OfflineRegion>?) {
222
+ val region = getRegionById(id, offlineRegions)
223
+
224
+ if (region == null) {
225
+ promise.resolve(null)
226
+ Log.w(NAME, "invalidateRegion - Unknown offline region")
227
+ return
228
+ }
229
+
230
+ region.invalidate(object : OfflineRegion.OfflineRegionInvalidateCallback {
231
+ override fun onInvalidate() {
232
+ promise.resolve(null)
233
+ }
234
+
235
+ override fun onError(error: String) {
236
+ promise.reject("invalidateRegion", error)
237
+ }
238
+ })
239
+ }
240
+
241
+ override fun onError(error: String) {
242
+ promise.reject("invalidateRegion", error)
243
+ }
244
+ })
245
+ }
246
+
247
+ override fun deletePack(id: String, promise: Promise) {
248
+ activateFileSource()
249
+ val offlineManager = OfflineManager.getInstance(context)
250
+
251
+ offlineManager.listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
252
+ override fun onList(offlineRegions: Array<OfflineRegion>?) {
253
+ val region = getRegionById(id, offlineRegions)
254
+
255
+ if (region == null) {
256
+ promise.resolve(null)
257
+ Log.w(NAME, "deleteRegion - Unknown offline region")
258
+ return
259
+ }
260
+
261
+ region.setDownloadState(OfflineRegion.STATE_INACTIVE)
262
+ region.delete(object : OfflineRegion.OfflineRegionDeleteCallback {
263
+ override fun onDelete() {
264
+ promise.resolve(null)
265
+ }
266
+
267
+ override fun onError(error: String) {
268
+ promise.reject("deleteRegion", error)
269
+ }
270
+ })
271
+ }
272
+
273
+ override fun onError(error: String) {
274
+ promise.reject("deleteRegion", error)
275
+ }
276
+ })
277
+ }
278
+
279
+ override fun pausePackDownload(id: String, promise: Promise) {
280
+ activateFileSource()
281
+ val offlineManager = OfflineManager.getInstance(context)
282
+
283
+ offlineManager.listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
284
+ override fun onList(offlineRegions: Array<OfflineRegion>?) {
285
+ val offlineRegion = getRegionById(id, offlineRegions)
286
+
287
+ if (offlineRegion == null) {
288
+ promise.reject("pauseRegionDownload", "Unknown offline region")
289
+ return
290
+ }
291
+
292
+ Handler(Looper.getMainLooper()).post {
293
+ offlineRegion.setDownloadState(OfflineRegion.STATE_INACTIVE)
294
+ promise.resolve(null)
295
+ }
296
+ }
297
+
298
+ override fun onError(error: String) {
299
+ promise.reject("pauseRegionDownload", error)
300
+ }
301
+ })
302
+ }
303
+
304
+ override fun resumePackDownload(id: String, promise: Promise) {
305
+ activateFileSource()
306
+ val offlineManager = OfflineManager.getInstance(context)
307
+
308
+ offlineManager.listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
309
+ override fun onList(offlineRegions: Array<OfflineRegion>?) {
310
+ val offlineRegion = getRegionById(id, offlineRegions)
311
+
312
+ if (offlineRegion == null) {
313
+ promise.reject("resumeRegionDownload", "Unknown offline region")
314
+ return
315
+ }
316
+
317
+ offlineRegion.setDownloadState(OfflineRegion.STATE_ACTIVE)
318
+ promise.resolve(null)
319
+ }
320
+
321
+ override fun onError(error: String) {
322
+ promise.reject("resumeRegionDownload", error)
323
+ }
324
+ })
325
+ }
326
+
327
+ override fun mergeOfflineRegions(path: String, promise: Promise) {
328
+ activateFileSource()
329
+ val offlineManager = OfflineManager.getInstance(context)
330
+
331
+ offlineManager.mergeOfflineRegions(
332
+ path, object : OfflineManager.MergeOfflineRegionsCallback {
333
+ override fun onMerge(offlineRegions: Array<OfflineRegion>?) {
334
+ promise.resolve(null)
335
+ }
336
+
337
+ override fun onError(error: String) {
338
+ promise.reject("mergeOfflineRegions", error)
339
+ }
340
+ })
341
+ }
342
+
343
+ override fun setTileCountLimit(limit: Double) {
344
+ val offlineManager = OfflineManager.getInstance(context)
345
+ offlineManager.setOfflineMapboxTileCountLimit(limit.toLong())
346
+ }
347
+
348
+ override fun setProgressEventThrottle(throttleValue: Double) {
349
+ progressEventThrottle = throttleValue
350
+ }
351
+
352
+ private fun makeDefinition(
353
+ latLngBounds: LatLngBounds, options: ReadableMap
354
+ ): OfflineTilePyramidRegionDefinition {
355
+ return OfflineTilePyramidRegionDefinition(
356
+ options.getString("mapStyle"),
357
+ latLngBounds,
358
+ options.getDouble("minZoom"),
359
+ options.getDouble("maxZoom"),
360
+ context.resources.displayMetrics.density
361
+ )
362
+ }
363
+
364
+ private fun parseRegionMetadata(region: OfflineRegion): JSONObject {
365
+ return try {
366
+ JSONObject(String(region.metadata))
367
+ } catch (e: JSONException) {
368
+ Log.w(NAME, "Failed to parse pack metadata: ${e.localizedMessage}")
369
+ JSONObject()
370
+ }
371
+ }
372
+
373
+ private fun runMigrations() {
374
+ activateFileSource()
375
+ val offlineManager = OfflineManager.getInstance(context)
376
+
377
+ offlineManager.listOfflineRegions(object : OfflineManager.ListOfflineRegionsCallback {
378
+ override fun onList(offlineRegions: Array<OfflineRegion>?) {
379
+ if (offlineRegions == null) {
380
+ Log.e(NAME, "No packs found for migration")
381
+
382
+ return
383
+ }
384
+
385
+ val regionsToMigrate = mutableListOf<OfflineRegion>()
386
+ for (region in offlineRegions) {
387
+ val metadata = parseRegionMetadata(region)
388
+ if (!metadata.has(MIGRATION_KEY) || metadata.getInt(MIGRATION_KEY) != MIGRATION_VERSION) {
389
+ regionsToMigrate.add(region)
390
+ }
391
+ }
392
+
393
+ if (regionsToMigrate.isEmpty()) {
394
+ Log.d(NAME, "No packs need migration")
395
+
396
+ return
397
+ }
398
+
399
+ Log.d(NAME, "Migrating ${regionsToMigrate.size} pack(s)")
400
+
401
+ for (region in regionsToMigrate) {
402
+ migrateRegion(region)
403
+ }
404
+ }
405
+
406
+ override fun onError(error: String) {
407
+ Log.e(NAME, "Failed to list packs for migration: $error")
408
+ }
409
+ })
410
+ }
411
+
412
+ private fun migrateRegion(region: OfflineRegion) {
413
+ val oldMetadata = parseRegionMetadata(region)
414
+
415
+ val newMetadata = JSONObject()
416
+ newMetadata.put(MIGRATION_KEY, MIGRATION_VERSION)
417
+ newMetadata.put("id", UUID.randomUUID().toString())
418
+ newMetadata.put("metadata", oldMetadata.toString())
419
+
420
+ region.updateMetadata(
421
+ newMetadata.toString().toByteArray(Charsets.UTF_8),
422
+ object : OfflineRegion.OfflineRegionUpdateMetadataCallback {
423
+ override fun onUpdate(metadata: ByteArray) {
424
+ val id = newMetadata.optString("id", "unknown")
425
+ Log.d(NAME, "Successfully migrated pack: $id")
426
+ }
427
+
428
+ override fun onError(error: String) {
429
+ Log.e(NAME, "Failed to migrate pack: $error")
430
+ }
431
+ })
432
+ }
433
+
434
+ private fun setOfflineRegionObserver(id: String, region: OfflineRegion) {
435
+ region.setObserver(object : OfflineRegion.OfflineRegionObserver {
436
+ var prevStatus: OfflineRegionStatus? = null
437
+ var timestamp = System.currentTimeMillis()
438
+
439
+ override fun onStatusChanged(status: OfflineRegionStatus) {
440
+ if (shouldSendUpdate(System.currentTimeMillis(), status)) {
441
+ emitOnProgress(makeRegionStatus(id, status))
442
+ timestamp = System.currentTimeMillis()
443
+ }
444
+ prevStatus = status
445
+ }
446
+
447
+ override fun onError(error: OfflineRegionError) {
448
+ emitOnError(makeErrorPayload(id, error.message))
449
+ }
450
+
451
+ override fun mapboxTileCountLimitExceeded(limit: Long) {
452
+ val message = "Mapbox tile limit exceeded $limit"
453
+ emitOnError(makeErrorPayload(id, message))
454
+ }
455
+
456
+ private fun shouldSendUpdate(
457
+ currentTimestamp: Long, curStatus: OfflineRegionStatus
458
+ ): Boolean {
459
+ val prev = prevStatus ?: return false
460
+
461
+ if (prev.downloadState != curStatus.downloadState) {
462
+ return true
463
+ }
464
+
465
+ if (currentTimestamp - timestamp > progressEventThrottle) {
466
+ return true
467
+ }
468
+
469
+ return false
470
+ }
471
+ })
472
+
473
+ region.setDownloadState(OfflineRegion.STATE_ACTIVE)
474
+ }
475
+
476
+ private fun makeErrorPayload(id: String, message: String): WritableMap {
477
+ val payload = WritableNativeMap()
478
+ payload.putString("id", id)
479
+ payload.putString("message", message)
480
+
481
+ return payload
482
+ }
483
+
484
+ private fun makeRegionStatus(
485
+ id: String, status: OfflineRegionStatus
486
+ ): WritableMap {
487
+ val map = Arguments.createMap()
488
+
489
+ var state = "inactive"
490
+ var percentage: Double
491
+
492
+ if (status.isComplete) {
493
+ state = "complete"
494
+ percentage = 100.0
495
+ } else {
496
+ percentage = if (status.requiredResourceCount >= 0) {
497
+ 100.0 * status.completedResourceCount / status.requiredResourceCount
498
+ } else {
499
+ 0.0
500
+ }
501
+
502
+ if (status.downloadState == OfflineRegion.STATE_ACTIVE) {
503
+ state = "active"
504
+ }
505
+ }
506
+
507
+ map.putString("id", id)
508
+ map.putString("state", state)
509
+ map.putDouble("percentage", percentage)
510
+ map.putInt("completedResourceCount", status.completedResourceCount.toInt())
511
+ map.putInt("completedResourceSize", status.completedResourceSize.toInt())
512
+ map.putInt("completedTileSize", status.completedTileSize.toInt())
513
+ map.putInt("completedTileCount", status.completedTileCount.toInt())
514
+ map.putInt("requiredResourceCount", status.requiredResourceCount.toInt())
515
+
516
+ return map
517
+ }
518
+
519
+ private fun getBoundsFromOptions(options: ReadableMap): LatLngBounds {
520
+ val boundsArray = options.getArray("bounds")
521
+ val bounds = GeoJSONUtils.toLatLngBounds(boundsArray)
522
+ ?: throw IllegalArgumentException("bounds must be an array of 4 numbers [west, south, east, north]")
523
+
524
+ return bounds
525
+ }
526
+
527
+ private fun fromOfflineRegion(region: OfflineRegion): WritableMap {
528
+ val map = Arguments.createMap()
529
+
530
+ val metadata = parseRegionMetadata(region)
531
+
532
+ map.putString("id", metadata.optString("id", ""))
533
+ map.putString("metadata", metadata.optString("metadata", "{}"))
534
+
535
+ region.definition.bounds?.let { bounds ->
536
+ map.putArray("bounds", GeoJSONUtils.fromLatLngBounds(bounds))
537
+ }
538
+
539
+ return map
540
+ }
541
+
542
+ private fun getRegionById(id: String?, offlineRegions: Array<OfflineRegion>?): OfflineRegion? {
543
+ if (id.isNullOrEmpty() || offlineRegions == null) {
544
+ return null
545
+ }
546
+
547
+ for (region in offlineRegions) {
548
+ val metadata = parseRegionMetadata(region)
549
+ if (metadata.has("id") && id == metadata.optString("id")) {
550
+ return region
551
+ }
552
+ }
553
+
554
+ return null
555
+ }
556
+
557
+ private fun activateFileSource() {
558
+ val fileSource = FileSource.getInstance(context)
559
+ fileSource.activate()
560
+ }
561
+ }
@@ -55,6 +55,7 @@ typedef void (^StyleLoadedBlock)(MLNStyle *__nonnull style);
55
55
 
56
56
  @property (nonatomic, assign) BOOL reactCompassEnabled;
57
57
  @property (nonatomic, strong, nullable) NSDictionary<NSString *, NSNumber *> *reactCompassPosition;
58
+ @property (nonatomic, assign) BOOL reactCompassHiddenFacingNorth;
58
59
 
59
60
  @property (nonatomic, assign) MLNCoordinateBounds maxBounds;
60
61
 
@@ -462,6 +462,11 @@ static double const M2PI = M_PI * 2;
462
462
  }];
463
463
  }
464
464
 
465
+ - (void)setReactCompassHiddenFacingNorth:(BOOL)reactCompassHiddenFacingNorth {
466
+ _reactCompassHiddenFacingNorth = reactCompassHiddenFacingNorth;
467
+ self.compassView.compassVisibility = _reactCompassHiddenFacingNorth ? MLNOrnamentVisibilityAdaptive : MLNOrnamentVisibilityVisible;
468
+ }
469
+
465
470
  - (void)setReactShowUserLocation:(BOOL)reactShowUserLocation {
466
471
  self.showsUserLocation = reactShowUserLocation;
467
472
  }
@@ -399,6 +399,10 @@ static NSDictionary *convertFollyDynamicToNSDictionary(const folly::dynamic &dyn
399
399
  [_view setReactCompassPosition:compassPosition];
400
400
  }
401
401
 
402
+ if (oldViewProps.compassHiddenFacingNorth != newViewProps.compassHiddenFacingNorth) {
403
+ [_view setReactCompassHiddenFacingNorth:newViewProps.compassHiddenFacingNorth];
404
+ }
405
+
402
406
  [super updateProps:props oldProps:oldProps];
403
407
  }
404
408
 
@@ -1,7 +1,6 @@
1
1
  #import "MLRNModule.h"
2
2
  #import "MLRNCustomHeaders.h"
3
3
  #import "MLRNEventTypes.h"
4
- #import "MLRNOfflineModule.h"
5
4
  #import "MLRNSource.h"
6
5
  @import MapLibre;
7
6
 
@@ -27,22 +26,10 @@ RCT_EXPORT_MODULE();
27
26
  NSMutableDictionary *styleSourceConsts = [[NSMutableDictionary alloc] init];
28
27
  [styleSourceConsts setObject:DEFAULT_SOURCE_ID forKey:@"DefaultSourceID"];
29
28
 
30
- // offline module callback names
31
- NSMutableDictionary *offlineModuleCallbackNames = [[NSMutableDictionary alloc] init];
32
- [offlineModuleCallbackNames setObject:RCT_MLRN_OFFLINE_CALLBACK_ERROR forKey:@"Error"];
33
- [offlineModuleCallbackNames setObject:RCT_MLRN_OFFLINE_CALLBACK_PROGRESS forKey:@"Progress"];
34
-
35
- NSMutableDictionary *offlinePackDownloadState = [[NSMutableDictionary alloc] init];
36
- [offlinePackDownloadState setObject:@(MLNOfflinePackStateInactive) forKey:@"Inactive"];
37
- [offlinePackDownloadState setObject:@(MLNOfflinePackStateActive) forKey:@"Active"];
38
- [offlinePackDownloadState setObject:@(MLNOfflinePackStateComplete) forKey:@"Complete"];
39
-
40
29
  return @{
41
30
  @"StyleURL" : styleURLS,
42
31
  @"EventTypes" : eventTypes,
43
32
  @"StyleSource" : styleSourceConsts,
44
- @"OfflineCallbackName" : offlineModuleCallbackNames,
45
- @"OfflinePackDownloadState" : offlinePackDownloadState,
46
33
  };
47
34
  }
48
35
 
@@ -1,11 +1,8 @@
1
- #import <React/RCTBridgeModule.h>
2
- #import <React/RCTEventEmitter.h>
1
+ #import <MapLibreReactNativeSpec/MapLibreReactNativeSpec.h>
3
2
 
4
- @import MapLibre;
3
+ #import <React/RCTInitializing.h>
5
4
 
6
- @interface MLRNOfflineModule : RCTEventEmitter <RCTBridgeModule>
7
-
8
- extern NSString *const RCT_MLRN_OFFLINE_CALLBACK_PROGRESS;
9
- extern NSString *const RCT_MLRN_OFFLINE_CALLBACK_ERROR;
5
+ @interface MLRNOfflineModule
6
+ : NativeOfflineModuleSpecBase <NativeOfflineModuleSpec, RCTInitializing>
10
7
 
11
8
  @end