Skip to content

Commit

Permalink
Merge branch 'rhmi_custom_resources'
Browse files Browse the repository at this point in the history
  • Loading branch information
hufman committed Aug 3, 2024
2 parents 156e95f + c3d1b0d commit 59b2781
Show file tree
Hide file tree
Showing 14 changed files with 51 additions and 14 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
/build
/captures
/external
/app/src/main/assets/carapplications
/app/src/test/resources/*.xml
.externalNativeBuild
sentry.properties
Expand Down
4 changes: 4 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ android {
// enable async RHMI data updates
// big performance improvement, and seemingly doesn't even cause bugs!
buildConfigField "boolean", "ASYNC_RHMI_APPLICATION", "true"

// with custom hackery, the car HMI can accept unsigned resources
// if your car has this, set this option to attempt to send these unsigned resources
buildConfigField "boolean", "SEND_UNSIGNED_RESOURCES", (System.env.AndroidAutoIdrive_SendUnsignedResources ? System.env.AndroidAutoIdrive_SendUnsignedResources : AndroidAutoIdrive_SendUnsignedResources)
}

signingConfigs {
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/assets/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

basecoreOnlineServices
bmwone
calendar
cdsbaseapp
multimedia
news
smartthings
spotify
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import io.bimmergestalt.idriveconnectkit.android.IDriveConnectionStatus
import io.bimmergestalt.idriveconnectkit.android.security.SecurityAccess
import io.bimmergestalt.idriveconnectkit.rhmi.*
import io.bimmergestalt.idriveconnectkit.rhmi.deserialization.loadFromXML
import me.hufman.androidautoidrive.BuildConfig
import me.hufman.androidautoidrive.CarAppWidgetAssetResources
import me.hufman.androidautoidrive.carapp.FocusTriggerController
import me.hufman.androidautoidrive.carapp.L
Expand All @@ -19,7 +20,7 @@ import me.hufman.androidautoidrive.carapp.notifications.views.ID5PopupView
import me.hufman.androidautoidrive.utils.GraphicsHelpers
import java.util.zip.ZipInputStream

class ID5StatusbarApp(val iDriveConnectionStatus: IDriveConnectionStatus, val securityAccess: SecurityAccess, carAppAssets: CarAppWidgetAssetResources, graphicsHelpers: GraphicsHelpers) {
class ID5StatusbarApp(val iDriveConnectionStatus: IDriveConnectionStatus, val securityAccess: SecurityAccess, carAppAssets: CarAppWidgetAssetResources, unsignedcarAppAssets: CarAppWidgetAssetResources, graphicsHelpers: GraphicsHelpers) {
val carConnection: BMWRemotingServer
val carApp: RHMIApplication
val infoState: RHMIState.PlainState
Expand All @@ -45,7 +46,16 @@ class ID5StatusbarApp(val iDriveConnectionStatus: IDriveConnectionStatus, val se
carConnection.rhmi_setResourceCached(rhmiHandle, BMWRemoting.RHMIResourceType.IMAGEDB, carAppAssets.getImagesDB(iDriveConnectionStatus.brand ?: "common"))
carConnection.rhmi_setResourceCached(rhmiHandle, BMWRemoting.RHMIResourceType.WIDGETDB, carAppAssets.getWidgetsDB(iDriveConnectionStatus.brand ?: "common"))

// no text, so sneaky
if (BuildConfig.SEND_UNSIGNED_RESOURCES) {
try {
carConnection.rhmi_setResourceCached(rhmiHandle, BMWRemoting.RHMIResourceType.TEXTDB, unsignedcarAppAssets.getTextsDB(iDriveConnectionStatus.brand ?: "common"))
} catch (e: Exception) {
Log.w(TAG, "Unsigned resources were not accepted by car")
}
} else {
// no text, so sneaky
}

carConnection.rhmi_initialize(rhmiHandle)

carApp = RHMIApplicationSynchronized(RHMIApplicationIdempotent(RHMIApplicationEtch(carConnection, rhmiHandle)), carConnection)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class NotificationAppService: CarAppService() {
// using a handler to automatically handle shutting down during init
val carappReadout = ReadoutApp(iDriveConnectionStatus, securityAccess,
CarAppAssetResources(applicationContext, "news"),
CarAppAssetResources(applicationContext, "carinfo_unsigned"),
handler, applicationContext.resources, AppSettingsViewer())
carappNotifications?.readoutInteractions?.readoutController = carappReadout.readoutController
this.carappReadout = carappReadout
Expand All @@ -60,7 +61,9 @@ class NotificationAppService: CarAppService() {
// start up the id5 statusbar app
// using a handler to automatically handle shutting down during init
val carappStatusbar = ID5StatusbarApp(iDriveConnectionStatus, securityAccess,
CarAppWidgetAssetResources(applicationContext, "bmwone"), GraphicsHelpersAndroid())
CarAppWidgetAssetResources(applicationContext, "bmwone"),
CarAppWidgetAssetResources(applicationContext, "id5statusbar_unsigned"),
GraphicsHelpersAndroid())
carappNotifications?.id5Upgrade(carappStatusbar)
this.carappStatusbar = carappStatusbar
Log.i(MainService.TAG, "Finished initializing id5 statusbar")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import io.bimmergestalt.idriveconnectkit.rhmi.*
import io.bimmergestalt.idriveconnectkit.rhmi.deserialization.loadFromXML
import kotlinx.coroutines.android.asCoroutineDispatcher
import me.hufman.androidautoidrive.AppSettings
import me.hufman.androidautoidrive.BuildConfig
import me.hufman.androidautoidrive.CarInformation
import me.hufman.androidautoidrive.R
import me.hufman.androidautoidrive.carapp.*
Expand All @@ -28,7 +29,7 @@ import me.hufman.androidautoidrive.carapp.carinfo.views.CategoryView
import me.hufman.androidautoidrive.cds.*
import me.hufman.androidautoidrive.utils.Utils

class ReadoutApp(val iDriveConnectionStatus: IDriveConnectionStatus, val securityAccess: SecurityAccess, val carAppAssets: CarAppResources, val handler: Handler, val resources: Resources, val appSettings: AppSettings) {
class ReadoutApp(val iDriveConnectionStatus: IDriveConnectionStatus, val securityAccess: SecurityAccess, val carAppAssets: CarAppResources, val unsignedCarAppAssets: CarAppResources, val handler: Handler, val resources: Resources, val appSettings: AppSettings) {
private val coroutineContext = handler.asCoroutineDispatcher()
val carConnection: BMWRemotingServer
var rhmiHandle: Int = -1
Expand Down Expand Up @@ -98,7 +99,17 @@ class ReadoutApp(val iDriveConnectionStatus: IDriveConnectionStatus, val securit
rhmiHandle = carConnection.rhmi_create(null, BMWRemoting.RHMIMetaData("me.hufman.androidautoidrive.notification.readout", BMWRemoting.VersionInfo(0, 1, 0),
"me.hufman.androidautoidrive.notification.readout", "me.hufman"))
carConnection.rhmi_setResourceCached(rhmiHandle, BMWRemoting.RHMIResourceType.DESCRIPTION, carAppAssets.getUiDescription())
// no icons or text, so sneaky
if (BuildConfig.SEND_UNSIGNED_RESOURCES) {
try {
carConnection.rhmi_setResourceCached(rhmiHandle, BMWRemoting.RHMIResourceType.TEXTDB, unsignedCarAppAssets.getTextsDB(iDriveConnectionStatus.brand ?: "common"))
carConnection.rhmi_setResourceCached(rhmiHandle, BMWRemoting.RHMIResourceType.IMAGEDB, unsignedCarAppAssets.getImagesDB(iDriveConnectionStatus.brand ?: "common"))
} catch (e: Exception) {
Log.w(TAG, "Unsigned resources were not accepted by car")
}
} else {
// no icons or text, so sneaky
}

carConnection.rhmi_initialize(rhmiHandle)
carConnection.rhmi_addActionEventHandler(rhmiHandle, "me.hufman.androidautoidrive.notification.readout", -1)
carConnection.rhmi_addHmiEventHandler(rhmiHandle, "me.hufman.androidautoidrive.notification.readout", -1, -1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class ReadoutAppTest {
fun testAppInit() {
val mockServer = MockBMWRemotingServer()
IDriveConnection.mockRemotingServer = mockServer
val app = ReadoutApp(iDriveConnectionStatus, securityAccess, carAppResources, mock(), resources, AppSettingsViewer())
val app = ReadoutApp(iDriveConnectionStatus, securityAccess, carAppResources, mock(), mock(), resources, AppSettingsViewer())

val labelComponent = app.infoState.state.componentsList.filterIsInstance<RHMIComponent.Button>().first()
assertEquals(L.CARINFO_TITLE, mockServer.data[labelComponent.model])
Expand All @@ -53,7 +53,7 @@ class ReadoutAppTest {
fun testTTSCallback() {
val mockServer = MockBMWRemotingServer()
IDriveConnection.mockRemotingServer = mockServer
val app = ReadoutApp(iDriveConnectionStatus, securityAccess, carAppResources, mock(), resources, AppSettingsViewer())
val app = ReadoutApp(iDriveConnectionStatus, securityAccess, carAppResources, mock(), mock(), resources, AppSettingsViewer())

IDriveConnection.mockRemotingClient?.cds_onPropertyChangedEvent(1, "113", "hmi.tts",
"{\"TTSState\": {\"state\": 0, \"type\": \"app\", \"currentblock\": 0}}" )
Expand All @@ -69,7 +69,7 @@ class ReadoutAppTest {
fun testTTSTrigger() {
val mockServer = MockBMWRemotingServer()
IDriveConnection.mockRemotingServer = mockServer
val app = ReadoutApp(iDriveConnectionStatus, securityAccess, carAppResources, mock(), resources, AppSettingsViewer())
val app = ReadoutApp(iDriveConnectionStatus, securityAccess, carAppResources, mock(), mock(), resources, AppSettingsViewer())

app.readoutController.readout(listOf("Test Output"))
val speechList = mockServer.data[app.readoutController.speechList.id] as BMWRemoting.RHMIDataTable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class StatusbarAppTest {
fun testAppInit() {
val mockServer = MockBMWRemotingServer()
IDriveConnection.mockRemotingServer = mockServer
val app = ID5StatusbarApp(iDriveConnectionStatus, securityAccess, carAppResources, graphicsHelpers)
val app = ID5StatusbarApp(iDriveConnectionStatus, securityAccess, carAppResources, mock(), graphicsHelpers)

assertEquals(null, mockServer.resources[BMWRemoting.RHMIResourceType.TEXTDB])
assertArrayEquals(carAppResources.getImagesDB("")?.readBytes(), mockServer.resources[BMWRemoting.RHMIResourceType.IMAGEDB])
Expand All @@ -80,7 +80,7 @@ class StatusbarAppTest {
fun testAppEntrybutton() {
val mockServer = MockBMWRemotingServer()
IDriveConnection.mockRemotingServer = mockServer
val app = ID5StatusbarApp(iDriveConnectionStatus, securityAccess, carAppResources, graphicsHelpers)
val app = ID5StatusbarApp(iDriveConnectionStatus, securityAccess, carAppResources, mock(), graphicsHelpers)

val button = app.carApp.components.values.filterIsInstance<RHMIComponent.EntryButton>().first()
button.getAction()?.asRAAction()?.rhmiActionCallback?.onActionEvent(mapOf(0.toByte() to 0))
Expand All @@ -91,7 +91,7 @@ class StatusbarAppTest {
fun testNotificationCenterController() {
val mockServer = MockBMWRemotingServer()
IDriveConnection.mockRemotingServer = mockServer
val app = ID5StatusbarApp(iDriveConnectionStatus, securityAccess, carAppResources, graphicsHelpers)
val app = ID5StatusbarApp(iDriveConnectionStatus, securityAccess, carAppResources, mock(), graphicsHelpers)
val controller = app.statusbarController
val event = app.notificationEvent

Expand Down Expand Up @@ -143,7 +143,7 @@ class StatusbarAppTest {
fun testID5Popup() {
val mockServer = MockBMWRemotingServer()
IDriveConnection.mockRemotingServer = mockServer
val app = ID5StatusbarApp(iDriveConnectionStatus, securityAccess, carAppResources, graphicsHelpers)
val app = ID5StatusbarApp(iDriveConnectionStatus, securityAccess, carAppResources, mock(), graphicsHelpers)
app.showNotificationController = showNotificationController
val popupState = app.carApp.states.values.filterIsInstance<RHMIState.PopupState>().first()
val popupView = app.popupView
Expand Down
3 changes: 2 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ AndroidAutoIdrive_GmapsApiKey=unset
AndroidAutoIdrive_MapboxAccessToken=unset
AndroidAutoIdrive_MapboxDownloadToken=unset
AndroidAutoIdrive_SentryDsn=unset
AndroidAutoIdrive_SpotifyApiKey=invalid
AndroidAutoIdrive_SpotifyApiKey=invalid
AndroidAutoIdrive_SendUnsignedResources=false

0 comments on commit 59b2781

Please sign in to comment.