@javascriptcommon/react-native-carplay 2.3.11 → 2.4.2

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 (136) hide show
  1. package/android/build.gradle +110 -0
  2. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  3. package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  4. package/android/gradle.properties +20 -0
  5. package/android/gradlew +234 -0
  6. package/android/gradlew.bat +89 -0
  7. package/android/src/main/AndroidManifest.xml +30 -0
  8. package/android/src/main/AndroidManifestNew.xml +30 -0
  9. package/android/src/main/java/org/birkir/carplay/CarPlayModule.kt +321 -0
  10. package/android/src/main/java/org/birkir/carplay/CarPlayPackage.kt +18 -0
  11. package/android/src/main/java/org/birkir/carplay/CarPlayService.kt +35 -0
  12. package/android/src/main/java/org/birkir/carplay/CarPlaySession.kt +126 -0
  13. package/android/src/main/java/org/birkir/carplay/parser/Ext.kt +11 -0
  14. package/android/src/main/java/org/birkir/carplay/parser/Parser.kt +18 -0
  15. package/android/src/main/java/org/birkir/carplay/parser/RCTGridTemplate.kt +28 -0
  16. package/android/src/main/java/org/birkir/carplay/parser/RCTListTemplate.kt +64 -0
  17. package/android/src/main/java/org/birkir/carplay/parser/RCTMapTemplate.kt +128 -0
  18. package/android/src/main/java/org/birkir/carplay/parser/RCTMessageTemplate.kt +28 -0
  19. package/android/src/main/java/org/birkir/carplay/parser/RCTPaneTemplate.kt +24 -0
  20. package/android/src/main/java/org/birkir/carplay/parser/RCTSearchTemplate.kt +41 -0
  21. package/android/src/main/java/org/birkir/carplay/parser/RCTTabTemplate.kt +157 -0
  22. package/android/src/main/java/org/birkir/carplay/parser/RCTTemplate.kt +419 -0
  23. package/android/src/main/java/org/birkir/carplay/parser/TemplateParser.kt +35 -0
  24. package/android/src/main/java/org/birkir/carplay/screens/CarScreen.kt +76 -0
  25. package/android/src/main/java/org/birkir/carplay/screens/CarScreenContext.kt +10 -0
  26. package/android/src/main/java/org/birkir/carplay/utils/EventEmitter.kt +167 -0
  27. package/android/src/main/java/org/birkir/carplay/utils/VirtualRenderer.kt +75 -0
  28. package/ios/RCTConvert+RNCarPlay.h +7 -8
  29. package/ios/RCTConvert+RNCarPlay.m +4 -3
  30. package/ios/RNCarPlay.h +11 -14
  31. package/ios/RNCarPlay.m +749 -945
  32. package/ios/RNCarPlayViewController.h +10 -0
  33. package/ios/RNCarPlayViewController.m +50 -0
  34. package/lib/CarPlay.d.ts +183 -0
  35. package/lib/CarPlay.d.ts.map +1 -0
  36. package/lib/index.d.ts +44 -0
  37. package/lib/index.d.ts.map +1 -0
  38. package/lib/interfaces/Action.d.ts +13 -0
  39. package/lib/interfaces/Action.d.ts.map +1 -0
  40. package/lib/interfaces/AlertAction.d.ts +6 -0
  41. package/lib/interfaces/AlertAction.d.ts.map +1 -0
  42. package/lib/interfaces/BarButton.d.ts +39 -0
  43. package/lib/interfaces/BarButton.d.ts.map +1 -0
  44. package/lib/interfaces/CarColor.d.ts +2 -0
  45. package/lib/interfaces/CarColor.d.ts.map +1 -0
  46. package/lib/interfaces/GridButton.d.ts +24 -0
  47. package/lib/interfaces/GridButton.d.ts.map +1 -0
  48. package/lib/interfaces/Header.d.ts +15 -0
  49. package/lib/interfaces/Header.d.ts.map +1 -0
  50. package/lib/interfaces/ListItem.d.ts +90 -0
  51. package/lib/interfaces/ListItem.d.ts.map +1 -0
  52. package/lib/interfaces/ListItemUpdate.d.ts +15 -0
  53. package/lib/interfaces/ListItemUpdate.d.ts.map +1 -0
  54. package/lib/interfaces/ListSection.d.ts +21 -0
  55. package/lib/interfaces/ListSection.d.ts.map +1 -0
  56. package/lib/interfaces/Maneuver.d.ts +31 -0
  57. package/lib/interfaces/Maneuver.d.ts.map +1 -0
  58. package/lib/interfaces/MapButton.d.ts +27 -0
  59. package/lib/interfaces/MapButton.d.ts.map +1 -0
  60. package/lib/interfaces/NavigationAlert.d.ts +44 -0
  61. package/lib/interfaces/NavigationAlert.d.ts.map +1 -0
  62. package/lib/interfaces/NavigationInfo.d.ts +17 -0
  63. package/lib/interfaces/NavigationInfo.d.ts.map +1 -0
  64. package/lib/interfaces/NavigationStep.d.ts +17 -0
  65. package/lib/interfaces/NavigationStep.d.ts.map +1 -0
  66. package/lib/interfaces/Pane.d.ts +29 -0
  67. package/lib/interfaces/Pane.d.ts.map +1 -0
  68. package/lib/interfaces/PauseReason.d.ts +8 -0
  69. package/lib/interfaces/PauseReason.d.ts.map +1 -0
  70. package/lib/interfaces/Place.d.ts +11 -0
  71. package/lib/interfaces/Place.d.ts.map +1 -0
  72. package/lib/interfaces/TextConfiguration.d.ts +6 -0
  73. package/lib/interfaces/TextConfiguration.d.ts.map +1 -0
  74. package/lib/interfaces/TimeRemainingColor.d.ts +2 -0
  75. package/lib/interfaces/TimeRemainingColor.d.ts.map +1 -0
  76. package/lib/interfaces/TravelEstimates.d.ts +37 -0
  77. package/lib/interfaces/TravelEstimates.d.ts.map +1 -0
  78. package/lib/interfaces/VoiceControlState.d.ts +8 -0
  79. package/lib/interfaces/VoiceControlState.d.ts.map +1 -0
  80. package/lib/navigation/NavigationSession.d.ts +18 -0
  81. package/lib/navigation/NavigationSession.d.ts.map +1 -0
  82. package/lib/navigation/Trip.d.ts +22 -0
  83. package/lib/navigation/Trip.d.ts.map +1 -0
  84. package/lib/templates/ActionSheetTemplate.d.ts +18 -0
  85. package/lib/templates/ActionSheetTemplate.d.ts.map +1 -0
  86. package/lib/templates/AlertTemplate.d.ts +17 -0
  87. package/lib/templates/AlertTemplate.d.ts.map +1 -0
  88. package/lib/templates/ContactTemplate.d.ts +36 -0
  89. package/lib/templates/ContactTemplate.d.ts.map +1 -0
  90. package/lib/templates/GridTemplate.d.ts +38 -0
  91. package/lib/templates/GridTemplate.d.ts.map +1 -0
  92. package/lib/templates/InformationTemplate.d.ts +28 -0
  93. package/lib/templates/InformationTemplate.d.ts.map +1 -0
  94. package/lib/templates/ListTemplate.d.ts +127 -0
  95. package/lib/templates/ListTemplate.d.ts.map +1 -0
  96. package/lib/templates/ListTemplate.js +24 -16
  97. package/lib/templates/MapTemplate.d.ts +171 -0
  98. package/lib/templates/MapTemplate.d.ts.map +1 -0
  99. package/lib/templates/NowPlayingTemplate.d.ts +31 -0
  100. package/lib/templates/NowPlayingTemplate.d.ts.map +1 -0
  101. package/lib/templates/PointOfInterestTemplate.d.ts +33 -0
  102. package/lib/templates/PointOfInterestTemplate.d.ts.map +1 -0
  103. package/lib/templates/SearchTemplate.d.ts +32 -0
  104. package/lib/templates/SearchTemplate.d.ts.map +1 -0
  105. package/lib/templates/SearchTemplate.js +2 -2
  106. package/lib/templates/TabBarTemplate.d.ts +27 -0
  107. package/lib/templates/TabBarTemplate.d.ts.map +1 -0
  108. package/lib/templates/Template.d.ts +82 -0
  109. package/lib/templates/Template.d.ts.map +1 -0
  110. package/lib/templates/Template.js +1 -1
  111. package/lib/templates/VoiceControlTemplate.d.ts +18 -0
  112. package/lib/templates/VoiceControlTemplate.d.ts.map +1 -0
  113. package/lib/templates/android/AndroidNavigationBaseTemplate.d.ts +19 -0
  114. package/lib/templates/android/AndroidNavigationBaseTemplate.d.ts.map +1 -0
  115. package/lib/templates/android/MessageTemplate.d.ts +16 -0
  116. package/lib/templates/android/MessageTemplate.d.ts.map +1 -0
  117. package/lib/templates/android/NavigationTemplate.d.ts +44 -0
  118. package/lib/templates/android/NavigationTemplate.d.ts.map +1 -0
  119. package/lib/templates/android/PaneTemplate.d.ts +13 -0
  120. package/lib/templates/android/PaneTemplate.d.ts.map +1 -0
  121. package/lib/templates/android/PlaceListMapTemplate.d.ts +58 -0
  122. package/lib/templates/android/PlaceListMapTemplate.d.ts.map +1 -0
  123. package/lib/templates/android/PlaceListNavigationTemplate.d.ts +51 -0
  124. package/lib/templates/android/PlaceListNavigationTemplate.d.ts.map +1 -0
  125. package/lib/templates/android/RoutePreviewNavigationTemplate.d.ts +60 -0
  126. package/lib/templates/android/RoutePreviewNavigationTemplate.d.ts.map +1 -0
  127. package/package.json +3 -3
  128. package/react-native-carplay.podspec +3 -3
  129. package/src/CarPlay.ts +28 -16
  130. package/src/interfaces/ListItem.ts +14 -8
  131. package/src/interfaces/ListSection.ts +1 -1
  132. package/src/templates/ListTemplate.ts +64 -44
  133. package/src/templates/NowPlayingTemplate.ts +10 -3
  134. package/src/templates/SearchTemplate.ts +2 -2
  135. package/src/templates/Template.ts +1 -1
  136. package/README.md +0 -633
@@ -0,0 +1,128 @@
1
+ package org.birkir.carplay.parser
2
+
3
+ import androidx.car.app.CarContext
4
+ import androidx.car.app.model.PlaceListMapTemplate
5
+ import androidx.car.app.model.Template
6
+ import androidx.car.app.navigation.model.MapController
7
+ import androidx.car.app.navigation.model.MapTemplate
8
+ import androidx.car.app.navigation.model.NavigationTemplate
9
+ import androidx.car.app.navigation.model.PanModeListener
10
+ import androidx.car.app.navigation.model.PlaceListNavigationTemplate
11
+ import androidx.car.app.navigation.model.RoutePreviewNavigationTemplate
12
+ import com.facebook.react.bridge.ReadableMap
13
+ import org.birkir.carplay.screens.CarScreenContext
14
+
15
+ class RCTMapTemplate(
16
+ context: CarContext,
17
+ carScreenContext: CarScreenContext
18
+ ) : RCTTemplate(context, carScreenContext) {
19
+
20
+ override fun parse(props: ReadableMap): Template {
21
+ val type = props.getString("type")
22
+ val actionStrip = props.getArray("actions")?.let {
23
+ parseActionStrip(it)
24
+ }
25
+ val header = props.getMap("header")?.let {
26
+ parseHeader(it)
27
+ }
28
+ val headerAction = props.getMap("headerAction")?.let {
29
+ parseAction(it)
30
+ }
31
+ val itemList = props.getArray("items")?.let {
32
+ parseItemList(it, "row")
33
+ }
34
+ val pane = props.getMap("pane")?.let {
35
+ parsePane(it)
36
+ }
37
+ val mapActionStrip = props.getArray("mapButtons")?.let {
38
+ parseActionStrip(it)
39
+ }
40
+ val panModeListener = PanModeListener { isInPanMode ->
41
+ if (isInPanMode) {
42
+ eventEmitter.didShowPanningInterface()
43
+ } else {
44
+ eventEmitter.didDismissPanningInterface()
45
+ }
46
+ }
47
+ val mapController = MapController.Builder().apply {
48
+ mapActionStrip?.let { setMapActionStrip(it); }
49
+ setPanModeListener(panModeListener)
50
+ }.build()
51
+ when (type) {
52
+ "navigation" -> {
53
+ return NavigationTemplate.Builder().apply {
54
+ actionStrip?.let { setActionStrip(it) }
55
+ props.getString("backgroundColor")?.let {
56
+ setBackgroundColor(parseColor(it))
57
+ }
58
+ props.getMap("travelEstimate")?.let {
59
+ setDestinationTravelEstimate(parseTravelEstimate(it))
60
+ }
61
+ props.getMap("navigationInfo")?.let {
62
+ setNavigationInfo(parseNavigationInfo(it))
63
+ }
64
+ mapActionStrip?.let { setMapActionStrip(it) }
65
+ setPanModeListener(panModeListener)
66
+ }.build()
67
+ }
68
+
69
+ "place-list-map" -> {
70
+ return PlaceListMapTemplate.Builder().apply {
71
+ actionStrip?.let { setActionStrip(it) }
72
+ props.getMap("anchor")?.let { setAnchor(parsePlace(it)) }
73
+ if (props.hasKey("currentLocationEnabled")) {
74
+ setCurrentLocationEnabled(props.getBoolean("currentLocationEnabled"))
75
+ }
76
+ headerAction?.let { setHeaderAction(it) }
77
+ itemList?.let { setItemList(it) }
78
+ setLoading(props.isLoading())
79
+ setOnContentRefreshListener {
80
+ // @todo eventEmitter?.contentDidRefresh
81
+ }
82
+ props.getString("title")?.let { setTitle(it) }
83
+ }.build()
84
+ }
85
+
86
+ "place-list-navigation" -> {
87
+ return PlaceListNavigationTemplate.Builder().apply {
88
+ actionStrip?.let { setActionStrip(it) }
89
+ header?.let { setHeader(it) }
90
+ itemList?.let { setItemList(it) }
91
+ setLoading(props.isLoading())
92
+ mapActionStrip?.let { setActionStrip(it) }
93
+ setOnContentRefreshListener {
94
+ // @todo eventEmitter?.contentDidRefresh
95
+ }
96
+ setPanModeListener(panModeListener)
97
+ }.build()
98
+ }
99
+
100
+ "route-preview" -> {
101
+ return RoutePreviewNavigationTemplate.Builder().apply {
102
+ actionStrip?.let { setActionStrip(it) }
103
+ header?.let { setHeader(it) }
104
+ headerAction?.let { setHeaderAction(headerAction) }
105
+ itemList?.let { setItemList(it) }
106
+ setLoading(props.isLoading())
107
+ mapActionStrip?.let { setMapActionStrip(it); }
108
+ props.getMap("navigateAction")?.let { setNavigateAction(parseAction(it)) }
109
+ setPanModeListener(panModeListener)
110
+ }.build()
111
+ }
112
+
113
+ else -> {
114
+ return MapTemplate.Builder().apply {
115
+ header?.let { setHeader(it) }
116
+ itemList?.let { setItemList(it) }
117
+ actionStrip?.let { setActionStrip(it) }
118
+ setMapController(mapController)
119
+ pane?.let { setPane(it) }
120
+ }.build()
121
+ }
122
+ }
123
+ }
124
+
125
+ companion object {
126
+ const val TAG = "RCTMapTemplate"
127
+ }
128
+ }
@@ -0,0 +1,28 @@
1
+ package org.birkir.carplay.parser
2
+
3
+ import androidx.car.app.CarContext
4
+ import androidx.car.app.model.MessageTemplate
5
+ import com.facebook.react.bridge.ReadableMap
6
+ import org.birkir.carplay.screens.CarScreenContext
7
+
8
+ class RCTMessageTemplate(
9
+ context: CarContext,
10
+ carScreenContext: CarScreenContext
11
+ ) : RCTTemplate(context, carScreenContext) {
12
+ override fun parse(props: ReadableMap): MessageTemplate {
13
+ val message = props.getString("message") ?: "No message"
14
+ val messageText = parseCarText(message, props)
15
+ return MessageTemplate.Builder(messageText).apply {
16
+ props.getArray("actions")?.let { setActionStrip(parseActionStrip(it)) }
17
+ props.getMap("headerAction")?.let { setHeaderAction(parseAction(it)) }
18
+ props.getMap("icon")?.let { setIcon(parseCarIcon(it)) }
19
+ props.getString("title")?.let { setTitle(it) }
20
+ props.getString("debugMessage")?.let { setDebugMessage(it) }
21
+ setLoading(props.isLoading())
22
+ }.build()
23
+ }
24
+
25
+ companion object {
26
+ const val TAG = "RCTMessageTemplate"
27
+ }
28
+ }
@@ -0,0 +1,24 @@
1
+ package org.birkir.carplay.parser
2
+
3
+ import androidx.car.app.CarContext
4
+ import androidx.car.app.model.PaneTemplate
5
+ import com.facebook.react.bridge.ReadableMap
6
+ import org.birkir.carplay.screens.CarScreenContext
7
+
8
+ class RCTPaneTemplate(
9
+ context: CarContext,
10
+ carScreenContext: CarScreenContext
11
+ ) : RCTTemplate(context, carScreenContext) {
12
+ override fun parse(props: ReadableMap): PaneTemplate {
13
+ val pane = parsePane(props.getMap("pane")!!)
14
+ return PaneTemplate.Builder(pane).apply {
15
+ props.getArray("actions")?.let { setActionStrip(parseActionStrip(it)) }
16
+ props.getMap("headerAction")?.let { setHeaderAction(parseAction(it)) }
17
+ props.getString("title")?.let { setTitle(it) }
18
+ }.build()
19
+ }
20
+
21
+ companion object {
22
+ const val TAG = "RCTPaneTemplate"
23
+ }
24
+ }
@@ -0,0 +1,41 @@
1
+ package org.birkir.carplay.parser
2
+
3
+ import androidx.car.app.CarContext
4
+ import androidx.car.app.model.SearchTemplate
5
+ import androidx.car.app.model.SearchTemplate.SearchCallback
6
+ import com.facebook.react.bridge.ReadableMap
7
+ import org.birkir.carplay.screens.CarScreenContext
8
+
9
+
10
+ class RCTSearchTemplate(
11
+ context: CarContext,
12
+ carScreenContext: CarScreenContext
13
+ ) : RCTTemplate(context, carScreenContext) {
14
+ override fun parse(props: ReadableMap): SearchTemplate {
15
+ return SearchTemplate.Builder(object : SearchCallback {
16
+ override fun onSearchTextChanged(searchText: String) {
17
+ eventEmitter.updatedSearchText(searchText)
18
+ }
19
+
20
+ override fun onSearchSubmitted(searchText: String) {
21
+ eventEmitter.searchButtonPressed(searchText)
22
+ }
23
+ }).apply {
24
+ props.getArray("actions")?.let { setActionStrip(parseActionStrip(it)) }
25
+ props.getMap("headerAction")?.let { setHeaderAction(parseAction(it)) }
26
+ props.getString("initialSearchText")?.let {
27
+ setInitialSearchText(it)
28
+ }
29
+ props.getArray("items")?.let { setItemList(parseItemList(it, "row")) }
30
+ setLoading(props.isLoading())
31
+ props.getString("searchHint")?.let { setSearchHint(it) }
32
+ if (props.hasKey("showKeyboardByDefault")) {
33
+ setShowKeyboardByDefault(props.getBoolean("showKeyboardByDefault"))
34
+ }
35
+ }.build()
36
+ }
37
+
38
+ companion object {
39
+ const val TAG = "RCTSearchTemplate"
40
+ }
41
+ }
@@ -0,0 +1,157 @@
1
+ package org.birkir.carplay.parser
2
+
3
+ import android.util.Log
4
+ import androidx.car.app.CarContext
5
+ import androidx.car.app.model.Action
6
+ import androidx.car.app.model.CarIcon
7
+ import androidx.car.app.model.ItemList
8
+ import androidx.car.app.model.ListTemplate
9
+ import androidx.car.app.model.Row
10
+ import androidx.car.app.model.Tab
11
+ import androidx.car.app.model.TabContents
12
+ import androidx.car.app.model.TabTemplate
13
+ import androidx.car.app.model.TabTemplate.TabCallback
14
+ import com.facebook.react.bridge.ReadableMap
15
+ import org.birkir.carplay.screens.CarScreenContext
16
+
17
+ class RCTTabTemplate(
18
+ context: CarContext,
19
+ carScreenContext: CarScreenContext
20
+ ) : RCTTemplate(context, carScreenContext) {
21
+ private val tabContentsMap = mutableMapOf<String, TabContents>()
22
+ private val tabInfoMap = mutableMapOf<String, TabInfo>()
23
+ private var currentTemplate: TabTemplate? = null
24
+ private var activeTabId: String? = null
25
+ private var isLoading: Boolean = false
26
+
27
+ data class TabInfo(val title: String, val icon: CarIcon, val headerAction: Action)
28
+
29
+ private val tabCallback = object : TabCallback {
30
+ override fun onTabSelected(tabContentId: String) {
31
+ Log.d(TAG, "Tab selected: $tabContentId")
32
+ eventEmitter.didSelectTemplate(tabContentId)
33
+ updateContents(tabContentId)
34
+ }
35
+ }
36
+
37
+ override fun parse(props: ReadableMap): TabTemplate {
38
+ tabInfoMap.clear()
39
+ isLoading = props.isLoading()
40
+ val builder = TabTemplate.Builder(tabCallback)
41
+ builder.setLoading(isLoading)
42
+
43
+ props.getArray("templates")?.let { templatesArray ->
44
+ for (i in 0 until templatesArray.size()) {
45
+ try {
46
+ val tabProps = templatesArray.getMap(i)
47
+ parseTabInfo(tabProps, builder)
48
+ } catch (e: Exception) {
49
+ Log.e(TAG, "Error parsing tab at index $i", e)
50
+ }
51
+ }
52
+
53
+ // Set first tab as active
54
+ tabInfoMap.keys.firstOrNull()?.let { firstTabId ->
55
+ setActiveTab(builder, firstTabId)
56
+ }
57
+ }
58
+
59
+ currentTemplate = builder.build()
60
+ return currentTemplate!!
61
+ }
62
+
63
+ private fun parseTabInfo(tabProps: ReadableMap, builder: TabTemplate.Builder) {
64
+ val id = tabProps.getString("id") ?: return
65
+ val tab = parseTab(tabProps)
66
+ builder.addTab(tab)
67
+
68
+ val headerAction = tabProps.getMap("headerAction")?.let { parseAction(it) } ?: Action.APP_ICON
69
+ tabInfoMap[id] = TabInfo(tab.title.toString(), tab.icon, headerAction)
70
+ }
71
+
72
+ private fun setActiveTab(builder: TabTemplate.Builder, tabId: String) {
73
+ builder.setTabContents(getTabContents(tabId))
74
+ builder.setActiveTabContentId(tabId)
75
+ builder.setHeaderAction(tabInfoMap[tabId]?.headerAction ?: Action.APP_ICON)
76
+ activeTabId = tabId
77
+ }
78
+
79
+ private fun buildCurrentTemplate() {
80
+ val builder = TabTemplate.Builder(tabCallback).setLoading(isLoading)
81
+
82
+ tabInfoMap.forEach { (id, info) ->
83
+ builder.addTab(Tab.Builder()
84
+ .setTitle(info.title)
85
+ .setIcon(info.icon)
86
+ .setContentId(id)
87
+ .build())
88
+ }
89
+
90
+ activeTabId?.let { activeId ->
91
+ setActiveTab(builder, activeId)
92
+ }
93
+
94
+ currentTemplate = builder.build()
95
+ }
96
+
97
+ private fun parseTab(props: ReadableMap): Tab {
98
+ return Tab.Builder().apply {
99
+ props.getString("id")?.let { setContentId(it) }
100
+ val config = props.getMap("config")
101
+ when {
102
+ config != null -> {
103
+ setTitle(config.getString("tabTitle") ?: DEFAULT_TAB_TITLE)
104
+ setIcon(CarIcon.APP_ICON) // TODO: Parse custom icon if needed
105
+ }
106
+ else -> {
107
+ setTitle(DEFAULT_TAB_TITLE)
108
+ setIcon(CarIcon.APP_ICON)
109
+ }
110
+ }
111
+ }.build()
112
+ }
113
+
114
+ private fun getTabContents(templateId: String): TabContents {
115
+ return tabContentsMap.getOrPut(templateId) {
116
+ val screen = carScreenContext.screens[templateId]
117
+ val template = screen?.template
118
+
119
+ when {
120
+ template != null -> {
121
+ Log.d(TAG, "Template found for $templateId: $template")
122
+ TabContents.Builder(template).build()
123
+ }
124
+ else -> createDefaultTabContents(templateId)
125
+ }
126
+ }
127
+ }
128
+
129
+ private fun createDefaultTabContents(templateId: String): TabContents {
130
+ val defaultItemList = ItemList.Builder()
131
+ .addItem(Row.Builder().setTitle("No content available for $templateId").build())
132
+ .build()
133
+ val defaultTemplate = ListTemplate.Builder()
134
+ .setTitle(DEFAULT_CONTENT_TITLE)
135
+ .setSingleList(defaultItemList)
136
+ .build()
137
+ return TabContents.Builder(defaultTemplate).build()
138
+ }
139
+
140
+ private fun updateContents(tabContentId: String) {
141
+ Log.d(TAG, "Updating contents for tab: $tabContentId")
142
+ activeTabId = tabContentId
143
+ buildCurrentTemplate()
144
+ // Update the main tab screen with the new template
145
+ carScreenContext.screens[carScreenContext.screenMarker]?.apply {
146
+ setTemplate(currentTemplate, carScreenContext.screenMarker, null)
147
+ invalidate()
148
+ }
149
+ Log.d(TAG, "Template updated for tab $tabContentId")
150
+ }
151
+
152
+ companion object {
153
+ const val TAG = "RCTTabTemplate"
154
+ private const val DEFAULT_TAB_TITLE = "Untitled Tab"
155
+ private const val DEFAULT_CONTENT_TITLE = "Default Content"
156
+ }
157
+ }