@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.
- package/README.md +1 -1
- package/android/src/auto/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAndroidAutomotive.kt +25 -0
- package/android/src/automotive/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/AndroidTelemetryObserver.kt +2 -5
- package/android/src/automotive/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAndroidAutomotive.kt +185 -0
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/SignInWithGoogleActivity.kt +14 -20
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/AutomotivePermissionRequestTemplate.kt +3 -3
- package/android/src/main/java/com/margelo/nitro/swe/iternio/reactnativeautoplay/template/SignInTemplate.kt +15 -12
- package/android/src/main/res/drawable/google.xml +18 -0
- package/lib/hooks/useAndroidAutoTelemetry.d.ts +13 -4
- package/lib/hooks/useAndroidAutoTelemetry.js +6 -3
- package/lib/hooks/useIsAutoPlayFocused.d.ts +7 -0
- package/lib/hooks/useIsAutoPlayFocused.js +20 -0
- package/lib/hybrid.d.ts +2 -0
- package/lib/hybrid.js +2 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +4 -0
- package/lib/specs/AndroidAutomotive.nitro.d.ts +33 -0
- package/lib/specs/AndroidAutomotive.nitro.js +13 -0
- package/lib/specs/AutomotivePermissionRequestTemplate.d.ts +11 -0
- package/lib/specs/AutomotivePermissionRequestTemplate.js +1 -0
- package/lib/specs/AutomotivePermissionRequestTemplate.nitro.d.ts +11 -0
- package/lib/specs/AutomotivePermissionRequestTemplate.nitro.js +1 -0
- package/lib/templates/AutomotivePermissionRequestTemplate.d.ts +23 -0
- package/lib/templates/AutomotivePermissionRequestTemplate.js +18 -0
- package/lib/types/SignInMethod.d.ts +1 -0
- package/nitro.json +3 -0
- package/nitrogen/generated/android/ReactNativeAutoPlay+autolinking.cmake +2 -0
- package/nitrogen/generated/android/ReactNativeAutoPlayOnLoad.cpp +16 -2
- package/nitrogen/generated/android/c++/JActiveCarUxRestrictions.hpp +89 -0
- package/nitrogen/generated/android/c++/JAppFocusState.hpp +61 -0
- package/nitrogen/generated/android/c++/JCarUxRestrictions.hpp +82 -0
- package/nitrogen/generated/android/c++/JFunc_void_ActiveCarUxRestrictions.hpp +80 -0
- package/nitrogen/generated/android/c++/JFunc_void_AppFocusState.hpp +78 -0
- package/nitrogen/generated/android/c++/JGoogleSignIn.hpp +7 -3
- package/nitrogen/generated/android/c++/JHybridAndroidAutomotiveSpec.cpp +120 -0
- package/nitrogen/generated/android/c++/JHybridAndroidAutomotiveSpec.hpp +70 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/ActiveCarUxRestrictions.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/AppFocusState.kt +41 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/CarUxRestrictions.kt +31 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_ActiveCarUxRestrictions.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/Func_void_AppFocusState.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/GoogleSignIn.kt +8 -5
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/swe/iternio/reactnativeautoplay/HybridAndroidAutomotiveSpec.kt +88 -0
- package/nitrogen/generated/shared/c++/ActiveCarUxRestrictions.hpp +97 -0
- package/nitrogen/generated/shared/c++/AppFocusState.hpp +87 -0
- package/nitrogen/generated/shared/c++/CarUxRestrictions.hpp +81 -0
- package/nitrogen/generated/shared/c++/GoogleSignIn.hpp +6 -2
- package/nitrogen/generated/shared/c++/HybridAndroidAutomotiveSpec.cpp +25 -0
- package/nitrogen/generated/shared/c++/HybridAndroidAutomotiveSpec.hpp +71 -0
- package/package.json +1 -1
- package/src/hooks/useAndroidAutoTelemetry.ts +48 -32
- package/src/index.ts +11 -0
- package/src/specs/AndroidAutomotive.nitro.ts +37 -0
- 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!
|
|
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(
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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,
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
|
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(
|
|
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
|
+
}
|
package/lib/hybrid.d.ts
ADDED
package/lib/hybrid.js
ADDED
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 {};
|