@iternio/react-native-auto-play 0.2.0-alpha.4 → 0.2.0-alpha.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.
Files changed (54) hide show
  1. package/README.md +1 -1
  2. package/android/src/auto/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAndroidAutomotive.kt +25 -0
  3. package/android/src/automotive/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AndroidTelemetryObserver.kt +2 -5
  4. package/android/src/automotive/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAndroidAutomotive.kt +185 -0
  5. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/SignInWithGoogleActivity.kt +14 -20
  6. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/AutomotivePermissionRequestTemplate.kt +3 -3
  7. package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/SignInTemplate.kt +15 -12
  8. package/android/src/main/res/drawable/google.xml +18 -0
  9. package/lib/hooks/useAndroidAutoTelemetry.d.ts +13 -4
  10. package/lib/hooks/useAndroidAutoTelemetry.js +6 -3
  11. package/lib/hooks/useIsAutoPlayFocused.d.ts +7 -0
  12. package/lib/hooks/useIsAutoPlayFocused.js +20 -0
  13. package/lib/hybrid.d.ts +2 -0
  14. package/lib/hybrid.js +2 -0
  15. package/lib/index.d.ts +4 -0
  16. package/lib/index.js +4 -0
  17. package/lib/specs/AndroidAutomotive.nitro.d.ts +33 -0
  18. package/lib/specs/AndroidAutomotive.nitro.js +13 -0
  19. package/lib/specs/AutomotivePermissionRequestTemplate.d.ts +11 -0
  20. package/lib/specs/AutomotivePermissionRequestTemplate.js +1 -0
  21. package/lib/specs/AutomotivePermissionRequestTemplate.nitro.d.ts +11 -0
  22. package/lib/specs/AutomotivePermissionRequestTemplate.nitro.js +1 -0
  23. package/lib/templates/AutomotivePermissionRequestTemplate.d.ts +23 -0
  24. package/lib/templates/AutomotivePermissionRequestTemplate.js +18 -0
  25. package/lib/types/SignInMethod.d.ts +1 -0
  26. package/nitro.json +3 -0
  27. package/nitrogen/generated/android/ReactNativeAutoPlay+autolinking.cmake +2 -0
  28. package/nitrogen/generated/android/ReactNativeAutoPlayOnLoad.cpp +16 -2
  29. package/nitrogen/generated/android/c++/JActiveCarUxRestrictions.hpp +89 -0
  30. package/nitrogen/generated/android/c++/JAppFocusState.hpp +61 -0
  31. package/nitrogen/generated/android/c++/JCarUxRestrictions.hpp +82 -0
  32. package/nitrogen/generated/android/c++/JFunc_void_ActiveCarUxRestrictions.hpp +80 -0
  33. package/nitrogen/generated/android/c++/JFunc_void_AppFocusState.hpp +78 -0
  34. package/nitrogen/generated/android/c++/JGoogleSignIn.hpp +7 -3
  35. package/nitrogen/generated/android/c++/JHybridAndroidAutomotiveSpec.cpp +120 -0
  36. package/nitrogen/generated/android/c++/JHybridAndroidAutomotiveSpec.hpp +70 -0
  37. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/ActiveCarUxRestrictions.kt +47 -0
  38. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/AppFocusState.kt +41 -0
  39. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/CarUxRestrictions.kt +31 -0
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_ActiveCarUxRestrictions.kt +80 -0
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_AppFocusState.kt +80 -0
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/GoogleSignIn.kt +8 -5
  43. package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAndroidAutomotiveSpec.kt +88 -0
  44. package/nitrogen/generated/shared/c++/ActiveCarUxRestrictions.hpp +97 -0
  45. package/nitrogen/generated/shared/c++/AppFocusState.hpp +87 -0
  46. package/nitrogen/generated/shared/c++/CarUxRestrictions.hpp +81 -0
  47. package/nitrogen/generated/shared/c++/GoogleSignIn.hpp +6 -2
  48. package/nitrogen/generated/shared/c++/HybridAndroidAutomotiveSpec.cpp +25 -0
  49. package/nitrogen/generated/shared/c++/HybridAndroidAutomotiveSpec.hpp +71 -0
  50. package/package.json +1 -1
  51. package/src/hooks/useAndroidAutoTelemetry.ts +48 -32
  52. package/src/index.ts +11 -0
  53. package/src/specs/AndroidAutomotive.nitro.ts +37 -0
  54. package/src/types/SignInMethod.ts +1 -0
package/README.md CHANGED
@@ -896,7 +896,7 @@ See [this issue](https://github.com/mrousavy/nitro/issues/382) for details.
896
896
 
897
897
  ## Contributing
898
898
 
899
- Contributions are welcome! Please submit a pull request.
899
+ Contributions are welcome! Feel free to open up a [discussion](https://github.com/Iternio-Planning-AB/react-native-auto-play/discussions) or submit a pull request.
900
900
 
901
901
  ## License
902
902
 
@@ -0,0 +1,25 @@
1
+ package com.margelo.nitro.swe.iternio.reactnativeautoplay
2
+
3
+ class InvalidPlatformException(message: String) : Exception(message)
4
+
5
+ class HybridAndroidAutomotive : HybridAndroidAutomotiveSpec() {
6
+ override fun registerCarUxRestrictionsListener(callback: (restrictions: ActiveCarUxRestrictions) -> Unit): () -> Unit {
7
+ throw InvalidPlatformException("registerCarUxRestrictionsListener is supported on Android Automotive only!")
8
+ }
9
+
10
+ override fun getCarUxRestrictions(): ActiveCarUxRestrictions {
11
+ throw InvalidPlatformException("getCarUxRestrictions is supported on Android Automotive only!")
12
+ }
13
+
14
+ override fun registerAppFocusListener(callback: (state: AppFocusState) -> Unit): () -> Unit {
15
+ throw InvalidPlatformException("registerAppFocusListener is supported on Android Automotive only!")
16
+ }
17
+
18
+ override fun getAppFocusState(): AppFocusState {
19
+ throw InvalidPlatformException("getAppFocusState is supported on Android Automotive only!")
20
+ }
21
+
22
+ override fun requestAppFocus(): () -> Unit {
23
+ throw InvalidPlatformException("requestAppFocus is supported on Android Automotive only!")
24
+ }
25
+ }
@@ -7,6 +7,7 @@ import android.car.VehiclePropertyIds
7
7
  import android.car.hardware.CarPropertyValue
8
8
  import android.car.hardware.property.CarPropertyManager
9
9
  import android.util.Log
10
+ import com.margelo.nitro.NitroModules
10
11
  import kotlin.math.floor
11
12
 
12
13
  private val REQUIRED_VEHICLE_PROPERTY_IDS = listOf(
@@ -156,14 +157,10 @@ object AndroidTelemetryObserver : TelemetryObserver() {
156
157
  return false
157
158
  }
158
159
 
159
- val carContext = AndroidAutoSession.getRootContext() ?: throw IllegalArgumentException(
160
- "Car context not available, failed to start telemetry"
161
- )
162
-
163
160
  // create new instance so we can access all props after permissions were granted
164
161
  mCar?.disconnect()
165
162
 
166
- val car = Car.createCar(carContext)
163
+ val car = Car.createCar(NitroModules.applicationContext)
167
164
  mCar = car
168
165
 
169
166
  fetchStaticData(car)
@@ -0,0 +1,185 @@
1
+ package com.margelo.nitro.swe.iternio.reactnativeautoplay
2
+
3
+ import android.car.Car
4
+ import android.car.CarAppFocusManager
5
+ import android.car.CarAppFocusManager.OnAppFocusChangedListener
6
+ import android.car.CarAppFocusManager.OnAppFocusOwnershipCallback
7
+ import android.car.drivingstate.CarUxRestrictionsManager
8
+ import com.margelo.nitro.NitroModules
9
+ import java.util.concurrent.CopyOnWriteArrayList
10
+ import java.util.concurrent.atomic.AtomicReference
11
+
12
+ class HybridAndroidAutomotive : HybridAndroidAutomotiveSpec() {
13
+ private val car = Car.createCar(NitroModules.applicationContext)
14
+
15
+ private val onAppFocusOwnershipCallback = object : OnAppFocusOwnershipCallback {
16
+ override fun onAppFocusOwnershipGranted(appType: Int) {
17
+ isFocusOwned.set(true)
18
+ notifyAppFocusListeners()
19
+ }
20
+
21
+ override fun onAppFocusOwnershipLost(appType: Int) {
22
+ isFocusOwned.set(false)
23
+ notifyAppFocusListeners()
24
+ }
25
+ }
26
+
27
+ private val onAppFocusChangedListener = OnAppFocusChangedListener { appType, active ->
28
+ isFocusActive.set(active)
29
+
30
+ val carAppFocusManager = car.getCarManager(Car.APP_FOCUS_SERVICE) as CarAppFocusManager
31
+ // docs say we should not get an event when we receive our own app focus
32
+ // but we still do get it. so we do a check here again if we own it or not
33
+ val isOwned = carAppFocusManager.isOwningFocus(
34
+ onAppFocusOwnershipCallback, appType
35
+ )
36
+ isFocusOwned.set(isOwned)
37
+
38
+ notifyAppFocusListeners()
39
+ }
40
+
41
+ override fun registerCarUxRestrictionsListener(callback: (restrictions: ActiveCarUxRestrictions) -> Unit): () -> Unit {
42
+ if (uxRestrictionListeners.isEmpty()) {
43
+ val uxRestrictionManager =
44
+ car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager
45
+
46
+ uxRestrictionManager.registerListener {
47
+ notifyUxRestrictionListeners(it.toActiveCarUxRestrictions())
48
+ }
49
+ }
50
+
51
+ uxRestrictionListeners.add(callback)
52
+
53
+ return {
54
+ uxRestrictionListeners.remove(callback)
55
+
56
+ if (uxRestrictionListeners.isEmpty()) {
57
+ val uxRestrictionManager =
58
+ car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager
59
+
60
+ uxRestrictionManager.unregisterListener()
61
+ }
62
+ }
63
+ }
64
+
65
+ override fun getCarUxRestrictions(): ActiveCarUxRestrictions {
66
+ val uxRestrictionManager =
67
+ car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager
68
+
69
+ return uxRestrictionManager.currentCarUxRestrictions.toActiveCarUxRestrictions()
70
+ }
71
+
72
+ override fun registerAppFocusListener(callback: (state: AppFocusState) -> Unit): () -> Unit {
73
+ if (appFocusListeners.isEmpty()) {
74
+ val carAppFocusManager = car.getCarManager(Car.APP_FOCUS_SERVICE) as CarAppFocusManager
75
+
76
+ carAppFocusManager.addFocusListener(
77
+ onAppFocusChangedListener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION
78
+ )
79
+ }
80
+
81
+ appFocusListeners.add(callback)
82
+
83
+ return {
84
+ appFocusListeners.remove(callback)
85
+
86
+ if (appFocusListeners.isEmpty()) {
87
+ val carAppFocusManager =
88
+ car.getCarManager(Car.APP_FOCUS_SERVICE) as CarAppFocusManager
89
+
90
+ carAppFocusManager.removeFocusListener(
91
+ onAppFocusChangedListener, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION
92
+ )
93
+ }
94
+ }
95
+ }
96
+
97
+ override fun getAppFocusState(): AppFocusState {
98
+ val carAppFocusManager = car.getCarManager(Car.APP_FOCUS_SERVICE) as CarAppFocusManager
99
+ val isOwned = carAppFocusManager.isOwningFocus(
100
+ onAppFocusOwnershipCallback, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION
101
+ )
102
+
103
+ isFocusOwned.set(isOwned)
104
+
105
+ return AppFocusState(isOwned, isFocusActive.get())
106
+ }
107
+
108
+ override fun requestAppFocus(): () -> Unit {
109
+ val carAppFocusManager = car.getCarManager(Car.APP_FOCUS_SERVICE) as CarAppFocusManager
110
+ carAppFocusManager.requestAppFocus(
111
+ CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION, onAppFocusOwnershipCallback
112
+ )
113
+
114
+ return {
115
+ carAppFocusManager.abandonAppFocus(
116
+ onAppFocusOwnershipCallback, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION
117
+ )
118
+ }
119
+ }
120
+
121
+ companion object {
122
+ private var isFocusOwned = AtomicReference<Boolean?>(null)
123
+ private var isFocusActive = AtomicReference<Boolean?>(null)
124
+
125
+ private val uxRestrictionListeners =
126
+ CopyOnWriteArrayList<(ActiveCarUxRestrictions) -> Unit>()
127
+ private val appFocusListeners = CopyOnWriteArrayList<(AppFocusState) -> Unit>()
128
+
129
+ private fun notifyAppFocusListeners() {
130
+ val state = AppFocusState(isFocusOwned.get(), isFocusActive.get())
131
+
132
+ appFocusListeners.forEach {
133
+ it(state)
134
+ }
135
+ }
136
+
137
+ private fun notifyUxRestrictionListeners(restrictions: ActiveCarUxRestrictions) {
138
+ uxRestrictionListeners.forEach {
139
+ it(restrictions)
140
+ }
141
+ }
142
+ }
143
+
144
+ fun android.car.drivingstate.CarUxRestrictions.toActiveCarUxRestrictions(): ActiveCarUxRestrictions {
145
+ val restrictions = mutableListOf<CarUxRestrictions>()
146
+
147
+ if ((activeRestrictions and android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_DIALPAD) != 0) {
148
+ restrictions.add(CarUxRestrictions.UX_RESTRICTIONS_NO_DIALPAD)
149
+ }
150
+ if ((activeRestrictions and android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_FILTERING) != 0) {
151
+ restrictions.add(CarUxRestrictions.UX_RESTRICTIONS_NO_FILTERING)
152
+ }
153
+ if ((activeRestrictions and android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_LIMIT_STRING_LENGTH) != 0) {
154
+ restrictions.add(CarUxRestrictions.UX_RESTRICTIONS_LIMIT_STRING_LENGTH)
155
+ }
156
+ if ((activeRestrictions and android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_KEYBOARD) != 0) {
157
+ restrictions.add(CarUxRestrictions.UX_RESTRICTIONS_NO_KEYBOARD)
158
+ }
159
+ if ((activeRestrictions and android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO) != 0) {
160
+ restrictions.add(CarUxRestrictions.UX_RESTRICTIONS_NO_VIDEO)
161
+ }
162
+ if ((activeRestrictions and android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_LIMIT_CONTENT) != 0) {
163
+ restrictions.add(CarUxRestrictions.UX_RESTRICTIONS_LIMIT_CONTENT)
164
+ }
165
+ if ((activeRestrictions and android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP) != 0) {
166
+ restrictions.add(CarUxRestrictions.UX_RESTRICTIONS_NO_SETUP)
167
+ }
168
+ if ((activeRestrictions and android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_TEXT_MESSAGE) != 0) {
169
+ restrictions.add(CarUxRestrictions.UX_RESTRICTIONS_NO_TEXT_MESSAGE)
170
+ }
171
+ if ((activeRestrictions and android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION) != 0) {
172
+ restrictions.add(CarUxRestrictions.UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION)
173
+ }
174
+ if (activeRestrictions == android.car.drivingstate.CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED) {
175
+ restrictions.add(CarUxRestrictions.UX_RESTRICTIONS_FULLY_RESTRICTED)
176
+ }
177
+
178
+ return ActiveCarUxRestrictions(
179
+ maxContentDepth = maxContentDepth.toDouble(),
180
+ maxCumulativeContentItems = maxCumulativeContentItems.toDouble(),
181
+ maxRestrictedStringLength = maxRestrictedStringLength.toDouble(),
182
+ activeRestrictions = restrictions.toTypedArray()
183
+ )
184
+ }
185
+ }
@@ -1,6 +1,5 @@
1
1
  package com.margelo.nitro.swe.iternio.reactnativeautoplay
2
2
 
3
- import android.content.Intent
4
3
  import android.os.Binder
5
4
  import android.os.Bundle
6
5
  import android.os.IBinder
@@ -17,29 +16,25 @@ class SignInWithGoogleActivity : ComponentActivity() {
17
16
  override fun onCreate(savedInstanceState: Bundle?) {
18
17
  super.onCreate(savedInstanceState)
19
18
 
20
- val signInCompleteCallback =
21
- intent.extras!!.getBinder(BINDER_KEY) as OnSignInComplete?
19
+ val signInCompleteCallback = intent.extras?.getBinder(BINDER_KEY) as OnSignInComplete?
22
20
 
23
21
  val serverClientId = intent.extras?.getString("serverClientId")
24
22
  ?: throw InvalidParameterException("missing serverClientId parameter")
25
23
 
26
- val activityResultLauncher =
27
- registerForActivityResult<Intent, ActivityResult>(
28
- ActivityResultContracts.StartActivityForResult()
29
- ) { result: ActivityResult ->
30
- val account =
31
- GoogleSignIn.getSignedInAccountFromIntent(
32
- result.data
33
- ).result
34
- signInCompleteCallback!!.onSignInComplete(account)
35
- finish()
36
- }
24
+ val activityResultLauncher = registerForActivityResult(
25
+ ActivityResultContracts.StartActivityForResult()
26
+ ) { result: ActivityResult ->
27
+ val account = GoogleSignIn.getSignedInAccountFromIntent(
28
+ result.data
29
+ ).result
30
+ signInCompleteCallback?.onSignInComplete(account)
31
+ finish()
32
+ }
37
33
 
38
34
  val googleSignInClient = GoogleSignIn.getClient(
39
- this, GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
40
- .requestServerAuthCode(serverClientId)
41
- .requestEmail()
42
- .build()
35
+ this,
36
+ GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
37
+ .requestServerAuthCode(serverClientId).requestEmail().build()
43
38
  )
44
39
  activityResultLauncher.launch(googleSignInClient.signInIntent)
45
40
  }
@@ -47,8 +42,7 @@ class SignInWithGoogleActivity : ComponentActivity() {
47
42
  /**
48
43
  * Binder callback to provide to the sign in activity.
49
44
  */
50
- abstract
51
- class OnSignInComplete : Binder(), IBinder {
45
+ abstract class OnSignInComplete : Binder(), IBinder {
52
46
  /**
53
47
  * Notifies that sign in flow completed.
54
48
  *
@@ -41,7 +41,7 @@ class AutomotivePermissionRequestTemplate(
41
41
 
42
42
  if (granted) {
43
43
  setResult(PermissionRequestResult(permissions, arrayOf()))
44
- screenManager.pop()
44
+ finish()
45
45
  return
46
46
  }
47
47
 
@@ -85,7 +85,7 @@ class AutomotivePermissionRequestTemplate(
85
85
  granted.toTypedArray(), denied.toTypedArray()
86
86
  )
87
87
  )
88
- screenManager.pop()
88
+ finish()
89
89
  })
90
90
  })
91
91
  cancelButtonText?.let {
@@ -93,7 +93,7 @@ class AutomotivePermissionRequestTemplate(
93
93
  setTitle(it)
94
94
  setOnClickListener(ParkedOnlyOnClickListener.create {
95
95
  setResult(PermissionRequestResult(arrayOf(), permissions))
96
- screenManager.pop()
96
+ finish()
97
97
  })
98
98
  }.build())
99
99
  }
@@ -3,11 +3,10 @@ package com.margelo.nitro.swe.iternio.reactnativeautoplay.template
3
3
  import android.content.Intent
4
4
  import android.net.Uri
5
5
  import android.os.Bundle
6
- import androidx.annotation.Nullable
7
6
  import androidx.car.app.CarContext
8
- import androidx.car.app.CarToast
9
7
  import androidx.car.app.model.Action
10
8
  import androidx.car.app.model.ActionStrip
9
+ import androidx.car.app.model.CarIcon
11
10
  import androidx.car.app.model.InputCallback
12
11
  import androidx.car.app.model.ParkedOnlyOnClickListener
13
12
  import androidx.car.app.model.Template
@@ -18,14 +17,17 @@ import androidx.car.app.model.signin.PinSignInMethod
18
17
  import androidx.car.app.model.signin.ProviderSignInMethod
19
18
  import androidx.car.app.model.signin.QRCodeSignInMethod
20
19
  import androidx.car.app.model.signin.SignInTemplate
20
+ import androidx.core.graphics.drawable.IconCompat
21
21
  import com.google.android.gms.auth.api.signin.GoogleSignInAccount
22
22
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.KeyboardType
23
23
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.NitroAction
24
24
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.NitroActionType
25
+ import com.margelo.nitro.swe.iternio.reactnativeautoplay.R
25
26
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.SignInTemplateConfig
26
27
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.SignInWithGoogleActivity
27
28
  import com.margelo.nitro.swe.iternio.reactnativeautoplay.TextInputType
28
29
  import java.security.InvalidParameterException
30
+ import androidx.core.net.toUri
29
31
 
30
32
  class SignInTemplate(
31
33
  context: CarContext, config: SignInTemplateConfig
@@ -69,13 +71,11 @@ class SignInTemplate(
69
71
  val templateBuilder = when {
70
72
  qrSignIn != null -> {
71
73
  val url = qrSignIn.url
72
- ?: throw InvalidParameterException("missing url parameter")
73
- SignInTemplate.Builder(QRCodeSignInMethod(Uri.parse(url)))
74
+ SignInTemplate.Builder(QRCodeSignInMethod(url.toUri()))
74
75
  }
75
76
 
76
77
  pinSignIn != null -> {
77
78
  val pin = pinSignIn.pin
78
- ?: throw InvalidParameterException("missing pin parameter")
79
79
  SignInTemplate.Builder(PinSignInMethod(pin))
80
80
  }
81
81
 
@@ -106,18 +106,21 @@ class SignInTemplate(
106
106
 
107
107
  googleSignIn != null -> {
108
108
  val signInAction = Action.Builder().apply {
109
+ setTitle(googleSignIn.signInButtonText)
110
+ setIcon(
111
+ CarIcon.Builder(
112
+ IconCompat.createWithResource(
113
+ context, R.drawable.google
114
+ )
115
+ ).build()
116
+ )
109
117
  setOnClickListener(ParkedOnlyOnClickListener.create {
110
118
  val extras = Bundle(1)
111
119
  extras.putBinder(
112
120
  SignInWithGoogleActivity.BINDER_KEY,
113
121
  object : SignInWithGoogleActivity.OnSignInComplete() {
114
- override fun onSignInComplete(@Nullable account: GoogleSignInAccount?) {
122
+ override fun onSignInComplete(account: GoogleSignInAccount?) {
115
123
  if (account == null) {
116
- CarToast.makeText(
117
- context,
118
- "Error signing in",
119
- CarToast.LENGTH_LONG
120
- ).show()
121
124
  googleSignIn.callback("Error signing in", null)
122
125
  } else {
123
126
  googleSignIn.callback(
@@ -127,7 +130,7 @@ class SignInTemplate(
127
130
  email = account.email,
128
131
  id = account.id,
129
132
  displayName = account.displayName,
130
- photoUrl = account.photoUrl.toString(),
133
+ photoUrl = account.photoUrl?.toString(),
131
134
  idToken = account.idToken,
132
135
  givenName = account.givenName,
133
136
  familyName = account.familyName
@@ -0,0 +1,18 @@
1
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+ android:width="24dp"
3
+ android:height="24dp"
4
+ android:viewportWidth="24"
5
+ android:viewportHeight="24">
6
+ <path
7
+ android:pathData="M22.56,12.25c0,-0.78 -0.07,-1.53 -0.2,-2.25H12v4.26h5.92c-0.26,1.37 -1.04,2.53 -2.21,3.31v2.77h3.57c2.08,-1.92 3.28,-4.74 3.28,-8.09z"
8
+ android:fillColor="#4285F4"/>
9
+ <path
10
+ android:pathData="M12,23c2.97,0 5.46,-0.98 7.28,-2.66l-3.57,-2.77c-0.98,0.66 -2.23,1.06 -3.71,1.06 -2.86,0 -5.29,-1.93 -6.16,-4.53H2.18v2.84C3.99,20.53 7.7,23 12,23z"
11
+ android:fillColor="#34A853"/>
12
+ <path
13
+ android:pathData="M5.84,14.09c-0.22,-0.66 -0.35,-1.36 -0.35,-2.09s0.13,-1.43 0.35,-2.09V7.07H2.18C1.43,8.55 1,10.22 1,12s0.43,3.45 1.18,4.93l2.85,-2.22 0.81,-0.62z"
14
+ android:fillColor="#FBBC05"/>
15
+ <path
16
+ android:pathData="M12,5.38c1.62,0 3.06,0.56 4.21,1.64l3.15,-3.15C17.45,2.09 14.97,1 12,1 7.7,1 3.99,3.47 2.18,7.07l3.66,2.84c0.87,-2.6 3.3,-4.53 6.16,-4.53z"
17
+ android:fillColor="#EA4335"/>
18
+ </vector>
@@ -1,11 +1,11 @@
1
1
  import type { AndroidAutoPermissions, Telemetry } from '../types/Telemetry';
2
- interface Props {
2
+ type Props = {
3
3
  /**
4
4
  * Can be used to delay asking for permissions if set to false. True by default.
5
5
  * Can be used to request other permissions first, so the permission request dialogs do not overlap.
6
6
  * @default true
7
7
  */
8
- requestTelemetryPermissions?: boolean;
8
+ requestTelemetryPermissions: true;
9
9
  /**
10
10
  * The permissions to check.
11
11
  */
@@ -27,7 +27,16 @@ interface Props {
27
27
  */
28
28
  cancelButtonText?: string;
29
29
  };
30
- }
30
+ /**
31
+ * set to true in case your build variant targets Android Automotive
32
+ */
33
+ isAndroidAutomotive?: boolean;
34
+ } | {
35
+ requestTelemetryPermissions: false | undefined;
36
+ requiredPermissions?: never;
37
+ automotivePermissionRequest?: never;
38
+ isAndroidAutomotive?: boolean;
39
+ };
31
40
  /**
32
41
  * Hook to check if the telemetry permissions are granted. If the permissions are not granted, it will request them from the user.
33
42
  *
@@ -35,7 +44,7 @@ interface Props {
35
44
  * @param requestTelemetryPermissions If true, the telemetry permissions will be requested from the user. Can be set to false initially, in case other permissions need to be requested first, so the permission request dialogs do not overlap.
36
45
  * @param requiredPermissions The permissions to check.
37
46
  */
38
- export declare const useAndroidAutoTelemetry: ({ requestTelemetryPermissions, requiredPermissions, automotivePermissionRequest, }: Props) => {
47
+ export declare const useAndroidAutoTelemetry: ({ requestTelemetryPermissions, requiredPermissions, automotivePermissionRequest, isAndroidAutomotive, }: Props) => {
39
48
  /**
40
49
  * null on pending permission check, True if the telemetry permissions are granted, false otherwise.
41
50
  */
@@ -8,12 +8,15 @@ import { HybridAndroidAutoTelemetry, HybridAutoPlay } from '..';
8
8
  * @param requestTelemetryPermissions If true, the telemetry permissions will be requested from the user. Can be set to false initially, in case other permissions need to be requested first, so the permission request dialogs do not overlap.
9
9
  * @param requiredPermissions The permissions to check.
10
10
  */
11
- export const useAndroidAutoTelemetry = ({ requestTelemetryPermissions = true, requiredPermissions, automotivePermissionRequest, }) => {
11
+ export const useAndroidAutoTelemetry = ({ requestTelemetryPermissions = true, requiredPermissions = [], automotivePermissionRequest, isAndroidAutomotive = false, }) => {
12
12
  const [permissionsGranted, setPermissionsGranted] = useState(null);
13
13
  const [telemetry, setTelemetry] = useState(undefined);
14
14
  const [error, setError] = useState(undefined);
15
- const [isConnected, setIsConnected] = useState(false);
15
+ const [isConnected, setIsConnected] = useState(isAndroidAutomotive);
16
16
  useEffect(() => {
17
+ if (isAndroidAutomotive) {
18
+ return;
19
+ }
17
20
  const removeDidConnect = HybridAutoPlay.addListener('didConnect', () => setIsConnected(true));
18
21
  const removeDidDisconnect = HybridAutoPlay.addListener('didDisconnect', () => setIsConnected(false));
19
22
  setIsConnected(HybridAutoPlay.isConnected());
@@ -21,7 +24,7 @@ export const useAndroidAutoTelemetry = ({ requestTelemetryPermissions = true, re
21
24
  removeDidConnect();
22
25
  removeDidDisconnect();
23
26
  };
24
- }, []);
27
+ }, [isAndroidAutomotive]);
25
28
  useEffect(() => {
26
29
  const checkPermissions = async () => {
27
30
  const state = await Promise.all(requiredPermissions.map((permission) => PermissionsAndroid.check(permission).catch(() => false)));
@@ -0,0 +1,7 @@
1
+ /**
2
+ * A hook to determine if the CarPlay/Android Auto screen is currently focused (visible).
3
+ *
4
+ * @param moduleName The name of the module to listen to.
5
+ * @returns `true` if the screen is focused, `false` otherwise.
6
+ */
7
+ export declare function useIsAutoPlayFocused(moduleName: string): boolean;
@@ -0,0 +1,20 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { HybridAutoPlay } from '..';
3
+ /**
4
+ * A hook to determine if the CarPlay/Android Auto screen is currently focused (visible).
5
+ *
6
+ * @param moduleName The name of the module to listen to.
7
+ * @returns `true` if the screen is focused, `false` otherwise.
8
+ */
9
+ export function useIsAutoPlayFocused(moduleName) {
10
+ const [isFocused, setIsFocused] = useState(false);
11
+ useEffect(() => {
12
+ const remove = HybridAutoPlay.addListenerRenderState(moduleName, (state) => {
13
+ setIsFocused(state === 'didAppear');
14
+ });
15
+ return () => {
16
+ remove();
17
+ };
18
+ }, [moduleName]);
19
+ return isFocused;
20
+ }
@@ -0,0 +1,2 @@
1
+ import type { AutoPlay as NitroAutoPlay } from './specs/AutoPlay.nitro';
2
+ export declare const HybridAutoPlay: NitroAutoPlay;
package/lib/hybrid.js ADDED
@@ -0,0 +1,2 @@
1
+ import { NitroModules } from 'react-native-nitro-modules';
2
+ export const HybridAutoPlay = NitroModules.createHybridObject('AutoPlay');
package/lib/index.d.ts CHANGED
@@ -1,7 +1,9 @@
1
+ import type { AndroidAutomotive } from './specs/AndroidAutomotive.nitro';
1
2
  import type { AndroidAutoTelemetry as NitroAndroidAutoTelemetry } from './specs/AndroidAutoTelemetry.nitro';
2
3
  import type { AutoPlay as NitroAutoPlay } from './specs/AutoPlay.nitro';
3
4
  export declare const HybridAutoPlay: NitroAutoPlay;
4
5
  export declare const HybridAndroidAutoTelemetry: NitroAndroidAutoTelemetry | null;
6
+ export declare const HybridAndroidAutomotive: AndroidAutomotive | null;
5
7
  /**
6
8
  * These are the static module names for the app running on the mobile device, head unit screen and the CarPlay dashboard.
7
9
  * Clusters generate uuids on native side that are passed in the RootComponentInitialProps
@@ -19,6 +21,8 @@ export * from './hooks/useSafeAreaInsets';
19
21
  export * from './hooks/useVoiceInput';
20
22
  export * from './scenes/AutoPlayCluster';
21
23
  export * from './scenes/CarPlayDashboardScene';
24
+ export type { ActiveCarUxRestrictions, AppFocusState, } from './specs/AndroidAutomotive.nitro';
25
+ export { CarUxRestrictions } from './specs/AndroidAutomotive.nitro';
22
26
  export * from './templates/GridTemplate';
23
27
  export * from './templates/InformationTemplate';
24
28
  export * from './templates/ListTemplate';
package/lib/index.js CHANGED
@@ -6,6 +6,9 @@ export const HybridAutoPlay = NitroModules.createHybridObject('AutoPlay');
6
6
  export const HybridAndroidAutoTelemetry = Platform.OS === 'android'
7
7
  ? NitroModules.createHybridObject('AndroidAutoTelemetry')
8
8
  : null;
9
+ export const HybridAndroidAutomotive = Platform.OS === 'android'
10
+ ? NitroModules.createHybridObject('AndroidAutomotive')
11
+ : null;
9
12
  /**
10
13
  * These are the static module names for the app running on the mobile device, head unit screen and the CarPlay dashboard.
11
14
  * Clusters generate uuids on native side that are passed in the RootComponentInitialProps
@@ -24,6 +27,7 @@ export * from './hooks/useSafeAreaInsets';
24
27
  export * from './hooks/useVoiceInput';
25
28
  export * from './scenes/AutoPlayCluster';
26
29
  export * from './scenes/CarPlayDashboardScene';
30
+ export { CarUxRestrictions } from './specs/AndroidAutomotive.nitro';
27
31
  export * from './templates/GridTemplate';
28
32
  export * from './templates/InformationTemplate';
29
33
  export * from './templates/ListTemplate';
@@ -0,0 +1,33 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+ import type { CleanupCallback } from '../types/Event';
3
+ export declare enum CarUxRestrictions {
4
+ UX_RESTRICTIONS_FULLY_RESTRICTED = 511,
5
+ UX_RESTRICTIONS_LIMIT_CONTENT = 32,
6
+ UX_RESTRICTIONS_LIMIT_STRING_LENGTH = 4,
7
+ UX_RESTRICTIONS_NO_DIALPAD = 1,
8
+ UX_RESTRICTIONS_NO_FILTERING = 2,
9
+ UX_RESTRICTIONS_NO_KEYBOARD = 8,
10
+ UX_RESTRICTIONS_NO_SETUP = 64,
11
+ UX_RESTRICTIONS_NO_TEXT_MESSAGE = 128,
12
+ UX_RESTRICTIONS_NO_VIDEO = 16,
13
+ UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION = 256
14
+ }
15
+ export interface ActiveCarUxRestrictions {
16
+ maxContentDepth: number;
17
+ maxCumulativeContentItems: number;
18
+ maxRestrictedStringLength: number;
19
+ activeRestrictions: Array<CarUxRestrictions>;
20
+ }
21
+ export interface AppFocusState {
22
+ isFocusOwned?: boolean;
23
+ isFocusActive?: boolean;
24
+ }
25
+ export interface AndroidAutomotive extends HybridObject<{
26
+ android: 'kotlin';
27
+ }> {
28
+ registerCarUxRestrictionsListener(callback: (restrictions: ActiveCarUxRestrictions) => void): CleanupCallback;
29
+ getCarUxRestrictions(): ActiveCarUxRestrictions;
30
+ registerAppFocusListener(callback: (state: AppFocusState) => void): CleanupCallback;
31
+ getAppFocusState(): AppFocusState;
32
+ requestAppFocus(): CleanupCallback;
33
+ }
@@ -0,0 +1,13 @@
1
+ export var CarUxRestrictions;
2
+ (function (CarUxRestrictions) {
3
+ CarUxRestrictions[CarUxRestrictions["UX_RESTRICTIONS_FULLY_RESTRICTED"] = 511] = "UX_RESTRICTIONS_FULLY_RESTRICTED";
4
+ CarUxRestrictions[CarUxRestrictions["UX_RESTRICTIONS_LIMIT_CONTENT"] = 32] = "UX_RESTRICTIONS_LIMIT_CONTENT";
5
+ CarUxRestrictions[CarUxRestrictions["UX_RESTRICTIONS_LIMIT_STRING_LENGTH"] = 4] = "UX_RESTRICTIONS_LIMIT_STRING_LENGTH";
6
+ CarUxRestrictions[CarUxRestrictions["UX_RESTRICTIONS_NO_DIALPAD"] = 1] = "UX_RESTRICTIONS_NO_DIALPAD";
7
+ CarUxRestrictions[CarUxRestrictions["UX_RESTRICTIONS_NO_FILTERING"] = 2] = "UX_RESTRICTIONS_NO_FILTERING";
8
+ CarUxRestrictions[CarUxRestrictions["UX_RESTRICTIONS_NO_KEYBOARD"] = 8] = "UX_RESTRICTIONS_NO_KEYBOARD";
9
+ CarUxRestrictions[CarUxRestrictions["UX_RESTRICTIONS_NO_SETUP"] = 64] = "UX_RESTRICTIONS_NO_SETUP";
10
+ CarUxRestrictions[CarUxRestrictions["UX_RESTRICTIONS_NO_TEXT_MESSAGE"] = 128] = "UX_RESTRICTIONS_NO_TEXT_MESSAGE";
11
+ CarUxRestrictions[CarUxRestrictions["UX_RESTRICTIONS_NO_VIDEO"] = 16] = "UX_RESTRICTIONS_NO_VIDEO";
12
+ CarUxRestrictions[CarUxRestrictions["UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION"] = 256] = "UX_RESTRICTIONS_NO_VOICE_TRANSCRIPTION";
13
+ })(CarUxRestrictions || (CarUxRestrictions = {}));
@@ -0,0 +1,11 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+ import type { NitroAutomotivePermissionRequestTemplateConfig } from '../templates/AutomotivePermissionRequestTemplate';
3
+ import type { NitroTemplateConfig } from './AutoPlay.nitro';
4
+ interface AutomotivePermissionRequestTemplateConfig extends NitroTemplateConfig, NitroAutomotivePermissionRequestTemplateConfig {
5
+ }
6
+ export interface AutomotivePermissionRequestTemplate extends HybridObject<{
7
+ android: 'kotlin';
8
+ }> {
9
+ createAutomotivePermissionRequestTemplate(config: AutomotivePermissionRequestTemplateConfig): void;
10
+ }
11
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,11 @@
1
+ import type { HybridObject } from 'react-native-nitro-modules';
2
+ import type { NitroAutomotivePermissionRequestTemplateConfig } from '../templates/AutomotivePermissionRequestTemplate';
3
+ import type { NitroTemplateConfig } from './AutoPlay.nitro';
4
+ interface AutomotivePermissionRequestTemplateConfig extends NitroTemplateConfig, NitroAutomotivePermissionRequestTemplateConfig {
5
+ }
6
+ export interface AutomotivePermissionRequestTemplate extends HybridObject<{
7
+ android: 'kotlin';
8
+ }> {
9
+ createAutomotivePermissionRequestTemplate(config: AutomotivePermissionRequestTemplateConfig): void;
10
+ }
11
+ export {};