react-native-maps 0.29.2 → 0.30.1

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 (44) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +10 -3
  3. package/__tests__/AnimatedRegion.test.js +62 -0
  4. package/docs/geojson.md +6 -3
  5. package/docs/installation.md +1 -1
  6. package/docs/marker.md +3 -2
  7. package/docs/tiles.md +35 -0
  8. package/index.d.ts +27 -9
  9. package/index.js +1 -3
  10. package/lib/android/build.gradle +3 -1
  11. package/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapTileProvider.java +515 -0
  12. package/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapTileWorker.java +122 -0
  13. package/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapUrlTile.java +124 -55
  14. package/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapUrlTileManager.java +34 -0
  15. package/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapView.java +6 -1
  16. package/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapWMSTile.java +31 -87
  17. package/lib/android/src/main/java/com/airbnb/android/react/maps/AirMapWMSTileManager.java +22 -2
  18. package/lib/components/AnimatedRegion.js +67 -101
  19. package/lib/components/Geojson.js +214 -84
  20. package/lib/components/MapCircle.js +1 -1
  21. package/lib/components/MapHeatmap.js +2 -2
  22. package/lib/components/MapMarker.js +4 -2
  23. package/lib/components/MapPolygon.js +1 -1
  24. package/lib/components/MapPolyline.js +1 -1
  25. package/lib/components/MapUrlTile.js +54 -2
  26. package/lib/components/MapView.js +4 -4
  27. package/lib/components/MapWMSTile.js +49 -2
  28. package/lib/components/decorateMapComponent.js +1 -1
  29. package/lib/ios/AirGoogleMaps/AIRGoogleMapMarker.h +1 -0
  30. package/lib/ios/AirGoogleMaps/AIRGoogleMapMarker.m +8 -0
  31. package/lib/ios/AirGoogleMaps/AIRGoogleMapMarkerManager.m +1 -0
  32. package/lib/ios/AirMaps/AIRMapManager.m +6 -1
  33. package/lib/ios/AirMaps/AIRMapMarkerManager.m +1 -0
  34. package/lib/ios/AirMaps/AIRMapUrlTile.h +22 -4
  35. package/lib/ios/AirMaps/AIRMapUrlTile.m +124 -34
  36. package/lib/ios/AirMaps/AIRMapUrlTileCachedOverlay.h +17 -0
  37. package/lib/ios/AirMaps/AIRMapUrlTileCachedOverlay.m +235 -0
  38. package/lib/ios/AirMaps/AIRMapUrlTileManager.m +6 -1
  39. package/lib/ios/AirMaps/AIRMapWMSTile.h +10 -16
  40. package/lib/ios/AirMaps/AIRMapWMSTile.m +68 -101
  41. package/lib/ios/AirMaps/AIRMapWMSTileManager.m +6 -1
  42. package/lib/ios/AirMaps.xcodeproj/project.pbxproj +7 -1
  43. package/package.json +24 -5
  44. package/react-native-google-maps.podspec +2 -2
@@ -0,0 +1,515 @@
1
+ package com.airbnb.android.react.maps;
2
+
3
+ import android.content.Context;
4
+
5
+ import android.util.Log;
6
+ import java.util.concurrent.TimeUnit;
7
+ import java.util.concurrent.Future;
8
+ import java.util.List;
9
+
10
+ import android.graphics.Bitmap;
11
+ import android.graphics.BitmapFactory;
12
+ import android.graphics.Canvas;
13
+ import android.graphics.Color;
14
+ import android.graphics.Paint;
15
+ import android.graphics.Rect;
16
+
17
+ import androidx.annotation.NonNull;
18
+ import androidx.work.OneTimeWorkRequest;
19
+ import androidx.work.WorkManager;
20
+ import androidx.work.WorkRequest;
21
+ import androidx.work.Worker;
22
+ import androidx.work.WorkerParameters;
23
+ import androidx.work.Data;
24
+ import androidx.work.Constraints;
25
+ import androidx.work.NetworkType;
26
+ import androidx.work.ExistingWorkPolicy;
27
+ import androidx.work.Operation;
28
+ import androidx.work.WorkInfo;
29
+
30
+ import com.google.android.gms.maps.GoogleMap;
31
+ import com.google.android.gms.maps.model.Tile;
32
+ import com.google.android.gms.maps.model.TileOverlay;
33
+ import com.google.android.gms.maps.model.TileOverlayOptions;
34
+ import com.google.android.gms.maps.model.TileProvider;
35
+ import com.google.android.gms.maps.model.UrlTileProvider;
36
+
37
+ import java.lang.System;
38
+
39
+ import java.io.ByteArrayOutputStream;
40
+ import java.io.File;
41
+ import java.io.FileInputStream;
42
+ import java.io.FileOutputStream;
43
+ import java.io.InputStream;
44
+ import java.io.OutputStream;
45
+ import java.io.IOException;
46
+
47
+ import java.net.MalformedURLException;
48
+ import java.net.URL;
49
+ import java.net.URLConnection;
50
+
51
+ public class AirMapTileProvider implements TileProvider {
52
+
53
+ class AIRMapUrlTileProvider extends UrlTileProvider {
54
+ private String urlTemplate;
55
+
56
+ public AIRMapUrlTileProvider(int width, int height, String urlTemplate) {
57
+ super(width, height);
58
+ this.urlTemplate = urlTemplate;
59
+ }
60
+
61
+ @Override
62
+ public URL getTileUrl(int x, int y, int zoom) {
63
+
64
+ if (AirMapTileProvider.this.flipY == true) {
65
+ y = (1 << zoom) - y - 1;
66
+ }
67
+
68
+ String s = this.urlTemplate
69
+ .replace("{x}", Integer.toString(x))
70
+ .replace("{y}", Integer.toString(y))
71
+ .replace("{z}", Integer.toString(zoom));
72
+ URL url = null;
73
+
74
+ if(AirMapTileProvider.this.maximumZ > 0 && zoom > AirMapTileProvider.this.maximumZ) {
75
+ return url;
76
+ }
77
+
78
+ if(AirMapTileProvider.this.minimumZ > 0 && zoom < AirMapTileProvider.this.minimumZ) {
79
+ return url;
80
+ }
81
+
82
+ try {
83
+ url = new URL(s);
84
+ } catch (MalformedURLException e) {
85
+ throw new AssertionError(e);
86
+ }
87
+ return url;
88
+ }
89
+
90
+ public void setUrlTemplate(String urlTemplate) {
91
+ this.urlTemplate = urlTemplate;
92
+ }
93
+ }
94
+
95
+ protected static final int BUFFER_SIZE = 16 * 1024;
96
+ protected static final int TARGET_TILE_SIZE = 512;
97
+ protected UrlTileProvider tileProvider;
98
+ protected String urlTemplate;
99
+ protected int tileSize;
100
+ protected boolean doubleTileSize;
101
+ protected int maximumZ;
102
+ protected int maximumNativeZ;
103
+ protected int minimumZ;
104
+ protected boolean flipY;
105
+ protected String tileCachePath;
106
+ protected int tileCacheMaxAge;
107
+ protected boolean offlineMode;
108
+ protected Context context;
109
+ protected boolean customMode;
110
+
111
+ public AirMapTileProvider(int tileSizet, boolean doubleTileSize, String urlTemplate,
112
+ int maximumZ, int maximumNativeZ, int minimumZ, boolean flipY, String tileCachePath,
113
+ int tileCacheMaxAge, boolean offlineMode, Context context, boolean customMode) {
114
+ this.tileProvider = new AIRMapUrlTileProvider(tileSizet, tileSizet, urlTemplate);
115
+
116
+ this.tileSize = tileSizet;
117
+ this.doubleTileSize = doubleTileSize;
118
+ this.urlTemplate = urlTemplate;
119
+ this.maximumZ = maximumZ;
120
+ this.maximumNativeZ = maximumNativeZ;
121
+ this.minimumZ = minimumZ;
122
+ this.flipY = flipY;
123
+ this.tileCachePath = tileCachePath;
124
+ this.tileCacheMaxAge = tileCacheMaxAge;
125
+ this.offlineMode = offlineMode;
126
+ this.context = context;
127
+ this.customMode = customMode;
128
+ }
129
+
130
+ @Override
131
+ public Tile getTile(int x, int y, int zoom) {
132
+ if (!this.customMode) return this.tileProvider.getTile(x, y, zoom);
133
+
134
+ byte[] image = null;
135
+ int maximumZ = this.maximumZ > 0 ? this.maximumZ : Integer.MAX_VALUE;
136
+
137
+ if (this.tileSize == 256 && this.doubleTileSize && zoom + 1 <= this.maximumNativeZ && zoom + 1 <= maximumZ) {
138
+ Log.d("urlTile", "pullTilesFromHigherZoom");
139
+ image = pullTilesFromHigherZoom(x, y, zoom);
140
+ }
141
+
142
+ if (zoom > this.maximumNativeZ) {
143
+ Log.d("urlTile", "scaleLowerZoomTile");
144
+ image = scaleLowerZoomTile(x, y, zoom, this.maximumNativeZ);
145
+ }
146
+
147
+ if (image == null && zoom <= maximumZ) {
148
+ Log.d("urlTile", "getTileImage");
149
+ image = getTileImage(x, y, zoom);
150
+ }
151
+
152
+ if (image == null && this.tileCachePath != null && this.offlineMode) {
153
+ Log.d("urlTile", "findLowerZoomTileForScaling");
154
+ int zoomLevelToStart = (zoom > this.maximumNativeZ) ? this.maximumNativeZ - 1 : zoom - 1;
155
+ int minimumZoomToSearch = this.minimumZ >= zoom - 3 ? this.minimumZ : zoom - 3;
156
+ for (int tryZoom = zoomLevelToStart; tryZoom >= minimumZoomToSearch; tryZoom--) {
157
+ image = scaleLowerZoomTile(x, y, zoom, tryZoom);
158
+ if (image != null) {
159
+ break;
160
+ }
161
+ }
162
+ }
163
+
164
+ return image == null ? null : new Tile(this.tileSize, this.tileSize, image);
165
+ }
166
+
167
+ byte[] getTileImage(int x, int y, int zoom) {
168
+ byte[] image = null;
169
+ boolean fallbackOnSyncFetch = false;
170
+
171
+ if (this.tileCachePath != null) {
172
+ image = readTileImage(x, y, zoom);
173
+ if (image != null) {
174
+ Log.d("urlTile: tile cache HIT for ", Integer.toString(zoom) +
175
+ "/" + Integer.toString(x) + "/" + Integer.toString(y));
176
+ } else {
177
+ Log.d("urlTile: tile cache MISS for ", Integer.toString(zoom) +
178
+ "/" + Integer.toString(x) + "/" + Integer.toString(y));
179
+ }
180
+ if (image != null && !this.offlineMode) {
181
+ checkForRefresh(x, y, zoom);
182
+ }
183
+ }
184
+
185
+ if (image == null && !this.offlineMode && this.tileCachePath != null) {
186
+ String fileName = getTileFilename(x, y, zoom);
187
+ Constraints constraints = new Constraints.Builder()
188
+ .setRequiredNetworkType(NetworkType.CONNECTED)
189
+ .build();
190
+ OneTimeWorkRequest tileRefreshWorkRequest = new OneTimeWorkRequest.Builder(AirMapTileWorker.class)
191
+ .setConstraints(constraints)
192
+ .addTag(fileName)
193
+ .setInputData(
194
+ new Data.Builder()
195
+ .putString("url", getTileUrl(x, y, zoom).toString())
196
+ .putString("filename", fileName)
197
+ .putInt("maxAge", -1)
198
+ .build()
199
+ )
200
+ .build();
201
+ if (tileRefreshWorkRequest != null) {
202
+ WorkManager workManager = WorkManager.getInstance(this.context.getApplicationContext());
203
+ Operation fetchOperation = workManager
204
+ .enqueueUniqueWork(fileName, ExistingWorkPolicy.KEEP, tileRefreshWorkRequest);
205
+ Future<Operation.State.SUCCESS> operationFuture = fetchOperation.getResult();
206
+ try {
207
+ operationFuture.get(1L, TimeUnit.SECONDS);
208
+ Thread.sleep(500);
209
+ Future<List<WorkInfo>> fetchFuture = workManager.getWorkInfosByTag(fileName);
210
+ List<WorkInfo> workInfo = fetchFuture.get(1L, TimeUnit.SECONDS);
211
+ Log.d("urlTile: ", workInfo.get(0).toString());
212
+ if (this.tileCachePath != null) {
213
+ image = readTileImage(x, y, zoom);
214
+ if (image != null) {
215
+ Log.d("urlTile: tile cache fetch HIT for ", Integer.toString(zoom) +
216
+ "/" + Integer.toString(x) + "/" + Integer.toString(y));
217
+ } else {
218
+ Log.d("urlTile: tile cache fetch MISS for ", Integer.toString(zoom) +
219
+ "/" + Integer.toString(x) + "/" + Integer.toString(y));
220
+ }
221
+ }
222
+ } catch (Exception e) {
223
+ e.printStackTrace();
224
+ }
225
+ } else {
226
+ fallbackOnSyncFetch = true;
227
+ }
228
+ } else if (fallbackOnSyncFetch || (image == null && !this.offlineMode)) {
229
+ Log.d("urlTile", "Normal fetch");
230
+ image = fetchTile(x, y, zoom);
231
+ if (image == null) {
232
+ Log.d("urlTile: tile fetch TIMEOUT / FAIL for ", Integer.toString(zoom) +
233
+ "/" + Integer.toString(x) + "/" + Integer.toString(y));
234
+ }
235
+ }
236
+
237
+ return image;
238
+ }
239
+
240
+ byte[] pullTilesFromHigherZoom(int x, int y, int zoom) {
241
+ byte[] data;
242
+ Bitmap image = getNewBitmap();
243
+ Canvas canvas = new Canvas(image);
244
+ Paint paint = new Paint();
245
+
246
+ x = x * 2;
247
+ y = y * 2;
248
+ byte[] leftTop = getTileImage(x, y, zoom + 1);
249
+ byte[] leftBottom = getTileImage(x, y + 1, zoom + 1);
250
+ byte[] rightTop = getTileImage(x + 1, y, zoom + 1);
251
+ byte[] rightBottom = getTileImage(x + 1, y + 1, zoom + 1);
252
+
253
+ if (leftTop == null || leftBottom == null || rightTop == null || rightBottom == null) {
254
+ return null;
255
+ }
256
+
257
+ Bitmap bitmap;
258
+
259
+ bitmap = BitmapFactory.decodeByteArray(leftTop, 0, leftTop.length);
260
+ canvas.drawBitmap(bitmap, 0, 0, paint);
261
+ bitmap.recycle();
262
+
263
+ bitmap = BitmapFactory.decodeByteArray(leftBottom, 0, leftBottom.length);
264
+ canvas.drawBitmap(bitmap, 0, 256, paint);
265
+ bitmap.recycle();
266
+
267
+ bitmap = BitmapFactory.decodeByteArray(rightTop, 0, rightTop.length);
268
+ canvas.drawBitmap(bitmap, 256, 0, paint);
269
+ bitmap.recycle();
270
+
271
+ bitmap = BitmapFactory.decodeByteArray(rightBottom, 0, rightBottom.length);
272
+ canvas.drawBitmap(bitmap, 256, 256, paint);
273
+ bitmap.recycle();
274
+
275
+ data = bitmapToByteArray(image);
276
+ image.recycle();
277
+ return data;
278
+ }
279
+
280
+ Bitmap getNewBitmap() {
281
+ Bitmap image = Bitmap.createBitmap(TARGET_TILE_SIZE, TARGET_TILE_SIZE, Bitmap.Config.ARGB_8888);
282
+ image.eraseColor(Color.TRANSPARENT);
283
+ return image;
284
+ }
285
+
286
+ byte[] bitmapToByteArray(Bitmap bm) {
287
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
288
+ bm.compress(Bitmap.CompressFormat.PNG, 100, bos);
289
+
290
+ byte[] data = bos.toByteArray();
291
+ try {
292
+ bos.close();
293
+ } catch (Exception e) {
294
+ e.printStackTrace();
295
+ }
296
+ return data;
297
+ }
298
+
299
+ byte[] scaleLowerZoomTile(int x, int y, int zoom, int maximumZoom) {
300
+ int overZoomLevel = zoom - maximumZoom;
301
+ int zoomFactor = 1 << overZoomLevel;
302
+
303
+ int xParent = x >> overZoomLevel;
304
+ int yParent = y >> overZoomLevel;
305
+ int zoomParent = zoom - overZoomLevel;
306
+
307
+ int xOffset = x % zoomFactor;;
308
+ int yOffset = y % zoomFactor;
309
+
310
+ byte[] data;
311
+ Bitmap image = getNewBitmap();
312
+ Canvas canvas = new Canvas(image);
313
+ Paint paint = new Paint();
314
+
315
+ data = getTileImage(xParent, yParent, zoomParent);
316
+ if (data == null) return null;
317
+
318
+ Bitmap sourceImage;
319
+ sourceImage = BitmapFactory.decodeByteArray(data, 0, data.length);
320
+
321
+ int subTileSize = this.tileSize / zoomFactor;
322
+ Rect sourceRect = new Rect(xOffset * subTileSize, yOffset * subTileSize, xOffset * subTileSize + subTileSize , yOffset * subTileSize + subTileSize);
323
+ Rect targetRect = new Rect(0,0,TARGET_TILE_SIZE, TARGET_TILE_SIZE);
324
+ canvas.drawBitmap(sourceImage, sourceRect, targetRect, paint);
325
+ sourceImage.recycle();
326
+
327
+ data = bitmapToByteArray(image);
328
+ image.recycle();
329
+ return data;
330
+ }
331
+
332
+ void checkForRefresh(int x, int y, int zoom) {
333
+ String fileName = getTileFilename(x, y, zoom);
334
+ File file = new File(fileName);
335
+ long lastModified = file.lastModified();
336
+ long now = System.currentTimeMillis();
337
+
338
+ if ((now - lastModified) / 1000 > this.tileCacheMaxAge) {
339
+ Log.d("urlTile", "Refreshing");
340
+ Constraints constraints = new Constraints.Builder()
341
+ .setRequiredNetworkType(NetworkType.CONNECTED)
342
+ .build();
343
+ OneTimeWorkRequest tileRefreshWorkRequest = new OneTimeWorkRequest.Builder(AirMapTileWorker.class)
344
+ .setConstraints(constraints)
345
+ .addTag(fileName)
346
+ .setInputData(
347
+ new Data.Builder()
348
+ .putString("url", getTileUrl(x, y, zoom).toString())
349
+ .putString("filename", fileName)
350
+ .putInt("maxAge", this.tileCacheMaxAge)
351
+ .build()
352
+ )
353
+ .build();
354
+ if (tileRefreshWorkRequest != null) {
355
+ WorkManager.getInstance(this.context.getApplicationContext())
356
+ .enqueueUniqueWork(fileName, ExistingWorkPolicy.KEEP, tileRefreshWorkRequest);
357
+ }
358
+ }
359
+ }
360
+
361
+ byte[] fetchTile(int x, int y, int zoom) {
362
+ URL url = getTileUrl(x, y, zoom);
363
+ ByteArrayOutputStream buffer = null;
364
+ InputStream in = null;
365
+
366
+ try {
367
+ URLConnection conn = url.openConnection();
368
+ in = conn.getInputStream();
369
+ buffer = new ByteArrayOutputStream();
370
+
371
+ int nRead;
372
+ byte[] data = new byte[BUFFER_SIZE];
373
+
374
+ while ((nRead = in.read(data, 0, BUFFER_SIZE)) != -1) {
375
+ buffer.write(data, 0, nRead);
376
+ }
377
+ buffer.flush();
378
+
379
+ return buffer.toByteArray();
380
+ } catch (IOException e) {
381
+ e.printStackTrace();
382
+ return null;
383
+ } catch (OutOfMemoryError e) {
384
+ e.printStackTrace();
385
+ return null;
386
+ } finally {
387
+ if (in != null) try { in.close(); } catch (Exception ignored) {}
388
+ if (buffer != null) try { buffer.close(); } catch (Exception ignored) {}
389
+ }
390
+ }
391
+
392
+ byte[] readTileImage(int x, int y, int zoom) {
393
+ InputStream in = null;
394
+ ByteArrayOutputStream buffer = null;
395
+ String fileName = getTileFilename(x, y, zoom);
396
+ if (fileName == null) {
397
+ return null;
398
+ }
399
+
400
+ File file = new File(fileName);
401
+
402
+ try {
403
+ in = new FileInputStream(file);
404
+ buffer = new ByteArrayOutputStream();
405
+
406
+ int nRead;
407
+ byte[] data = new byte[BUFFER_SIZE];
408
+
409
+ while ((nRead = in.read(data, 0, BUFFER_SIZE)) != -1) {
410
+ buffer.write(data, 0, nRead);
411
+ }
412
+ buffer.flush();
413
+
414
+ if (this.tileCacheMaxAge == 0) {
415
+ file.setLastModified(System.currentTimeMillis());
416
+ }
417
+
418
+ return buffer.toByteArray();
419
+ } catch (IOException e) {
420
+ e.printStackTrace();
421
+ return null;
422
+ } catch (OutOfMemoryError e) {
423
+ e.printStackTrace();
424
+ return null;
425
+ } finally {
426
+ if (in != null) try { in.close(); } catch (Exception ignored) {}
427
+ if (buffer != null) try { buffer.close(); } catch (Exception ignored) {}
428
+ }
429
+ }
430
+
431
+ boolean writeTileImage(byte[] image, int x, int y, int zoom) {
432
+ OutputStream out = null;
433
+ String fileName = getTileFilename(x, y, zoom);
434
+ if (fileName == null) {
435
+ return false;
436
+ }
437
+
438
+ try {
439
+ File file = new File(fileName);
440
+ file.getParentFile().mkdirs();
441
+ out = new FileOutputStream(file);
442
+ out.write(image);
443
+
444
+ return true;
445
+ } catch (IOException e) {
446
+ e.printStackTrace();
447
+ return false;
448
+ } catch (OutOfMemoryError e) {
449
+ e.printStackTrace();
450
+ return false;
451
+ } finally {
452
+ if (out != null) try { out.close(); } catch (Exception ignored) {}
453
+ }
454
+ }
455
+
456
+ String getTileFilename(int x, int y, int zoom) {
457
+ if (this.tileCachePath == null) {
458
+ return null;
459
+ }
460
+ String s = this.tileCachePath + '/' + Integer.toString(zoom) +
461
+ "/" + Integer.toString(x) + "/" + Integer.toString(y);
462
+ return s;
463
+ }
464
+
465
+ protected URL getTileUrl(int x, int y, int zoom) {
466
+ return this.tileProvider.getTileUrl(x, y, zoom);
467
+ }
468
+
469
+ public void setUrlTemplate(String urlTemplate) {
470
+ this.urlTemplate = urlTemplate;
471
+ }
472
+
473
+ public void setTileSize(int tileSize) {
474
+ if (this.tileSize != tileSize) {
475
+ this.tileProvider = new AIRMapUrlTileProvider(tileSize, tileSize, urlTemplate);
476
+ }
477
+ this.tileSize = tileSize;
478
+ }
479
+
480
+ public void setDoubleTileSize(boolean doubleTileSize) {
481
+ this.doubleTileSize = doubleTileSize;
482
+ }
483
+
484
+ public void setMaximumZ(int maximumZ) {
485
+ this.maximumZ = maximumZ;
486
+ }
487
+
488
+ public void setMaximumNativeZ(int maximumNativeZ) {
489
+ this.maximumNativeZ = maximumNativeZ;
490
+ }
491
+
492
+ public void setMinimumZ(int minimumZ) {
493
+ this.minimumZ = minimumZ;
494
+ }
495
+
496
+ public void setFlipY(boolean flipY) {
497
+ this.flipY = flipY;
498
+ }
499
+
500
+ public void setTileCachePath(String tileCachePath) {
501
+ this.tileCachePath = tileCachePath;
502
+ }
503
+
504
+ public void setTileCacheMaxAge(int tileCacheMaxAge) {
505
+ this.tileCacheMaxAge = tileCacheMaxAge;
506
+ }
507
+
508
+ public void setOfflineMode(boolean offlineMode) {
509
+ this.offlineMode = offlineMode;
510
+ }
511
+
512
+ public void setCustomMode() {
513
+ this.customMode = customMode;
514
+ }
515
+ }
@@ -0,0 +1,122 @@
1
+ package com.airbnb.android.react.maps;
2
+
3
+ import android.content.Context;
4
+
5
+ import android.util.Log;
6
+
7
+ import androidx.annotation.NonNull;
8
+ import androidx.work.Worker;
9
+ import androidx.work.WorkerParameters;
10
+
11
+ import java.io.ByteArrayOutputStream;
12
+ import java.io.File;
13
+ import java.io.FileInputStream;
14
+ import java.io.FileOutputStream;
15
+ import java.io.InputStream;
16
+ import java.io.OutputStream;
17
+ import java.io.IOException;
18
+
19
+ import java.net.MalformedURLException;
20
+ import java.net.URL;
21
+
22
+ public class AirMapTileWorker extends Worker {
23
+ private static final int BUFFER_SIZE = 16 * 1024;
24
+
25
+ public AirMapTileWorker(
26
+ @NonNull Context context,
27
+ @NonNull WorkerParameters params) {
28
+ super(context, params);
29
+ }
30
+
31
+ @Override
32
+ public Result doWork() {
33
+ byte[] image = null;
34
+ URL url = null;
35
+ String fileName = getInputData().getString("filename");
36
+
37
+ try {
38
+ int tileCacheMaxAge = getInputData().getInt("maxAge", 0);
39
+ if (tileCacheMaxAge >= 0) {
40
+ File file = new File(fileName);
41
+ long lastModified = file.lastModified();
42
+ long now = System.currentTimeMillis();
43
+ if ((now - lastModified) / 1000 < tileCacheMaxAge) return Result.failure();
44
+ }
45
+ } catch (Error e) {
46
+ return Result.failure();
47
+ }
48
+
49
+ try {
50
+ url = new URL(getInputData().getString("url"));
51
+ } catch (MalformedURLException e) {
52
+ throw new AssertionError(e);
53
+ }
54
+
55
+ image = fetchTile(url);
56
+ if (image != null) {
57
+ boolean success = writeTileImage(image, fileName);
58
+ if (!success) {
59
+ return Result.failure();
60
+ }
61
+ } else {
62
+ return Result.retry();
63
+ }
64
+
65
+ // Indicate whether the work finished successfully with the Result
66
+ Log.d("urlTile", "Worker fetched " + fileName);
67
+ return Result.success();
68
+ }
69
+
70
+ private byte[] fetchTile(URL url) {
71
+ ByteArrayOutputStream buffer = null;
72
+ InputStream in = null;
73
+
74
+ try {
75
+ in = url.openStream();
76
+ buffer = new ByteArrayOutputStream();
77
+
78
+ int nRead;
79
+ byte[] data = new byte[BUFFER_SIZE];
80
+
81
+ while ((nRead = in.read(data, 0, BUFFER_SIZE)) != -1) {
82
+ buffer.write(data, 0, nRead);
83
+ }
84
+ buffer.flush();
85
+
86
+ return buffer.toByteArray();
87
+ } catch (IOException e) {
88
+ e.printStackTrace();
89
+ return null;
90
+ } catch (OutOfMemoryError e) {
91
+ e.printStackTrace();
92
+ return null;
93
+ } finally {
94
+ if (in != null) try { in.close(); } catch (Exception ignored) {}
95
+ if (buffer != null) try { buffer.close(); } catch (Exception ignored) {}
96
+ }
97
+ }
98
+
99
+ private boolean writeTileImage(byte[] image, String fileName) {
100
+ OutputStream out = null;
101
+ if (fileName == null) {
102
+ return false;
103
+ }
104
+
105
+ try {
106
+ File file = new File(fileName);
107
+ file.getParentFile().mkdirs();
108
+ out = new FileOutputStream(file);
109
+ out.write(image);
110
+
111
+ return true;
112
+ } catch (IOException e) {
113
+ e.printStackTrace();
114
+ return false;
115
+ } catch (OutOfMemoryError e) {
116
+ e.printStackTrace();
117
+ return false;
118
+ } finally {
119
+ if (out != null) try { out.close(); } catch (Exception ignored) {}
120
+ }
121
+ }
122
+ }