@iternio/react-native-auto-play 0.0.6 → 0.0.8

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.
@@ -65,6 +65,8 @@ android {
65
65
 
66
66
  buildConfigField "float", "SCALE_FACTOR", getExtOrDefault("androidAutoScaleFactor")
67
67
  buildConfigField "long", "TELEMETRY_UPDATE_INTERVAL", getExtOrDefault("androidTelemetryUpdateInterval")
68
+ buildConfigField "long", "CLUSTER_SPLASH_DELAY_MS", getExtOrDefault("clusterSplashDelayMs")
69
+ buildConfigField "long", "CLUSTER_SPLASH_DURATION_MS", getExtOrDefault("clusterSplashDurationMs")
68
70
  }
69
71
 
70
72
  externalNativeBuild {
@@ -7,4 +7,6 @@ ReactNativeAutoPlay_ndkVersion=27.1.12297006
7
7
  ReactNativeAutoPlay_isAutomotiveApp=false
8
8
  # apply a scale factor on the root view that affects everything rendered by react-native, does not affect templates
9
9
  ReactNativeAutoPlay_androidAutoScaleFactor=1.5f
10
- ReactNativeAutoPlay_androidTelemetryUpdateInterval=4000
10
+ ReactNativeAutoPlay_androidTelemetryUpdateInterval=4000
11
+ ReactNativeAutoPlay_clusterSplashDelayMs=1000
12
+ ReactNativeAutoPlay_clusterSplashDurationMs=500
@@ -1,20 +1,31 @@
1
1
  package com.margelo.nitro.swe.iternio.reactnativeautoplay
2
2
 
3
+ import android.Manifest
3
4
  import android.annotation.SuppressLint
5
+ import android.app.Notification
6
+ import android.app.NotificationChannel
7
+ import android.app.NotificationManager
4
8
  import android.content.ComponentName
5
9
  import android.content.Intent
6
10
  import android.content.ServiceConnection
7
11
  import android.content.pm.ApplicationInfo
12
+ import android.content.pm.PackageManager
13
+ import android.graphics.Bitmap
8
14
  import android.os.IBinder
15
+ import android.util.Log
9
16
  import androidx.car.app.CarAppService
10
17
  import androidx.car.app.Session
11
18
  import androidx.car.app.SessionInfo
19
+ import androidx.car.app.notification.CarAppExtender
12
20
  import androidx.car.app.validation.HostValidator
21
+ import androidx.core.app.NotificationCompat
22
+ import androidx.core.app.NotificationManagerCompat
13
23
  import androidx.lifecycle.DefaultLifecycleObserver
14
24
  import androidx.lifecycle.LifecycleOwner
15
25
  import com.facebook.react.ReactApplication
16
26
  import com.facebook.react.bridge.LifecycleEventListener
17
27
  import com.facebook.react.bridge.ReactContext
28
+ import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.AppInfo
18
29
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.ReactContextResolver
19
30
  import kotlinx.coroutines.CoroutineScope
20
31
  import kotlinx.coroutines.Dispatchers
@@ -22,6 +33,7 @@ import kotlinx.coroutines.launch
22
33
 
23
34
  class AndroidAutoService : CarAppService() {
24
35
  private lateinit var reactContext: ReactContext
36
+ private lateinit var notificationManager: NotificationManager
25
37
 
26
38
  private var isServiceBound = false
27
39
  private var isSessionStarted = false
@@ -39,11 +51,21 @@ class AndroidAutoService : CarAppService() {
39
51
 
40
52
  override fun onCreate() {
41
53
  super.onCreate()
54
+ instance = this
42
55
 
43
56
  CoroutineScope(Dispatchers.Main).launch {
44
57
  reactContext = ReactContextResolver.getReactContext(application as ReactApplication)
45
58
  reactContext.addLifecycleEventListener(reactLifecycleObserver)
46
59
  }
60
+
61
+ notificationManager = getSystemService(NotificationManager::class.java)
62
+ val appLabel = AppInfo.getApplicationLabel(this)
63
+
64
+ getSystemService(NotificationManager::class.java).createNotificationChannel(
65
+ NotificationChannel(
66
+ CHANNEL_ID, appLabel, NotificationManager.IMPORTANCE_DEFAULT
67
+ )
68
+ )
47
69
  }
48
70
 
49
71
  override fun onCreateSession(sessionInfo: SessionInfo): Session {
@@ -60,6 +82,7 @@ class AndroidAutoService : CarAppService() {
60
82
 
61
83
  override fun onDestroy() {
62
84
  super.onDestroy()
85
+ instance = null
63
86
 
64
87
  stopForeground(STOP_FOREGROUND_REMOVE)
65
88
 
@@ -120,7 +143,61 @@ class AndroidAutoService : CarAppService() {
120
143
  }
121
144
  }
122
145
 
146
+ private fun createNotification(
147
+ title: String?, text: String?, largeIcon: Bitmap?
148
+ ): Notification {
149
+ return NotificationCompat.Builder(this, CHANNEL_ID)
150
+ .setSmallIcon(R.drawable.ic_notification).setOngoing(true)
151
+ .setCategory(NotificationCompat.CATEGORY_NAVIGATION).setOnlyAlertOnce(true)
152
+ .setWhen(System.currentTimeMillis()).setPriority(NotificationManager.IMPORTANCE_LOW)
153
+ .extend(
154
+ CarAppExtender.Builder().setImportance(NotificationManagerCompat.IMPORTANCE_LOW)
155
+ .build()
156
+ ).apply {
157
+ title?.let {
158
+ setContentTitle(it)
159
+ }
160
+ text?.let {
161
+ setContentText(it)
162
+ setTicker(it)
163
+ }
164
+ largeIcon?.let {
165
+ setLargeIcon(it)
166
+ }
167
+ }.build()
168
+ }
169
+
170
+ fun startForeground() {
171
+ val isLocationPermissionGranted =
172
+ checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED || checkSelfPermission(
173
+ Manifest.permission.ACCESS_FINE_LOCATION
174
+ ) == PackageManager.PERMISSION_GRANTED
175
+
176
+ if (!isLocationPermissionGranted) {
177
+ Log.w(TAG, "location permission not granted, unable to start foreground service!")
178
+ return
179
+ }
180
+
181
+ try {
182
+ startForeground(
183
+ NOTIFICATION_ID, createNotification(null, null, null)
184
+ )
185
+ } catch (e: SecurityException) {
186
+ Log.e(TAG, "failed to start foreground service", e)
187
+ }
188
+ }
189
+
190
+ fun notify(title: String?, text: String?, icon: Bitmap?) {
191
+ val notification = createNotification(title, text, icon)
192
+ notificationManager.notify(NOTIFICATION_ID, notification)
193
+ }
194
+
123
195
  companion object {
124
196
  const val TAG = "AndroidAutoService"
197
+ private const val NOTIFICATION_ID = 1
198
+ private const val CHANNEL_ID = "AutoPlayServiceChannel"
199
+
200
+ var instance: AndroidAutoService? = null
201
+ private set
125
202
  }
126
203
  }
@@ -28,7 +28,7 @@ class HybridCluster : HybridClusterSpec() {
28
28
  val carContext = AndroidAutoSession.getCarContext(clusterId)
29
29
  ?: throw IllegalArgumentException("initRootView failed, carContext for $clusterId cluster not found")
30
30
  val result = ThreadUtil.postOnUiAndAwait {
31
- VirtualRenderer(carContext, clusterId)
31
+ VirtualRenderer(carContext, clusterId, isCluster = true)
32
32
  }
33
33
  if (result.isFailure) {
34
34
  throw result.exceptionOrNull()
@@ -7,9 +7,12 @@ import android.graphics.Rect
7
7
  import android.hardware.display.DisplayManager
8
8
  import android.os.Bundle
9
9
  import android.view.Display
10
+ import android.view.LayoutInflater
10
11
  import android.view.View
11
12
  import android.view.ViewGroup
13
+ import android.view.ViewTreeObserver
12
14
  import android.widget.FrameLayout
15
+ import android.widget.TextView
13
16
  import androidx.car.app.AppManager
14
17
  import androidx.car.app.CarContext
15
18
  import androidx.car.app.SurfaceCallback
@@ -25,6 +28,7 @@ import com.facebook.react.uimanager.DisplayMetricsHolder
25
28
  import com.facebook.react.uimanager.UIManagerHelper
26
29
  import com.facebook.react.uimanager.common.UIManagerType
27
30
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.template.AndroidAutoTemplate
31
+ import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.AppInfo
28
32
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.Debouncer
29
33
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.utils.ReactContextResolver
30
34
  import kotlinx.coroutines.CoroutineScope
@@ -33,7 +37,9 @@ import kotlinx.coroutines.launch
33
37
  import kotlin.math.floor
34
38
 
35
39
  class VirtualRenderer(
36
- private val context: CarContext, private val moduleName: String
40
+ private val context: CarContext,
41
+ private val moduleName: String,
42
+ private val isCluster: Boolean = false
37
43
  ) {
38
44
  private lateinit var uiManager: FabricUIManager
39
45
  private lateinit var display: Display
@@ -48,6 +54,8 @@ class VirtualRenderer(
48
54
  private var height: Int = 0
49
55
  private var width: Int = 0
50
56
 
57
+ private var splashWillDisappear = false
58
+
51
59
  /**
52
60
  * scale is the actual scale factor required to calculate proper insets and is passed in initialProperties to js side
53
61
  */
@@ -55,7 +63,7 @@ class VirtualRenderer(
55
63
  val scale = BuildConfig.SCALE_FACTOR * virtualScreenDensity
56
64
 
57
65
  init {
58
- virtualRenderer.put(moduleName, this)
66
+ virtualRenderer[moduleName] = this
59
67
 
60
68
  CoroutineScope(Dispatchers.Main).launch {
61
69
  reactContext =
@@ -63,7 +71,7 @@ class VirtualRenderer(
63
71
 
64
72
  if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
65
73
  uiManager = UIManagerHelper.getUIManager(
66
- reactContext, UIManagerType.Companion.FABRIC
74
+ reactContext, UIManagerType.FABRIC
67
75
  ) as FabricUIManager
68
76
  }
69
77
 
@@ -80,6 +88,11 @@ class VirtualRenderer(
80
88
  var visibleArea = Rect(0, 0, 0, 0)
81
89
 
82
90
  override fun onSurfaceAvailable(surfaceContainer: SurfaceContainer) {
91
+ if (surfaceContainer.surface == null) {
92
+ Log.w(TAG, "surface is null")
93
+ return
94
+ }
95
+
83
96
  val manager = context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
84
97
  val virtualDisplay = manager.createVirtualDisplay(
85
98
  moduleName,
@@ -188,7 +201,7 @@ class VirtualRenderer(
188
201
  val left = floor((visibleArea.left + additionalMarginLeft) / scale).toDouble()
189
202
  val right =
190
203
  floor((width - visibleArea.right + additionalMarginRight) / scale).toDouble()
191
- HybridAutoPlay.Companion.emitSafeAreaInsets(
204
+ HybridAutoPlay.emitSafeAreaInsets(
192
205
  moduleName = moduleName,
193
206
  top = top,
194
207
  bottom = bottom,
@@ -213,7 +226,7 @@ class VirtualRenderer(
213
226
  defaultMargin
214
227
  ) / scale
215
228
  ).toDouble()
216
- HybridAutoPlay.Companion.emitSafeAreaInsets(
229
+ HybridAutoPlay.emitSafeAreaInsets(
217
230
  moduleName = moduleName,
218
231
  top = top,
219
232
  bottom = bottom,
@@ -227,9 +240,9 @@ class VirtualRenderer(
227
240
  }
228
241
 
229
242
  private fun getMapTemplateConfig(): MapTemplateConfig? {
230
- val screenManager = AndroidAutoScreen.Companion.getScreen(moduleName)?.screenManager ?: return null
243
+ val screenManager = AndroidAutoScreen.getScreen(moduleName)?.screenManager ?: return null
231
244
  val marker = screenManager.top.marker ?: return null
232
- return AndroidAutoTemplate.Companion.getConfig(marker) as MapTemplateConfig?
245
+ return AndroidAutoTemplate.getConfig(marker) as MapTemplateConfig?
233
246
  }
234
247
 
235
248
  private fun initRenderer() {
@@ -283,12 +296,18 @@ class VirtualRenderer(
283
296
  override fun onCreate(savedInstanceState: Bundle?) {
284
297
  super.onCreate(savedInstanceState)
285
298
 
299
+ var splashScreenView: View? = null
300
+
286
301
  if (!this@VirtualRenderer::reactRootView.isInitialized) {
302
+ splashScreenView =
303
+ if (isCluster) getClusterSplashScreen(context, height, width) else null
304
+
287
305
  val instanceManager =
288
306
  (context.applicationContext as ReactApplication).reactNativeHost.reactInstanceManager
289
307
  reactRootView = ReactRootView(context.applicationContext).apply {
290
308
  layoutParams = FrameLayout.LayoutParams(
291
- (this@MapPresentation.width / reactNativeScale).toInt(), (this@MapPresentation.height / reactNativeScale).toInt()
309
+ (this@MapPresentation.width / reactNativeScale).toInt(),
310
+ (this@MapPresentation.height / reactNativeScale).toInt()
292
311
  )
293
312
  scaleX = reactNativeScale
294
313
  scaleY = reactNativeScale
@@ -296,6 +315,10 @@ class VirtualRenderer(
296
315
  pivotY = 0f
297
316
  setBackgroundColor(Color.DKGRAY)
298
317
 
318
+ splashScreenView?.let {
319
+ removeClusterSplashScreen({ viewTreeObserver }, it)
320
+ }
321
+
299
322
  startReactApplication(instanceManager, moduleName, initialProperties)
300
323
  runApplication()
301
324
  }
@@ -308,9 +331,13 @@ class VirtualRenderer(
308
331
  FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT
309
332
  )
310
333
  clipChildren = false
334
+
335
+ addView(reactRootView)
311
336
  }
312
337
 
313
- rootContainer.addView(reactRootView)
338
+ splashScreenView?.let {
339
+ rootContainer.addView(it)
340
+ }
314
341
 
315
342
  setContentView(rootContainer)
316
343
  }
@@ -331,7 +358,12 @@ class VirtualRenderer(
331
358
  reactSurfaceImpl = ReactSurfaceImpl(context, moduleName, initialProperties)
332
359
  }
333
360
 
361
+ var splashScreenView: View? = null
362
+
334
363
  if (!this@VirtualRenderer::reactSurfaceView.isInitialized) {
364
+ splashScreenView =
365
+ if (isCluster) getClusterSplashScreen(context, height, width) else null
366
+
335
367
  reactSurfaceView = ReactSurfaceView(context, reactSurfaceImpl).apply {
336
368
  layoutParams = FrameLayout.LayoutParams(
337
369
  (width / reactNativeScale).toInt(), (height / reactNativeScale).toInt()
@@ -341,6 +373,10 @@ class VirtualRenderer(
341
373
  pivotX = 0f
342
374
  pivotY = 0f
343
375
  setBackgroundColor(Color.DKGRAY)
376
+
377
+ splashScreenView?.let {
378
+ removeClusterSplashScreen({ viewTreeObserver }, it)
379
+ }
344
380
  }
345
381
 
346
382
  reactSurfaceId = uiManager.startSurface(
@@ -363,17 +399,63 @@ class VirtualRenderer(
363
399
  (reactSurfaceView.parent as ViewGroup).removeView(reactSurfaceView)
364
400
  }
365
401
 
366
- setContentView(FrameLayout(context).apply {
402
+ val rootContainer = FrameLayout(context).apply {
367
403
  layoutParams = FrameLayout.LayoutParams(
368
404
  FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT
369
405
  )
370
406
  clipChildren = false
371
407
 
372
408
  addView(reactSurfaceView)
373
- })
409
+ }
410
+
411
+ splashScreenView?.let {
412
+ rootContainer.addView(it)
413
+ }
414
+
415
+ setContentView(rootContainer)
374
416
  }
375
417
  }
376
418
 
419
+ private fun getClusterSplashScreen(
420
+ context: Context, containerHeight: Int, containerWidth: Int
421
+ ): View {
422
+ val layout =
423
+ LayoutInflater.from(context).inflate(R.layout.cluster_splashscreen, null, false)
424
+ val text = layout.findViewById<TextView>(R.id.splash_text)
425
+
426
+ AppInfo.getApplicationIcon(context)?.let {
427
+ val maxIconSize = minOf(64, (0.25 * maxOf(containerHeight, containerWidth)).toInt())
428
+
429
+ it.setBounds(0, 0, maxIconSize, maxIconSize)
430
+ text.setCompoundDrawables(null, it, null, null)
431
+ }
432
+
433
+ text.text = AppInfo.getApplicationLabel(context)
434
+
435
+ return layout
436
+ }
437
+
438
+ private fun removeClusterSplashScreen(
439
+ getViewTreeObserver: () -> ViewTreeObserver, splashScreenView: View
440
+ ) {
441
+ getViewTreeObserver().addOnGlobalLayoutListener(object :
442
+ ViewTreeObserver.OnGlobalLayoutListener {
443
+ override fun onGlobalLayout() {
444
+ if (splashWillDisappear) {
445
+ return
446
+ }
447
+ splashWillDisappear = true
448
+
449
+ splashScreenView.animate().alpha(0f)
450
+ .setStartDelay(BuildConfig.CLUSTER_SPLASH_DELAY_MS)
451
+ .setDuration(BuildConfig.CLUSTER_SPLASH_DURATION_MS).withEndAction {
452
+ (splashScreenView.parent as? ViewGroup)?.removeView(splashScreenView)
453
+ getViewTreeObserver().removeOnGlobalLayoutListener(this)
454
+ }
455
+ }
456
+ })
457
+ }
458
+
377
459
  companion object {
378
460
  const val TAG = "VirtualRenderer"
379
461
 
@@ -1,5 +1,6 @@
1
1
  package com.margelo.nitro.swe.iternio.reactnativeautoplay.template
2
2
 
3
+ import android.app.Service
3
4
  import android.graphics.Color
4
5
  import androidx.car.app.AppManager
5
6
  import androidx.car.app.CarContext
@@ -20,6 +21,7 @@ import com.facebook.react.bridge.UiThreadUtil
20
21
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.AlertActionStyle
21
22
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.AlertDismissalReason
22
23
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.AndroidAutoScreen
24
+ import com.margelo.nitro.swe.iternio.reactnativeautoplay.AndroidAutoService
23
25
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.AndroidAutoSession
24
26
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.MapTemplateConfig
25
27
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.NitroAction
@@ -262,6 +264,8 @@ class MapTemplate(
262
264
 
263
265
  updateTripDestinations()
264
266
  }
267
+
268
+ AndroidAutoService.instance?.startForeground()
265
269
  }
266
270
 
267
271
  fun updateTravelEstimates(steps: Array<TripPoint>) {
@@ -287,6 +291,8 @@ class MapTemplate(
287
291
  navigationInfo = null
288
292
 
289
293
  AndroidAutoScreen.invalidateSurfaceScreens()
294
+
295
+ AndroidAutoService.instance?.stopForeground(Service.STOP_FOREGROUND_REMOVE)
290
296
  }
291
297
 
292
298
  fun updateManeuvers(maneuvers: NitroManeuver) {
@@ -340,11 +346,29 @@ class MapTemplate(
340
346
  cardBackgroundColor = Parser.parseColor(backgroundColor)
341
347
 
342
348
  val currentStep = Parser.parseStep(context, current)
349
+ val currentDistance = Parser.parseDistance(current.travelEstimates.distanceRemaining)
350
+
343
351
  val nextStep = next?.let { Parser.parseStep(context, it) }
344
352
 
353
+ AndroidAutoService.instance?.let {
354
+ val notificationIcon = Parser.parseImageToBitmap(
355
+ context,
356
+ current.symbolImage.asFirstOrNull(),
357
+ current.symbolImage.asSecondOrNull()
358
+ )
359
+
360
+ val notificationText = currentStep.cue?.toString()
361
+
362
+ val notificationTitle = "${currentDistance.displayDistance.toInt()} ${Parser.parseDistanceUnit(currentDistance.displayUnit)}"
363
+
364
+ it.notify(
365
+ notificationTitle, notificationText, notificationIcon
366
+ )
367
+ }
368
+
345
369
  navigationInfo = RoutingInfo.Builder().apply {
346
370
  setCurrentStep(
347
- currentStep, Parser.parseDistance(current.travelEstimates.distanceRemaining)
371
+ currentStep, currentDistance
348
372
  )
349
373
  nextStep?.let { setNextStep(it) }
350
374
  }.build()
@@ -707,6 +707,17 @@ object Parser {
707
707
  }
708
708
  }
709
709
 
710
+ fun parseDistanceUnit(@Distance.Unit displayUnit: Int): String {
711
+ return when (displayUnit) {
712
+ Distance.UNIT_FEET -> "ft"
713
+ Distance.UNIT_KILOMETERS, Distance.UNIT_KILOMETERS_P1 -> "km"
714
+ Distance.UNIT_METERS -> "m"
715
+ Distance.UNIT_MILES, Distance.UNIT_MILES_P1 -> "mi"
716
+ Distance.UNIT_YARDS -> "yd"
717
+ else -> ""
718
+ }
719
+ }
720
+
710
721
  fun parseMapWithContentConfig(
711
722
  context: CarContext, mapConfig: NitroBaseMapTemplateConfig?, template: Template
712
723
  ): Template {
@@ -0,0 +1,7 @@
1
+ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="128dp" android:viewportHeight="192" android:viewportWidth="192" android:width="128dp">
2
+
3
+ <path android:fillColor="@android:color/transparent" android:pathData="M56,146H26.07c-3.11,0 -5.03,-3.4 -3.43,-6.06l69.93,-116.24c1.55,-2.58 5.3,-2.58 6.86,0l69.93,116.24c1.6,2.67 -0.32,6.06 -3.43,6.06H136" android:strokeColor="@android:color/white" android:strokeLineJoin="round" android:strokeWidth="12"/>
4
+
5
+ <path android:fillColor="@android:color/transparent" android:pathData="m42,170 l54,-92 54,92 -54,-24 -54,24Z" android:strokeColor="@android:color/white" android:strokeLineJoin="round" android:strokeWidth="12"/>
6
+
7
+ </vector>
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:layout_width="match_parent"
4
+ android:layout_height="match_parent"
5
+ android:background="#444444"
6
+ android:gravity="center"
7
+ android:orientation="vertical">
8
+
9
+ <TextView
10
+ android:id="@+id/splash_text"
11
+ android:layout_width="match_parent"
12
+ android:layout_height="wrap_content"
13
+ android:drawableTop="@android:drawable/star_big_off"
14
+ android:drawablePadding="16sp"
15
+ android:gravity="center"
16
+ android:text="react-native-auto-play"
17
+ android:textColor="@android:color/white"
18
+ android:textSize="20sp"
19
+ android:textStyle="bold"
20
+ android:typeface="normal" />
21
+
22
+ </LinearLayout>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iternio/react-native-auto-play",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Android Auto and Apple CarPlay for react-native",
5
5
  "main": "lib/index",
6
6
  "module": "lib/index",