@iternio/react-native-auto-play 0.0.5 → 0.0.7
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.
- package/android/build.gradle +2 -0
- package/android/gradle.properties +3 -1
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AndroidAutoService.kt +77 -0
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridCluster.kt +1 -1
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/VirtualRenderer.kt +88 -11
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/MapTemplate.kt +25 -1
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/Parser.kt +11 -0
- package/android/src/main/res/drawable/ic_notification.xml +7 -0
- package/android/src/main/res/layout/cluster_splashscreen.xml +22 -0
- package/ios/scenes/AutoPlayScene.swift +1 -8
- package/ios/scenes/SceneStore.swift +15 -3
- package/ios/scenes/WindowApplicationSceneDelegate.swift +14 -5
- package/package.json +1 -1
package/android/build.gradle
CHANGED
|
@@ -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
|
}
|
package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridCluster.kt
CHANGED
|
@@ -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()
|
package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/VirtualRenderer.kt
CHANGED
|
@@ -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,
|
|
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
|
|
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.
|
|
74
|
+
reactContext, UIManagerType.FABRIC
|
|
67
75
|
) as FabricUIManager
|
|
68
76
|
}
|
|
69
77
|
|
|
@@ -188,7 +196,7 @@ class VirtualRenderer(
|
|
|
188
196
|
val left = floor((visibleArea.left + additionalMarginLeft) / scale).toDouble()
|
|
189
197
|
val right =
|
|
190
198
|
floor((width - visibleArea.right + additionalMarginRight) / scale).toDouble()
|
|
191
|
-
HybridAutoPlay.
|
|
199
|
+
HybridAutoPlay.emitSafeAreaInsets(
|
|
192
200
|
moduleName = moduleName,
|
|
193
201
|
top = top,
|
|
194
202
|
bottom = bottom,
|
|
@@ -213,7 +221,7 @@ class VirtualRenderer(
|
|
|
213
221
|
defaultMargin
|
|
214
222
|
) / scale
|
|
215
223
|
).toDouble()
|
|
216
|
-
HybridAutoPlay.
|
|
224
|
+
HybridAutoPlay.emitSafeAreaInsets(
|
|
217
225
|
moduleName = moduleName,
|
|
218
226
|
top = top,
|
|
219
227
|
bottom = bottom,
|
|
@@ -227,9 +235,9 @@ class VirtualRenderer(
|
|
|
227
235
|
}
|
|
228
236
|
|
|
229
237
|
private fun getMapTemplateConfig(): MapTemplateConfig? {
|
|
230
|
-
val screenManager = AndroidAutoScreen.
|
|
238
|
+
val screenManager = AndroidAutoScreen.getScreen(moduleName)?.screenManager ?: return null
|
|
231
239
|
val marker = screenManager.top.marker ?: return null
|
|
232
|
-
return AndroidAutoTemplate.
|
|
240
|
+
return AndroidAutoTemplate.getConfig(marker) as MapTemplateConfig?
|
|
233
241
|
}
|
|
234
242
|
|
|
235
243
|
private fun initRenderer() {
|
|
@@ -283,12 +291,18 @@ class VirtualRenderer(
|
|
|
283
291
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
284
292
|
super.onCreate(savedInstanceState)
|
|
285
293
|
|
|
294
|
+
var splashScreenView: View? = null
|
|
295
|
+
|
|
286
296
|
if (!this@VirtualRenderer::reactRootView.isInitialized) {
|
|
297
|
+
splashScreenView =
|
|
298
|
+
if (isCluster) getClusterSplashScreen(context, height, width) else null
|
|
299
|
+
|
|
287
300
|
val instanceManager =
|
|
288
301
|
(context.applicationContext as ReactApplication).reactNativeHost.reactInstanceManager
|
|
289
302
|
reactRootView = ReactRootView(context.applicationContext).apply {
|
|
290
303
|
layoutParams = FrameLayout.LayoutParams(
|
|
291
|
-
(this@MapPresentation.width / reactNativeScale).toInt(),
|
|
304
|
+
(this@MapPresentation.width / reactNativeScale).toInt(),
|
|
305
|
+
(this@MapPresentation.height / reactNativeScale).toInt()
|
|
292
306
|
)
|
|
293
307
|
scaleX = reactNativeScale
|
|
294
308
|
scaleY = reactNativeScale
|
|
@@ -296,6 +310,10 @@ class VirtualRenderer(
|
|
|
296
310
|
pivotY = 0f
|
|
297
311
|
setBackgroundColor(Color.DKGRAY)
|
|
298
312
|
|
|
313
|
+
splashScreenView?.let {
|
|
314
|
+
removeClusterSplashScreen({ viewTreeObserver }, it)
|
|
315
|
+
}
|
|
316
|
+
|
|
299
317
|
startReactApplication(instanceManager, moduleName, initialProperties)
|
|
300
318
|
runApplication()
|
|
301
319
|
}
|
|
@@ -308,9 +326,13 @@ class VirtualRenderer(
|
|
|
308
326
|
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT
|
|
309
327
|
)
|
|
310
328
|
clipChildren = false
|
|
329
|
+
|
|
330
|
+
addView(reactRootView)
|
|
311
331
|
}
|
|
312
332
|
|
|
313
|
-
|
|
333
|
+
splashScreenView?.let {
|
|
334
|
+
rootContainer.addView(it)
|
|
335
|
+
}
|
|
314
336
|
|
|
315
337
|
setContentView(rootContainer)
|
|
316
338
|
}
|
|
@@ -331,7 +353,12 @@ class VirtualRenderer(
|
|
|
331
353
|
reactSurfaceImpl = ReactSurfaceImpl(context, moduleName, initialProperties)
|
|
332
354
|
}
|
|
333
355
|
|
|
356
|
+
var splashScreenView: View? = null
|
|
357
|
+
|
|
334
358
|
if (!this@VirtualRenderer::reactSurfaceView.isInitialized) {
|
|
359
|
+
splashScreenView =
|
|
360
|
+
if (isCluster) getClusterSplashScreen(context, height, width) else null
|
|
361
|
+
|
|
335
362
|
reactSurfaceView = ReactSurfaceView(context, reactSurfaceImpl).apply {
|
|
336
363
|
layoutParams = FrameLayout.LayoutParams(
|
|
337
364
|
(width / reactNativeScale).toInt(), (height / reactNativeScale).toInt()
|
|
@@ -341,6 +368,10 @@ class VirtualRenderer(
|
|
|
341
368
|
pivotX = 0f
|
|
342
369
|
pivotY = 0f
|
|
343
370
|
setBackgroundColor(Color.DKGRAY)
|
|
371
|
+
|
|
372
|
+
splashScreenView?.let {
|
|
373
|
+
removeClusterSplashScreen({ viewTreeObserver }, it)
|
|
374
|
+
}
|
|
344
375
|
}
|
|
345
376
|
|
|
346
377
|
reactSurfaceId = uiManager.startSurface(
|
|
@@ -363,15 +394,61 @@ class VirtualRenderer(
|
|
|
363
394
|
(reactSurfaceView.parent as ViewGroup).removeView(reactSurfaceView)
|
|
364
395
|
}
|
|
365
396
|
|
|
366
|
-
|
|
397
|
+
val rootContainer = FrameLayout(context).apply {
|
|
367
398
|
layoutParams = FrameLayout.LayoutParams(
|
|
368
399
|
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT
|
|
369
400
|
)
|
|
370
401
|
clipChildren = false
|
|
371
402
|
|
|
372
403
|
addView(reactSurfaceView)
|
|
373
|
-
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
splashScreenView?.let {
|
|
407
|
+
rootContainer.addView(it)
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
setContentView(rootContainer)
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
private fun getClusterSplashScreen(
|
|
415
|
+
context: Context, containerHeight: Int, containerWidth: Int
|
|
416
|
+
): View {
|
|
417
|
+
val layout =
|
|
418
|
+
LayoutInflater.from(context).inflate(R.layout.cluster_splashscreen, null, false)
|
|
419
|
+
val text = layout.findViewById<TextView>(R.id.splash_text)
|
|
420
|
+
|
|
421
|
+
AppInfo.getApplicationIcon(context)?.let {
|
|
422
|
+
val maxIconSize = minOf(64, (0.25 * maxOf(containerHeight, containerWidth)).toInt())
|
|
423
|
+
|
|
424
|
+
it.setBounds(0, 0, maxIconSize, maxIconSize)
|
|
425
|
+
text.setCompoundDrawables(null, it, null, null)
|
|
374
426
|
}
|
|
427
|
+
|
|
428
|
+
text.text = AppInfo.getApplicationLabel(context)
|
|
429
|
+
|
|
430
|
+
return layout
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private fun removeClusterSplashScreen(
|
|
434
|
+
getViewTreeObserver: () -> ViewTreeObserver, splashScreenView: View
|
|
435
|
+
) {
|
|
436
|
+
getViewTreeObserver().addOnGlobalLayoutListener(object :
|
|
437
|
+
ViewTreeObserver.OnGlobalLayoutListener {
|
|
438
|
+
override fun onGlobalLayout() {
|
|
439
|
+
if (splashWillDisappear) {
|
|
440
|
+
return
|
|
441
|
+
}
|
|
442
|
+
splashWillDisappear = true
|
|
443
|
+
|
|
444
|
+
splashScreenView.animate().alpha(0f)
|
|
445
|
+
.setStartDelay(BuildConfig.CLUSTER_SPLASH_DELAY_MS)
|
|
446
|
+
.setDuration(BuildConfig.CLUSTER_SPLASH_DURATION_MS).withEndAction {
|
|
447
|
+
(splashScreenView.parent as? ViewGroup)?.removeView(splashScreenView)
|
|
448
|
+
getViewTreeObserver().removeOnGlobalLayoutListener(this)
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
})
|
|
375
452
|
}
|
|
376
453
|
|
|
377
454
|
companion object {
|
|
@@ -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,
|
|
371
|
+
currentStep, currentDistance
|
|
348
372
|
)
|
|
349
373
|
nextStep?.let { setNextStep(it) }
|
|
350
374
|
}.build()
|
package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/Parser.kt
CHANGED
|
@@ -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>
|
|
@@ -12,8 +12,6 @@ import CarPlay
|
|
|
12
12
|
#endif
|
|
13
13
|
|
|
14
14
|
class AutoPlayScene: UIResponder {
|
|
15
|
-
public private(set) var state: VisibilityState = .diddisappear
|
|
16
|
-
|
|
17
15
|
var initialProperties: [String: Any] = [:]
|
|
18
16
|
var moduleName: String?
|
|
19
17
|
var window: UIWindow?
|
|
@@ -75,16 +73,11 @@ class AutoPlayScene: UIResponder {
|
|
|
75
73
|
}
|
|
76
74
|
|
|
77
75
|
func setState(state: VisibilityState) {
|
|
78
|
-
self.state = state
|
|
79
|
-
|
|
80
76
|
guard let moduleName = self.moduleName else {
|
|
81
77
|
return
|
|
82
78
|
}
|
|
83
79
|
|
|
84
|
-
|
|
85
|
-
moduleName: moduleName,
|
|
86
|
-
state: state
|
|
87
|
-
)
|
|
80
|
+
SceneStore.setState(moduleName: moduleName, state: state)
|
|
88
81
|
}
|
|
89
82
|
|
|
90
83
|
func initRootView() {
|
|
@@ -10,6 +10,9 @@ import CarPlay
|
|
|
10
10
|
class SceneStore {
|
|
11
11
|
static let rootModuleName = "AutoPlayRoot"
|
|
12
12
|
static let dashboardModuleName = "CarPlayDashboard"
|
|
13
|
+
static let windowSceneModuleName = "main"
|
|
14
|
+
|
|
15
|
+
private static var renderState = [String: VisibilityState]()
|
|
13
16
|
|
|
14
17
|
private static var store: [String: AutoPlayScene] = [:]
|
|
15
18
|
|
|
@@ -34,7 +37,16 @@ class SceneStore {
|
|
|
34
37
|
}
|
|
35
38
|
|
|
36
39
|
static func getState(moduleName: String) -> VisibilityState? {
|
|
37
|
-
return
|
|
40
|
+
return renderState[moduleName]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static func setState(moduleName: String, state: VisibilityState) {
|
|
44
|
+
renderState[moduleName] = state
|
|
45
|
+
|
|
46
|
+
HybridAutoPlay.emitRenderState(
|
|
47
|
+
moduleName: moduleName,
|
|
48
|
+
state: state
|
|
49
|
+
)
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
static func getDashboardScene() throws -> DashboardSceneDelegate? {
|
|
@@ -67,11 +79,11 @@ class SceneStore {
|
|
|
67
79
|
|
|
68
80
|
return scene as? ClusterSceneDelegate
|
|
69
81
|
}
|
|
70
|
-
|
|
82
|
+
|
|
71
83
|
static func getRootScene() -> AutoPlayScene? {
|
|
72
84
|
return store[SceneStore.rootModuleName]
|
|
73
85
|
}
|
|
74
|
-
|
|
86
|
+
|
|
75
87
|
static func getRootTraitCollection() -> UITraitCollection {
|
|
76
88
|
return store[SceneStore.rootModuleName]!.traitCollection
|
|
77
89
|
}
|
|
@@ -33,21 +33,30 @@ class WindowApplicationSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
func sceneDidBecomeActive(_ scene: UIScene) {
|
|
36
|
-
|
|
36
|
+
SceneStore.setState(
|
|
37
|
+
moduleName: SceneStore.windowSceneModuleName,
|
|
38
|
+
state: .didappear
|
|
39
|
+
)
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
func sceneWillResignActive(_ scene: UIScene) {
|
|
40
|
-
|
|
41
|
-
moduleName:
|
|
43
|
+
SceneStore.setState(
|
|
44
|
+
moduleName: SceneStore.windowSceneModuleName,
|
|
42
45
|
state: .willdisappear
|
|
43
46
|
)
|
|
44
47
|
}
|
|
45
48
|
|
|
46
49
|
func sceneWillEnterForeground(_ scene: UIScene) {
|
|
47
|
-
|
|
50
|
+
SceneStore.setState(
|
|
51
|
+
moduleName: SceneStore.windowSceneModuleName,
|
|
52
|
+
state: .willappear
|
|
53
|
+
)
|
|
48
54
|
}
|
|
49
55
|
|
|
50
56
|
func sceneDidEnterBackground(_ scene: UIScene) {
|
|
51
|
-
|
|
57
|
+
SceneStore.setState(
|
|
58
|
+
moduleName: SceneStore.windowSceneModuleName,
|
|
59
|
+
state: .diddisappear
|
|
60
|
+
)
|
|
52
61
|
}
|
|
53
62
|
}
|