summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenigan <rheniga1@MGC12Z921DLVCG.fbpld77.ford.com>2021-04-14 14:40:20 -0400
committerHenigan <rheniga1@MGC12Z921DLVCG.fbpld77.ford.com>2021-04-14 14:40:20 -0400
commit75512263f50748901b99487e8eb1bcdd9e551fb8 (patch)
tree8968ce8c31ed2f749286f46861dee06b99211761
parente1ef1b496bcc874e92a850fa7b0c9cb70d5bf6a3 (diff)
parent0d6a4b03db46e4ff9bf55fe38d34d39bad9dbe12 (diff)
downloadsdl_android-75512263f50748901b99487e8eb1bcdd9e551fb8.tar.gz
Merge branch 'release/5.1.0_RC'5.1.0
-rw-r--r--CHANGELOG.md117
-rw-r--r--README.md20
-rw-r--r--VERSION2
-rw-r--r--android/README.md4
-rw-r--r--android/build.gradle2
-rw-r--r--android/gradle/wrapper/gradle-wrapper.properties4
-rwxr-xr-xandroid/hello_sdl_android/build.gradle4
-rwxr-xr-xandroid/hello_sdl_android/src/main/AndroidManifest.xml10
-rwxr-xr-xandroid/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java38
-rw-r--r--android/sdl_android/build.gradle13
-rw-r--r--android/sdl_android/libs/TaskMaster-0.5.jarbin0 -> 17590 bytes
-rw-r--r--android/sdl_android/src/androidTest/assets/json/AddCommand.json4
-rw-r--r--android/sdl_android/src/androidTest/assets/json/AddSubMenu.json6
-rw-r--r--android/sdl_android/src/androidTest/assets/json/GetVehicleData.json41
-rw-r--r--android/sdl_android/src/androidTest/assets/json/SubscribeVehicleData.json12
-rw-r--r--android/sdl_android/src/androidTest/assets/json/UnsubscribeVehicleData.json12
-rw-r--r--android/sdl_android/src/androidTest/java/com/android/grafika/gles/OffscreenSurfaceTest.java105
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/AlertViewTest.java78
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java11
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/audio/AudioStreamManagerTest.java19
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java721
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java21
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertAudioDataTest.java56
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertManagerTest.java166
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java315
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java93
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java58
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperationTest.java4
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java10
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java175
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetTests.java19
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java21
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java47
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java97
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandManagerTests.java31
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java297
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/VideoStreamManagerTests.java69
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/VideoStreamingRangeTests.java89
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java225
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java149
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/VehicleDataHelper.java41
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/RPCStructTests.java59
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/RPCGenericTests.java2
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/AppCapabilityTests.java90
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/BodyInformationTests.java58
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/ClimateDataTests.java83
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/DoorStatusTests.java65
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/GateStatusTests.java65
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/KeyboardCapabilitiesTests.java82
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/KeyboardPropertiesTests.java14
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/MenuParamsTests.java10
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/RoofStatusTests.java75
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/SeatOccupancyTests.java83
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/SeatStatusTests.java76
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/SeekStreamingIndicatorTests.java61
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VideoStreamingCapabilityTests.java27
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/WindowCapabilityTests.java13
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/AppCapabilityTypeTests.java63
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/DoorStatusTypeTests.java73
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/ImageFieldNameTests.java8
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardEventTests.java8
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardInputMaskTests.java69
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardLayoutTests.java4
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/StaticIconNameTests.java5
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/TextFieldNameTests.java18
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/VehicleDataTypeTests.java8
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnAppCapabilityUpdatedTests.java75
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnVehicleDataTests.java48
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/AddCommandTests.java10
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/AddSubmenuTests.java20
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/GetVehicleDataTests.java10
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/SetMediaClockTimerTests.java24
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/SubscribeVehicleDataTests.java10
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/UnsubscribeVehicleDataTests.java10
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/GetVehicleDataResponseTests.java54
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/SubscribeVehicleDataResponseTest.java19
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/UnsubscribeVehicleDataResponseTest.java19
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockInterfaceBroker.java3
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java5
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/VideoStreamingParametersTest.java10
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/util/VersionTest.java7
-rw-r--r--android/sdl_android/src/main/java/com/android/grafika/gles/Drawable2d.java197
-rw-r--r--android/sdl_android/src/main/java/com/android/grafika/gles/EglCore.java372
-rw-r--r--android/sdl_android/src/main/java/com/android/grafika/gles/EglSurfaceBase.java197
-rw-r--r--android/sdl_android/src/main/java/com/android/grafika/gles/FullFrameRect.java91
-rw-r--r--android/sdl_android/src/main/java/com/android/grafika/gles/GlUtil.java195
-rw-r--r--android/sdl_android/src/main/java/com/android/grafika/gles/OffscreenSurface.java39
-rw-r--r--android/sdl_android/src/main/java/com/android/grafika/gles/Texture2dProgram.java344
-rw-r--r--android/sdl_android/src/main/java/com/android/grafika/gles/WindowSurface.java90
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java293
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java143
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManagerListener.java9
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompat.java111
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompatOperation.java84
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java10
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/file/FileManager.java118
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java10
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java53
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicManager.java5
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java389
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/ImageResolutionKind.java8
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/Resolution.java19
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/VideoStreamingRange.java125
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/session/SdlSession.java9
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/streaming/video/SdlRemoteDisplay.java38
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/util/IntegrationValidator.java11
-rw-r--r--android/settings.gradle1
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/AlertCompletionListener.java11
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java18
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/ISdl.java13
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/ManagerUtility.java35
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java608
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/file/DeleteFileOperation.java103
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/file/DispatchGroup.java65
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/file/FileManagerCompletionListener.java42
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/file/ListFilesOperation.java111
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/file/SdlFileWrapper.java56
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java379
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java179
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/lifecycle/RpcConverter.java13
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/AlertAudioData.java82
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/AlertCanceledListener.java40
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/AlertView.java292
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/AudioData.java168
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java342
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java97
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java55
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/DispatchGroup.java60
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/PresentAlertOperation.java524
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java22
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java14
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicState.java (renamed from base/src/main/java/com/smartdevicelink/managers/screen/TextsAndGraphicsState.java)0
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperation.java25
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java135
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java62
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSet.java60
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/KeyboardListener.java2
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java16
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java3
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java2
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java240
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java230
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java152
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommand.java33
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java230
-rw-r--r--base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java140
-rw-r--r--base/src/main/java/com/smartdevicelink/protocol/enums/ControlFrameTags.java13
-rw-r--r--base/src/main/java/com/smartdevicelink/protocol/enums/FunctionID.java1
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java61
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/AddCommand.java32
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/AddSubMenu.java101
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/AppCapability.java141
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/AppInfo.java51
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/BodyInformation.java222
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/ClimateData.java157
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/DisplayCapabilities.java2
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/DoorStatus.java142
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/GateStatus.java142
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleData.java100
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleDataResponse.java74
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardCapabilities.java132
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardLayoutCapability.java145
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardProperties.java72
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/MenuParams.java75
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/OnAppCapabilityUpdated.java114
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/OnVehicleData.java94
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/RegisterAppInterface.java21
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/RegisterAppInterfaceResponse.java21
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/RoofStatus.java171
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/SeatOccupancy.java132
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/SeatStatus.java141
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/SeekStreamingIndicator.java146
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/SetMediaClockTimer.java115
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/Show.java41
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/ShowConstantTbt.java30
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleData.java100
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleDataResponse.java71
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleData.java100
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleDataResponse.java71
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/VideoStreamingCapability.java74
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/WindowCapability.java25
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/enums/AppCapabilityType.java55
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/enums/DoorStatusType.java56
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/enums/ImageFieldName.java14
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardEvent.java9
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardInputMask.java57
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardLayout.java6
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/enums/SeekIndicatorType.java54
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/enums/StaticIconName.java7
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/enums/TextFieldName.java43
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/enums/VehicleDataType.java12
-rw-r--r--base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java35
-rw-r--r--base/src/main/java/com/smartdevicelink/session/ISdlSessionListener.java4
-rw-r--r--base/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java89
-rw-r--r--base/src/main/java/com/smartdevicelink/transport/SdlPsm.java13
-rw-r--r--base/src/main/java/com/smartdevicelink/util/BitConverter.java29
-rw-r--r--base/src/main/java/com/smartdevicelink/util/SystemInfo.java68
m---------generator/rpc_spec0
-rw-r--r--generator/templates/base_template.java2
-rw-r--r--generator/templates/javadoc_version_info.java4
-rw-r--r--javaEE/hello_sdl_java_ee/src/main/java/com/smartdevicelink/SdlService.java102
-rw-r--r--javaEE/javaEE/README.md6
-rw-r--r--javaEE/javaEE/build.gradle2
-rw-r--r--javaEE/javaEE/libs/TaskMaster-0.5.jarbin0 -> 17590 bytes
-rw-r--r--javaSE/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java37
-rw-r--r--javaSE/javaSE/README.md4
-rw-r--r--javaSE/javaSE/build.gradle2
-rw-r--r--javaSE/javaSE/libs/TaskMaster-0.5.jarbin0 -> 17590 bytes
-rw-r--r--javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java2
-rw-r--r--javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java144
-rw-r--r--javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManagerListener.java9
-rw-r--r--javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/FileManager.java93
-rw-r--r--javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java18
-rw-r--r--javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicManager.java5
-rw-r--r--javaSE/javaSE/src/main/java/com/smartdevicelink/session/SdlSession.java9
-rw-r--r--javaSE/javaSE/src/main/java/com/smartdevicelink/util/Log.java8
216 files changed, 14468 insertions, 1929 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a4b7d4cbd..09260926b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,127 +1,100 @@
-# 5.0.0 Release Notes
+# 5.1.0 Release Notes
## Summary:
||Version|
|--|--|
-| **Protocol** | 5.3.0
-| **RPC** | 7.0.0
-| **Tested Targeting** | Android 29
+| **Protocol** | 5.4.0
+| **RPC** | 7.1.0
+| **Tested Targeting** | Android 30
## Features:
-- [[SDL 0242] Subtle Alert Style](https://github.com/smartdevicelink/sdl_java_suite/issues/1120)
+- [[SDL 0296] Possibility to update video streaming capabilities during ignition cycle](https://github.com/smartdevicelink/sdl_java_suite/issues/1410)
-- [[SDL 0278] Screen Manager Template Management](https://github.com/smartdevicelink/sdl_java_suite/issues/1327)
+- [[SDL 0282] Screen Manager Alert Manager](https://github.com/smartdevicelink/sdl_java_suite/issues/1304)
-- [[SDL 0148] Template Improvements: Additional SubMenus](https://github.com/smartdevicelink/sdl_java_suite/issues/716)
+- [[SDL 0269] New vehicle data ClimateData ](https://github.com/smartdevicelink/sdl_java_suite/issues/1247)
-- [[SDL 0268] Main Menu Updating and Pagination](https://github.com/smartdevicelink/sdl_java_suite/issues/1254)
+- [[SDL 0262] New vehicle data SeatOccupancy](https://github.com/smartdevicelink/sdl_java_suite/issues/1240)
-- [[SDL 0152] Driver Distraction Improvements: Command List Limitations](https://github.com/smartdevicelink/sdl_java_suite/issues/729)
+- [[SDL 0198] - Media Skip Indicators](https://github.com/smartdevicelink/sdl_java_suite/issues/871)
-- [[SDL 0311] Make RPC Setters Chainable](https://github.com/smartdevicelink/sdl_java_suite/issues/1421)
-
-- [[SDL 0308] Add a Reason Parameter to All Protocol NAKs](https://github.com/smartdevicelink/sdl_java_suite/issues/1379)
-
-- [[SDL 0253] New vehicle data StabilityControlsStatus](https://github.com/smartdevicelink/sdl_java_suite/issues/1204)
-
-- [[SDL 0266] New vehicle data GearStatus](https://github.com/smartdevicelink/sdl_java_suite/issues/1256)
-
-- [[SDL 0261] New vehicle data WindowStatus](https://github.com/smartdevicelink/sdl_java_suite/issues/1243)
-
-- [[SDL 0257] New vehicle data HandsOffSteering](https://github.com/smartdevicelink/sdl_java_suite/issues/1224)
-
-- [[SDL 0256] Refactor Fuel Information Related Vehicle Data](https://github.com/smartdevicelink/sdl_java_suite/issues/1223)
-
-- [[SDL 0273] WebEngine Projection mode](https://github.com/smartdevicelink/sdl_java_suite/issues/1375)
-
-- [[SDL 0202] Supported Character Sets](https://github.com/smartdevicelink/sdl_java_suite/issues/950)
## Enhancements:
-- [[SDL 0286] Java Suite Cleanup](https://github.com/smartdevicelink/sdl_java_suite/issues/1306)
-
-- [Android X support](https://github.com/smartdevicelink/sdl_java_suite/issues/1094)
+- [[SDL 0180] Broaden Choice Uniqueness](https://github.com/smartdevicelink/sdl_java_suite/issues/811)
-- [RPC Generator should use @deprecated annotation in JavaDocs when applicable](https://github.com/smartdevicelink/sdl_java_suite/issues/1448)
+- [[SDL 0305] Homogenize TextFieldName](https://github.com/smartdevicelink/sdl_java_suite/issues/1367)
-- [RPC Generator needs to be updated to generate correct Android X imports](https://github.com/smartdevicelink/sdl_java_suite/issues/1444)
+- [[SDL 0238] Keyboard Enhancements](https://github.com/smartdevicelink/sdl_java_suite/issues/1121)
-- [onError should be removed from OnRPCResponseListener](https://github.com/smartdevicelink/sdl_java_suite/issues/1455)
+- [[SDL 0267] Main Menu UI Updates](https://github.com/smartdevicelink/sdl_java_suite/issues/1246)
-- [SystemCapabilityManager & HapticInterfaceManager should be moved to the managers package](https://github.com/smartdevicelink/sdl_java_suite/issues/1432)
+- [[SDL 0274] Add preferred FPS to VideoStreamingCapability](https://github.com/smartdevicelink/sdl_java_suite/issues/1268)
-- [[SDL 0193] Update SDL-Android minimum SDK](https://github.com/smartdevicelink/sdl_java_suite/issues/835)
+- [[SDL 0292] Improve VirtualDisplayEncoder for stable frame rate](https://github.com/smartdevicelink/sdl_java_suite/issues/1361)
-- [Refactor Text & Graphic Manager](https://github.com/smartdevicelink/sdl_java_suite/issues/1464)
+- [[SDL 0255] Enhance BodyInformation vehicle data ](https://github.com/smartdevicelink/sdl_java_suite/issues/1234)
-- [Refactor session and protocol interfaces](https://github.com/smartdevicelink/sdl_java_suite/pull/1430)
+- [[SDL 0244] Custom Playback Rates for SetMediaClockTimer](https://github.com/smartdevicelink/sdl_java_suite/issues/1147)
-- [Remove SdlProxy classes](https://github.com/smartdevicelink/sdl_java_suite/pull/1471)
+- [Refactor FileManager to use queues ](https://github.com/smartdevicelink/sdl_java_suite/issues/1572)
-- [Remove FrameData Class](https://github.com/smartdevicelink/sdl_java_suite/pull/1466)
+- [Taskmaster should be updated to the latest version](https://github.com/smartdevicelink/sdl_java_suite/issues/1589)
-- [Two StreamPacketizer classes](https://github.com/smartdevicelink/sdl_java_suite/issues/1272)
+- [Refactor VoiceCommandManager to use queues](https://github.com/smartdevicelink/sdl_java_suite/issues/1549)
-- [SdlFile in javaSE should have a constructor that takes URI as file source](https://github.com/smartdevicelink/sdl_java_suite/issues/1469)
+- [[SDL 0323] - Align video streaming parameters with VideoStreamingCapability](https://github.com/smartdevicelink/sdl_java_suite/issues/1569)
-- [The default value for overwrite property in SdlFile should be false to align with iOS ](https://github.com/smartdevicelink/sdl_java_suite/issues/1451)
+- [[SDL 0285] ShowConstantTBT Description Update](https://github.com/smartdevicelink/sdl_java_suite/issues/1573)
-- [Deprecated RPC APIs that don't exist in the spec should be removed ](https://github.com/smartdevicelink/sdl_java_suite/issues/1446)
-- [[SDL 0200] - Removing URL Parameter Max Length](https://github.com/smartdevicelink/sdl_java_suite/issues/906)
-
-- [Integration validator](https://github.com/smartdevicelink/sdl_java_suite/pull/1436)
+## Bug Fixes:
-- [Gradle dependencies needs to be updates](https://github.com/smartdevicelink/sdl_java_suite/issues/1459)
+- [Multiframe encryption is not implemented correctly](https://github.com/smartdevicelink/sdl_java_suite/issues/1642)
-- [BSON library should be updated to the latest version (1.2.2)](https://github.com/smartdevicelink/sdl_java_suite/issues/1542)
+- [Empty string bug with Menu Cell](https://github.com/smartdevicelink/sdl_java_suite/issues/1623)
-- [Update Project Documentation in README](https://github.com/smartdevicelink/sdl_java_suite/issues/1534)
+- [CRC is not set for PutFile in FileManager](https://github.com/smartdevicelink/sdl_java_suite/issues/1622)
-- [Feature/lockscreen ui testing](https://github.com/smartdevicelink/sdl_java_suite/pull/1527)
+- [Fix spelling of "auxillary" in StaticIconName.java](https://github.com/smartdevicelink/sdl_java_suite/issues/1632)
-## Bug Fixes:
+- [Main Menu UI Updates Don't Take TextFieldName / ImageFieldName into account](https://github.com/smartdevicelink/sdl_java_suite/issues/1628)
-- [LockScreen behavior is incorrect for Display Always and Dismissible](https://github.com/smartdevicelink/sdl_java_suite/issues/1515)
+- [Choice set default timeout needs to have the same behavior as the AlertManager and needs to be aligned with iOS](https://github.com/smartdevicelink/sdl_java_suite/issues/1630)
-- [NPE with SoftButtonCapabilities using Core](https://github.com/smartdevicelink/sdl_java_suite/issues/1499)
+- [@deprecated annotation in class Javadoc makes the whole class marked as deprecated ](https://github.com/smartdevicelink/sdl_java_suite/issues/1626)
-- [NPE Enum doesn't exist](https://github.com/smartdevicelink/sdl_java_suite/issues/1495)
+- [SDL failed to start video streaming if users enable WiFi on the head unit after the NaviAPP is activated](https://github.com/smartdevicelink/sdl_java_suite/issues/1235)
-- [NullPointerException While updating Image/Text after layout Change.](https://github.com/smartdevicelink/sdl_java_suite/issues/1465)
+- [SDL failed to start video streaming if users enable WiFi on the phone later 150s after the NaviAPP is activated](https://github.com/smartdevicelink/sdl_java_suite/issues/1236)
-- [javaSE and javaEE links in the readme file don't reference the latest version of the library ](https://github.com/smartdevicelink/sdl_java_suite/issues/1449)
+- [Missing permission check in IntegrationValidator](https://github.com/smartdevicelink/sdl_java_suite/issues/1615)
-- [ScreenManager doesn't work when app register on backup transport](https://github.com/smartdevicelink/sdl_java_suite/issues/1518)
+- [OnRPCNotificationListener for ON_Button_Press JavaSE ](https://github.com/smartdevicelink/sdl_java_suite/issues/1397)
-- [Difficult to import sdl_android as source library](https://github.com/smartdevicelink/sdl_java_suite/issues/1048)
+- [Remove Refresh Handler from SdlRemoteDisplay](https://github.com/smartdevicelink/sdl_java_suite/issues/858)
-- [Some cloud apps fail to download icons ](https://github.com/smartdevicelink/sdl_java_suite/issues/1513)
+- [New thread is started for every decode operation in AudioDecoderCompat](https://github.com/smartdevicelink/sdl_java_suite/issues/1596)
-- [ANRs (Application Not Responding) occur when switching from App1 to App2 on HU.](https://github.com/smartdevicelink/sdl_java_suite/issues/1398)
+- [Tests fail because current code state doesn't match MOBILE_API](https://github.com/smartdevicelink/sdl_java_suite/issues/1597)
-- [Lists with initial values of null are not returned properly in RPCs](https://github.com/smartdevicelink/sdl_java_suite/issues/1473)
+- [Only auto-overwrite non-persistent files on RPC v4.3 and below](https://github.com/smartdevicelink/sdl_java_suite/issues/1580)
-- [ClassCastException when calling RPC getter method returning Float type](https://github.com/smartdevicelink/sdl_java_suite/issues/1407)
+- [Avoid re-uploading blank artwork](https://github.com/smartdevicelink/sdl_java_suite/issues/1579)
-- [TransportManager.exitLegacyMode () : NullPointerException ](https://github.com/smartdevicelink/sdl_java_suite/issues/1412)
+- [OnServiceError for Nav and PCM is called even if no service from that type is started](https://github.com/smartdevicelink/sdl_java_suite/issues/1574)
-- [RPC Generator is not generating the corresponding Javadoc for min & max attributes ](https://github.com/smartdevicelink/sdl_java_suite/issues/1438)
+- [Setting a `SDLArtwork`'s `overwrite` property to true does not work with screen manager](https://github.com/smartdevicelink/sdl_java_suite/issues/1346)
-- [RPC Generator is generating enum values with wrong capitalization](https://github.com/smartdevicelink/sdl_java_suite/issues/1425)
+- [Supporting devices with SDL 2.0.0](https://github.com/smartdevicelink/sdl_java_suite/issues/1541)
-- [RPC generator strips text after @TODO](https://github.com/smartdevicelink/sdl_java_suite/issues/1506)
+- [Gradle 4.1 compatibility issues ](https://github.com/smartdevicelink/sdl_java_suite/issues/1551)
-- [Some RPCs in the code do not exactly match the spec](https://github.com/smartdevicelink/sdl_java_suite/issues/1545)
+- [BitConverter NullPointerExeptions](https://github.com/smartdevicelink/sdl_java_suite/issues/193)
-- [Fix issue in setting MediaTrack in the T&G manager](https://github.com/smartdevicelink/sdl_java_suite/pull/1544)
+- [HelloSDL JavaEE 5.1 updates](https://github.com/smartdevicelink/sdl_java_suite/issues/1639)
-- [There are a few deprecated functions that don't link to the correct function](https://github.com/smartdevicelink/sdl_java_suite/issues/1536)
-- [Some cloud apps fail to download icons](https://github.com/smartdevicelink/sdl_java_suite/issues/1513)
-
-- [Fix inconsistency with HMICapabilities API names](https://github.com/smartdevicelink/sdl_java_suite/pull/1508)
-- [LockScreen behavior is incorrect for Display Always and Dismissible](https://github.com/smartdevicelink/sdl_java_suite/issues/1515)
diff --git a/README.md b/README.md
index 9ca1f7014..47ffcc3f0 100644
--- a/README.md
+++ b/README.md
@@ -29,7 +29,7 @@ Contents and timing for SDL Java Suite releases can be tracked on the [GitHub Pr
Additional information about recent and upcoming SDL Releases can be found in the [SDL Evolution README](https://github.com/smartdevicelink/sdl_evolution#recent-and-upcoming-releases).
## SmartDeviceLink Android
-[ ![Download Android](https://api.bintray.com/packages/smartdevicelink/sdl_android/sdl_android/images/download.svg) ](https://bintray.com/smartdevicelink/sdl_android/sdl_android/_latestVersion)
+[ ![Download Android](https://api.bintray.com/packages/smartdevicelink/sdl_android/sdl_android/images/download.svg) ](https://search.maven.org/artifact/com.smartdevicelink/sdl_android/5.1.0/aar)
### Installation
@@ -39,14 +39,14 @@ To compile with the latest release of SDL Android, include the following in your
```sh
repositories {
- jcenter()
+ mavenCentral()
}
dependencies {
- implementation 'com.smartdevicelink:sdl_android:4.+'
+ implementation 'com.smartdevicelink:sdl_android:5.1.0'
}
```
-For Maven or Ivy snippets please look at [Bintray](https://bintray.com/smartdevicelink/sdl_android/sdl_android)
+For Maven or Ivy snippets please look at [Maven Central](https://search.maven.org/artifact/com.smartdevicelink/sdl_android)
#### Manually
@@ -67,7 +67,7 @@ Developers using Proguard to shrink and obfuscate their code should be sure to i
### JavaSE
-[ ![Download JavaSE](https://api.bintray.com/packages/smartdevicelink/sdl_java_se/sdl_javase/images/download.svg) ](https://bintray.com/smartdevicelink/sdl_java_se/sdl_javase/_latestVersion)
+[ ![Download JavaSE](https://api.bintray.com/packages/smartdevicelink/sdl_java_se/sdl_javase/images/download.svg) ](https://search.maven.org/artifact/com.smartdevicelink/sdl_java_se/5.1.0/jar)
The JavaSE project is meant to allow SDL compatibility for embedded applications.
@@ -77,16 +77,16 @@ To compile with the latest release of SDL JavaSE, include the following in your
```sh
repositories {
- jcenter()
+ mavenCentral()
}
dependencies {
- implementation 'com.smartdevicelink:sdl_java_se:4.+'
+ implementation 'com.smartdevicelink:sdl_java_se:5.1.0'
}
```
### JavaEE
-[ ![Download JavaEE](https://api.bintray.com/packages/smartdevicelink/sdl_java_ee/sdl_javaee/images/download.svg) ](https://bintray.com/smartdevicelink/sdl_java_ee/sdl_javaee/_latestVersion)
+[ ![Download JavaEE](https://api.bintray.com/packages/smartdevicelink/sdl_java_ee/sdl_javaee/images/download.svg) ](https://search.maven.org/artifact/com.smartdevicelink/sdl_java_ee/5.1.0/jar)
The JavaEE project is meant to allow SDL compatibility for web applications.
@@ -96,10 +96,10 @@ To compile with the latest release of SDL JavaEE, include the following in your
```sh
repositories {
- jcenter()
+ mavenCentral()
}
dependencies {
- implementation 'com.smartdevicelink:sdl_java_ee:4.+'
+ implementation 'com.smartdevicelink:sdl_java_ee:5.1.0'
}
```
diff --git a/VERSION b/VERSION
index 0062ac971..831446cbd 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-5.0.0
+5.1.0
diff --git a/android/README.md b/android/README.md
index e6a89c85e..822534f98 100644
--- a/android/README.md
+++ b/android/README.md
@@ -10,10 +10,10 @@ To compile with the latest release of SDL Android, include the following in your
```
repositories {
- jcenter()
+ mavenCentral()
}
dependencies {
- implementation 'com.smartdevicelink:sdl_android:4.+'
+ implementation 'com.smartdevicelink:sdl_android:5.1.0'
}
```
diff --git a/android/build.gradle b/android/build.gradle
index 7ecff6a6a..e18f58322 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:4.0.1'
+ classpath 'com.android.tools.build:gradle:4.1.1'
// NOTE: Do not place your application dependencies here; they belong
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 6691197d2..d62774b23 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Aug 19 13:26:39 EDT 2020
+#Fri Nov 06 10:04:32 EST 2020
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
diff --git a/android/hello_sdl_android/build.gradle b/android/hello_sdl_android/build.gradle
index 6270e028d..7640e89c5 100755
--- a/android/hello_sdl_android/build.gradle
+++ b/android/hello_sdl_android/build.gradle
@@ -1,11 +1,11 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 29
+ compileSdkVersion 30
defaultConfig {
applicationId "com.sdl.hellosdlandroid"
minSdkVersion 16
- targetSdkVersion 29
+ targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
diff --git a/android/hello_sdl_android/src/main/AndroidManifest.xml b/android/hello_sdl_android/src/main/AndroidManifest.xml
index 289b4c7e7..1183e7b2c 100755
--- a/android/hello_sdl_android/src/main/AndroidManifest.xml
+++ b/android/hello_sdl_android/src/main/AndroidManifest.xml
@@ -12,6 +12,16 @@
<!-- Required to use the USB Accessory mode -->
<uses-feature android:name="android.hardware.usb.accessory" />
+ <!-- Required when targeting Android API 30+ -->
+ <queries>
+ <intent>
+ <action android:name="com.smartdevicelink.router.service" />
+ </intent>
+ <intent>
+ <action android:name="sdl.router.startservice" />
+ </intent>
+ </queries>
+
<application
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
diff --git a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java
index 0b83e0842..c6e9ce3ef 100755
--- a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java
+++ b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java
@@ -11,11 +11,13 @@ import android.os.Build;
import android.os.IBinder;
import android.util.Log;
+import com.smartdevicelink.managers.AlertCompletionListener;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.SdlManager;
import com.smartdevicelink.managers.SdlManagerListener;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.managers.lifecycle.LifecycleConfigurationUpdate;
+import com.smartdevicelink.managers.screen.AlertView;
import com.smartdevicelink.managers.screen.OnButtonListener;
import com.smartdevicelink.managers.screen.choiceset.ChoiceCell;
import com.smartdevicelink.managers.screen.choiceset.ChoiceSet;
@@ -26,7 +28,6 @@ import com.smartdevicelink.managers.screen.menu.VoiceCommand;
import com.smartdevicelink.managers.screen.menu.VoiceCommandSelectionListener;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCNotification;
-import com.smartdevicelink.proxy.rpc.Alert;
import com.smartdevicelink.proxy.rpc.OnButtonEvent;
import com.smartdevicelink.proxy.rpc.OnButtonPress;
import com.smartdevicelink.proxy.rpc.OnHMIStatus;
@@ -47,6 +48,7 @@ import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.transport.MultiplexTransportConfig;
import com.smartdevicelink.transport.TCPTransportConfig;
import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.SystemInfo;
import java.util.ArrayList;
import java.util.Arrays;
@@ -239,6 +241,12 @@ public class SdlService extends Service {
return null;
}
}
+
+ @Override
+ public boolean onSystemInfoReceived(SystemInfo systemInfo) {
+ //Check the SystemInfo object to ensure that the connection to the device should continue
+ return true;
+ }
};
// Create App Icon, this is set in the SdlManager builder
@@ -290,7 +298,7 @@ public class SdlService extends Service {
// some voice commands
List<String> voice2 = Collections.singletonList("Cell two");
- MenuCell mainCell1 = new MenuCell("Test Cell 1 (speak)", livio, null, new MenuSelectionListener() {
+ MenuCell mainCell1 = new MenuCell("Test Cell 1 (speak)", "Secondary Text", "Tertiary Text", livio, livio, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
Log.i(TAG, "Test cell 1 triggered. Source: " + trigger.toString());
@@ -298,7 +306,7 @@ public class SdlService extends Service {
}
});
- MenuCell mainCell2 = new MenuCell("Test Cell 2", null, voice2, new MenuSelectionListener() {
+ MenuCell mainCell2 = new MenuCell("Test Cell 2", "Secondary Text", null, null, null, voice2, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
Log.i(TAG, "Test cell 2 triggered. Source: " + trigger.toString());
@@ -307,14 +315,14 @@ public class SdlService extends Service {
// SUB MENU
- MenuCell subCell1 = new MenuCell("SubCell 1", null, null, new MenuSelectionListener() {
+ MenuCell subCell1 = new MenuCell("SubCell 1", null, null, null, null, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
Log.i(TAG, "Sub cell 1 triggered. Source: " + trigger.toString());
}
});
- MenuCell subCell2 = new MenuCell("SubCell 2", null, null, new MenuSelectionListener() {
+ MenuCell subCell2 = new MenuCell("SubCell 2", null, null, null, null, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
Log.i(TAG, "Sub cell 2 triggered. Source: " + trigger.toString());
@@ -322,16 +330,16 @@ public class SdlService extends Service {
});
// sub menu parent cell
- MenuCell mainCell3 = new MenuCell("Test Cell 3 (sub menu)", MenuLayout.LIST, null, Arrays.asList(subCell1, subCell2));
+ MenuCell mainCell3 = new MenuCell("Test Cell 3 (sub menu)", null, null, MenuLayout.LIST, null, null, Arrays.asList(subCell1, subCell2));
- MenuCell mainCell4 = new MenuCell("Show Perform Interaction", null, null, new MenuSelectionListener() {
+ MenuCell mainCell4 = new MenuCell("Show Perform Interaction", null, null, null, null, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
showPerformInteraction();
}
});
- MenuCell mainCell5 = new MenuCell("Clear the menu", null, null, new MenuSelectionListener() {
+ MenuCell mainCell5 = new MenuCell("Clear the menu", null, null, null,null, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
Log.i(TAG, "Clearing Menu. Source: " + trigger.toString());
@@ -417,10 +425,16 @@ public class SdlService extends Service {
}
private void showAlert(String text) {
- Alert alert = new Alert();
- alert.setAlertText1(text);
- alert.setDuration(5000);
- sdlManager.sendRPC(alert);
+ AlertView.Builder builder = new AlertView.Builder();
+ builder.setText(text);
+ builder.setTimeout(5);
+ AlertView alertView = builder.build();
+ sdlManager.getScreenManager().presentAlert(alertView, new AlertCompletionListener() {
+ @Override
+ public void onComplete(boolean success, Integer tryAgainTime) {
+ Log.i(TAG, "Alert presented: "+ success);
+ }
+ });
}
// Choice Set
diff --git a/android/sdl_android/build.gradle b/android/sdl_android/build.gradle
index aeda52ab6..46c1221ba 100644
--- a/android/sdl_android/build.gradle
+++ b/android/sdl_android/build.gradle
@@ -1,14 +1,15 @@
apply plugin: 'com.android.library'
android {
- compileSdkVersion 29
+ compileSdkVersion 30
defaultConfig {
minSdkVersion 16
- targetSdkVersion 29
- versionCode 17
+ targetSdkVersion 30
+ versionCode 18
versionName new File(projectDir.path, ('/../../VERSION')).text.trim()
- testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
+ buildConfigField "String", "VERSION_NAME", '\"' + versionName + '\"'
resValue "string", "SDL_LIB_VERSION", '\"' + versionName + '\"'
+ testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}
buildTypes {
release {
@@ -41,8 +42,8 @@ android {
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
- api 'com.smartdevicelink:bson_java_port:1.2.2'
- api 'com.livio.taskmaster:taskmaster:0.3.0'
+ //api 'com.livio.taskmaster:taskmaster:0.4.0'
+ api 'com.smartdevicelink:bson_java_port:1.2.4'
api 'androidx.lifecycle:lifecycle-extensions:2.2.0'
api 'androidx.annotation:annotation:1.1.0'
annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.2.0'
diff --git a/android/sdl_android/libs/TaskMaster-0.5.jar b/android/sdl_android/libs/TaskMaster-0.5.jar
new file mode 100644
index 000000000..a7881051b
--- /dev/null
+++ b/android/sdl_android/libs/TaskMaster-0.5.jar
Binary files differ
diff --git a/android/sdl_android/src/androidTest/assets/json/AddCommand.json b/android/sdl_android/src/androidTest/assets/json/AddCommand.json
index c8dfe5267..88f3cdf4c 100644
--- a/android/sdl_android/src/androidTest/assets/json/AddCommand.json
+++ b/android/sdl_android/src/androidTest/assets/json/AddCommand.json
@@ -17,6 +17,10 @@
"parentID": 194,
"position": 1,
"menuName": "My Command"
+ },
+ "secondaryImage": {
+ "value": "cmdImage1.png",
+ "imageType": "DYNAMIC"
}
}
},
diff --git a/android/sdl_android/src/androidTest/assets/json/AddSubMenu.json b/android/sdl_android/src/androidTest/assets/json/AddSubMenu.json
index 9b5cbd11b..2c9a7e326 100644
--- a/android/sdl_android/src/androidTest/assets/json/AddSubMenu.json
+++ b/android/sdl_android/src/androidTest/assets/json/AddSubMenu.json
@@ -9,6 +9,12 @@
"menuIcon": {
"value": "cmdImage1.png",
"imageType": "DYNAMIC"
+ },
+ "secondaryText": "Secondary Text",
+ "tertiaryText": "Tertiary Text",
+ "secondaryImage": {
+ "value": "cmdImage1.png",
+ "imageType": "DYNAMIC"
}
}
},
diff --git a/android/sdl_android/src/androidTest/assets/json/GetVehicleData.json b/android/sdl_android/src/androidTest/assets/json/GetVehicleData.json
index b31a0e4db..8ce24a63e 100644
--- a/android/sdl_android/src/androidTest/assets/json/GetVehicleData.json
+++ b/android/sdl_android/src/androidTest/assets/json/GetVehicleData.json
@@ -35,7 +35,9 @@
"gearStatus": true,
"oemCustomVehicleDataName": true,
"windowStatus": true,
- "stabilityControlsStatus": true
+ "stabilityControlsStatus": true,
+ "climateData": true,
+ "seatOccupancy": true
}
},
"response": {
@@ -183,6 +185,17 @@
"transmissionType": "ELECTRIC_VARIABLE",
"actualGear": "PARK"
},
+ "climateData": {
+ "atmosphericPressure": 100.0,
+ "externalTemperature": {
+ "unit": "CELSIUS",
+ "value": 100.0
+ },
+ "cabinTemperature": {
+ "unit": "CELSIUS",
+ "value": 100.0
+ }
+ },
"windowStatus": [
{
"location": {
@@ -194,7 +207,31 @@
"deviation": 3
}
}
- ]
+ ],
+ "seatOccupancy": {
+ "seatsOccupied": [
+ {
+ "seatLocation": {
+ "grid": {
+ "row": 2,
+ "col": 3
+ }
+ },
+ "conditionActive": true
+ }
+ ],
+ "seatsBelted": [
+ {
+ "seatLocation": {
+ "grid": {
+ "row": 2,
+ "col": 3
+ }
+ },
+ "conditionActive": true
+ }
+ ]
+ }
}
}
} \ No newline at end of file
diff --git a/android/sdl_android/src/androidTest/assets/json/SubscribeVehicleData.json b/android/sdl_android/src/androidTest/assets/json/SubscribeVehicleData.json
index 51123213c..57a8aa20b 100644
--- a/android/sdl_android/src/androidTest/assets/json/SubscribeVehicleData.json
+++ b/android/sdl_android/src/androidTest/assets/json/SubscribeVehicleData.json
@@ -35,7 +35,9 @@
"gearStatus": true,
"oemCustomVehicleDataName": true,
"windowStatus": true,
- "handsOffSteering": true
+ "handsOffSteering": true,
+ "climateData": true,
+ "seatOccupancy": true
}
},
"response": {
@@ -173,6 +175,14 @@
"windowStatus": {
"dataType": "VEHICLEDATA_WINDOWSTATUS",
"resultCode": "SUCCESS"
+ },
+ "climateData": {
+ "dataType": "VEHICLEDATA_CLIMATEDATA",
+ "resultCode": "SUCCESS"
+ },
+ "seatOccupancy": {
+ "dataType": "VEHICLEDATA_SEATOCCUPANCY",
+ "resultCode": "SUCCESS"
}
}
}
diff --git a/android/sdl_android/src/androidTest/assets/json/UnsubscribeVehicleData.json b/android/sdl_android/src/androidTest/assets/json/UnsubscribeVehicleData.json
index 7f57869a5..3dfbf984c 100644
--- a/android/sdl_android/src/androidTest/assets/json/UnsubscribeVehicleData.json
+++ b/android/sdl_android/src/androidTest/assets/json/UnsubscribeVehicleData.json
@@ -35,7 +35,9 @@
"handsOffSteering": true,
"gearStatus": true,
"oemCustomVehicleDataName": true,
- "windowStatus": true
+ "windowStatus": true,
+ "climateData": true,
+ "seatOccupancy": true
}
},
"response": {
@@ -173,6 +175,14 @@
"windowStatus": {
"dataType": "VEHICLEDATA_WINDOWSTATUS",
"resultCode": "SUCCESS"
+ },
+ "seatOccupancy": {
+ "dataType": "VEHICLEDATA_SEATOCCUPANCY",
+ "resultCode": "SUCCESS"
+ },
+ "climateData": {
+ "dataType": "VEHICLEDATA_CLIMATEDATA",
+ "resultCode": "SUCCESS"
}
}
}
diff --git a/android/sdl_android/src/androidTest/java/com/android/grafika/gles/OffscreenSurfaceTest.java b/android/sdl_android/src/androidTest/java/com/android/grafika/gles/OffscreenSurfaceTest.java
new file mode 100644
index 000000000..2f93c8307
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/android/grafika/gles/OffscreenSurfaceTest.java
@@ -0,0 +1,105 @@
+package com.android.grafika.gles;
+
+import android.opengl.GLES20;
+import android.os.Environment;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import android.util.Log;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+import static junit.framework.TestCase.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+public class OffscreenSurfaceTest {
+
+ private final String TAG = OffscreenSurfaceTest.class.getSimpleName();
+ private final int mWidth = 1280;
+ private final int mHeight = 720;
+ private final int mIterations = 100;
+
+ @Test
+ public void testReadPixels() {
+ EglCore eglCore = new EglCore(null, 0);
+ OffscreenSurface offscreenSurface = new OffscreenSurface(eglCore, mWidth, mHeight);
+ float time = runReadPixelsTest(offscreenSurface);
+ Log.d(TAG, "runReadPixelsTest returns " + time + " msec");
+ }
+
+ // HELPER test method
+ /**
+ * Does a simple bit of rendering and then reads the pixels back.
+ *
+ * @return total time (msec order) spent on glReadPixels()
+ */
+ private float runReadPixelsTest(OffscreenSurface eglSurface) {
+ long totalTime = 0;
+
+ eglSurface.makeCurrent();
+
+ ByteBuffer pixelBuf = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
+ pixelBuf.order(ByteOrder.LITTLE_ENDIAN);
+
+ Log.d(TAG, "Running...");
+ float colorMult = 1.0f / mIterations;
+ for (int i = 0; i < mIterations; i++) {
+ if ((i % (mIterations / 8)) == 0) {
+ Log.d(TAG, "iteration " + i);
+ }
+
+ // Clear the screen to a solid color, then add a rectangle. Change the color
+ // each time.
+ float r = i * colorMult;
+ float g = 1.0f - r;
+ float b = (r + g) / 2.0f;
+ GLES20.glClearColor(r, g, b, 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+
+ GLES20.glEnable(GLES20.GL_SCISSOR_TEST);
+ GLES20.glScissor(mWidth / 4, mHeight / 4, mWidth / 2, mHeight / 2);
+ GLES20.glClearColor(b, g, r, 1.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
+
+ // Try to ensure that rendering has finished.
+ GLES20.glFinish();
+ GLES20.glReadPixels(0, 0, 1, 1,
+ GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixelBuf);
+
+ // Time individual extraction. Ideally we'd be timing a bunch of these calls
+ // and measuring the aggregate time, but we want the isolated time, and if we
+ // just read the same buffer repeatedly we might get some sort of cache effect.
+ long startWhen = System.nanoTime();
+ GLES20.glReadPixels(0, 0, mWidth, mHeight,
+ GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, pixelBuf);
+ totalTime += System.nanoTime() - startWhen;
+ }
+ Log.d(TAG, "done");
+
+ // It's not the good idea to request external strage permission in unit test.
+ boolean requireStoragePermission = false;
+ if (requireStoragePermission) {
+ long startWhen = System.nanoTime();
+ File file = new File(Environment.getExternalStorageDirectory(),
+ "test.png");
+ try {
+ eglSurface.saveFrame(file);
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ Log.d(TAG, "Saved frame in " + ((System.nanoTime() - startWhen) / 1000000) + "ms");
+ assertTrue(file.exists());
+ } else {
+ // here' we can recognize Unit Test succeeded, but anyway checks to see totalTime and buffer capacity.
+ assertTrue(pixelBuf.capacity() > 0 && totalTime > 0);
+ }
+
+ return (float)totalTime / 1000000f;
+ }
+
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/AlertViewTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/AlertViewTest.java
new file mode 100644
index 000000000..041d0bbed
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/AlertViewTest.java
@@ -0,0 +1,78 @@
+package com.smartdevicelink;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.managers.screen.AlertAudioData;
+import com.smartdevicelink.managers.screen.AlertView;
+import com.smartdevicelink.managers.screen.SoftButtonObject;
+import com.smartdevicelink.managers.screen.SoftButtonState;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+@RunWith(AndroidJUnit4.class)
+public class AlertViewTest {
+
+ @Test
+ public void testAlertView() {
+ SdlArtwork artwork1 = new SdlArtwork("test1", FileType.GRAPHIC_PNG, 1, true);
+ SdlArtwork artwork2 = new SdlArtwork("test2", FileType.GRAPHIC_PNG, 2, true);
+
+ SoftButtonState softButtonState1 = new SoftButtonState("object1-state1", "o1s1", new SdlArtwork("image1", FileType.GRAPHIC_PNG, 3, true));
+ SoftButtonObject softButtonObject1 = new SoftButtonObject("object1", Arrays.asList(softButtonState1), softButtonState1.getName(), null);
+ SoftButtonObject softButtonObject2 = new SoftButtonObject("object2", Arrays.asList(softButtonState1), softButtonState1.getName(), null);
+
+ AlertAudioData alertAudioData = new AlertAudioData("hi");
+
+ AlertView.Builder builder = new AlertView.Builder();
+ builder.setText("Test");
+ builder.setTertiaryText("Test");
+ builder.setSecondaryText("Test");
+ builder.setTimeout(1);
+ builder.setIcon(artwork1);
+ builder.setSoftButtons(Collections.singletonList(softButtonObject1));
+ builder.setDefaultTimeOut(3);
+ builder.setAudio(alertAudioData);
+ builder.setShowWaitIndicator(true);
+ AlertView alertView = builder.build();
+
+ assertEquals(alertView.getText(), "Test");
+ assertEquals(alertView.getSecondaryText(), "Test");
+ assertEquals(alertView.getTertiaryText(), "Test");
+ assertTrue(alertView.getAudio().getAudioData().size() > 0);
+ assertEquals(alertView.getIcon().getName(), "test1");
+ assertEquals(alertView.getSoftButtons().get(0).getName(), "object1");
+ assertEquals(alertView.getDefaultTimeout(), 3);
+ assertEquals(alertView.getTimeout().intValue(), 3);
+ assertEquals(alertView.isShowWaitIndicator(), true);
+
+ alertView.setText("Test2");
+ alertView.setTertiaryText("Test2");
+ alertView.setSecondaryText("Test2");
+ alertView.setDefaultTimeout(6);
+ alertView.setTimeout(6);
+ alertView.setAudio(alertAudioData);
+ alertView.setIcon(artwork2);
+ alertView.setSoftButtons(Collections.singletonList(softButtonObject2));
+ alertView.setShowWaitIndicator(false);
+
+ assertEquals(alertView.getText(), "Test2");
+ assertEquals(alertView.getSecondaryText(), "Test2");
+ assertEquals(alertView.getTertiaryText(), "Test2");
+ assertTrue(alertView.getAudio().getAudioData().size() > 0);
+ assertEquals(alertView.getIcon().getName(), "test2");
+ assertEquals(alertView.getSoftButtons().get(0).getName(), "object2");
+ assertEquals(alertView.getDefaultTimeout(), 6);
+ assertEquals(alertView.getTimeout().intValue(), 6);
+ assertEquals(alertView.isShowWaitIndicator(), false);
+
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java
index c263db59d..db9ce11ac 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java
@@ -7,6 +7,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.lifecycle.LifecycleConfigurationUpdate;
import com.smartdevicelink.managers.lockscreen.LockScreenConfig;
+import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCMessage;
import com.smartdevicelink.proxy.RPCRequest;
@@ -23,6 +24,7 @@ import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
import com.smartdevicelink.test.TestValues;
import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.transport.TCPTransportConfig;
+import com.smartdevicelink.util.SystemInfo;
import org.junit.Before;
import org.junit.Test;
@@ -114,6 +116,11 @@ public class SdlManagerTests {
public LifecycleConfigurationUpdate managerShouldUpdateLifecycle(Language language, Language hmiLanguage) {
return null;
}
+
+ @Override
+ public boolean onSystemInfoReceived(SystemInfo systemInfo) {
+ return true;
+ }
};
// build manager object - use all setters, will test using getters below
@@ -134,6 +141,10 @@ public class SdlManagerTests {
// mock internalInterface and set it manually
internalInterface = mock(ISdl.class);
+ manager.set_internalInterface(internalInterface);
+ PermissionManager permissionManager = mock(PermissionManager.class);
+
+ when(internalInterface.getPermissionManager()).thenReturn(permissionManager);
when(internalInterface.getTaskmaster()).thenReturn(new Taskmaster.Builder().build());
manager._internalInterface = internalInterface;
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/audio/AudioStreamManagerTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/audio/AudioStreamManagerTest.java
index ce8183b9d..d81b9ccc6 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/audio/AudioStreamManagerTest.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/audio/AudioStreamManagerTest.java
@@ -9,6 +9,7 @@ import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.audio.AudioStreamManager.SampleType;
@@ -50,6 +51,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
public class AudioStreamManagerTest extends TestCase {
public static final String TAG = AudioStreamManagerTest.class.getSimpleName();
@@ -107,7 +109,7 @@ public class AudioStreamManagerTest extends TestCase {
}
};
- ISdl internalInterface = mock(ISdl.class);
+ ISdl internalInterface = createISdlMock();
SystemCapabilityManager systemCapabilityManager = mock(SystemCapabilityManager.class);
doReturn(systemCapabilityManager).when(internalInterface).getSystemCapabilityManager();
AudioPassThruCapabilities audioCapabilities = new AudioPassThruCapabilities(SamplingRate._16KHZ, BitsPerSample._16_BIT, AudioType.PCM);
@@ -299,7 +301,7 @@ public class AudioStreamManagerTest extends TestCase {
}
};
- ISdl internalInterface = mock(ISdl.class);
+ ISdl internalInterface = createISdlMock();
SystemCapabilityManager systemCapabilityManager = mock(SystemCapabilityManager.class);
doReturn(systemCapabilityManager).when(internalInterface).getSystemCapabilityManager();
doReturn(true).when(internalInterface).isConnected();
@@ -530,7 +532,7 @@ public class AudioStreamManagerTest extends TestCase {
}
};
- ISdl internalInterface = mock(ISdl.class);
+ ISdl internalInterface = createISdlMock();
SystemCapabilityManager systemCapabilityManager = mock(SystemCapabilityManager.class);
doReturn(systemCapabilityManager).when(internalInterface).getSystemCapabilityManager();
doReturn(true).when(internalInterface).isConnected();
@@ -609,7 +611,7 @@ public class AudioStreamManagerTest extends TestCase {
}
};
- ISdl internalInterface = mock(ISdl.class);
+ ISdl internalInterface = createISdlMock();
SystemCapabilityManager systemCapabilityManager = mock(SystemCapabilityManager.class);
doReturn(systemCapabilityManager).when(internalInterface).getSystemCapabilityManager();
doReturn(true).when(internalInterface).isConnected();
@@ -747,4 +749,13 @@ public class AudioStreamManagerTest extends TestCase {
stream.write((int) ((audiolength >> 16) & 0xff));
stream.write((int) ((audiolength >> 24) & 0xff));
}
+
+ private ISdl createISdlMock() {
+ ISdl internalInterface = mock(ISdl.class);
+ Taskmaster taskmaster = new Taskmaster.Builder().build();
+ taskmaster.start();
+
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
+ return internalInterface;
+ }
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java
index e45d455e0..90bedd434 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java
@@ -2,14 +2,17 @@ package com.smartdevicelink.managers.file;
import android.content.Context;
import android.net.Uri;
+import android.os.Handler;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.proxy.RPCMessage;
import com.smartdevicelink.proxy.RPCRequest;
import com.smartdevicelink.proxy.rpc.DeleteFile;
@@ -18,11 +21,12 @@ import com.smartdevicelink.proxy.rpc.ListFiles;
import com.smartdevicelink.proxy.rpc.ListFilesResponse;
import com.smartdevicelink.proxy.rpc.PutFile;
import com.smartdevicelink.proxy.rpc.PutFileResponse;
+import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
import com.smartdevicelink.proxy.rpc.enums.FileType;
import com.smartdevicelink.proxy.rpc.enums.Result;
import com.smartdevicelink.proxy.rpc.enums.StaticIconName;
-import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
import com.smartdevicelink.test.TestValues;
+import com.smartdevicelink.util.Version;
import org.junit.Before;
import org.junit.Test;
@@ -30,20 +34,21 @@ import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
-import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
/**
* This is a unit test class for the SmartDeviceLink library manager class :
@@ -54,73 +59,21 @@ public class FileManagerTests {
public static final String TAG = "FileManagerTests";
private Context mTestContext;
private SdlFile validFile;
+ private Handler mainHandler;
+ private Version rpcVersion;
+ private long mtuSize;
// SETUP / HELPERS
@Before
public void setUp() throws Exception {
mTestContext = getInstrumentation().getTargetContext();
- validFile = new SdlFile();
- validFile.setName(TestValues.GENERAL_STRING);
- validFile.setFileData(TestValues.GENERAL_BYTE_ARRAY);
- validFile.setPersistent(false);
+ mainHandler = new Handler(mTestContext.getMainLooper());
+ rpcVersion = new Version(7, 0, 0);
+ mtuSize = 131072;
+ validFile = new SdlFile(TestValues.GENERAL_STRING, FileType.BINARY, TestValues.GENERAL_STRING.getBytes(), false);
}
- private Answer<Void> onPutFileFailureOnError = new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- RPCRequest message = (RPCRequest) args[0];
- if (message instanceof PutFile) {
- int correlationId = message.getCorrelationID();
- PutFileResponse putFileResponse = new PutFileResponse(false, Result.REJECTED);
- putFileResponse.setInfo("Binary data empty");
- message.getOnRPCResponseListener().onResponse(correlationId, putFileResponse);
- }
- return null;
- }
- };
-
- private Answer<Void> onSendRequestsFailOnError = new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- List<RPCRequest> rpcs = (List<RPCRequest>) args[0];
- OnMultipleRequestListener listener = (OnMultipleRequestListener) args[1];
- if (rpcs.get(0) instanceof PutFile) {
- for (RPCRequest message : rpcs) {
- int correlationId = message.getCorrelationID();
- listener.addCorrelationId(correlationId);
- PutFileResponse putFileResponse = new PutFileResponse(false, Result.REJECTED);
- putFileResponse.setInfo("Binary data empty");
- listener.onResponse(correlationId, putFileResponse);
- }
- listener.onFinished();
- }
- return null;
- }
- };
-
- private Answer<Void> onListFileUploadSuccess = new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- List<RPCRequest> rpcs = (List<RPCRequest>) args[0];
- OnMultipleRequestListener listener = (OnMultipleRequestListener) args[1];
- if (rpcs.get(0) instanceof PutFile) {
- for (RPCRequest message : rpcs) {
- int correlationId = message.getCorrelationID();
- listener.addCorrelationId(correlationId);
- PutFileResponse putFileResponse = new PutFileResponse();
- putFileResponse.setSuccess(true);
- listener.onResponse(correlationId, putFileResponse);
- }
- listener.onFinished();
- }
- return null;
- }
- };
-
private Answer<Void> onListFilesSuccess = new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
@@ -169,104 +122,72 @@ public class FileManagerTests {
}
};
- private Answer<Void> onListDeleteRequestSuccess = new Answer<Void>() {
+ private Answer<Void> onDeleteFileSuccess = new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
- List<RPCRequest> rpcs = (List<RPCRequest>) args[0];
- OnMultipleRequestListener listener = (OnMultipleRequestListener) args[1];
- if (rpcs.get(0) instanceof DeleteFile) {
- for (RPCRequest message : rpcs) {
- int correlationId = message.getCorrelationID();
- listener.addCorrelationId(correlationId);
- DeleteFileResponse deleteFileResponse = new DeleteFileResponse();
- deleteFileResponse.setSuccess(true);
- listener.onResponse(correlationId, deleteFileResponse);
- }
- listener.onFinished();
+ RPCRequest message = (RPCRequest) args[0];
+ if (message instanceof DeleteFile) {
+ int correlationId = message.getCorrelationID();
+ DeleteFileResponse deleteFileResponse = new DeleteFileResponse();
+ deleteFileResponse.setSuccess(true);
+ message.getOnRPCResponseListener().onResponse(correlationId, deleteFileResponse);
}
return null;
}
};
- private Answer<Void> onListDeleteRequestFail = new Answer<Void>() {
+ private Answer<Void> onDeleteFileFailure = new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
- List<RPCRequest> rpcs = (List<RPCRequest>) args[0];
- OnMultipleRequestListener listener = (OnMultipleRequestListener) args[1];
- if (rpcs.get(0) instanceof DeleteFile) {
- for (RPCRequest message : rpcs) {
- int correlationId = message.getCorrelationID();
- listener.addCorrelationId(correlationId);
- DeleteFileResponse deleteFileResponse = new DeleteFileResponse(false, Result.REJECTED);
- deleteFileResponse.setInfo("Binary data empty");
- listener.onResponse(correlationId, deleteFileResponse);
- }
- listener.onFinished();
- }
- return null;
- }
- };
-
- private Answer<Void> onSendRequestsFailPartialOnError = new Answer<Void>() {
- @Override
- public Void answer(InvocationOnMock invocation) throws Throwable {
- Object[] args = invocation.getArguments();
- List<RPCRequest> rpcs = (List<RPCRequest>) args[0];
- OnMultipleRequestListener listener = (OnMultipleRequestListener) args[1];
- if (rpcs.get(0) instanceof PutFile) {
- boolean flip = false;
- for (RPCRequest message : rpcs) {
- int correlationId = message.getCorrelationID();
- listener.addCorrelationId(correlationId);
- PutFileResponse putFileResponse = new PutFileResponse();
- if (flip) {
- putFileResponse.setSuccess(true);
- flip = false;
- listener.onResponse(correlationId, putFileResponse);
- } else {
- flip = true;
- putFileResponse.setSuccess(false);
- putFileResponse.setResultCode(Result.REJECTED);
- putFileResponse.setInfo("Binary data empty");
- listener.onResponse(correlationId, putFileResponse);
- }
- }
- listener.onFinished();
+ RPCRequest message = (RPCRequest) args[0];
+ if (message instanceof DeleteFile) {
+ int correlationId = message.getCorrelationID();
+ DeleteFileResponse deleteFileResponse = new DeleteFileResponse(false, Result.REJECTED);
+ deleteFileResponse.setInfo("Binary data empty");
+ message.getOnRPCResponseListener().onResponse(correlationId, deleteFileResponse);
}
return null;
}
};
- // TESTS
-
/**
* Test deleting list of files, success
*/
@Test
public void testDeleteRemoteFilesWithNamesSuccess() {
- final ISdl internalInterface = mock(ISdl.class);
+ final ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
- doAnswer(onListDeleteRequestSuccess).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
+ doAnswer(onDeleteFileSuccess).when(internalInterface).sendRPC(any(DeleteFile.class));
- final List<String> fileNames = new ArrayList<>();
- fileNames.add("Julian");
- fileNames.add("Jake");
+ final List<String> fileNames = Arrays.asList("Julian", "Jake");
FileManagerConfig fileManagerConfig = new FileManagerConfig();
- fileManagerConfig.setFileRetryCount(2);
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
fileManager.start(new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
+ public void onComplete(final boolean success) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(success);
+ }
+ });
+
+ fileManager.mutableRemoteFileNames.addAll(fileNames);
+
fileManager.deleteRemoteFilesWithNames(fileNames, new MultipleFileCompletionListener() {
@Override
- public void onComplete(Map<String, String> errors) {
- assertTrue(errors == null);
+ public void onComplete(final Map<String, String> errors) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertNull(errors);
+ }
+ });
}
});
}
@@ -278,27 +199,36 @@ public class FileManagerTests {
*/
@Test
public void testDeleteRemoteFilesWithNamesFail() {
- final ISdl internalInterface = mock(ISdl.class);
+ final ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
- doAnswer(onListDeleteRequestFail).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
+ doAnswer(onDeleteFileFailure).when(internalInterface).sendRPC(any(DeleteFile.class));
- final List<String> fileNames = new ArrayList<>();
- fileNames.add("Julian");
- fileNames.add("Jake");
+ final List<String> fileNames = Arrays.asList("Julian", "Jake");
FileManagerConfig fileManagerConfig = new FileManagerConfig();
- fileManagerConfig.setFileRetryCount(2);
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
fileManager.start(new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
+ public void onComplete(final boolean success) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(success);
+ }
+ });
+
+ fileManager.mutableRemoteFileNames.addAll(fileNames);
fileManager.deleteRemoteFilesWithNames(fileNames, new MultipleFileCompletionListener() {
@Override
- public void onComplete(Map<String, String> errors) {
- assertTrue(errors.size() == 2);
+ public void onComplete(final Map<String, String> errors) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(2, errors.size());
+ }
+ });
}
});
}
@@ -310,10 +240,10 @@ public class FileManagerTests {
*/
@Test
public void testFileUploadRetry() {
- final ISdl internalInterface = mock(ISdl.class);
+ final ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
- doAnswer(onPutFileFailureOnError).when(internalInterface).sendRPC(any(PutFile.class));
+ doAnswer(onPutFileFailure).when(internalInterface).sendRPC(any(PutFile.class));
FileManagerConfig fileManagerConfig = new FileManagerConfig();
fileManagerConfig.setFileRetryCount(2);
@@ -324,17 +254,28 @@ public class FileManagerTests {
fileManager.start(new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
+ public void onComplete(final boolean success1) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(success1);
+ }
+ });
+
fileManager.uploadFile(validFile, new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertFalse(success);
+ public void onComplete(final boolean success2) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(success2);
+ verify(internalInterface, times(4)).sendRPC(any(RPCMessage.class));
+ }
+ });
}
});
}
});
- verify(internalInterface, times(4)).sendRPC(any(RPCMessage.class));
}
/**
@@ -342,24 +283,12 @@ public class FileManagerTests {
*/
@Test
public void testArtworkUploadRetry() {
- final ISdl internalInterface = mock(ISdl.class);
+ final ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
- doAnswer(onPutFileFailureOnError).when(internalInterface).sendRPC(any(PutFile.class));
-
- final SdlFile validFile2 = new SdlFile();
- validFile2.setName(TestValues.GENERAL_STRING + "2");
- validFile2.setFileData(TestValues.GENERAL_BYTE_ARRAY);
- validFile2.setPersistent(false);
- validFile2.setType(FileType.GRAPHIC_PNG);
-
- final SdlFile validFile3 = new SdlFile();
- validFile3.setName(TestValues.GENERAL_STRING + "3");
- validFile3.setFileData(TestValues.GENERAL_BYTE_ARRAY);
- validFile3.setPersistent(false);
- validFile3.setType(FileType.GRAPHIC_BMP);
+ doAnswer(onPutFileFailure).when(internalInterface).sendRPC(any(PutFile.class));
- validFile.setType(FileType.GRAPHIC_JPEG);
+ final SdlArtwork validArtwork = new SdlArtwork(TestValues.GENERAL_STRING + "1", FileType.GRAPHIC_JPEG, TestValues.GENERAL_STRING.getBytes(), false);
FileManagerConfig fileManagerConfig = new FileManagerConfig();
fileManagerConfig.setArtworkRetryCount(2);
@@ -367,33 +296,28 @@ public class FileManagerTests {
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
fileManager.start(new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
- fileManager.uploadFile(validFile, new CompletionListener() {
- @Override
- public void onComplete(boolean success) {
- assertFalse(success);
- verify(internalInterface, times(4)).sendRPC(any(RPCMessage.class));
- }
- });
-
- fileManager.uploadFile(validFile2, new CompletionListener() {
+ public void onComplete(final boolean success1) {
+ assertOnMainThread(new Runnable() {
@Override
- public void onComplete(boolean success) {
- assertFalse(success);
- verify(internalInterface, times(7)).sendRPC(any(RPCMessage.class));
+ public void run() {
+ assertTrue(success1);
}
});
- fileManager.uploadFile(validFile3, new CompletionListener() {
+ fileManager.uploadArtwork(validArtwork, new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertFalse(success);
+ public void onComplete(final boolean success2) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(success2);
+ verify(internalInterface, times(4)).sendRPC(any(RPCMessage.class));
+ }
+ });
}
});
}
});
- verify(internalInterface, times(10)).sendRPC(any(RPCMessage.class));
}
/**
@@ -401,41 +325,44 @@ public class FileManagerTests {
*/
@Test
public void testListFilesUploadRetry() {
- final ISdl internalInterface = mock(ISdl.class);
+ final ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
- doAnswer(onSendRequestsFailOnError).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
-
- SdlFile validFile2 = new SdlFile();
- validFile2.setName(TestValues.GENERAL_STRING + "2");
- validFile2.setFileData(TestValues.GENERAL_BYTE_ARRAY);
- validFile2.setPersistent(false);
- validFile2.setType(FileType.GRAPHIC_JPEG);
+ doAnswer(onPutFileFailure).when(internalInterface).sendRPC(any(PutFile.class));
- validFile.setType(FileType.AUDIO_WAVE);
+ final SdlArtwork validFile2 = new SdlArtwork(TestValues.GENERAL_STRING + "2", FileType.GRAPHIC_JPEG, TestValues.GENERAL_STRING.getBytes(), false);
- final List<SdlFile> list = new ArrayList<>();
- list.add(validFile);
- list.add(validFile2);
+ final List<SdlFile> list = Arrays.asList(validFile, validFile2);
FileManagerConfig fileManagerConfig = new FileManagerConfig();
- fileManagerConfig.setArtworkRetryCount(2);
- fileManagerConfig.setFileRetryCount(4);
+ fileManagerConfig.setFileRetryCount(3);
+ fileManagerConfig.setArtworkRetryCount(4);
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
fileManager.start(new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- fileManager.uploadFiles(list, new MultipleFileCompletionListener() {
+ public void onComplete(final boolean success1) {
+ assertOnMainThread(new Runnable() {
@Override
- public void onComplete(Map<String, String> errors) {
- assertTrue(errors.size() == 2); // We need to make sure it kept track of both Files
+ public void run() {
+ assertTrue(success1);
}
});
+ fileManager.uploadFiles(list, new MultipleFileCompletionListener() {
+ @Override
+ public void onComplete(final Map<String, String> errors) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(2, errors.size()); // We need to make sure it kept track of both Files
+ verify(internalInterface, times(9)).sendRPC(any(PutFile.class));
+ }
+ });
+ }
+ });
}
});
- verify(internalInterface, times(5)).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
}
/**
@@ -443,7 +370,7 @@ public class FileManagerTests {
*/
@Test
public void testInitializationSuccess() {
- ISdl internalInterface = mock(ISdl.class);
+ ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
@@ -451,11 +378,16 @@ public class FileManagerTests {
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
fileManager.start(new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
- assertEquals(fileManager.getState(), BaseSubManager.READY);
- assertEquals(fileManager.getRemoteFileNames(), TestValues.GENERAL_STRING_LIST);
- assertEquals(TestValues.GENERAL_INT, fileManager.getBytesAvailable());
+ public void onComplete(final boolean success) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(success);
+ assertEquals(fileManager.getState(), BaseSubManager.READY);
+ assertEquals(fileManager.getRemoteFileNames(), TestValues.GENERAL_STRING_LIST);
+ assertEquals(TestValues.GENERAL_INT, fileManager.getBytesAvailable());
+ }
+ });
}
});
}
@@ -465,8 +397,7 @@ public class FileManagerTests {
*/
@Test
public void testFileUploadSuccess() {
- ISdl internalInterface = mock(ISdl.class);
-
+ ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
doAnswer(onPutFileSuccess).when(internalInterface).sendRPC(any(PutFile.class));
@@ -475,19 +406,31 @@ public class FileManagerTests {
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
fileManager.start(new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
+ public void onComplete(final boolean success1) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(success1);
+ }
+ });
+
fileManager.uploadFile(validFile, new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
+ public void onComplete(final boolean success2) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(success2);
+ assertTrue(fileManager.getRemoteFileNames().contains(validFile.getName()));
+ assertTrue(fileManager.hasUploadedFile(validFile));
+ assertEquals(TestValues.GENERAL_INT, fileManager.getBytesAvailable());
+ }
+ });
+
}
});
}
});
- assertTrue(fileManager.getRemoteFileNames().contains(validFile.getName()));
- assertTrue(fileManager.hasUploadedFile(validFile));
- assertEquals(TestValues.GENERAL_INT, fileManager.getBytesAvailable());
}
/**
@@ -495,7 +438,7 @@ public class FileManagerTests {
*/
@Test
public void testFileUploadFailure() {
- ISdl internalInterface = mock(ISdl.class);
+ ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
doAnswer(onPutFileFailure).when(internalInterface).sendRPC(any(PutFile.class));
@@ -504,14 +447,24 @@ public class FileManagerTests {
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
fileManager.start(new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
+ public void onComplete(final boolean success1) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(success1);
+ }
+ });
fileManager.uploadFile(validFile, new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertFalse(success);
- assertFalse(fileManager.getRemoteFileNames().contains(validFile.getName()));
- assertFalse(fileManager.hasUploadedFile(validFile));
+ public void onComplete(final boolean success2) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(success2);
+ assertFalse(fileManager.getRemoteFileNames().contains(validFile.getName()));
+ assertFalse(fileManager.hasUploadedFile(validFile));
+ }
+ });
}
});
}
@@ -523,7 +476,7 @@ public class FileManagerTests {
*/
@Test
public void testFileUploadForStaticIcon() {
- ISdl internalInterface = mock(ISdl.class);
+ final ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
@@ -531,18 +484,28 @@ public class FileManagerTests {
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
fileManager.start(new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
+ public void onComplete(final boolean success1) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(success1);
+ }
+ });
SdlArtwork artwork = new SdlArtwork(StaticIconName.ALBUM);
fileManager.uploadFile(artwork, new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
+ public void onComplete(final boolean success2) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(success2);
+ verify(internalInterface, times(1)).sendRPC(any(RPCMessage.class));
+ }
+ });
}
});
}
});
- verify(internalInterface, times(1)).sendRPC(any(RPCMessage.class));
}
/**
@@ -550,31 +513,41 @@ public class FileManagerTests {
*/
@Test
public void testMultipleFileUploadsForStaticIcon() {
- ISdl internalInterface = mock(ISdl.class);
+ final ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
- doAnswer(onListFileUploadSuccess).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
+ doAnswer(onPutFileSuccess).when(internalInterface).sendRPC(any(PutFile.class));
FileManagerConfig fileManagerConfig = new FileManagerConfig();
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
fileManager.start(new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
- SdlArtwork artwork = new SdlArtwork(StaticIconName.ALBUM);
+ public void onComplete(final boolean success1) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(success1);
+ }
+ });
+
+ SdlArtwork artwork1 = new SdlArtwork(StaticIconName.ALBUM);
SdlArtwork artwork2 = new SdlArtwork(StaticIconName.FILENAME);
- List<SdlArtwork> testStaticIconUpload = new ArrayList<>();
- testStaticIconUpload.add(artwork);
- testStaticIconUpload.add(artwork2);
+ List<SdlArtwork> testStaticIconUpload = Arrays.asList(artwork1, artwork2);
+
fileManager.uploadFiles(testStaticIconUpload, new MultipleFileCompletionListener() {
@Override
- public void onComplete(Map<String, String> errors) {
- assertTrue(errors == null);
+ public void onComplete(final Map<String, String> errors) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(2, errors.size());
+ verify(internalInterface, times(0)).sendRPC(any(PutFile.class));
+ }
+ });
}
});
}
});
- verify(internalInterface, times(0)).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
}
/**
@@ -582,10 +555,10 @@ public class FileManagerTests {
*/
@Test
public void testMultipleFileUploadsForPartialStaticIcon() {
- ISdl internalInterface = mock(ISdl.class);
+ final ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
- doAnswer(onListFileUploadSuccess).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
+ doAnswer(onPutFileSuccess).when(internalInterface).sendRPC(any(PutFile.class));
FileManagerConfig fileManagerConfig = new FileManagerConfig();
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
@@ -595,19 +568,21 @@ public class FileManagerTests {
assertTrue(success);
SdlArtwork artwork = new SdlArtwork(StaticIconName.ALBUM);
SdlArtwork artwork2 = new SdlArtwork(StaticIconName.FILENAME);
- List<SdlFile> testFileuploads = new ArrayList<>();
- testFileuploads.add(artwork);
- testFileuploads.add(artwork2);
- testFileuploads.add(validFile);
- fileManager.uploadFiles(testFileuploads, new MultipleFileCompletionListener() {
+ List<SdlFile> testFileUploads = Arrays.asList(artwork, artwork2, validFile);
+ fileManager.uploadFiles(testFileUploads, new MultipleFileCompletionListener() {
@Override
- public void onComplete(Map<String, String> errors) {
- assertTrue(errors == null);
+ public void onComplete(final Map<String, String> errors) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(2, errors.size());
+ verify(internalInterface, times(1)).sendRPC(any(PutFile.class));
+ }
+ });
}
});
}
});
- verify(internalInterface, times(1)).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
}
/**
@@ -615,7 +590,7 @@ public class FileManagerTests {
*/
@Test
public void testInvalidSdlFileInput() {
- ISdl internalInterface = mock(ISdl.class);
+ ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
@@ -623,27 +598,35 @@ public class FileManagerTests {
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
fileManager.start(new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
- SdlFile sdlFile = new SdlFile();
- // Don't set name
+ public void onComplete(final boolean success1) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(success1);
+ }
+ });
+
+ SdlFile sdlFile;
+
+ // Test 1 - Don't set name
+ sdlFile = new SdlFile();
sdlFile.setFileData(TestValues.GENERAL_BYTE_ARRAY);
checkForUploadFailure(fileManager, sdlFile);
+ // Test 2 - Don't set data
sdlFile = new SdlFile();
sdlFile.setName(TestValues.GENERAL_STRING);
- // Don't set data
checkForUploadFailure(fileManager, sdlFile);
+ // Test 3 - Give an invalid resource ID
sdlFile = new SdlFile();
sdlFile.setName(TestValues.GENERAL_STRING);
- // Give an invalid resource ID
sdlFile.setResourceId(TestValues.GENERAL_INT);
checkForUploadFailure(fileManager, sdlFile);
+ // Test4 - Set invalid Uri
sdlFile = new SdlFile();
sdlFile.setName(TestValues.GENERAL_STRING);
- // Set invalid Uri
Uri testUri = Uri.parse("http://www.google.com");
sdlFile.setUri(testUri);
checkForUploadFailure(fileManager, sdlFile);
@@ -658,19 +641,17 @@ public class FileManagerTests {
* @param sdlFile - SdlFile with invalid data to test uploading
*/
private void checkForUploadFailure(FileManager fileManager, SdlFile sdlFile) {
- boolean error = false;
-
- try {
- fileManager.uploadFile(sdlFile, new CompletionListener() {
- @Override
- public void onComplete(boolean success) {
- }
- });
- } catch (IllegalArgumentException e) {
- error = true;
- }
-
- assertTrue(error);
+ fileManager.uploadFile(sdlFile, new CompletionListener() {
+ @Override
+ public void onComplete(final boolean success) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertFalse(success);
+ }
+ });
+ }
+ });
}
/**
@@ -683,8 +664,7 @@ public class FileManagerTests {
// Set invalid type
for (FileType fileType : FileType.values()) {
boolean shouldError = true, didError = false;
- if (fileType.equals(FileType.GRAPHIC_BMP) || fileType.equals(FileType.GRAPHIC_PNG)
- || fileType.equals(FileType.GRAPHIC_JPEG)) {
+ if (fileType.equals(FileType.GRAPHIC_BMP) || fileType.equals(FileType.GRAPHIC_PNG) || fileType.equals(FileType.GRAPHIC_JPEG)) {
shouldError = false;
}
try {
@@ -701,85 +681,31 @@ public class FileManagerTests {
*/
@Test
public void testMultipleFileUpload() {
- ISdl internalInterface = mock(ISdl.class);
+ ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
- doAnswer(onListFileUploadSuccess).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
+ doAnswer(onPutFileSuccess).when(internalInterface).sendRPC(any(PutFile.class));
FileManagerConfig fileManagerConfig = new FileManagerConfig();
-
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
fileManager.start(new CompletionListener() {
@Override
public void onComplete(boolean success) {
- assertTrue(success);
- final List<SdlFile> filesToUpload = new ArrayList<>();
- filesToUpload.add(validFile);
-
- SdlFile validFile2 = new SdlFile();
- validFile2.setName(TestValues.GENERAL_STRING + "2");
- validFile2.setFileData(TestValues.GENERAL_BYTE_ARRAY);
- validFile2.setPersistent(false);
- validFile2.setType(FileType.GRAPHIC_JPEG);
- filesToUpload.add(validFile2);
-
+ SdlFile validFile2 = new SdlFile(TestValues.GENERAL_STRING + "2", FileType.GRAPHIC_JPEG, TestValues.GENERAL_STRING.getBytes(), false);
+ List<SdlFile> filesToUpload = Arrays.asList(validFile, validFile2);
fileManager.uploadFiles(filesToUpload, new MultipleFileCompletionListener() {
@Override
- public void onComplete(Map<String, String> errors) {
- assertNull(errors);
- }
- });
- }
- });
- }
-
- /**
- * Testing uploading multiple files with some failing.
- */
- @Test
- public void testMultipleFileUploadPartialFailure() {
- ISdl internalInterface = mock(ISdl.class);
-
- doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
- doAnswer(onSendRequestsFailPartialOnError).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
-
- SdlFile validFile2 = new SdlFile();
- validFile2.setName(TestValues.GENERAL_STRING + "2");
- validFile2.setFileData(TestValues.GENERAL_BYTE_ARRAY);
- validFile2.setPersistent(false);
- validFile2.setType(FileType.GRAPHIC_JPEG);
-
- SdlFile validFile3 = new SdlFile();
- validFile3.setName(TestValues.GENERAL_STRING + "3");
- validFile3.setFileData(TestValues.GENERAL_BYTE_ARRAY);
- validFile3.setPersistent(false);
- validFile3.setType(FileType.GRAPHIC_JPEG);
-
- validFile.setType(FileType.AUDIO_WAVE);
-
- final List<SdlFile> filesToUpload = new ArrayList<>();
- filesToUpload.add(validFile);
- filesToUpload.add(validFile2);
- filesToUpload.add(validFile3);
-
- FileManagerConfig fileManagerConfig = new FileManagerConfig();
- fileManagerConfig.setArtworkRetryCount(0);
- fileManagerConfig.setFileRetryCount(0);
- final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
- fileManager.start(new CompletionListener() {
- @Override
- public void onComplete(boolean success) {
- fileManager.uploadFiles(filesToUpload,
- new MultipleFileCompletionListener() {
+ public void onComplete(final Map<String, String> errors) {
+ assertOnMainThread(new Runnable() {
@Override
- public void onComplete(Map<String, String> errors) {
- assertTrue(errors.size() == 2);
+ public void run() {
+ assertNull(errors);
}
});
+ }
+ });
}
});
- assertFalse(fileManager.hasUploadedFile(validFile) && fileManager.hasUploadedFile(validFile3));
- assertTrue(fileManager.hasUploadedFile(validFile2));
}
/**
@@ -787,37 +713,26 @@ public class FileManagerTests {
*/
@Test
public void testMultipleArtworkUploadSuccess() {
- ISdl internalInterface = mock(ISdl.class);
+ ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
- doAnswer(onListFileUploadSuccess).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
+ doAnswer(onPutFileSuccess).when(internalInterface).sendRPC(any(PutFile.class));
FileManagerConfig fileManagerConfig = new FileManagerConfig();
final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
fileManager.start(new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
- int fileNum = 1;
- final List<SdlArtwork> artworkToUpload = new ArrayList<>();
- SdlArtwork sdlArtwork = new SdlArtwork();
- sdlArtwork.setName("art" + fileNum++);
- Uri uri = Uri.parse("android.resource://" + mTestContext.getPackageName() + "/drawable/ic_sdl");
- sdlArtwork.setUri(uri);
- sdlArtwork.setType(FileType.GRAPHIC_PNG);
- artworkToUpload.add(sdlArtwork);
-
- sdlArtwork = new SdlArtwork();
- sdlArtwork.setName("art" + fileNum++);
- uri = Uri.parse("android.resource://" + mTestContext.getPackageName() + "/drawable/sdl_tray_icon");
- sdlArtwork.setUri(uri);
- sdlArtwork.setType(FileType.GRAPHIC_PNG);
- artworkToUpload.add(sdlArtwork);
-
- fileManager.uploadFiles(artworkToUpload,
- new MultipleFileCompletionListener() {
+ public void onComplete(boolean success1) {
+ SdlArtwork sdlArtwork1 = new SdlArtwork("artwork1", FileType.GRAPHIC_JPEG, Uri.parse("android.resource://" + mTestContext.getPackageName() + "/drawable/ic_sdl"), false);
+ SdlArtwork sdlArtwork2 = new SdlArtwork("artwork2", FileType.GRAPHIC_PNG, Uri.parse("android.resource://" + mTestContext.getPackageName() + "/drawable/sdl_tray_icon"), false);
+ final List<SdlArtwork> artworkToUpload = Arrays.asList(sdlArtwork1, sdlArtwork2);
+
+ fileManager.uploadFiles(artworkToUpload, new MultipleFileCompletionListener() {
+ @Override
+ public void onComplete(final Map<String, String> errors) {
+ assertOnMainThread(new Runnable() {
@Override
- public void onComplete(Map<String, String> errors) {
+ public void run() {
assertNull(errors);
List<String> uploadedFileNames = fileManager.getRemoteFileNames();
for (SdlArtwork artwork : artworkToUpload) {
@@ -825,29 +740,8 @@ public class FileManagerTests {
}
}
});
- }
- });
- }
-
- /**
- * Testing uploading persistent SdlFile
- */
- @Test
- public void testPersistentFileUploaded() {
- ISdl internalInterface = mock(ISdl.class);
-
- doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
-
- final SdlFile file = new SdlFile();
- file.setName(TestValues.GENERAL_STRING_LIST.get(0));
- file.setPersistent(true);
-
- FileManagerConfig fileManagerConfig = new FileManagerConfig();
- final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig);
- fileManager.start(new CompletionListener() {
- @Override
- public void onComplete(boolean success) {
- assertTrue(fileManager.hasUploadedFile(file));
+ }
+ });
}
});
}
@@ -870,7 +764,7 @@ public class FileManagerTests {
*/
@Test
public void testOverwriteFileProperty() {
- ISdl internalInterface = mock(ISdl.class);
+ final ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
doAnswer(onPutFileSuccess).when(internalInterface).sendRPC(any(PutFile.class));
@@ -881,24 +775,32 @@ public class FileManagerTests {
fileManager.start(new CompletionListener() {
@Override
public void onComplete(boolean success) {
- assertTrue(success);
fileManager.uploadFile(validFile, new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
+ public void onComplete(final boolean success1) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(success1);
+ }
+ });
validFile.setOverwrite(false);
fileManager.uploadFile(validFile, new CompletionListener() {
@Override
- public void onComplete(boolean success) {
- assertTrue(success);
+ public void onComplete(final boolean success2) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(success2);
+ verify(internalInterface, times(2)).sendRPC(any(RPCMessage.class));
+ }
+ });
}
});
-
}
});
}
});
- verify(internalInterface, times(2)).sendRPC(any(RPCMessage.class));
}
/**
@@ -907,20 +809,14 @@ public class FileManagerTests {
*/
@Test
public void testOverWriteFilePropertyListFiles() {
- final ISdl internalInterface = mock(ISdl.class);
+ final ISdl internalInterface = createISdlMock();
doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class));
- doAnswer(onListFileUploadSuccess).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
+ doAnswer(onPutFileSuccess).when(internalInterface).sendRPC(any(PutFile.class));
- final SdlFile validFile2 = new SdlFile();
- validFile2.setName(TestValues.GENERAL_STRING + "2");
- validFile2.setFileData(TestValues.GENERAL_BYTE_ARRAY);
- validFile2.setPersistent(false);
- validFile2.setType(FileType.GRAPHIC_JPEG);
+ final SdlArtwork validFile2 = new SdlArtwork(TestValues.GENERAL_STRING + "2", FileType.GRAPHIC_JPEG, TestValues.GENERAL_STRING.getBytes(), false);
- final List<SdlFile> list = new ArrayList<>();
- list.add(validFile);
- list.add(validFile2);
+ final List<SdlFile> list = Arrays.asList(validFile, validFile2);
FileManagerConfig fileManagerConfig = new FileManagerConfig();
fileManagerConfig.setArtworkRetryCount(2);
@@ -937,8 +833,14 @@ public class FileManagerTests {
validFile2.setOverwrite(false);
fileManager.uploadFiles(list, new MultipleFileCompletionListener() {
@Override
- public void onComplete(Map<String, String> errors) {
- assertNull(errors);
+ public void onComplete(final Map<String, String> errors) {
+ assertOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ assertNull(errors);
+ verify(internalInterface, times(2)).sendRPC(any(PutFile.class));
+ }
+ });
}
});
}
@@ -946,7 +848,6 @@ public class FileManagerTests {
}
});
- verify(internalInterface, times(1)).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
}
/**
@@ -1005,4 +906,20 @@ public class FileManagerTests {
artwork2 = new SdlFile("image1", FileType.GRAPHIC_PNG, 1, false);
assertTrue(artwork1.equals(artwork2));
}
+
+ // Asserts on Taskmaster threads will fail silently so we need to do the assertions on main thread if the code is triggered from Taskmaster
+ private void assertOnMainThread(Runnable runnable) {
+ mainHandler.post(runnable);
+ }
+
+ private ISdl createISdlMock() {
+ ISdl internalInterface = mock(ISdl.class);
+ Taskmaster taskmaster = new Taskmaster.Builder().build();
+ taskmaster.start();
+
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(rpcVersion));
+ when(internalInterface.getMtu(any(SessionType.class))).thenReturn(mtuSize);
+ return internalInterface;
+ }
} \ No newline at end of file
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java
index 58d972b38..28f09f8bb 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java
@@ -7,6 +7,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.ManagerUtility;
+import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.protocol.ISdlServiceListener;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.protocol.enums.SessionType;
@@ -98,6 +99,8 @@ public class SystemCapabilityManagerTests {
videoStreamingCapability.setMaxBitrate(TestValues.GENERAL_INT);
videoStreamingCapability.setPreferredResolution(TestValues.GENERAL_IMAGERESOLUTION);
videoStreamingCapability.setSupportedFormats(TestValues.GENERAL_VIDEOSTREAMINGFORMAT_LIST);
+ videoStreamingCapability.setPreferredFPS(TestValues.GENERAL_INTEGER);
+ videoStreamingCapability.setAdditionalVideoStreamingCapabilities(TestValues.GENERAL_ADDITIONAL_CAPABILITY_LIST);
systemCapability.setCapabilityForType(SystemCapabilityType.VIDEO_STREAMING, videoStreamingCapability);
}
@@ -204,8 +207,8 @@ public class SystemCapabilityManagerTests {
@Test
public void testNullDisplayCapabilitiesEnablesAllTextAndImageFields() {
List<DisplayCapability> displayCapabilityList = createDisplayCapabilityList(null, TestValues.GENERAL_BUTTONCAPABILITIES_LIST, TestValues.GENERAL_SOFTBUTTONCAPABILITIES_LIST);
- assertEquals(displayCapabilityList.get(0).getWindowCapabilities().get(0).getTextFields().size(), 32);
- assertEquals(displayCapabilityList.get(0).getWindowCapabilities().get(0).getImageFields().size(), 16);
+ assertEquals(displayCapabilityList.get(0).getWindowCapabilities().get(0).getTextFields().size(), 38);
+ assertEquals(displayCapabilityList.get(0).getWindowCapabilities().get(0).getImageFields().size(), 18);
}
@Test
@@ -213,7 +216,9 @@ public class SystemCapabilityManagerTests {
VideoStreamingCapability vsCapability = new VideoStreamingCapability();
vsCapability.setMaxBitrate(TestValues.GENERAL_INT);
vsCapability.setPreferredResolution(TestValues.GENERAL_IMAGERESOLUTION);
+ vsCapability.setAdditionalVideoStreamingCapabilities(TestValues.GENERAL_ADDITIONAL_CAPABILITY_LIST);
vsCapability.setSupportedFormats(TestValues.GENERAL_VIDEOSTREAMINGFORMAT_LIST);
+ vsCapability.setPreferredFPS(TestValues.GENERAL_INTEGER);
SystemCapability cap = new SystemCapability();
cap.setSystemCapabilityType(SystemCapabilityType.VIDEO_STREAMING);
@@ -959,7 +964,7 @@ public class SystemCapabilityManagerTests {
}
@Override
- public void startVideoService(VideoStreamingParameters parameters, boolean encrypted) {
+ public void startVideoService(VideoStreamingParameters parameters, boolean encrypted, boolean withPendingRestart) {
}
@Override
@@ -1038,6 +1043,11 @@ public class SystemCapabilityManagerTests {
}
@Override
+ public long getMtu(SessionType serviceType) {
+ return 0;
+ }
+
+ @Override
public boolean isTransportForServiceAvailable(SessionType serviceType) {
return false;
}
@@ -1059,5 +1069,10 @@ public class SystemCapabilityManagerTests {
public SystemCapabilityManager getSystemCapabilityManager() {
return null;
}
+
+ @Override
+ public PermissionManager getPermissionManager() {
+ return null;
+ }
}
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertAudioDataTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertAudioDataTest.java
new file mode 100644
index 000000000..3459c6e34
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertAudioDataTest.java
@@ -0,0 +1,56 @@
+package com.smartdevicelink.managers.screen;
+
+import android.content.Context;
+import android.net.Uri;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import java.util.Collections;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+public class AlertAudioDataTest {
+ SdlFile testAudio;
+
+ @Before
+ public void setUp() throws Exception {
+ Context mTestContext = getInstrumentation().getContext();
+ Uri uri1 = Uri.parse("android.resource://" + mTestContext.getPackageName() + "/raw/test_audio_square_250hz_80amp_1s.mp3");
+ testAudio = new SdlFile("TestAudioFile", FileType.AUDIO_MP3, uri1, false);
+ }
+
+ @Test
+ public void testConstructors() {
+ AlertAudioData alertAudioData1 = new AlertAudioData("phoneticString", SpeechCapabilities.TEXT);
+ alertAudioData1.setPlayTone(true);
+ assertEquals("phoneticString", alertAudioData1.getAudioData().get(0).getText());
+ assertTrue(alertAudioData1.isPlayTone());
+
+ AlertAudioData alertAudioData2 = new AlertAudioData("spokenString");
+ assertEquals("spokenString", alertAudioData2.getAudioData().get(0).getText());
+
+ AlertAudioData alertAudioData3 = new AlertAudioData(testAudio);
+ assertEquals(alertAudioData3.getAudioData().get(0).getText(), testAudio.getName());
+ }
+
+ @Test
+ public void testAdd() {
+ AlertAudioData alertAudioData1 = new AlertAudioData("phoneticString", SpeechCapabilities.TEXT);
+ alertAudioData1.addAudioFiles(Collections.singletonList(testAudio));
+ alertAudioData1.addPhoneticSpeechSynthesizerStrings(Collections.singletonList("addition"), SpeechCapabilities.TEXT);
+ alertAudioData1.addSpeechSynthesizerStrings(Collections.singletonList("addition2"));
+ alertAudioData1.addAudioFiles(Collections.singletonList(testAudio));
+ assertEquals("phoneticString", alertAudioData1.getAudioData().get(0).getText());
+ assertEquals(testAudio.getName(), alertAudioData1.getAudioData().get(1).getText());
+ assertEquals("addition", alertAudioData1.getAudioData().get(2).getText());
+ assertEquals("addition2", alertAudioData1.getAudioData().get(3).getText());
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertManagerTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertManagerTest.java
new file mode 100644
index 000000000..1655b3d08
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertManagerTest.java
@@ -0,0 +1,166 @@
+package com.smartdevicelink.managers.screen;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.livio.taskmaster.Taskmaster;
+import com.smartdevicelink.managers.AlertCompletionListener;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.lifecycle.OnSystemCapabilityListener;
+import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
+import com.smartdevicelink.managers.permission.OnPermissionChangeListener;
+import com.smartdevicelink.managers.permission.PermissionManager;
+import com.smartdevicelink.managers.permission.PermissionStatus;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.rpc.DisplayCapability;
+import com.smartdevicelink.proxy.rpc.ImageField;
+import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities;
+import com.smartdevicelink.proxy.rpc.TextField;
+import com.smartdevicelink.proxy.rpc.WindowCapability;
+import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
+import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
+import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
+import com.smartdevicelink.test.TestValues;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class AlertManagerTest {
+ AlertManager alertManager;
+
+ @Before
+ public void setUp() throws Exception {
+ // mock things
+ ISdl internalInterface = mock(ISdl.class);
+ FileManager fileManager = mock(FileManager.class);
+ PermissionManager permissionManager = mock(PermissionManager.class);
+
+ when(internalInterface.getPermissionManager()).thenReturn(permissionManager);
+
+ Taskmaster taskmaster = new Taskmaster.Builder().build();
+ taskmaster.start();
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
+
+ Answer<Void> permissionAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ OnPermissionChangeListener onPermissionChangeListener = (OnPermissionChangeListener) args[2];
+ Map<FunctionID, PermissionStatus > allowedPermissions = new HashMap<>();
+ int permissionGroupStatus = PermissionManager.PERMISSION_GROUP_STATUS_DISALLOWED;
+ onPermissionChangeListener.onPermissionsChange(allowedPermissions,permissionGroupStatus);
+ return null;
+ }
+ };
+ doAnswer(permissionAnswer).when(permissionManager).addListener(any(List.class), anyInt(), any(OnPermissionChangeListener.class));
+
+ Answer<Void> onSystemCapabilityAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ OnSystemCapabilityListener onSystemCapabilityListener = (OnSystemCapabilityListener) args[1];
+ WindowCapability windowCapability = getWindowCapability(3);
+ DisplayCapability displayCapability = new DisplayCapability();
+ displayCapability.setWindowCapabilities(Collections.singletonList(windowCapability));
+ List<DisplayCapability> capabilities = Collections.singletonList(displayCapability);
+ onSystemCapabilityListener.onCapabilityRetrieved(capabilities);
+ return null;
+ }
+ };
+
+ SystemCapabilityManager systemCapabilityManager = mock(SystemCapabilityManager.class);
+ doAnswer(onSystemCapabilityAnswer).when(systemCapabilityManager).addOnSystemCapabilityListener(eq(SystemCapabilityType.DISPLAYS), any(OnSystemCapabilityListener.class));
+ doReturn(systemCapabilityManager).when(internalInterface).getSystemCapabilityManager();
+
+ alertManager = new AlertManager(internalInterface, fileManager);
+ }
+
+ @Test
+ public void testInstantiation() {
+ assertNotNull(alertManager.currentWindowCapability);
+ assertNotNull(alertManager.nextCancelId);
+ assertFalse(alertManager.isAlertRPCAllowed);
+ }
+
+ @Test
+ public void testPresentAlert() {
+ AlertView.Builder builder = new AlertView.Builder();
+ AlertView alertView = builder.build();
+ alertManager.presentAlert(alertView, new AlertCompletionListener() {
+ @Override
+ public void onComplete(boolean success, Integer tryAgainTime) {
+
+ }
+ });
+ assertTrue(alertManager.transactionQueue.getTasksAsList().size() == 1);
+ }
+
+ private WindowCapability getWindowCapability(int numberOfAlertFields) {
+ TextField alertText1 = new TextField();
+ alertText1.setName(TextFieldName.alertText1);
+ TextField alertText2 = new TextField();
+ alertText2.setName(TextFieldName.alertText2);
+ TextField alertText3 = new TextField();
+ alertText3.setName(TextFieldName.alertText3);
+ TextField mainField4 = new TextField();
+ mainField4.setName(TextFieldName.mainField4);
+
+ List<TextField> textFieldList = new ArrayList<>();
+
+ textFieldList.add(alertText1);
+ textFieldList.add(alertText2);
+ textFieldList.add(alertText3);
+
+ List<TextField> returnList = new ArrayList<>();
+
+ if (numberOfAlertFields > 0) {
+ for (int i = 0; i < numberOfAlertFields; i++) {
+ returnList.add(textFieldList.get(i));
+ }
+ }
+
+ WindowCapability windowCapability = new WindowCapability();
+ windowCapability.setTextFields(returnList);
+
+ ImageField imageField = new ImageField();
+ imageField.setName(ImageFieldName.alertIcon);
+ List<ImageField> imageFieldList = new ArrayList<>();
+ imageFieldList.add(imageField);
+ windowCapability.setImageFields(imageFieldList);
+
+ windowCapability.setImageFields(imageFieldList);
+
+ SoftButtonCapabilities softButtonCapabilities = new SoftButtonCapabilities();
+ softButtonCapabilities.setImageSupported(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setShortPressAvailable(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setLongPressAvailable(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setUpDownAvailable(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setTextSupported(TestValues.GENERAL_BOOLEAN);
+
+ windowCapability.setSoftButtonCapabilities(Collections.singletonList(softButtonCapabilities));
+ return windowCapability;
+ }
+
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java
new file mode 100644
index 000000000..1283992a2
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java
@@ -0,0 +1,315 @@
+package com.smartdevicelink.managers.screen;
+
+import android.content.Context;
+import android.net.Uri;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.managers.AlertCompletionListener;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.file.MultipleFileCompletionListener;
+import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.rpc.Alert;
+import com.smartdevicelink.proxy.rpc.AlertResponse;
+import com.smartdevicelink.proxy.rpc.CancelInteraction;
+import com.smartdevicelink.proxy.rpc.CancelInteractionResponse;
+import com.smartdevicelink.proxy.rpc.ImageField;
+import com.smartdevicelink.proxy.rpc.OnButtonEvent;
+import com.smartdevicelink.proxy.rpc.OnButtonPress;
+import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
+import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities;
+import com.smartdevicelink.proxy.rpc.TextField;
+import com.smartdevicelink.proxy.rpc.WindowCapability;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
+import com.smartdevicelink.test.TestValues;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class PresentAlertOperationTest {
+
+ private PresentAlertOperation presentAlertOperation;
+ private WindowCapability defaultMainWindowCapability;
+ private AlertView alertView;
+ private AlertAudioData alertAudioData;
+ SdlArtwork testAlertArtwork, testSoftButtonArtwork;
+ ISdl internalInterface;
+ FileManager fileManager;
+ SoftButtonState alertSoftButtonState;
+ SoftButtonObject alertSoftButtonObject;
+ private List<SpeechCapabilities> speechCapabilities;
+ SdlFile testAudio;
+ AlertCompletionListener alertCompletionListener;
+ BaseAlertManager.AlertSoftButtonClearListener alertSoftButtonClearListener;
+
+ private Answer<Void> onArtworkUploadSuccess = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ MultipleFileCompletionListener listener = (MultipleFileCompletionListener) args[1];
+ listener.onComplete(null);
+ return null;
+ }
+ };
+
+ private Answer<Void> onAlertSuccess = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ RPCRequest message = (RPCRequest) args[0];
+ if (message instanceof Alert) {
+ int correlationId = message.getCorrelationID();
+ AlertResponse alertResponse = new AlertResponse();
+ alertResponse.setSuccess(true);
+ message.getOnRPCResponseListener().onResponse(correlationId, alertResponse);
+ }
+ return null;
+ }
+ };
+
+ private Answer<Void> onCancelAlert = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ RPCRequest message = (RPCRequest) args[0];
+ if (message instanceof CancelInteraction) {
+ int correlationId = message.getCorrelationID();
+ CancelInteractionResponse cancelInteraction = new CancelInteractionResponse();
+ cancelInteraction.setSuccess(true);
+ message.getOnRPCResponseListener().onResponse(correlationId, cancelInteraction);
+ }
+ return null;
+ }
+ };
+
+ Task task;
+ @Before
+ public void setUp() throws Exception {
+ Context mTestContext = getInstrumentation().getContext();
+ // mock things
+ internalInterface = mock(ISdl.class);
+ fileManager = mock(FileManager.class);
+ task = mock(Task.class);
+
+ alertSoftButtonClearListener = new BaseAlertManager.AlertSoftButtonClearListener() {
+ @Override
+ public void onButtonClear(List<SoftButtonObject> softButtonObjects) {
+
+ }
+ };
+ testAlertArtwork = new SdlArtwork();
+ testAlertArtwork.setName("testArtwork1");
+ Uri uri1 = Uri.parse("android.resource://" + mTestContext.getPackageName() + "/drawable/ic_sdl");
+ testAlertArtwork.setUri(uri1);
+ testAlertArtwork.setType(FileType.GRAPHIC_PNG);
+
+ testSoftButtonArtwork = new SdlArtwork();
+ Uri uri2 = Uri.parse("android.resource://" + mTestContext.getPackageName() + "drawable-hdpi/sdl_lockscreen_icon.png");
+ testSoftButtonArtwork.setName("testArtwork2");
+ testSoftButtonArtwork.setUri(uri2);
+ testSoftButtonArtwork.setType(FileType.GRAPHIC_PNG);
+
+ Uri uri3 = Uri.parse("android.resource://" + mTestContext.getPackageName() + "/raw/test_audio_square_250hz_80amp_1s.mp3");
+ testAudio = new SdlFile("TestAudioFile", FileType.AUDIO_MP3, uri3, false);
+
+ alertAudioData = new AlertAudioData("Spoken Sting");
+ alertAudioData.setPlayTone(true);
+ alertAudioData.addAudioFiles(Collections.singletonList(testAudio));
+
+ alertSoftButtonState = new SoftButtonState("state1", "State 1", testSoftButtonArtwork);
+ SoftButtonObject.OnEventListener onEventListener = new SoftButtonObject.OnEventListener() {
+ @Override
+ public void onPress(SoftButtonObject softButtonObject, OnButtonPress onButtonPress) {
+
+ }
+
+ @Override
+ public void onEvent(SoftButtonObject softButtonObject, OnButtonEvent onButtonEvent) {
+
+ }
+ };
+ alertSoftButtonObject = new SoftButtonObject("Soft button 1", alertSoftButtonState, onEventListener);
+
+ AlertView.Builder builder = new AlertView.Builder();
+ builder.setText("test");
+ builder.setSecondaryText("secondaryText");
+ builder.setTertiaryText("tertiaryText");
+ builder.setAudio(alertAudioData);
+ builder.setIcon(testAlertArtwork);
+ builder.setDefaultTimeOut(10);
+ builder.setTimeout(5);
+ builder.setSoftButtons(Collections.singletonList(alertSoftButtonObject));
+ builder.setShowWaitIndicator(true);
+ alertView = builder.build();
+
+ defaultMainWindowCapability = getWindowCapability(3);
+ speechCapabilities = new ArrayList<SpeechCapabilities>();
+ speechCapabilities.add(SpeechCapabilities.FILE);
+ alertCompletionListener = new AlertCompletionListener() {
+ @Override
+ public void onComplete(boolean success, Integer tryAgainTime) {
+
+ }
+ };
+ presentAlertOperation = new PresentAlertOperation(internalInterface, alertView, defaultMainWindowCapability, speechCapabilities, fileManager, 1, alertCompletionListener, alertSoftButtonClearListener);
+ when(fileManager.fileNeedsUpload(any(SdlFile.class))).thenReturn(true);
+ }
+
+ @Test
+ public void testPresentAlertTruncatedText() {
+ doAnswer(onAlertSuccess).when(internalInterface).sendRPC(any(Alert.class));
+ // Same response works for uploading artworks as it does for files
+
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+ WindowCapability windowCapability = getWindowCapability(1);
+ PresentAlertOperation presentAlertOperation = new PresentAlertOperation(internalInterface, alertView, windowCapability, speechCapabilities, fileManager, 1, alertCompletionListener, alertSoftButtonClearListener);
+ Alert alert = presentAlertOperation.alertRpc();
+
+ assertEquals(alert.getAlertText1(), alertView.getText() + " - " + alertView.getSecondaryText() + " - " + alertView.getTertiaryText());
+
+ windowCapability = getWindowCapability(2);
+
+ presentAlertOperation = new PresentAlertOperation(internalInterface, alertView, windowCapability, speechCapabilities, fileManager, 1, alertCompletionListener, alertSoftButtonClearListener);
+ alert = presentAlertOperation.alertRpc();
+ assertEquals(alert.getAlertText1(), alertView.getText());
+ assertEquals(alert.getAlertText2(),alertView.getSecondaryText() + " - " + alertView.getTertiaryText());
+ }
+
+ @Test
+ public void testPresentAlertHappyPath() {
+ doAnswer(onAlertSuccess).when(internalInterface).sendRPC(any(Alert.class));
+ // Same response works for uploading artworks as it does for files
+ doAnswer(onArtworkUploadSuccess).when(fileManager).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+ doAnswer(onArtworkUploadSuccess).when(fileManager).uploadFiles(any(List.class), any(MultipleFileCompletionListener.class));
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+
+ // Test Images need to be uploaded, sending text and uploading images
+ presentAlertOperation.onExecute();
+
+ // Verifies that uploadArtworks gets called only with the fist presentAlertOperation.onExecute call
+ verify(fileManager, times(1)).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+
+ verify(fileManager, times(1)).uploadFiles(any(List.class), any(MultipleFileCompletionListener.class));
+
+ verify(internalInterface, times(1)).sendRPC(any(Alert.class));
+ }
+
+ @Test
+ public void testPresentAlertNoAudioAndArtwork() {
+ doAnswer(onAlertSuccess).when(internalInterface).sendRPC(any(Alert.class));
+
+ AlertView.Builder builder = new AlertView.Builder();
+ builder.setText("Hi");
+ builder.build();
+ AlertView alertView1 = builder.build();
+
+ presentAlertOperation = new PresentAlertOperation(internalInterface, alertView1, defaultMainWindowCapability, speechCapabilities, fileManager, 2, alertCompletionListener, alertSoftButtonClearListener);
+
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+
+ // Test Images need to be uploaded, sending text and uploading images
+ presentAlertOperation.onExecute();
+
+ // Verifies that uploadArtworks gets called only with the fist presentAlertOperation.onExecute call
+ verify(fileManager, times(0)).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+ verify(fileManager, times(0)).uploadFiles(any(List.class), any(MultipleFileCompletionListener.class));
+
+ verify(internalInterface, times(1)).sendRPC(any(Alert.class));
+ }
+
+ @Test
+ public void testPresentAlertNoImages() {
+ doAnswer(onAlertSuccess).when(internalInterface).sendRPC(any(Alert.class));
+ // Same response works for uploading artworks as it does for files
+ doAnswer(onArtworkUploadSuccess).when(fileManager).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+ doAnswer(onArtworkUploadSuccess).when(fileManager).uploadFiles(any(List.class), any(MultipleFileCompletionListener.class));
+
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+
+ // Test Images need to be uploaded, sending text and uploading images
+ presentAlertOperation.onExecute();
+
+ // Verifies that uploadArtworks gets called only with the fist presentAlertOperation.onExecute call
+ verify(fileManager, times(1)).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+ verify(internalInterface, times(1)).sendRPC(any(Alert.class));
+ }
+
+ @Test
+ public void testCancelOperation() {
+ //Cancel right away
+ presentAlertOperation.cancelTask();
+ presentAlertOperation.onExecute();
+ verify(internalInterface, times(0)).sendRPC(any(Alert.class));
+ }
+
+ private WindowCapability getWindowCapability(int numberOfAlertFields) {
+ TextField alertText1 = new TextField();
+ alertText1.setName(TextFieldName.alertText1);
+ TextField alertText2 = new TextField();
+ alertText2.setName(TextFieldName.alertText2);
+ TextField alertText3 = new TextField();
+ alertText3.setName(TextFieldName.alertText3);
+ TextField mainField4 = new TextField();
+ mainField4.setName(TextFieldName.mainField4);
+
+ List<TextField> textFieldList = new ArrayList<>();
+
+ textFieldList.add(alertText1);
+ textFieldList.add(alertText2);
+ textFieldList.add(alertText3);
+
+ List<TextField> returnList = new ArrayList<>();
+
+ if (numberOfAlertFields > 0) {
+ for (int i = 0; i < numberOfAlertFields; i++) {
+ returnList.add(textFieldList.get(i));
+ }
+ }
+
+ WindowCapability windowCapability = new WindowCapability();
+ windowCapability.setTextFields(returnList);
+
+ ImageField imageField = new ImageField();
+ imageField.setName(ImageFieldName.alertIcon);
+ List<ImageField> imageFieldList = new ArrayList<>();
+ imageFieldList.add(imageField);
+ windowCapability.setImageFields(imageFieldList);
+
+ windowCapability.setImageFields(imageFieldList);
+
+ SoftButtonCapabilities softButtonCapabilities = new SoftButtonCapabilities();
+ softButtonCapabilities.setImageSupported(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setShortPressAvailable(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setLongPressAvailable(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setUpDownAvailable(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setTextSupported(TestValues.GENERAL_BOOLEAN);
+
+ windowCapability.setSoftButtonCapabilities(Collections.singletonList(softButtonCapabilities));
+ return windowCapability;
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java
index 627a900f6..6ccf93aeb 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java
@@ -7,6 +7,7 @@ import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.file.FileManager;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdatesMode;
import com.smartdevicelink.proxy.rpc.enums.FileType;
import com.smartdevicelink.proxy.rpc.enums.MetadataType;
@@ -18,9 +19,11 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
import static org.mockito.Mockito.mock;
@@ -41,6 +44,9 @@ public class ScreenManagerTests {
ISdl internalInterface = mock(ISdl.class);
when(internalInterface.getTaskmaster()).thenReturn(new Taskmaster.Builder().build());
+ PermissionManager permissionManager = mock(PermissionManager.class);
+
+ when(internalInterface.getPermissionManager()).thenReturn(permissionManager);
FileManager fileManager = mock(FileManager.class);
screenManager = new ScreenManager(internalInterface, fileManager);
screenManager.start(null);
@@ -174,4 +180,91 @@ public class ScreenManagerTests {
assertEquals(screenManager.getSoftButtonObjectById(200), softButtonObject2);
}
+ @Test
+ public void testSettingSoftButtonId() {
+ // Create softButtonObject1
+ SoftButtonState softButtonState1 = new SoftButtonState("object1-state1", "it is", testArtwork);
+ SoftButtonState softButtonState2 = new SoftButtonState("object1-state2", "Wed", testArtwork);
+ SoftButtonObject softButtonObject1 = new SoftButtonObject("object1", Arrays.asList(softButtonState1, softButtonState2), softButtonState1.getName(), null);
+ softButtonObject1.setButtonId(100);
+
+ // Create softButtonObject2
+ SoftButtonState softButtonState3 = new SoftButtonState("object2-state1", "my", testArtwork);
+ SoftButtonState softButtonState4 = new SoftButtonState("object2-state2", "dudes!", null);
+ SoftButtonObject softButtonObject2 = new SoftButtonObject("object2", Arrays.asList(softButtonState3, softButtonState4), softButtonState3.getName(), null);
+ softButtonObject2.setButtonId(200);
+
+ List<SoftButtonObject> softButtonObjects = Arrays.asList(softButtonObject1, softButtonObject2);
+ assertTrue(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER));
+ screenManager.softButtonIDBySoftButtonManager.add(200);
+ assertFalse(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.ALERT_MANAGER));
+ screenManager.softButtonIDByAlertManager.add(100);
+ assertFalse(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER));
+ screenManager.softButtonIDByAlertManager.clear();
+ screenManager.softButtonIDBySoftButtonManager.clear();
+ assertTrue(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.ALERT_MANAGER));
+ softButtonObject1.setButtonId(400);
+ softButtonObject2.setButtonId(500);
+ assertTrue(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER));
+ SoftButtonObject softButtonObject3 = new SoftButtonObject("object1", Arrays.asList(softButtonState1, softButtonState2), softButtonState1.getName(), null);
+ SoftButtonObject softButtonObject4 = new SoftButtonObject("object2", Arrays.asList(softButtonState3, softButtonState4), softButtonState3.getName(), null);
+ assertTrue(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER));
+
+
+
+
+
+ }
+ @Test
+ public void testAssigningIdsToSoftButtonObjects() {
+ SoftButtonObject sbo1, sbo2, sbo3, sbo4, sbo5;
+
+ // Case 1 - don't set id for any button (Manager should set ids automatically starting from 1 and up)
+ sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ screenManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5), BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER);
+ assertEquals("SoftButtonObject id doesn't match the expected value", 1, sbo1.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 2, sbo2.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 3, sbo3.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 4, sbo4.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 5, sbo5.getButtonId());
+
+
+ // Case 2 - Set ids for all buttons (Manager shouldn't alter the ids set by developer)
+ sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo1.setButtonId(100);
+ sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo2.setButtonId(200);
+ sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo3.setButtonId(300);
+ sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo4.setButtonId(400);
+ sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo5.setButtonId(500);
+ screenManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5), BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER);
+ assertEquals("SoftButtonObject id doesn't match the expected value", 100, sbo1.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 200, sbo2.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 300, sbo3.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 400, sbo4.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 500, sbo5.getButtonId());
+
+
+ // Case 3 - Set ids for some buttons (Manager shouldn't alter the ids set by developer. And it should assign ids for the ones that don't have id)
+ sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo1.setButtonId(50);
+ sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo4.setButtonId(100);
+ sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ screenManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5), BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER);
+ assertEquals("SoftButtonObject id doesn't match the expected value", 50, sbo1.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 101, sbo2.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 102, sbo3.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 100, sbo4.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 103, sbo5.getButtonId());
+ }
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java
index 01635dd7d..becc01a74 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java
@@ -8,6 +8,7 @@ import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.file.FileManager;
import com.smartdevicelink.managers.file.MultipleFileCompletionListener;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
import com.smartdevicelink.managers.lifecycle.OnSystemCapabilityListener;
import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
import com.smartdevicelink.protocol.enums.FunctionID;
@@ -129,6 +130,10 @@ public class SoftButtonManagerTests {
doAnswer(onFileManagerUploadAnswer).when(fileManager).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+ // We still want the mock fileManager to use the real implementation for fileNeedsUpload()
+ when(fileManager.fileNeedsUpload(any(SdlFile.class))).thenCallRealMethod();
+
+
// Create softButtonManager
Taskmaster taskmaster = new Taskmaster.Builder().build();
taskmaster.start();
@@ -299,59 +304,6 @@ public class SoftButtonManagerTests {
assertEquals(softButtonState1, softButtonObject1.getCurrentState());
}
- @Test
- public void testAssigningIdsToSoftButtonObjects() {
- SoftButtonObject sbo1, sbo2, sbo3, sbo4, sbo5;
-
- // Case 1 - don't set id for any button (Manager should set ids automatically starting from 1 and up)
- sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- softButtonManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5));
- assertEquals("SoftButtonObject id doesn't match the expected value", 1, sbo1.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 2, sbo2.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 3, sbo3.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 4, sbo4.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 5, sbo5.getButtonId());
-
-
- // Case 2 - Set ids for all buttons (Manager shouldn't alter the ids set by developer)
- sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo1.setButtonId(100);
- sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo2.setButtonId(200);
- sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo3.setButtonId(300);
- sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo4.setButtonId(400);
- sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo5.setButtonId(500);
- softButtonManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5));
- assertEquals("SoftButtonObject id doesn't match the expected value", 100, sbo1.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 200, sbo2.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 300, sbo3.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 400, sbo4.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 500, sbo5.getButtonId());
-
-
- // Case 3 - Set ids for some buttons (Manager shouldn't alter the ids set by developer. And it should assign ids for the ones that don't have id)
- sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo1.setButtonId(50);
- sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo4.setButtonId(100);
- sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- softButtonManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5));
- assertEquals("SoftButtonObject id doesn't match the expected value", 50, sbo1.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 101, sbo2.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 102, sbo3.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 100, sbo4.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 103, sbo5.getButtonId());
- }
-
/**
* Test custom overridden softButtonObject equals method
*/
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperationTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperationTest.java
index a893a76a7..4af040863 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperationTest.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperationTest.java
@@ -176,6 +176,10 @@ public class TextAndGraphicUpdateOperationTest {
// mock things
internalInterface = mock(ISdl.class);
fileManager = mock(FileManager.class);
+
+ // We still want the mock fileManager to use the real implementation for fileNeedsUpload()
+ when(fileManager.fileNeedsUpload(any(SdlFile.class))).thenCallRealMethod();
+
setUpCompletionListener();
textField1 = "It is";
textField2 = "Wednesday";
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java
index 4bdd3ff93..0dde875ea 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java
@@ -61,6 +61,7 @@ public class ChoiceCellTests {
choiceCell.setVoiceCommands(TestValues.GENERAL_STRING_LIST);
choiceCell.setArtwork(artwork);
choiceCell.setSecondaryArtwork(artwork);
+ choiceCell.setUniqueText(TestValues.GENERAL_STRING);
// use getters and assert equality
assertEquals(choiceCell.getText(), TestValues.GENERAL_STRING);
@@ -70,6 +71,7 @@ public class ChoiceCellTests {
assertEquals(choiceCell.getArtwork(), artwork);
assertEquals(choiceCell.getSecondaryArtwork(), artwork);
assertEquals(choiceCell.getChoiceId(), MAX_ID);
+ assertEquals(choiceCell.getUniqueText(), TestValues.GENERAL_STRING);
}
@Test
@@ -87,6 +89,7 @@ public class ChoiceCellTests {
assertEquals(choiceCell.getArtwork(), artwork);
assertEquals(choiceCell.getSecondaryArtwork(), artwork);
assertEquals(choiceCell.getChoiceId(), MAX_ID);
+ assertEquals(choiceCell.getUniqueText(), choiceCell.getText());
choiceCell = new ChoiceCell(TestValues.GENERAL_STRING, TestValues.GENERAL_STRING, TestValues.GENERAL_STRING, TestValues.GENERAL_STRING_LIST, artwork, artwork);
@@ -97,6 +100,8 @@ public class ChoiceCellTests {
assertEquals(choiceCell.getArtwork(), artwork);
assertEquals(choiceCell.getSecondaryArtwork(), artwork);
assertEquals(choiceCell.getChoiceId(), MAX_ID);
+ assertEquals(choiceCell.getUniqueText(), choiceCell.getText());
+
}
@Test
@@ -116,6 +121,11 @@ public class ChoiceCellTests {
choiceCell3.setSecondaryText(TestValues.GENERAL_STRING);
choiceCell3.setTertiaryText(TestValues.GENERAL_STRING);
+ //UniqueText should not be taken into consideration when checking equality
+ choiceCell.setUniqueText(TestValues.GENERAL_STRING);
+ choiceCell2.setUniqueText(TestValues.GENERAL_STRING);
+ choiceCell3.setUniqueText(TestValues.GENERAL_STRING);
+
// Make sure our overridden method works, even though these are different objects in memory
assertTrue(choiceCell.equals(choiceCell2));
assertFalse(choiceCell.equals(choiceCell3));
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java
index c7c0df7e4..16ea857f5 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java
@@ -34,15 +34,19 @@
*/
package com.smartdevicelink.managers.screen.choiceset;
-
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.proxy.rpc.KeyboardCapabilities;
+import com.smartdevicelink.proxy.rpc.KeyboardLayoutCapability;
import com.smartdevicelink.proxy.rpc.KeyboardProperties;
+import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
+import com.smartdevicelink.proxy.rpc.WindowCapability;
import com.smartdevicelink.proxy.rpc.enums.HMILevel;
+import com.smartdevicelink.proxy.rpc.enums.KeyboardInputMask;
import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout;
import com.smartdevicelink.proxy.rpc.enums.KeypressMode;
import com.smartdevicelink.proxy.rpc.enums.Language;
@@ -54,10 +58,12 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import static junit.framework.TestCase.assertEquals;
@@ -86,6 +92,7 @@ public class ChoiceSetManagerTests {
FileManager fileManager = mock(FileManager.class);
taskmaster = new Taskmaster.Builder().build();
when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(7, 0));
csm = new ChoiceSetManager(internalInterface, fileManager);
assertEquals(csm.getState(), BaseSubManager.SETTING_UP);
@@ -148,29 +155,37 @@ public class ChoiceSetManagerTests {
ChoiceSet choiceSet1 = new ChoiceSet("test", Collections.<ChoiceCell>emptyList(), choiceSetSelectionListener);
assertFalse(csm.setUpChoiceSet(choiceSet1));
- // cells cant have duplicate text
+ // Identical cells will not be allowed
ChoiceCell cell1 = new ChoiceCell("test");
ChoiceCell cell2 = new ChoiceCell("test");
ChoiceSet choiceSet2 = new ChoiceSet("test", Arrays.asList(cell1, cell2), choiceSetSelectionListener);
assertFalse(csm.setUpChoiceSet(choiceSet2));
- // cells cannot mix and match VR / non-VR
- ChoiceCell cell3 = new ChoiceCell("test", Collections.singletonList("Test"), null);
- ChoiceCell cell4 = new ChoiceCell("test2");
+ // cells that have duplicate text will be allowed if there is another property to make them unique because a unique name will be assigned and used
+ ChoiceCell cell3 = new ChoiceCell("test");
+ cell3.setSecondaryText("text 1");
+ ChoiceCell cell4 = new ChoiceCell("test");
+ cell4.setSecondaryText("text 2");
ChoiceSet choiceSet3 = new ChoiceSet("test", Arrays.asList(cell3, cell4), choiceSetSelectionListener);
- assertFalse(csm.setUpChoiceSet(choiceSet3));
+ assertTrue(csm.setUpChoiceSet(choiceSet3));
- // VR Commands must be unique
+ // cells cannot mix and match VR / non-VR
ChoiceCell cell5 = new ChoiceCell("test", Collections.singletonList("Test"), null);
- ChoiceCell cell6 = new ChoiceCell("test2", Collections.singletonList("Test"), null);
+ ChoiceCell cell6 = new ChoiceCell("test2");
ChoiceSet choiceSet4 = new ChoiceSet("test", Arrays.asList(cell5, cell6), choiceSetSelectionListener);
assertFalse(csm.setUpChoiceSet(choiceSet4));
- // Passing Case
+ // VR Commands must be unique
ChoiceCell cell7 = new ChoiceCell("test", Collections.singletonList("Test"), null);
- ChoiceCell cell8 = new ChoiceCell("test2", Collections.singletonList("Test2"), null);
+ ChoiceCell cell8 = new ChoiceCell("test2", Collections.singletonList("Test"), null);
ChoiceSet choiceSet5 = new ChoiceSet("test", Arrays.asList(cell7, cell8), choiceSetSelectionListener);
- assertTrue(csm.setUpChoiceSet(choiceSet5));
+ assertFalse(csm.setUpChoiceSet(choiceSet5));
+
+ // Passing Case
+ ChoiceCell cell9 = new ChoiceCell("test", Collections.singletonList("Test"), null);
+ ChoiceCell cell10 = new ChoiceCell("test2", Collections.singletonList("Test2"), null);
+ ChoiceSet choiceSet6 = new ChoiceSet("test", Arrays.asList(cell9, cell10), choiceSetSelectionListener);
+ assertTrue(csm.setUpChoiceSet(choiceSet6));
}
@Test
@@ -193,7 +208,7 @@ public class ChoiceSetManagerTests {
ChoiceCell cell1 = new ChoiceCell("test");
ChoiceCell cell2 = new ChoiceCell("test2");
ChoiceCell cell3 = new ChoiceCell("test3");
- HashSet<ChoiceCell> cellSet = new HashSet<>();
+ LinkedHashSet<ChoiceCell> cellSet = new LinkedHashSet<>();
cellSet.add(cell1);
cellSet.add(cell2);
cellSet.add(cell3);
@@ -209,6 +224,32 @@ public class ChoiceSetManagerTests {
}
@Test
+ public void testAddUniqueNamesToCells() {
+ ChoiceCell cell1 = new ChoiceCell("McDonalds", "1 mile away", null, null, null, null);
+ ChoiceCell cell2 = new ChoiceCell("McDonalds", "2 mile away", null, null, null, null);
+ ChoiceCell cell3 = new ChoiceCell("Starbucks", "3 mile away", null, null, null, null);
+ ChoiceCell cell4 = new ChoiceCell("McDonalds", "4 mile away", null, null, null, null);
+ ChoiceCell cell5 = new ChoiceCell("Starbucks", "5 mile away", null, null, null, null);
+ ChoiceCell cell6 = new ChoiceCell("Meijer", "6 mile away", null, null, null, null);
+ LinkedHashSet<ChoiceCell> cellList = new LinkedHashSet<>();
+ cellList.add(cell1);
+ cellList.add(cell2);
+ cellList.add(cell3);
+ cellList.add(cell4);
+ cellList.add(cell5);
+ cellList.add(cell6);
+
+ csm.addUniqueNamesToCells(cellList);
+
+ assertEquals(cell1.getUniqueText(), "McDonalds");
+ assertEquals(cell2.getUniqueText(), "McDonalds (2)");
+ assertEquals(cell3.getUniqueText(), "Starbucks");
+ assertEquals(cell4.getUniqueText(), "McDonalds (3)");
+ assertEquals(cell5.getUniqueText(), "Starbucks (2)");
+ assertEquals(cell6.getUniqueText(), "Meijer");
+ }
+
+ @Test
public void testChoicesToBeRemovedFromPendingWithArray() {
ChoiceCell cell1 = new ChoiceCell("test");
@@ -289,6 +330,116 @@ public class ChoiceSetManagerTests {
}
@Test
+ public void testDefaultWindowCapabilityNotSet() throws NoSuchFieldException, IllegalAccessException {
+ ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
+ FileManager fileManager = mock(FileManager.class);
+
+ // Test direct set
+ ChoiceSetManager newCSM = new ChoiceSetManager(internalInterface, fileManager);
+ newCSM.setKeyboardConfiguration(newCSM.defaultKeyboardConfiguration());
+ Field field = BaseChoiceSetManager.class.getDeclaredField("keyboardConfiguration");
+ field.setAccessible(true);
+ KeyboardProperties properties = (KeyboardProperties)field.get(newCSM);
+ assertEquals(properties, csm.defaultKeyboardConfiguration());
+
+ // Test presentKeyboard
+ newCSM = new ChoiceSetManager(internalInterface, fileManager);
+ newCSM.presentKeyboard("qwerty", newCSM.defaultKeyboardConfiguration(), null);
+ field = BaseChoiceSetManager.class.getDeclaredField("keyboardConfiguration");
+ field.setAccessible(true);
+ properties = (KeyboardProperties)field.get(newCSM);
+ assertEquals(properties, csm.defaultKeyboardConfiguration());
+ }
+
+ @Test
+ public void testDefaultWindowCapabilityTooManyKeys() throws NoSuchFieldException, IllegalAccessException {
+ ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
+ FileManager fileManager = mock(FileManager.class);
+
+ ChoiceSetManager newCSM = new ChoiceSetManager(internalInterface, fileManager);
+ WindowCapability smallKeysAmountCapability = new WindowCapability();
+ KeyboardCapabilities capabilities = new KeyboardCapabilities();
+ capabilities.setMaskInputCharactersSupported(true);
+ KeyboardLayout layout = KeyboardLayout.QWERTY;
+ capabilities.setSupportedKeyboards(Collections.singletonList(new KeyboardLayoutCapability(layout, 1)));
+ smallKeysAmountCapability.setKeyboardCapabilities(capabilities);
+ newCSM.defaultMainWindowCapability = smallKeysAmountCapability;
+
+ KeyboardProperties setProperties = new KeyboardProperties();
+ setProperties.setKeyboardLayout(layout);
+ setProperties.setCustomKeys(Arrays.asList("1", "2"));
+
+ newCSM.setKeyboardConfiguration(setProperties);
+ Field field = BaseChoiceSetManager.class.getDeclaredField("keyboardConfiguration");
+ field.setAccessible(true);
+
+ KeyboardProperties getProperties = (KeyboardProperties)field.get(newCSM);
+
+ assertEquals(getProperties.getCustomKeys().size(), 1);
+ }
+
+ @Test
+ public void testCustomKeysNull() throws NoSuchFieldException, IllegalAccessException {
+ ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
+ FileManager fileManager = mock(FileManager.class);
+
+ ChoiceSetManager newCSM = new ChoiceSetManager(internalInterface, fileManager);
+ WindowCapability smallKeysAmountCapability = new WindowCapability();
+ KeyboardCapabilities capabilities = new KeyboardCapabilities();
+ capabilities.setMaskInputCharactersSupported(true);
+ KeyboardLayout layout = KeyboardLayout.QWERTY;
+ capabilities.setSupportedKeyboards(Collections.singletonList(new KeyboardLayoutCapability(layout, 0)));
+ smallKeysAmountCapability.setKeyboardCapabilities(capabilities);
+ newCSM.defaultMainWindowCapability = smallKeysAmountCapability;
+
+ KeyboardProperties setProperties = new KeyboardProperties();
+ setProperties.setKeyboardLayout(layout);
+ setProperties.setCustomKeys(new ArrayList<String>());
+
+ newCSM.setKeyboardConfiguration(setProperties);
+ Field field = BaseChoiceSetManager.class.getDeclaredField("keyboardConfiguration");
+ field.setAccessible(true);
+
+ KeyboardProperties getProperties = (KeyboardProperties)field.get(newCSM);
+
+ assertNull(getProperties.getCustomKeys());
+ }
+
+ @Test
+ public void testMaskInputCharactersNotSupported() throws NoSuchFieldException, IllegalAccessException {
+ ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
+ FileManager fileManager = mock(FileManager.class);
+
+ ChoiceSetManager newCSM = new ChoiceSetManager(internalInterface, fileManager);
+ WindowCapability maskInputNotSupportedCapability = new WindowCapability();
+ KeyboardCapabilities capabilities = new KeyboardCapabilities();
+
+ capabilities.setMaskInputCharactersSupported(false);
+ KeyboardLayout layout = KeyboardLayout.QWERTY;
+ capabilities.setSupportedKeyboards(Collections.singletonList(new KeyboardLayoutCapability(layout, 0)));
+
+ maskInputNotSupportedCapability.setKeyboardCapabilities(capabilities);
+
+ newCSM.defaultMainWindowCapability = maskInputNotSupportedCapability;
+
+ KeyboardProperties setProperties = new KeyboardProperties();
+ setProperties.setKeyboardLayout(layout);
+ setProperties.setMaskInputCharacters(KeyboardInputMask.DISABLE_INPUT_KEY_MASK);
+
+ newCSM.setKeyboardConfiguration(setProperties);
+ Field field = BaseChoiceSetManager.class.getDeclaredField("keyboardConfiguration");
+ field.setAccessible(true);
+
+ KeyboardProperties getProperties = (KeyboardProperties)field.get(newCSM);
+
+ assertNull(getProperties.getMaskInputCharacters());
+ }
+
+ @Test
public void testDismissingExecutingKeyboard() {
Integer testCancelID = 42;
PresentKeyboardOperation testKeyboardOp = mock(PresentKeyboardOperation.class);
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetTests.java
index 4eeb04927..1e2c7359a 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetTests.java
@@ -79,6 +79,23 @@ public class ChoiceSetTests {
assertEquals(choiceSet.getTimeout(), defaultTimeout);
assertEquals(choiceSet.getChoices(), choices);
assertEquals(choiceSet.getChoiceSetSelectionListener(), listener);
+
+ // Test timeout and default timeout
+ choiceSet.setDefaultTimeout(20);
+ assertEquals(choiceSet.getDefaultTimeout(), 20);
+ choiceSet.setDefaultTimeout(1);
+ assertEquals(choiceSet.getDefaultTimeout(), 5);
+ choiceSet.setDefaultTimeout(101);
+ assertEquals(choiceSet.getDefaultTimeout(), 100);
+
+ choiceSet.setTimeout(20);
+ assertEquals(choiceSet.getTimeout().intValue(), 20);
+ choiceSet.setTimeout(1);
+ assertEquals(choiceSet.getTimeout().intValue(), 5);
+ choiceSet.setTimeout(101);
+ assertEquals(choiceSet.getTimeout().intValue(), 100);
+ // Reset default value for other unit test
+ choiceSet.setDefaultTimeout(10);
}
@Test
@@ -86,6 +103,7 @@ public class ChoiceSetTests {
// first constructor was tested in previous method, use the rest here
ChoiceSet choiceSet = new ChoiceSet(TestValues.GENERAL_STRING, layout, TestValues.GENERAL_INTEGER, TestValues.GENERAL_STRING, TestValues.GENERAL_STRING, TestValues.GENERAL_STRING, TestValues.GENERAL_VRHELPITEM_LIST, TestValues.GENERAL_KEYBOARDPROPERTIES, choices, listener);
+
assertEquals(choiceSet.getTitle(), TestValues.GENERAL_STRING);
assertEquals(choiceSet.getInitialPrompt().get(0).getText(), TestValues.GENERAL_STRING);
assertEquals(choiceSet.getHelpPrompt().get(0).getText(), TestValues.GENERAL_STRING);
@@ -95,6 +113,7 @@ public class ChoiceSetTests {
assertEquals(choiceSet.getChoices(), choices);
assertEquals(choiceSet.getChoiceSetSelectionListener(), listener);
+
ChoiceSet choiceSet2 = new ChoiceSet(TestValues.GENERAL_STRING, layout, TestValues.GENERAL_INTEGER, TestValues.GENERAL_TTSCHUNK_LIST, TestValues.GENERAL_TTSCHUNK_LIST, TestValues.GENERAL_TTSCHUNK_LIST, TestValues.GENERAL_VRHELPITEM_LIST, TestValues.GENERAL_KEYBOARDPROPERTIES, choices, listener);
assertEquals(choiceSet2.getTitle(), TestValues.GENERAL_STRING);
assertEquals(choiceSet2.getInitialPrompt(), TestValues.GENERAL_TTSCHUNK_LIST);
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java
index db592dfb6..9e879a73a 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java
@@ -40,6 +40,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.file.FileManager;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
import com.smartdevicelink.proxy.rpc.ImageField;
import com.smartdevicelink.proxy.rpc.TextField;
import com.smartdevicelink.proxy.rpc.WindowCapability;
@@ -57,13 +58,16 @@ import org.junit.runner.RunWith;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
@RunWith(AndroidJUnit4.class)
public class PreloadChoicesOperationTests {
@@ -78,7 +82,7 @@ public class PreloadChoicesOperationTests {
ChoiceCell cell1 = new ChoiceCell("cell 1");
ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK);
- HashSet<ChoiceCell> cellsToPreload = new HashSet<>();
+ LinkedHashSet<ChoiceCell> cellsToPreload = new LinkedHashSet<>();
cellsToPreload.add(cell1);
cellsToPreload.add(cell2);
@@ -101,6 +105,10 @@ public class PreloadChoicesOperationTests {
ISdl internalInterface = mock(ISdl.class);
FileManager fileManager = mock(FileManager.class);
+
+ // We still want the mock fileManager to use the real implementation for fileNeedsUpload()
+ when(fileManager.fileNeedsUpload(any(SdlFile.class))).thenCallRealMethod();
+
preloadChoicesOperation = new PreloadChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null);
}
@@ -111,7 +119,7 @@ public class PreloadChoicesOperationTests {
ChoiceCell cell1 = new ChoiceCell("cell 1");
ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK);
- HashSet<ChoiceCell> cellsToPreload = new HashSet<>();
+ LinkedHashSet<ChoiceCell> cellsToPreload = new LinkedHashSet<>();
cellsToPreload.add(cell1);
cellsToPreload.add(cell2);
@@ -128,7 +136,7 @@ public class PreloadChoicesOperationTests {
ChoiceCell cell1 = new ChoiceCell("cell 1");
ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK);
- HashSet<ChoiceCell> cellsToPreload = new HashSet<>();
+ LinkedHashSet<ChoiceCell> cellsToPreload = new LinkedHashSet<>();
cellsToPreload.add(cell1);
cellsToPreload.add(cell2);
@@ -147,13 +155,6 @@ public class PreloadChoicesOperationTests {
preloadChoicesOperationEmptyCapability = new PreloadChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null);
}
-
- @Test
- public void testArtworkNeedsUpload() {
- boolean test = preloadChoicesOperation.artworkNeedsUpload(TestValues.GENERAL_ARTWORK);
- assertTrue(test);
- }
-
@Test
public void testArtworksToUpload() {
List<SdlArtwork> artworksToUpload = preloadChoicesOperation.artworksToUpload();
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java
index dfd0477e5..b0e703e33 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java
@@ -70,6 +70,10 @@ public class MenuCellTests {
menuCell.setVoiceCommands(TestValues.GENERAL_STRING_LIST);
menuCell.setMenuSelectionListener(menuSelectionListener);
menuCell.setSubMenuLayout(TestValues.GENERAL_MENU_LAYOUT);
+ menuCell.setSecondaryText(TestValues.GENERAL_STRING);
+ menuCell.setTertiaryText(TestValues.GENERAL_STRING);
+ menuCell.setSecondaryArtwork(TestValues.GENERAL_ARTWORK);
+ menuCell.setUniqueTitle(TestValues.GENERAL_STRING);
// use getters and assert equality
assertEquals(menuCell.getTitle(), TestValues.GENERAL_STRING);
@@ -79,6 +83,10 @@ public class MenuCellTests {
assertEquals(menuCell.getCellId(), TestValues.GENERAL_MENU_MAX_ID);
assertEquals(menuCell.getParentCellId(), TestValues.GENERAL_MENU_MAX_ID);
assertEquals(menuCell.getSubMenuLayout(), TestValues.GENERAL_MENU_LAYOUT);
+ assertEquals(menuCell.getSecondaryText(), TestValues.GENERAL_STRING);
+ assertEquals(menuCell.getTertiaryText(), TestValues.GENERAL_STRING);
+ assertEquals(menuCell.getSecondaryArtwork(), TestValues.GENERAL_ARTWORK);
+ assertEquals(menuCell.getUniqueTitle(), TestValues.GENERAL_STRING);
}
@Test
@@ -91,16 +99,42 @@ public class MenuCellTests {
assertEquals(menuCell3.getIcon(), TestValues.GENERAL_ARTWORK);
assertEquals(menuCell3.getVoiceCommands(), TestValues.GENERAL_STRING_LIST);
assertEquals(menuCell3.getMenuSelectionListener(), menuSelectionListener);
+ assertEquals(menuCell3.getUniqueTitle(), TestValues.GENERAL_STRING);
MenuCell menuCell4 = new MenuCell(TestValues.GENERAL_STRING, null, null, menuSelectionListener);
assertEquals(menuCell4.getTitle(), TestValues.GENERAL_STRING);
assertEquals(menuCell4.getMenuSelectionListener(), menuSelectionListener);
+ assertEquals(menuCell4.getUniqueTitle(), TestValues.GENERAL_STRING);
MenuCell menuCell5 = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_MENU_LAYOUT, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_MENUCELL_LIST);
assertEquals(menuCell5.getTitle(), TestValues.GENERAL_STRING);
assertEquals(menuCell5.getIcon(), TestValues.GENERAL_ARTWORK);
assertEquals(menuCell5.getSubMenuLayout(), TestValues.GENERAL_MENU_LAYOUT);
assertEquals(menuCell5.getSubCells(), TestValues.GENERAL_MENUCELL_LIST);
+ assertEquals(menuCell5.getUniqueTitle(), TestValues.GENERAL_STRING);
+
+
+ MenuCell menuCell6 = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_STRING, TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_STRING_LIST, menuSelectionListener);
+ assertEquals(menuCell6.getTitle(), TestValues.GENERAL_STRING);
+ assertEquals(menuCell6.getIcon(), TestValues.GENERAL_ARTWORK);
+ assertEquals(menuCell6.getSecondaryText(), TestValues.GENERAL_STRING);
+ assertEquals(menuCell6.getTertiaryText(), TestValues.GENERAL_STRING);
+ assertEquals(menuCell6.getSecondaryArtwork(), TestValues.GENERAL_ARTWORK);
+ assertEquals(menuCell6.getVoiceCommands(), TestValues.GENERAL_STRING_LIST);
+ assertEquals(menuCell6.getMenuSelectionListener(), menuSelectionListener);
+
+ MenuCell menuCell7 = new MenuCell(TestValues.GENERAL_STRING, null, null, null, null, null, menuSelectionListener);
+ assertEquals(menuCell7.getTitle(), TestValues.GENERAL_STRING);
+ assertEquals(menuCell7.getMenuSelectionListener(), menuSelectionListener);
+
+ MenuCell menuCell8 = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_STRING, TestValues.GENERAL_STRING, TestValues.GENERAL_MENU_LAYOUT, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_MENUCELL_LIST);
+ assertEquals(menuCell8.getTitle(), TestValues.GENERAL_STRING);
+ assertEquals(menuCell8.getIcon(), TestValues.GENERAL_ARTWORK);
+ assertEquals(menuCell8.getSecondaryText(), TestValues.GENERAL_STRING);
+ assertEquals(menuCell8.getTertiaryText(), TestValues.GENERAL_STRING);
+ assertEquals(menuCell8.getSecondaryArtwork(), TestValues.GENERAL_ARTWORK);
+ assertEquals(menuCell8.getSubMenuLayout(), TestValues.GENERAL_MENU_LAYOUT);
+ assertEquals(menuCell8.getSubCells(), TestValues.GENERAL_MENUCELL_LIST);
}
@Test
@@ -109,7 +143,13 @@ public class MenuCellTests {
//We should use assertTrue (or assertFalse) because we want to use the overridden equals() method
MenuCell menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_STRING_LIST, menuSelectionListener);
+ menuCell.setSecondaryText(TestValues.GENERAL_STRING);
+ menuCell.setTertiaryText(TestValues.GENERAL_STRING);
+ menuCell.setSecondaryArtwork(TestValues.GENERAL_ARTWORK);
MenuCell menuCell2 = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_STRING_LIST, menuSelectionListener);
+ menuCell2.setSecondaryText(TestValues.GENERAL_STRING);
+ menuCell2.setTertiaryText(TestValues.GENERAL_STRING);
+ menuCell2.setSecondaryArtwork(TestValues.GENERAL_ARTWORK);
// these are the same object, should be equal.
assertTrue(menuCell.equals(menuCell));
@@ -126,6 +166,9 @@ public class MenuCellTests {
@Test
public void testClone() {
MenuCell original = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_STRING_LIST, menuSelectionListener);
+ original.setSecondaryText(TestValues.GENERAL_STRING);
+ original.setTertiaryText(TestValues.GENERAL_STRING);
+ original.setSecondaryArtwork(TestValues.GENERAL_ARTWORK);
MenuCell clone = original.clone();
assertNotNull(clone);
@@ -134,8 +177,12 @@ public class MenuCellTests {
assertEquals(original.getTitle(), clone.getTitle());
assertEquals(original.getCellId(), clone.getCellId());
assertEquals(original.getParentCellId(), clone.getParentCellId());
+ assertEquals(original.getUniqueTitle(), clone.getUniqueTitle());
+ assertEquals(original.getSecondaryText(), clone.getSecondaryText());
+ assertEquals(original.getTertiaryText(), clone.getTertiaryText());
SdlArtworkTests.equalTest(original.getIcon(), clone.getIcon());
+ SdlArtworkTests.equalTest(original.getSecondaryArtwork(), clone.getSecondaryArtwork());
//Test subcells
List<MenuCell> subcells = new ArrayList<>();
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java
index 43392b626..2419023f6 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java
@@ -75,6 +75,7 @@ import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -89,6 +90,7 @@ public class MenuManagerTests {
private MenuManager menuManager;
private List<MenuCell> cells;
private MenuCell mainCell1, mainCell4;
+ final ISdl internalInterface = mock(ISdl.class);
// SETUP / HELPERS
@@ -97,7 +99,6 @@ public class MenuManagerTests {
cells = createTestCells();
- final ISdl internalInterface = mock(ISdl.class);
FileManager fileManager = mock(FileManager.class);
// When internalInterface.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, OnRPCNotificationListener) is called
@@ -136,6 +137,11 @@ public class MenuManagerTests {
};
doAnswer(answer).when(internalInterface).sendRPC(any(SetGlobalProperties.class));
+ SdlMsgVersion version = new SdlMsgVersion();
+ version.setMajorVersion(7);
+ version.setMinorVersion(0);
+ doReturn(version).when(internalInterface).getSdlMsgVersion();
+
menuManager = new MenuManager(internalInterface, fileManager);
// Check some stuff during setup
@@ -530,6 +536,68 @@ public class MenuManagerTests {
}
+ @Test
+ public void testSettingUniqueMenuNames() {
+ //Testing using SDLMsgVersion 7.0, at this version uniqueTitles will be set
+
+ // Make sure we can send an empty menu with no issues
+ // start fresh
+ menuManager.oldMenuCells = null;
+ menuManager.menuCells = null;
+ menuManager.inProgressUpdate = null;
+ menuManager.waitingUpdateMenuCells = null;
+ menuManager.waitingOnHMIUpdate = false;
+
+ menuManager.currentHMILevel = HMILevel.HMI_FULL;
+ // send new cells. They should set the old way
+ List<MenuCell> oldMenu = createDynamicMenu6_forUniqueNamesTest();
+ menuManager.setMenuCells(oldMenu);
+ assertEquals(menuManager.menuCells.size(), 4);
+ assertEquals(menuManager.menuCells.get(0).getUniqueTitle(), "A");
+ assertEquals(menuManager.menuCells.get(1).getUniqueTitle(), "A (2)");
+ assertEquals(menuManager.menuCells.get(2).getUniqueTitle(), "A (3)");
+ assertEquals(menuManager.menuCells.get(3).getUniqueTitle(), "A (4)");
+
+ assertEquals((menuManager.menuCells.get(3).getSubCells().size()), 4);
+ assertEquals(menuManager.menuCells.get(3).getSubCells().get(0).getUniqueTitle(), "A");
+ assertEquals(menuManager.menuCells.get(3).getSubCells().get(1).getUniqueTitle(), "A (2)");
+ assertEquals(menuManager.menuCells.get(3).getSubCells().get(2).getUniqueTitle(), "A (3)");
+ assertEquals(menuManager.menuCells.get(3).getSubCells().get(3).getUniqueTitle(), "A (4)");
+ }
+
+ @Test
+ public void testAllowingNonUniqueTitles() {
+ //Testing using SDLMsgVersion 7.1, at this version uniqueTitles will be set
+ SdlMsgVersion version = new SdlMsgVersion();
+ version.setMajorVersion(7);
+ version.setMinorVersion(1);
+ doReturn(version).when(internalInterface).getSdlMsgVersion();
+
+ // Make sure we can send an empty menu with no issues
+ // start fresh
+ menuManager.oldMenuCells = null;
+ menuManager.menuCells = null;
+ menuManager.inProgressUpdate = null;
+ menuManager.waitingUpdateMenuCells = null;
+ menuManager.waitingOnHMIUpdate = false;
+
+ menuManager.currentHMILevel = HMILevel.HMI_FULL;
+ // send new cells. They should set the old way
+ List<MenuCell> oldMenu = createDynamicMenu6_forUniqueNamesTest();
+ menuManager.setMenuCells(oldMenu);
+ assertEquals(menuManager.menuCells.size(), 4);
+ assertEquals(menuManager.menuCells.get(0).getUniqueTitle(), "A");
+ assertEquals(menuManager.menuCells.get(1).getUniqueTitle(), "A");
+ assertEquals(menuManager.menuCells.get(2).getUniqueTitle(), "A");
+ assertEquals(menuManager.menuCells.get(3).getUniqueTitle(), "A");
+
+ assertEquals((menuManager.menuCells.get(3).getSubCells().size()), 4);
+ assertEquals(menuManager.menuCells.get(3).getSubCells().get(0).getUniqueTitle(), "A");
+ assertEquals(menuManager.menuCells.get(3).getSubCells().get(1).getUniqueTitle(), "A");
+ assertEquals(menuManager.menuCells.get(3).getSubCells().get(2).getUniqueTitle(), "A");
+ assertEquals(menuManager.menuCells.get(3).getSubCells().get(3).getUniqueTitle(), "A");
+ }
+
// HELPERS
// Emulate what happens when Core sends OnHMIStatus notification
@@ -754,4 +822,31 @@ public class MenuManagerTests {
}
+ private List<MenuCell> createDynamicMenu6_forUniqueNamesTest() {
+ MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class);
+ MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class);
+ MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class);
+ MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class);
+
+ SdlArtwork icon1 = new SdlArtwork("livio", FileType.GRAPHIC_PNG, R.drawable.sdl_lockscreen_icon, false);
+ SdlArtwork icon2 = new SdlArtwork("livio2", FileType.GRAPHIC_PNG, R.drawable.ic_sdl, false);
+ SdlArtwork icon3 = new SdlArtwork("livio3", FileType.GRAPHIC_PNG, R.drawable.sdl_tray_icon, false);
+ SdlArtwork icon4 = new SdlArtwork("livio4", FileType.GRAPHIC_PNG, R.drawable.spp_error, false);
+
+ MenuCell A = new MenuCell("A", icon1, null, menuSelectionListenerA);
+
+ MenuCell B = new MenuCell("A", icon2, null, menuSelectionListenerB);
+
+ MenuCell C = new MenuCell("A", icon3, null, menuSelectionListenerC);
+
+ MenuCell subA = new MenuCell("A", icon1, null, menuSelectionListenerA);
+ MenuCell subB = new MenuCell("A", icon2, null, menuSelectionListenerB);
+ MenuCell subC = new MenuCell("A", icon3, null, menuSelectionListenerC);
+ MenuCell subD = new MenuCell("A", icon4, null, menuSelectionListenerD);
+
+ MenuCell D = new MenuCell("A", MenuLayout.LIST, icon4, Arrays.asList(subA, subB, subC, subD));
+
+ return Arrays.asList(A, B, C, D);
+ }
+
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandManagerTests.java
index 98fc161db..8fd025aac 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandManagerTests.java
@@ -34,6 +34,7 @@ package com.smartdevicelink.managers.screen.menu;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.ISdl;
@@ -56,7 +57,6 @@ import java.util.Collections;
import java.util.List;
import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
@@ -66,6 +66,7 @@ import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
@RunWith(AndroidJUnit4.class)
public class VoiceCommandManagerTests {
@@ -112,19 +113,19 @@ public class VoiceCommandManagerTests {
};
doAnswer(onCommandAnswer).when(internalInterface).addOnRPCNotificationListener(eq(FunctionID.ON_COMMAND), any(OnRPCNotificationListener.class));
+ Taskmaster taskmaster = new Taskmaster.Builder().build();
+ taskmaster.start();
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
voiceCommandManager = new VoiceCommandManager(internalInterface);
// Check some stuff during setup
- assertEquals(voiceCommandManager.currentHMILevel, HMILevel.HMI_NONE);
+ assertNull(voiceCommandManager.currentHMILevel);
assertEquals(voiceCommandManager.getState(), BaseSubManager.SETTING_UP);
assertEquals(voiceCommandManager.lastVoiceCommandId, voiceCommandIdMin);
- assertFalse(voiceCommandManager.hasQueuedUpdate);
- assertFalse(voiceCommandManager.waitingOnHMIUpdate);
assertNotNull(voiceCommandManager.commandListener);
assertNotNull(voiceCommandManager.hmiListener);
assertNull(voiceCommandManager.voiceCommands);
- assertNull(voiceCommandManager.oldVoiceCommands);
- assertNull(voiceCommandManager.inProgressUpdate);
+ assertNull(voiceCommandManager.currentVoiceCommands);
}
@After
@@ -134,11 +135,8 @@ public class VoiceCommandManagerTests {
assertEquals(voiceCommandManager.lastVoiceCommandId, voiceCommandIdMin);
assertNull(voiceCommandManager.voiceCommands);
- assertNull(voiceCommandManager.oldVoiceCommands);
+ assertNull(voiceCommandManager.currentVoiceCommands);
assertNull(voiceCommandManager.currentHMILevel);
- assertNull(voiceCommandManager.inProgressUpdate);
- assertFalse(voiceCommandManager.hasQueuedUpdate);
- assertFalse(voiceCommandManager.waitingOnHMIUpdate);
// after everything, make sure we are in the correct state
assertEquals(voiceCommandManager.getState(), BaseSubManager.SHUTDOWN);
}
@@ -162,9 +160,6 @@ public class VoiceCommandManagerTests {
voiceCommandManager.currentHMILevel = HMILevel.HMI_NONE;
voiceCommandManager.setVoiceCommands(commands);
- // updating voice commands before HMI is ready
- assertNull(voiceCommandManager.inProgressUpdate);
- assertTrue(voiceCommandManager.waitingOnHMIUpdate);
// these are the 2 commands we have waiting
assertEquals(voiceCommandManager.voiceCommands.size(), 2);
assertEquals(voiceCommandManager.currentHMILevel, HMILevel.HMI_NONE);
@@ -173,20 +168,10 @@ public class VoiceCommandManagerTests {
sendFakeCoreOnHMIFullNotifications();
// Listener should be triggered - which sets new HMI level and should proceed to send our pending update
assertEquals(voiceCommandManager.currentHMILevel, HMILevel.HMI_FULL);
- // This being false means it received the hmi notification and sent the pending commands
- assertFalse(voiceCommandManager.waitingOnHMIUpdate);
}
@Test
public void testUpdatingCommands() {
-
- // we have previously sent 2 VoiceCommand objects. we will now update it and have just one
-
- // make sure the system returns us 2 delete commands
- assertEquals(voiceCommandManager.deleteCommandsForVoiceCommands(commands).size(), 2);
- // when we only send one command to update, we should only be returned one add command
- assertEquals(voiceCommandManager.addCommandsForVoiceCommands(Collections.singletonList(command)).size(), 1);
-
// Send a new single command, and test that its listener works, as it gets called from the VCM
voiceCommandManager.setVoiceCommands(Collections.singletonList(command3));
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java
new file mode 100644
index 000000000..1c6f2b1c4
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperationTests.java
@@ -0,0 +1,297 @@
+package com.smartdevicelink.managers.screen.menu;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.rpc.AddCommand;
+import com.smartdevicelink.proxy.rpc.AddCommandResponse;
+import com.smartdevicelink.proxy.rpc.DeleteCommand;
+import com.smartdevicelink.proxy.rpc.DeleteCommandResponse;
+import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
+import com.smartdevicelink.util.DebugTool;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+@RunWith(AndroidJUnit4.class)
+public class VoiceCommandUpdateOperationTests {
+
+ private static final String TAG = "VoiceCommandReplaceOperationTests";
+ VoiceCommandUpdateOperation voiceCommandUpdateOperation;
+ ISdl internalInterface;
+ VoiceCommandUpdateOperation.VoiceCommandChangesListener voiceCommandChangesListener;
+ List<String> list1 = Collections.singletonList("Command one");
+ List<String> list2 = Collections.singletonList("Command two");
+ List<String> list3 = Collections.singletonList("Command three");
+ List<String> list4 = Collections.singletonList("Command four");
+
+
+ VoiceCommand voiceCommand1 = new VoiceCommand(list1, new VoiceCommandSelectionListener() {
+ @Override
+ public void onVoiceCommandSelected() {
+
+ }
+ });
+
+ VoiceCommand voiceCommand2 = new VoiceCommand(list2, new VoiceCommandSelectionListener() {
+ @Override
+ public void onVoiceCommandSelected() {
+
+ }
+ });
+
+ VoiceCommand voiceCommand3 = new VoiceCommand(list3, new VoiceCommandSelectionListener() {
+ @Override
+ public void onVoiceCommandSelected() {
+
+ }
+ });
+
+ VoiceCommand voiceCommand4 = new VoiceCommand(list4, new VoiceCommandSelectionListener() {
+ @Override
+ public void onVoiceCommandSelected() {
+
+ }
+ });
+
+ List<VoiceCommand> deleteList = new ArrayList<>();
+ List<VoiceCommand> addList = new ArrayList<>();
+
+ @Before
+ public void setup() {
+ deleteList.clear();
+ addList.clear();
+ voiceCommand1.setCommandId(1);
+ voiceCommand2.setCommandId(2);
+ voiceCommand3.setCommandId(3);
+ voiceCommand4.setCommandId(4);
+ deleteList.add(voiceCommand1);
+ deleteList.add(voiceCommand2);
+ addList.add(voiceCommand3);
+ addList.add(voiceCommand4);
+ }
+
+ @Test
+ public void verifyCanceledTaskDoesNotSendAnyRPCs() {
+ internalInterface = mock(ISdl.class);
+
+ voiceCommandChangesListener = new VoiceCommandUpdateOperation.VoiceCommandChangesListener() {
+ @Override
+ public void updateVoiceCommands(List<VoiceCommand> newCurrentVoiceCommands, HashMap<RPCRequest, String> errorObject) {
+
+ }
+ };
+
+ voiceCommandUpdateOperation = new VoiceCommandUpdateOperation(internalInterface, deleteList, addList, voiceCommandChangesListener);
+ voiceCommandUpdateOperation.cancelTask();
+ voiceCommandUpdateOperation.onExecute();
+ verify(internalInterface, times(0)).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
+ }
+
+ @Test
+ public void verifyErrorObjectIsSetCorrectly() {
+ internalInterface = mock(ISdl.class);
+
+ doAnswer(new Answer<Void>() {
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ DeleteCommand deleteCommand = null;
+ AddCommand addCommand = null;
+
+ try {
+ deleteCommand = (DeleteCommand) ((List<Object>)invocation.getArguments()[0]).get(0);
+ } catch (Exception e) {
+ DebugTool.logInfo(TAG, "not DeleteCommands: " + e);
+ }
+
+ try {
+ addCommand = (AddCommand) ((List<Object>)invocation.getArguments()[0]).get(0);
+ } catch (Exception e) {
+ DebugTool.logInfo(TAG, "not AddCommands: " + e);
+ }
+
+ if (deleteCommand != null) {
+ DeleteCommandResponse badResponse = new DeleteCommandResponse();
+ badResponse.setSuccess(false);
+ List<DeleteCommand> deleteCommands = ((List<DeleteCommand>)invocation.getArguments()[0]);
+ for (DeleteCommand command : deleteCommands) {
+ badResponse.setCorrelationID(command.getCorrelationID());
+ ((OnMultipleRequestListener)invocation.getArguments()[1]).onResponse(command.getCorrelationID(), badResponse);
+ }
+ } else if (addCommand != null) {
+ AddCommandResponse badResponse = new AddCommandResponse();
+ badResponse.setSuccess(false);
+ List<AddCommand> addCommands = ((List<AddCommand>)invocation.getArguments()[0]);
+ for (AddCommand command : addCommands) {
+ badResponse.setCorrelationID(command.getCorrelationID());
+ ((OnMultipleRequestListener)invocation.getArguments()[1]).onResponse(command.getCorrelationID(), badResponse);
+ }
+ } else {
+ DebugTool.logInfo(TAG, "CallBacks failed");
+ return null;
+ }
+ ((OnMultipleRequestListener)invocation.getArguments()[1]).onFinished();
+ return null;
+ }
+ }).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
+
+ voiceCommandChangesListener = new VoiceCommandUpdateOperation.VoiceCommandChangesListener() {
+ @Override
+ public void updateVoiceCommands(List<VoiceCommand> newCurrentVoiceCommands, HashMap<RPCRequest, String> errorObject) {
+ assertEquals(4, errorObject.size());
+ assertEquals(2, newCurrentVoiceCommands.size());
+ assertEquals(newCurrentVoiceCommands.get(0).getVoiceCommands().get(0), voiceCommand1.getVoiceCommands().get(0));
+ assertEquals(newCurrentVoiceCommands.get(1).getVoiceCommands().get(0), voiceCommand2.getVoiceCommands().get(0));
+ }
+ };
+
+ VoiceCommandUpdateOperation.VoiceCommandChangesListener listenerSpy = Mockito.spy(voiceCommandChangesListener);
+
+ voiceCommandUpdateOperation = new VoiceCommandUpdateOperation(internalInterface, deleteList, addList, listenerSpy);
+ voiceCommandUpdateOperation.onExecute();
+
+ verify(listenerSpy, times(1)).updateVoiceCommands(any(List.class), any(HashMap.class));
+ }
+
+ @Test
+ public void verifySuccessIsSetCorrectly() {
+ internalInterface = mock(ISdl.class);
+
+ doAnswer(new Answer<Void>() {
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ DeleteCommand deleteCommand = null;
+ AddCommand addCommand = null;
+
+ try {
+ deleteCommand = (DeleteCommand) ((List<Object>)invocation.getArguments()[0]).get(0);
+ } catch (Exception e) {
+ DebugTool.logInfo(TAG, "not DeleteCommands: " + e);
+ }
+
+ try {
+ addCommand = (AddCommand) ((List<Object>)invocation.getArguments()[0]).get(0);
+ } catch (Exception e) {
+ DebugTool.logInfo(TAG, "not AddCommands: " + e);
+ }
+
+ if (deleteCommand != null) {
+ DeleteCommandResponse successResponse = new DeleteCommandResponse();
+ successResponse.setSuccess(true);
+ List<DeleteCommand> deleteCommands = ((List<DeleteCommand>)invocation.getArguments()[0]);
+ for (DeleteCommand command : deleteCommands) {
+ successResponse.setCorrelationID(command.getCorrelationID());
+ ((OnMultipleRequestListener)invocation.getArguments()[1]).onResponse(command.getCorrelationID(), successResponse);
+ }
+ } else if (addCommand != null) {
+ AddCommandResponse successResponse = new AddCommandResponse();
+ successResponse.setSuccess(true);
+ List<AddCommand> addCommands = ((List<AddCommand>)invocation.getArguments()[0]);
+ for (AddCommand command : addCommands) {
+ successResponse.setCorrelationID(command.getCorrelationID());
+ ((OnMultipleRequestListener)invocation.getArguments()[1]).onResponse(command.getCorrelationID(), successResponse);
+ }
+ } else {
+ DebugTool.logInfo(TAG, "CallBacks failed");
+ return null;
+ }
+ ((OnMultipleRequestListener)invocation.getArguments()[1]).onFinished();
+ return null;
+ }
+ }).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
+
+ voiceCommandChangesListener = new VoiceCommandUpdateOperation.VoiceCommandChangesListener() {
+ @Override
+ public void updateVoiceCommands(List<VoiceCommand> newCurrentVoiceCommands, HashMap<RPCRequest, String> errorObject) {
+ assertEquals(0, errorObject.size());
+ assertEquals(2, newCurrentVoiceCommands.size());
+ assertEquals(newCurrentVoiceCommands.get(0).getVoiceCommands().get(0), voiceCommand3.getVoiceCommands().get(0));
+ assertEquals(newCurrentVoiceCommands.get(1).getVoiceCommands().get(0), voiceCommand4.getVoiceCommands().get(0));
+ }
+ };
+
+ VoiceCommandUpdateOperation.VoiceCommandChangesListener listenerSpy = Mockito.spy(voiceCommandChangesListener);
+
+ voiceCommandUpdateOperation = new VoiceCommandUpdateOperation(internalInterface, deleteList, addList, listenerSpy);
+ voiceCommandUpdateOperation.onExecute();
+
+ verify(listenerSpy, times(1)).updateVoiceCommands(any(List.class), any(HashMap.class));
+ }
+
+ @Test
+ public void verifySendingAnEmptyListWillClearVoiceCommands() {
+ internalInterface = mock(ISdl.class);
+
+ doAnswer(new Answer<Void>() {
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ DeleteCommand deleteCommand = null;
+ AddCommand addCommand = null;
+
+ try {
+ deleteCommand = (DeleteCommand) ((List<Object>)invocation.getArguments()[0]).get(0);
+ } catch (Exception e) {
+ DebugTool.logInfo(TAG, "not DeleteCommands: " + e);
+ }
+
+ try {
+ addCommand = (AddCommand) ((List<Object>)invocation.getArguments()[0]).get(0);
+ } catch (Exception e) {
+ DebugTool.logInfo(TAG, "not AddCommands: " + e);
+ }
+
+ if (deleteCommand != null) {
+ DeleteCommandResponse successResponse = new DeleteCommandResponse();
+ successResponse.setSuccess(true);
+ List<DeleteCommand> deleteCommands = ((List<DeleteCommand>)invocation.getArguments()[0]);
+ for (DeleteCommand command : deleteCommands) {
+ successResponse.setCorrelationID(command.getCorrelationID());
+ ((OnMultipleRequestListener)invocation.getArguments()[1]).onResponse(command.getCorrelationID(), successResponse);
+ }
+ } else if (addCommand != null) {
+ AddCommandResponse successResponse = new AddCommandResponse();
+ successResponse.setSuccess(true);
+ List<AddCommand> addCommands = ((List<AddCommand>)invocation.getArguments()[0]);
+ for (AddCommand command : addCommands) {
+ successResponse.setCorrelationID(command.getCorrelationID());
+ ((OnMultipleRequestListener)invocation.getArguments()[1]).onResponse(command.getCorrelationID(), successResponse);
+ }
+ } else {
+ DebugTool.logInfo(TAG, "CallBacks failed");
+ return null;
+ }
+ ((OnMultipleRequestListener)invocation.getArguments()[1]).onFinished();
+ return null;
+ }
+ }).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class));
+
+ voiceCommandChangesListener = new VoiceCommandUpdateOperation.VoiceCommandChangesListener() {
+ @Override
+ public void updateVoiceCommands(List<VoiceCommand> newCurrentVoiceCommands, HashMap<RPCRequest, String> errorObject) {
+ assertEquals(0, errorObject.size());
+ assertEquals(0, newCurrentVoiceCommands.size());
+ }
+ };
+
+ VoiceCommandUpdateOperation.VoiceCommandChangesListener listenerSpy = Mockito.spy(voiceCommandChangesListener);
+
+ voiceCommandUpdateOperation = new VoiceCommandUpdateOperation(internalInterface, deleteList, new ArrayList<VoiceCommand>(), listenerSpy);
+ voiceCommandUpdateOperation.onExecute();
+
+ verify(listenerSpy, times(1)).updateVoiceCommands(any(List.class), any(HashMap.class));
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/VideoStreamManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/VideoStreamManagerTests.java
index 54d90359d..bdba132fc 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/VideoStreamManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/VideoStreamManagerTests.java
@@ -40,6 +40,7 @@ import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
+import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@@ -70,6 +71,7 @@ public class VideoStreamManagerTests {
public static final String TAG = "VideoStreamManagerTests";
private Context mTestContext;
private static boolean touchEventOccured = false;
+ private static boolean viewResizedCalled = false;
// SETUP / HELPERS
@@ -94,6 +96,11 @@ public class VideoStreamManagerTests {
}
@Override
+ public void onViewResized(int width, int height) {
+ viewResizedCalled = true;
+ }
+
+ @Override
public boolean onTouchEvent(@NonNull MotionEvent event) {
touchEventOccured = true;
return super.onTouchEvent(event);
@@ -106,6 +113,7 @@ public class VideoStreamManagerTests {
public void testInitialization() {
ISdl internalInterface = mock(ISdl.class);
when(internalInterface.getProtocolVersion()).thenReturn(new Version(5, 1, 0));
+ when(internalInterface.getSystemCapabilityManager()).thenReturn(mock(SystemCapabilityManager.class));
RegisterAppInterfaceResponse mockRegisterAppInterfaceResponse = new RegisterAppInterfaceResponse();
VehicleType mockVehicleType = new VehicleType();
@@ -160,7 +168,7 @@ public class VideoStreamManagerTests {
VideoStreamingParameters params = new VideoStreamingParameters();
boolean encrypted = false;
videoStreamManager.startStreaming(params, encrypted);
- verify(internalInterface, times(0)).startVideoService(params, encrypted);
+ verify(internalInterface, times(0)).startVideoService(params, encrypted, false);
}
});
}
@@ -168,6 +176,7 @@ public class VideoStreamManagerTests {
@Test
public void testRemoteDisplayStream() {
ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getSystemCapabilityManager()).thenReturn(mock(SystemCapabilityManager.class));
RegisterAppInterfaceResponse mockRegisterAppInterfaceResponse = new RegisterAppInterfaceResponse();
VehicleType mockVehicleType = new VehicleType();
@@ -268,9 +277,7 @@ public class VideoStreamManagerTests {
hmiListener[0].onNotified(fullNotification);
videoStreamManager.startRemoteDisplayStream(mTestContext, TestPresentation.class, null, false);
-
- //assertTrue(touchEventOccured);
-
+ assertTrue(touchEventOccured);
videoStreamManager.dispose();
assertTrue(listenerSet.isEmpty());
}
@@ -279,8 +286,56 @@ public class VideoStreamManagerTests {
}
@Test
+ public void testOnViewResized() {
+ ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getSystemCapabilityManager()).thenReturn(mock(SystemCapabilityManager.class));
+
+ final OnRPCNotificationListener[] hmiListener = {null};
+
+ Answer<Void> onAddHMIListener = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ hmiListener[0] = (OnRPCNotificationListener) args[1];
+ return null;
+ }
+ };
+
+ doAnswer(onAddHMIListener).when(internalInterface).addOnRPCNotificationListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCNotificationListener.class));
+
+ final VideoStreamManager videoStreamManager = new VideoStreamManager(internalInterface);
+ videoStreamManager.start(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ assertTrue(success);
+ OnHMIStatus fullNotification = new OnHMIStatus();
+ fullNotification.setHmiLevel(HMILevel.HMI_FULL);
+ hmiListener[0].onNotified(fullNotification);
+
+ videoStreamManager.startRemoteDisplayStream(mTestContext, TestPresentation.class, null, false);
+
+ Field sdlRemoteDisplayField = null;
+ try {
+ sdlRemoteDisplayField =
+ VideoStreamManager.class.getDeclaredField("sdlRemoteDisplay");
+ } catch (NoSuchFieldException e) { e.printStackTrace(); }
+ sdlRemoteDisplayField.setAccessible(true);
+
+ SdlRemoteDisplay display = null;
+ try {
+ display = (SdlRemoteDisplay) sdlRemoteDisplayField.get(videoStreamManager);
+ } catch (IllegalAccessException e) { e.printStackTrace(); }
+ display.onViewResized(100, 100);
+
+ assertTrue(viewResizedCalled);
+ }
+ });
+ }
+
+ @Test
public void testConvertTouchEvent() {
ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getSystemCapabilityManager()).thenReturn(mock(SystemCapabilityManager.class));
RegisterAppInterfaceResponse mockRegisterAppInterfaceResponse = new RegisterAppInterfaceResponse();
VehicleType mockVehicleType = new VehicleType();
@@ -495,6 +550,7 @@ public class VideoStreamManagerTests {
private void assertMotionEventWithScale(int width, int height, float scale) {
ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getSystemCapabilityManager()).thenReturn(mock(SystemCapabilityManager.class));
RegisterAppInterfaceResponse mockRegisterAppInterfaceResponse = new RegisterAppInterfaceResponse();
VehicleType mockVehicleType = new VehicleType();
@@ -534,7 +590,10 @@ public class VideoStreamManagerTests {
@Test
public void testIsHMIStateVideoStreamCapable() {
- VideoStreamManager videoStreamManager = new VideoStreamManager(mock(ISdl.class));
+ ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getSystemCapabilityManager()).thenReturn(mock(SystemCapabilityManager.class));
+
+ VideoStreamManager videoStreamManager = new VideoStreamManager(internalInterface);
// Case 1 (VideoStreamingState = STREAMABLE)
assertTrue(videoStreamManager.isHMIStateVideoStreamCapable(createOnHMIStatus(HMILevel.HMI_FULL, VideoStreamingState.STREAMABLE)));
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/VideoStreamingRangeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/VideoStreamingRangeTests.java
new file mode 100644
index 000000000..12ed8675e
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/VideoStreamingRangeTests.java
@@ -0,0 +1,89 @@
+package com.smartdevicelink.managers.video;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.smartdevicelink.managers.video.resolution.Resolution;
+import com.smartdevicelink.managers.video.resolution.VideoStreamingRange;
+import com.smartdevicelink.proxy.rpc.ImageResolution;
+
+import junit.framework.TestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@RunWith(AndroidJUnit4.class)
+public class VideoStreamingRangeTests extends TestCase {
+
+ VideoStreamingRange testRange;
+ Resolution minResolution;
+ Resolution maxResolution;
+ Double minimumDiagonal;
+ Double minimumAspectRatio;
+ Double maximumAspectRatio;
+
+ @Before
+ public void setUp() {
+ minResolution = new Resolution(640, 480);
+ maxResolution = new Resolution(1920, 1080);
+ minimumDiagonal = 3.3;
+ minimumAspectRatio = 1.0;
+ maximumAspectRatio = 2.0;
+ testRange = new VideoStreamingRange(minResolution, maxResolution, minimumDiagonal, minimumAspectRatio, maximumAspectRatio);
+ }
+
+ @Test
+ public void testResolutionInRange() {
+ ImageResolution inRangeResolution = new ImageResolution(800, 600);
+ assertTrue(testRange.isImageResolutionInRange(inRangeResolution));
+ }
+
+ @Test
+ public void testResolutionOutRange() {
+ ImageResolution outOfRangeResolution = new ImageResolution(600, 800);
+ assertFalse(testRange.isImageResolutionInRange(outOfRangeResolution));
+ }
+
+ @Test
+ public void testResolutionWithNullResolutionRange() {
+ testRange.setMinSupportedResolution(null);
+ testRange.setMaxSupportedResolution(null);
+ Integer randomResolutionParameter = new Random().nextInt(3);
+ ImageResolution randomResolution = new ImageResolution(randomResolutionParameter, randomResolutionParameter);
+ assertTrue(testRange.isImageResolutionInRange(randomResolution));
+ }
+
+ @Test
+ public void testResolutionWithDisabledResolutionRange() {
+ ImageResolution resolution = new ImageResolution(0, 0);
+ assertFalse(testRange.isImageResolutionInRange(resolution));
+ }
+
+ @Test
+ public void testAspectRatioInRange() {
+ Double inRangeAspectRatio = 1.5;
+ assertTrue(testRange.isAspectRatioInRange(inRangeAspectRatio));
+ }
+
+ @Test
+ public void testAspectRatioOutRange() {
+ Double outOfRangeAspectRatio = 0.9;
+ assertFalse(testRange.isAspectRatioInRange(outOfRangeAspectRatio));
+ }
+
+ @Test
+ public void testAspectRatioWithNullAspectRatioRange() {
+ testRange.setMaximumAspectRatio(null);
+ testRange.setMinimumAspectRatio(null);
+ Double randomAspectRatioParameter = new Random().nextDouble();
+ assertTrue(testRange.isAspectRatioInRange(randomAspectRatioParameter));
+ }
+
+ @Test
+ public void testAspectRatioWithDisabledAspectRatioRange() {
+ Double aspectRatio = 0.;
+ assertFalse(testRange.isAspectRatioInRange(aspectRatio));
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java
index edf446b3c..713770984 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java
@@ -16,105 +16,10 @@ import com.smartdevicelink.managers.screen.menu.VoiceCommand;
import com.smartdevicelink.managers.screen.menu.VoiceCommandSelectionListener;
import com.smartdevicelink.protocol.SdlProtocol;
import com.smartdevicelink.protocol.enums.FunctionID;
-import com.smartdevicelink.proxy.rpc.AppInfo;
-import com.smartdevicelink.proxy.rpc.AppServiceCapability;
-import com.smartdevicelink.proxy.rpc.AppServiceData;
-import com.smartdevicelink.proxy.rpc.AppServiceManifest;
-import com.smartdevicelink.proxy.rpc.AppServiceRecord;
-import com.smartdevicelink.proxy.rpc.AppServicesCapabilities;
-import com.smartdevicelink.proxy.rpc.AudioControlCapabilities;
-import com.smartdevicelink.proxy.rpc.AudioControlData;
-import com.smartdevicelink.proxy.rpc.AudioPassThruCapabilities;
-import com.smartdevicelink.proxy.rpc.ButtonCapabilities;
-import com.smartdevicelink.proxy.rpc.Choice;
-import com.smartdevicelink.proxy.rpc.ClimateControlCapabilities;
-import com.smartdevicelink.proxy.rpc.ClimateControlData;
-import com.smartdevicelink.proxy.rpc.CloudAppProperties;
-import com.smartdevicelink.proxy.rpc.Coordinate;
-import com.smartdevicelink.proxy.rpc.DIDResult;
-import com.smartdevicelink.proxy.rpc.DateTime;
-import com.smartdevicelink.proxy.rpc.DeviceInfo;
-import com.smartdevicelink.proxy.rpc.DisplayCapabilities;
-import com.smartdevicelink.proxy.rpc.DisplayCapability;
-import com.smartdevicelink.proxy.rpc.DriverDistractionCapability;
-import com.smartdevicelink.proxy.rpc.DynamicUpdateCapabilities;
-import com.smartdevicelink.proxy.rpc.EqualizerSettings;
-import com.smartdevicelink.proxy.rpc.GearStatus;
-import com.smartdevicelink.proxy.rpc.Grid;
-import com.smartdevicelink.proxy.rpc.HMICapabilities;
-import com.smartdevicelink.proxy.rpc.HMIPermissions;
-import com.smartdevicelink.proxy.rpc.HMISettingsControlCapabilities;
-import com.smartdevicelink.proxy.rpc.HMISettingsControlData;
-import com.smartdevicelink.proxy.rpc.HapticRect;
-import com.smartdevicelink.proxy.rpc.Image;
-import com.smartdevicelink.proxy.rpc.ImageField;
-import com.smartdevicelink.proxy.rpc.ImageResolution;
-import com.smartdevicelink.proxy.rpc.KeyboardProperties;
-import com.smartdevicelink.proxy.rpc.LightCapabilities;
-import com.smartdevicelink.proxy.rpc.LightControlCapabilities;
-import com.smartdevicelink.proxy.rpc.LightControlData;
-import com.smartdevicelink.proxy.rpc.LightState;
-import com.smartdevicelink.proxy.rpc.LocationDetails;
-import com.smartdevicelink.proxy.rpc.MassageCushionFirmness;
-import com.smartdevicelink.proxy.rpc.MassageModeData;
-import com.smartdevicelink.proxy.rpc.MediaServiceData;
-import com.smartdevicelink.proxy.rpc.MediaServiceManifest;
-import com.smartdevicelink.proxy.rpc.MenuParams;
-import com.smartdevicelink.proxy.rpc.MetadataTags;
-import com.smartdevicelink.proxy.rpc.ModuleData;
-import com.smartdevicelink.proxy.rpc.ModuleInfo;
-import com.smartdevicelink.proxy.rpc.NavigationCapability;
-import com.smartdevicelink.proxy.rpc.NavigationInstruction;
-import com.smartdevicelink.proxy.rpc.NavigationServiceData;
-import com.smartdevicelink.proxy.rpc.NavigationServiceManifest;
-import com.smartdevicelink.proxy.rpc.OasisAddress;
-import com.smartdevicelink.proxy.rpc.ParameterPermissions;
-import com.smartdevicelink.proxy.rpc.PermissionItem;
-import com.smartdevicelink.proxy.rpc.PhoneCapability;
-import com.smartdevicelink.proxy.rpc.PresetBankCapabilities;
-import com.smartdevicelink.proxy.rpc.RGBColor;
-import com.smartdevicelink.proxy.rpc.RadioControlCapabilities;
-import com.smartdevicelink.proxy.rpc.RadioControlData;
-import com.smartdevicelink.proxy.rpc.RdsData;
-import com.smartdevicelink.proxy.rpc.Rectangle;
-import com.smartdevicelink.proxy.rpc.RemoteControlCapabilities;
-import com.smartdevicelink.proxy.rpc.ScreenParams;
-import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
-import com.smartdevicelink.proxy.rpc.SeatControlCapabilities;
-import com.smartdevicelink.proxy.rpc.SeatControlData;
-import com.smartdevicelink.proxy.rpc.SeatLocation;
-import com.smartdevicelink.proxy.rpc.SeatMemoryAction;
-import com.smartdevicelink.proxy.rpc.SingleTireStatus;
-import com.smartdevicelink.proxy.rpc.SisData;
-import com.smartdevicelink.proxy.rpc.SoftButton;
-import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities;
-import com.smartdevicelink.proxy.rpc.StabilityControlsStatus;
-import com.smartdevicelink.proxy.rpc.StartTime;
-import com.smartdevicelink.proxy.rpc.StationIDNumber;
-import com.smartdevicelink.proxy.rpc.SystemCapability;
-import com.smartdevicelink.proxy.rpc.TTSChunk;
-import com.smartdevicelink.proxy.rpc.Temperature;
-import com.smartdevicelink.proxy.rpc.TemplateColorScheme;
-import com.smartdevicelink.proxy.rpc.TemplateConfiguration;
-import com.smartdevicelink.proxy.rpc.TextField;
-import com.smartdevicelink.proxy.rpc.TouchCoord;
-import com.smartdevicelink.proxy.rpc.TouchEvent;
-import com.smartdevicelink.proxy.rpc.TouchEventCapabilities;
-import com.smartdevicelink.proxy.rpc.Turn;
-import com.smartdevicelink.proxy.rpc.VehicleDataResult;
-import com.smartdevicelink.proxy.rpc.VehicleType;
-import com.smartdevicelink.proxy.rpc.VideoStreamingCapability;
-import com.smartdevicelink.proxy.rpc.VideoStreamingFormat;
-import com.smartdevicelink.proxy.rpc.VrHelpItem;
-import com.smartdevicelink.proxy.rpc.WeatherAlert;
-import com.smartdevicelink.proxy.rpc.WeatherData;
-import com.smartdevicelink.proxy.rpc.WeatherServiceData;
-import com.smartdevicelink.proxy.rpc.WeatherServiceManifest;
-import com.smartdevicelink.proxy.rpc.WindowCapability;
-import com.smartdevicelink.proxy.rpc.WindowState;
-import com.smartdevicelink.proxy.rpc.WindowStatus;
-import com.smartdevicelink.proxy.rpc.WindowTypeCapabilities;
+import com.smartdevicelink.proxy.rpc.AppCapability;
+import com.smartdevicelink.proxy.rpc.*;
import com.smartdevicelink.proxy.rpc.enums.AmbientLightStatus;
+import com.smartdevicelink.proxy.rpc.enums.AppCapabilityType;
import com.smartdevicelink.proxy.rpc.enums.AppHMIType;
import com.smartdevicelink.proxy.rpc.enums.AppInterfaceUnregisteredReason;
import com.smartdevicelink.proxy.rpc.enums.AppServiceType;
@@ -137,6 +42,7 @@ import com.smartdevicelink.proxy.rpc.enums.Direction;
import com.smartdevicelink.proxy.rpc.enums.DisplayMode;
import com.smartdevicelink.proxy.rpc.enums.DisplayType;
import com.smartdevicelink.proxy.rpc.enums.DistanceUnit;
+import com.smartdevicelink.proxy.rpc.enums.DoorStatusType;
import com.smartdevicelink.proxy.rpc.enums.DriverDistractionState;
import com.smartdevicelink.proxy.rpc.enums.ECallConfirmationStatus;
import com.smartdevicelink.proxy.rpc.enums.EmergencyEventType;
@@ -153,6 +59,7 @@ import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
import com.smartdevicelink.proxy.rpc.enums.ImageType;
import com.smartdevicelink.proxy.rpc.enums.InteractionMode;
import com.smartdevicelink.proxy.rpc.enums.KeyboardEvent;
+import com.smartdevicelink.proxy.rpc.enums.KeyboardInputMask;
import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout;
import com.smartdevicelink.proxy.rpc.enums.KeypressMode;
import com.smartdevicelink.proxy.rpc.enums.Language;
@@ -181,6 +88,7 @@ import com.smartdevicelink.proxy.rpc.enums.RequestType;
import com.smartdevicelink.proxy.rpc.enums.Result;
import com.smartdevicelink.proxy.rpc.enums.SamplingRate;
import com.smartdevicelink.proxy.rpc.enums.SeatMemoryActionType;
+import com.smartdevicelink.proxy.rpc.enums.SeekIndicatorType;
import com.smartdevicelink.proxy.rpc.enums.ServiceUpdateReason;
import com.smartdevicelink.proxy.rpc.enums.SoftButtonType;
import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
@@ -356,6 +264,8 @@ public class TestValues {
public static final VideoStreamingProtocol GENERAL_VIDEOSTREAMINGPROTOCOL = VideoStreamingProtocol.RAW;
public static final VideoStreamingCodec GENERAL_VIDEOSTREAMINGCODEC = VideoStreamingCodec.H264;
public static final VideoStreamingCapability GENERAL_VIDEOSTREAMINGCAPABILITY = new VideoStreamingCapability();
+ public static final VideoStreamingCapability GENERAL_ADDITIONAL_CAPABILITY = new VideoStreamingCapability();
+ public static final List<VideoStreamingCapability> GENERAL_ADDITIONAL_CAPABILITY_LIST = new ArrayList<>();
public static final VideoStreamingFormat GENERAL_VIDEOSTREAMINGFORMAT = new VideoStreamingFormat();
public static final RGBColor GENERAL_RGBCOLOR = new RGBColor();
public static final TemplateColorScheme GENERAL_DAYCOLORSCHEME = new TemplateColorScheme();
@@ -366,6 +276,8 @@ public class TestValues {
public static final DriverDistractionState GENERAL_DRIVERDISTRACTIONSTATE = DriverDistractionState.DD_ON;
public static final List<LocationDetails> GENERAL_LOCATIONDETAILS_LIST = Arrays.asList(new LocationDetails[]{TestValues.GENERAL_LOCATIONDETAILS, TestValues.GENERAL_LOCATIONDETAILS});
public static final AudioStreamingIndicator GENERAL_AUDIO_STREAMING_INDICATOR = AudioStreamingIndicator.PLAY;
+ public static final SeekStreamingIndicator GENERAL_SEEK_STREAMING_INDICATOR = new SeekStreamingIndicator(SeekIndicatorType.TRACK);
+ public static final SeekIndicatorType GENERAL_SEEKINDICATORTYPE = SeekIndicatorType.TRACK;
public static final String GENERAL_APP_ID = "123e4567e8";
public static final String GENERAL_FULL_APP_ID = "123e4567-e89b-12d3-a456-426655440000";
public static final String GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME = "oemCustomVehicleDataName";
@@ -404,7 +316,10 @@ public class TestValues {
public static final DistanceUnit GENERAL_DISTANCEUNIT = DistanceUnit.KILOMETERS;
public static final LightStatus GENERAL_LIGHTSTATUS = LightStatus.OFF;
public static final RadioBand GENERAL_RADIOBAND = RadioBand.AM;
+ public static final AppCapabilityType GENERAL_APPCAPABILITYTYPE = AppCapabilityType.VIDEO_STREAMING;
+ public static final AppCapability GENERAL_APPCAPABILITY = new AppCapability();
public static final ClimateControlData GENERAL_CLIMATECONTROLDATA = new ClimateControlData();
+ public static final ClimateData GENERAL_CLIMATEDATA = new ClimateData();
public static final SeatControlData GENERAL_SEATCONTROLDATA = new SeatControlData();
public static final RdsData GENERAL_RDSDATA = new RdsData();
public static final StationIDNumber GENERAL_STATIONIDNUMBER = new StationIDNumber();
@@ -425,8 +340,23 @@ public class TestValues {
public static final LightControlData GENERAL_LIGHTCONTROLDATA = new LightControlData();
public static final HMISettingsControlData GENERAL_HMISETTINGSCONTROLDATA = new HMISettingsControlData();
public static final DynamicUpdateCapabilities GENERAL_DYNAMICUPDATECAPABILITIES = new DynamicUpdateCapabilities();
+ public static final KeyboardCapabilities GENERAL_KEYBOARD_CAPABILITIES = new KeyboardCapabilities();
public static final WindowState GENERAL_WINDOWSTATE = new WindowState();
+ public static final SeatOccupancy GENERAL_SEAT_OCCUPANCY = new SeatOccupancy();
+ public static final List<SeatStatus> GENERAL_SEATS_OCCUPIED = new ArrayList<SeatStatus>(1);
+ public static final List<SeatStatus> GENERAL_SEATS_BELTED = new ArrayList<SeatStatus>(1);
+ public static final SeatStatus GENERAL_SEAT_STATUS = new SeatStatus();
+ public static final SeatLocation GENERAL_SEATLOCATION = new SeatLocation();
+ public static final DoorStatusType GENERAL_DOORSTATUSTYPE = DoorStatusType.REMOVED;
+
+ public static final DoorStatus GENERAL_DOOR_STATUS = new DoorStatus();
+ public static final GateStatus GENERAL_GATE_STATUS = new GateStatus();
+ public static final RoofStatus GENERAL_ROOF_STATUS = new RoofStatus();
+
+ public static final ArrayList<DoorStatus> GENERAL_DOOR_STATUS_LIST = new ArrayList<>(1);
+ public static final ArrayList<GateStatus> GENERAL_GATE_STATUS_LIST = new ArrayList<>(1);
+ public static final ArrayList<RoofStatus> GENERAL_ROOF_STATUS_LIST = new ArrayList<>(1);
public static final VehicleDataResult GENERAL_OEM_CUSTOM_VEHICLE_DATA = new VehicleDataResult();
public static final TemplateConfiguration GENERAL_TEMPLATE_CONFIGURATION = new TemplateConfiguration();
@@ -446,7 +376,6 @@ public class TestValues {
public static final FuelType GENERAL_FUELTYPE = FuelType.GASOLINE;
public static final LockScreenConfig GENERAL_LOCKSCREENCONFIG = new LockScreenConfig();
public static final Grid GENERAL_GRID = new Grid();
- public static final SeatLocation GENERAL_SEAT_LOCATION = new SeatLocation();
public static final ModuleInfo GENERAL_MODULE_INFO = new ModuleInfo();
public static final StabilityControlsStatus GENERAL_STABILITY_CONTROL_STATUS = new StabilityControlsStatus();
public static final VehicleDataStatus GENERAL_ESC_SYSTEM = VehicleDataStatus.ON;
@@ -464,6 +393,9 @@ public class TestValues {
public static final List<Turn> GENERAL_TURN_LIST = new ArrayList<Turn>();
public static final List<Choice> GENERAL_CHOICE_LIST = new ArrayList<Choice>();
public static final List<String> GENERAL_STRING_LIST = Arrays.asList(new String[]{"a", "b"});
+ public static final KeyboardInputMask GENERAL_KEYBOARD_INPUT_MASK = KeyboardInputMask.ENABLE_INPUT_KEY_MASK;
+ public static final KeyboardLayoutCapability GENERAL_KEYBOARD_LAYOUT_CAPABILITY = new KeyboardLayoutCapability(GENERAL_KEYBOARDLAYOUT, 1);
+ public static final List<KeyboardLayoutCapability> GENERAL_SUPPORTED_KEYBOARDS_LIST = new ArrayList<KeyboardLayoutCapability>();
public static final List<Integer> GENERAL_INTEGER_LIST = Arrays.asList(new Integer[]{-1, -2});
public static final List<TTSChunk> GENERAL_TTSCHUNK_LIST = new ArrayList<TTSChunk>(2);
public static final List<HMILevel> GENERAL_HMILEVEL_LIST = Arrays.asList(new HMILevel[]{HMILevel.HMI_FULL, HMILevel.HMI_BACKGROUND});
@@ -590,7 +522,20 @@ public class TestValues {
public static final JSONArray JSON_IMAGE_TYPES = new JSONArray();
public static final JSONObject JSON_DISPLAYCAPABILITY = new JSONObject();
public static final JSONArray JSON_DISPLAYCAPABILITY_LIST = new JSONArray();
+ public static final JSONArray JSON_SUPPORTED_KEYBOARDS_LIST = new JSONArray();
public static final JSONObject JSON_DYNAMICUPDATECAPABILITIES = new JSONObject();
+ public static final JSONObject JSON_KEYBOARD_CAPABILITY = new JSONObject();
+ public static final JSONArray JSON_ROOF_STATUSES = new JSONArray();
+ public static final JSONArray JSON_DOOR_STATUSES = new JSONArray();
+ public static final JSONArray JSON_GATE_STATUSES = new JSONArray();
+ public static final JSONObject JSON_ROOF_STATUS = new JSONObject();
+ public static final JSONObject JSON_DOOR_STATUS = new JSONObject();
+ public static final JSONObject JSON_GATE_STATUS = new JSONObject();
+ public static final JSONObject JSON_SEEK_STREAMING_INDICATOR = new JSONObject();
+ public static final JSONArray JSON_GENERAL_SEATS_OCCUPIED = new JSONArray();
+ public static final JSONArray JSON_GENERAL_SEATS_BELTED = new JSONArray();
+ public static final JSONObject JSON_OBJECT_GENERAL_SEATS_BELTED = new JSONObject();
+ public static final JSONObject JSON_OBJECT_GENERAL_SEATS_OCCUPIED = new JSONObject();
static {
GENERAL_TOUCHEVENTCAPABILITIES.setDoublePressAvailable(GENERAL_BOOLEAN);
@@ -765,7 +710,7 @@ public class TestValues {
GENERAL_MEDIACLOCKFORMAT_LIST.add(MediaClockFormat.CLOCK1);
GENERAL_MEDIACLOCKFORMAT_LIST.add(MediaClockFormat.CLOCK2);
- GENERAL_SEAT_LIST.add(GENERAL_SEAT_LOCATION);
+ GENERAL_SEAT_LIST.add(GENERAL_SEATLOCATION);
GENERAL_IMAGE.setValue(GENERAL_STRING);
GENERAL_IMAGE.setImageType(GENERAL_IMAGETYPE);
@@ -912,10 +857,20 @@ public class TestValues {
GENERAL_VIDEOSTREAMINGFORMAT_LIST.add(GENERAL_VIDEOSTREAMINGFORMAT);
GENERAL_VIDEOSTREAMINGFORMAT_LIST.add(GENERAL_VIDEOSTREAMINGFORMAT);
+ GENERAL_ADDITIONAL_CAPABILITY.setPreferredResolution(GENERAL_IMAGERESOLUTION);
+ GENERAL_ADDITIONAL_CAPABILITY.setDiagonalScreenSize(GENERAL_DOUBLE);
+ GENERAL_ADDITIONAL_CAPABILITY.setScale(GENERAL_DOUBLE);
+
+ GENERAL_ADDITIONAL_CAPABILITY_LIST.add(GENERAL_ADDITIONAL_CAPABILITY);
+ GENERAL_ADDITIONAL_CAPABILITY.setAdditionalVideoStreamingCapabilities(GENERAL_ADDITIONAL_CAPABILITY_LIST);
+
GENERAL_VIDEOSTREAMINGCAPABILITY.setMaxBitrate(GENERAL_INT);
GENERAL_VIDEOSTREAMINGCAPABILITY.setPreferredResolution(GENERAL_IMAGERESOLUTION);
GENERAL_VIDEOSTREAMINGCAPABILITY.setSupportedFormats(GENERAL_VIDEOSTREAMINGFORMAT_LIST);
GENERAL_VIDEOSTREAMINGCAPABILITY.setIsHapticSpatialDataSupported(GENERAL_BOOLEAN);
+ GENERAL_VIDEOSTREAMINGCAPABILITY.setDiagonalScreenSize(GENERAL_DOUBLE);
+ GENERAL_VIDEOSTREAMINGCAPABILITY.setPixelPerInch(GENERAL_DOUBLE);
+ GENERAL_VIDEOSTREAMINGCAPABILITY.setScale(GENERAL_DOUBLE);
GENERAL_CLIMATECONTROLCAPABILITIES.setModuleName(GENERAL_STRING);
GENERAL_CLIMATECONTROLCAPABILITIES.setFanSpeedAvailable(GENERAL_BOOLEAN);
@@ -1154,7 +1109,62 @@ public class TestValues {
GENERAL_STABILITY_CONTROL_STATUS.setEscSystem(GENERAL_ESC_SYSTEM);
GENERAL_STABILITY_CONTROL_STATUS.setTrailerSwayControl(GENERAL_S_WAY_CONTROL);
+ GENERAL_SUPPORTED_KEYBOARDS_LIST.add(GENERAL_KEYBOARD_LAYOUT_CAPABILITY);
+
+ GENERAL_KEYBOARD_CAPABILITIES.setMaskInputCharactersSupported(TestValues.GENERAL_BOOLEAN);
+ GENERAL_KEYBOARD_CAPABILITIES.setSupportedKeyboards(GENERAL_SUPPORTED_KEYBOARDS_LIST);
+
+ GENERAL_APPCAPABILITY.setVideoStreamingCapability(GENERAL_VIDEOSTREAMINGCAPABILITY);
+ GENERAL_APPCAPABILITY.setAppCapabilityType(GENERAL_APPCAPABILITYTYPE);
+
+ // SEAT_OCCUPANCY
+ GENERAL_SEATLOCATION.setGrid(GENERAL_LOCATION_GRID);
+
+ GENERAL_SEAT_STATUS.setConditionActive(GENERAL_BOOLEAN);
+ GENERAL_SEAT_STATUS.setSeatLocation(GENERAL_SEATLOCATION);
+
+ GENERAL_SEATS_BELTED.add(GENERAL_SEAT_STATUS);
+ GENERAL_SEATS_OCCUPIED.add(GENERAL_SEAT_STATUS);
+
+ GENERAL_SEAT_OCCUPANCY.setSeatsBelted(GENERAL_SEATS_BELTED);
+ GENERAL_SEAT_OCCUPANCY.setSeatsOccupied(GENERAL_SEATS_OCCUPIED);
+
+ GENERAL_ROOF_STATUS.setLocation(GENERAL_GRID);
+ GENERAL_ROOF_STATUS.setState(GENERAL_WINDOW_STATE);
+ GENERAL_ROOF_STATUS.setStatus(GENERAL_DOORSTATUSTYPE);
+ GENERAL_ROOF_STATUS_LIST.add(GENERAL_ROOF_STATUS);
+
+ GENERAL_GATE_STATUS.setLocation(GENERAL_GRID);
+ GENERAL_GATE_STATUS.setStatus(GENERAL_DOORSTATUSTYPE);
+ GENERAL_GATE_STATUS_LIST.add(GENERAL_GATE_STATUS);
+
+ GENERAL_DOOR_STATUS.setLocation(GENERAL_GRID);
+ GENERAL_DOOR_STATUS.setStatus(GENERAL_DOORSTATUSTYPE);
+ GENERAL_DOOR_STATUS_LIST.add(GENERAL_DOOR_STATUS);
+
+ // Climate Data
+ GENERAL_CLIMATEDATA.setAtmosphericPressure(GENERAL_FLOAT);
+ GENERAL_CLIMATEDATA.setCabinTemperature(GENERAL_TEMPERATURE);
+ GENERAL_CLIMATEDATA.setExternalTemperature(GENERAL_TEMPERATURE);
+
try {
+
+ for (KeyboardLayoutCapability keyboard : GENERAL_SUPPORTED_KEYBOARDS_LIST) {
+ JSON_SUPPORTED_KEYBOARDS_LIST.put(keyboard.serializeJSON());
+ }
+
+ JSON_KEYBOARD_CAPABILITY.put(KeyboardCapabilities.KEY_MASK_INPUT_CHARACTERS_SUPPORTED, GENERAL_BOOLEAN);
+ JSON_KEYBOARD_CAPABILITY.put(KeyboardCapabilities.KEY_SUPPORTED_KEYBOARDS, JSON_SUPPORTED_KEYBOARDS_LIST);
+
+
+ JSON_OBJECT_GENERAL_SEATS_OCCUPIED.put(SeatStatus.KEY_SEAT_LOCATION, GENERAL_SEATLOCATION.serializeJSON());
+ JSON_OBJECT_GENERAL_SEATS_OCCUPIED.put(SeatStatus.KEY_CONDITION_ACTIVE, GENERAL_BOOLEAN);
+ JSON_GENERAL_SEATS_OCCUPIED.put(JSON_OBJECT_GENERAL_SEATS_OCCUPIED);
+
+ JSON_OBJECT_GENERAL_SEATS_BELTED.put(SeatStatus.KEY_SEAT_LOCATION, GENERAL_SEATLOCATION.serializeJSON());
+ JSON_OBJECT_GENERAL_SEATS_BELTED.put(SeatStatus.KEY_CONDITION_ACTIVE,GENERAL_BOOLEAN);
+ JSON_GENERAL_SEATS_BELTED.put(JSON_OBJECT_GENERAL_SEATS_BELTED);
+
JSON_HMIPERMISSIONS.put(HMIPermissions.KEY_ALLOWED, GENERAL_HMILEVEL_LIST);
JSON_HMIPERMISSIONS.put(HMIPermissions.KEY_USER_DISALLOWED, GENERAL_HMILEVEL_LIST);
@@ -1385,12 +1395,27 @@ public class TestValues {
JSON_TEXTFIELDTYPES.put(MetadataType.MEDIA_ALBUM);
JSON_TEXTFIELDTYPES.put(MetadataType.MEDIA_ARTIST);
+ JSON_GRID.put(SeatLocation.KEY_GRID, GENERAL_LOCATION_GRID.serializeJSON());
JSON_SEAT_LOCATIONS.put(JSON_GRID);
JSON_MODULE_INFO.put(ModuleInfo.KEY_MODULE_ID, TestValues.GENERAL_STRING);
JSON_MODULE_INFO.put(ModuleInfo.KEY_MODULE_LOCATION, TestValues.JSON_GRID);
JSON_MODULE_INFO.put(ModuleInfo.KEY_MODULE_SERVICE_AREA, TestValues.JSON_GRID);
JSON_MODULE_INFO.put(ModuleInfo.KEY_MULTIPLE_ACCESS_ALLOWED, TestValues.GENERAL_BOOLEAN);
+ JSON_ROOF_STATUS.put(RoofStatus.KEY_STATUS, GENERAL_DOORSTATUSTYPE);
+ JSON_ROOF_STATUS.put(RoofStatus.KEY_LOCATION, JSON_GRID);
+ JSON_ROOF_STATUS.put(RoofStatus.KEY_STATE, GENERAL_WINDOW_STATE.serializeJSON());
+
+ JSON_DOOR_STATUS.put(DoorStatus.KEY_STATUS, GENERAL_DOORSTATUSTYPE);
+ JSON_DOOR_STATUS.put(DoorStatus.KEY_LOCATION, JSON_GRID);
+
+ JSON_GATE_STATUS.put(GateStatus.KEY_STATUS, GENERAL_DOORSTATUSTYPE);
+ JSON_GATE_STATUS.put(GateStatus.KEY_LOCATION, JSON_GRID);
+
+ JSON_ROOF_STATUSES.put(JSON_ROOF_STATUS);
+ JSON_DOOR_STATUSES.put(JSON_DOOR_STATUS);
+ JSON_GATE_STATUSES.put(JSON_GATE_STATUS);
+ JSON_SEEK_STREAMING_INDICATOR.put(SeekStreamingIndicator.KEY_TYPE, SeekIndicatorType.TRACK);
} catch (JSONException e) {
Log.e("Test", "Static Json Construction Failed.", e);
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java
index 724be920b..e8d89c818 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java
@@ -10,6 +10,7 @@ import com.smartdevicelink.proxy.rpc.enums.DefrostZone;
import com.smartdevicelink.proxy.rpc.enums.FileType;
import com.smartdevicelink.proxy.rpc.enums.HMILevel;
import com.smartdevicelink.proxy.rpc.enums.HmiZoneCapabilities;
+import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout;
import com.smartdevicelink.proxy.rpc.enums.PRNDL;
import com.smartdevicelink.proxy.rpc.enums.PrerecordedSpeech;
import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
@@ -3910,6 +3911,23 @@ public class Validator {
return approxPosition1.equals(approxPosition2) && deviation1.equals(deviation2);
}
+ public static boolean validateSeatOccupancies(SeatOccupancy item1, SeatOccupancy item2) {
+ if (item1 == null) {
+ return (item2 == null);
+ }
+ if (item2 == null) {
+ return (item1 == null);
+ }
+
+ List<SeatStatus> seatsBelted1 = item1.getSeatsBelted();
+ List<SeatStatus> seatsOccupied1 = item1.getSeatsOccupied();
+
+ List<SeatStatus> seatsBelted2 = item2.getSeatsBelted();
+ List<SeatStatus> seatsOccupied2 = item2.getSeatsOccupied();
+
+ return validateSeatStatuses(seatsBelted1, seatsBelted2) && validateSeatStatuses(seatsOccupied1, seatsOccupied2);
+ }
+
public static boolean validateStabilityControlStatus(StabilityControlsStatus status1, StabilityControlsStatus status2) {
if (status1 == null) {
return (status2 == null);
@@ -3929,4 +3947,135 @@ public class Validator {
}
return status1.getDataType().equals(status2.getDataType()) && status1.getResultCode().equals(status2.getResultCode());
}
+
+ public static boolean validateKeyboardLayoutCapability(KeyboardLayoutCapability keyboard1, KeyboardLayoutCapability keyboard2) {
+ if (keyboard1 == null) {
+ return (keyboard2 == null);
+ }
+ if (keyboard2 == null) {
+ return (keyboard1 == null);
+ }
+ return keyboard1.getKeyboardLayout().equals(keyboard2.getKeyboardLayout()) && keyboard1.getNumConfigurableKeys().equals(keyboard2.getNumConfigurableKeys());
+ }
+
+ public static boolean validateKeyboardCapabilities(KeyboardCapabilities keyboardCapabilities1, KeyboardCapabilities keyboardCapabilities2) {
+ if (keyboardCapabilities1 == null) {
+ return (keyboardCapabilities2 == null);
+ }
+ if (keyboardCapabilities2 == null) {
+ return (keyboardCapabilities1 == null);
+ }
+
+ boolean keyboardsEqual = true;
+ if (keyboardCapabilities1.getSupportedKeyboards() != null && keyboardCapabilities2.getSupportedKeyboards() != null) {
+
+ for (KeyboardLayoutCapability keyboardLayoutCapability1 : keyboardCapabilities1.getSupportedKeyboards()) {
+ for (KeyboardLayoutCapability keyboardLayoutCapability2 : keyboardCapabilities2.getSupportedKeyboards()) {
+ if (!validateKeyboardLayoutCapability(keyboardLayoutCapability1, keyboardLayoutCapability2)) {
+ keyboardsEqual = false;
+ break;
+ }
+ }
+ if (!keyboardsEqual) {
+ break;
+ }
+ }
+ }
+
+ return keyboardsEqual && keyboardCapabilities1.getMaskInputCharactersSupported().equals(keyboardCapabilities2.getMaskInputCharactersSupported());
+ }
+
+ public static boolean validateSeatStatuses(List<SeatStatus> seatStatusesItem1, List<SeatStatus> seatStatusesItem2) {
+ if (seatStatusesItem1 == null) {
+ return (seatStatusesItem2 == null);
+ }
+ if (seatStatusesItem2 == null) {
+ return (seatStatusesItem1 == null);
+ }
+
+ if (seatStatusesItem1.size() != seatStatusesItem2.size()) {
+ return false;
+ }
+
+ Iterator<SeatStatus> iterator1 = seatStatusesItem1.iterator();
+ Iterator<SeatStatus> iterator2 = seatStatusesItem2.iterator();
+
+ while (iterator1.hasNext() && iterator2.hasNext()) {
+ SeatStatus seatStatus1 = iterator1.next();
+ SeatStatus seatStatus2 = iterator2.next();
+
+ if (!validateSeatStatus(seatStatus1, seatStatus2)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static boolean validateSeatStatus(SeatStatus item1, SeatStatus item2) {
+ if (item1 == null) {
+ return (item2 == null);
+ }
+ if (item2 == null) {
+ return (item1 == null);
+ }
+
+ if (item1.getConditionActive() != item2.getConditionActive()) {
+ return false;
+ }
+
+ return validateSeatLocation(item1.getSeatLocation(), item2.getSeatLocation());
+ }
+
+ public static boolean validateDoorStatus(DoorStatus status1, DoorStatus status2) {
+ if (status1 == null) {
+ return (status2 == null);
+ }
+ if (status2 == null) {
+ return (status2 == null);
+ }
+ boolean gridValidated = validateGrid(status1.getLocation(), status2.getLocation());
+ return gridValidated && status1.getStatus().equals(status2.getStatus());
+ }
+
+ public static boolean validateGateStatus(GateStatus status1, GateStatus status2) {
+ if (status1 == null) {
+ return (status2 == null);
+ }
+ if (status2 == null) {
+ return (status2 == null);
+ }
+ boolean gridValidated = validateGrid(status1.getLocation(), status2.getLocation());
+ return gridValidated && status1.getStatus().equals(status2.getStatus());
+ }
+
+ public static boolean validateRoofStatus(RoofStatus status1, RoofStatus status2) {
+ if (status1 == null) {
+ return (status2 == null);
+ }
+ if (status2 == null) {
+ return (status2 == null);
+ }
+ boolean gridValidated = validateGrid(status1.getLocation(), status2.getLocation());
+ return gridValidated && status1.getStatus().equals(status2.getStatus()) && validateWindowStates(status1.getState(), status2.getState());
+ }
+
+ public static boolean validateClimateData(ClimateData climateData1, ClimateData climateData2) {
+ if (climateData1 == null) {
+ return (climateData2 == null);
+ }
+ if (climateData2 == null) {
+ return (climateData1 == null);
+ }
+
+ if (!validateTemperature(climateData1.getExternalTemperature(), climateData2.getExternalTemperature())) {
+ return false;
+ }
+
+ if (!validateTemperature(climateData1.getCabinTemperature(), climateData2.getCabinTemperature())) {
+ return false;
+ }
+
+ return climateData1.getAtmosphericPressure().floatValue() == climateData2.getAtmosphericPressure().floatValue();
+ }
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/VehicleDataHelper.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/VehicleDataHelper.java
index a13486983..a53e6aa69 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/VehicleDataHelper.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/VehicleDataHelper.java
@@ -3,6 +3,7 @@ package com.smartdevicelink.test;
import com.smartdevicelink.proxy.rpc.AirbagStatus;
import com.smartdevicelink.proxy.rpc.BeltStatus;
import com.smartdevicelink.proxy.rpc.BodyInformation;
+import com.smartdevicelink.proxy.rpc.ClimateData;
import com.smartdevicelink.proxy.rpc.ClusterModeStatus;
import com.smartdevicelink.proxy.rpc.DeviceStatus;
import com.smartdevicelink.proxy.rpc.ECallInfo;
@@ -15,8 +16,12 @@ import com.smartdevicelink.proxy.rpc.Grid;
import com.smartdevicelink.proxy.rpc.HeadLampStatus;
import com.smartdevicelink.proxy.rpc.MyKey;
import com.smartdevicelink.proxy.rpc.OnVehicleData;
+import com.smartdevicelink.proxy.rpc.SeatLocation;
+import com.smartdevicelink.proxy.rpc.SeatOccupancy;
+import com.smartdevicelink.proxy.rpc.SeatStatus;
import com.smartdevicelink.proxy.rpc.SingleTireStatus;
import com.smartdevicelink.proxy.rpc.StabilityControlsStatus;
+import com.smartdevicelink.proxy.rpc.Temperature;
import com.smartdevicelink.proxy.rpc.TireStatus;
import com.smartdevicelink.proxy.rpc.WindowState;
import com.smartdevicelink.proxy.rpc.WindowStatus;
@@ -38,6 +43,7 @@ import com.smartdevicelink.proxy.rpc.enums.PRNDL;
import com.smartdevicelink.proxy.rpc.enums.PowerModeQualificationStatus;
import com.smartdevicelink.proxy.rpc.enums.PowerModeStatus;
import com.smartdevicelink.proxy.rpc.enums.PrimaryAudioSource;
+import com.smartdevicelink.proxy.rpc.enums.TemperatureUnit;
import com.smartdevicelink.proxy.rpc.enums.TransmissionType;
import com.smartdevicelink.proxy.rpc.enums.TurnSignal;
import com.smartdevicelink.proxy.rpc.enums.VehicleDataEventStatus;
@@ -90,6 +96,15 @@ public class VehicleDataHelper {
public static final String OEM_CUSTOM_VEHICLE_DATA_STATE = "oemCustomVehicleDataState";
public static final Boolean HANDS_OFF_STEERING = Boolean.TRUE;
+ // Climate Data
+ public static final ClimateData CLIMATE_DATA = new ClimateData();
+ public static final Float CLIMATE_DATA_ATM_PRESSURE = TestValues.GENERAL_FLOAT;
+ public static final Temperature CLIMATE_DATA_CABIN_TEMP = new Temperature(TemperatureUnit.CELSIUS, TestValues.GENERAL_FLOAT);
+ public static final Temperature CLIMATE_DATA_EXT_TEMP = new Temperature(TemperatureUnit.CELSIUS, TestValues.GENERAL_FLOAT);
+
+ // Seat Occupancy
+ public static final SeatOccupancy SEAT_OCCUPANCY = new SeatOccupancy();
+
//other variables inside some of the above objects
// tire status
public static final WarningLightStatus TIRE_PRESSURE_TELL_TALE = WarningLightStatus.ON;
@@ -226,6 +241,11 @@ public class VehicleDataHelper {
//GetVehicleDataResponse data which stores the same things
public static final GetVehicleDataResponse VEHICLE_DATA_RESPONSE = new GetVehicleDataResponse();
+ public static final List<SeatStatus> SEATS_OCCUPIED = new ArrayList<SeatStatus>(1);
+ public static final List<SeatStatus> SEATS_BELTED = new ArrayList<SeatStatus>(1);
+ public static final SeatStatus SEAT_STATUS = new SeatStatus();
+ public static final SeatLocation SEAT_LOCATION = new SeatLocation();
+
static {
//TIRE_PRESSURE set up
TIRE_PRESSURE.setPressureTellTale(TIRE_PRESSURE_TELL_TALE);
@@ -335,6 +355,11 @@ public class VehicleDataHelper {
EMERGENCY_EVENT.setMaximumChangeVelocity(EMERGENCY_EVENT_MAX_CHANGE_VELOCITY);
EMERGENCY_EVENT.setMultipleEvents(EMERGENCY_EVENT_MULTIPLE_EVENTS);
+ // Climate Data
+ CLIMATE_DATA.setAtmosphericPressure(CLIMATE_DATA_ATM_PRESSURE);
+ CLIMATE_DATA.setCabinTemperature(CLIMATE_DATA_CABIN_TEMP);
+ CLIMATE_DATA.setExternalTemperature(CLIMATE_DATA_EXT_TEMP);
+
//CLUSTER_MODE_STATUS set up
CLUSTER_MODE_STATUS.setPowerModeActive(CLUSTER_MODE_STATUS_POWER_MODE_ACTIVE);
CLUSTER_MODE_STATUS.setPowerModeQualificationStatus(CLUSTER_MODE_STATUS_POWER_MODE_QUALIFICATION_STATUS);
@@ -373,10 +398,23 @@ public class VehicleDataHelper {
GEAR_STATUS.setUserSelectedGear(USER_SELECTED_GEAR);
GEAR_STATUS.setActualGear(ACTUAL_GEAR);
+ // SEAT_OCCUPANCY
+ SEAT_LOCATION.setGrid(LOCATION_GRID);
+
+ SEAT_STATUS.setConditionActive(true);
+ SEAT_STATUS.setSeatLocation(SEAT_LOCATION);
+
+ SEATS_BELTED.add(SEAT_STATUS);
+ SEATS_OCCUPIED.add(SEAT_STATUS);
+
+ SEAT_OCCUPANCY.setSeatsBelted(SEATS_BELTED);
+ SEAT_OCCUPANCY.setSeatsOccupied(SEATS_OCCUPIED);
+
//set up the OnVehicleData object
VEHICLE_DATA.setSpeed(SPEED);
VEHICLE_DATA.setRpm(RPM);
VEHICLE_DATA.setExternalTemperature(EXTERNAL_TEMPERATURE);
+ VEHICLE_DATA.setClimateData(CLIMATE_DATA);
VEHICLE_DATA.setFuelLevel(FUEL_LEVEL);
VEHICLE_DATA.setVin(VIN);
VEHICLE_DATA.setPrndl(PRNDL_FINAL);
@@ -408,10 +446,12 @@ public class VehicleDataHelper {
VEHICLE_DATA.setStabilityControlsStatus(STABILITY_CONTROLS_STATUS);
VEHICLE_DATA.setOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, OEM_CUSTOM_VEHICLE_DATA_STATE);
VEHICLE_DATA.setHandsOffSteering(HANDS_OFF_STEERING);
+ VEHICLE_DATA.setSeatOccupancy(SEAT_OCCUPANCY);
//set up the GetVehicleDataResponse object
VEHICLE_DATA_RESPONSE.setSpeed(SPEED);
VEHICLE_DATA_RESPONSE.setRpm(RPM);
VEHICLE_DATA_RESPONSE.setExternalTemperature(EXTERNAL_TEMPERATURE);
+ VEHICLE_DATA_RESPONSE.setClimateData(CLIMATE_DATA);
VEHICLE_DATA_RESPONSE.setFuelLevel(FUEL_LEVEL);
VEHICLE_DATA_RESPONSE.setVin(VIN);
VEHICLE_DATA_RESPONSE.setPrndl(PRNDL_FINAL);
@@ -443,6 +483,7 @@ public class VehicleDataHelper {
VEHICLE_DATA_RESPONSE.setStabilityControlsStatus(STABILITY_CONTROLS_STATUS);
VEHICLE_DATA_RESPONSE.setOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, OEM_CUSTOM_VEHICLE_DATA_STATE);
VEHICLE_DATA_RESPONSE.setHandsOffSteering(HANDS_OFF_STEERING);
+ VEHICLE_DATA_RESPONSE.setSeatOccupancy(SEAT_OCCUPANCY);
}
private VehicleDataHelper() {
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/RPCStructTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/RPCStructTests.java
index 3cb7a1f3c..da539eba7 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/RPCStructTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/RPCStructTests.java
@@ -1,5 +1,6 @@
package com.smartdevicelink.test.proxy;
+import com.smartdevicelink.proxy.RPCMessage;
import com.smartdevicelink.proxy.RPCStruct;
import com.smartdevicelink.proxy.rpc.AirbagStatus;
import com.smartdevicelink.proxy.rpc.Choice;
@@ -183,4 +184,62 @@ public class RPCStructTests extends TestCase {
fail(e.getMessage());
}
}
+
+ public void testClone() {
+ Hashtable<String, Object> map = new Hashtable<>();
+ String key = "test";
+ Integer value = 42;
+ map.put(key, value);
+
+ RPCStruct rpcStruct = new RPCStruct(map);
+ rpcStruct.setPayloadProtected(true);
+ RPCStruct rpcClone = rpcStruct.clone();
+
+ assertEquals(rpcStruct.getStore().get("test"), rpcClone.getStore().get("test"));
+ assertTrue(rpcClone.isPayloadProtected());
+ }
+
+ public void testHashCode() {
+ Hashtable<String, Object> map = new Hashtable<>();
+ String key = "test";
+ Integer value = 42;
+ map.put(key, value);
+
+ RPCStruct rpcStruct = new RPCStruct(map);
+ RPCStruct rpcStruct2 = new RPCStruct(map);
+
+ Hashtable<String, Object> map2 = new Hashtable<>();
+ String key2 = "test2";
+ Integer value2 = 24;
+ map2.put(key2, value2);
+
+ RPCStruct rpcStruct3 = new RPCStruct(map2);
+
+ assertEquals(rpcStruct.hashCode(), rpcStruct2.hashCode());
+ assertNotSame(rpcStruct2.hashCode(), rpcStruct3.hashCode());
+ }
+
+ public void testEquals() {
+ Hashtable<String, Object> map = new Hashtable<>();
+ String key = "test";
+ Integer value = 42;
+ map.put(key, value);
+
+ RPCStruct rpcStruct = new RPCStruct(map);
+
+ RPCStruct rpcClone = rpcStruct.clone();
+
+ RPCStruct rpcNull = new RPCStruct(null);
+
+ RPCMessage nonStruct = new RPCMessage("TEST");
+
+ RPCStruct rpcStruct2 = new RPCStruct(map);
+
+ assertFalse(rpcStruct.equals(null));
+ assertTrue(rpcStruct.equals(rpcStruct));
+ assertFalse(rpcStruct.equals(nonStruct));
+ assertFalse(rpcStruct.equals(rpcNull));
+ assertTrue(rpcStruct.equals(rpcStruct2));
+ assertTrue(rpcStruct.equals(rpcClone));
+ }
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/RPCGenericTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/RPCGenericTests.java
index 178c7a125..e4a61b9ed 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/RPCGenericTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/RPCGenericTests.java
@@ -5,6 +5,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.xmlpull.v1.XmlPullParser;
@@ -45,6 +46,7 @@ import static junit.framework.TestCase.fail;
* - The enums have a value for every element in the spec and the names and deprecation status match the spec
*/
@RunWith(AndroidJUnit4.class)
+@Ignore //Remove this annotation before running these tests
public class RPCGenericTests {
private final String XML_FILE_NAME = "MOBILE_API.xml";
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/AppCapabilityTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/AppCapabilityTests.java
new file mode 100644
index 000000000..225dc393c
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/AppCapabilityTests.java
@@ -0,0 +1,90 @@
+package com.smartdevicelink.test.rpc.datatypes;
+
+import com.smartdevicelink.marshal.JsonRPCMarshaller;
+import com.smartdevicelink.proxy.rpc.AppCapability;
+import com.smartdevicelink.proxy.rpc.VideoStreamingCapability;
+import com.smartdevicelink.proxy.rpc.enums.AppCapabilityType;
+import com.smartdevicelink.test.JsonUtils;
+import com.smartdevicelink.test.TestValues;
+import com.smartdevicelink.test.Validator;
+
+import junit.framework.TestCase;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+
+import static com.smartdevicelink.proxy.rpc.AppCapability.KEY_APP_CAPABILITY_TYPE;
+import static com.smartdevicelink.proxy.rpc.AppCapability.KEY_VIDEO_STREAMING_CAPABILITY;
+
+public class AppCapabilityTests extends TestCase {
+ private AppCapability msg;
+
+ @Override
+ public void setUp() {
+
+ msg = new AppCapability();
+ msg.setAppCapabilityType(TestValues.GENERAL_APPCAPABILITYTYPE);
+ msg.setVideoStreamingCapability(TestValues.GENERAL_VIDEOSTREAMINGCAPABILITY);
+ }
+
+ /**
+ * Tests the expected values of the RPC message.
+ */
+ public void testRpcValues () {
+ // Test Values
+ VideoStreamingCapability videoStreamingCapability = msg.getVideoStreamingCapability();
+ AppCapabilityType videoStreamingCapabilityType = msg.getAppCapabilityType();
+
+ // Valid Tests
+ assertEquals(TestValues.GENERAL_VIDEOSTREAMINGCAPABILITY, videoStreamingCapability);
+ assertEquals(TestValues.GENERAL_APPCAPABILITYTYPE, videoStreamingCapabilityType);
+
+ // Invalid/Null Tests
+ AppCapability msg = new AppCapability();
+ assertNotNull(TestValues.NOT_NULL, msg);
+
+ assertNull(TestValues.NULL, msg.getAppCapabilityType());
+ assertNull(TestValues.NULL, msg.getVideoStreamingCapability());
+ }
+
+ public void testJson(){
+ JSONObject reference = new JSONObject();
+
+ try{
+ reference.put(KEY_APP_CAPABILITY_TYPE, TestValues.GENERAL_APPCAPABILITYTYPE);
+ reference.put(KEY_VIDEO_STREAMING_CAPABILITY, TestValues.GENERAL_VIDEOSTREAMINGCAPABILITY);
+
+ JSONObject underTest = msg.serializeJSON();
+ assertEquals(TestValues.MATCH, reference.length(), underTest.length());
+
+ Iterator<?> iterator = reference.keys();
+ while(iterator.hasNext()){
+
+ String key = (String) iterator.next();
+
+ if (key.equals(KEY_APP_CAPABILITY_TYPE)) {
+ assertEquals(
+ TestValues.MATCH,
+ JsonUtils.readObjectFromJsonObject(reference, key),
+ JsonUtils.readObjectFromJsonObject(underTest, key)
+ );
+
+ } else if (key.equals(KEY_VIDEO_STREAMING_CAPABILITY)) {
+ JSONObject testEquals = (JSONObject) JsonUtils.readObjectFromJsonObject(underTest, key);
+ Hashtable<String, Object> hashTest = JsonRPCMarshaller.deserializeJSONObject(testEquals);
+ assertTrue(
+ TestValues.TRUE,
+ Validator.validateVideoStreamingCapability(
+ TestValues.GENERAL_VIDEOSTREAMINGCAPABILITY, new VideoStreamingCapability(hashTest)
+ )
+ );
+ }
+ }
+ } catch(JSONException e){
+ fail(TestValues.JSON_FAIL);
+ }
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/BodyInformationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/BodyInformationTests.java
index 1db5aa77a..f0a33599a 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/BodyInformationTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/BodyInformationTests.java
@@ -1,18 +1,29 @@
package com.smartdevicelink.test.rpc.datatypes;
+import com.smartdevicelink.marshal.JsonRPCMarshaller;
import com.smartdevicelink.proxy.rpc.BodyInformation;
+import com.smartdevicelink.proxy.rpc.DoorStatus;
+import com.smartdevicelink.proxy.rpc.GateStatus;
+import com.smartdevicelink.proxy.rpc.RoofStatus;
import com.smartdevicelink.proxy.rpc.enums.IgnitionStableStatus;
import com.smartdevicelink.proxy.rpc.enums.IgnitionStatus;
import com.smartdevicelink.test.JsonUtils;
import com.smartdevicelink.test.TestValues;
+import com.smartdevicelink.test.Validator;
import junit.framework.TestCase;
+import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import java.util.Hashtable;
import java.util.Iterator;
+import static com.smartdevicelink.proxy.rpc.BodyInformation.KEY_DOOR_STATUSES;
+import static com.smartdevicelink.proxy.rpc.BodyInformation.KEY_GATE_STATUSES;
+import static com.smartdevicelink.proxy.rpc.BodyInformation.KEY_ROOF_STATUSES;
+
/**
* This is a unit test class for the SmartDeviceLink library project class :
* {@link com.smartdevicelink.proxy.rpc.BodyInformation}
@@ -32,6 +43,10 @@ public class BodyInformationTests extends TestCase {
msg.setPassengerDoorAjar(TestValues.GENERAL_BOOLEAN);
msg.setRearLeftDoorAjar(TestValues.GENERAL_BOOLEAN);
msg.setRearRightDoorAjar(TestValues.GENERAL_BOOLEAN);
+
+ msg.setDoorStatuses(TestValues.GENERAL_DOOR_STATUS_LIST);
+ msg.setGateStatuses(TestValues.GENERAL_GATE_STATUS_LIST);
+ msg.setRoofStatuses(TestValues.GENERAL_ROOF_STATUS_LIST);
}
/**
@@ -52,6 +67,9 @@ public class BodyInformationTests extends TestCase {
assertEquals(TestValues.MATCH, TestValues.GENERAL_BOOLEAN, (boolean) msg.getPassengerDoorAjar());
assertEquals(TestValues.MATCH, TestValues.GENERAL_BOOLEAN, (boolean) msg.getRearLeftDoorAjar());
assertEquals(TestValues.MATCH, TestValues.GENERAL_BOOLEAN, (boolean) msg.getRearRightDoorAjar());
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_GATE_STATUS_LIST, msg.getGateStatuses());
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_ROOF_STATUS_LIST, msg.getRoofStatuses());
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_DOOR_STATUS_LIST, msg.getDoorStatuses());
// Invalid/Null Tests
BodyInformation msg = new BodyInformation();
@@ -64,6 +82,9 @@ public class BodyInformationTests extends TestCase {
assertNull(TestValues.NULL, msg.getPassengerDoorAjar());
assertNull(TestValues.NULL, msg.getRearLeftDoorAjar());
assertNull(TestValues.NULL, msg.getRearRightDoorAjar());
+ assertNull(TestValues.NULL, msg.getGateStatuses());
+ assertNull(TestValues.NULL, msg.getRoofStatuses());
+ assertNull(TestValues.NULL, msg.getDoorStatuses());
}
public void testJson() {
@@ -77,6 +98,9 @@ public class BodyInformationTests extends TestCase {
reference.put(BodyInformation.KEY_PASSENGER_DOOR_AJAR, TestValues.GENERAL_BOOLEAN);
reference.put(BodyInformation.KEY_REAR_LEFT_DOOR_AJAR, TestValues.GENERAL_BOOLEAN);
reference.put(BodyInformation.KEY_REAR_RIGHT_DOOR_AJAR, TestValues.GENERAL_BOOLEAN);
+ reference.put(KEY_DOOR_STATUSES, TestValues.JSON_DOOR_STATUSES);
+ reference.put(KEY_GATE_STATUSES, TestValues.JSON_GATE_STATUSES);
+ reference.put(KEY_ROOF_STATUSES, TestValues.JSON_ROOF_STATUSES);
JSONObject underTest = msg.serializeJSON();
assertEquals(TestValues.MATCH, reference.length(), underTest.length());
@@ -84,7 +108,39 @@ public class BodyInformationTests extends TestCase {
Iterator<?> iterator = reference.keys();
while (iterator.hasNext()) {
String key = (String) iterator.next();
- assertEquals(TestValues.MATCH, JsonUtils.readObjectFromJsonObject(reference, key), JsonUtils.readObjectFromJsonObject(underTest, key));
+ if (key.equals(KEY_DOOR_STATUSES)) {
+
+ JSONArray array1 = JsonUtils.readJsonArrayFromJsonObject(reference, key);
+ JSONArray array2 = JsonUtils.readJsonArrayFromJsonObject(underTest, key);
+
+ for (int i = 0; i < array1.length(); i++) {
+ Hashtable<String, Object> h1 = JsonRPCMarshaller.deserializeJSONObject(array1.getJSONObject(i));
+ Hashtable<String, Object> h2 = JsonRPCMarshaller.deserializeJSONObject(array2.getJSONObject(i));
+ assertTrue(Validator.validateDoorStatus(new DoorStatus(h1), new DoorStatus(h2)));
+
+ }
+ } else if (key.equals(KEY_GATE_STATUSES)) {
+
+ JSONArray array1 = JsonUtils.readJsonArrayFromJsonObject(reference, key);
+ JSONArray array2 = JsonUtils.readJsonArrayFromJsonObject(underTest, key);
+
+ for (int i = 0; i < array1.length(); i++) {
+ Hashtable<String, Object> h1 = JsonRPCMarshaller.deserializeJSONObject(array1.getJSONObject(i));
+ Hashtable<String, Object> h2 = JsonRPCMarshaller.deserializeJSONObject(array2.getJSONObject(i));
+ assertTrue(Validator.validateGateStatus(new GateStatus(h1), new GateStatus(h2)));
+ }
+ } else if (key.equals(KEY_ROOF_STATUSES)) {
+ JSONArray array1 = JsonUtils.readJsonArrayFromJsonObject(reference, key);
+ JSONArray array2 = JsonUtils.readJsonArrayFromJsonObject(underTest, key);
+
+ for (int i = 0; i < array1.length(); i++) {
+ Hashtable<String, Object> h1 = JsonRPCMarshaller.deserializeJSONObject(array1.getJSONObject(i));
+ Hashtable<String, Object> h2 = JsonRPCMarshaller.deserializeJSONObject(array2.getJSONObject(i));
+ assertTrue(Validator.validateRoofStatus(new RoofStatus(h1), new RoofStatus(h2)));
+ }
+ } else {
+ assertEquals(TestValues.MATCH, JsonUtils.readObjectFromJsonObject(reference, key), JsonUtils.readObjectFromJsonObject(underTest, key));
+ }
}
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/ClimateDataTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/ClimateDataTests.java
new file mode 100644
index 000000000..4fb5e9c0f
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/ClimateDataTests.java
@@ -0,0 +1,83 @@
+package com.smartdevicelink.test.rpc.datatypes;
+
+import com.smartdevicelink.marshal.JsonRPCMarshaller;
+import com.smartdevicelink.proxy.rpc.ClimateData;
+import com.smartdevicelink.proxy.rpc.Temperature;
+import com.smartdevicelink.test.JsonUtils;
+import com.smartdevicelink.test.TestValues;
+import com.smartdevicelink.test.Validator;
+
+import junit.framework.TestCase;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+
+public class ClimateDataTests extends TestCase {
+
+ private ClimateData msg;
+
+ @Override
+ public void setUp() {
+ msg = new ClimateData();
+
+ msg.setExternalTemperature(TestValues.GENERAL_TEMPERATURE);
+ msg.setCabinTemperature(TestValues.GENERAL_TEMPERATURE);
+ msg.setAtmosphericPressure(TestValues.GENERAL_FLOAT);
+ }
+
+ /**
+ * Tests the expected values of the RPC message.
+ */
+ public void testRpcValues() {
+ // Test Values
+ Temperature cabinTemperature = msg.getCabinTemperature();
+ Temperature externalTemperature = msg.getExternalTemperature();
+ Float atmosphericPressure = msg.getAtmosphericPressure();
+
+ // Valid Tests
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_TEMPERATURE, cabinTemperature);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_TEMPERATURE, externalTemperature);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_FLOAT, atmosphericPressure);
+
+ // Invalid/Null Tests
+ ClimateData msg = new ClimateData();
+ assertNotNull(TestValues.NOT_NULL, msg);
+
+ assertNull(TestValues.NULL, msg.getAtmosphericPressure());
+ assertNull(TestValues.NULL, msg.getCabinTemperature());
+ assertNull(TestValues.NULL, msg.getExternalTemperature());
+ }
+
+ public void testJson() {
+ JSONObject reference = new JSONObject();
+
+ try {
+ reference.put(ClimateData.KEY_EXTERNAL_TEMPERATURE, JsonRPCMarshaller.serializeHashtable(TestValues.GENERAL_TEMPERATURE.getStore()));
+ reference.put(ClimateData.KEY_CABIN_TEMPERATURE, JsonRPCMarshaller.serializeHashtable(TestValues.GENERAL_TEMPERATURE.getStore()));
+ reference.put(ClimateData.KEY_ATMOSPHERIC_PRESSURE, TestValues.GENERAL_FLOAT);
+
+ JSONObject underTest = msg.serializeJSON();
+ assertEquals(TestValues.MATCH, reference.length(), underTest.length());
+
+ Iterator<?> iterator = reference.keys();
+ while (iterator.hasNext()) {
+ String key = (String) iterator.next();
+
+ if (key.equals(ClimateData.KEY_CABIN_TEMPERATURE) || key.equals(ClimateData.KEY_EXTERNAL_TEMPERATURE)) {
+ JSONObject objectEquals = (JSONObject) JsonUtils.readObjectFromJsonObject(reference, key);
+ JSONObject testEquals = (JSONObject) JsonUtils.readObjectFromJsonObject(underTest, key);
+ Hashtable<String, Object> hashReference = JsonRPCMarshaller.deserializeJSONObject(objectEquals);
+ Hashtable<String, Object> hashTest = JsonRPCMarshaller.deserializeJSONObject(testEquals);
+ assertTrue(TestValues.TRUE, Validator.validateTemperature(new Temperature(hashReference), new Temperature(hashTest)));
+ } else {
+ assertEquals(TestValues.MATCH, JsonUtils.readObjectFromJsonObject(reference, key), JsonUtils.readObjectFromJsonObject(underTest, key));
+ }
+ }
+ } catch (JSONException e) {
+ fail(TestValues.JSON_FAIL);
+ }
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/DoorStatusTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/DoorStatusTests.java
new file mode 100644
index 000000000..b2b64eb9f
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/DoorStatusTests.java
@@ -0,0 +1,65 @@
+package com.smartdevicelink.test.rpc.datatypes;
+
+import com.smartdevicelink.marshal.JsonRPCMarshaller;
+import com.smartdevicelink.proxy.rpc.DoorStatus;
+import com.smartdevicelink.proxy.rpc.Grid;
+import com.smartdevicelink.proxy.rpc.RoofStatus;
+import com.smartdevicelink.proxy.rpc.enums.DoorStatusType;
+import com.smartdevicelink.test.JsonUtils;
+import com.smartdevicelink.test.TestValues;
+import com.smartdevicelink.test.Validator;
+
+import junit.framework.TestCase;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+
+public class DoorStatusTests extends TestCase {
+ private DoorStatus msg;
+
+ @Override
+ public void setUp() {
+ msg = new DoorStatus(TestValues.GENERAL_GRID, TestValues.GENERAL_DOORSTATUSTYPE);
+ }
+
+ public void testRpcValues() {
+ // Test Values
+ Grid location = msg.getLocation();
+ DoorStatusType status = msg.getStatus();
+
+ // Valid Tests
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_GRID, location);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_DOORSTATUSTYPE, status);
+ }
+
+ public void testJson() {
+ JSONObject reference = new JSONObject();
+
+ try {
+ reference.put(RoofStatus.KEY_STATUS, TestValues.GENERAL_DOORSTATUSTYPE);
+ reference.put(RoofStatus.KEY_LOCATION, TestValues.JSON_GRID);
+
+ JSONObject underTest = msg.serializeJSON();
+ assertEquals(TestValues.MATCH, reference.length(), underTest.length());
+
+ Iterator<?> iterator = reference.keys();
+ while (iterator.hasNext()) {
+ String key = (String) iterator.next();
+ if (key.equals(RoofStatus.KEY_LOCATION)) {
+ Hashtable<String, Object> hs1 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(reference, key));
+ Hashtable<String, Object> hs2 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(underTest, key));
+
+ assertTrue(Validator.validateGrid(new Grid(hs1), new Grid(hs2)));
+
+ } else {
+ assertEquals(TestValues.MATCH, JsonUtils.readObjectFromJsonObject(reference, key), JsonUtils.readObjectFromJsonObject(underTest, key));
+ }
+ }
+ } catch (JSONException e) {
+ fail(TestValues.JSON_FAIL);
+ }
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/GateStatusTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/GateStatusTests.java
new file mode 100644
index 000000000..5424b4066
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/GateStatusTests.java
@@ -0,0 +1,65 @@
+package com.smartdevicelink.test.rpc.datatypes;
+
+import com.smartdevicelink.marshal.JsonRPCMarshaller;
+import com.smartdevicelink.proxy.rpc.GateStatus;
+import com.smartdevicelink.proxy.rpc.Grid;
+import com.smartdevicelink.proxy.rpc.RoofStatus;
+import com.smartdevicelink.proxy.rpc.enums.DoorStatusType;
+import com.smartdevicelink.test.JsonUtils;
+import com.smartdevicelink.test.TestValues;
+import com.smartdevicelink.test.Validator;
+
+import junit.framework.TestCase;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+
+public class GateStatusTests extends TestCase {
+ private GateStatus msg;
+
+ @Override
+ public void setUp() {
+ msg = new GateStatus(TestValues.GENERAL_GRID, TestValues.GENERAL_DOORSTATUSTYPE);
+ }
+
+ public void testRpcValues() {
+ // Test Values
+ Grid location = msg.getLocation();
+ DoorStatusType status = msg.getStatus();
+
+ // Valid Tests
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_GRID, location);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_DOORSTATUSTYPE, status);
+ }
+
+ public void testJson() {
+ JSONObject reference = new JSONObject();
+
+ try {
+ reference.put(RoofStatus.KEY_STATUS, TestValues.GENERAL_DOORSTATUSTYPE);
+ reference.put(RoofStatus.KEY_LOCATION, TestValues.JSON_GRID);
+
+ JSONObject underTest = msg.serializeJSON();
+ assertEquals(TestValues.MATCH, reference.length(), underTest.length());
+
+ Iterator<?> iterator = reference.keys();
+ while (iterator.hasNext()) {
+ String key = (String) iterator.next();
+ if (key.equals(RoofStatus.KEY_LOCATION)) {
+ Hashtable<String, Object> hs1 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(reference, key));
+ Hashtable<String, Object> hs2 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(underTest, key));
+
+ assertTrue(Validator.validateGrid(new Grid(hs1), new Grid(hs2)));
+
+ } else {
+ assertEquals(TestValues.MATCH, JsonUtils.readObjectFromJsonObject(reference, key), JsonUtils.readObjectFromJsonObject(underTest, key));
+ }
+ }
+ } catch (JSONException e) {
+ fail(TestValues.JSON_FAIL);
+ }
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/KeyboardCapabilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/KeyboardCapabilitiesTests.java
new file mode 100644
index 000000000..13d8970d6
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/KeyboardCapabilitiesTests.java
@@ -0,0 +1,82 @@
+package com.smartdevicelink.test.rpc.datatypes;
+
+import com.smartdevicelink.marshal.JsonRPCMarshaller;
+import com.smartdevicelink.proxy.rpc.KeyboardCapabilities;
+import com.smartdevicelink.proxy.rpc.KeyboardLayoutCapability;
+import com.smartdevicelink.test.JsonUtils;
+import com.smartdevicelink.test.TestValues;
+import com.smartdevicelink.test.Validator;
+
+import junit.framework.TestCase;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+public class KeyboardCapabilitiesTests extends TestCase {
+ private KeyboardCapabilities msg;
+
+ @Override
+ public void setUp() {
+ msg = new KeyboardCapabilities();
+
+ msg.setMaskInputCharactersSupported(TestValues.GENERAL_BOOLEAN);
+ msg.setSupportedKeyboards(TestValues.GENERAL_SUPPORTED_KEYBOARDS_LIST);
+ }
+
+ /**
+ * Tests the expected values of the RPC message.
+ */
+ public void testRpcValues() {
+ // Test Values
+ Boolean maskInputCharactersSupported = msg.getMaskInputCharactersSupported();
+ List<KeyboardLayoutCapability> supportedKeyboards = msg.getSupportedKeyboards();
+
+ // Valid Tests
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_BOOLEAN, (boolean) maskInputCharactersSupported);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_SUPPORTED_KEYBOARDS_LIST, supportedKeyboards);
+
+ // Invalid/Null Tests
+ KeyboardCapabilities msg = new KeyboardCapabilities();
+ assertNotNull(TestValues.NOT_NULL, msg);
+
+ assertNull(TestValues.NULL, msg.getMaskInputCharactersSupported());
+ assertNull(TestValues.NULL, msg.getSupportedKeyboards());
+ }
+
+ public void testJson() {
+ JSONObject reference = new JSONObject();
+
+ try {
+ reference.put(KeyboardCapabilities.KEY_MASK_INPUT_CHARACTERS_SUPPORTED, TestValues.GENERAL_BOOLEAN);
+ reference.put(KeyboardCapabilities.KEY_SUPPORTED_KEYBOARDS, TestValues.JSON_SUPPORTED_KEYBOARDS_LIST);
+
+ JSONObject underTest = msg.serializeJSON();
+ assertEquals(TestValues.MATCH, reference.length(), underTest.length());
+
+ Iterator<?> iterator = reference.keys();
+ while (iterator.hasNext()) {
+ String key = (String) iterator.next();
+ if (key.equals(KeyboardCapabilities.KEY_SUPPORTED_KEYBOARDS)) {
+ JSONArray referenceArray = JsonUtils.readJsonArrayFromJsonObject(reference, key);
+ JSONArray underTestArray = JsonUtils.readJsonArrayFromJsonObject(underTest, key);
+ assertEquals(TestValues.MATCH, referenceArray.length(), underTestArray.length());
+
+ for (int i = 0; i < referenceArray.length(); i++) {
+ Hashtable<String, Object> hashReference = JsonRPCMarshaller.deserializeJSONObject(referenceArray.getJSONObject(i));
+ Hashtable<String, Object> hashTest = JsonRPCMarshaller.deserializeJSONObject(underTestArray.getJSONObject(i));
+ assertTrue(TestValues.TRUE, Validator.validateKeyboardLayoutCapability(new KeyboardLayoutCapability(hashReference), new KeyboardLayoutCapability(hashTest)));
+ }
+ } else {
+ assertEquals(TestValues.MATCH, JsonUtils.readObjectFromJsonObject(reference, key), JsonUtils.readObjectFromJsonObject(underTest, key));
+ }
+ }
+ } catch (JSONException e) {
+ fail(TestValues.JSON_FAIL);
+ }
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/KeyboardPropertiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/KeyboardPropertiesTests.java
index e06bd5e0b..2d02f10f7 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/KeyboardPropertiesTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/KeyboardPropertiesTests.java
@@ -1,6 +1,7 @@
package com.smartdevicelink.test.rpc.datatypes;
import com.smartdevicelink.proxy.rpc.KeyboardProperties;
+import com.smartdevicelink.proxy.rpc.enums.KeyboardInputMask;
import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout;
import com.smartdevicelink.proxy.rpc.enums.KeypressMode;
import com.smartdevicelink.proxy.rpc.enums.Language;
@@ -34,8 +35,11 @@ public class KeyboardPropertiesTests extends TestCase {
msg.setKeypressMode(TestValues.GENERAL_KEYPRESSMODE);
msg.setLanguage(TestValues.GENERAL_LANGUAGE);
msg.setLimitedCharacterList(TestValues.GENERAL_STRING_LIST);
+ msg.setCustomKeys(TestValues.GENERAL_STRING_LIST);
+ msg.setMaskInputCharacters(TestValues.GENERAL_KEYBOARD_INPUT_MASK);
}
+
/**
* Tests the expected values of the RPC message.
*/
@@ -47,6 +51,8 @@ public class KeyboardPropertiesTests extends TestCase {
KeypressMode keypressMode = msg.getKeypressMode();
Language language = msg.getLanguage();
List<String> limitedChars = msg.getLimitedCharacterList();
+ KeyboardInputMask keyInputMask = msg.getMaskInputCharacters();
+ List<String> customKeys = msg.getCustomKeys();
// Valid Tests
assertEquals(TestValues.MATCH, TestValues.GENERAL_STRING, autoCompleteText);
@@ -57,6 +63,8 @@ public class KeyboardPropertiesTests extends TestCase {
assertEquals(TestValues.MATCH, TestValues.GENERAL_LANGUAGE, language);
assertEquals(TestValues.MATCH, TestValues.GENERAL_STRING_LIST.size(), limitedChars.size());
assertTrue(TestValues.TRUE, Validator.validateStringList(TestValues.GENERAL_STRING_LIST, limitedChars));
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_KEYBOARD_INPUT_MASK, keyInputMask);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_STRING_LIST, customKeys);
// Invalid/Null Tests
KeyboardProperties msg = new KeyboardProperties();
@@ -69,6 +77,8 @@ public class KeyboardPropertiesTests extends TestCase {
assertNull(TestValues.NULL, msg.getLanguage());
assertNull(TestValues.NULL, msg.getKeyboardLayout());
assertNull(TestValues.NULL, msg.getLimitedCharacterList());
+ assertNull(TestValues.NULL, msg.getMaskInputCharacters());
+ assertNull(TestValues.NULL, msg.getCustomKeys());
}
public void testJson() {
@@ -81,6 +91,8 @@ public class KeyboardPropertiesTests extends TestCase {
reference.put(KeyboardProperties.KEY_KEYPRESS_MODE, TestValues.GENERAL_KEYPRESSMODE);
reference.put(KeyboardProperties.KEY_LANGUAGE, TestValues.GENERAL_LANGUAGE);
reference.put(KeyboardProperties.KEY_LIMITED_CHARACTER_LIST, JsonUtils.createJsonArray(TestValues.GENERAL_STRING_LIST));
+ reference.put(KeyboardProperties.KEY_CUSTOM_KEYS, JsonUtils.createJsonArray(TestValues.GENERAL_STRING_LIST));
+ reference.put(KeyboardProperties.KEY_MASK_INPUT_CHARACTERS, TestValues.GENERAL_KEYBOARD_INPUT_MASK);
JSONObject underTest = msg.serializeJSON();
assertEquals(TestValues.MATCH, reference.length(), underTest.length());
@@ -88,7 +100,7 @@ public class KeyboardPropertiesTests extends TestCase {
Iterator<?> iterator = reference.keys();
while (iterator.hasNext()) {
String key = (String) iterator.next();
- if (key.equals(KeyboardProperties.KEY_LIMITED_CHARACTER_LIST) || key.equals(KeyboardProperties.KEY_AUTO_COMPLETE_LIST)) {
+ if (key.equals(KeyboardProperties.KEY_LIMITED_CHARACTER_LIST) || key.equals(KeyboardProperties.KEY_AUTO_COMPLETE_LIST) || key.equals(KeyboardProperties.KEY_CUSTOM_KEYS)) {
assertTrue(TestValues.TRUE, Validator.validateStringList(JsonUtils.readStringListFromJsonObject(reference, key), JsonUtils.readStringListFromJsonObject(underTest, key)));
} else {
assertEquals(TestValues.MATCH, JsonUtils.readObjectFromJsonObject(reference, key), JsonUtils.readObjectFromJsonObject(underTest, key));
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/MenuParamsTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/MenuParamsTests.java
index 998d8f4d4..edea4a706 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/MenuParamsTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/MenuParamsTests.java
@@ -26,6 +26,8 @@ public class MenuParamsTests extends TestCase {
msg.setMenuName(TestValues.GENERAL_STRING);
msg.setParentID(TestValues.GENERAL_INT);
msg.setPosition(TestValues.GENERAL_INT);
+ msg.setSecondaryText(TestValues.GENERAL_STRING);
+ msg.setTertiaryText(TestValues.GENERAL_STRING);
}
/**
@@ -36,11 +38,15 @@ public class MenuParamsTests extends TestCase {
String menuName = msg.getMenuName();
int parentId = msg.getParentID();
int position = msg.getPosition();
+ String secondaryText = msg.getSecondaryText();
+ String tertiaryText = msg.getTertiaryText();
// Valid Tests
assertEquals(TestValues.MATCH, TestValues.GENERAL_STRING, menuName);
assertEquals(TestValues.MATCH, TestValues.GENERAL_INT, parentId);
assertEquals(TestValues.MATCH, TestValues.GENERAL_INT, position);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_STRING, secondaryText);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_STRING, tertiaryText);
// Invalid/Null Tests
MenuParams msg = new MenuParams();
@@ -49,6 +55,8 @@ public class MenuParamsTests extends TestCase {
assertNull(TestValues.NULL, msg.getMenuName());
assertNull(TestValues.NULL, msg.getParentID());
assertNull(TestValues.NULL, msg.getPosition());
+ assertNull(TestValues.NULL, msg.getSecondaryText());
+ assertNull(TestValues.NULL, msg.getTertiaryText());
}
public void testJson() {
@@ -58,6 +66,8 @@ public class MenuParamsTests extends TestCase {
reference.put(MenuParams.KEY_MENU_NAME, TestValues.GENERAL_STRING);
reference.put(MenuParams.KEY_PARENT_ID, TestValues.GENERAL_INT);
reference.put(MenuParams.KEY_POSITION, TestValues.GENERAL_INT);
+ reference.put(MenuParams.KEY_SECONDARY_TEXT, TestValues.GENERAL_STRING);
+ reference.put(MenuParams.KEY_TERTIARY_TEXT, TestValues.GENERAL_STRING);
JSONObject underTest = msg.serializeJSON();
assertEquals(TestValues.MATCH, reference.length(), underTest.length());
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/RoofStatusTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/RoofStatusTests.java
new file mode 100644
index 000000000..4afc9d778
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/RoofStatusTests.java
@@ -0,0 +1,75 @@
+package com.smartdevicelink.test.rpc.datatypes;
+
+import com.smartdevicelink.marshal.JsonRPCMarshaller;
+import com.smartdevicelink.proxy.rpc.Grid;
+import com.smartdevicelink.proxy.rpc.RoofStatus;
+import com.smartdevicelink.proxy.rpc.WindowState;
+import com.smartdevicelink.proxy.rpc.enums.DoorStatusType;
+import com.smartdevicelink.test.JsonUtils;
+import com.smartdevicelink.test.TestValues;
+import com.smartdevicelink.test.Validator;
+
+import junit.framework.TestCase;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+
+public class RoofStatusTests extends TestCase {
+
+ private RoofStatus msg;
+
+ @Override
+ public void setUp() {
+ msg = new RoofStatus(TestValues.GENERAL_GRID, TestValues.GENERAL_DOORSTATUSTYPE);
+ msg.setState(TestValues.GENERAL_WINDOW_STATE);
+ }
+
+ public void testRpcValues() {
+ // Test Values
+ Grid location = msg.getLocation();
+ DoorStatusType status = msg.getStatus();
+ WindowState windowState = msg.getState();
+
+ // Valid Tests
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_GRID, location);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_DOORSTATUSTYPE, status);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_WINDOW_STATE, windowState);
+ }
+
+ public void testJson() {
+ JSONObject reference = new JSONObject();
+
+ try {
+ reference.put(RoofStatus.KEY_STATUS, TestValues.GENERAL_DOORSTATUSTYPE);
+ reference.put(RoofStatus.KEY_STATE, TestValues.GENERAL_WINDOW_STATE.serializeJSON());
+ reference.put(RoofStatus.KEY_LOCATION, TestValues.JSON_GRID);
+
+ JSONObject underTest = msg.serializeJSON();
+ assertEquals(TestValues.MATCH, reference.length(), underTest.length());
+
+ Iterator<?> iterator = reference.keys();
+ while (iterator.hasNext()) {
+ String key = (String) iterator.next();
+ if (key.equals(RoofStatus.KEY_STATE)) {
+ Hashtable<String, Object> hs1 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(reference, key));
+ Hashtable<String, Object> hs2 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(underTest, key));
+
+ assertTrue(Validator.validateWindowStates(new WindowState(hs1), new WindowState(hs2)));
+ } else if (key.equals(RoofStatus.KEY_LOCATION)) {
+ Hashtable<String, Object> hs1 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(reference, key));
+ Hashtable<String, Object> hs2 = JsonRPCMarshaller.deserializeJSONObject((JSONObject) JsonUtils.readObjectFromJsonObject(underTest, key));
+
+ assertTrue(Validator.validateGrid(new Grid(hs1), new Grid(hs2)));
+
+ } else {
+ assertEquals(TestValues.MATCH, JsonUtils.readObjectFromJsonObject(reference, key), JsonUtils.readObjectFromJsonObject(underTest, key));
+ }
+ }
+ } catch (JSONException e) {
+ fail(TestValues.JSON_FAIL);
+ }
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/SeatOccupancyTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/SeatOccupancyTests.java
new file mode 100644
index 000000000..70da261b0
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/SeatOccupancyTests.java
@@ -0,0 +1,83 @@
+package com.smartdevicelink.test.rpc.datatypes;
+
+
+import com.smartdevicelink.marshal.JsonRPCMarshaller;
+import com.smartdevicelink.proxy.rpc.SeatOccupancy;
+import com.smartdevicelink.proxy.rpc.SeatStatus;
+import com.smartdevicelink.test.JsonUtils;
+import com.smartdevicelink.test.TestValues;
+import com.smartdevicelink.test.Validator;
+
+import junit.framework.TestCase;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+
+public class SeatOccupancyTests extends TestCase {
+
+ private SeatOccupancy msg;
+
+ @Override
+ public void setUp() {
+ msg = new SeatOccupancy();
+
+ msg.setSeatsOccupied(TestValues.GENERAL_SEATS_OCCUPIED);
+ msg.setSeatsBelted(TestValues.GENERAL_SEATS_BELTED);
+ }
+
+ /**
+ * Tests the expected values of the RPC message.
+ */
+ public void testRpcValues() {
+ // Test Values
+ List<SeatStatus> seatsBelted = msg.getSeatsBelted();
+ List<SeatStatus> seatsOccupied = msg.getSeatsOccupied();
+
+ // Valid Tests
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_SEATS_BELTED, seatsBelted);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_SEATS_OCCUPIED, seatsOccupied);
+
+ // Invalid/Null Tests
+ SeatOccupancy msg = new SeatOccupancy();
+ assertNotNull(TestValues.NOT_NULL, msg);
+
+ assertNull(TestValues.NULL, msg.getSeatsBelted());
+ assertNull(TestValues.NULL, msg.getSeatsOccupied());
+ }
+
+ public void testJson() {
+ JSONObject reference = new JSONObject();
+
+ try {
+ reference.put(SeatOccupancy.KEY_SEATS_OCCUPIED, TestValues.JSON_GENERAL_SEATS_OCCUPIED);
+ reference.put(SeatOccupancy.KEY_SEATS_BELTED, TestValues.JSON_GENERAL_SEATS_BELTED);
+
+ JSONObject underTest = msg.serializeJSON();
+ assertEquals(TestValues.MATCH, reference.length(), underTest.length());
+
+ Iterator<?> iterator = reference.keys();
+ while (iterator.hasNext()) {
+ String key = (String) iterator.next();
+ if (key.equals(SeatOccupancy.KEY_SEATS_OCCUPIED) || key.equals(SeatOccupancy.KEY_SEATS_BELTED)) {
+ JSONArray referenceArray = JsonUtils.readJsonArrayFromJsonObject(reference, key);
+ JSONArray underTestArray = JsonUtils.readJsonArrayFromJsonObject(underTest, key);
+ assertEquals(TestValues.MATCH, referenceArray.length(), underTestArray.length());
+
+ for (int i = 0; i < referenceArray.length(); i++) {
+ Hashtable<String, Object> hashReference = JsonRPCMarshaller.deserializeJSONObject(referenceArray.getJSONObject(i));
+ Hashtable<String, Object> hashTest = JsonRPCMarshaller.deserializeJSONObject(underTestArray.getJSONObject(i));
+ assertTrue(TestValues.TRUE, Validator.validateSeatStatus(new SeatStatus(hashReference), new SeatStatus(hashTest)));
+ }
+
+ }
+ }
+ } catch (JSONException e) {
+ fail(TestValues.JSON_FAIL);
+ }
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/SeatStatusTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/SeatStatusTests.java
new file mode 100644
index 000000000..14e97d123
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/SeatStatusTests.java
@@ -0,0 +1,76 @@
+package com.smartdevicelink.test.rpc.datatypes;
+
+import com.smartdevicelink.marshal.JsonRPCMarshaller;
+import com.smartdevicelink.proxy.rpc.SeatLocation;
+import com.smartdevicelink.proxy.rpc.SeatStatus;
+import com.smartdevicelink.test.JsonUtils;
+import com.smartdevicelink.test.TestValues;
+import com.smartdevicelink.test.Validator;
+
+import junit.framework.TestCase;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Hashtable;
+import java.util.Iterator;
+
+public class SeatStatusTests extends TestCase {
+
+ private SeatStatus msg;
+
+ @Override
+ public void setUp() {
+ msg = new SeatStatus();
+
+ msg.setSeatLocation(TestValues.GENERAL_SEATLOCATION);
+ msg.setConditionActive(TestValues.GENERAL_BOOLEAN);
+ }
+
+ /**
+ * Tests the expected values of the RPC message.
+ */
+ public void testRpcValues() {
+ // Test Values
+ SeatLocation location = msg.getSeatLocation();
+ Boolean conditionActive = msg.getConditionActive();
+
+ // Valid Tests
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_SEATLOCATION, location);
+ assertEquals(TestValues.GENERAL_BOOLEAN, (boolean) conditionActive);
+
+ // Invalid/Null Tests
+ SeatStatus msg = new SeatStatus();
+ assertNotNull(TestValues.NOT_NULL, msg);
+
+ assertNull(TestValues.NULL, msg.getSeatLocation());
+ assertNull(TestValues.NULL, msg.getConditionActive());
+ }
+
+ public void testJson() {
+ JSONObject reference = new JSONObject();
+
+ try {
+ reference.put(SeatStatus.KEY_CONDITION_ACTIVE, TestValues.GENERAL_BOOLEAN);
+ reference.put(SeatStatus.KEY_SEAT_LOCATION, TestValues.GENERAL_SEATLOCATION.serializeJSON());
+
+ JSONObject underTest = msg.serializeJSON();
+ assertEquals(TestValues.MATCH, reference.length(), underTest.length());
+
+ Iterator<?> iterator = reference.keys();
+ while (iterator.hasNext()) {
+ String key = (String) iterator.next();
+ if (key.equals(SeatStatus.KEY_SEAT_LOCATION)) {
+ Hashtable<String, Object> locationOne = JsonRPCMarshaller.deserializeJSONObject(JsonUtils.readJsonObjectFromJsonObject(reference, key));
+ Hashtable<String, Object> locationTwo = JsonRPCMarshaller.deserializeJSONObject(JsonUtils.readJsonObjectFromJsonObject(underTest, key));
+ assertTrue(TestValues.TRUE, Validator.validateSeatLocation(new SeatLocation(locationOne), new SeatLocation(locationTwo)));
+ } else {
+ assertEquals(TestValues.MATCH, JsonUtils.readObjectFromJsonObject(reference, key), JsonUtils.readObjectFromJsonObject(underTest, key));
+
+ }
+ }
+ } catch (JSONException e) {
+ fail(TestValues.JSON_FAIL);
+ }
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/SeekStreamingIndicatorTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/SeekStreamingIndicatorTests.java
new file mode 100644
index 000000000..924960adf
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/SeekStreamingIndicatorTests.java
@@ -0,0 +1,61 @@
+package com.smartdevicelink.test.rpc.datatypes;
+
+import com.smartdevicelink.proxy.rpc.SeekStreamingIndicator;
+import com.smartdevicelink.proxy.rpc.enums.SeekIndicatorType;
+import com.smartdevicelink.test.JsonUtils;
+import com.smartdevicelink.test.TestValues;
+
+import junit.framework.TestCase;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.Iterator;
+
+public class SeekStreamingIndicatorTests extends TestCase {
+
+ private SeekStreamingIndicator msg;
+
+ @Override
+ protected void setUp() throws Exception {
+ msg = new SeekStreamingIndicator();
+ assertNotNull(TestValues.NOT_NULL, msg);
+
+ msg.setType(SeekIndicatorType.TRACK);
+ msg.setSeekTime(1);
+ }
+
+ public void testRpcValues() {
+ SeekIndicatorType indicator = msg.getType();
+ int seekTime = msg.getSeekTime();
+
+ assertEquals(TestValues.MATCH, SeekIndicatorType.TRACK, indicator);
+ assertEquals(TestValues.MATCH, 1, seekTime);
+
+ SeekStreamingIndicator msg = new SeekStreamingIndicator();
+ assertNotNull(TestValues.NOT_NULL, msg);
+
+ assertNull(TestValues.NULL, msg.getType());
+ assertNull(TestValues.NULL, msg.getSeekTime());
+ }
+
+ public void testJson() {
+ JSONObject reference = new JSONObject();
+
+ try {
+ reference.put(SeekStreamingIndicator.KEY_TYPE, SeekIndicatorType.TRACK);
+ reference.put(SeekStreamingIndicator.KEY_SEEK_TIME, 1);
+
+ JSONObject underTest = msg.serializeJSON();
+ assertEquals(TestValues.MATCH, reference.length(), underTest.length());
+
+ Iterator<?> iterator = reference.keys();
+ while (iterator.hasNext()) {
+ String key = (String) iterator.next();
+ assertEquals(TestValues.MATCH, JsonUtils.readObjectFromJsonObject(reference, key), JsonUtils.readObjectFromJsonObject(underTest, key));
+ }
+ } catch (JSONException e) {
+ fail(TestValues.JSON_FAIL);
+ }
+ }
+} \ No newline at end of file
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VideoStreamingCapabilityTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VideoStreamingCapabilityTests.java
index 422cd5ddc..c13199223 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VideoStreamingCapabilityTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VideoStreamingCapabilityTests.java
@@ -13,6 +13,7 @@ import junit.framework.TestCase;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
+import org.junit.Test;
import java.util.Hashtable;
import java.util.Iterator;
@@ -33,6 +34,8 @@ public class VideoStreamingCapabilityTests extends TestCase {
msg.setDiagonalScreenSize(TestValues.GENERAL_DOUBLE);
msg.setPixelPerInch(TestValues.GENERAL_DOUBLE);
msg.setScale(TestValues.GENERAL_DOUBLE);
+ msg.setAdditionalVideoStreamingCapabilities(TestValues.GENERAL_ADDITIONAL_CAPABILITY_LIST);
+ msg.setPreferredFPS(TestValues.GENERAL_INTEGER);
}
/**
@@ -47,6 +50,8 @@ public class VideoStreamingCapabilityTests extends TestCase {
Double diagonalScreenSize = msg.getDiagonalScreenSize();
Double pixelPerInch = msg.getPixelPerInch();
Double scale = msg.getScale();
+ List<VideoStreamingCapability> additionalVideoStreamingCapabilities = msg.getAdditionalVideoStreamingCapabilities();
+ Integer preferredFPS = msg.getPreferredFPS();
// Valid Tests
assertEquals(TestValues.MATCH, (List<VideoStreamingFormat>) TestValues.GENERAL_VIDEOSTREAMINGFORMAT_LIST, format);
@@ -56,6 +61,7 @@ public class VideoStreamingCapabilityTests extends TestCase {
assertEquals(TestValues.MATCH, TestValues.GENERAL_DOUBLE, diagonalScreenSize);
assertEquals(TestValues.MATCH, TestValues.GENERAL_DOUBLE, pixelPerInch);
assertEquals(TestValues.MATCH, TestValues.GENERAL_DOUBLE, scale);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_ADDITIONAL_CAPABILITY_LIST, additionalVideoStreamingCapabilities);
// Invalid/Null Tests
VideoStreamingCapability msg = new VideoStreamingCapability();
@@ -68,10 +74,13 @@ public class VideoStreamingCapabilityTests extends TestCase {
assertNull(TestValues.NULL, msg.getDiagonalScreenSize());
assertNull(TestValues.NULL, msg.getPixelPerInch());
assertNull(TestValues.NULL, msg.getScale());
+ assertNull(TestValues.NULL, msg.getAdditionalVideoStreamingCapabilities());
+ assertNull(TestValues.NULL, msg.getPreferredFPS());
}
public void testJson() {
JSONObject reference = new JSONObject();
+ msg.setAdditionalVideoStreamingCapabilities(null);
try {
reference.put(VideoStreamingCapability.KEY_MAX_BITRATE, TestValues.GENERAL_INT);
@@ -81,6 +90,7 @@ public class VideoStreamingCapabilityTests extends TestCase {
reference.put(VideoStreamingCapability.KEY_DIAGONAL_SCREEN_SIZE, TestValues.GENERAL_DOUBLE);
reference.put(VideoStreamingCapability.KEY_PIXEL_PER_INCH, TestValues.GENERAL_DOUBLE);
reference.put(VideoStreamingCapability.KEY_SCALE, TestValues.GENERAL_DOUBLE);
+ reference.put(VideoStreamingCapability.KEY_PREFERRED_FPS, TestValues.GENERAL_INTEGER);
JSONObject underTest = msg.serializeJSON();
assertEquals(TestValues.MATCH, reference.length(), underTest.length());
@@ -89,7 +99,7 @@ public class VideoStreamingCapabilityTests extends TestCase {
while (iterator.hasNext()) {
String key = (String) iterator.next();
- if (key.equals(VideoStreamingCapability.KEY_MAX_BITRATE) || key.equals(VideoStreamingCapability.KEY_HAPTIC_SPATIAL_DATA_SUPPORTED)) {
+ if (key.equals(VideoStreamingCapability.KEY_MAX_BITRATE) || key.equals(VideoStreamingCapability.KEY_HAPTIC_SPATIAL_DATA_SUPPORTED) || key.equals(VideoStreamingCapability.KEY_PREFERRED_FPS)) {
assertTrue(TestValues.TRUE, JsonUtils.readIntegerFromJsonObject(reference, key) == JsonUtils.readIntegerFromJsonObject(underTest, key));
} else if (key.equals(VideoStreamingCapability.KEY_PREFERRED_RESOLUTION)) {
ImageResolution irReference = (ImageResolution) JsonUtils.readObjectFromJsonObject(reference, key);
@@ -111,4 +121,19 @@ public class VideoStreamingCapabilityTests extends TestCase {
fail(TestValues.JSON_FAIL);
}
}
+
+ @Test
+ public void testFormatMethod(){
+ List<VideoStreamingCapability> additionalCapabilities = msg.getAdditionalVideoStreamingCapabilities();
+ msg.format(null, false);
+ assertEquals(additionalCapabilities, msg.getAdditionalVideoStreamingCapabilities());
+ }
+
+ @Test
+ public void testFormatWillRemoveSelf(){
+ List<VideoStreamingCapability> additionalCapabilities = msg.getAdditionalVideoStreamingCapabilities();
+ additionalCapabilities.add(msg);
+ msg.format(null, false);
+ assertEquals(additionalCapabilities.size(), msg.getAdditionalVideoStreamingCapabilities().size());
+ }
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/WindowCapabilityTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/WindowCapabilityTests.java
index cee5b5f84..0ad5e466d 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/WindowCapabilityTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/WindowCapabilityTests.java
@@ -4,6 +4,7 @@ import com.smartdevicelink.marshal.JsonRPCMarshaller;
import com.smartdevicelink.proxy.rpc.ButtonCapabilities;
import com.smartdevicelink.proxy.rpc.DynamicUpdateCapabilities;
import com.smartdevicelink.proxy.rpc.ImageField;
+import com.smartdevicelink.proxy.rpc.KeyboardCapabilities;
import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities;
import com.smartdevicelink.proxy.rpc.TextField;
import com.smartdevicelink.proxy.rpc.WindowCapability;
@@ -46,6 +47,7 @@ public class WindowCapabilityTests extends TestCase {
msg.setSoftButtonCapabilities(TestValues.GENERAL_SOFTBUTTONCAPABILITIES_LIST);
msg.setMenuLayoutsAvailable(TestValues.GENERAL_MENU_LAYOUT_LIST);
msg.setDynamicUpdateCapabilities(TestValues.GENERAL_DYNAMICUPDATECAPABILITIES);
+ msg.setKeyboardCapabilities(TestValues.GENERAL_KEYBOARD_CAPABILITIES);
}
/**
@@ -63,6 +65,7 @@ public class WindowCapabilityTests extends TestCase {
List<SoftButtonCapabilities> softButtonCapabilities = msg.getSoftButtonCapabilities();
List<MenuLayout> menuLayouts = msg.getMenuLayoutsAvailable();
DynamicUpdateCapabilities dynamicUpdateCapabilities = msg.getDynamicUpdateCapabilities();
+ KeyboardCapabilities keyboardCapabilities = msg.getKeyboardCapabilities();
// Valid Tests
assertEquals(TestValues.MATCH, TestValues.GENERAL_INT, windowID);
@@ -75,6 +78,7 @@ public class WindowCapabilityTests extends TestCase {
assertEquals(TestValues.MATCH, TestValues.GENERAL_SOFTBUTTONCAPABILITIES_LIST.size(), softButtonCapabilities.size());
assertEquals(TestValues.MATCH, TestValues.GENERAL_MENU_LAYOUT_LIST.size(), menuLayouts.size());
assertEquals(TestValues.MATCH, TestValues.GENERAL_DYNAMICUPDATECAPABILITIES, dynamicUpdateCapabilities);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_KEYBOARD_CAPABILITIES, keyboardCapabilities);
for (int i = 0; i < TestValues.GENERAL_TEXTFIELD_LIST.size(); i++) {
assertTrue(TestValues.TRUE, Validator.validateTextFields(TestValues.GENERAL_TEXTFIELD_LIST.get(i), textFields.get(i)));
@@ -113,6 +117,7 @@ public class WindowCapabilityTests extends TestCase {
assertNull(TestValues.NULL, msg.getSoftButtonCapabilities());
assertNull(TestValues.NULL, msg.getMenuLayoutsAvailable());
assertNull(TestValues.NULL, msg.getDynamicUpdateCapabilities());
+ assertNull(TestValues.NULL, msg.getKeyboardCapabilities());
}
public void testJson() {
@@ -129,6 +134,7 @@ public class WindowCapabilityTests extends TestCase {
reference.put(WindowCapability.KEY_SOFT_BUTTON_CAPABILITIES, TestValues.JSON_SOFTBUTTONCAPABILITIES);
reference.put(WindowCapability.KEY_MENU_LAYOUTS_AVAILABLE, JsonUtils.createJsonArray(TestValues.GENERAL_MENU_LAYOUT_LIST));
reference.put(WindowCapability.KEY_DYNAMIC_UPDATE_CAPABILITIES, TestValues.JSON_DYNAMICUPDATECAPABILITIES);
+ reference.put(WindowCapability.KEY_KEYBOARD_CAPABILITIES, TestValues.JSON_KEYBOARD_CAPABILITY);
JSONObject underTest = msg.serializeJSON();
assertEquals(TestValues.MATCH, reference.length(), underTest.length());
@@ -214,6 +220,13 @@ public class WindowCapabilityTests extends TestCase {
imageFieldNameListTest.add((ImageFieldName) underTestArray.get(i));
}
assertTrue(TestValues.TRUE, imageFieldNameListReference.containsAll(imageFieldNameListTest) && imageFieldNameListTest.containsAll(imageFieldNameListReference));
+ } else if (key.equals(WindowCapability.KEY_KEYBOARD_CAPABILITIES)){
+ JSONObject jsonReference = JsonUtils.readJsonObjectFromJsonObject(reference, key);
+ JSONObject jsonUnderTest = JsonUtils.readJsonObjectFromJsonObject(underTest, key);
+
+ Hashtable<String, Object> hashReference = JsonRPCMarshaller.deserializeJSONObject(jsonReference);
+ Hashtable<String, Object> hashTest = JsonRPCMarshaller.deserializeJSONObject(jsonUnderTest);
+ assertTrue(TestValues.TRUE, Validator.validateKeyboardCapabilities(new KeyboardCapabilities(hashReference), new KeyboardCapabilities(hashTest)));
} else {
assertEquals(TestValues.MATCH, JsonUtils.readObjectFromJsonObject(reference, key), JsonUtils.readObjectFromJsonObject(underTest, key));
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/AppCapabilityTypeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/AppCapabilityTypeTests.java
new file mode 100644
index 000000000..dd1f08080
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/AppCapabilityTypeTests.java
@@ -0,0 +1,63 @@
+package com.smartdevicelink.test.rpc.enums;
+
+import com.smartdevicelink.proxy.rpc.enums.AppCapabilityType;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class AppCapabilityTypeTests extends TestCase {
+
+ /**
+ * Verifies that the enum values are not null upon valid assignment.
+ */
+ public void testValidEnums () {
+ String example = "VIDEO_STREAMING";
+ AppCapabilityType enumVideoStreaming = AppCapabilityType.valueForString(example);
+
+ assertNotNull("VIDEO_STREAMING returned null", enumVideoStreaming);
+ }
+
+ /**
+ * Verifies that an invalid assignment is null.
+ */
+ public void testInvalidEnum () {
+ String example = "VidEOs_Treamin";
+ try {
+ AppCapabilityType temp = AppCapabilityType.valueForString(example);
+ assertNull("Result of valueForString should be null.", temp);
+ }
+ catch (IllegalArgumentException exception) {
+ fail("Invalid enum throws IllegalArgumentException.");
+ }
+ }
+
+ /**
+ * Verifies that a null assignment is invalid.
+ */
+ public void testNullEnum () {
+ String example = null;
+ try {
+ AppCapabilityType temp = AppCapabilityType.valueForString(example);
+ assertNull("Result of valueForString should be null.", temp);
+ }
+ catch (NullPointerException exception) {
+ fail("Null string throws NullPointerException.");
+ }
+ }
+
+ /**
+ * Verifies the possible enum values of AppCapabilityType.
+ */
+ public void testListEnum() {
+ List<AppCapabilityType> enumValueList = Arrays.asList(AppCapabilityType.values());
+
+ List<AppCapabilityType> enumTestList = new ArrayList<AppCapabilityType>();
+ enumTestList.add(AppCapabilityType.VIDEO_STREAMING);
+
+ assertTrue("Enum value list does not match enum class list",
+ enumValueList.containsAll(enumTestList) && enumTestList.containsAll(enumValueList));
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/DoorStatusTypeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/DoorStatusTypeTests.java
new file mode 100644
index 000000000..0a83f8e58
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/DoorStatusTypeTests.java
@@ -0,0 +1,73 @@
+package com.smartdevicelink.test.rpc.enums;
+
+import com.smartdevicelink.proxy.rpc.enums.DoorStatusType;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class DoorStatusTypeTests extends TestCase {
+
+ /**
+ * Verifies that the enum values are not null upon valid assignment.
+ */
+ public void testValidEnums() {
+ String example = "CLOSED";
+ DoorStatusType closed = DoorStatusType.valueForString(example);
+ example = "LOCKED";
+ DoorStatusType locked = DoorStatusType.valueForString(example);
+ example = "AJAR";
+ DoorStatusType ajar = DoorStatusType.valueForString(example);
+ example = "REMOVED";
+ DoorStatusType removed = DoorStatusType.valueForString(example);
+
+ assertNotNull("CLOSED returned null", closed);
+ assertNotNull("LOCKED returned null", locked);
+ assertNotNull("AJAR returned null", ajar);
+ assertNotNull("REMOVED returned null", removed);
+ }
+
+ /**
+ * Verifies that an invalid assignment is null.
+ */
+ public void testInvalidEnum() {
+ String example = "cloS_ed";
+ try {
+ DoorStatusType temp = DoorStatusType.valueForString(example);
+ assertNull("Result of valueForString should be null.", temp);
+ } catch (IllegalArgumentException exception) {
+ fail("Invalid enum throws IllegalArgumentException.");
+ }
+ }
+
+ /**
+ * Verifies that a null assignment is invalid.
+ */
+ public void testNullEnum() {
+ String example = null;
+ try {
+ DoorStatusType temp = DoorStatusType.valueForString(example);
+ assertNull("Result of valueForString should be null.", temp);
+ } catch (NullPointerException exception) {
+ fail("Null string throws NullPointerException.");
+ }
+ }
+
+ /**
+ * Verifies the possible enum values of DriverDistractionState.
+ */
+ public void testListEnum() {
+ List<DoorStatusType> enumValueList = Arrays.asList(DoorStatusType.values());
+
+ List<DoorStatusType> enumTestList = new ArrayList<DoorStatusType>();
+ enumTestList.add(DoorStatusType.CLOSED);
+ enumTestList.add(DoorStatusType.LOCKED);
+ enumTestList.add(DoorStatusType.AJAR);
+ enumTestList.add(DoorStatusType.REMOVED);
+
+ assertTrue("Enum value list does not match enum class list",
+ enumValueList.containsAll(enumTestList) && enumTestList.containsAll(enumValueList));
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/ImageFieldNameTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/ImageFieldNameTests.java
index 7e4f28dde..4e2030eaf 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/ImageFieldNameTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/ImageFieldNameTests.java
@@ -44,6 +44,10 @@ public class ImageFieldNameTests extends TestCase {
ImageFieldName enumLocationImage = ImageFieldName.valueForString(example);
example = "secondaryGraphic";
ImageFieldName enumSecondaryGraphic = ImageFieldName.valueForString(example);
+ example = "menuCommandSecondaryImage";
+ ImageFieldName menuCommandSecondaryImage = ImageFieldName.valueForString(example);
+ example = "menuSubMenuSecondaryImage";
+ ImageFieldName menuSubMenuSecondaryImage = ImageFieldName.valueForString(example);
assertNotNull("softButtonImage returned null", enumSoftButtonImage);
assertNotNull("choiceImage returned null", enumChoiceImage);
@@ -58,6 +62,8 @@ public class ImageFieldNameTests extends TestCase {
assertNotNull("showConstantTBTNextTurnIcon returned null", enumShowConstantTbtNextTurnIcon);
assertNotNull("location image returned null", enumLocationImage);
assertNotNull("secondary graphic returned null", enumSecondaryGraphic);
+ assertNotNull("menuCommandSecondaryImage returned null", menuCommandSecondaryImage);
+ assertNotNull("menuSubMenuSecondaryImage returned null", menuSubMenuSecondaryImage);
}
/**
@@ -109,6 +115,8 @@ public class ImageFieldNameTests extends TestCase {
enumTestList.add(ImageFieldName.alertIcon);
enumTestList.add(ImageFieldName.subtleAlertIcon);
enumTestList.add(ImageFieldName.subMenuIcon);
+ enumTestList.add(ImageFieldName.menuCommandSecondaryImage);
+ enumTestList.add(ImageFieldName.menuSubMenuSecondaryImage);
assertTrue("Enum value list does not match enum class list",
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardEventTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardEventTests.java
index 13a28cfda..740603fe4 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardEventTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardEventTests.java
@@ -28,12 +28,18 @@ public class KeyboardEventTests extends TestCase {
KeyboardEvent enumEntryAborted = KeyboardEvent.valueForString(example);
example = "ENTRY_VOICE";
KeyboardEvent enumEntryVoice = KeyboardEvent.valueForString(example);
+ example = "INPUT_KEY_MASK_ENABLED";
+ KeyboardEvent enumInputMaskEnabled = KeyboardEvent.valueForString(example);
+ example = "INPUT_KEY_MASK_DISABLED";
+ KeyboardEvent enumInputMaskDisabled = KeyboardEvent.valueForString(example);
assertNotNull("KEYPRESS returned null", enumKeypress);
assertNotNull("ENTRY_SUBMITTED returned null", enumEntrySubmitted);
assertNotNull("ENTRY_CANCELLED returned null", enumEntryCancelled);
assertNotNull("ENTRY_ABORTED returned null", enumEntryAborted);
assertNotNull("ENTRY_VOICE returned null", enumEntryVoice);
+ assertNotNull("INPUT_KEY_MASK_ENABLED returned null", enumInputMaskEnabled);
+ assertNotNull("INPUT_KEY_MASK_DISABLED returned null", enumInputMaskDisabled);
}
/**
@@ -74,6 +80,8 @@ public class KeyboardEventTests extends TestCase {
enumTestList.add(KeyboardEvent.ENTRY_CANCELLED);
enumTestList.add(KeyboardEvent.ENTRY_ABORTED);
enumTestList.add(KeyboardEvent.ENTRY_VOICE);
+ enumTestList.add(KeyboardEvent.INPUT_KEY_MASK_ENABLED);
+ enumTestList.add(KeyboardEvent.INPUT_KEY_MASK_DISABLED);
assertTrue("Enum value list does not match enum class list",
enumValueList.containsAll(enumTestList) && enumTestList.containsAll(enumValueList));
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardInputMaskTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardInputMaskTests.java
new file mode 100644
index 000000000..66cee940c
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardInputMaskTests.java
@@ -0,0 +1,69 @@
+package com.smartdevicelink.test.rpc.enums;
+
+import com.smartdevicelink.proxy.rpc.enums.KeyboardInputMask;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class KeyboardInputMaskTests extends TestCase {
+
+ /**
+ * Verifies that the enum values are not null upon valid assignment.
+ */
+ public void testValidEnums() {
+ String example = "ENABLE_INPUT_KEY_MASK";
+ KeyboardInputMask enumEnableInputKeyMask = KeyboardInputMask.valueForString(example);
+ example = "DISABLE_INPUT_KEY_MASK";
+ KeyboardInputMask enumDisableInputKeyMask = KeyboardInputMask.valueForString(example);
+ example = "USER_CHOICE_INPUT_KEY_MASK";
+ KeyboardInputMask enumUserChoiceInputKeyMask = KeyboardInputMask.valueForString(example);
+
+ assertNotNull("ENABLE_INPUT_KEY_MASK returned null", enumEnableInputKeyMask);
+ assertNotNull("DISABLE_INPUT_KEY_MASK returned null", enumDisableInputKeyMask);
+ assertNotNull("USER_CHOICE_INPUT_KEY_MASK returned null", enumUserChoiceInputKeyMask);
+ }
+
+ /**
+ * Verifies that an invalid assignment is null.
+ */
+ public void testInvalidEnum() {
+ String example = "ENAablE_INPUT_KEY_MASK";
+ try {
+ KeyboardInputMask temp = KeyboardInputMask.valueForString(example);
+ assertNull("Result of valueForString should be null.", temp);
+ } catch (IllegalArgumentException exception) {
+ fail("Invalid enum throws IllegalArgumentException.");
+ }
+ }
+
+ /**
+ * Verifies that a null assignment is invalid.
+ */
+ public void testNullEnum() {
+ String example = null;
+ try {
+ KeyboardInputMask temp = KeyboardInputMask.valueForString(example);
+ assertNull("Result of valueForString should be null.", temp);
+ } catch (NullPointerException exception) {
+ fail("Null string throws NullPointerException.");
+ }
+ }
+
+ /**
+ * Verifies the possible enum values of MassageCushion.
+ */
+ public void testListEnum() {
+ List<KeyboardInputMask> enumValueList = Arrays.asList(KeyboardInputMask.values());
+
+ List<KeyboardInputMask> enumTestList = new ArrayList<KeyboardInputMask>();
+ enumTestList.add(KeyboardInputMask.ENABLE_INPUT_KEY_MASK);
+ enumTestList.add(KeyboardInputMask.DISABLE_INPUT_KEY_MASK);
+ enumTestList.add(KeyboardInputMask.USER_CHOICE_INPUT_KEY_MASK);
+
+ assertTrue("Enum value list does not match enum class list",
+ enumValueList.containsAll(enumTestList) && enumTestList.containsAll(enumValueList));
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardLayoutTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardLayoutTests.java
index c75b9d7e9..52a7236e7 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardLayoutTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/KeyboardLayoutTests.java
@@ -24,10 +24,13 @@ public class KeyboardLayoutTests extends TestCase {
KeyboardLayout enumQwertz = KeyboardLayout.valueForString(example);
example = "AZERTY";
KeyboardLayout enumAzerty = KeyboardLayout.valueForString(example);
+ example = "NUMERIC";
+ KeyboardLayout enumNumeric = KeyboardLayout.valueForString(example);
assertNotNull("QWERTY returned null", enumQwerty);
assertNotNull("QWERTZ returned null", enumQwertz);
assertNotNull("AZERTY returned null", enumAzerty);
+ assertNotNull("NUMERIC returned null", enumNumeric);
}
/**
@@ -66,6 +69,7 @@ public class KeyboardLayoutTests extends TestCase {
enumTestList.add(KeyboardLayout.QWERTY);
enumTestList.add(KeyboardLayout.QWERTZ);
enumTestList.add(KeyboardLayout.AZERTY);
+ enumTestList.add(KeyboardLayout.NUMERIC);
assertTrue("Enum value list does not match enum class list",
enumValueList.containsAll(enumTestList) && enumTestList.containsAll(enumValueList));
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/StaticIconNameTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/StaticIconNameTests.java
index e4c81deea..82866744c 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/StaticIconNameTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/StaticIconNameTests.java
@@ -48,8 +48,8 @@ public class StaticIconNameTests extends TestCase {
assertNotNull("AUDIOBOOK_NARRATOR returned null", AUDIOBOOK_NARRATOR);
example = "0x45";
- StaticIconName AUXILLARY_AUDIO = StaticIconName.valueForString(example);
- assertNotNull("AUXILLARY_AUDIO returned null", AUXILLARY_AUDIO);
+ StaticIconName AUXILIARY_AUDIO = StaticIconName.valueForString(example);
+ assertNotNull("AUXILIARY_AUDIO returned null", AUXILIARY_AUDIO);
example = "0x86";
StaticIconName BACK = StaticIconName.valueForString(example);
@@ -755,6 +755,7 @@ public class StaticIconNameTests extends TestCase {
enumTestList.add(StaticIconName.AUDIOBOOK_EPISODE);
enumTestList.add(StaticIconName.AUDIOBOOK_NARRATOR);
enumTestList.add(StaticIconName.AUXILLARY_AUDIO);
+ enumTestList.add(StaticIconName.AUXILIARY_AUDIO);
enumTestList.add(StaticIconName.BACK);
enumTestList.add(StaticIconName.BATTERY_CAPACITY_0_OF_5);
enumTestList.add(StaticIconName.BATTERY_CAPACITY_1_OF_5);
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/TextFieldNameTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/TextFieldNameTests.java
index 0eb05f848..9f7b9a8e6 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/TextFieldNameTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/TextFieldNameTests.java
@@ -77,6 +77,14 @@ public class TextFieldNameTests extends TestCase {
TextFieldName enumPhone = TextFieldName.valueForString(example);
example = "templateTitle";
TextFieldName templateTitle = TextFieldName.valueForString(example);
+ example = "menuCommandSecondaryText";
+ TextFieldName menuCommandSecondaryText = TextFieldName.valueForString(example);
+ example = "menuCommandTertiaryText";
+ TextFieldName menuCommandTertiaryText = TextFieldName.valueForString(example);
+ example = "menuSubMenuSecondaryText";
+ TextFieldName menuSubMenuSecondaryText = TextFieldName.valueForString(example);
+ example = "menuSubMenuTertiaryText";
+ TextFieldName menuSubMenuTertiaryText = TextFieldName.valueForString(example);
assertNotNull("mainField1 returned null", enumMainField1);
assertNotNull("mainField2 returned null", enumMainField2);
@@ -103,6 +111,10 @@ public class TextFieldNameTests extends TestCase {
assertNotNull("tertiaryText returned null", enumTertiaryText);
assertNotNull("menuTitle returned null", enumMenuTitle);
assertNotNull("templateTitle returned null", templateTitle);
+ assertNotNull("menuCommandSecondaryText returned null", menuCommandSecondaryText);
+ assertNotNull("menuCommandTertiaryText returned null", menuCommandTertiaryText);
+ assertNotNull("menuSubMenuSecondaryText returned null", menuSubMenuSecondaryText);
+ assertNotNull("menuSubMenuTertiaryText returned null", menuSubMenuTertiaryText);
assertNotNull(TestValues.NOT_NULL, enumLocName);
@@ -172,10 +184,16 @@ public class TextFieldNameTests extends TestCase {
enumTestList.add(TextFieldName.locationDescription);
enumTestList.add(TextFieldName.addressLines);
enumTestList.add(TextFieldName.phoneNumber);
+ enumTestList.add(TextFieldName.timeToDestination);
+ enumTestList.add(TextFieldName.turnText);
enumTestList.add(TextFieldName.templateTitle);
enumTestList.add(TextFieldName.subtleAlertText1);
enumTestList.add(TextFieldName.subtleAlertText2);
enumTestList.add(TextFieldName.subtleAlertSoftButtonText);
+ enumTestList.add(TextFieldName.menuCommandSecondaryText);
+ enumTestList.add(TextFieldName.menuCommandTertiaryText);
+ enumTestList.add(TextFieldName.menuSubMenuSecondaryText);
+ enumTestList.add(TextFieldName.menuSubMenuTertiaryText);
assertTrue("Enum value list does not match enum class list",
enumValueList.containsAll(enumTestList) && enumTestList.containsAll(enumValueList));
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/VehicleDataTypeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/VehicleDataTypeTests.java
index 1b270a9dd..94fbe0100 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/VehicleDataTypeTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/VehicleDataTypeTests.java
@@ -90,6 +90,10 @@ public class VehicleDataTypeTests extends TestCase {
VehicleDataType enumVehicleDataTypeWindowStatus = VehicleDataType.valueForString(example);
example = "VEHICLEDATA_STABILITYCONTROLSSTATUS";
VehicleDataType enumVehicleDataStabilityControlsStatus = VehicleDataType.valueForString(example);
+ example = "VEHICLEDATA_CLIMATEDATA";
+ VehicleDataType enumVehicleDataClimateData = VehicleDataType.valueForString(example);
+ example = "VEHICLEDATA_SEATOCCUPANCY";
+ VehicleDataType enumVehicleDataSeatOccupancy = VehicleDataType.valueForString(example);
assertNotNull("VEHICLEDATA_GPS returned null", enumVehicleDataGps);
assertNotNull("VEHICLEDATA_SPEED returned null", enumVehicleDataSpeed);
@@ -127,6 +131,8 @@ public class VehicleDataTypeTests extends TestCase {
assertNotNull("VEHICLEDATA_WINDOWSTATUS returned null", enumVehicleDataTypeWindowStatus);
assertNotNull("VEHICLEDATA_GEARSTATUS returned null", enumVehicleDataGearStatus);
assertNotNull("VEHICLEDATA_STABILITYCONTROLSSTATUS returned null", enumVehicleDataStabilityControlsStatus);
+ assertNotNull("VEHICLEDATA_CLIMATEDATA returned null", enumVehicleDataClimateData);
+ assertNotNull("VEHICLEDATA_SEATOCCUPANCY returned null", enumVehicleDataSeatOccupancy);
}
/**
@@ -198,6 +204,8 @@ public class VehicleDataTypeTests extends TestCase {
enumTestList.add(VehicleDataType.VEHICLEDATA_WINDOWSTATUS);
enumTestList.add(VehicleDataType.VEHICLEDATA_GEARSTATUS);
enumTestList.add(VehicleDataType.VEHICLEDATA_STABILITYCONTROLSSTATUS);
+ enumTestList.add(VehicleDataType.VEHICLEDATA_CLIMATEDATA);
+ enumTestList.add(VehicleDataType.VEHICLEDATA_SEATOCCUPANCY);
assertTrue("Enum value list does not match enum class list",
enumValueList.containsAll(enumTestList) && enumTestList.containsAll(enumValueList));
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnAppCapabilityUpdatedTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnAppCapabilityUpdatedTests.java
new file mode 100644
index 000000000..eaca5122c
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnAppCapabilityUpdatedTests.java
@@ -0,0 +1,75 @@
+package com.smartdevicelink.test.rpc.notifications;
+
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.rpc.AppCapability;
+import com.smartdevicelink.proxy.rpc.OnAppCapabilityUpdated;
+import com.smartdevicelink.test.BaseRpcTests;
+import com.smartdevicelink.test.TestValues;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.fail;
+
+public class OnAppCapabilityUpdatedTests extends BaseRpcTests {
+
+ @Override
+ protected RPCMessage createMessage(){
+ OnAppCapabilityUpdated msg = new OnAppCapabilityUpdated();
+ msg.setAppCapability(TestValues.GENERAL_APPCAPABILITY);
+
+ return msg;
+ }
+
+ @Override
+ protected String getMessageType(){
+ return RPCMessage.KEY_NOTIFICATION;
+ }
+
+ @Override
+ protected String getCommandType(){
+ return FunctionID.ON_APP_CAPABILITY_UPDATED.toString();
+ }
+
+ @Override
+ protected JSONObject getExpectedParameters(int sdlVersion){
+ JSONObject result = new JSONObject();
+
+ try{
+ result.put(OnAppCapabilityUpdated.KEY_APP_CAPABILITY, TestValues.GENERAL_APPCAPABILITY.serializeJSON());
+ }catch(JSONException e){
+ fail(TestValues.JSON_FAIL);
+ }
+
+ return result;
+ }
+
+ /**
+ * Tests the expected values of the RPC message.
+ */
+ @Test
+ public void testRpcValues () {
+ // Test Values
+ AppCapability appCapability = ( (OnAppCapabilityUpdated) msg ).getAppCapability();
+
+ // Valid Tests
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_APPCAPABILITY, appCapability);
+
+ // Invalid/Null Tests
+ OnAppCapabilityUpdated msg = new OnAppCapabilityUpdated();
+ assertNotNull(TestValues.NOT_NULL, msg);
+ testNullBase(msg);
+
+ assertNull(TestValues.NULL, msg.getAppCapability());
+
+ // test constructor with param
+ msg = new OnAppCapabilityUpdated(TestValues.GENERAL_APPCAPABILITY);
+ AppCapability generalAppCapability = msg.getAppCapability();
+ assertEquals(generalAppCapability, TestValues.GENERAL_APPCAPABILITY);
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnVehicleDataTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnVehicleDataTests.java
index cb60f7572..0710c79f5 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnVehicleDataTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/notifications/OnVehicleDataTests.java
@@ -6,6 +6,7 @@ import com.smartdevicelink.proxy.RPCMessage;
import com.smartdevicelink.proxy.rpc.AirbagStatus;
import com.smartdevicelink.proxy.rpc.BeltStatus;
import com.smartdevicelink.proxy.rpc.BodyInformation;
+import com.smartdevicelink.proxy.rpc.ClimateData;
import com.smartdevicelink.proxy.rpc.ClusterModeStatus;
import com.smartdevicelink.proxy.rpc.DeviceStatus;
import com.smartdevicelink.proxy.rpc.ECallInfo;
@@ -17,6 +18,7 @@ import com.smartdevicelink.proxy.rpc.GetVehicleDataResponse;
import com.smartdevicelink.proxy.rpc.HeadLampStatus;
import com.smartdevicelink.proxy.rpc.MyKey;
import com.smartdevicelink.proxy.rpc.OnVehicleData;
+import com.smartdevicelink.proxy.rpc.SeatOccupancy;
import com.smartdevicelink.proxy.rpc.SingleTireStatus;
import com.smartdevicelink.proxy.rpc.StabilityControlsStatus;
import com.smartdevicelink.proxy.rpc.TireStatus;
@@ -107,6 +109,8 @@ public class OnVehicleDataTests extends BaseRpcTests {
result.put(OnVehicleData.KEY_GEAR_STATUS, VehicleDataHelper.GEAR_STATUS);
result.put(OnVehicleData.KEY_WINDOW_STATUS, VehicleDataHelper.WINDOW_STATUS_LIST);
result.put(OnVehicleData.KEY_STABILITY_CONTROLS_STATUS, VehicleDataHelper.STABILITY_CONTROLS_STATUS);
+ result.put(OnVehicleData.KEY_SEAT_OCCUPANCY, VehicleDataHelper.SEAT_OCCUPANCY);
+ result.put(OnVehicleData.KEY_CLIMATE_DATA, VehicleDataHelper.CLIMATE_DATA);
result.put(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, VehicleDataHelper.OEM_CUSTOM_VEHICLE_DATA_STATE);
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
@@ -152,9 +156,11 @@ public class OnVehicleDataTests extends BaseRpcTests {
TurnSignal turnSignal = ((OnVehicleData) msg).getTurnSignal();
ElectronicParkBrakeStatus electronicParkBrakeStatus = ((OnVehicleData) msg).getElectronicParkBrakeStatus();
GearStatus gearStatus = ((OnVehicleData) msg).getGearStatus();
+ ClimateData climateData = ((OnVehicleData) msg).getClimateData();
StabilityControlsStatus stabilityControlsStatus = ((OnVehicleData) msg).getStabilityControlsStatus();
Object oemCustomVehicleData = ((OnVehicleData) msg).getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME);
Boolean handsOffSteering = ((OnVehicleData) msg).getHandsOffSteering();
+ SeatOccupancy seatOccupancy = ((OnVehicleData) msg).getSeatOccupancy();
// Valid Tests
assertEquals(TestValues.MATCH, VehicleDataHelper.SPEED, speed);
assertEquals(TestValues.MATCH, VehicleDataHelper.RPM, rpm);
@@ -176,6 +182,7 @@ public class OnVehicleDataTests extends BaseRpcTests {
assertEquals(TestValues.MATCH, VehicleDataHelper.WIPER_STATUS, wiper);
assertTrue(TestValues.TRUE, Validator.validateHeadLampStatus(VehicleDataHelper.HEAD_LAMP_STATUS, lamp));
assertEquals(TestValues.MATCH, VehicleDataHelper.ACC_PEDAL_POSITION, pedal);
+ assertEquals(TestValues.MATCH, VehicleDataHelper.CLIMATE_DATA, climateData);
assertEquals(TestValues.MATCH, VehicleDataHelper.STEERING_WHEEL_ANGLE, wheel);
assertTrue(TestValues.TRUE, Validator.validateECallInfo(VehicleDataHelper.E_CALL_INFO, ecall));
assertTrue(TestValues.TRUE, Validator.validateAirbagStatus(VehicleDataHelper.AIRBAG_STATUS, airbag));
@@ -189,6 +196,7 @@ public class OnVehicleDataTests extends BaseRpcTests {
assertEquals(TestValues.MATCH, VehicleDataHelper.GEAR_STATUS, gearStatus);
assertEquals(TestValues.MATCH, VehicleDataHelper.STABILITY_CONTROLS_STATUS, stabilityControlsStatus);
assertEquals(TestValues.MATCH, VehicleDataHelper.OEM_CUSTOM_VEHICLE_DATA_STATE, oemCustomVehicleData);
+ assertEquals(TestValues.MATCH, VehicleDataHelper.SEAT_OCCUPANCY, seatOccupancy);
assertEquals(TestValues.MATCH, VehicleDataHelper.HANDS_OFF_STEERING, handsOffSteering);
// Invalid/Null Tests
OnVehicleData msg = new OnVehicleData();
@@ -227,7 +235,9 @@ public class OnVehicleDataTests extends BaseRpcTests {
assertNull(TestValues.NULL, msg.getHandsOffSteering());
assertNull(TestValues.NULL, msg.getWindowStatus());
assertNull(TestValues.NULL, msg.getGearStatus());
+ assertNull(TestValues.NULL, msg.getClimateData());
assertNull(TestValues.NULL, msg.getStabilityControlsStatus());
+ assertNull(TestValues.NULL, msg.getSeatOccupancy());
assertNull(TestValues.NULL, msg.getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
}
@@ -251,6 +261,8 @@ public class OnVehicleDataTests extends BaseRpcTests {
JSONObject windowStatusObj = new JSONObject();
JSONObject gearStatusObj = new JSONObject();
JSONObject stabilityControlStatusObj = new JSONObject();
+ JSONObject climateDataObj = new JSONObject();
+ JSONObject seatOccupancyObj = new JSONObject();
JSONArray fuelRangeArrayObj = new JSONArray();
JSONArray windowStatusArrayObj = new JSONArray();
@@ -397,6 +409,10 @@ public class OnVehicleDataTests extends BaseRpcTests {
gearStatusObj.put(GearStatus.KEY_TRANSMISSION_TYPE, VehicleDataHelper.TRANSMISSION_TYPE);
gearStatusObj.put(GearStatus.KEY_ACTUAL_GEAR, VehicleDataHelper.ACTUAL_GEAR);
+ // SEAT OCCUPANCY
+ seatOccupancyObj.put(SeatOccupancy.KEY_SEATS_OCCUPIED, VehicleDataHelper.SEATS_OCCUPIED);
+ seatOccupancyObj.put(SeatOccupancy.KEY_SEATS_BELTED, VehicleDataHelper.SEATS_BELTED);
+
reference.put(OnVehicleData.KEY_SPEED, VehicleDataHelper.SPEED);
reference.put(OnVehicleData.KEY_RPM, VehicleDataHelper.RPM);
reference.put(OnVehicleData.KEY_EXTERNAL_TEMPERATURE, VehicleDataHelper.EXTERNAL_TEMPERATURE);
@@ -431,6 +447,14 @@ public class OnVehicleDataTests extends BaseRpcTests {
reference.put(OnVehicleData.KEY_GEAR_STATUS, gearStatusObj);
reference.put(OnVehicleData.KEY_STABILITY_CONTROLS_STATUS, stabilityControlStatusObj);
reference.put(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, VehicleDataHelper.OEM_CUSTOM_VEHICLE_DATA_STATE);
+ reference.put(OnVehicleData.KEY_SEAT_OCCUPANCY, seatOccupancyObj);
+
+ // CLIMATE_DATA
+ climateDataObj.put(ClimateData.KEY_ATMOSPHERIC_PRESSURE, VehicleDataHelper.CLIMATE_DATA_ATM_PRESSURE);
+ climateDataObj.put(ClimateData.KEY_CABIN_TEMPERATURE, VehicleDataHelper.CLIMATE_DATA_CABIN_TEMP.serializeJSON());
+ climateDataObj.put(ClimateData.KEY_EXTERNAL_TEMPERATURE, VehicleDataHelper.CLIMATE_DATA_EXT_TEMP.serializeJSON());
+
+ reference.put(OnVehicleData.KEY_CLIMATE_DATA, climateDataObj);
JSONObject underTest = msg.serializeJSON();
//go inside underTest and only return the JSONObject inside the parameters key inside the notification key
@@ -528,6 +552,14 @@ public class OnVehicleDataTests extends BaseRpcTests {
assertTrue(TestValues.TRUE, Validator.validateGearStatuses(
new GearStatus(JsonRPCMarshaller.deserializeJSONObject(myKeyObjReference)),
new GearStatus(JsonRPCMarshaller.deserializeJSONObject(myKeyObjTest))));
+ }
+ else if (key.equals(OnVehicleData.KEY_SEAT_OCCUPANCY)) {
+ JSONObject myKeyObjReference = JsonUtils.readJsonObjectFromJsonObject(reference, key);
+ JSONObject myKeyObjTest = JsonUtils.readJsonObjectFromJsonObject(underTest, key);
+
+ assertTrue(TestValues.TRUE, Validator.validateSeatOccupancies(
+ new SeatOccupancy(JsonRPCMarshaller.deserializeJSONObject(myKeyObjReference)),
+ new SeatOccupancy(JsonRPCMarshaller.deserializeJSONObject(myKeyObjTest))));
} else if (key.equals(OnVehicleData.KEY_ENGINE_OIL_LIFE)) {
assertEquals(JsonUtils.readDoubleFromJsonObject(reference, key), JsonUtils.readDoubleFromJsonObject(underTest, key));
} else if (key.equals(OnVehicleData.KEY_HANDS_OFF_STEERING)) {
@@ -550,7 +582,21 @@ public class OnVehicleDataTests extends BaseRpcTests {
assertTrue(TestValues.TRUE, Validator.validateFuelRange(
fuelRangeRefereceList,
fuelRangeUnderTestList));
- } else if (key.equals(OnVehicleData.KEY_WINDOW_STATUS)) {
+ } else if (key.equals(GetVehicleDataResponse.KEY_CLIMATE_DATA)) {
+ JSONObject myKeyObjReference = JsonUtils.readJsonObjectFromJsonObject(reference, key);
+ JSONObject myKeyObjTest = JsonUtils.readJsonObjectFromJsonObject(underTest, key);
+
+ ClimateData climateData1 = new ClimateData(JsonRPCMarshaller.deserializeJSONObject(myKeyObjReference));
+ ClimateData climateData2 = new ClimateData(JsonRPCMarshaller.deserializeJSONObject(myKeyObjTest));
+
+ assertTrue("JSON value didn't match expected value for key \"" + key + "\".",
+ Validator.validateClimateData(
+ climateData1,
+ climateData2
+ )
+ );
+ }
+ else if (key.equals(OnVehicleData.KEY_WINDOW_STATUS)) {
JSONArray windowStatusArrayObjReference = JsonUtils.readJsonArrayFromJsonObject(reference, key);
List<WindowStatus> windowStatusReferenceList = new ArrayList<>();
for (int index = 0; index < windowStatusArrayObjReference.length(); index++) {
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/AddCommandTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/AddCommandTests.java
index ebcb09943..f76e5eadd 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/AddCommandTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/AddCommandTests.java
@@ -40,6 +40,7 @@ public class AddCommandTests extends BaseRpcTests {
msg.setMenuParams(TestValues.GENERAL_MENUPARAMS);
msg.setVrCommands(TestValues.GENERAL_STRING_LIST);
msg.setCmdID(TestValues.GENERAL_INT);
+ msg.setSecondaryImage(TestValues.GENERAL_IMAGE);
return msg;
}
@@ -63,6 +64,7 @@ public class AddCommandTests extends BaseRpcTests {
result.put(AddCommand.KEY_MENU_PARAMS, TestValues.JSON_MENUPARAMS);
result.put(AddCommand.KEY_VR_COMMANDS, JsonUtils.createJsonArray(TestValues.GENERAL_STRING_LIST));
result.put(AddCommand.KEY_CMD_ID, TestValues.GENERAL_INT);
+ result.put(AddCommand.KEY_SECONDARY_IMAGE, TestValues.JSON_IMAGE);
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
}
@@ -80,11 +82,13 @@ public class AddCommandTests extends BaseRpcTests {
Image testImage = ((AddCommand) msg).getCmdIcon();
MenuParams testMenuParams = ((AddCommand) msg).getMenuParams();
List<String> testVrCommands = ((AddCommand) msg).getVrCommands();
+ Image testSecondaryImage = ((AddCommand) msg).getSecondaryImage();
// Valid Tests
assertNotNull(TestValues.NOT_NULL, testMenuParams);
assertNotNull(TestValues.NOT_NULL, testImage);
assertNotNull(TestValues.NOT_NULL, testVrCommands);
+ assertNotNull(TestValues.NOT_NULL, testSecondaryImage);
assertEquals(TestValues.MATCH, TestValues.GENERAL_INT, testCmdId);
assertEquals(TestValues.MATCH, TestValues.GENERAL_STRING_LIST.size(), testVrCommands.size());
@@ -92,6 +96,7 @@ public class AddCommandTests extends BaseRpcTests {
assertTrue(TestValues.TRUE, Validator.validateMenuParams(TestValues.GENERAL_MENUPARAMS, testMenuParams));
assertTrue(TestValues.TRUE, Validator.validateImage(TestValues.GENERAL_IMAGE, testImage));
assertTrue(TestValues.TRUE, Validator.validateStringList(TestValues.GENERAL_STRING_LIST, testVrCommands));
+ assertTrue(TestValues.TRUE, Validator.validateImage(TestValues.GENERAL_IMAGE, testSecondaryImage));
// Invalid/Null Tests
AddCommand msg = new AddCommand();
@@ -102,6 +107,7 @@ public class AddCommandTests extends BaseRpcTests {
assertNull(TestValues.NULL, msg.getCmdID());
assertNull(TestValues.NULL, msg.getMenuParams());
assertNull(TestValues.NULL, msg.getVrCommands());
+ assertNull(TestValues.NULL, msg.getSecondaryImage());
}
/**
@@ -140,6 +146,10 @@ public class AddCommandTests extends BaseRpcTests {
Image referenceCmdIcon = new Image(JsonRPCMarshaller.deserializeJSONObject(cmdIcon));
assertTrue(TestValues.TRUE, Validator.validateImage(referenceCmdIcon, cmd.getCmdIcon()));
+ JSONObject secondaryIcon = JsonUtils.readJsonObjectFromJsonObject(parameters, AddCommand.KEY_SECONDARY_IMAGE);
+ Image referenceSecondaryIcon = new Image(JsonRPCMarshaller.deserializeJSONObject(secondaryIcon));
+ assertTrue(TestValues.TRUE, Validator.validateImage(referenceSecondaryIcon, cmd.getSecondaryImage()));
+
} catch (JSONException e) {
e.printStackTrace();
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/AddSubmenuTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/AddSubmenuTests.java
index e135d3217..db70162ab 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/AddSubmenuTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/AddSubmenuTests.java
@@ -41,6 +41,9 @@ public class AddSubmenuTests extends BaseRpcTests {
msg.setMenuIcon(TestValues.GENERAL_IMAGE);
msg.setMenuLayout(TestValues.GENERAL_MENU_LAYOUT);
msg.setParentID(TestValues.GENERAL_MENU_MAX_ID);
+ msg.setSecondaryText(TestValues.GENERAL_STRING);
+ msg.setTertiaryText(TestValues.GENERAL_STRING);
+ msg.setSecondaryImage(TestValues.GENERAL_IMAGE);
return msg;
}
@@ -66,6 +69,9 @@ public class AddSubmenuTests extends BaseRpcTests {
result.put(AddSubMenu.KEY_MENU_ICON, TestValues.JSON_IMAGE);
result.put(AddSubMenu.KEY_MENU_LAYOUT, TestValues.GENERAL_MENU_LAYOUT);
result.put(AddSubMenu.KEY_PARENT_ID, TestValues.GENERAL_MENU_MAX_ID);
+ result.put(AddSubMenu.KEY_SECONDARY_TEXT, TestValues.GENERAL_STRING);
+ result.put(AddSubMenu.KEY_TERTIARY_TEXT, TestValues.GENERAL_STRING);
+ result.put(AddSubMenu.KEY_SECONDARY_IMAGE, TestValues.JSON_IMAGE);
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
}
@@ -85,6 +91,9 @@ public class AddSubmenuTests extends BaseRpcTests {
Image testMenuIcon = ((AddSubMenu) msg).getMenuIcon();
MenuLayout testMenuLayout = ((AddSubMenu) msg).getMenuLayout();
int testParentID = ((AddSubMenu) msg).getParentID();
+ String testSecondaryText = ((AddSubMenu) msg).getSecondaryText();
+ String testTertiaryText = ((AddSubMenu) msg).getTertiaryText();
+ Image testSecondaryImage = ((AddSubMenu) msg).getSecondaryImage();
// Valid Tests
assertEquals("Menu ID didn't match input menu ID.", TestValues.GENERAL_INT, testMenuId);
@@ -93,6 +102,9 @@ public class AddSubmenuTests extends BaseRpcTests {
assertTrue("Menu icon didn't match input icon.", Validator.validateImage(TestValues.GENERAL_IMAGE, testMenuIcon));
assertEquals("Menu layout didn't match input menu layout.", TestValues.GENERAL_MENU_LAYOUT, testMenuLayout);
assertEquals("Parent ID didn't match input Parent ID.", TestValues.GENERAL_MENU_MAX_ID, testParentID);
+ assertEquals("Secondary Text didn't match input Secondary Text.", TestValues.GENERAL_STRING, testSecondaryText);
+ assertEquals("Tertiary Text didn't match input Tertiary Text.", TestValues.GENERAL_STRING, testTertiaryText);
+ assertTrue("Secondary Image didn't match input Secondary Image.", Validator.validateImage(TestValues.GENERAL_IMAGE, testSecondaryImage));
// Invalid/Null Tests
@@ -106,6 +118,9 @@ public class AddSubmenuTests extends BaseRpcTests {
assertNull(TestValues.NULL, msg.getMenuIcon());
assertNull(TestValues.NULL, msg.getMenuLayout());
assertNull(TestValues.NULL, msg.getParentID());
+ assertNull(TestValues.NULL, msg.getSecondaryText());
+ assertNull(TestValues.NULL, msg.getTertiaryText());
+ assertNull(TestValues.NULL, msg.getSecondaryImage());
}
/**
@@ -134,10 +149,15 @@ public class AddSubmenuTests extends BaseRpcTests {
assertEquals(TestValues.MATCH, JsonUtils.readStringFromJsonObject(parameters, AddSubMenu.KEY_MENU_NAME), cmd.getMenuName());
assertEquals(TestValues.MATCH, JsonUtils.readObjectFromJsonObject(parameters, AddSubMenu.KEY_MENU_LAYOUT), cmd.getMenuLayout());
assertEquals(TestValues.MATCH, JsonUtils.readIntegerFromJsonObject(parameters, AddSubMenu.KEY_PARENT_ID), cmd.getParentID());
+ assertEquals(TestValues.MATCH, JsonUtils.readStringFromJsonObject(parameters, AddSubMenu.KEY_SECONDARY_TEXT), cmd.getSecondaryText());
+ assertEquals(TestValues.MATCH, JsonUtils.readStringFromJsonObject(parameters, AddSubMenu.KEY_TERTIARY_TEXT), cmd.getTertiaryText());
JSONObject menuIcon = JsonUtils.readJsonObjectFromJsonObject(parameters, AddSubMenu.KEY_MENU_ICON);
Image referenceMenuIcon = new Image(JsonRPCMarshaller.deserializeJSONObject(menuIcon));
assertTrue(TestValues.TRUE, Validator.validateImage(referenceMenuIcon, cmd.getMenuIcon()));
+ JSONObject secondaryIcon = JsonUtils.readJsonObjectFromJsonObject(parameters, AddSubMenu.KEY_SECONDARY_IMAGE);
+ Image referenceSecondaryIcon = new Image(JsonRPCMarshaller.deserializeJSONObject(secondaryIcon));
+ assertTrue(TestValues.TRUE, Validator.validateImage(referenceSecondaryIcon, cmd.getSecondaryImage()));
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/GetVehicleDataTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/GetVehicleDataTests.java
index fe726018f..17f66e47c 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/GetVehicleDataTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/GetVehicleDataTests.java
@@ -65,6 +65,8 @@ public class GetVehicleDataTests extends BaseRpcTests {
msg.setWindowStatus(TestValues.GENERAL_BOOLEAN);
msg.setGearStatus(TestValues.GENERAL_BOOLEAN);
msg.setStabilityControlsStatus(TestValues.GENERAL_BOOLEAN);
+ msg.setSeatOccupancy(TestValues.GENERAL_BOOLEAN);
+ msg.setClimateData(TestValues.GENERAL_BOOLEAN);
msg.setOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, TestValues.GENERAL_BOOLEAN);
return msg;
@@ -118,6 +120,8 @@ public class GetVehicleDataTests extends BaseRpcTests {
result.put(GetVehicleData.KEY_GEAR_STATUS, TestValues.GENERAL_BOOLEAN);
result.put(GetVehicleData.KEY_WINDOW_STATUS, TestValues.GENERAL_BOOLEAN);
result.put(GetVehicleData.KEY_STABILITY_CONTROLS_STATUS, TestValues.GENERAL_BOOLEAN);
+ result.put(GetVehicleData.KEY_CLIMATE_DATA, TestValues.GENERAL_BOOLEAN);
+ result.put(GetVehicleData.KEY_SEAT_OCCUPANCY, TestValues.GENERAL_BOOLEAN);
result.put(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, TestValues.GENERAL_BOOLEAN);
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
@@ -166,6 +170,8 @@ public class GetVehicleDataTests extends BaseRpcTests {
assertTrue(TestValues.TRUE, ((GetVehicleData) msg).getWindowStatus());
assertTrue(TestValues.TRUE, ((GetVehicleData) msg).getGearStatus());
assertTrue(TestValues.TRUE, ((GetVehicleData) msg).getStabilityControlsStatus());
+ assertTrue(TestValues.TRUE, ((GetVehicleData) msg).getSeatOccupancy());
+ assertTrue(TestValues.TRUE, ((GetVehicleData) msg).getClimateData());
assertTrue(TestValues.TRUE, ((GetVehicleData) msg).getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
// Invalid/Null Tests
@@ -206,6 +212,8 @@ public class GetVehicleDataTests extends BaseRpcTests {
assertNull(TestValues.NULL, msg.getWindowStatus());
assertNull(TestValues.NULL, msg.getGearStatus());
assertNull(TestValues.NULL, msg.getStabilityControlsStatus());
+ assertNull(TestValues.NULL, msg.getClimateData());
+ assertNull(TestValues.NULL, msg.getSeatOccupancy());
assertNull(TestValues.NULL, msg.getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
}
@@ -261,7 +269,9 @@ public class GetVehicleDataTests extends BaseRpcTests {
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, GetVehicleData.KEY_HANDS_OFF_STEERING), cmd.getHandsOffSteering());
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, GetVehicleData.KEY_WINDOW_STATUS), cmd.getWindowStatus());
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, GetVehicleData.KEY_GEAR_STATUS), cmd.getGearStatus());
+ assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, GetVehicleData.KEY_SEAT_OCCUPANCY), cmd.getSeatOccupancy());
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, GetVehicleData.KEY_STABILITY_CONTROLS_STATUS), cmd.getStabilityControlsStatus());
+ assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, GetVehicleData.KEY_CLIMATE_DATA), cmd.getClimateData());
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME), cmd.getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/SetMediaClockTimerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/SetMediaClockTimerTests.java
index 7f22c0701..eb660927b 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/SetMediaClockTimerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/SetMediaClockTimerTests.java
@@ -3,9 +3,11 @@ package com.smartdevicelink.test.rpc.requests;
import com.smartdevicelink.marshal.JsonRPCMarshaller;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.rpc.SeekStreamingIndicator;
import com.smartdevicelink.proxy.rpc.SetMediaClockTimer;
import com.smartdevicelink.proxy.rpc.StartTime;
import com.smartdevicelink.proxy.rpc.enums.AudioStreamingIndicator;
+import com.smartdevicelink.proxy.rpc.enums.SeekIndicatorType;
import com.smartdevicelink.proxy.rpc.enums.UpdateMode;
import com.smartdevicelink.test.BaseRpcTests;
import com.smartdevicelink.test.JsonUtils;
@@ -40,6 +42,9 @@ public class SetMediaClockTimerTests extends BaseRpcTests {
msg.setEndTime(TestValues.GENERAL_STARTTIME);
msg.setUpdateMode(TestValues.GENERAL_UPDATEMODE);
msg.setAudioStreamingIndicator(TestValues.GENERAL_AUDIO_STREAMING_INDICATOR);
+ msg.setCountRate(TestValues.GENERAL_FLOAT);
+ msg.setForwardSeekIndicator(TestValues.GENERAL_SEEK_STREAMING_INDICATOR);
+ msg.setBackSeekIndicator(TestValues.GENERAL_SEEK_STREAMING_INDICATOR);
return msg;
}
@@ -63,6 +68,9 @@ public class SetMediaClockTimerTests extends BaseRpcTests {
result.put(SetMediaClockTimer.KEY_END_TIME, TestValues.JSON_STARTTIME);
result.put(SetMediaClockTimer.KEY_UPDATE_MODE, TestValues.GENERAL_UPDATEMODE);
result.put(SetMediaClockTimer.KEY_AUDIO_STREAMING_INDICATOR, TestValues.GENERAL_AUDIO_STREAMING_INDICATOR);
+ result.put(SetMediaClockTimer.KEY_COUNT_RATE, TestValues.GENERAL_FLOAT);
+ result.put(SetMediaClockTimer.KEY_FORWARD_SEEK_INDICATOR, TestValues.JSON_SEEK_STREAMING_INDICATOR);
+ result.put(SetMediaClockTimer.KEY_BACK_SEEK_INDICATOR, TestValues.JSON_SEEK_STREAMING_INDICATOR);
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
}
@@ -80,12 +88,18 @@ public class SetMediaClockTimerTests extends BaseRpcTests {
StartTime testEndTime = ((SetMediaClockTimer) msg).getEndTime();
UpdateMode testUpdateMode = ((SetMediaClockTimer) msg).getUpdateMode();
AudioStreamingIndicator testAudioStreamingIndicator = ((SetMediaClockTimer) msg).getAudioStreamingIndicator();
+ Float testCountRate = ((SetMediaClockTimer) msg).getCountRate();
+ SeekStreamingIndicator testForwardSeekStreamingIndicator = ((SetMediaClockTimer) msg).getForwardSeekIndicator();
+ SeekStreamingIndicator testBackSeekStreamingIndicator = ((SetMediaClockTimer) msg).getBackSeekIndicator();
// Valid Tests
assertEquals(TestValues.MATCH, TestValues.GENERAL_UPDATEMODE, testUpdateMode);
assertEquals(TestValues.MATCH, TestValues.GENERAL_AUDIO_STREAMING_INDICATOR, testAudioStreamingIndicator);
assertTrue(TestValues.TRUE, Validator.validateStartTime(TestValues.GENERAL_STARTTIME, testStartTime));
assertTrue(TestValues.TRUE, Validator.validateStartTime(TestValues.GENERAL_STARTTIME, testEndTime));
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_FLOAT, testCountRate);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_SEEK_STREAMING_INDICATOR, testForwardSeekStreamingIndicator);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_SEEK_STREAMING_INDICATOR, testBackSeekStreamingIndicator);
// Invalid/Null Tests
SetMediaClockTimer msg = new SetMediaClockTimer();
@@ -96,6 +110,9 @@ public class SetMediaClockTimerTests extends BaseRpcTests {
assertNull(TestValues.NULL, msg.getEndTime());
assertNull(TestValues.NULL, msg.getUpdateMode());
assertNull(TestValues.NULL, msg.getAudioStreamingIndicator());
+ assertNull(TestValues.NULL, msg.getCountRate());
+ assertNull(TestValues.NULL, msg.getForwardSeekIndicator());
+ assertNull(TestValues.NULL, msg.getBackSeekIndicator());
}
/**
@@ -110,10 +127,14 @@ public class SetMediaClockTimerTests extends BaseRpcTests {
SetMediaClockTimer msg;
msg = SetMediaClockTimer.countUpFromStartTimeInterval(timeInterval1, timeInterval2, TestValues.GENERAL_AUDIO_STREAMING_INDICATOR);
+ msg.setForwardSeekIndicator(TestValues.GENERAL_SEEK_STREAMING_INDICATOR);
+ msg.setBackSeekIndicator(TestValues.GENERAL_SEEK_STREAMING_INDICATOR);
assertEquals(TestValues.MATCH, msg.getUpdateMode(), UpdateMode.COUNTUP);
assertTrue(TestValues.TRUE, Validator.validateStartTime(startTime1, msg.getStartTime()));
assertTrue(TestValues.TRUE, Validator.validateStartTime(startTime2, msg.getEndTime()));
assertEquals(TestValues.MATCH, TestValues.GENERAL_AUDIO_STREAMING_INDICATOR, msg.getAudioStreamingIndicator());
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_SEEK_STREAMING_INDICATOR, msg.getForwardSeekIndicator());
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_SEEK_STREAMING_INDICATOR, msg.getBackSeekIndicator());
msg = SetMediaClockTimer.countUpFromStartTime(startTime1, startTime2, TestValues.GENERAL_AUDIO_STREAMING_INDICATOR);
assertEquals(TestValues.MATCH, msg.getUpdateMode(), UpdateMode.COUNTUP);
@@ -162,6 +183,9 @@ public class SetMediaClockTimerTests extends BaseRpcTests {
assertNull(TestValues.NULL, msg.getStartTime());
assertNull(TestValues.NULL, msg.getEndTime());
assertEquals(TestValues.MATCH, TestValues.GENERAL_AUDIO_STREAMING_INDICATOR, msg.getAudioStreamingIndicator());
+
+ msg = new SetMediaClockTimer().setCountRate(TestValues.GENERAL_FLOAT);
+ assertEquals(TestValues.GENERAL_FLOAT, msg.getCountRate());
}
/**
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/SubscribeVehicleDataTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/SubscribeVehicleDataTests.java
index 528eeb128..32750af89 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/SubscribeVehicleDataTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/SubscribeVehicleDataTests.java
@@ -63,7 +63,9 @@ public class SubscribeVehicleDataTests extends BaseRpcTests {
msg.setHandsOffSteering(TestValues.GENERAL_BOOLEAN);
msg.setWindowStatus(TestValues.GENERAL_BOOLEAN);
msg.setGearStatus(TestValues.GENERAL_BOOLEAN);
+ msg.setSeatOccupancy(TestValues.GENERAL_BOOLEAN);
msg.setStabilityControlsStatus(TestValues.GENERAL_BOOLEAN);
+ msg.setClimateData(TestValues.GENERAL_BOOLEAN);
msg.setOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, TestValues.GENERAL_BOOLEAN);
return msg;
@@ -116,6 +118,8 @@ public class SubscribeVehicleDataTests extends BaseRpcTests {
result.put(SubscribeVehicleData.KEY_WINDOW_STATUS, TestValues.GENERAL_BOOLEAN);
result.put(SubscribeVehicleData.KEY_GEAR_STATUS, TestValues.GENERAL_BOOLEAN);
result.put(SubscribeVehicleData.KEY_STABILITY_CONTROLS_STATUS, TestValues.GENERAL_BOOLEAN);
+ result.put(SubscribeVehicleData.KEY_CLIMATE_DATA, TestValues.GENERAL_BOOLEAN);
+ result.put(SubscribeVehicleData.KEY_SEAT_OCCUPANCY, TestValues.GENERAL_BOOLEAN);
result.put(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, TestValues.GENERAL_BOOLEAN);
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
@@ -163,6 +167,8 @@ public class SubscribeVehicleDataTests extends BaseRpcTests {
assertTrue(TestValues.MATCH, ((SubscribeVehicleData) msg).getWindowStatus());
assertTrue(TestValues.MATCH, ((SubscribeVehicleData) msg).getGearStatus());
assertTrue(TestValues.MATCH, ((SubscribeVehicleData) msg).getStabilityControlsStatus());
+ assertTrue(TestValues.MATCH, ((SubscribeVehicleData) msg).getClimateData());
+ assertTrue(TestValues.MATCH, ((SubscribeVehicleData) msg).getSeatOccupancy());
assertTrue(TestValues.MATCH, ((SubscribeVehicleData) msg).getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
// Invalid/Null Tests
@@ -202,6 +208,8 @@ public class SubscribeVehicleDataTests extends BaseRpcTests {
assertNull(TestValues.NULL, msg.getWindowStatus());
assertNull(TestValues.NULL, msg.getGearStatus());
assertNull(TestValues.NULL, msg.getStabilityControlsStatus());
+ assertNull(TestValues.NULL, msg.getClimateData());
+ assertNull(TestValues.NULL, msg.getSeatOccupancy());
assertNull(TestValues.NULL, msg.getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
}
@@ -256,6 +264,8 @@ public class SubscribeVehicleDataTests extends BaseRpcTests {
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, SubscribeVehicleData.KEY_HANDS_OFF_STEERING), cmd.getHandsOffSteering());
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, SubscribeVehicleData.KEY_WINDOW_STATUS), cmd.getWindowStatus());
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, SubscribeVehicleData.KEY_STABILITY_CONTROLS_STATUS), cmd.getStabilityControlsStatus());
+ assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, SubscribeVehicleData.KEY_CLIMATE_DATA), cmd.getClimateData());
+ assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, SubscribeVehicleData.KEY_SEAT_OCCUPANCY), cmd.getSeatOccupancy());
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME), cmd.getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/UnsubscribeVehicleDataTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/UnsubscribeVehicleDataTests.java
index cfe2548f3..4aeb5ce66 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/UnsubscribeVehicleDataTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/requests/UnsubscribeVehicleDataTests.java
@@ -67,6 +67,8 @@ public class UnsubscribeVehicleDataTests extends BaseRpcTests {
msg.setHandsOffSteering(TestValues.GENERAL_BOOLEAN);
msg.setWindowStatus(TestValues.GENERAL_BOOLEAN);
msg.setStabilityControlsStatus(TestValues.GENERAL_BOOLEAN);
+ msg.setSeatOccupancy(TestValues.GENERAL_BOOLEAN);
+ msg.setClimateData(TestValues.GENERAL_BOOLEAN);
msg.setOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, TestValues.GENERAL_BOOLEAN);
return msg;
@@ -120,6 +122,8 @@ public class UnsubscribeVehicleDataTests extends BaseRpcTests {
result.put(UnsubscribeVehicleData.KEY_WINDOW_STATUS, TestValues.GENERAL_BOOLEAN);
result.put(UnsubscribeVehicleData.KEY_GEAR_STATUS, TestValues.GENERAL_BOOLEAN);
result.put(UnsubscribeVehicleData.KEY_STABILITY_CONTROLS_STATUS, TestValues.GENERAL_BOOLEAN);
+ result.put(UnsubscribeVehicleData.KEY_SEAT_OCCUPANCY, TestValues.GENERAL_BOOLEAN);
+ result.put(UnsubscribeVehicleData.KEY_CLIMATE_DATA, TestValues.GENERAL_BOOLEAN);
result.put(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, TestValues.GENERAL_BOOLEAN);
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
@@ -167,6 +171,8 @@ public class UnsubscribeVehicleDataTests extends BaseRpcTests {
assertTrue(TestValues.TRUE, ((UnsubscribeVehicleData) msg).getWindowStatus());
assertTrue(TestValues.TRUE, ((UnsubscribeVehicleData) msg).getGearStatus());
assertTrue(TestValues.TRUE, ((UnsubscribeVehicleData) msg).getStabilityControlsStatus());
+ assertTrue(TestValues.TRUE, ((UnsubscribeVehicleData) msg).getClimateData());
+ assertTrue(TestValues.TRUE, ((UnsubscribeVehicleData) msg).getSeatOccupancy());
assertTrue(TestValues.TRUE, ((UnsubscribeVehicleData) msg).getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
// Invalid/Null Tests
UnsubscribeVehicleData msg = new UnsubscribeVehicleData();
@@ -206,6 +212,8 @@ public class UnsubscribeVehicleDataTests extends BaseRpcTests {
assertNull(TestValues.NULL, msg.getWindowStatus());
assertNull(TestValues.NULL, msg.getGearStatus());
assertNull(TestValues.NULL, msg.getStabilityControlsStatus());
+ assertNull(TestValues.NULL, msg.getSeatOccupancy());
+ assertNull(TestValues.NULL, msg.getClimateData());
assertNull(TestValues.NULL, msg.getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
}
@@ -262,6 +270,8 @@ public class UnsubscribeVehicleDataTests extends BaseRpcTests {
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, UnsubscribeVehicleData.KEY_WINDOW_STATUS), cmd.getWindowStatus());
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, UnsubscribeVehicleData.KEY_GEAR_STATUS), cmd.getGearStatus());
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, UnsubscribeVehicleData.KEY_STABILITY_CONTROLS_STATUS), cmd.getStabilityControlsStatus());
+ assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, UnsubscribeVehicleData.KEY_CLIMATE_DATA), cmd.getClimateData());
+ assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, UnsubscribeVehicleData.KEY_SEAT_OCCUPANCY), cmd.getSeatOccupancy());
assertEquals(TestValues.MATCH, JsonUtils.readBooleanFromJsonObject(parameters, TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME), cmd.getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
} catch (JSONException e) {
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/GetVehicleDataResponseTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/GetVehicleDataResponseTests.java
index 606283cc7..8430fd728 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/GetVehicleDataResponseTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/GetVehicleDataResponseTests.java
@@ -6,6 +6,7 @@ import com.smartdevicelink.proxy.RPCMessage;
import com.smartdevicelink.proxy.rpc.AirbagStatus;
import com.smartdevicelink.proxy.rpc.BeltStatus;
import com.smartdevicelink.proxy.rpc.BodyInformation;
+import com.smartdevicelink.proxy.rpc.ClimateData;
import com.smartdevicelink.proxy.rpc.ClusterModeStatus;
import com.smartdevicelink.proxy.rpc.DeviceStatus;
import com.smartdevicelink.proxy.rpc.ECallInfo;
@@ -16,6 +17,7 @@ import com.smartdevicelink.proxy.rpc.GearStatus;
import com.smartdevicelink.proxy.rpc.GetVehicleDataResponse;
import com.smartdevicelink.proxy.rpc.HeadLampStatus;
import com.smartdevicelink.proxy.rpc.MyKey;
+import com.smartdevicelink.proxy.rpc.SeatOccupancy;
import com.smartdevicelink.proxy.rpc.SingleTireStatus;
import com.smartdevicelink.proxy.rpc.StabilityControlsStatus;
import com.smartdevicelink.proxy.rpc.TireStatus;
@@ -105,6 +107,8 @@ public class GetVehicleDataResponseTests extends BaseRpcTests {
result.put(GetVehicleDataResponse.KEY_HANDS_OFF_STEERING, VehicleDataHelper.HANDS_OFF_STEERING);
result.put(GetVehicleDataResponse.KEY_GEAR_STATUS, VehicleDataHelper.GEAR_STATUS);
result.put(GetVehicleDataResponse.KEY_STABILITY_CONTROLS_STATUS, VehicleDataHelper.STABILITY_CONTROLS_STATUS);
+ result.put(GetVehicleDataResponse.KEY_CLIMATE_DATA, VehicleDataHelper.CLIMATE_DATA);
+ result.put(GetVehicleDataResponse.KEY_SEAT_OCCUPANCY, VehicleDataHelper.SEAT_OCCUPANCY);
result.put(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, VehicleDataHelper.OEM_CUSTOM_VEHICLE_DATA_STATE);
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
@@ -133,6 +137,8 @@ public class GetVehicleDataResponseTests extends BaseRpcTests {
JSONObject fuelRangeObj = new JSONObject();
JSONObject windowStatusObj = new JSONObject();
JSONObject gearStatusObj = new JSONObject();
+ JSONObject seatOccupancyObj = new JSONObject();
+ JSONObject climateDataObj = new JSONObject();
JSONArray fuelRangeArrayObj = new JSONArray();
JSONArray windowStatusArrayObj = new JSONArray();
@@ -278,6 +284,15 @@ public class GetVehicleDataResponseTests extends BaseRpcTests {
windowStatusObj.put(WindowStatus.KEY_STATE, VehicleDataHelper.WINDOW_STATE);
windowStatusArrayObj.put(windowStatusObj);
+ // CLIMATE_DATA
+ climateDataObj.put(ClimateData.KEY_ATMOSPHERIC_PRESSURE, VehicleDataHelper.CLIMATE_DATA_ATM_PRESSURE);
+ climateDataObj.put(ClimateData.KEY_CABIN_TEMPERATURE, VehicleDataHelper.CLIMATE_DATA_CABIN_TEMP.serializeJSON());
+ climateDataObj.put(ClimateData.KEY_EXTERNAL_TEMPERATURE, VehicleDataHelper.CLIMATE_DATA_EXT_TEMP.serializeJSON());
+
+ // SEAT_OCCUPANCY
+ seatOccupancyObj.put(SeatOccupancy.KEY_SEATS_BELTED, VehicleDataHelper.SEATS_BELTED);
+ seatOccupancyObj.put(SeatOccupancy.KEY_SEATS_OCCUPIED, VehicleDataHelper.SEATS_OCCUPIED);
+
reference.put(GetVehicleDataResponse.KEY_SPEED, VehicleDataHelper.SPEED);
reference.put(GetVehicleDataResponse.KEY_RPM, VehicleDataHelper.RPM);
reference.put(GetVehicleDataResponse.KEY_EXTERNAL_TEMPERATURE, VehicleDataHelper.EXTERNAL_TEMPERATURE);
@@ -307,10 +322,12 @@ public class GetVehicleDataResponseTests extends BaseRpcTests {
reference.put(GetVehicleDataResponse.KEY_FUEL_RANGE, fuelRangeArrayObj);
reference.put(GetVehicleDataResponse.KEY_TURN_SIGNAL, TurnSignal.OFF);
reference.put(GetVehicleDataResponse.KEY_GEAR_STATUS, gearStatusObj);
+ reference.put(GetVehicleDataResponse.KEY_CLIMATE_DATA, climateDataObj);
reference.put(GetVehicleDataResponse.KEY_ELECTRONIC_PARK_BRAKE_STATUS, VehicleDataHelper.ELECTRONIC_PARK_BRAKE_STATUS);
reference.put(GetVehicleDataResponse.KEY_WINDOW_STATUS, windowStatusArrayObj);
reference.put(GetVehicleDataResponse.KEY_HANDS_OFF_STEERING, VehicleDataHelper.HANDS_OFF_STEERING);
reference.put(GetVehicleDataResponse.KEY_STABILITY_CONTROLS_STATUS, stabilityControlsStatusObj);
+ reference.put(GetVehicleDataResponse.KEY_SEAT_OCCUPANCY, seatOccupancyObj);
reference.put(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, VehicleDataHelper.OEM_CUSTOM_VEHICLE_DATA_STATE);
JSONObject underTest = msg.serializeJSON();
@@ -424,6 +441,13 @@ public class GetVehicleDataResponseTests extends BaseRpcTests {
assertTrue(TestValues.TRUE, Validator.validateGearStatuses(
new GearStatus(JsonRPCMarshaller.deserializeJSONObject(myKeyObjReference)),
new GearStatus(JsonRPCMarshaller.deserializeJSONObject(myKeyObjTest))));
+ } else if (key.equals(GetVehicleDataResponse.KEY_SEAT_OCCUPANCY)) {
+ JSONObject myKeyObjReference = JsonUtils.readJsonObjectFromJsonObject(reference, key);
+ JSONObject myKeyObjTest = JsonUtils.readJsonObjectFromJsonObject(underTest, key);
+
+ assertTrue(TestValues.TRUE, Validator.validateSeatOccupancies(
+ new SeatOccupancy(JsonRPCMarshaller.deserializeJSONObject(myKeyObjReference)),
+ new SeatOccupancy(JsonRPCMarshaller.deserializeJSONObject(myKeyObjTest))));
} else if (key.equals(GetVehicleDataResponse.KEY_STABILITY_CONTROLS_STATUS)) {
JSONObject myKeyObjReference = JsonUtils.readJsonObjectFromJsonObject(reference, key);
JSONObject myKeyObjTest = JsonUtils.readJsonObjectFromJsonObject(underTest, key);
@@ -437,6 +461,24 @@ public class GetVehicleDataResponseTests extends BaseRpcTests {
status2
)
);
+ } else if (key.equals(GetVehicleDataResponse.KEY_CLIMATE_DATA)) {
+
+
+ JSONObject myKeyObjReference = JsonUtils.readJsonObjectFromJsonObject(reference, key);
+ JSONObject myKeyObjTest = JsonUtils.readJsonObjectFromJsonObject(underTest, key);
+
+ Hashtable<String, Object> hashReference = JsonRPCMarshaller.deserializeJSONObject(myKeyObjReference);
+ Hashtable<String, Object> hashTest = JsonRPCMarshaller.deserializeJSONObject(myKeyObjTest);
+
+ ClimateData climateData1 = new ClimateData(hashReference);
+ ClimateData climateData2 = new ClimateData(hashTest);
+
+ assertTrue("JSON value didn't match expected value for key \"" + key + "\".",
+ Validator.validateClimateData(
+ climateData1,
+ climateData2
+ )
+ );
} else if (key.equals(GetVehicleDataResponse.KEY_FUEL_RANGE)) {
JSONArray fuelRangeArrayObjReference = JsonUtils.readJsonArrayFromJsonObject(reference, key);
List<FuelRange> fuelRangeRefereceList = new ArrayList<FuelRange>();
@@ -525,6 +567,8 @@ public class GetVehicleDataResponseTests extends BaseRpcTests {
assertEquals(TestValues.MATCH, VehicleDataHelper.GEAR_STATUS, ((GetVehicleDataResponse) msg).getGearStatus());
assertEquals(TestValues.MATCH, VehicleDataHelper.HANDS_OFF_STEERING, ((GetVehicleDataResponse) msg).getHandsOffSteering());
assertEquals(TestValues.MATCH, VehicleDataHelper.STABILITY_CONTROLS_STATUS, ((GetVehicleDataResponse) msg).getStabilityControlsStatus());
+ assertEquals(TestValues.MATCH, VehicleDataHelper.CLIMATE_DATA, ((GetVehicleDataResponse) msg).getClimateData());
+ assertEquals(TestValues.MATCH, VehicleDataHelper.SEAT_OCCUPANCY, ((GetVehicleDataResponse) msg).getSeatOccupancy());
assertEquals(TestValues.MATCH, VehicleDataHelper.OEM_CUSTOM_VEHICLE_DATA_STATE, ((GetVehicleDataResponse) msg).getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
// Invalid/Null Tests
@@ -563,6 +607,8 @@ public class GetVehicleDataResponseTests extends BaseRpcTests {
assertNull(TestValues.NULL, msg.getHandsOffSteering());
assertNull(TestValues.NULL, msg.getStabilityControlsStatus());
assertNull(TestValues.NULL, msg.getWindowStatus());
+ assertNull(TestValues.NULL, msg.getClimateData());
+ assertNull(TestValues.NULL, msg.getSeatOccupancy());
assertNull(TestValues.NULL, msg.getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
}
@@ -647,11 +693,19 @@ public class GetVehicleDataResponseTests extends BaseRpcTests {
ClusterModeStatus clusterModeStatus = new ClusterModeStatus(JsonRPCMarshaller.deserializeJSONObject(clusterModeStatusObj));
assertTrue(TestValues.TRUE, Validator.validateClusterModeStatus(clusterModeStatus, cmd.getClusterModeStatus()));
+ JSONObject climateDataObj = JsonUtils.readJsonObjectFromJsonObject(parameters, GetVehicleDataResponse.KEY_CLIMATE_DATA);
+ ClimateData climateData = new ClimateData(JsonRPCMarshaller.deserializeJSONObject(climateDataObj));
+ assertTrue(TestValues.TRUE, Validator.validateClimateData(climateData, cmd.getClimateData()));
+
JSONObject gearStatusObj = JsonUtils.readJsonObjectFromJsonObject(parameters, GetVehicleDataResponse.KEY_GEAR_STATUS);
GearStatus gearStatus = new GearStatus(JsonRPCMarshaller.deserializeJSONObject(gearStatusObj));
GearStatus cmdStatus = cmd.getGearStatus();
assertTrue(TestValues.TRUE, Validator.validateGearStatuses(gearStatus, cmdStatus));
+ JSONObject seatOccupancyObj = JsonUtils.readJsonObjectFromJsonObject(parameters, GetVehicleDataResponse.KEY_SEAT_OCCUPANCY);
+ SeatOccupancy seatOccupancy = new SeatOccupancy(JsonRPCMarshaller.deserializeJSONObject(seatOccupancyObj));
+ assertTrue(TestValues.TRUE, Validator.validateSeatOccupancies(seatOccupancy, cmd.getSeatOccupancy()));
+
JSONObject myKeyObj = JsonUtils.readJsonObjectFromJsonObject(parameters, GetVehicleDataResponse.KEY_MY_KEY);
MyKey myKey = new MyKey(JsonRPCMarshaller.deserializeJSONObject(myKeyObj));
assertTrue(TestValues.TRUE, Validator.validateMyKey(myKey, cmd.getMyKey()));
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/SubscribeVehicleDataResponseTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/SubscribeVehicleDataResponseTest.java
index 6f9c9a45c..278a29b35 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/SubscribeVehicleDataResponseTest.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/SubscribeVehicleDataResponseTest.java
@@ -70,6 +70,8 @@ public class SubscribeVehicleDataResponseTest extends BaseRpcTests {
msg.setWindowStatus(TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_WINDOWSTATUS.ordinal()));
msg.setStabilityControlsStatus(TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_STABILITYCONTROLSSTATUS.ordinal()));
msg.setOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA);
+ msg.setSeatOccupancy(TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_SEATOCCUPANCY.ordinal()));
+ msg.setClimateData(TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_CLIMATEDATA.ordinal()));
return msg;
}
@@ -128,6 +130,9 @@ public class SubscribeVehicleDataResponseTest extends BaseRpcTests {
result.put(SubscribeVehicleDataResponse.KEY_ELECTRONIC_PARK_BRAKE_STATUS, TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_ELECTRONICPARKBRAKESTATUS.ordinal()).serializeJSON());
result.put(SubscribeVehicleDataResponse.KEY_HANDS_OFF_STEERING, TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_HANDSOFFSTEERING.ordinal()).serializeJSON());
result.put(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA.serializeJSON());
+ result.put(SubscribeVehicleDataResponse.KEY_CLIMATE_DATA, TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_CLIMATEDATA.ordinal()).serializeJSON());
+ result.put(SubscribeVehicleDataResponse.KEY_SEAT_OCCUPANCY, TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_SEATOCCUPANCY.ordinal()).serializeJSON());
+
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
}
@@ -174,7 +179,9 @@ public class SubscribeVehicleDataResponseTest extends BaseRpcTests {
VehicleDataResult testWindowStatus = ((SubscribeVehicleDataResponse) msg).getWindowStatus();
VehicleDataResult testGearStatus = ((SubscribeVehicleDataResponse) msg).getGearStatus();
VehicleDataResult testStabilityControlStatus = ((SubscribeVehicleDataResponse) msg).getStabilityControlsStatus();
+ VehicleDataResult testClimateData = ((SubscribeVehicleDataResponse) msg).getClimateData();
VehicleDataResult testOEMCustomVehicleData = ((SubscribeVehicleDataResponse) msg).getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME);
+ VehicleDataResult testSeatOccupancy = ((SubscribeVehicleDataResponse) msg).getSeatOccupancy();
// Valid Tests
assertTrue(TestValues.TRUE, testResult.equals(TestValues.GENERAL_RESULT));
@@ -211,6 +218,8 @@ public class SubscribeVehicleDataResponseTest extends BaseRpcTests {
assertTrue(TestValues.TRUE, testWindowStatus.getDataType().equals(VehicleDataType.VEHICLEDATA_WINDOWSTATUS));
assertTrue(TestValues.TRUE, testStabilityControlStatus.getDataType().equals(VehicleDataType.VEHICLEDATA_STABILITYCONTROLSSTATUS));
assertTrue(TestValues.TRUE, testOEMCustomVehicleData.getOEMCustomVehicleDataType().equals(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
+ assertTrue(TestValues.TRUE, testClimateData.getDataType().equals(VehicleDataType.VEHICLEDATA_CLIMATEDATA));
+ assertTrue(TestValues.TRUE, testSeatOccupancy.getDataType().equals(VehicleDataType.VEHICLEDATA_SEATOCCUPANCY));
// Invalid/Null Tests
SubscribeVehicleDataResponse msg = new SubscribeVehicleDataResponse();
@@ -250,6 +259,8 @@ public class SubscribeVehicleDataResponseTest extends BaseRpcTests {
assertNull(TestValues.NULL, msg.getWindowStatus());
assertNull(TestValues.NULL, msg.getGearStatus());
assertNull(TestValues.NULL, msg.getStabilityControlsStatus());
+ assertNull(TestValues.NULL, msg.getClimateData());
+ assertNull(TestValues.NULL, msg.getSeatOccupancy());
assertNull(TestValues.NULL, msg.getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
}
@@ -402,9 +413,17 @@ public class SubscribeVehicleDataResponseTest extends BaseRpcTests {
VehicleDataResult referenceStabilityControlStatus = new VehicleDataResult(JsonRPCMarshaller.deserializeJSONObject(stabilityControlStatus));
assertTrue(TestValues.TRUE, Validator.validateStabilityControlStatus(referenceStabilityControlStatus, cmd.getStabilityControlsStatus()));
+ JSONObject climateData = JsonUtils.readJsonObjectFromJsonObject(parameters, SubscribeVehicleDataResponse.KEY_CLIMATE_DATA);
+ VehicleDataResult referenceClimateData = new VehicleDataResult(JsonRPCMarshaller.deserializeJSONObject(climateData));
+ assertTrue(TestValues.TRUE, Validator.validateVehicleDataResult(referenceClimateData, cmd.getClimateData()));
+
JSONObject oemCustomVehicleDataName = JsonUtils.readJsonObjectFromJsonObject(parameters, TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME);
VehicleDataResult referenceOemCustomData = new VehicleDataResult(JsonRPCMarshaller.deserializeJSONObject(oemCustomVehicleDataName));
assertTrue(TestValues.TRUE, Validator.validateVehicleDataResult(referenceOemCustomData, cmd.getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME)));
+
+ JSONObject seatOccupancy = JsonUtils.readJsonObjectFromJsonObject(parameters, SubscribeVehicleDataResponse.KEY_SEAT_OCCUPANCY);
+ VehicleDataResult referenceSeatOccupancy = new VehicleDataResult(JsonRPCMarshaller.deserializeJSONObject(seatOccupancy));
+ assertTrue(TestValues.TRUE, Validator.validateVehicleDataResult(referenceSeatOccupancy, cmd.getSeatOccupancy()));
} catch (JSONException e) {
e.printStackTrace();
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/UnsubscribeVehicleDataResponseTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/UnsubscribeVehicleDataResponseTest.java
index 1d5504b08..6169de07f 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/UnsubscribeVehicleDataResponseTest.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/responses/UnsubscribeVehicleDataResponseTest.java
@@ -70,8 +70,10 @@ public class UnsubscribeVehicleDataResponseTest extends BaseRpcTests {
msg.setWindowStatus(TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_WINDOWSTATUS.ordinal()));
msg.setHandsOffSteering(TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_HANDSOFFSTEERING.ordinal()));
msg.setGearStatus(TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_GEARSTATUS.ordinal()));
+ msg.setSeatOccupancy(TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_SEATOCCUPANCY.ordinal()));
msg.setStabilityControlsStatus(TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_STABILITYCONTROLSSTATUS.ordinal()));
msg.setOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA);
+ msg.setClimateData(TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_CLIMATEDATA.ordinal()));
return msg;
}
@@ -129,8 +131,11 @@ public class UnsubscribeVehicleDataResponseTest extends BaseRpcTests {
result.put(SubscribeVehicleDataResponse.KEY_STABILITY_CONTROLS_STATUS, TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_STABILITYCONTROLSSTATUS.ordinal()).serializeJSON());
result.put(SubscribeVehicleDataResponse.KEY_ELECTRONIC_PARK_BRAKE_STATUS, TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_ELECTRONICPARKBRAKESTATUS.ordinal()).serializeJSON());
result.put(SubscribeVehicleDataResponse.KEY_HANDS_OFF_STEERING, TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_HANDSOFFSTEERING.ordinal()).serializeJSON());
+ result.put(SubscribeVehicleDataResponse.KEY_SEAT_OCCUPANCY, TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_SEATOCCUPANCY.ordinal()).serializeJSON());
result.put(SubscribeVehicleDataResponse.KEY_WINDOW_STATUS, TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_WINDOWSTATUS.ordinal()).serializeJSON());
result.put(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME, TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA.serializeJSON());
+ result.put(SubscribeVehicleDataResponse.KEY_CLIMATE_DATA, TestValues.GENERAL_VEHICLEDATARESULT_LIST.get(VehicleDataType.VEHICLEDATA_CLIMATEDATA.ordinal()).serializeJSON());
+
} catch (JSONException e) {
fail(TestValues.JSON_FAIL);
}
@@ -177,7 +182,9 @@ public class UnsubscribeVehicleDataResponseTest extends BaseRpcTests {
VehicleDataResult testGearStatus = ((UnsubscribeVehicleDataResponse) msg).getGearStatus();
VehicleDataResult testWindowStatus = ((UnsubscribeVehicleDataResponse) msg).getWindowStatus();
VehicleDataResult testStabilityControlStatus = ((UnsubscribeVehicleDataResponse) msg).getStabilityControlsStatus();
+ VehicleDataResult testClimateData = ((UnsubscribeVehicleDataResponse) msg).getClimateData();
VehicleDataResult testOemCustomData = ((UnsubscribeVehicleDataResponse) msg).getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME);
+ VehicleDataResult testSeatOccupancy = ((UnsubscribeVehicleDataResponse) msg).getSeatOccupancy();
// Valid Tests
assertTrue(TestValues.TRUE, testResult.equals(TestValues.GENERAL_RESULT));
@@ -213,7 +220,9 @@ public class UnsubscribeVehicleDataResponseTest extends BaseRpcTests {
assertTrue(TestValues.TRUE, testWindowStatus.getDataType().equals(VehicleDataType.VEHICLEDATA_WINDOWSTATUS));
assertTrue(TestValues.TRUE, testStabilityControlStatus.getDataType().equals(VehicleDataType.VEHICLEDATA_STABILITYCONTROLSSTATUS));
assertTrue(TestValues.TRUE, testOemCustomData.getOEMCustomVehicleDataType().equals(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
+ assertTrue(TestValues.TRUE, testClimateData.getDataType().equals(VehicleDataType.VEHICLEDATA_CLIMATEDATA));
assertTrue(TestValues.TRUE, testGearStatus.getDataType().equals(VehicleDataType.VEHICLEDATA_GEARSTATUS));
+ assertTrue(TestValues.TRUE, testSeatOccupancy.getDataType().equals(VehicleDataType.VEHICLEDATA_SEATOCCUPANCY));
// Invalid/Null Tests
UnsubscribeVehicleDataResponse msg = new UnsubscribeVehicleDataResponse();
@@ -254,6 +263,8 @@ public class UnsubscribeVehicleDataResponseTest extends BaseRpcTests {
assertNull(TestValues.NULL, msg.getWindowStatus());
assertNull(TestValues.NULL, msg.getHandsOffSteering());
assertNull(TestValues.NULL, msg.getStabilityControlsStatus());
+ assertNull(TestValues.NULL, msg.getClimateData());
+ assertNull(TestValues.NULL, msg.getSeatOccupancy());
assertNull(TestValues.NULL, msg.getOEMCustomVehicleData(TestValues.GENERAL_OEM_CUSTOM_VEHICLE_DATA_NAME));
}
@@ -409,6 +420,14 @@ public class UnsubscribeVehicleDataResponseTest extends BaseRpcTests {
JSONObject stabilityControlsStatus = JsonUtils.readJsonObjectFromJsonObject(parameters, UnsubscribeVehicleDataResponse.KEY_STABILITY_CONTROLS_STATUS);
VehicleDataResult referenceStabilityControlStatus = new VehicleDataResult(JsonRPCMarshaller.deserializeJSONObject(stabilityControlsStatus));
assertTrue(TestValues.TRUE, Validator.validateStabilityControlStatus(referenceStabilityControlStatus, cmd.getStabilityControlsStatus()));
+
+ JSONObject climateData = JsonUtils.readJsonObjectFromJsonObject(parameters, UnsubscribeVehicleDataResponse.KEY_CLIMATE_DATA);
+ VehicleDataResult referenceClimateData = new VehicleDataResult(JsonRPCMarshaller.deserializeJSONObject(climateData));
+ assertTrue(TestValues.TRUE, Validator.validateVehicleDataResult(referenceClimateData, cmd.getClimateData()));
+
+ JSONObject seatOccupancy = JsonUtils.readJsonObjectFromJsonObject(parameters, UnsubscribeVehicleDataResponse.KEY_SEAT_OCCUPANCY);
+ VehicleDataResult referenceSeatOccupancy = new VehicleDataResult(JsonRPCMarshaller.deserializeJSONObject(seatOccupancy));
+ assertTrue(TestValues.TRUE, Validator.validateVehicleDataResult(referenceSeatOccupancy, cmd.getSeatOccupancy()));
} catch (JSONException e) {
e.printStackTrace();
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockInterfaceBroker.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockInterfaceBroker.java
index 364e6c976..57d2fa1b2 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockInterfaceBroker.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/MockInterfaceBroker.java
@@ -3,6 +3,7 @@ package com.smartdevicelink.test.streaming;
import com.smartdevicelink.proxy.RPCMessage;
import com.smartdevicelink.session.ISdlSessionListener;
import com.smartdevicelink.transport.BaseTransportConfig;
+import com.smartdevicelink.util.SystemInfo;
import com.smartdevicelink.util.Version;
/**
@@ -24,7 +25,7 @@ public class MockInterfaceBroker implements ISdlSessionListener {
}
@Override
- public void onSessionStarted(int sessionID, Version version) {
+ public void onSessionStarted(int sessionID, Version version, SystemInfo systemInfo) {
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java
index 134de0519..7f4d23c96 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java
@@ -106,6 +106,11 @@ public class SdlRemoteDisplayTest extends TestCase {
}
+ @Override
+ public void onViewResized(int width, int height) {
+
+ }
+
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/VideoStreamingParametersTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/VideoStreamingParametersTest.java
index ca7924e8f..1e7db99e4 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/VideoStreamingParametersTest.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/VideoStreamingParametersTest.java
@@ -8,6 +8,7 @@ import com.smartdevicelink.proxy.rpc.VideoStreamingFormat;
import com.smartdevicelink.proxy.rpc.enums.VideoStreamingCodec;
import com.smartdevicelink.proxy.rpc.enums.VideoStreamingProtocol;
import com.smartdevicelink.streaming.video.VideoStreamingParameters;
+import com.smartdevicelink.test.TestValues;
import org.junit.Before;
import org.junit.Test;
@@ -196,4 +197,13 @@ public class VideoStreamingParametersTest {
assertFalse(params.getFormat().equals(format));
}
+
+ @Test
+ public void testAdditionalCapabilities() {
+ capability.setAdditionalVideoStreamingCapabilities(TestValues.GENERAL_ADDITIONAL_CAPABILITY_LIST);
+ assertEquals(TestValues.GENERAL_ADDITIONAL_CAPABILITY_LIST, capability.getAdditionalVideoStreamingCapabilities());
+
+ capability.setAdditionalVideoStreamingCapabilities(null);
+ assertNull(capability.getAdditionalVideoStreamingCapabilities());
+ }
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/util/VersionTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/util/VersionTest.java
index 88ff2f8d2..4eda42042 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/util/VersionTest.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/util/VersionTest.java
@@ -8,6 +8,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.fail;
@RunWith(AndroidJUnit4.class)
public class VersionTest {
@@ -27,10 +28,8 @@ public class VersionTest {
public void testConstructorIncorrect() {
try {
Version version = new Version("1.2");
- } catch (Exception e) {
- assert true;
- }
- assert false;
+ fail();
+ } catch (Exception e) {}
}
@Test
diff --git a/android/sdl_android/src/main/java/com/android/grafika/gles/Drawable2d.java b/android/sdl_android/src/main/java/com/android/grafika/gles/Drawable2d.java
new file mode 100644
index 000000000..52fcabf1f
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/android/grafika/gles/Drawable2d.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.grafika.gles;
+
+import java.nio.FloatBuffer;
+
+/**
+ * Base class for stuff we like to draw.
+ */
+public class Drawable2d {
+ private static final int SIZEOF_FLOAT = 4;
+
+ /**
+ * Simple equilateral triangle (1.0 per side). Centered on (0,0).
+ */
+ private static final float TRIANGLE_COORDS[] = {
+ 0.0f, 0.577350269f, // 0 top
+ -0.5f, -0.288675135f, // 1 bottom left
+ 0.5f, -0.288675135f // 2 bottom right
+ };
+ private static final float TRIANGLE_TEX_COORDS[] = {
+ 0.5f, 0.0f, // 0 top center
+ 0.0f, 1.0f, // 1 bottom left
+ 1.0f, 1.0f, // 2 bottom right
+ };
+ private static final FloatBuffer TRIANGLE_BUF =
+ GlUtil.createFloatBuffer(TRIANGLE_COORDS);
+ private static final FloatBuffer TRIANGLE_TEX_BUF =
+ GlUtil.createFloatBuffer(TRIANGLE_TEX_COORDS);
+
+ /**
+ * Simple square, specified as a triangle strip. The square is centered on (0,0) and has
+ * a size of 1x1.
+ * <p>
+ * Triangles are 0-1-2 and 2-1-3 (counter-clockwise winding).
+ */
+ private static final float RECTANGLE_COORDS[] = {
+ -0.5f, -0.5f, // 0 bottom left
+ 0.5f, -0.5f, // 1 bottom right
+ -0.5f, 0.5f, // 2 top left
+ 0.5f, 0.5f, // 3 top right
+ };
+ private static final float RECTANGLE_TEX_COORDS[] = {
+ 0.0f, 1.0f, // 0 bottom left
+ 1.0f, 1.0f, // 1 bottom right
+ 0.0f, 0.0f, // 2 top left
+ 1.0f, 0.0f // 3 top right
+ };
+ private static final FloatBuffer RECTANGLE_BUF =
+ GlUtil.createFloatBuffer(RECTANGLE_COORDS);
+ private static final FloatBuffer RECTANGLE_TEX_BUF =
+ GlUtil.createFloatBuffer(RECTANGLE_TEX_COORDS);
+
+ /**
+ * A "full" square, extending from -1 to +1 in both dimensions. When the model/view/projection
+ * matrix is identity, this will exactly cover the viewport.
+ * <p>
+ * The texture coordinates are Y-inverted relative to RECTANGLE. (This seems to work out
+ * right with external textures from SurfaceTexture.)
+ */
+ private static final float FULL_RECTANGLE_COORDS[] = {
+ -1.0f, -1.0f, // 0 bottom left
+ 1.0f, -1.0f, // 1 bottom right
+ -1.0f, 1.0f, // 2 top left
+ 1.0f, 1.0f, // 3 top right
+ };
+ private static final float FULL_RECTANGLE_TEX_COORDS[] = {
+ 0.0f, 0.0f, // 0 bottom left
+ 1.0f, 0.0f, // 1 bottom right
+ 0.0f, 1.0f, // 2 top left
+ 1.0f, 1.0f // 3 top right
+ };
+ private static final FloatBuffer FULL_RECTANGLE_BUF =
+ GlUtil.createFloatBuffer(FULL_RECTANGLE_COORDS);
+ private static final FloatBuffer FULL_RECTANGLE_TEX_BUF =
+ GlUtil.createFloatBuffer(FULL_RECTANGLE_TEX_COORDS);
+
+
+ private FloatBuffer mVertexArray;
+ private FloatBuffer mTexCoordArray;
+ private int mVertexCount;
+ private int mCoordsPerVertex;
+ private int mVertexStride;
+ private int mTexCoordStride;
+ private Prefab mPrefab;
+
+ /**
+ * Enum values for constructor.
+ */
+ public enum Prefab {
+ TRIANGLE, RECTANGLE, FULL_RECTANGLE
+ }
+
+ /**
+ * Prepares a drawable from a "pre-fabricated" shape definition.
+ * <p>
+ * Does no EGL/GL operations, so this can be done at any time.
+ */
+ public Drawable2d(Prefab shape) {
+ switch (shape) {
+ case TRIANGLE:
+ mVertexArray = TRIANGLE_BUF;
+ mTexCoordArray = TRIANGLE_TEX_BUF;
+ mCoordsPerVertex = 2;
+ mVertexStride = mCoordsPerVertex * SIZEOF_FLOAT;
+ mVertexCount = TRIANGLE_COORDS.length / mCoordsPerVertex;
+ break;
+ case RECTANGLE:
+ mVertexArray = RECTANGLE_BUF;
+ mTexCoordArray = RECTANGLE_TEX_BUF;
+ mCoordsPerVertex = 2;
+ mVertexStride = mCoordsPerVertex * SIZEOF_FLOAT;
+ mVertexCount = RECTANGLE_COORDS.length / mCoordsPerVertex;
+ break;
+ case FULL_RECTANGLE:
+ mVertexArray = FULL_RECTANGLE_BUF;
+ mTexCoordArray = FULL_RECTANGLE_TEX_BUF;
+ mCoordsPerVertex = 2;
+ mVertexStride = mCoordsPerVertex * SIZEOF_FLOAT;
+ mVertexCount = FULL_RECTANGLE_COORDS.length / mCoordsPerVertex;
+ break;
+ default:
+ throw new RuntimeException("Unknown shape " + shape);
+ }
+ mTexCoordStride = 2 * SIZEOF_FLOAT;
+ mPrefab = shape;
+ }
+
+ /**
+ * Returns the array of vertices.
+ * <p>
+ * To avoid allocations, this returns internal state. The caller must not modify it.
+ */
+ public FloatBuffer getVertexArray() {
+ return mVertexArray;
+ }
+
+ /**
+ * Returns the array of texture coordinates.
+ * <p>
+ * To avoid allocations, this returns internal state. The caller must not modify it.
+ */
+ public FloatBuffer getTexCoordArray() {
+ return mTexCoordArray;
+ }
+
+ /**
+ * Returns the number of vertices stored in the vertex array.
+ */
+ public int getVertexCount() {
+ return mVertexCount;
+ }
+
+ /**
+ * Returns the width, in bytes, of the data for each vertex.
+ */
+ public int getVertexStride() {
+ return mVertexStride;
+ }
+
+ /**
+ * Returns the width, in bytes, of the data for each texture coordinate.
+ */
+ public int getTexCoordStride() {
+ return mTexCoordStride;
+ }
+
+ /**
+ * Returns the number of position coordinates per vertex. This will be 2 or 3.
+ */
+ public int getCoordsPerVertex() {
+ return mCoordsPerVertex;
+ }
+
+ @Override
+ public String toString() {
+ if (mPrefab != null) {
+ return "[Drawable2d: " + mPrefab + "]";
+ } else {
+ return "[Drawable2d: ...]";
+ }
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/android/grafika/gles/EglCore.java b/android/sdl_android/src/main/java/com/android/grafika/gles/EglCore.java
new file mode 100644
index 000000000..9e814a529
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/android/grafika/gles/EglCore.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright 2013 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.grafika.gles;
+
+import android.graphics.SurfaceTexture;
+import android.opengl.EGL14;
+import android.opengl.EGLConfig;
+import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLExt;
+import android.opengl.EGLSurface;
+import android.util.Log;
+import android.view.Surface;
+
+
+/**
+ * Core EGL state (display, context, config).
+ * <p>
+ * The EGLContext must only be attached to one thread at a time. This class is not thread-safe.
+ */
+public final class EglCore {
+ private static final String TAG = "EglCore";
+
+ /**
+ * Constructor flag: surface must be recordable. This discourages EGL from using a
+ * pixel format that cannot be converted efficiently to something usable by the video
+ * encoder.
+ */
+ public static final int FLAG_RECORDABLE = 0x01;
+
+ /**
+ * Constructor flag: ask for GLES3, fall back to GLES2 if not available. Without this
+ * flag, GLES2 is used.
+ */
+ public static final int FLAG_TRY_GLES3 = 0x02;
+
+ // Android-specific extension.
+ private static final int EGL_RECORDABLE_ANDROID = 0x3142;
+
+ private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
+ private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
+ private EGLConfig mEGLConfig = null;
+ private int mGlVersion = -1;
+
+
+ /**
+ * Prepares EGL display and context.
+ * <p>
+ * Equivalent to EglCore(null, 0).
+ */
+ public EglCore() {
+ this(null, 0);
+ }
+
+ /**
+ * Prepares EGL display and context.
+ * <p>
+ * @param sharedContext The context to share, or null if sharing is not desired.
+ * @param flags Configuration bit flags, e.g. FLAG_RECORDABLE.
+ */
+ public EglCore(EGLContext sharedContext, int flags) {
+ if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
+ throw new RuntimeException("EGL already set up");
+ }
+
+ if (sharedContext == null) {
+ sharedContext = EGL14.EGL_NO_CONTEXT;
+ }
+
+ mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+ if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
+ throw new RuntimeException("unable to get EGL14 display");
+ }
+ int[] version = new int[2];
+ if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
+ mEGLDisplay = null;
+ throw new RuntimeException("unable to initialize EGL14");
+ }
+
+ // Try to get a GLES3 context, if requested.
+ if ((flags & FLAG_TRY_GLES3) != 0) {
+ //Log.d(TAG, "Trying GLES 3");
+ EGLConfig config = getConfig(flags, 3);
+ if (config != null) {
+ int[] attrib3_list = {
+ EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
+ EGL14.EGL_NONE
+ };
+ EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext,
+ attrib3_list, 0);
+
+ if (EGL14.eglGetError() == EGL14.EGL_SUCCESS) {
+ //Log.d(TAG, "Got GLES 3 config");
+ mEGLConfig = config;
+ mEGLContext = context;
+ mGlVersion = 3;
+ }
+ }
+ }
+ if (mEGLContext == EGL14.EGL_NO_CONTEXT) { // GLES 2 only, or GLES 3 attempt failed
+ //Log.d(TAG, "Trying GLES 2");
+ EGLConfig config = getConfig(flags, 2);
+ if (config == null) {
+ throw new RuntimeException("Unable to find a suitable EGLConfig");
+ }
+ int[] attrib2_list = {
+ EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
+ EGL14.EGL_NONE
+ };
+ EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext,
+ attrib2_list, 0);
+ checkEglError("eglCreateContext");
+ mEGLConfig = config;
+ mEGLContext = context;
+ mGlVersion = 2;
+ }
+
+ // Confirm with query.
+ int[] values = new int[1];
+ EGL14.eglQueryContext(mEGLDisplay, mEGLContext, EGL14.EGL_CONTEXT_CLIENT_VERSION,
+ values, 0);
+ Log.d(TAG,"EGLContext created, client version " + values[0]);
+ }
+
+ /**
+ * Finds a suitable EGLConfig.
+ *
+ * @param flags Bit flags from constructor.
+ * @param version Must be 2 or 3.
+ */
+ private EGLConfig getConfig(int flags, int version) {
+ int renderableType = EGL14.EGL_OPENGL_ES2_BIT;
+ if (version >= 3) {
+ renderableType |= EGLExt.EGL_OPENGL_ES3_BIT_KHR;
+ }
+
+ // The actual surface is generally RGBA or RGBX, so situationally omitting alpha
+ // doesn't really help. It can also lead to a huge performance hit on glReadPixels()
+ // when reading into a GL_RGBA buffer.
+ int[] attribList = {
+ EGL14.EGL_RED_SIZE, 8,
+ EGL14.EGL_GREEN_SIZE, 8,
+ EGL14.EGL_BLUE_SIZE, 8,
+ EGL14.EGL_ALPHA_SIZE, 8,
+ //EGL14.EGL_DEPTH_SIZE, 16,
+ //EGL14.EGL_STENCIL_SIZE, 8,
+ EGL14.EGL_RENDERABLE_TYPE, renderableType,
+ EGL14.EGL_NONE, 0, // placeholder for recordable [@-3]
+ EGL14.EGL_NONE
+ };
+ if ((flags & FLAG_RECORDABLE) != 0) {
+ attribList[attribList.length - 3] = EGL_RECORDABLE_ANDROID;
+ attribList[attribList.length - 2] = 1;
+ }
+ EGLConfig[] configs = new EGLConfig[1];
+ int[] numConfigs = new int[1];
+ if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
+ numConfigs, 0)) {
+ Log.d(TAG,"unable to find RGB8888 / " + version + " EGLConfig");
+ return null;
+ }
+ return configs[0];
+ }
+
+ /**
+ * Discards all resources held by this class, notably the EGL context. This must be
+ * called from the thread where the context was created.
+ * <p>
+ * On completion, no context will be current.
+ */
+ public void release() {
+ if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
+ // Android is unusual in that it uses a reference-counted EGLDisplay. So for
+ // every eglInitialize() we need an eglTerminate().
+ EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
+ EGL14.EGL_NO_CONTEXT);
+ EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
+ EGL14.eglReleaseThread();
+ EGL14.eglTerminate(mEGLDisplay);
+ }
+
+ mEGLDisplay = EGL14.EGL_NO_DISPLAY;
+ mEGLContext = EGL14.EGL_NO_CONTEXT;
+ mEGLConfig = null;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
+ // We're limited here -- finalizers don't run on the thread that holds
+ // the EGL state, so if a surface or context is still current on another
+ // thread we can't fully release it here. Exceptions thrown from here
+ // are quietly discarded. Complain in the log file.
+ Log.e(TAG,"WARNING: EglCore was not explicitly released -- state may be leaked");
+ release();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Destroys the specified surface. Note the EGLSurface won't actually be destroyed if it's
+ * still current in a context.
+ */
+ public void releaseSurface(EGLSurface eglSurface) {
+ EGL14.eglDestroySurface(mEGLDisplay, eglSurface);
+ }
+
+ /**
+ * Creates an EGL surface associated with a Surface.
+ * <p>
+ * If this is destined for MediaCodec, the EGLConfig should have the "recordable" attribute.
+ */
+ public EGLSurface createWindowSurface(Object surface) {
+ if (!(surface instanceof Surface) && !(surface instanceof SurfaceTexture)) {
+ throw new RuntimeException("invalid surface: " + surface);
+ }
+
+ // Create a window surface, and attach it to the Surface we received.
+ int[] surfaceAttribs = {
+ EGL14.EGL_NONE
+ };
+ EGLSurface eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surface,
+ surfaceAttribs, 0);
+ checkEglError("eglCreateWindowSurface");
+ if (eglSurface == null) {
+ throw new RuntimeException("surface was null");
+ }
+ return eglSurface;
+ }
+
+ /**
+ * Creates an EGL surface associated with an offscreen buffer.
+ */
+ public EGLSurface createOffscreenSurface(int width, int height) {
+ int[] surfaceAttribs = {
+ EGL14.EGL_WIDTH, width,
+ EGL14.EGL_HEIGHT, height,
+ EGL14.EGL_NONE
+ };
+ EGLSurface eglSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig,
+ surfaceAttribs, 0);
+ checkEglError("eglCreatePbufferSurface");
+ if (eglSurface == null) {
+ throw new RuntimeException("surface was null");
+ }
+ return eglSurface;
+ }
+
+ /**
+ * Makes our EGL context current, using the supplied surface for both "draw" and "read".
+ */
+ public void makeCurrent(EGLSurface eglSurface) {
+ if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
+ // called makeCurrent() before create?
+ Log.d(TAG,"NOTE: makeCurrent w/o display");
+ }
+ if (!EGL14.eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext)) {
+ throw new RuntimeException("eglMakeCurrent failed");
+ }
+ }
+
+ /**
+ * Makes our EGL context current, using the supplied "draw" and "read" surfaces.
+ */
+ public void makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) {
+ if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
+ // called makeCurrent() before create?
+ Log.d(TAG,"NOTE: makeCurrent w/o display");
+ }
+ if (!EGL14.eglMakeCurrent(mEGLDisplay, drawSurface, readSurface, mEGLContext)) {
+ throw new RuntimeException("eglMakeCurrent(draw,read) failed");
+ }
+ }
+
+ /**
+ * Makes no context current.
+ */
+ public void makeNothingCurrent() {
+ if (!EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
+ EGL14.EGL_NO_CONTEXT)) {
+ throw new RuntimeException("eglMakeCurrent failed");
+ }
+ }
+
+ /**
+ * Calls eglSwapBuffers. Use this to "publish" the current frame.
+ *
+ * @return false on failure
+ */
+ public boolean swapBuffers(EGLSurface eglSurface) {
+ return EGL14.eglSwapBuffers(mEGLDisplay, eglSurface);
+ }
+
+ /**
+ * Sends the presentation time stamp to EGL. Time is expressed in nanoseconds.
+ */
+ public void setPresentationTime(EGLSurface eglSurface, long nsecs) {
+ EGLExt.eglPresentationTimeANDROID(mEGLDisplay, eglSurface, nsecs);
+ }
+
+ /**
+ * Returns true if our context and the specified surface are current.
+ */
+ public boolean isCurrent(EGLSurface eglSurface) {
+ return mEGLContext.equals(EGL14.eglGetCurrentContext()) &&
+ eglSurface.equals(EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW));
+ }
+
+ /**
+ * Performs a simple surface query.
+ */
+ public int querySurface(EGLSurface eglSurface, int what) {
+ int[] value = new int[1];
+ EGL14.eglQuerySurface(mEGLDisplay, eglSurface, what, value, 0);
+ return value[0];
+ }
+
+ /**
+ * Queries a string value.
+ */
+ public String queryString(int what) {
+ return EGL14.eglQueryString(mEGLDisplay, what);
+ }
+
+ /**
+ * Returns the GLES version this context is configured for (currently 2 or 3).
+ */
+ public int getGlVersion() {
+ return mGlVersion;
+ }
+
+ /**
+ * Writes the current display, context, and surface to the log.
+ */
+ public static void logCurrent(String msg) {
+ EGLDisplay display;
+ EGLContext context;
+ EGLSurface surface;
+
+ display = EGL14.eglGetCurrentDisplay();
+ context = EGL14.eglGetCurrentContext();
+ surface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW);
+ Log.i(TAG,"Current EGL (" + msg + "): display=" + display + ", context=" + context + ", surface=" + surface);
+ }
+
+ /**
+ * Checks for EGL errors. Throws an exception if an error has been raised.
+ */
+ private void checkEglError(String msg) {
+ int error;
+ if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
+ throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error));
+ }
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/android/grafika/gles/EglSurfaceBase.java b/android/sdl_android/src/main/java/com/android/grafika/gles/EglSurfaceBase.java
new file mode 100644
index 000000000..3223a4073
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/android/grafika/gles/EglSurfaceBase.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright 2013 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.grafika.gles;
+
+import android.graphics.Bitmap;
+import android.opengl.EGL14;
+import android.opengl.EGLSurface;
+import android.opengl.GLES20;
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Common base class for EGL surfaces.
+ * <p>
+ * There can be multiple surfaces associated with a single context.
+ */
+public class EglSurfaceBase {
+ private static final String TAG = "EglSurfaceBase";
+
+ // EglCore object we're associated with. It may be associated with multiple surfaces.
+ protected EglCore mEglCore;
+
+ private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
+ private int mWidth = -1;
+ private int mHeight = -1;
+
+ protected EglSurfaceBase(EglCore eglCore) {
+ mEglCore = eglCore;
+ }
+
+ /**
+ * Creates a window surface.
+ * <p>
+ * @param surface May be a Surface or SurfaceTexture.
+ */
+ public void createWindowSurface(Object surface) {
+ if (mEGLSurface != EGL14.EGL_NO_SURFACE) {
+ throw new IllegalStateException("surface already created");
+ }
+ mEGLSurface = mEglCore.createWindowSurface(surface);
+
+ // Don't cache width/height here, because the size of the underlying surface can change
+ // out from under us (see e.g. HardwareScalerActivity).
+ //mWidth = mEglCore.querySurface(mEGLSurface, EGL14.EGL_WIDTH);
+ //mHeight = mEglCore.querySurface(mEGLSurface, EGL14.EGL_HEIGHT);
+ }
+
+ /**
+ * Creates an off-screen surface.
+ */
+ public void createOffscreenSurface(int width, int height) {
+ if (mEGLSurface != EGL14.EGL_NO_SURFACE) {
+ throw new IllegalStateException("surface already created");
+ }
+ mEGLSurface = mEglCore.createOffscreenSurface(width, height);
+ mWidth = width;
+ mHeight = height;
+ }
+
+ /**
+ * Returns the surface's width, in pixels.
+ * <p>
+ * If this is called on a window surface, and the underlying surface is in the process
+ * of changing size, we may not see the new size right away (e.g. in the "surfaceChanged"
+ * callback). The size should match after the next buffer swap.
+ */
+ public int getWidth() {
+ if (mWidth < 0) {
+ return mEglCore.querySurface(mEGLSurface, EGL14.EGL_WIDTH);
+ } else {
+ return mWidth;
+ }
+ }
+
+ /**
+ * Returns the surface's height, in pixels.
+ */
+ public int getHeight() {
+ if (mHeight < 0) {
+ return mEglCore.querySurface(mEGLSurface, EGL14.EGL_HEIGHT);
+ } else {
+ return mHeight;
+ }
+ }
+
+ /**
+ * Release the EGL surface.
+ */
+ public void releaseEglSurface() {
+ mEglCore.releaseSurface(mEGLSurface);
+ mEGLSurface = EGL14.EGL_NO_SURFACE;
+ mWidth = mHeight = -1;
+ }
+
+ /**
+ * Makes our EGL context and surface current.
+ */
+ public void makeCurrent() {
+ mEglCore.makeCurrent(mEGLSurface);
+ }
+
+ /**
+ * Makes our EGL context and surface current for drawing, using the supplied surface
+ * for reading.
+ */
+ public void makeCurrentReadFrom(EglSurfaceBase readSurface) {
+ mEglCore.makeCurrent(mEGLSurface, readSurface.mEGLSurface);
+ }
+
+ /**
+ * Calls eglSwapBuffers. Use this to "publish" the current frame.
+ *
+ * @return false on failure
+ */
+ public boolean swapBuffers() {
+ boolean result = mEglCore.swapBuffers(mEGLSurface);
+ if (!result) {
+ Log.d(TAG,"WARNING: swapBuffers() failed");
+ }
+ return result;
+ }
+
+ /**
+ * Sends the presentation time stamp to EGL.
+ *
+ * @param nsecs Timestamp, in nanoseconds.
+ */
+ public void setPresentationTime(long nsecs) {
+ mEglCore.setPresentationTime(mEGLSurface, nsecs);
+ }
+
+ /**
+ * Saves the EGL surface to a file.
+ * <p>
+ * Expects that this object's EGL surface is current.
+ */
+ public void saveFrame(File file) throws IOException {
+ if (!mEglCore.isCurrent(mEGLSurface)) {
+ throw new RuntimeException("Expected EGL context/surface is not current");
+ }
+
+ // glReadPixels fills in a "direct" ByteBuffer with what is essentially big-endian RGBA
+ // data (i.e. a byte of red, followed by a byte of green...). While the Bitmap
+ // constructor that takes an int[] wants little-endian ARGB (blue/red swapped), the
+ // Bitmap "copy pixels" method wants the same format GL provides.
+ //
+ // Ideally we'd have some way to re-use the ByteBuffer, especially if we're calling
+ // here often.
+ //
+ // Making this even more interesting is the upside-down nature of GL, which means
+ // our output will look upside down relative to what appears on screen if the
+ // typical GL conventions are used.
+
+ String filename = file.toString();
+
+ int width = getWidth();
+ int height = getHeight();
+ ByteBuffer buf = ByteBuffer.allocateDirect(width * height * 4);
+ buf.order(ByteOrder.LITTLE_ENDIAN);
+ GLES20.glReadPixels(0, 0, width, height,
+ GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
+ GlUtil.checkGlError("glReadPixels");
+ buf.rewind();
+
+ BufferedOutputStream bos = null;
+ try {
+ bos = new BufferedOutputStream(new FileOutputStream(filename));
+ Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ bmp.copyPixelsFromBuffer(buf);
+ bmp.compress(Bitmap.CompressFormat.PNG, 90, bos);
+ bmp.recycle();
+ } finally {
+ if (bos != null) bos.close();
+ }
+ Log.d(TAG,"Saved " + width + "x" + height + " frame as '" + filename + "'");
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/android/grafika/gles/FullFrameRect.java b/android/sdl_android/src/main/java/com/android/grafika/gles/FullFrameRect.java
new file mode 100644
index 000000000..74c8dac44
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/android/grafika/gles/FullFrameRect.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.grafika.gles;
+
+import android.opengl.Matrix;
+
+/**
+ * This class essentially represents a viewport-sized sprite that will be rendered with
+ * a texture, usually from an external source like the camera or video decoder.
+ */
+public class FullFrameRect {
+ private final Drawable2d mRectDrawable = new Drawable2d(Drawable2d.Prefab.FULL_RECTANGLE);
+ private Texture2dProgram mProgram;
+
+ /**
+ * Prepares the object.
+ *
+ * @param program The program to use. FullFrameRect takes ownership, and will release
+ * the program when no longer needed.
+ */
+ public FullFrameRect(Texture2dProgram program) {
+ mProgram = program;
+ }
+
+ /**
+ * Releases resources.
+ * <p>
+ * This must be called with the appropriate EGL context current (i.e. the one that was
+ * current when the constructor was called). If we're about to destroy the EGL context,
+ * there's no value in having the caller make it current just to do this cleanup, so you
+ * can pass a flag that will tell this function to skip any EGL-context-specific cleanup.
+ */
+ public void release(boolean doEglCleanup) {
+ if (mProgram != null) {
+ if (doEglCleanup) {
+ mProgram.release();
+ }
+ mProgram = null;
+ }
+ }
+
+ /**
+ * Returns the program currently in use.
+ */
+ public Texture2dProgram getProgram() {
+ return mProgram;
+ }
+
+ /**
+ * Changes the program. The previous program will be released.
+ * <p>
+ * The appropriate EGL context must be current.
+ */
+ public void changeProgram(Texture2dProgram program) {
+ mProgram.release();
+ mProgram = program;
+ }
+
+ /**
+ * Creates a texture object suitable for use with drawFrame().
+ */
+ public int createTextureObject() {
+ return mProgram.createTextureObject();
+ }
+
+ /**
+ * Draws a viewport-filling rect, texturing it with the specified texture object.
+ */
+ public void drawFrame(int textureId, float[] texMatrix) {
+ // Use the identity matrix for MVP so our 2x2 FULL_RECTANGLE covers the viewport.
+ mProgram.draw(GlUtil.IDENTITY_MATRIX, mRectDrawable.getVertexArray(), 0,
+ mRectDrawable.getVertexCount(), mRectDrawable.getCoordsPerVertex(),
+ mRectDrawable.getVertexStride(),
+ texMatrix, mRectDrawable.getTexCoordArray(), textureId,
+ mRectDrawable.getTexCoordStride());
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/android/grafika/gles/GlUtil.java b/android/sdl_android/src/main/java/com/android/grafika/gles/GlUtil.java
new file mode 100644
index 000000000..4eed8d00c
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/android/grafika/gles/GlUtil.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.grafika.gles;
+
+import android.opengl.GLES20;
+import android.opengl.GLES30;
+import android.opengl.Matrix;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.FloatBuffer;
+
+/**
+ * Some OpenGL utility functions.
+ */
+public class GlUtil {
+ private static final String TAG = "GlUtil";
+
+ /** Identity matrix for general use. Don't modify or life will get weird. */
+ public static final float[] IDENTITY_MATRIX;
+ static {
+ IDENTITY_MATRIX = new float[16];
+ Matrix.setIdentityM(IDENTITY_MATRIX, 0);
+ }
+
+ private static final int SIZEOF_FLOAT = 4;
+
+
+ private GlUtil() {} // do not instantiate
+
+ /**
+ * Creates a new program from the supplied vertex and fragment shaders.
+ *
+ * @return A handle to the program, or 0 on failure.
+ */
+ public static int createProgram(String vertexSource, String fragmentSource) {
+ int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
+ if (vertexShader == 0) {
+ return 0;
+ }
+ int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
+ if (pixelShader == 0) {
+ return 0;
+ }
+
+ int program = GLES20.glCreateProgram();
+ checkGlError("glCreateProgram");
+ if (program == 0) {
+ Log.d(TAG,"Could not create program");
+ }
+ GLES20.glAttachShader(program, vertexShader);
+ checkGlError("glAttachShader");
+ GLES20.glAttachShader(program, pixelShader);
+ checkGlError("glAttachShader");
+ GLES20.glLinkProgram(program);
+ int[] linkStatus = new int[1];
+ GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
+ if (linkStatus[0] != GLES20.GL_TRUE) {
+ Log.e(TAG,"Could not link program: ");
+ Log.e(TAG,GLES20.glGetProgramInfoLog(program));
+ GLES20.glDeleteProgram(program);
+ program = 0;
+ }
+ return program;
+ }
+
+ /**
+ * Compiles the provided shader source.
+ *
+ * @return A handle to the shader, or 0 on failure.
+ */
+ public static int loadShader(int shaderType, String source) {
+ int shader = GLES20.glCreateShader(shaderType);
+ checkGlError("glCreateShader type=" + shaderType);
+ GLES20.glShaderSource(shader, source);
+ GLES20.glCompileShader(shader);
+ int[] compiled = new int[1];
+ GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
+ if (compiled[0] == 0) {
+ Log.e(TAG,"Could not compile shader " + shaderType + ":");
+ Log.e(TAG," " + GLES20.glGetShaderInfoLog(shader));
+ GLES20.glDeleteShader(shader);
+ shader = 0;
+ }
+ return shader;
+ }
+
+ /**
+ * Checks to see if a GLES error has been raised.
+ */
+ public static void checkGlError(String op) {
+ int error = GLES20.glGetError();
+ if (error != GLES20.GL_NO_ERROR) {
+ String msg = op + ": glError 0x" + Integer.toHexString(error);
+ Log.e(TAG,msg);
+ throw new RuntimeException(msg);
+ }
+ }
+
+ /**
+ * Checks to see if the location we obtained is valid. GLES returns -1 if a label
+ * could not be found, but does not set the GL error.
+ * <p>
+ * Throws a RuntimeException if the location is invalid.
+ */
+ public static void checkLocation(int location, String label) {
+ if (location < 0) {
+ throw new RuntimeException("Unable to locate '" + label + "' in program");
+ }
+ }
+
+ /**
+ * Creates a texture from raw data.
+ *
+ * @param data Image data, in a "direct" ByteBuffer.
+ * @param width Texture width, in pixels (not bytes).
+ * @param height Texture height, in pixels.
+ * @param format Image data format (use constant appropriate for glTexImage2D(), e.g. GL_RGBA).
+ * @return Handle to texture.
+ */
+ public static int createImageTexture(ByteBuffer data, int width, int height, int format) {
+ int[] textureHandles = new int[1];
+ int textureHandle;
+
+ GLES20.glGenTextures(1, textureHandles, 0);
+ textureHandle = textureHandles[0];
+ GlUtil.checkGlError("glGenTextures");
+
+ // Bind the texture handle to the 2D texture target.
+ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle);
+
+ // Configure min/mag filtering, i.e. what scaling method do we use if what we're rendering
+ // is smaller or larger than the source image.
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_LINEAR);
+ GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
+ GLES20.GL_LINEAR);
+ GlUtil.checkGlError("loadImageTexture");
+
+ // Load the data from the buffer into the texture handle.
+ GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, /*level*/ 0, format,
+ width, height, /*border*/ 0, format, GLES20.GL_UNSIGNED_BYTE, data);
+ GlUtil.checkGlError("loadImageTexture");
+
+ return textureHandle;
+ }
+
+ /**
+ * Allocates a direct float buffer, and populates it with the float array data.
+ */
+ public static FloatBuffer createFloatBuffer(float[] coords) {
+ // Allocate a direct ByteBuffer, using 4 bytes per float, and copy coords into it.
+ ByteBuffer bb = ByteBuffer.allocateDirect(coords.length * SIZEOF_FLOAT);
+ bb.order(ByteOrder.nativeOrder());
+ FloatBuffer fb = bb.asFloatBuffer();
+ fb.put(coords);
+ fb.position(0);
+ return fb;
+ }
+
+ /**
+ * Writes GL version info to the log.
+ */
+ public static void logVersionInfo() {
+ Log.i(TAG,"vendor : " + GLES20.glGetString(GLES20.GL_VENDOR));
+ Log.i(TAG,"renderer: " + GLES20.glGetString(GLES20.GL_RENDERER));
+ Log.i(TAG,"version : " + GLES20.glGetString(GLES20.GL_VERSION));
+
+ if (false) {
+ int[] values = new int[1];
+ GLES30.glGetIntegerv(GLES30.GL_MAJOR_VERSION, values, 0);
+ int majorVersion = values[0];
+ GLES30.glGetIntegerv(GLES30.GL_MINOR_VERSION, values, 0);
+ int minorVersion = values[0];
+ if (GLES30.glGetError() == GLES30.GL_NO_ERROR) {
+ Log.i(TAG,"iversion: " + majorVersion + "." + minorVersion);
+ }
+ }
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/android/grafika/gles/OffscreenSurface.java b/android/sdl_android/src/main/java/com/android/grafika/gles/OffscreenSurface.java
new file mode 100644
index 000000000..8b33d87a4
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/android/grafika/gles/OffscreenSurface.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.grafika.gles;
+
+/**
+ * Off-screen EGL surface (pbuffer).
+ * <p>
+ * It's good practice to explicitly release() the surface, preferably from a "finally" block.
+ */
+public class OffscreenSurface extends EglSurfaceBase {
+ /**
+ * Creates an off-screen surface with the specified width and height.
+ */
+ public OffscreenSurface(EglCore eglCore, int width, int height) {
+ super(eglCore);
+ createOffscreenSurface(width, height);
+ }
+
+ /**
+ * Releases any resources associated with the surface.
+ */
+ public void release() {
+ releaseEglSurface();
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/android/grafika/gles/Texture2dProgram.java b/android/sdl_android/src/main/java/com/android/grafika/gles/Texture2dProgram.java
new file mode 100644
index 000000000..1faed6776
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/android/grafika/gles/Texture2dProgram.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright 2014 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.grafika.gles;
+
+import android.opengl.GLES11Ext;
+import android.opengl.GLES20;
+import android.util.Log;
+
+import java.nio.FloatBuffer;
+import java.util.Arrays;
+
+/**
+ * GL program and supporting functions for textured 2D shapes.
+ */
+public class Texture2dProgram {
+ private static final String TAG = "Texture2dProgram";
+
+ public enum ProgramType {
+ TEXTURE_2D, TEXTURE_EXT, TEXTURE_EXT_BW, TEXTURE_EXT_FILT
+ }
+
+ // Simple vertex shader, used for all programs.
+ private static final String VERTEX_SHADER =
+ "uniform mat4 uMVPMatrix;\n" +
+ "uniform mat4 uTexMatrix;\n" +
+ "attribute vec4 aPosition;\n" +
+ "attribute vec4 aTextureCoord;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "void main() {\n" +
+ " gl_Position = uMVPMatrix * aPosition;\n" +
+ " vTextureCoord = (uTexMatrix * aTextureCoord).xy;\n" +
+ "}\n";
+
+ // Simple fragment shader for use with "normal" 2D textures.
+ private static final String FRAGMENT_SHADER_2D =
+ "precision mediump float;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "uniform sampler2D sTexture;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
+ "}\n";
+
+ // Simple fragment shader for use with external 2D textures (e.g. what we get from
+ // SurfaceTexture).
+ private static final String FRAGMENT_SHADER_EXT =
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "precision mediump float;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "uniform samplerExternalOES sTexture;\n" +
+ "void main() {\n" +
+ " gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
+ "}\n";
+
+ // Fragment shader that converts color to black & white with a simple transformation.
+ private static final String FRAGMENT_SHADER_EXT_BW =
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "precision mediump float;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "uniform samplerExternalOES sTexture;\n" +
+ "void main() {\n" +
+ " vec4 tc = texture2D(sTexture, vTextureCoord);\n" +
+ " float color = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;\n" +
+ " gl_FragColor = vec4(color, color, color, 1.0);\n" +
+ "}\n";
+
+ // Fragment shader with a convolution filter. The upper-left half will be drawn normally,
+ // the lower-right half will have the filter applied, and a thin red line will be drawn
+ // at the border.
+ //
+ // This is not optimized for performance. Some things that might make this faster:
+ // - Remove the conditionals. They're used to present a half & half view with a red
+ // stripe across the middle, but that's only useful for a demo.
+ // - Unroll the loop. Ideally the compiler does this for you when it's beneficial.
+ // - Bake the filter kernel into the shader, instead of passing it through a uniform
+ // array. That, combined with loop unrolling, should reduce memory accesses.
+ public static final int KERNEL_SIZE = 9;
+ private static final String FRAGMENT_SHADER_EXT_FILT =
+ "#extension GL_OES_EGL_image_external : require\n" +
+ "#define KERNEL_SIZE " + KERNEL_SIZE + "\n" +
+ "precision highp float;\n" +
+ "varying vec2 vTextureCoord;\n" +
+ "uniform samplerExternalOES sTexture;\n" +
+ "uniform float uKernel[KERNEL_SIZE];\n" +
+ "uniform vec2 uTexOffset[KERNEL_SIZE];\n" +
+ "uniform float uColorAdjust;\n" +
+ "void main() {\n" +
+ " int i = 0;\n" +
+ " vec4 sum = vec4(0.0);\n" +
+ " if (vTextureCoord.x < vTextureCoord.y - 0.005) {\n" +
+ " for (i = 0; i < KERNEL_SIZE; i++) {\n" +
+ " vec4 texc = texture2D(sTexture, vTextureCoord + uTexOffset[i]);\n" +
+ " sum += texc * uKernel[i];\n" +
+ " }\n" +
+ " sum += uColorAdjust;\n" +
+ " } else if (vTextureCoord.x > vTextureCoord.y + 0.005) {\n" +
+ " sum = texture2D(sTexture, vTextureCoord);\n" +
+ " } else {\n" +
+ " sum.r = 1.0;\n" +
+ " }\n" +
+ " gl_FragColor = sum;\n" +
+ "}\n";
+
+ private ProgramType mProgramType;
+
+ // Handles to the GL program and various components of it.
+ private int mProgramHandle;
+ private int muMVPMatrixLoc;
+ private int muTexMatrixLoc;
+ private int muKernelLoc;
+ private int muTexOffsetLoc;
+ private int muColorAdjustLoc;
+ private int maPositionLoc;
+ private int maTextureCoordLoc;
+
+ private int mTextureTarget;
+
+ private float[] mKernel = new float[KERNEL_SIZE];
+ private float[] mTexOffset;
+ private float mColorAdjust;
+
+
+ /**
+ * Prepares the program in the current EGL context.
+ */
+ public Texture2dProgram(ProgramType programType) {
+ mProgramType = programType;
+
+ switch (programType) {
+ case TEXTURE_2D:
+ mTextureTarget = GLES20.GL_TEXTURE_2D;
+ mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER_2D);
+ break;
+ case TEXTURE_EXT:
+ mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
+ mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER_EXT);
+ break;
+ case TEXTURE_EXT_BW:
+ mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
+ mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER_EXT_BW);
+ break;
+ case TEXTURE_EXT_FILT:
+ mTextureTarget = GLES11Ext.GL_TEXTURE_EXTERNAL_OES;
+ mProgramHandle = GlUtil.createProgram(VERTEX_SHADER, FRAGMENT_SHADER_EXT_FILT);
+ break;
+ default:
+ throw new RuntimeException("Unhandled type " + programType);
+ }
+ if (mProgramHandle == 0) {
+ throw new RuntimeException("Unable to create program");
+ }
+ Log.e(TAG,"Created program " + mProgramHandle + " (" + programType + ")");
+
+ // get locations of attributes and uniforms
+
+ maPositionLoc = GLES20.glGetAttribLocation(mProgramHandle, "aPosition");
+ GlUtil.checkLocation(maPositionLoc, "aPosition");
+ maTextureCoordLoc = GLES20.glGetAttribLocation(mProgramHandle, "aTextureCoord");
+ GlUtil.checkLocation(maTextureCoordLoc, "aTextureCoord");
+ muMVPMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uMVPMatrix");
+ GlUtil.checkLocation(muMVPMatrixLoc, "uMVPMatrix");
+ muTexMatrixLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTexMatrix");
+ GlUtil.checkLocation(muTexMatrixLoc, "uTexMatrix");
+ muKernelLoc = GLES20.glGetUniformLocation(mProgramHandle, "uKernel");
+ if (muKernelLoc < 0) {
+ // no kernel in this one
+ muKernelLoc = -1;
+ muTexOffsetLoc = -1;
+ muColorAdjustLoc = -1;
+ } else {
+ // has kernel, must also have tex offset and color adj
+ muTexOffsetLoc = GLES20.glGetUniformLocation(mProgramHandle, "uTexOffset");
+ GlUtil.checkLocation(muTexOffsetLoc, "uTexOffset");
+ muColorAdjustLoc = GLES20.glGetUniformLocation(mProgramHandle, "uColorAdjust");
+ GlUtil.checkLocation(muColorAdjustLoc, "uColorAdjust");
+
+ // initialize default values
+ setKernel(new float[] {0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f}, 0f);
+ setTexSize(256, 256);
+ }
+ }
+
+ /**
+ * Releases the program.
+ * <p>
+ * The appropriate EGL context must be current (i.e. the one that was used to create
+ * the program).
+ */
+ public void release() {
+ Log.d(TAG,"deleting program " + mProgramHandle);
+ GLES20.glDeleteProgram(mProgramHandle);
+ mProgramHandle = -1;
+ }
+
+ /**
+ * Returns the program type.
+ */
+ public ProgramType getProgramType() {
+ return mProgramType;
+ }
+
+ /**
+ * Creates a texture object suitable for use with this program.
+ * <p>
+ * On exit, the texture will be bound.
+ */
+ public int createTextureObject() {
+ int[] textures = new int[1];
+ GLES20.glGenTextures(1, textures, 0);
+ GlUtil.checkGlError("glGenTextures");
+
+ int texId = textures[0];
+ GLES20.glBindTexture(mTextureTarget, texId);
+ GlUtil.checkGlError("glBindTexture " + texId);
+
+ GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER,
+ GLES20.GL_NEAREST);
+ GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER,
+ GLES20.GL_LINEAR);
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S,
+ GLES20.GL_CLAMP_TO_EDGE);
+ GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T,
+ GLES20.GL_CLAMP_TO_EDGE);
+ GlUtil.checkGlError("glTexParameter");
+
+ return texId;
+ }
+
+ /**
+ * Configures the convolution filter values.
+ *
+ * @param values Normalized filter values; must be KERNEL_SIZE elements.
+ */
+ public void setKernel(float[] values, float colorAdj) {
+ if (values.length != KERNEL_SIZE) {
+ throw new IllegalArgumentException("Kernel size is " + values.length +
+ " vs. " + KERNEL_SIZE);
+ }
+ System.arraycopy(values, 0, mKernel, 0, KERNEL_SIZE);
+ mColorAdjust = colorAdj;
+ Log.d(TAG,"filt kernel: " + Arrays.toString(mKernel) + ", adj=" + colorAdj);
+ }
+
+ /**
+ * Sets the size of the texture. This is used to find adjacent texels when filtering.
+ */
+ public void setTexSize(int width, int height) {
+ float rw = 1.0f / width;
+ float rh = 1.0f / height;
+
+ // Don't need to create a new array here, but it's syntactically convenient.
+ mTexOffset = new float[] {
+ -rw, -rh, 0f, -rh, rw, -rh,
+ -rw, 0f, 0f, 0f, rw, 0f,
+ -rw, rh, 0f, rh, rw, rh
+ };
+ Log.d(TAG,"filt size: " + width + "x" + height + ": " + Arrays.toString(mTexOffset));
+ }
+
+ /**
+ * Issues the draw call. Does the full setup on every call.
+ *
+ * @param mvpMatrix The 4x4 projection matrix.
+ * @param vertexBuffer Buffer with vertex position data.
+ * @param firstVertex Index of first vertex to use in vertexBuffer.
+ * @param vertexCount Number of vertices in vertexBuffer.
+ * @param coordsPerVertex The number of coordinates per vertex (e.g. x,y is 2).
+ * @param vertexStride Width, in bytes, of the position data for each vertex (often
+ * vertexCount * sizeof(float)).
+ * @param texMatrix A 4x4 transformation matrix for texture coords. (Primarily intended
+ * for use with SurfaceTexture.)
+ * @param texBuffer Buffer with vertex texture data.
+ * @param texStride Width, in bytes, of the texture data for each vertex.
+ */
+ public void draw(float[] mvpMatrix, FloatBuffer vertexBuffer, int firstVertex,
+ int vertexCount, int coordsPerVertex, int vertexStride,
+ float[] texMatrix, FloatBuffer texBuffer, int textureId, int texStride) {
+ GlUtil.checkGlError("draw start");
+
+ // Select the program.
+ GLES20.glUseProgram(mProgramHandle);
+ GlUtil.checkGlError("glUseProgram");
+
+ // Set the texture.
+ GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
+ GLES20.glBindTexture(mTextureTarget, textureId);
+
+ // Copy the model / view / projection matrix over.
+ GLES20.glUniformMatrix4fv(muMVPMatrixLoc, 1, false, mvpMatrix, 0);
+ GlUtil.checkGlError("glUniformMatrix4fv");
+
+ // Copy the texture transformation matrix over.
+ GLES20.glUniformMatrix4fv(muTexMatrixLoc, 1, false, texMatrix, 0);
+ GlUtil.checkGlError("glUniformMatrix4fv");
+
+ // Enable the "aPosition" vertex attribute.
+ GLES20.glEnableVertexAttribArray(maPositionLoc);
+ GlUtil.checkGlError("glEnableVertexAttribArray");
+
+ // Connect vertexBuffer to "aPosition".
+ GLES20.glVertexAttribPointer(maPositionLoc, coordsPerVertex,
+ GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);
+ GlUtil.checkGlError("glVertexAttribPointer");
+
+ // Enable the "aTextureCoord" vertex attribute.
+ GLES20.glEnableVertexAttribArray(maTextureCoordLoc);
+ GlUtil.checkGlError("glEnableVertexAttribArray");
+
+ // Connect texBuffer to "aTextureCoord".
+ GLES20.glVertexAttribPointer(maTextureCoordLoc, 2,
+ GLES20.GL_FLOAT, false, texStride, texBuffer);
+ GlUtil.checkGlError("glVertexAttribPointer");
+
+ // Populate the convolution kernel, if present.
+ if (muKernelLoc >= 0) {
+ GLES20.glUniform1fv(muKernelLoc, KERNEL_SIZE, mKernel, 0);
+ GLES20.glUniform2fv(muTexOffsetLoc, KERNEL_SIZE, mTexOffset, 0);
+ GLES20.glUniform1f(muColorAdjustLoc, mColorAdjust);
+ }
+
+ // Draw the rect.
+ GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, firstVertex, vertexCount);
+ GlUtil.checkGlError("glDrawArrays");
+
+ // Done -- disable vertex array, texture, and program.
+ GLES20.glDisableVertexAttribArray(maPositionLoc);
+ GLES20.glDisableVertexAttribArray(maTextureCoordLoc);
+ GLES20.glBindTexture(mTextureTarget, 0);
+ GLES20.glUseProgram(0);
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/android/grafika/gles/WindowSurface.java b/android/sdl_android/src/main/java/com/android/grafika/gles/WindowSurface.java
new file mode 100644
index 000000000..adefce528
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/android/grafika/gles/WindowSurface.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2013 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.grafika.gles;
+
+import android.graphics.SurfaceTexture;
+import android.view.Surface;
+
+/**
+ * Recordable EGL window surface.
+ * <p>
+ * It's good practice to explicitly release() the surface, preferably from a "finally" block.
+ */
+public class WindowSurface extends EglSurfaceBase {
+ private Surface mSurface;
+ private boolean mReleaseSurface;
+
+ /**
+ * Associates an EGL surface with the native window surface.
+ * <p>
+ * Set releaseSurface to true if you want the Surface to be released when release() is
+ * called. This is convenient, but can interfere with framework classes that expect to
+ * manage the Surface themselves (e.g. if you release a SurfaceView's Surface, the
+ * surfaceDestroyed() callback won't fire).
+ */
+ public WindowSurface(EglCore eglCore, Surface surface, boolean releaseSurface) {
+ super(eglCore);
+ createWindowSurface(surface);
+ mSurface = surface;
+ mReleaseSurface = releaseSurface;
+ }
+
+ /**
+ * Associates an EGL surface with the SurfaceTexture.
+ */
+ public WindowSurface(EglCore eglCore, SurfaceTexture surfaceTexture) {
+ super(eglCore);
+ createWindowSurface(surfaceTexture);
+ }
+
+ /**
+ * Releases any resources associated with the EGL surface (and, if configured to do so,
+ * with the Surface as well).
+ * <p>
+ * Does not require that the surface's EGL context be current.
+ */
+ public void release() {
+ releaseEglSurface();
+ if (mSurface != null) {
+ if (mReleaseSurface) {
+ mSurface.release();
+ }
+ mSurface = null;
+ }
+ }
+
+ /**
+ * Recreate the EGLSurface, using the new EglBase. The caller should have already
+ * freed the old EGLSurface with releaseEglSurface().
+ * <p>
+ * This is useful when we want to update the EGLSurface associated with a Surface.
+ * For example, if we want to share with a different EGLContext, which can only
+ * be done by tearing down and recreating the context. (That's handled by the caller;
+ * this just creates a new EGLSurface for the Surface we were handed earlier.)
+ * <p>
+ * If the previous EGLSurface isn't fully destroyed, e.g. it's still current on a
+ * context somewhere, the create call will fail with complaints from the Surface
+ * about already being connected.
+ */
+ public void recreate(EglCore newEglCore) {
+ if (mSurface == null) {
+ throw new RuntimeException("not yet implemented for SurfaceTexture");
+ }
+ mEglCore = newEglCore; // switch to new context
+ createWindowSurface(mSurface); // create new surface
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java b/android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java
index 6a670dba3..7c8df5b98 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java
@@ -34,15 +34,26 @@ package com.smartdevicelink.encoder;
import android.annotation.TargetApi;
import android.content.Context;
+import android.graphics.SurfaceTexture;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
+import android.opengl.GLES20;
import android.os.Build;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.view.Display;
import android.view.Surface;
+import com.android.grafika.gles.EglCore;
+import com.android.grafika.gles.FullFrameRect;
+import com.android.grafika.gles.OffscreenSurface;
+import com.android.grafika.gles.Texture2dProgram;
+import com.android.grafika.gles.WindowSurface;
import com.smartdevicelink.proxy.rpc.ImageResolution;
import com.smartdevicelink.proxy.rpc.VideoStreamingFormat;
import com.smartdevicelink.proxy.rpc.enums.VideoStreamingCodec;
@@ -72,6 +83,14 @@ public class VirtualDisplayEncoder {
//For older (<21) OS versions
private Thread encoderThread;
+ private CaptureThread mCaptureThread;
+ private EglCore mEglCore;
+ private OffscreenSurface mDummySurface;
+ private int mTextureId = -1;
+ private SurfaceTexture mInterSurfaceTexture;
+ private Surface mInterSurface;
+ private FullFrameRect mFullFrameBlit;
+ private WindowSurface mEncoderSurface;
/**
* Initialization method for VirtualDisplayEncoder object. MUST be called before start() or shutdown()
@@ -106,10 +125,29 @@ public class VirtualDisplayEncoder {
return this.streamingParams;
}
+ /**
+ * This method is deprecated; setStreamingParams with having stableFrameRate should be used.
+ */
+ @Deprecated
public void setStreamingParams(int displayDensity, ImageResolution resolution, int frameRate, int bitrate, int interval, VideoStreamingFormat format) {
this.streamingParams = new VideoStreamingParameters(displayDensity, frameRate, bitrate, interval, resolution, format);
}
+ /**
+ * setter of every parameter in streamingParams.
+ * @param displayDensity
+ * @param resolution
+ * @param frameRate
+ * @param bitrate
+ * @param interval
+ * @param format
+ * @param stableFramerate
+ */
+ public void setStreamingParams(int displayDensity, ImageResolution resolution, int frameRate, int bitrate, int interval, VideoStreamingFormat format, boolean stableFramerate) {
+ this.streamingParams = new VideoStreamingParameters(displayDensity, frameRate, bitrate, interval, resolution, format, stableFramerate);
+ }
+
+ @SuppressWarnings("unused")
public void setStreamingParams(VideoStreamingParameters streamingParams) {
this.streamingParams = streamingParams;
}
@@ -127,17 +165,50 @@ public class VirtualDisplayEncoder {
return;
}
+ int width = streamingParams.getResolution().getResolutionWidth();
+ int height = streamingParams.getResolution().getResolutionHeight();
+ if (streamingParams.isStableFrameRate()) {
+ setupGLES(width, height);
+ }
+
synchronized (STREAMING_LOCK) {
try {
- inputSurface = prepareVideoEncoder();
+ if (streamingParams.isStableFrameRate()) {
+ // We use WindowSurface for the input of MediaCodec.
+ mEncoderSurface = new WindowSurface(mEglCore, prepareVideoEncoder(), true);
+ virtualDisplay = mDisplayManager.createVirtualDisplay(TAG,
+ width, height, streamingParams.getDisplayDensity(), mInterSurface, DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION);
+
+ startEncoder();
+ // also start capture thread.
+ final ConditionVariable cond = new ConditionVariable();
+ mCaptureThread = new CaptureThread(mEglCore, mInterSurfaceTexture, mTextureId,
+ mEncoderSurface, mFullFrameBlit, width, height, streamingParams.getFrameRate(), new Runnable() {
+ @Override
+ public void run() {
+ cond.open();
+ }
+ });
+ mCaptureThread.start();
+ cond.block(); // make sure Capture thread exists.
+
+ // setup listener prior to the surface is attached to VirtualDisplay.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ mInterSurfaceTexture.setOnFrameAvailableListener(mCaptureThread, mCaptureThread.getHandler());
+ } else {
+ mInterSurfaceTexture.setOnFrameAvailableListener(mCaptureThread);
+ }
+ } else {
+ inputSurface = prepareVideoEncoder();
- // Create a virtual display that will output to our encoder.
- virtualDisplay = mDisplayManager.createVirtualDisplay(TAG,
- streamingParams.getResolution().getResolutionWidth(), streamingParams.getResolution().getResolutionHeight(),
- streamingParams.getDisplayDensity(), inputSurface, DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION);
+ // Create a virtual display that will output to our encoder.
+ virtualDisplay = mDisplayManager.createVirtualDisplay(TAG,
+ streamingParams.getResolution().getResolutionWidth(), streamingParams.getResolution().getResolutionHeight(),
+ streamingParams.getDisplayDensity(), inputSurface, DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION);
- startEncoder();
+ startEncoder();
+ }
} catch (Exception ex) {
DebugTool.logError(TAG, "Unable to create Virtual Display.");
@@ -152,6 +223,16 @@ public class VirtualDisplayEncoder {
return;
}
try {
+ // cleanup GLES stuff
+ if (mCaptureThread != null) {
+ mCaptureThread.stopAsync();
+ try {
+ mCaptureThread.join();
+ } catch(InterruptedException e) {
+
+ }
+ mCaptureThread = null;
+ }
if (encoderThread != null) {
encoderThread.interrupt();
encoderThread = null;
@@ -177,6 +258,206 @@ public class VirtualDisplayEncoder {
}
}
+ /**
+ * setupGLES: create offscreen surface and surface texture.
+ * @param Width
+ * @param Height
+ */
+ private void setupGLES(int Width, int Height) {
+ mEglCore = new EglCore(null, 0);
+
+ // This 1x1 offscreen is created just to get the texture name (mTextureId).
+ // (To create a SurfaceTexture, we need a texture name. Texture name can be created by
+ // glGenTextures(), but for this method we need to acquire EGLContext. And to acquire
+ // EGLContext, we need to call eglMakeCurrent() on SurfaceTexture ... which is not created yet!
+ // So here, EGLContext is acquired by calling eglMakeCurrent() on a PBufferSurface which
+ // can be created without a texture name. That's why mDummySurface is not used anywhere.)
+ mDummySurface = new OffscreenSurface(mEglCore, 1, 1);
+ mDummySurface.makeCurrent();
+
+ mFullFrameBlit = new FullFrameRect(new Texture2dProgram(Texture2dProgram.ProgramType.TEXTURE_EXT));
+ mTextureId = mFullFrameBlit.createTextureObject();
+
+ mInterSurfaceTexture = new SurfaceTexture(mTextureId);
+ mInterSurfaceTexture.setDefaultBufferSize(Width, Height);
+ mInterSurface = new Surface(mInterSurfaceTexture);
+
+ // Some devices (e.g. Xperia Z4 with Android 5.0.1) do not allow eglMakeCurrent() called
+ // by multiple threads. (An EGLContext should be bound to a single thread.) Since the
+ // EGLContext will be accessed by CaptureThread from now on, unbind it from current thread.
+ mEglCore.makeNothingCurrent();
+ }
+
+ /**
+ * CatureThread: utilize OpenGl to capture the rendering thru intermediate surface and surface texture.
+ */
+ private final class CaptureThread extends Thread implements SurfaceTexture.OnFrameAvailableListener {
+
+ private static final int MSG_TICK = 1;
+ private static final int MSG_UPDATE_SURFACE = 2;
+ private static final int MSG_TERMINATE = -1;
+
+ private static final long END_MARGIN_NSEC = 1000000; // 1 msec
+ private static final long LONG_SLEEP_THRES_NSEC = 16000000; // 16 msec
+ private static final long LONG_SLEEP_MSEC = 10;
+
+ private Handler mHandler;
+ private Runnable mStartedCallback;
+
+ private EglCore mEgl;
+ private SurfaceTexture mSourceSurfaceTexture;
+ private int mSourceTextureId;
+ private WindowSurface mDestSurface;
+ private FullFrameRect mBlit;
+ private int mWidth;
+ private int mHeight;
+
+ private long mFrameIntervalNsec;
+ private long mStartNsec;
+ private long mNextTime;
+ private boolean mFirstInput;
+ private final float[] mMatrix = new float[16];
+
+ public CaptureThread(EglCore eglCore, SurfaceTexture sourceSurfaceTexture, int sourceTextureId,
+ WindowSurface destSurface, FullFrameRect blit, int width, int height, float fps,
+ Runnable onStarted) {
+ mEgl = eglCore;
+ mSourceSurfaceTexture = sourceSurfaceTexture;
+ mSourceTextureId = sourceTextureId;
+ mDestSurface = destSurface;
+ mBlit = blit;
+ mWidth = width;
+ mHeight = height;
+ mFrameIntervalNsec = (long)(1000000000 / fps);
+ mStartedCallback = onStarted;
+ }
+
+ @Override
+ public void run() {
+ Looper.prepare();
+
+ // create a Handler for this thread
+ mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_TICK: {
+ long now = System.nanoTime();
+ if (now > mNextTime - END_MARGIN_NSEC) {
+ drawImage(now);
+ mNextTime += mFrameIntervalNsec;
+ }
+
+ if (mNextTime - END_MARGIN_NSEC - now > LONG_SLEEP_THRES_NSEC) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TICK), LONG_SLEEP_MSEC);
+ } else {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TICK), 1);
+ }
+ break;
+ }
+ // this is for KitKat and below
+ case MSG_UPDATE_SURFACE:
+ updateSurface();
+ break;
+ case MSG_TERMINATE: {
+ removeCallbacksAndMessages(null);
+ Looper looper = Looper.myLooper();
+ if (looper != null) {
+ looper.quit();
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ };
+
+ mStartNsec = -1;
+ mFirstInput = true;
+
+ if (mStartedCallback != null) {
+ mStartedCallback.run();
+ }
+
+ Looper.loop();
+
+ // this is for safe (unbind EGLContext when terminating the thread)
+ mEgl.makeNothingCurrent();
+ }
+
+ // this may return null before mStartedCallback is called
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ // make sure this is called after mStartedCallback is called
+ public void stopAsync() {
+ if (mHandler != null) {
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_TERMINATE));
+ }
+ }
+
+ @Override
+ public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ // With API level 21 and higher, setOnFrameAvailableListener(listener, handler) is used
+ // so this method is called on the CaptureThread. We can call updateTexImage() directly.
+ updateSurface();
+ } else {
+ // With API level 20 and lower, setOnFrameAvailableListener(listener) is used, and
+ // this method is called on an "arbitrary" thread. (looks like the main thread is
+ // used for the most case.) So switch to CaptureThread before calling updateTexImage().
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_SURFACE));
+ }
+
+ if (mFirstInput) {
+ mFirstInput = false;
+ mNextTime = System.nanoTime();
+ // start the loop
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_TICK));
+ }
+ }
+
+ private void updateSurface() {
+ try {
+ mDestSurface.makeCurrent();
+ } catch (RuntimeException e) {
+ DebugTool.logError(TAG, "Runtime exception in updateSurface: " + e);
+ return;
+ }
+ // Workaround for the issue Nexus6,5x(6.0 or 6.0.1) stuck.
+ // See https://github.com/google/grafika/issues/43
+ // As in the comments, the nature of bug is still unclear...
+ // But it seems to have the effect to improve.
+ GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+ GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+ // get latest image from VirtualDisplay
+ mSourceSurfaceTexture.updateTexImage();
+ mSourceSurfaceTexture.getTransformMatrix(mMatrix);
+ }
+
+ private void drawImage(long currentTime) {
+ if (mStartNsec < 0) {
+ // first frame
+ mStartNsec = currentTime;
+ }
+
+ try {
+ mDestSurface.makeCurrent();
+ // draw from mInterSurfaceTexture to mEncoderSurface
+ GLES20.glViewport(0, 0, mWidth, mHeight);
+ mBlit.drawFrame(mSourceTextureId, mMatrix);
+ } catch (RuntimeException e) {
+ DebugTool.logError(TAG, "Runtime exception in updateSurface: " + e);
+ return;
+ }
+
+ // output to encoder
+ mDestSurface.setPresentationTime(currentTime - mStartNsec);
+ mDestSurface.swapBuffers();
+ }
+ }
+
private Surface prepareVideoEncoder() {
if (streamingParams == null || streamingParams.getResolution() == null || streamingParams.getFormat() == null) {
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java
index 09adbbaa6..866b6e80b 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java
@@ -40,19 +40,33 @@ import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.audio.AudioStreamManager;
import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
import com.smartdevicelink.managers.lockscreen.LockScreenConfig;
import com.smartdevicelink.managers.lockscreen.LockScreenManager;
import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.managers.screen.ScreenManager;
import com.smartdevicelink.managers.video.VideoStreamManager;
+import com.smartdevicelink.protocol.ISdlServiceListener;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.protocol.enums.SessionType;
+import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.rpc.RegisterAppInterfaceResponse;
+import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
import com.smartdevicelink.proxy.rpc.enums.AppHMIType;
+import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCRequestListener;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.transport.MultiplexTransportConfig;
import com.smartdevicelink.transport.enums.TransportType;
import com.smartdevicelink.transport.utl.TransportRecord;
import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.Version;
import java.util.List;
@@ -374,4 +388,133 @@ public class SdlManager extends BaseSdlManager {
return sdlManager;
}
}
+
+ ISdl _internalInterface = new ISdl() {
+ @Override
+ public void start() {
+ lifecycleManager.getInternalInterface(SdlManager.this).start();
+ }
+
+ @Override
+ public void stop() {
+ lifecycleManager.getInternalInterface(SdlManager.this).start();
+ }
+
+ @Override
+ public boolean isConnected() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).isConnected();
+ }
+
+ @Override
+ public void addServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addServiceListener(serviceType, sdlServiceListener);
+ }
+
+ @Override
+ public void removeServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).removeServiceListener(serviceType, sdlServiceListener);
+ }
+
+ @Override
+ public void startVideoService(VideoStreamingParameters parameters, boolean encrypted, boolean withPendingRestart) {
+ lifecycleManager.getInternalInterface(SdlManager.this).startVideoService(parameters, encrypted, withPendingRestart);
+ }
+
+ @Override
+ public void startAudioService(boolean encrypted) {
+ lifecycleManager.getInternalInterface(SdlManager.this).startAudioService(encrypted);
+ }
+
+ @Override
+ public void sendRPC(RPCMessage message) {
+ lifecycleManager.getInternalInterface(SdlManager.this).sendRPC(message);
+ }
+
+ @Override
+ public void sendRPCs(List<? extends RPCMessage> rpcs, OnMultipleRequestListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).sendRPCs(rpcs, listener);
+ }
+
+ @Override
+ public void sendSequentialRPCs(List<? extends RPCMessage> rpcs, OnMultipleRequestListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).sendSequentialRPCs(rpcs, listener);
+ }
+
+ @Override
+ public void addOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addOnRPCNotificationListener(notificationId, listener);
+ }
+
+ @Override
+ public boolean removeOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCNotificationListener(notificationId, listener);
+ }
+
+ @Override
+ public void addOnRPCRequestListener(FunctionID functionID, OnRPCRequestListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addOnRPCRequestListener(functionID, listener);
+ }
+
+ @Override
+ public boolean removeOnRPCRequestListener(FunctionID functionID, OnRPCRequestListener listener) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCRequestListener(functionID, listener);
+ }
+
+ @Override
+ public void addOnRPCListener(FunctionID responseId, OnRPCListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addOnRPCListener(responseId, listener);
+ }
+
+ @Override
+ public boolean removeOnRPCListener(FunctionID responseId, OnRPCListener listener) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCListener(responseId, listener);
+ }
+
+ @Override
+ public RegisterAppInterfaceResponse getRegisterAppInterfaceResponse() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getRegisterAppInterfaceResponse();
+ }
+
+ @Override
+ public boolean isTransportForServiceAvailable(SessionType serviceType) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).isTransportForServiceAvailable(serviceType);
+ }
+
+ @NonNull
+ @Override
+ public SdlMsgVersion getSdlMsgVersion() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getSdlMsgVersion();
+ }
+
+ @NonNull
+ @Override
+ public Version getProtocolVersion() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getProtocolVersion();
+ }
+
+ @Override
+ public long getMtu(SessionType serviceType) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getMtu(serviceType);
+ }
+
+ @Override
+ public void startRPCEncryption() {
+ lifecycleManager.getInternalInterface(SdlManager.this).startRPCEncryption();
+ }
+
+ @Override
+ public Taskmaster getTaskmaster() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getTaskmaster();
+ }
+
+ @Override
+ public SystemCapabilityManager getSystemCapabilityManager() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getSystemCapabilityManager();
+ }
+
+ @Override
+ public PermissionManager getPermissionManager() {
+ return permissionManager;
+ }
+ };
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManagerListener.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManagerListener.java
index 99af62a74..fe06561ee 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManagerListener.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManagerListener.java
@@ -34,6 +34,7 @@ package com.smartdevicelink.managers;
import com.smartdevicelink.managers.lifecycle.LifecycleConfigurationUpdate;
import com.smartdevicelink.proxy.rpc.enums.Language;
+import com.smartdevicelink.util.SystemInfo;
public interface SdlManagerListener extends BaseSdlManagerListener {
@@ -68,4 +69,12 @@ public interface SdlManagerListener extends BaseSdlManagerListener {
* otherwise null to indicate that the language is not supported.
*/
LifecycleConfigurationUpdate managerShouldUpdateLifecycle(Language language, Language hmiLanguage);
+
+ /**
+ * A way to determine if this SDL session should continue to be active while
+ * connected to the determined system information of the vehicle.
+ * @param systemInfo systemInfo - the system information of the vehicle that this session is currently active on.
+ * @return Return true if this session should continue, false if the session should end
+ */
+ boolean onSystemInfoReceived(SystemInfo systemInfo);
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompat.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompat.java
index db6c56fe5..6a69f84ba 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompat.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompat.java
@@ -32,39 +32,35 @@
package com.smartdevicelink.managers.audio;
import android.content.Context;
-import android.media.MediaCodec;
-import android.media.MediaFormat;
import android.net.Uri;
import androidx.annotation.NonNull;
+import com.livio.taskmaster.Queue;
import com.smartdevicelink.managers.audio.AudioStreamManager.SampleType;
-import com.smartdevicelink.util.DebugTool;
import java.lang.ref.WeakReference;
-import java.nio.ByteBuffer;
/**
* The audio decoder to decode a single audio file to PCM.
* This decoder supports phones with api < 21 but uses methods deprecated with api 21.
*/
public class AudioDecoderCompat extends BaseAudioDecoder {
- private static final String TAG = AudioDecoderCompat.class.getSimpleName();
- private static final int DEQUEUE_TIMEOUT = 3000;
- private static Runnable sRunnable;
- private Thread mThread;
+ private WeakReference<Queue> transactionQueue;
/**
* Creates a new object of AudioDecoder.
*
- * @param audioSource The audio source to decode.
- * @param context The context object to use to open the audio source.
- * @param sampleRate The desired sample rate for decoded audio data.
- * @param sampleType The desired sample type (8bit, 16bit, float).
- * @param listener A listener who receives the decoded audio.
+ * @param transactionQueue The operation queue that can be used to run operations in order.
+ * @param audioSource The audio source to decode.
+ * @param context The context object to use to open the audio source.
+ * @param sampleRate The desired sample rate for decoded audio data.
+ * @param sampleType The desired sample type (8bit, 16bit, float).
+ * @param listener A listener who receives the decoded audio.
*/
- AudioDecoderCompat(@NonNull Uri audioSource, @NonNull Context context, int sampleRate, @SampleType int sampleType, AudioDecoderListener listener) {
+ AudioDecoderCompat(@NonNull Queue transactionQueue, @NonNull Uri audioSource, @NonNull Context context, int sampleRate, @SampleType int sampleType, AudioDecoderListener listener) {
super(audioSource, context, sampleRate, sampleType, listener);
+ this.transactionQueue = new WeakReference<>(transactionQueue);
}
/**
@@ -74,8 +70,11 @@ public class AudioDecoderCompat extends BaseAudioDecoder {
try {
initMediaComponents();
decoder.start();
- mThread = new Thread(new DecoderRunnable(AudioDecoderCompat.this));
- mThread.start();
+
+ if (transactionQueue != null && transactionQueue.get() != null) {
+ AudioDecoderCompatOperation operation = new AudioDecoderCompatOperation(this);
+ transactionQueue.get().add(operation, false);
+ }
} catch (Exception e) {
e.printStackTrace();
@@ -86,84 +85,4 @@ public class AudioDecoderCompat extends BaseAudioDecoder {
stop();
}
}
-
-
- /**
- * Runnable to decode audio data
- */
- private static class DecoderRunnable implements Runnable {
- final WeakReference<AudioDecoderCompat> weakReference;
-
- /**
- * Decodes all audio data from source
- *
- * @param audioDecoderCompat instance of this class
- */
- DecoderRunnable(@NonNull AudioDecoderCompat audioDecoderCompat) {
- weakReference = new WeakReference<>(audioDecoderCompat);
-
- }
-
- @Override
- public void run() {
- final AudioDecoderCompat reference = weakReference.get();
- if (reference == null) {
- DebugTool.logWarning(TAG, "AudioDecoderCompat reference was null");
- return;
- }
- final ByteBuffer[] inputBuffersArray = reference.decoder.getInputBuffers();
- final ByteBuffer[] outputBuffersArray = reference.decoder.getOutputBuffers();
- MediaCodec.BufferInfo outputBufferInfo = new MediaCodec.BufferInfo();
- MediaCodec.BufferInfo inputBufferInfo;
- ByteBuffer inputBuffer, outputBuffer;
- SampleBuffer sampleBuffer;
-
- while (reference != null && !reference.mThread.isInterrupted()) {
- int inputBuffersArrayIndex = 0;
- while (inputBuffersArrayIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
- inputBuffersArrayIndex = reference.decoder.dequeueInputBuffer(DEQUEUE_TIMEOUT);
- if (inputBuffersArrayIndex >= 0) {
- inputBuffer = inputBuffersArray[inputBuffersArrayIndex];
- inputBufferInfo = reference.onInputBufferAvailable(reference.extractor, inputBuffer);
- reference.decoder.queueInputBuffer(inputBuffersArrayIndex, inputBufferInfo.offset, inputBufferInfo.size, inputBufferInfo.presentationTimeUs, inputBufferInfo.flags);
- }
- }
-
- int outputBuffersArrayIndex = 0;
- while (outputBuffersArrayIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
- outputBuffersArrayIndex = reference.decoder.dequeueOutputBuffer(outputBufferInfo, DEQUEUE_TIMEOUT);
- if (outputBuffersArrayIndex >= 0) {
- outputBuffer = outputBuffersArray[outputBuffersArrayIndex];
- if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0 && outputBufferInfo.size != 0) {
- reference.decoder.releaseOutputBuffer(outputBuffersArrayIndex, false);
- } else if (outputBuffer.limit() > 0) {
- sampleBuffer = reference.onOutputBufferAvailable(outputBuffer);
- if (reference.listener != null) {
- reference.listener.onAudioDataAvailable(sampleBuffer);
- }
- reference.decoder.releaseOutputBuffer(outputBuffersArrayIndex, false);
- }
- } else if (outputBuffersArrayIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
- MediaFormat newFormat = reference.decoder.getOutputFormat();
- reference.onOutputFormatChanged(newFormat);
- }
- }
-
- if (outputBufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
- if (reference.listener != null) {
- reference.listener.onDecoderFinish(true);
- }
- reference.stop();
- try {
- reference.mThread.interrupt();
- } catch (Exception e) {
- e.printStackTrace();
- } finally {
- reference.mThread = null;
- break;
- }
- }
- }
- }
- }
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompatOperation.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompatOperation.java
new file mode 100644
index 000000000..1371f8474
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioDecoderCompatOperation.java
@@ -0,0 +1,84 @@
+package com.smartdevicelink.managers.audio;
+
+import android.media.MediaCodec;
+import android.media.MediaFormat;
+
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.util.DebugTool;
+
+import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+
+class AudioDecoderCompatOperation extends Task {
+ private static final String TAG = "AudioDecoderCompatOperation";
+ private final WeakReference<AudioDecoderCompat> audioDecoderCompatWeakReference;
+ private static final int DEQUEUE_TIMEOUT = 3000;
+
+ AudioDecoderCompatOperation(AudioDecoderCompat audioDecoderCompat) {
+ super("AudioDecoderCompatOperation");
+ this.audioDecoderCompatWeakReference = new WeakReference<>(audioDecoderCompat);
+ }
+
+ @Override
+ public void onExecute() {
+ start();
+ }
+
+ private void start() {
+ if (getState() == Task.CANCELED) {
+ return;
+ }
+
+ final AudioDecoderCompat audioDecoderCompat = audioDecoderCompatWeakReference.get();
+ if (audioDecoderCompat == null) {
+ DebugTool.logWarning(TAG, "AudioDecoderCompat reference was null");
+ return;
+ }
+ final ByteBuffer[] inputBuffersArray = audioDecoderCompat.decoder.getInputBuffers();
+ final ByteBuffer[] outputBuffersArray = audioDecoderCompat.decoder.getOutputBuffers();
+ MediaCodec.BufferInfo outputBufferInfo = new MediaCodec.BufferInfo();
+ MediaCodec.BufferInfo inputBufferInfo;
+ ByteBuffer inputBuffer, outputBuffer;
+ SampleBuffer sampleBuffer;
+
+ while (true) {
+ int inputBuffersArrayIndex = 0;
+ while (inputBuffersArrayIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
+ inputBuffersArrayIndex = audioDecoderCompat.decoder.dequeueInputBuffer(DEQUEUE_TIMEOUT);
+ if (inputBuffersArrayIndex >= 0) {
+ inputBuffer = inputBuffersArray[inputBuffersArrayIndex];
+ inputBufferInfo = audioDecoderCompat.onInputBufferAvailable(audioDecoderCompat.extractor, inputBuffer);
+ audioDecoderCompat.decoder.queueInputBuffer(inputBuffersArrayIndex, inputBufferInfo.offset, inputBufferInfo.size, inputBufferInfo.presentationTimeUs, inputBufferInfo.flags);
+ }
+ }
+
+ int outputBuffersArrayIndex = 0;
+ while (outputBuffersArrayIndex != MediaCodec.INFO_TRY_AGAIN_LATER) {
+ outputBuffersArrayIndex = audioDecoderCompat.decoder.dequeueOutputBuffer(outputBufferInfo, DEQUEUE_TIMEOUT);
+ if (outputBuffersArrayIndex >= 0) {
+ outputBuffer = outputBuffersArray[outputBuffersArrayIndex];
+ if ((outputBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0 && outputBufferInfo.size != 0) {
+ audioDecoderCompat.decoder.releaseOutputBuffer(outputBuffersArrayIndex, false);
+ } else if (outputBuffer.limit() > 0) {
+ sampleBuffer = audioDecoderCompat.onOutputBufferAvailable(outputBuffer);
+ if (audioDecoderCompat.listener != null) {
+ audioDecoderCompat.listener.onAudioDataAvailable(sampleBuffer);
+ }
+ audioDecoderCompat.decoder.releaseOutputBuffer(outputBuffersArrayIndex, false);
+ }
+ } else if (outputBuffersArrayIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ MediaFormat newFormat = audioDecoderCompat.decoder.getOutputFormat();
+ audioDecoderCompat.onOutputFormatChanged(newFormat);
+ }
+ }
+
+ if (outputBufferInfo.flags == MediaCodec.BUFFER_FLAG_END_OF_STREAM) {
+ if (audioDecoderCompat.listener != null) {
+ audioDecoderCompat.listener.onDecoderFinish(true);
+ }
+ audioDecoderCompat.stop();
+ break;
+ }
+ }
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java
index bb7269e57..667522372 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java
@@ -101,6 +101,7 @@ public class AudioStreamManager extends BaseAudioStreamManager {
private StreamPacketizer audioPacketizer;
private SdlSession sdlSession = null;
private SessionType sessionType = null;
+ private com.livio.taskmaster.Queue transactionQueue;
private final Runnable serviceCompletionTimeoutCallback = new Runnable() {
@Override
@@ -437,7 +438,7 @@ public class AudioStreamManager extends BaseAudioStreamManager {
decoder = new AudioDecoder(audioSource, context.get(), sdlSampleRate, sdlSampleType, decoderListener);
} else {
// this BaseAudioDecoder subclass uses methods deprecated with api 21
- decoder = new AudioDecoderCompat(audioSource, context.get(), sdlSampleRate, sdlSampleType, decoderListener);
+ decoder = new AudioDecoderCompat(getTransactionQueue(), audioSource, context.get(), sdlSampleRate, sdlSampleType, decoderListener);
}
synchronized (queue) {
@@ -449,6 +450,13 @@ public class AudioStreamManager extends BaseAudioStreamManager {
}
}
+ private com.livio.taskmaster.Queue getTransactionQueue() {
+ if (transactionQueue == null && internalInterface != null && internalInterface.getTaskmaster() != null) {
+ transactionQueue = internalInterface.getTaskmaster().createQueue("AudioDecoderCompat", 6, false);
+ }
+ return transactionQueue;
+ }
+
/**
* Pushes raw audio data to SDL Core.
* The audio file will be played immediately. If another audio file is currently playing,
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/FileManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/FileManager.java
index 252ee09f2..0ffeba849 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/FileManager.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/FileManager.java
@@ -34,34 +34,23 @@ package com.smartdevicelink.managers.file;
import android.content.Context;
import android.content.res.Resources;
-import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.file.filetypes.SdlFile;
-import com.smartdevicelink.proxy.rpc.PutFile;
import com.smartdevicelink.util.DebugTool;
-import java.io.IOException;
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
/**
* <strong>FileManager</strong> <br>
- * <p>
* Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br>
- * <p>
- * The SDLFileManager uploads files and keeps track of all the uploaded files names during a session. <br>
- * <p>
- * We need to add the following struct: SDLFile<br>
- * <p>
- * It is broken down to these areas: <br>
- * <p>
- * 1. Getters <br>
- * 2. Deletion methods <br>
- * 3. Uploading Files / Artwork
+ * The FileManager uploads files and keeps track of all the uploaded files names during a session. <br>
*/
public class FileManager extends BaseFileManager {
@@ -81,100 +70,33 @@ public class FileManager extends BaseFileManager {
this.context = new WeakReference<>(context);
}
- /**
- * Creates and returns a PutFile request that would upload a given SdlFile
- *
- * @param file SdlFile with fileName and one of A) fileData, B) Uri, or C) resourceID set
- * @return a valid PutFile request if SdlFile contained a fileName and sufficient data
- */
@Override
- PutFile createPutFile(@NonNull final SdlFile file) {
- PutFile putFile = new PutFile();
- if (file.getName() == null) {
- throw new IllegalArgumentException("You must specify an file name in the SdlFile");
- } else {
- putFile.setSdlFileName(file.getName());
+ InputStream openInputStreamWithFile(@NonNull SdlFile file) {
+ InputStream inputStream = null;
+
+ if (context.get() == null) {
+ DebugTool.logError(TAG, "Context is null. Cannot open file input stream!");
+ return null;
}
if (file.getResourceId() > 0) {
- // Use resource id to upload file
- byte[] contents = contentsOfResource(file.getResourceId());
- if (contents != null) {
- putFile.setFileData(contents);
- } else {
- throw new IllegalArgumentException("Resource file id was empty");
+ try {
+ inputStream = context.get().getResources().openRawResource(file.getResourceId());
+ } catch (Resources.NotFoundException e) {
+ DebugTool.logError(TAG, "File cannot be found.");
}
} else if (file.getUri() != null) {
- // Use URI to upload file
- byte[] contents = contentsOfUri(file.getUri());
- if (contents != null) {
- putFile.setFileData(contents);
- } else {
- throw new IllegalArgumentException("Uri was empty");
+ try {
+ inputStream = context.get().getContentResolver().openInputStream(file.getUri());
+ } catch (FileNotFoundException e) {
+ DebugTool.logError(TAG, String.format("File at %s cannot be found.", file.getUri()));
}
} else if (file.getFileData() != null) {
- // Use file data (raw bytes) to upload file
- putFile.setFileData(file.getFileData());
+ inputStream = new ByteArrayInputStream(file.getFileData());
} else {
- throw new IllegalArgumentException("The SdlFile to upload does " +
- "not specify its resourceId, Uri, or file data");
- }
-
- if (file.getType() != null) {
- putFile.setFileType(file.getType());
- }
- putFile.setPersistentFile(file.isPersistent());
-
- return putFile;
- }
-
- /**
- * Helper method to take resource files and turn them into byte arrays
- *
- * @param resource Resource file id
- * @return Resulting byte array
- */
- private byte[] contentsOfResource(int resource) {
- InputStream is = null;
- try {
- is = context.get().getResources().openRawResource(resource);
- return contentsOfInputStream(is);
- } catch (Resources.NotFoundException e) {
- DebugTool.logError(TAG, "Can't read from resource", e);
- return null;
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
+ DebugTool.logError(TAG, "The SdlFile to upload does not specify its resourceId, Uri, or file data");
}
- }
- /**
- * Helper method to take Uri and turn it into byte array
- *
- * @param uri Uri for desired file
- * @return Resulting byte array
- */
- private byte[] contentsOfUri(Uri uri) {
- InputStream is = null;
- try {
- is = context.get().getContentResolver().openInputStream(uri);
- return contentsOfInputStream(is);
- } catch (IOException e) {
- DebugTool.logError(TAG, "Can't read from Uri", e);
- return null;
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
+ return inputStream;
}
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java
index e53ec106c..1b54b0e1f 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java
@@ -131,10 +131,11 @@ public class LifecycleManager extends BaseLifecycleManager {
* When it returns it will attempt to store the accepted parameters if available.
*
* @param isEncrypted if the service should be encrypted
+ * @param afterPendingRestart specify true when the videoService is starting again after the display has been resized
* @param parameters the desired video streaming parameters
*/
@Override
- void startVideoService(boolean isEncrypted, VideoStreamingParameters parameters) {
+ void startVideoService(boolean isEncrypted, VideoStreamingParameters parameters, boolean afterPendingRestart) {
if (session == null) {
DebugTool.logWarning(TAG, "SdlSession is not created yet.");
return;
@@ -145,7 +146,7 @@ public class LifecycleManager extends BaseLifecycleManager {
}
session.setDesiredVideoParams(parameters);
- tryStartVideoStream(isEncrypted, parameters);
+ tryStartVideoStream(isEncrypted, parameters, afterPendingRestart);
}
/**
@@ -153,9 +154,10 @@ public class LifecycleManager extends BaseLifecycleManager {
* Only information from codecs, width and height are used during video format negotiation.
*
* @param isEncrypted Specify true if packets on this service have to be encrypted
+ * @param afterPendingRestart specify true when the videoService is starting again after the display has been resized
* @param parameters VideoStreamingParameters that are desired. Does not guarantee this is what will be accepted.
*/
- private void tryStartVideoStream(boolean isEncrypted, VideoStreamingParameters parameters) {
+ private void tryStartVideoStream(boolean isEncrypted, VideoStreamingParameters parameters, boolean afterPendingRestart) {
if (session == null) {
DebugTool.logWarning(TAG, "SdlSession is not created yet.");
return;
@@ -170,7 +172,7 @@ public class LifecycleManager extends BaseLifecycleManager {
}
- if (!videoServiceStartResponseReceived || !videoServiceStartResponse //If we haven't started the service before
+ if (afterPendingRestart || !videoServiceStartResponseReceived || !videoServiceStartResponse //If we haven't started the service before
|| (videoServiceStartResponse && isEncrypted && !session.isServiceProtected(SessionType.NAV))) { //Or the service has been started but we'd like to start an encrypted one
session.setDesiredVideoParams(parameters);
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java
new file mode 100644
index 000000000..c0a5d92e2
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.screen;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.file.FileManager;
+
+/**
+ * <strong>AlertManager</strong> <br>
+ * <p>
+ * Alert manager handles uploading images and audio needed by an alert, sending an alert and cancelling an alert.<br>
+ * Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br>
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class AlertManager extends BaseAlertManager {
+
+ public AlertManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
+ super(internalInterface, fileManager);
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicManager.java
index 1c665da8c..71f5af634 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicManager.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicManager.java
@@ -56,10 +56,7 @@ class TextAndGraphicManager extends BaseTextAndGraphicManager {
@Override
SdlArtwork getBlankArtwork() {
if (blankArtwork == null) {
- blankArtwork = new SdlArtwork();
- blankArtwork.setType(FileType.GRAPHIC_PNG);
- blankArtwork.setName("blankArtwork");
- blankArtwork.setResourceId(R.drawable.transparent);
+ blankArtwork = new SdlArtwork("blankArtwork", FileType.GRAPHIC_PNG, R.drawable.transparent, true);
}
return blankArtwork;
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java
index 714005041..cce131baa 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java
@@ -48,19 +48,25 @@ import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.StreamingStateMachine;
import com.smartdevicelink.managers.lifecycle.OnSystemCapabilityListener;
+import com.smartdevicelink.managers.video.resolution.ImageResolutionKind;
+import com.smartdevicelink.managers.video.resolution.Resolution;
+import com.smartdevicelink.managers.video.resolution.VideoStreamingRange;
import com.smartdevicelink.protocol.ISdlServiceListener;
import com.smartdevicelink.protocol.ProtocolMessage;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.proxy.RPCNotification;
+import com.smartdevicelink.proxy.rpc.AppCapability;
import com.smartdevicelink.proxy.rpc.DisplayCapabilities;
import com.smartdevicelink.proxy.rpc.ImageResolution;
+import com.smartdevicelink.proxy.rpc.OnAppCapabilityUpdated;
import com.smartdevicelink.proxy.rpc.OnHMIStatus;
import com.smartdevicelink.proxy.rpc.OnTouchEvent;
import com.smartdevicelink.proxy.rpc.TouchCoord;
import com.smartdevicelink.proxy.rpc.TouchEvent;
import com.smartdevicelink.proxy.rpc.VideoStreamingCapability;
import com.smartdevicelink.proxy.rpc.VideoStreamingFormat;
+import com.smartdevicelink.proxy.rpc.enums.AppCapabilityType;
import com.smartdevicelink.proxy.rpc.enums.HMILevel;
import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows;
import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
@@ -101,10 +107,17 @@ public class VideoStreamManager extends BaseVideoStreamManager {
private OnHMIStatus currentOnHMIStatus;
private final StreamingStateMachine stateMachine;
private VideoStreamingParameters parameters;
+ private VideoStreamingCapability originalCapability;
private IVideoStreamListener streamListener;
private boolean isTransportAvailable = false;
+ private Integer majorProtocolVersion;
+ private VideoStreamingRange supportedPortraitStreamingRange;
+ private VideoStreamingRange supportedLandscapeStreamingRange;
private boolean hasStarted;
private String vehicleMake = null;
+ private boolean isEncrypted = false;
+ private boolean withPendingRestart = false;
+ private boolean wasCapabilityListenerAdded = false;
private AbstractPacketizer videoPacketizer;
// INTERNAL INTERFACES
@@ -130,6 +143,7 @@ public class VideoStreamManager extends BaseVideoStreamManager {
if (capability != null && Boolean.TRUE.equals(capability.getIsHapticSpatialDataSupported())) {
hapticManager = new HapticInterfaceManager(internalInterface);
}
+ checkState();
startEncoder();
stateMachine.transitionToState(StreamingStateMachine.STARTED);
hasStarted = true;
@@ -139,12 +153,16 @@ public class VideoStreamManager extends BaseVideoStreamManager {
@Override
public void onServiceEnded(SdlSession session, SessionType type) {
if (SessionType.NAV.equals(type)) {
- stopVideoStream();
- if (remoteDisplay != null) {
- stopStreaming();
+ if (remoteDisplay !=null){
+ stopStreaming(withPendingRestart);
}
stateMachine.transitionToState(StreamingStateMachine.NONE);
transitionToState(SETTING_UP);
+
+ if (withPendingRestart){
+ VideoStreamManager manager = VideoStreamManager.this;
+ manager.internalInterface.startVideoService(manager.getLastCachedStreamingParameters(), manager.isEncrypted, withPendingRestart);
+ }
}
}
@@ -167,8 +185,14 @@ public class VideoStreamManager extends BaseVideoStreamManager {
}
OnHMIStatus prevOnHMIStatus = currentOnHMIStatus;
currentOnHMIStatus = onHMIStatus;
- if (!HMILevel.HMI_NONE.equals(currentOnHMIStatus.getHmiLevel()) && VideoStreamManager.this.parameters == null) {
- getVideoStreamingParams();
+ if (!HMILevel.HMI_NONE.equals(currentOnHMIStatus.getHmiLevel())) {
+ if (VideoStreamManager.this.parameters == null) {
+ getVideoStreamingParams();
+ }
+ if (!wasCapabilityListenerAdded) {
+ wasCapabilityListenerAdded = true;
+ internalInterface.getSystemCapabilityManager().addOnSystemCapabilityListener(SystemCapabilityType.VIDEO_STREAMING, systemCapabilityListener);
+ }
}
checkState();
if (hasStarted && (isHMIStateVideoStreamCapable(prevOnHMIStatus)) && (!isHMIStateVideoStreamCapable(currentOnHMIStatus))) {
@@ -192,6 +216,37 @@ public class VideoStreamManager extends BaseVideoStreamManager {
}
};
+ private final OnSystemCapabilityListener systemCapabilityListener = new OnSystemCapabilityListener() {
+ @Override
+ public void onCapabilityRetrieved(Object capability) {
+ VideoStreamingParameters params = (parameters == null) ? new VideoStreamingParameters() : new VideoStreamingParameters(parameters);
+
+ VideoStreamingCapability castedCapability = ((VideoStreamingCapability)capability);
+
+ // means only scale received
+ if (castedCapability.getPreferredResolution() == null &&
+ castedCapability.getScale() != null &&
+ castedCapability.getScale() != 0 &&
+ VideoStreamManager.this.parameters != null
+ && VideoStreamManager.this.parameters.getResolution() != null) {
+ // set cached resolution
+ castedCapability.setPreferredResolution(originalCapability.getPreferredResolution());
+ }
+ params.update(castedCapability, vehicleMake);//Streaming parameters are ready time to stream
+ VideoStreamManager.this.parameters = params;
+
+ VideoStreamManager.this.withPendingRestart = true;
+
+ virtualDisplayEncoder.setStreamingParams(params);
+ stopStreaming(true);
+ }
+
+ @Override
+ public void onError(String info) {
+ DebugTool.logInfo(TAG, "onError: " + info);
+ }
+ };
+
// MANAGER APIs
@RestrictTo(RestrictTo.Scope.LIBRARY)
public VideoStreamManager(ISdl internalInterface) {
@@ -209,7 +264,6 @@ public class VideoStreamManager extends BaseVideoStreamManager {
internalInterface.addOnRPCNotificationListener(FunctionID.ON_TOUCH_EVENT, touchListener);
// Listen for HMILevel changes
internalInterface.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, hmiListener);
-
stateMachine = new StreamingStateMachine();
}
@@ -243,12 +297,12 @@ public class VideoStreamManager extends BaseVideoStreamManager {
@Override
public void onCapabilityRetrieved(Object capability) {
VideoStreamingParameters params = new VideoStreamingParameters();
- params.update((VideoStreamingCapability) capability, vehicleMake); //Streaming parameters are ready time to stream
+ VideoStreamingCapability castedCapability = ((VideoStreamingCapability)capability);
+ VideoStreamManager.this.originalCapability = castedCapability;
+ params.update(castedCapability, vehicleMake);//Streaming parameters are ready time to stream
VideoStreamManager.this.parameters = params;
-
checkState();
-
- }
+ }
@Override
public void onError(String info) {
@@ -276,6 +330,35 @@ public class VideoStreamManager extends BaseVideoStreamManager {
/**
* Starts streaming a remote display to the module if there is a connected session. This method of streaming requires the device to be on API level 19 or higher
+ * Two ranges (supportedLandscapeStreamingRange and supportedLandscapeStreamingRange) can be provided with image dimension ranges and aspect ratio ranges that your remoteDisplay class supports.
+ * If the module's screen size for your app changes during streaming (i.e. to a collapsed view, split screen, preview mode, or picture-in-picture), your remoteDisplay will be resized to the new screen size.
+ * If either range is `null`, the default is to support all streaming ranges of that format (landscape or portrait).
+ * If you wish to disable support for streaming in a given format (landscape or portrait), set a VideoStreamingRange with all `0` values.
+ *
+ * NOTE If both supportedLandscapeStreamingRange and supportedLandscapeStreamingRange are disabled then the video will not stream.
+ *
+ * Any changes to screen size will notify the onViewResized method you have implemented in your remoteDisplay class.
+ *
+ *
+ * @param context a context that can be used to create the remote display
+ * @param remoteDisplayClass class object of the remote display. This class will be used to create an instance of the remote display and will be projected to the module
+ * @param parameters streaming parameters to be used when streaming. If null is sent in, the default/optimized options will be used.
+ * If you are unsure about what parameters to be used it is best to just send null and let the system determine what
+ * works best for the currently connected module.
+ * @param encrypted a flag of if the stream should be encrypted. Only set if you have a supplied encryption library that the module can understand.
+ * @param supportedLandscapeStreamingRange constraints for vehicle display : min/max aspect ratio, min/max resolutions, max diagonal size.
+ * @param supportedPortraitStreamingRange constraints for vehicle display : min/max aspect ratio, min/max resolutions, max diagonal size.
+ */
+ public void startRemoteDisplayStream(Context context, Class<? extends SdlRemoteDisplay> remoteDisplayClass, VideoStreamingParameters parameters, final boolean encrypted, VideoStreamingRange supportedLandscapeStreamingRange, VideoStreamingRange supportedPortraitStreamingRange) {
+ configureGlobalParameters(context, remoteDisplayClass, encrypted, supportedPortraitStreamingRange, supportedLandscapeStreamingRange);
+ if(majorProtocolVersion >= 5 && !internalInterface.getSystemCapabilityManager().isCapabilitySupported(SystemCapabilityType.VIDEO_STREAMING)){
+ stateMachine.transitionToState(StreamingStateMachine.ERROR);
+ return;
+ }
+ processCapabilitiesWithPendingStart(encrypted, parameters);
+ }
+ /**
+ * Starts streaming a remote display to the module if there is a connected session. This method of streaming requires the device to be on API level 19 or higher
*
* @param context a context that can be used to create the remote display
* @param remoteDisplayClass class object of the remote display. This class will be used to create an instance of the remote display and will be projected to the module
@@ -284,48 +367,87 @@ public class VideoStreamManager extends BaseVideoStreamManager {
* works best for the currently connected module.
* @param encrypted a flag of if the stream should be encrypted. Only set if you have a supplied encryption library that the module can understand.
*/
- public void startRemoteDisplayStream(Context context, Class<? extends SdlRemoteDisplay> remoteDisplayClass, VideoStreamingParameters parameters, final boolean encrypted) {
- this.context = new WeakReference<>(context);
- this.remoteDisplayClass = remoteDisplayClass;
- int majorProtocolVersion = internalInterface.getProtocolVersion().getMajor();
+ @Deprecated
+ public void startRemoteDisplayStream(Context context, Class<? extends SdlRemoteDisplay> remoteDisplayClass, VideoStreamingParameters parameters, final boolean encrypted){
+ configureGlobalParameters(context, remoteDisplayClass, encrypted, null, null);
boolean isCapabilitySupported = internalInterface.getSystemCapabilityManager() != null && internalInterface.getSystemCapabilityManager().isCapabilitySupported(SystemCapabilityType.VIDEO_STREAMING);
if (majorProtocolVersion >= 5 && !isCapabilitySupported) {
DebugTool.logError(TAG, "Video streaming not supported on this module");
stateMachine.transitionToState(StreamingStateMachine.ERROR);
return;
}
- if (parameters == null) {
- if (majorProtocolVersion >= 5) {
- if (internalInterface.getSystemCapabilityManager() != null) {
- internalInterface.getSystemCapabilityManager().getCapability(SystemCapabilityType.VIDEO_STREAMING, new OnSystemCapabilityListener() {
- @Override
- public void onCapabilityRetrieved(Object capability) {
- VideoStreamingParameters params = new VideoStreamingParameters();
- params.update((VideoStreamingCapability) capability, vehicleMake); //Streaming parameters are ready time to stream
- startStreaming(params, encrypted);
- }
+ processCapabilitiesWithPendingStart(encrypted, parameters);
+ }
+
+ private void configureGlobalParameters(Context context, Class<? extends SdlRemoteDisplay> remoteDisplayClass, boolean encrypted, VideoStreamingRange portraitRange, VideoStreamingRange landscapeRange) {
+ this.context = new WeakReference<>(context);
+ this.remoteDisplayClass = remoteDisplayClass;
+ this.isEncrypted = encrypted;
+ this.majorProtocolVersion = internalInterface.getProtocolVersion().getMajor();
+ this.supportedPortraitStreamingRange = portraitRange;
+ this.supportedLandscapeStreamingRange = landscapeRange;
+ }
+
+
+ private void processCapabilitiesWithPendingStart(final boolean encrypted, VideoStreamingParameters parameters) {
+ final VideoStreamingParameters params = (parameters == null) ? new VideoStreamingParameters() : new VideoStreamingParameters(parameters);
+ if (majorProtocolVersion >= 5) {
+ if (internalInterface.getSystemCapabilityManager() != null) {
+ internalInterface.getSystemCapabilityManager().getCapability(SystemCapabilityType.VIDEO_STREAMING, new OnSystemCapabilityListener() {
+ @Override
+ public void onCapabilityRetrieved(Object capability) {
+ VideoStreamingCapability castedCapability = ((VideoStreamingCapability) capability);
+ VideoStreamManager.this.originalCapability = castedCapability;
+
+ params.update(castedCapability, vehicleMake); //Streaming parameters are ready time to stream
+ VideoStreamManager.this.parameters = params;
+
+ VideoStreamingCapability capabilityToSend = new VideoStreamingCapability();
+ capabilityToSend.setAdditionalVideoStreamingCapabilities(getSupportedCapabilities(castedCapability));
- @Override
- public void onError(String info) {
- stateMachine.transitionToState(StreamingStateMachine.ERROR);
- DebugTool.logError(TAG, "Error retrieving video streaming capability: " + info);
+ if (capabilityToSend.getAdditionalVideoStreamingCapabilities() == null || capabilityToSend.getAdditionalVideoStreamingCapabilities().isEmpty()) {
+ stateMachine.transitionToState(StreamingStateMachine.STOPPED);
+ DebugTool.logError(TAG, "The Video stream was not started because there were no supported video streaming capabilities, please double check that the VideoStreamRanges provided are not disabled ranges");
+ return;
}
- }, false);
- }
- } else {
- //We just use default video streaming params
- VideoStreamingParameters params = new VideoStreamingParameters();
- DisplayCapabilities dispCap = null;
- if (internalInterface.getSystemCapabilityManager() != null) {
- dispCap = (DisplayCapabilities) internalInterface.getSystemCapabilityManager().getCapability(SystemCapabilityType.DISPLAY, null, false);
- }
- if (dispCap != null) {
- params.setResolution(dispCap.getScreenParams().getImageResolution());
- }
- startStreaming(params, encrypted);
+ AppCapability appCapability = new AppCapability(AppCapabilityType.VIDEO_STREAMING);
+ appCapability.setVideoStreamingCapability(capabilityToSend);
+
+ OnAppCapabilityUpdated onAppCapabilityUpdated = new OnAppCapabilityUpdated(appCapability);
+ internalInterface.sendRPC(onAppCapabilityUpdated);
+ startStreaming(params, encrypted);
+ }
+
+ @Override
+ public void onError(String info) {
+ stateMachine.transitionToState(StreamingStateMachine.ERROR);
+ DebugTool.logError(TAG, "Error retrieving video streaming capability: " + info);
+ }
+ }, false);
}
} else {
- startStreaming(parameters, encrypted);
+ //We just use default video streaming params
+ DisplayCapabilities dispCap = null;
+ if (internalInterface.getSystemCapabilityManager() != null) {
+ dispCap = (DisplayCapabilities) internalInterface.getSystemCapabilityManager().getCapability(SystemCapabilityType.DISPLAY, null, false);
+ }
+ if (dispCap != null) {
+ params.setResolution(dispCap.getScreenParams().getImageResolution());
+ }
+
+ VideoStreamingCapability videoStreamingCapability = new VideoStreamingCapability();
+ videoStreamingCapability.setScale(params.getScale())
+ .setPreferredResolution(params.getResolution())
+ .setAdditionalVideoStreamingCapabilities(new ArrayList<VideoStreamingCapability>());
+ //Compare the default params to the ranges set by the developer
+ List<VideoStreamingCapability> vscList = getSupportedCapabilities(videoStreamingCapability);
+ //If default params not within range the video will not stream
+ if (vscList == null || vscList.isEmpty()) {
+ stateMachine.transitionToState(StreamingStateMachine.STOPPED);
+ DebugTool.logError(TAG, "The Video stream was not started because there were no supported video streaming capabilities, please double check that the VideoStreamRanges provided are not disabled ranges");
+ return;
+ }
+ startStreaming(params, encrypted);
}
}
@@ -344,7 +466,7 @@ public class VideoStreamManager extends BaseVideoStreamManager {
return;
}
//Start the video service
- this.internalInterface.startVideoService(parameters, encrypted);
+ this.internalInterface.startVideoService(parameters, encrypted, false);
}
/**
@@ -352,11 +474,16 @@ public class VideoStreamManager extends BaseVideoStreamManager {
*/
private void startEncoder() {
try {
+ if (remoteDisplay != null) {
+ remoteDisplay.resizeView(parameters.getResolution().getResolutionWidth(), parameters.getResolution().getResolutionHeight());
+ }
+
virtualDisplayEncoder.init(this.context.get(), streamListener, parameters);
//We are all set so we can start streaming at at this point
virtualDisplayEncoder.start();
//Encoder should be up and running
createRemoteDisplay(virtualDisplayEncoder.getVirtualDisplay());
+
stateMachine.transitionToState(StreamingStateMachine.STARTED);
hasStarted = true;
} catch (Exception e) {
@@ -381,6 +508,25 @@ public class VideoStreamManager extends BaseVideoStreamManager {
}
/**
+ * Stops streaming from the remote display. To restart, call
+ *
+ * @see #resumeStreaming()
+ */
+ private void stopStreaming(boolean withPendingRestart) {
+ if (remoteDisplay != null && !withPendingRestart) {
+ remoteDisplay.stop();
+ this.withPendingRestart = false;
+ }
+ if (this.isStreaming()) {
+ if (virtualDisplayEncoder != null) {
+ virtualDisplayEncoder.shutDown();
+ }
+ stateMachine.transitionToState(StreamingStateMachine.STOPPED);
+ stopVideoStream();
+ }
+ }
+
+ /**
* Resumes streaming after calling
*
* @see #startRemoteDisplayStream(android.content.Context, Class, com.smartdevicelink.streaming.video.VideoStreamingParameters, boolean)
@@ -388,10 +534,10 @@ public class VideoStreamManager extends BaseVideoStreamManager {
* @see #stopStreaming()
*/
public void resumeStreaming() {
- if (stateMachine.getState() != StreamingStateMachine.STOPPED) {
- return;
+ int currentState = stateMachine.getState();
+ if (currentState == StreamingStateMachine.STOPPED) {
+ startEncoder();
}
- startEncoder();
}
/**
@@ -431,7 +577,7 @@ public class VideoStreamManager extends BaseVideoStreamManager {
return (stateMachine.getState() == StreamingStateMachine.READY) ||
(stateMachine.getState() == StreamingStateMachine.STARTED) ||
(stateMachine.getState() == StreamingStateMachine.STOPPED);
- }
+ }
/**
* Check if video is currently streaming and visible
@@ -516,12 +662,18 @@ public class VideoStreamManager extends BaseVideoStreamManager {
createTouchScalar(resolution, displayMetrics);
}
+ VideoStreamManager.this.remoteDisplay.resizeView(parameters.getResolution().getResolutionWidth(), parameters.getResolution().getResolutionHeight());
}
@Override
public void onInvalidated(final SdlRemoteDisplay remoteDisplay) {
//Our view has been invalidated
//A good time to refresh spatial data
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ VideoStreamManager.this.remoteDisplay.getDisplay().getMetrics(displayMetrics);
+ displayMetrics.widthPixels = (int) (parameters.getResolution().getResolutionWidth() * parameters.getScale());
+ displayMetrics.heightPixels = (int) (parameters.getResolution().getResolutionHeight() * parameters.getScale());
+ createTouchScalar(parameters.getResolution(), displayMetrics);
if (hapticManager != null) {
remoteDisplay.getMainView().post(new Runnable() {
@Override
@@ -648,6 +800,149 @@ public class VideoStreamManager extends BaseVideoStreamManager {
return motionEventList;
}
+ public VideoStreamingParameters getLastCachedStreamingParameters() {
+ return parameters;
+ }
+
+ private List<VideoStreamingCapability> getSupportedCapabilities(VideoStreamingCapability rootCapability){
+
+ List<VideoStreamingCapability> validCapabilities = new ArrayList<>();
+ if (rootCapability == null){
+ return null;
+ }
+
+ List<VideoStreamingCapability> allCapabilities = new ArrayList<>();
+ List<VideoStreamingCapability> additionalCapabilities = rootCapability.getAdditionalVideoStreamingCapabilities();
+ if (additionalCapabilities != null){
+ allCapabilities.addAll(additionalCapabilities);
+ }
+ rootCapability.setAdditionalVideoStreamingCapabilities(null);
+ allCapabilities.add(rootCapability);
+
+ for (VideoStreamingCapability capability : allCapabilities) {
+ ImageResolution imageResolution = capability.getPreferredResolution();
+ boolean matches = false;
+ switch (determineResolutionType(imageResolution)) {
+ case SQUARE:
+ matches = inRange(capability, this.supportedLandscapeStreamingRange) || inRange(capability, this.supportedPortraitStreamingRange);
+ break;
+ case PORTRAIT:
+ matches = inRange(capability, this.supportedPortraitStreamingRange);
+ break;
+ case LANDSCAPE:
+ matches = inRange(capability, this.supportedLandscapeStreamingRange);
+ break;
+ default:
+ break;
+ }
+
+ if (matches) {
+ capability.setAdditionalVideoStreamingCapabilities(null);
+ if (!validCapabilities.contains(capability)) {
+ validCapabilities.add(capability);
+ }
+ }
+ }
+
+ return validCapabilities;
+ }
+
+ private Boolean inRange(VideoStreamingCapability capability, VideoStreamingRange range){
+ if (capability == null) {
+ return false;
+ }
+ if (range == null) {
+ return true;
+ }
+
+ if (isZeroRange(range)){
+ return false;
+ }
+
+ if (range.getMinimumResolution() != null || range.getMaximumResolution() != null) {
+ if (!range.isImageResolutionInRange(makeScaledImageResolution(capability))) {
+ return false;
+ }
+ }
+
+ ImageResolution resolution = capability.getPreferredResolution();
+ if (resolution != null) {
+ Double currentAspectRatio = normalizeAspectRatio(resolution);
+ if (!range.isAspectRatioInRange(currentAspectRatio)){
+ return false;
+ }
+ }
+
+ if (capability.getDiagonalScreenSize() != null) {
+ double diagonal = capability.getDiagonalScreenSize();
+ if (range.getMinimumDiagonal() != null) {
+ if (range.getMinimumDiagonal() < 0.0 || range.getMinimumDiagonal() > diagonal) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ private Double normalizeAspectRatio(ImageResolution resolution) {
+ double width = resolution.getResolutionWidth();
+ double height = resolution.getResolutionHeight();
+
+ if (width <= 0.0 || height <= 0.0) {
+ return 0.0;
+ } else if (width < height) {
+ return height/width;
+ } else if (width > height) {
+ return width/height;
+ } else {
+ return 1.0;
+ }
+ }
+
+ private ImageResolution makeScaledImageResolution(VideoStreamingCapability capability) {
+ if (capability.getScale() == null) {
+ return capability.getPreferredResolution();
+ } else {
+ double scaledWidth = (double) capability.getPreferredResolution().getResolutionWidth() / capability.getScale();
+ double scaledHeight = (double) capability.getPreferredResolution().getResolutionHeight() / capability.getScale();
+ return new ImageResolution((int) scaledWidth, (int) scaledHeight);
+ }
+ }
+
+ private Boolean isZeroRange(VideoStreamingRange range){
+ if (range == null || range.getMaximumResolution() == null || range.getMinimumResolution() == null){
+ return false;
+ }
+ return isZeroResolution(range.getMaximumResolution()) && isZeroResolution(range.getMinimumResolution());
+ }
+
+ private boolean isZeroResolution(Resolution resolution){
+ if (resolution == null) {
+ return false;
+ }
+ return resolution.getResolutionHeight() != null && resolution.getResolutionWidth() != null && resolution.getResolutionHeight() <= 0 && resolution.getResolutionWidth() <= 0;
+ }
+
+ private ImageResolutionKind determineResolutionType(ImageResolution resolution) {
+ if (resolution == null) {
+ return ImageResolutionKind.UNDEFINED;
+ }
+ if (resolution.getResolutionHeight() == null || resolution.getResolutionWidth() == null || resolution.getResolutionWidth() <= 0 || resolution.getResolutionHeight() <= 0) {
+ return ImageResolutionKind.UNDEFINED;
+ }
+ float ratio = resolution.getResolutionWidth().floatValue() / resolution.getResolutionHeight().floatValue();
+ float ratioSquared = ratio * ratio;
+ float tolerance = 0.001f;
+ if (ratioSquared < 1.0 - tolerance) {
+ return ImageResolutionKind.PORTRAIT;
+ }
+ if (ratioSquared > 1.0 + tolerance) {
+ return ImageResolutionKind.LANDSCAPE;
+ }
+ return ImageResolutionKind.SQUARE;
+ }
+
/**
* Keeps track of the current motion event for VPM
*/
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/ImageResolutionKind.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/ImageResolutionKind.java
new file mode 100644
index 000000000..55c77f15a
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/ImageResolutionKind.java
@@ -0,0 +1,8 @@
+package com.smartdevicelink.managers.video.resolution;
+
+import androidx.annotation.RestrictTo;
+
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public enum ImageResolutionKind {
+ PORTRAIT, LANDSCAPE, SQUARE, UNDEFINED
+}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/Resolution.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/Resolution.java
new file mode 100644
index 000000000..9fd1d4c9c
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/Resolution.java
@@ -0,0 +1,19 @@
+package com.smartdevicelink.managers.video.resolution;
+
+public class Resolution {
+ private Integer resolutionWidth;
+ private Integer resolutionHeight;
+
+ public Resolution(Integer resolutionWidth, Integer resolutionHeight) {
+ this.resolutionWidth = resolutionWidth;
+ this.resolutionHeight = resolutionHeight;
+ }
+
+ public Integer getResolutionWidth() {
+ return resolutionWidth;
+ }
+
+ public Integer getResolutionHeight() {
+ return resolutionHeight;
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/VideoStreamingRange.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/VideoStreamingRange.java
new file mode 100644
index 000000000..f94220eef
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/resolution/VideoStreamingRange.java
@@ -0,0 +1,125 @@
+package com.smartdevicelink.managers.video.resolution;
+
+import com.smartdevicelink.proxy.rpc.ImageResolution;
+
+public class VideoStreamingRange {
+ private Resolution minimumResolution;
+ private Resolution maximumResolution;
+ private Double minimumDiagonal;
+ private Double minimumAspectRatio;
+ private Double maximumAspectRatio;
+
+ public VideoStreamingRange(Resolution minimumResolution, Resolution maximumResolution, Double minimumDiagonal, Double minimumAspectRatio, Double maximumAspectRatio) {
+ this.minimumResolution = minimumResolution;
+ this.maximumResolution = maximumResolution;
+ this.minimumDiagonal = minimumDiagonal;
+ this.minimumAspectRatio = minimumAspectRatio;
+ this.maximumAspectRatio = maximumAspectRatio;
+ }
+
+ private VideoStreamingRange() {
+ }
+
+ public Resolution getMinimumResolution() {
+ return minimumResolution;
+ }
+
+ public Resolution getMaximumResolution() {
+ return maximumResolution;
+ }
+
+ public Double getMinimumDiagonal() {
+ return minimumDiagonal;
+ }
+
+ public Double getMinimumAspectRatio() {
+ return minimumAspectRatio;
+ }
+
+ public Double getMaximumAspectRatio() {
+ return maximumAspectRatio;
+ }
+
+ public VideoStreamingRange setMinSupportedResolution(Resolution minSupportedResolution) {
+ this.minimumResolution = minSupportedResolution;
+ return this;
+ }
+
+ public VideoStreamingRange setMaxSupportedResolution(Resolution maxSupportedResolution) {
+ this.maximumResolution = maxSupportedResolution;
+ return this;
+ }
+
+ public VideoStreamingRange setMinimumDiagonal(Double minimumDiagonal) {
+ this.minimumDiagonal = minimumDiagonal;
+ return this;
+ }
+
+ public VideoStreamingRange setMinimumAspectRatio(Double minimumAspectRatio) {
+ this.minimumAspectRatio = minimumAspectRatio;
+ return this;
+ }
+
+ public VideoStreamingRange setMaximumAspectRatio(Double maximumAspectRatio) {
+ this.maximumAspectRatio = maximumAspectRatio;
+ return this;
+ }
+
+ public Boolean isImageResolutionInRange(ImageResolution currentResolution) {
+
+ if (currentResolution == null || (currentResolution.getResolutionHeight() == null && currentResolution.getResolutionWidth() == null)) {
+ return false;
+ }
+
+ if (minimumResolution == null && maximumResolution == null) {
+ return true;
+ }
+
+ if (minimumResolution == null || (minimumResolution.getResolutionWidth() == 0 && minimumResolution.getResolutionHeight() == 0)) {
+ return false;
+ }
+
+ if (maximumResolution == null || (maximumResolution.getResolutionWidth() == 0 && maximumResolution.getResolutionHeight() == 0)) {
+ return false;
+ }
+
+ Integer constraintHeightMax = maximumResolution.getResolutionHeight();
+ Integer constraintHeightMin = minimumResolution.getResolutionHeight();
+ Integer constraintWidthMax = maximumResolution.getResolutionWidth();
+ Integer constraintWidthMin = minimumResolution.getResolutionWidth();
+ Integer resolutionHeight = currentResolution.getResolutionHeight();
+ Integer resolutionWidth = currentResolution.getResolutionWidth();
+
+ if (currentResolution.getResolutionHeight() > 0 && currentResolution.getResolutionWidth() > 0 && constraintHeightMax != null && constraintHeightMin != null) {
+ if (!(resolutionHeight >= constraintHeightMin && resolutionHeight <= constraintHeightMax)) {
+ return false;
+ }
+
+ if (!(resolutionWidth >= constraintWidthMin && resolutionWidth <= constraintWidthMax)) {
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public Boolean isAspectRatioInRange(Double aspectRatio) {
+ if (maximumAspectRatio == null && minimumAspectRatio == null) {
+ return true;
+ }
+ if (aspectRatio == null){
+ return false;
+ }
+
+ boolean isInRange = true;
+ if (minimumAspectRatio != null) {
+ isInRange = aspectRatio >= minimumAspectRatio;
+ }
+ if (isInRange && maximumAspectRatio != null) {
+ isInRange = aspectRatio <= maximumAspectRatio;
+ }
+ return isInRange;
+ }
+
+}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/session/SdlSession.java b/android/sdl_android/src/main/java/com/smartdevicelink/session/SdlSession.java
index 480f4cdbb..74701c867 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/session/SdlSession.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/session/SdlSession.java
@@ -41,12 +41,15 @@ import com.smartdevicelink.protocol.ISdlServiceListener;
import com.smartdevicelink.protocol.SdlPacket;
import com.smartdevicelink.protocol.SdlProtocol;
import com.smartdevicelink.protocol.SdlProtocolBase;
+import com.smartdevicelink.protocol.enums.ControlFrameTags;
import com.smartdevicelink.protocol.enums.SessionType;
+import com.smartdevicelink.proxy.rpc.VehicleType;
import com.smartdevicelink.transport.MultiplexTransportConfig;
import com.smartdevicelink.transport.TCPTransportConfig;
import com.smartdevicelink.transport.enums.TransportType;
import com.smartdevicelink.util.DebugTool;
import com.smartdevicelink.util.MediaStreamingStatus;
+import com.smartdevicelink.util.SystemInfo;
import com.smartdevicelink.util.Version;
import java.lang.ref.WeakReference;
@@ -152,7 +155,11 @@ public class SdlSession extends BaseSdlSession {
if (serviceType != null && serviceType.eq(SessionType.RPC) && this.sessionId == -1) {
this.sessionId = sessionID;
- this.sessionListener.onSessionStarted(sessionID, version);
+ SystemInfo systemInfo = null;
+ if (version != null && version.isNewerThan(new Version(5, 4, 0)) >= 0) {
+ systemInfo = extractSystemInfo(packet);
+ }
+ this.sessionListener.onSessionStarted(sessionID, version, systemInfo);
}
if (isEncrypted) {
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/streaming/video/SdlRemoteDisplay.java b/android/sdl_android/src/main/java/com/smartdevicelink/streaming/video/SdlRemoteDisplay.java
index f704bf769..f8c4238c0 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/streaming/video/SdlRemoteDisplay.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/streaming/video/SdlRemoteDisplay.java
@@ -42,6 +42,7 @@ import android.os.Looper;
import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
@@ -49,6 +50,10 @@ import com.smartdevicelink.util.DebugTool;
import java.lang.reflect.Constructor;
import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
/**
* SdlRemoteDisplay is an abstract class that should be extended by developers to create their remote displays.
@@ -64,7 +69,8 @@ public abstract class SdlRemoteDisplay extends Presentation {
protected Window w;
protected View mainView;
- protected final Handler handler = new Handler();
+ protected final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
+ protected ScheduledFuture<?> refreshTaskScheduledFuture;
protected final Handler uiHandler = new Handler(Looper.getMainLooper());
protected Callback callback;
@@ -88,11 +94,13 @@ public abstract class SdlRemoteDisplay extends Presentation {
}
protected void startRefreshTask() {
- handler.postDelayed(mStartRefreshTaskCallback, REFRESH_RATE_MS);
+ refreshTaskScheduledFuture = executor.scheduleAtFixedRate(mStartRefreshTaskCallback, REFRESH_RATE_MS, REFRESH_RATE_MS, TimeUnit.MILLISECONDS);
}
protected void stopRefreshTask() {
- handler.removeCallbacks(mStartRefreshTaskCallback);
+ if (refreshTaskScheduledFuture != null) {
+ refreshTaskScheduledFuture.cancel(false);
+ }
}
protected final Runnable mStartRefreshTaskCallback = new Runnable() {
@@ -103,8 +111,6 @@ public abstract class SdlRemoteDisplay extends Presentation {
if (mainView != null) {
mainView.invalidate();
}
-
- handler.postDelayed(this, REFRESH_RATE_MS);
}
};
@@ -122,6 +128,28 @@ public abstract class SdlRemoteDisplay extends Presentation {
}
}
+ public void resizeView(final int newWidth, final int newHeight) {
+ uiHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ if (mainView != null) {
+ Constructor<? extends ViewGroup.LayoutParams> ctor =
+ mainView.getLayoutParams().getClass().getDeclaredConstructor(int.class, int.class);
+ mainView.setLayoutParams(ctor.newInstance(newWidth, newHeight));
+ mainView.requestLayout();
+ invalidate();
+ onViewResized(newWidth, newHeight);
+ }
+ } catch (Exception e) {
+ DebugTool.logError(TAG, "Exception thrown during view resize", e);
+ }
+ }
+ });
+ }
+
+ public abstract void onViewResized(int width, int height);
+
public void handleMotionEvent(final MotionEvent motionEvent) {
uiHandler.post(new Runnable() {
@Override
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/util/IntegrationValidator.java b/android/sdl_android/src/main/java/com/smartdevicelink/util/IntegrationValidator.java
index d88912c62..ba15ff4aa 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/util/IntegrationValidator.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/util/IntegrationValidator.java
@@ -62,7 +62,8 @@ public class IntegrationValidator {
builder.append("-----------------------------------\n");
List<ValidationResult> results = new ArrayList<>();
- results.add(checkPermissions(context));
+ ValidationResult permissionResults = checkPermissions(context);
+ results.add(permissionResults);
if ((flags & FLAG_SKIP_ROUTER_SERVICE_CHECK) == FLAG_SKIP_ROUTER_SERVICE_CHECK) {
results.add(new ValidationResult(true, "SdlRouterService checks were not performed yet due to supplied flags"));
@@ -75,7 +76,13 @@ public class IntegrationValidator {
}
}
- results.add(checkBroadcastReceiver(context));
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q || permissionResults.successful) {
+ //This is done so that we don't provide incorrect information regarding Android 11
+ //and the required new permission that causes the broadcast receiver check to fail.
+ results.add(checkBroadcastReceiver(context));
+ } else {
+ results.add(new ValidationResult(false, "SdlBroadcastReceiver checks were not performed yet due to failing permission check"));
+ }
boolean success = true;
for (ValidationResult result : results) {
diff --git a/android/settings.gradle b/android/settings.gradle
index a358feb22..da3e5e96f 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -1 +1,2 @@
+include ':bson_java_port-release'
include ':sdl_android', ':hello_sdl_android' \ No newline at end of file
diff --git a/base/src/main/java/com/smartdevicelink/managers/AlertCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/AlertCompletionListener.java
new file mode 100644
index 000000000..7801632ba
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/AlertCompletionListener.java
@@ -0,0 +1,11 @@
+package com.smartdevicelink.managers;
+
+public interface AlertCompletionListener {
+
+ /**
+ * Returns whether an Alert operation was successful or not along with tryAgainTime
+ * @param success - Boolean that is True if Operation was a success, False otherwise.
+ * @param tryAgainTime - Amount of time (in seconds) that an app must wait before resending an alert.
+ */
+ void onComplete(boolean success, Integer tryAgainTime);
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
index 0348cc1e7..24b0d8455 100644
--- a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
@@ -32,6 +32,7 @@
package com.smartdevicelink.managers;
import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
import com.smartdevicelink.managers.file.FileManager;
import com.smartdevicelink.managers.file.FileManagerConfig;
@@ -63,6 +64,7 @@ import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
import com.smartdevicelink.security.SdlSecurityBase;
import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.SystemInfo;
import com.smartdevicelink.util.Version;
import org.json.JSONException;
@@ -77,7 +79,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
abstract class BaseSdlManager {
- static final String TAG = "BaseSubManager";
+ static final String TAG = "BaseSdlManager";
final Object STATE_LOCK = new Object();
int state = -1;
String appId, appName, shortAppName, resumeHash;
@@ -149,6 +151,15 @@ abstract class BaseSdlManager {
public void onError(LifecycleManager lifeCycleManager, String info, Exception e) {
}
+
+ @Override
+ public boolean onSystemInfoReceived(SystemInfo systemInfo) {
+ if (managerListener != null) {
+ return managerListener.onSystemInfoReceived(systemInfo);
+ } else {
+ return true;
+ }
+ }
};
// Sub manager listener
@@ -861,4 +872,9 @@ abstract class BaseSdlManager {
lifecycleManager.startRPCEncryption();
}
}
+
+ @RestrictTo(RestrictTo.Scope.TESTS)
+ void set_internalInterface(ISdl _internalInterface) {
+ this._internalInterface = _internalInterface;
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/ISdl.java b/base/src/main/java/com/smartdevicelink/managers/ISdl.java
index 6d9a3c50f..94b9f0842 100644
--- a/base/src/main/java/com/smartdevicelink/managers/ISdl.java
+++ b/base/src/main/java/com/smartdevicelink/managers/ISdl.java
@@ -4,6 +4,7 @@ import androidx.annotation.NonNull;
import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
+import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.protocol.ISdlServiceListener;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.protocol.enums.SessionType;
@@ -90,8 +91,9 @@ public interface ISdl {
*
* @param parameters desired video streaming params for this service to be started with
* @param encrypted flag to start this service with encryption or not
+ * @param withPendingRestart flag to start this service right after OnSystemCapabilityUpdated
*/
- void startVideoService(VideoStreamingParameters parameters, boolean encrypted);
+ void startVideoService(VideoStreamingParameters parameters, boolean encrypted, boolean withPendingRestart);
/**
* Starts the Audio streaming service
@@ -213,6 +215,13 @@ public interface ISdl {
Version getProtocolVersion();
/**
+ * Get the max payload size for a packet to be sent to the module
+ *
+ * @return the max transfer unit
+ */
+ long getMtu(SessionType serviceType);
+
+ /**
* Start encrypted RPC service
*/
void startRPCEncryption();
@@ -220,4 +229,6 @@ public interface ISdl {
Taskmaster getTaskmaster();
SystemCapabilityManager getSystemCapabilityManager();
+
+ PermissionManager getPermissionManager();
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/ManagerUtility.java b/base/src/main/java/com/smartdevicelink/managers/ManagerUtility.java
index b5ad249bb..6b5297e21 100644
--- a/base/src/main/java/com/smartdevicelink/managers/ManagerUtility.java
+++ b/base/src/main/java/com/smartdevicelink/managers/ManagerUtility.java
@@ -108,6 +108,41 @@ public class ManagerUtility {
}
/**
+ * Method to get number of alert textFields allowed to be set according to WindowCapability
+ *
+ * @param windowCapability WindowCapability representing the capabilities of the desired window
+ * @return linesFound Number of alert textFields found in WindowCapability
+ */
+ public static int getMaxNumberOfAlertFieldLines(final WindowCapability windowCapability) {
+ int highestFound = 0;
+ if (windowCapability != null && windowCapability.getTextFields() != null) {
+ for (TextField field : windowCapability.getTextFields()) {
+ int fieldNumber = 0;
+ if (field != null && field.getName() != null) {
+ switch (field.getName()) {
+ case alertText1:
+ fieldNumber = 1;
+ break;
+ case alertText2:
+ fieldNumber = 2;
+ break;
+ case alertText3:
+ fieldNumber = 3;
+ break;
+ }
+ }
+ if (fieldNumber > 0) {
+ highestFound = Math.max(highestFound, fieldNumber);
+ if (highestFound == 3) {
+ break;
+ }
+ }
+ }
+ }
+ return highestFound;
+ }
+
+ /**
* Method to get a list of all available text fields
*
* @return list of all available text fields with CID1SET Character Set
diff --git a/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java b/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java
index 77ba2ef1e..d0b72eb1f 100644
--- a/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java
@@ -29,64 +29,47 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
-
package com.smartdevicelink.managers.file;
-
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
+import com.livio.taskmaster.Queue;
import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.managers.file.filetypes.SdlFile;
-import com.smartdevicelink.proxy.RPCRequest;
-import com.smartdevicelink.proxy.RPCResponse;
-import com.smartdevicelink.proxy.rpc.DeleteFile;
-import com.smartdevicelink.proxy.rpc.DeleteFileResponse;
-import com.smartdevicelink.proxy.rpc.ListFiles;
-import com.smartdevicelink.proxy.rpc.ListFilesResponse;
-import com.smartdevicelink.proxy.rpc.PutFile;
-import com.smartdevicelink.proxy.rpc.PutFileResponse;
-import com.smartdevicelink.proxy.rpc.enums.FileType;
import com.smartdevicelink.proxy.rpc.enums.Result;
-import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
-import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.Version;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* <strong>FileManager</strong> <br>
- * <p>
* Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br>
- * <p>
- * The SDLFileManager uploads files and keeps track of all the uploaded files names during a session. <br>
- * <p>
- * We need to add the following struct: SDLFile<br>
- * <p>
- * It is broken down to these areas: <br>
- * <p>
- * 1. Getters <br>
- * 2. Deletion methods <br>
- * 3. Uploading Files / Artwork
+ * The FileManager uploads files and keeps track of all the uploaded files names during a session. <br>
*/
abstract class BaseFileManager extends BaseSubManager {
final static String TAG = "FileManager";
final static int SPACE_AVAILABLE_MAX_VALUE = 2000000000;
- private List<String> remoteFiles;
- private final List<String> uploadedEphemeralFileNames;
- private int bytesAvailable = SPACE_AVAILABLE_MAX_VALUE;
- private final FileManagerConfig fileManagerConfig;
- private final HashMap<String, Integer> failedFileUploadsIndex;
+
+ final Set<String> mutableRemoteFileNames;
+ private final Set<String> uploadedEphemeralFileNames;
+ private int bytesAvailable;
+ private Queue transactionQueue;
+ private HashMap<String, Integer> failedFileUploadsCount;
+ private final int maxFileUploadAttempts;
+ private final int maxArtworkUploadAttempts;
/**
* Constructor for BaseFileManager
@@ -95,23 +78,47 @@ abstract class BaseFileManager extends BaseSubManager {
* @param fileManagerConfig FileManagerConfig
*/
BaseFileManager(ISdl internalInterface, FileManagerConfig fileManagerConfig) {
-
- // setup
super(internalInterface);
- uploadedEphemeralFileNames = new ArrayList<>();
- this.fileManagerConfig = fileManagerConfig;
- failedFileUploadsIndex = new HashMap<>();
+ this.bytesAvailable = 0;
+
+ this.mutableRemoteFileNames = new HashSet<>();
+ this.transactionQueue = internalInterface.getTaskmaster().createQueue("FileManager", 5, false);
+ this.uploadedEphemeralFileNames = new HashSet<>();
+
+ this.failedFileUploadsCount = new HashMap<>();
+ this.maxFileUploadAttempts = fileManagerConfig.getFileRetryCount() + 1;
+ this.maxArtworkUploadAttempts = fileManagerConfig.getArtworkRetryCount() + 1;
}
@Override
@RestrictTo(RestrictTo.Scope.LIBRARY)
public void start(CompletionListener listener) {
- // prepare manager - don't set state to ready until we have list of files
+ // Prepare manager - don't set state to ready until we have list of files
retrieveRemoteFiles();
super.start(listener);
}
- // GETTERS
+ @Override
+ public void dispose() {
+ super.dispose();
+
+ // Cancel the operations
+ if (transactionQueue != null) {
+ transactionQueue.close();
+ transactionQueue = null;
+ }
+
+ if (mutableRemoteFileNames != null) {
+ mutableRemoteFileNames.clear();
+ }
+
+ bytesAvailable = 0;
+
+ // Clear the failed uploads tracking so failed files can be uploaded again when a new connection has been established with Core
+ if (failedFileUploadsCount != null) {
+ failedFileUploadsCount.clear();
+ }
+ }
/**
* Returns a list of file names currently residing on core
@@ -119,12 +126,7 @@ abstract class BaseFileManager extends BaseSubManager {
* @return List<String> of remote file names
*/
public List<String> getRemoteFileNames() {
- if (getState() != BaseSubManager.READY) {
- // error and don't return list
- throw new IllegalArgumentException("FileManager is not READY");
- }
- // return list (this is synchronous at this point)
- return remoteFiles;
+ return new ArrayList<>(mutableRemoteFileNames);
}
/**
@@ -133,37 +135,46 @@ abstract class BaseFileManager extends BaseSubManager {
* @return int value representing The number of bytes still available
*/
public int getBytesAvailable() {
- return bytesAvailable;
+ return this.bytesAvailable;
}
private void retrieveRemoteFiles() {
- remoteFiles = new ArrayList<>();
- // hold list in remoteFiles class var
- ListFiles listFiles = new ListFiles();
- listFiles.setOnRPCResponseListener(new OnRPCResponseListener() {
+ listRemoteFilesWithCompletionListener(new FileManagerCompletionListener() {
@Override
- public void onResponse(int correlationId, RPCResponse response) {
- ListFilesResponse listFilesResponse = (ListFilesResponse) response;
- if (listFilesResponse.getSuccess()) {
- bytesAvailable = listFilesResponse.getSpaceAvailable() != null ? listFilesResponse.getSpaceAvailable() : SPACE_AVAILABLE_MAX_VALUE;
-
- if (listFilesResponse.getFilenames() != null) {
- remoteFiles.addAll(listFilesResponse.getFilenames());
- }
- // on callback set manager to ready state
- transitionToState(BaseSubManager.READY);
- } else {
- // file list could not be received. assume that setting can work and allow SDLManager to start
- DebugTool.logError(TAG, "File Manager could not list files");
- bytesAvailable = SPACE_AVAILABLE_MAX_VALUE;
- transitionToState(BaseSubManager.READY);
+ public void onComplete(boolean success, int bytesAvailable, Collection<String> fileNames, String errorMessage) {
+ if (errorMessage != null) {
+ // HAX: In the case we are DISALLOWED we still want to transition to a ready state.
+ // Some head units return DISALLOWED for this RPC but otherwise work.
+ DebugTool.logWarning(TAG, "ListFiles is disallowed. Certain file manager APIs may not work properly.");
+ transitionToState(READY);
+ return;
}
+
+ // If no error, make sure we're in the ready state
+ transitionToState(READY);
}
});
- internalInterface.sendRPC(listFiles);
}
- // DELETION
+ private void listRemoteFilesWithCompletionListener(final FileManagerCompletionListener completionListener) {
+ ListFilesOperation operation = new ListFilesOperation(internalInterface, new FileManagerCompletionListener() {
+ @Override
+ public void onComplete(boolean success, int bytesAvailable, Collection<String> fileNames, String errorMessage) {
+ if (errorMessage != null || !success) {
+ completionListener.onComplete(success, bytesAvailable, fileNames, errorMessage);
+ return;
+ }
+
+ // If there was no error, set our properties and call back to the completion listener
+ BaseFileManager.this.mutableRemoteFileNames.addAll(fileNames);
+ BaseFileManager.this.bytesAvailable = bytesAvailable;
+
+ completionListener.onComplete(success, bytesAvailable, fileNames, errorMessage);
+ }
+ });
+
+ transactionQueue.add(operation, false);
+ }
/**
* Attempts to delete the desired file from core, calls listener with indication of success/failure
@@ -172,24 +183,37 @@ abstract class BaseFileManager extends BaseSubManager {
* @param listener callback that is called on response from core
*/
public void deleteRemoteFileWithName(@NonNull final String fileName, final CompletionListener listener) {
- DeleteFile deleteFile = new DeleteFile();
- deleteFile.setSdlFileName(fileName);
- deleteFile.setOnRPCResponseListener(new OnRPCResponseListener() {
+ deleteRemoteFileWithNamePrivate(fileName, new FileManagerCompletionListener() {
@Override
- public void onResponse(int correlationId, RPCResponse response) {
- DeleteFileResponse deleteFileResponse = (DeleteFileResponse) response;
- if (deleteFileResponse.getSuccess()) {
- bytesAvailable = deleteFileResponse.getSpaceAvailable() != null ? deleteFileResponse.getSpaceAvailable() : SPACE_AVAILABLE_MAX_VALUE;
+ public void onComplete(boolean success, int bytesAvailable, Collection<String> fileNames, String errorMessage) {
+ if (listener != null) {
+ listener.onComplete(success);
+ }
+ }
+ });
+ }
- remoteFiles.remove(fileName);
- uploadedEphemeralFileNames.remove(fileName);
+ private void deleteRemoteFileWithNamePrivate(@NonNull final String fileName, final FileManagerCompletionListener listener) {
+ if (!mutableRemoteFileNames.contains(fileName) && listener != null) {
+ String errorMessage = "No such remote file is currently known";
+ listener.onComplete(false, bytesAvailable, mutableRemoteFileNames, errorMessage);
+ return;
+ }
+
+ DeleteFileOperation operation = new DeleteFileOperation(internalInterface, fileName, new FileManagerCompletionListener() {
+ @Override
+ public void onComplete(boolean success, int bytesAvailable, Collection<String> fileNames, String errorMessage) {
+ if (success) {
+ BaseFileManager.this.bytesAvailable = bytesAvailable;
+ BaseFileManager.this.mutableRemoteFileNames.remove(fileName);
}
+
if (listener != null) {
- listener.onComplete(deleteFileResponse.getSuccess());
+ listener.onComplete(success, bytesAvailable, mutableRemoteFileNames, errorMessage);
}
}
});
- internalInterface.sendRPC(deleteFile);
+ transactionQueue.add(operation, false);
}
/**
@@ -200,117 +224,129 @@ abstract class BaseFileManager extends BaseSubManager {
*/
public void deleteRemoteFilesWithNames(@NonNull List<String> fileNames, final MultipleFileCompletionListener listener) {
if (fileNames.isEmpty()) {
- return;
+ throw new IllegalArgumentException("This request requires that the array of files not be empty");
}
- final List<DeleteFile> deleteFileRequests = new ArrayList<>();
- for (String fileName : fileNames) {
- DeleteFile deleteFile = new DeleteFile();
- deleteFile.setSdlFileName(fileName);
- deleteFileRequests.add(deleteFile);
+
+ final Map<String, String> failedDeletes = new HashMap<>();
+
+ final DispatchGroup deleteFilesTask = new DispatchGroup();
+ deleteFilesTask.enter();
+
+ for (final String name : fileNames) {
+ deleteFilesTask.enter();
+ deleteRemoteFileWithNamePrivate(name, new FileManagerCompletionListener() {
+ @Override
+ public void onComplete(boolean success, int bytesAvailable, Collection<String> fileNames, String errorMessage) {
+ if (!success) {
+ failedDeletes.put(name, errorMessage);
+ }
+ deleteFilesTask.leave();
+ }
+ });
}
- final Map<String, String> errors = new HashMap<>();
- sendMultipleFileOperations(deleteFileRequests, listener, errors);
- }
- // UPLOAD FILES / ARTWORK
+ deleteFilesTask.leave();
+
+ // Wait for all files to be deleted
+ deleteFilesTask.notify(new Runnable() {
+ @Override
+ public void run() {
+ if (listener == null) {
+ return;
+ }
+ if (failedDeletes.size() > 0) {
+ listener.onComplete(failedDeletes);
+ return;
+ }
+ listener.onComplete(null);
+ }
+ });
+ }
/**
- * Creates and returns a PutFile request that would upload a given SdlFile
+ * Check if an SdlFile has been uploaded to core
*
- * @param file SdlFile with fileName and one of A) fileData, B) Uri, or C) resourceID set
- * @return a valid PutFile request if SdlFile contained a fileName and sufficient data
+ * @param file SdlFile
+ * @return boolean that tells whether file has been uploaded to core (true) or not (false)
*/
- abstract PutFile createPutFile(@NonNull final SdlFile file);
+ public boolean hasUploadedFile(@NonNull SdlFile file) {
+ // HAX: [#827](https://github.com/smartdevicelink/sdl_ios/issues/827) Older versions of Core had a bug where list files would cache incorrectly.
+ if (file.isPersistent() && mutableRemoteFileNames != null && mutableRemoteFileNames.contains(file.getName())) {
+ // If it's a persistent file, the bug won't present itself; just check if it's on the remote system
+ return true;
+ } else if (!file.isPersistent() && mutableRemoteFileNames != null && mutableRemoteFileNames.contains(file.getName()) && uploadedEphemeralFileNames.contains(file.getName())) {
+ // If it's an ephemeral file, the bug will present itself; check that it's a remote file AND that we've uploaded it this session
+ return true;
+ }
+ return false;
+ }
/**
- * Sends list of provided requests (strictly PutFile or DeleteFile) asynchronously through internalInterface,
- * calls listener on conclusion of sending requests.
+ * Check if an SdlFile needs to be uploaded to Core or not.
+ * It is different from hasUploadedFile() because it takes isStaticIcon and overwrite properties into consideration.
+ * ie, if the file is static icon, the method always returns false.
+ * If the file is dynamic, it returns true in one of these situations:
+ * 1) the file has the overwrite property set to true
+ * 2) the file hasn't been uploaded to Core before.
*
- * @param requests Non-empty list of PutFile or DeleteFile requests
- * @param listener MultipleFileCompletionListener that is called upon conclusion of sending requests
- * @param errors a hashMap that keeps track of RPCRequest that have failed to upload and returns to developer if listener is not null
+ * @param file the SdlFile that needs to be checked
+ * @return boolean that tells whether file needs to be uploaded to Core or not
*/
- private void sendMultipleFileOperations(final List<? extends RPCRequest> requests, final MultipleFileCompletionListener listener, final Map<String, String> errors) {
- final HashMap<Integer, RPCRequest> requestMap = new HashMap<>();
- final List<RPCRequest> requestsToResend = new ArrayList<>();
- final boolean deletionOperation;
- if (requests.get(0) instanceof PutFile) {
- deletionOperation = false;
- } else if (requests.get(0) instanceof DeleteFile) {
- deletionOperation = true;
- } else {
- return;
+ public boolean fileNeedsUpload(@NonNull SdlFile file) {
+ if (file != null && !file.isStaticIcon()) {
+ return file.getOverwrite() || !hasUploadedFile(file);
}
+ return false;
+ }
- OnMultipleRequestListener onMultipleRequestListener = new OnMultipleRequestListener() {
- int fileNum = 0;
+ /**
+ * Attempts to upload a list of SdlFiles to core
+ *
+ * @param files list of SdlFiles with file name and one of A) fileData, B) Uri, or C) resourceID set
+ * @param listener callback that is called once core responds to all upload requests
+ */
+ public void uploadFiles(@NonNull List<? extends SdlFile> files, final MultipleFileCompletionListener listener) {
+ if (files.isEmpty()) {
+ throw new IllegalArgumentException("This request requires that the array of files not be empty.");
+ }
- @Override
- public void addCorrelationId(int correlationId) {
- super.addCorrelationId(correlationId);
- requestMap.put(correlationId, requests.get(fileNum++));
- }
+ final Map<String, String> failedUploads = new HashMap<>();
+ final DispatchGroup uploadFilesTask = new DispatchGroup();
+ uploadFilesTask.enter();
+ // Wait for all files to be uploaded
+ uploadFilesTask.notify(new Runnable() {
@Override
- public void onUpdate(int remainingRequests) {
- }
+ public void run() {
+ if (listener == null) {
+ return;
+ }
- @Override
- public void onFinished() {
- if (!deletionOperation) {
- if (!requestsToResend.isEmpty()) {
- sendMultipleFileOperations(requestsToResend, listener, errors);
- } else if (listener != null) {
- listener.onComplete(errors.isEmpty() ? null : errors);
- }
- } else {
- if (listener != null) {
- listener.onComplete(errors.isEmpty() ? null : errors);
- }
+ if (failedUploads.size() > 0) {
+ listener.onComplete(failedUploads);
+ return;
}
+
+ listener.onComplete(null);
}
+ });
- @Override
- public void onResponse(int correlationId, RPCResponse response) {
- if (response.getSuccess()) {
- if (response instanceof PutFileResponse) {
- PutFileResponse putFileResponse = (PutFileResponse) response;
- bytesAvailable = putFileResponse.getSpaceAvailable() != null ? putFileResponse.getSpaceAvailable() : SPACE_AVAILABLE_MAX_VALUE;
-
- PutFile putFile = ((PutFile) requestMap.get(correlationId));
- if (putFile != null) {
- remoteFiles.add(putFile.getSdlFileName());
- uploadedEphemeralFileNames.add(putFile.getSdlFileName());
- }
-
- } else if (response instanceof DeleteFileResponse) {
- DeleteFileResponse deleteFileResponse = (DeleteFileResponse) response;
- bytesAvailable = deleteFileResponse.getSpaceAvailable() != null ? deleteFileResponse.getSpaceAvailable() : SPACE_AVAILABLE_MAX_VALUE;
-
- DeleteFile deleteFile = (DeleteFile) requestMap.get(correlationId);
- if (deleteFile != null) {
- remoteFiles.remove(deleteFile.getSdlFileName());
- uploadedEphemeralFileNames.remove(deleteFile.getSdlFileName());
- }
- }
- } else {
- final RPCRequest request = requestMap.get(correlationId);
- if (request != null) {
- if (!deletionOperation) {
- if (shouldReUploadFile(((PutFile) request).getSdlFileName(), ((PutFile) request).getFileType())) {
- request.setOnRPCResponseListener(null);
- requestsToResend.add(request);
- } else {
- errors.put(((PutFile) request).getSdlFileName(), buildErrorString(response.getResultCode(), response.getInfo()));
- }
- } else {
- errors.put(((DeleteFile) request).getSdlFileName(), buildErrorString(response.getResultCode(), response.getInfo()));
- }
+ for (int i = 0; i < files.size(); i++) {
+ final SdlFile file = files.get(i);
+ uploadFilesTask.enter();
+
+ uploadFilePrivate(file, new FileManagerCompletionListener() {
+ @Override
+ public void onComplete(boolean success, int bytesAvailable, Collection<String> fileNames, String errorMessage) {
+ if (!success) {
+ failedUploads.put(file.getName(), errorMessage);
}
+
+ uploadFilesTask.leave();
}
- }
- };
- internalInterface.sendRPCs(requests, onMultipleRequestListener);
+ });
+ }
+ uploadFilesTask.leave();
}
/**
@@ -320,94 +356,101 @@ abstract class BaseFileManager extends BaseSubManager {
* @param listener called when core responds to the attempt to upload the file
*/
public void uploadFile(@NonNull final SdlFile file, final CompletionListener listener) {
- if (file.isStaticIcon()) {
- DebugTool.logWarning(TAG, String.format("%s is a static icon and doesn't need to be uploaded", file.getName()));
- listener.onComplete(true);
- return;
- }
- if (!file.getOverwrite() && hasUploadedFile(file)) {
- DebugTool.logWarning(TAG, String.format("%s has already been uploaded and the overwrite property is set to false. It will not be uploaded again", file.getName()));
- listener.onComplete(true);
- return;
- }
- PutFile putFile = createPutFile(file);
- putFile.setOnRPCResponseListener(new OnRPCResponseListener() {
+ uploadFilePrivate(file, new FileManagerCompletionListener() {
@Override
- public void onResponse(int correlationId, RPCResponse response) {
- PutFileResponse putFileResponse = (PutFileResponse) response;
- if (putFileResponse.getSuccess()) {
- bytesAvailable = putFileResponse.getSpaceAvailable() != null ? putFileResponse.getSpaceAvailable() : SPACE_AVAILABLE_MAX_VALUE;
- remoteFiles.add(file.getName());
- uploadedEphemeralFileNames.add(file.getName());
- if (listener != null) {
- listener.onComplete(true);
- }
- } else {
- if (shouldReUploadFile(file.getName(), file.getType())) {
- uploadFile(file, listener);
- } else if (listener != null) {
- listener.onComplete(false);
- }
+ public void onComplete(boolean success, int bytesAvailable, Collection<String> fileNames, String errorMessage) {
+ if (!success && errorMessage != null) {
+ DebugTool.logWarning(TAG, errorMessage);
+ }
+
+ if (listener != null) {
+ listener.onComplete(success);
}
}
});
- internalInterface.sendRPC(putFile);
}
- /**
- * Check to see if file can be re-uploaded
- *
- * @param fileName a String that represents an SdlFile's name
- * @param fileType an instances of FileType that represents a type of File
- * @return true or false depending on if file with given type and name can be re-uploaded
- */
- private boolean shouldReUploadFile(String fileName, FileType fileType) {
- if (!failedFileUploadsIndex.containsKey(fileName)) {
- if (FileType.GRAPHIC_JPEG.equals(fileType) ||
- FileType.GRAPHIC_BMP.equals(fileType) ||
- FileType.GRAPHIC_PNG.equals(fileType)) {
- failedFileUploadsIndex.put(fileName, fileManagerConfig.getArtworkRetryCount());
- } else {
- failedFileUploadsIndex.put(fileName, fileManagerConfig.getFileRetryCount());
+ private void uploadFilePrivate(@NonNull final SdlFile file, final FileManagerCompletionListener listener) {
+ if (file == null) {
+ if (listener != null) {
+ listener.onComplete(false, bytesAvailable, null, "The file upload was canceled. The data for the file is missing.");
}
+ return;
}
- Integer fileRetryValue = failedFileUploadsIndex.get(fileName);
- if (fileRetryValue != null && fileRetryValue > 0) {
- failedFileUploadsIndex.put(fileName, fileRetryValue - 1);
- return true;
- }
- return false;
- }
- /**
- * Attempts to upload a list of SdlFiles to core
- *
- * @param files list of SdlFiles with file name and one of A) fileData, B) Uri, or C) resourceID set
- * @param listener callback that is called once core responds to all upload requests
- */
- public void uploadFiles(@NonNull List<? extends SdlFile> files, final MultipleFileCompletionListener listener) {
- if (files.isEmpty()) {
+ if (file.getName() == null) {
+ if (listener != null) {
+ listener.onComplete(false, bytesAvailable, null, "You must specify an file name in the SdlFile.");
+ }
return;
}
- final List<PutFile> putFileRequests = new ArrayList<>();
- for (SdlFile file : files) {
- if (file.isStaticIcon()) {
- DebugTool.logWarning(TAG, String.format("%s is a static icon and doesn't need to be uploaded", file.getName()));
- continue;
+
+ if (file.isStaticIcon()) {
+ if (listener != null) {
+ listener.onComplete(false, bytesAvailable, null, "The file upload was canceled. The file is a static icon, which cannot be uploaded.");
}
- if (!file.getOverwrite() && hasUploadedFile(file)) {
- DebugTool.logWarning(TAG, String.format("%s has already been uploaded and the overwrite property is set to false. It will not be uploaded again", file.getName()));
- continue;
+ return;
+ }
+
+ if (getState() != READY) {
+ if (listener != null) {
+ listener.onComplete(false, bytesAvailable, null, "The file manager was unable to send this file. This could be because the file manager has not started, or the head unit does not support files.");
}
- putFileRequests.add(createPutFile(file));
+ return;
}
- // if all files are static icons we complete listener with no errors
- if (putFileRequests.isEmpty()) {
- listener.onComplete(null);
- } else {
- final Map<String, String> errors = new HashMap<>();
- sendMultipleFileOperations(putFileRequests, listener, errors);
+
+ // HAX: [#827](https://github.com/smartdevicelink/sdl_ios/issues/827) Older versions of Core
+ // had a bug where list files would cache incorrectly. This led to attempted uploads failing
+ // due to the system thinking they were already there when they were not. This is only needed
+ // if connecting to Core v4.3.1 or less which corresponds to RPC v4.3.1 or less
+ Version rpcVersion = new Version(internalInterface.getSdlMsgVersion());
+ if (!file.isPersistent() && !hasUploadedFile(file) && new Version(4, 4, 0).isNewerThan(rpcVersion) == 1) {
+ file.setOverwrite(true);
}
+
+ // Check our overwrite settings and error out if it would overwrite
+ if (!file.getOverwrite() && mutableRemoteFileNames.contains(file.getName())) {
+ String errorMessage = "Cannot overwrite remote file. The remote file system already has a file of this name, and the file manager is set to not automatically overwrite files.";
+ DebugTool.logWarning(TAG, errorMessage);
+ if (listener != null) {
+ listener.onComplete(true, bytesAvailable, null, errorMessage);
+ }
+ return;
+ }
+
+ // If we didn't error out over the overwrite, then continue on
+ sdl_uploadFilePrivate(file, listener);
+ }
+
+ private void sdl_uploadFilePrivate(@NonNull final SdlFile file, final FileManagerCompletionListener listener) {
+ final String fileName = file.getName();
+
+ SdlFileWrapper fileWrapper = new SdlFileWrapper(file, new FileManagerCompletionListener() {
+ @Override
+ public void onComplete(boolean success, int bytesAvailable, Collection<String> fileNames, String errorMessage) {
+ if (success) {
+ BaseFileManager.this.bytesAvailable = bytesAvailable;
+ BaseFileManager.this.mutableRemoteFileNames.add(fileName);
+ BaseFileManager.this.uploadedEphemeralFileNames.add(fileName);
+ } else {
+ incrementFailedUploadCountForFileName(file.getName(), BaseFileManager.this.failedFileUploadsCount);
+
+ int maxUploadCount = file instanceof SdlArtwork ? maxArtworkUploadAttempts : maxFileUploadAttempts;
+ if (canFileBeUploadedAgain(file, maxUploadCount, failedFileUploadsCount)) {
+ DebugTool.logInfo(TAG, String.format("Attempting to resend file with name %s after a failed upload attempt", file.getName()));
+ sdl_uploadFilePrivate(file, listener);
+ return;
+ }
+ }
+
+ if (listener != null) {
+ listener.onComplete(success, bytesAvailable, null, errorMessage);
+ }
+ }
+ });
+
+ UploadFileOperation operation = new UploadFileOperation(internalInterface, this, fileWrapper);
+ transactionQueue.add(operation, false);
}
/**
@@ -431,22 +474,60 @@ abstract class BaseFileManager extends BaseSubManager {
}
/**
- * Check if an SdlFile has been uploaded to core
+ * Checks if an artwork needs to be uploaded to Core. The artwork should not be sent to Core if
+ * the artwork is already on Core or if the artwork is not on Core after the maximum number of
+ * repeated upload attempts has been reached.
*
- * @param file SdlFile
- * @return boolean that tells whether file has been uploaded to core (true) or not (false)
+ * @param file The file to be uploaded to Core
+ * @param maxUploadCount The max number of times the file is allowed to be uploaded to Core
+ * @param failedFileUploadsCount
+ * @return True if the file still needs to be (re)sent to Core; false if not.
*/
- public boolean hasUploadedFile(@NonNull SdlFile file) {
- if (file.isPersistent() && remoteFiles != null && remoteFiles.contains(file.getName())) {
- return true;
- } else if (!file.isPersistent() && remoteFiles != null && remoteFiles.contains(file.getName())
- && uploadedEphemeralFileNames.contains(file.getName())) {
- return true;
+ private boolean canFileBeUploadedAgain(SdlFile file, int maxUploadCount, HashMap<String, Integer> failedFileUploadsCount) {
+ if (getState() != READY) {
+ DebugTool.logWarning(TAG, String.format("File named %s failed to upload. The file manager has shutdown so the file upload will not retry.", file.getName()));
+ return false;
}
- return false;
+
+ if (file == null) {
+ DebugTool.logError(TAG, "File can not be uploaded because it is not a valid file.");
+ return false;
+ }
+
+ if (hasUploadedFile(file)) {
+ DebugTool.logInfo(TAG, String.format("File named %s has already been uploaded.", file.getName()));
+ return false;
+ }
+
+ Integer failedUploadCount = failedFileUploadsCount.get(file.getName());
+ boolean canFileBeUploadedAgain = (failedUploadCount == null) || (failedUploadCount < maxUploadCount);
+ if (!canFileBeUploadedAgain) {
+ DebugTool.logError(TAG, String.format("File named %s failed to upload. Max number of upload attempts reached.", file.getName()));
+ }
+
+ return canFileBeUploadedAgain;
}
- // HELPERS
+ /**
+ * Increments the number of upload attempts for a file name by 1.
+ *
+ * @param name The name used to upload the file to Core
+ * @param failedFileUploadsCount
+ * @return
+ */
+ private void incrementFailedUploadCountForFileName(String name, HashMap<String, Integer> failedFileUploadsCount) {
+ Integer currentFailedUploadCount = failedFileUploadsCount.get(name);
+ Integer newFailedUploadCount = (currentFailedUploadCount != null) ? (currentFailedUploadCount + 1) : 1;
+ failedFileUploadsCount.put(name, newFailedUploadCount);
+ DebugTool.logWarning(TAG, String.format("File with name %s failed to upload %s times", name, newFailedUploadCount));
+ }
+
+ /**
+ * Opens a socket for reading data.
+ *
+ * @param file The file containing the data or the file url of the data
+ */
+ abstract InputStream openInputStreamWithFile(@NonNull SdlFile file);
/**
* Builds an error string for a given Result and info string
@@ -455,33 +536,8 @@ abstract class BaseFileManager extends BaseSubManager {
* @param info String returned from OnRPCRequestListener.onError()
* @return Error string
*/
+ @Deprecated
static public String buildErrorString(Result resultCode, String info) {
return resultCode.toString() + " : " + info;
}
-
- /**
- * Helper method to take InputStream and turn it into byte array
- *
- * @param is valid InputStream
- * @return Resulting byte array
- */
- byte[] contentsOfInputStream(InputStream is) {
- if (is == null) {
- return null;
- }
- try {
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- final int bufferSize = 4096;
- final byte[] buffer = new byte[bufferSize];
- int available;
- while ((available = is.read(buffer)) >= 0) {
- os.write(buffer, 0, available);
- }
- return os.toByteArray();
- } catch (IOException e) {
- DebugTool.logError(TAG, "Can't read from InputStream", e);
- return null;
- }
- }
-
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/file/DeleteFileOperation.java b/base/src/main/java/com/smartdevicelink/managers/file/DeleteFileOperation.java
new file mode 100644
index 000000000..21df7f7c4
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/file/DeleteFileOperation.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.file;
+
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.rpc.DeleteFile;
+import com.smartdevicelink.proxy.rpc.DeleteFileResponse;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * Created by Bilal Alsharifi on 12/1/20.
+ */
+class DeleteFileOperation extends Task {
+ private static final String TAG = "DeleteFileOperation";
+ private final WeakReference<ISdl> internalInterface;
+ private String fileName;
+ private FileManagerCompletionListener completionListener;
+
+ DeleteFileOperation(ISdl internalInterface, String fileName, FileManagerCompletionListener completionListener) {
+ super("DeleteFileOperation");
+ this.internalInterface = new WeakReference<>(internalInterface);
+ this.fileName = fileName;
+ this.completionListener = completionListener;
+ }
+
+ @Override
+ public void onExecute() {
+ start();
+ }
+
+ private void start() {
+ if (getState() == Task.CANCELED) {
+ return;
+ }
+
+ deleteFile();
+ }
+
+ private void deleteFile() {
+ DeleteFile deleteFile = new DeleteFile(fileName);
+ deleteFile.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ DeleteFileResponse deleteFileResponse = (DeleteFileResponse) response;
+ boolean success = deleteFileResponse.getSuccess();
+
+ // If spaceAvailable is null, set it to the max value
+ int bytesAvailable = deleteFileResponse.getSpaceAvailable() != null ? deleteFileResponse.getSpaceAvailable() : BaseFileManager.SPACE_AVAILABLE_MAX_VALUE;
+
+ if (completionListener != null) {
+ String errorMessage = success ? null : response.getInfo() + ": " + response.getResultCode();
+ completionListener.onComplete(success, bytesAvailable, null, errorMessage);
+ }
+
+ onFinished();
+ }
+ });
+
+ if (internalInterface.get() != null) {
+ internalInterface.get().sendRPC(deleteFile);
+ }
+ }
+
+ @Override
+ public String getName() {
+ return super.getName() + " - " + fileName;
+ }
+}
+
diff --git a/base/src/main/java/com/smartdevicelink/managers/file/DispatchGroup.java b/base/src/main/java/com/smartdevicelink/managers/file/DispatchGroup.java
new file mode 100644
index 000000000..307a4bac4
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/file/DispatchGroup.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.file;
+
+/**
+ * Created by Bilal Alsharifi on 12/2/20.
+ */
+class DispatchGroup {
+ private int count;
+ private Runnable runnable;
+
+ DispatchGroup() {
+ count = 0;
+ }
+
+ synchronized void enter() {
+ count++;
+ }
+
+ synchronized void leave() {
+ count--;
+ run();
+ }
+
+ void notify(Runnable runnable) {
+ this.runnable = runnable;
+ run();
+ }
+
+ private void run() {
+ if (count <= 0 && runnable != null) {
+ runnable.run();
+ }
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/file/FileManagerCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/file/FileManagerCompletionListener.java
new file mode 100644
index 000000000..db75aed6b
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/file/FileManagerCompletionListener.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.file;
+
+import java.util.Collection;
+
+/**
+ * Created by Bilal Alsharifi on 12/1/20.
+ */
+interface FileManagerCompletionListener {
+ void onComplete(boolean success, int bytesAvailable, Collection<String> fileNames, String errorMessage);
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/file/ListFilesOperation.java b/base/src/main/java/com/smartdevicelink/managers/file/ListFilesOperation.java
new file mode 100644
index 000000000..794870459
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/file/ListFilesOperation.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.file;
+
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.rpc.ListFiles;
+import com.smartdevicelink.proxy.rpc.ListFilesResponse;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+/**
+ * Created by Bilal Alsharifi on 12/1/20.
+ */
+class ListFilesOperation extends Task {
+ private static final String TAG = "ListFilesOperation";
+ private UUID operationId;
+ private final WeakReference<ISdl> internalInterface;
+ private FileManagerCompletionListener completionListener;
+
+ ListFilesOperation(ISdl internalInterface, FileManagerCompletionListener completionListener) {
+ super("ListFilesOperation");
+ this.operationId = UUID.randomUUID();
+ this.internalInterface = new WeakReference<>(internalInterface);
+ this.completionListener = completionListener;
+ }
+
+ @Override
+ public void onExecute() {
+ start();
+ }
+
+ private void start() {
+ if (getState() == Task.CANCELED) {
+ return;
+ }
+
+ listFiles();
+ }
+
+ private void listFiles() {
+ ListFiles listFiles = new ListFiles();
+ listFiles.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ ListFilesResponse listFilesResponse = (ListFilesResponse) response;
+ boolean success = listFilesResponse.getSuccess();
+
+ List<String> fileNames = new ArrayList<>();
+ if (listFilesResponse.getFilenames() != null) {
+ fileNames.addAll(listFilesResponse.getFilenames());
+ }
+
+ // If spaceAvailable is null, set it to the max value
+ int bytesAvailable = listFilesResponse.getSpaceAvailable() != null ? listFilesResponse.getSpaceAvailable() : BaseFileManager.SPACE_AVAILABLE_MAX_VALUE;
+
+ if (completionListener != null) {
+ String errorMessage = success ? null : response.getInfo() + ": " + response.getResultCode();
+ completionListener.onComplete(success, bytesAvailable, fileNames, errorMessage);
+ }
+
+ onFinished();
+ }
+ });
+
+ if (internalInterface.get() != null) {
+ internalInterface.get().sendRPC(listFiles);
+ }
+ }
+
+ @Override
+ public String getName() {
+ return super.getName() + " - " + operationId;
+ }
+}
+
diff --git a/base/src/main/java/com/smartdevicelink/managers/file/SdlFileWrapper.java b/base/src/main/java/com/smartdevicelink/managers/file/SdlFileWrapper.java
new file mode 100644
index 000000000..bdb7b9a62
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/file/SdlFileWrapper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.file;
+
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+
+/**
+ * Created by Bilal Alsharifi on 12/1/20.
+ */
+class SdlFileWrapper {
+ private final SdlFile file;
+ private final FileManagerCompletionListener completionListener;
+
+ SdlFileWrapper(SdlFile file, FileManagerCompletionListener completionListener) {
+ this.file = file;
+ this.completionListener = completionListener;
+ }
+
+ SdlFile getFile() {
+ return file;
+ }
+
+ FileManagerCompletionListener getCompletionListener() {
+ return completionListener;
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java b/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java
new file mode 100644
index 000000000..820311a58
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.file;
+
+import androidx.annotation.NonNull;
+
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.protocol.SdlProtocolBase;
+import com.smartdevicelink.protocol.enums.SessionType;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.rpc.PutFile;
+import com.smartdevicelink.proxy.rpc.PutFileResponse;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+
+/**
+ * Created by Bilal Alsharifi on 12/1/20.
+ */
+class UploadFileOperation extends Task {
+ private static final String TAG = "UploadFileOperation";
+ private final WeakReference<ISdl> internalInterface;
+ private final WeakReference<BaseFileManager> fileManager;
+ private SdlFileWrapper fileWrapper;
+ private InputStream inputStream;
+ private int fileSize;
+ private String streamError;
+ private int bytesAvailable;
+ private int highestCorrelationIDReceived;
+
+ UploadFileOperation(ISdl internalInterface, BaseFileManager fileManager, SdlFileWrapper fileWrapper) {
+ super("UploadFileOperation");
+
+ this.internalInterface = new WeakReference<>(internalInterface);
+ this.fileManager = new WeakReference<>(fileManager);
+ this.fileWrapper = fileWrapper;
+ }
+
+ @Override
+ public void onExecute() {
+ start();
+ }
+
+ private void start() {
+ if (getState() == Task.CANCELED) {
+ return;
+ }
+
+ int mtuSize = 0;
+ if (internalInterface.get() != null) {
+ mtuSize = (int) internalInterface.get().getMtu(SessionType.RPC);
+ }
+ sendFile(this.fileWrapper.getFile(), mtuSize, this.fileWrapper.getCompletionListener());
+ }
+
+ /**
+ * Sends data asynchronously to the SDL Core by breaking the data into smaller packets, each of which is
+ * sent via a PutFile. If the SDL Core receives all the PutFile successfully, a success response with
+ * the amount of free storage space left on the SDL Core is returned. Otherwise the error returned by
+ * the SDL Core is passed along.
+ *
+ * @param file The file containing the data to be sent to the SDL Core
+ * @param mtuSize The maximum packet size allowed
+ * @param completionListener listener returning whether or not the upload was a success
+ */
+ private void sendFile(SdlFile file, int mtuSize, final FileManagerCompletionListener completionListener) {
+ streamError = null;
+ bytesAvailable = BaseFileManager.SPACE_AVAILABLE_MAX_VALUE;
+ highestCorrelationIDReceived = -1;
+
+ if (getState() == Task.CANCELED) {
+ String errorMessage = "The file upload transaction was canceled before it could be completed.";
+ completionListener.onComplete(false, bytesAvailable, null, errorMessage);
+ onFinished();
+ return;
+ }
+
+ if (file == null) {
+ String errorMessage = "The file manager was unable to send the file. This could be because the file does not exist at the specified file path or that passed data is invalid.";
+ completionListener.onComplete(false, bytesAvailable, null, errorMessage);
+ onFinished();
+ return;
+ }
+
+ if (fileManager.get() != null) {
+ this.inputStream = fileManager.get().openInputStreamWithFile(file);
+ this.fileSize = getFileSizeFromInputStream(inputStream);
+ }
+
+ int maxBulkDataSize = getMaxBulkDataSize(mtuSize, file, fileSize);
+
+ // If the file does not exist or the passed data is null, return an error
+ if (inputStream == null || fileSize == 0) {
+ closeInputStream();
+
+ String errorMessage = "The file manager was unable to send the file. This could be because the file does not exist at the specified file path or that passed data is invalid.";
+ completionListener.onComplete(false, bytesAvailable, null, errorMessage);
+ onFinished();
+ return;
+ }
+
+ final DispatchGroup putFileGroup = new DispatchGroup();
+ putFileGroup.enter();
+
+ // Wait for all packets be sent before returning whether or not the upload was a success
+ putFileGroup.notify(new Runnable() {
+ @Override
+ public void run() {
+ closeInputStream();
+
+ if (streamError != null || getState() == Task.CANCELED) {
+ completionListener.onComplete(false, bytesAvailable, null, streamError);
+ } else {
+ completionListener.onComplete(true, bytesAvailable, null, null);
+ }
+
+ onFinished();
+ }
+ });
+
+ // Break the data into small pieces, each of which will be sent in a separate PutFile
+ int currentOffset = 0;
+ int numberOfPieces = ((fileSize - 1) / maxBulkDataSize) + 1;
+ for (int i = 0; i < numberOfPieces; i++) {
+ putFileGroup.enter();
+
+ // Get a chunk of data from the input stream
+ int putFileLength = getPutFileLengthForOffset(currentOffset, fileSize, maxBulkDataSize);
+ int putFileBulkDataSize = getDataSizeForOffset(currentOffset, fileSize, maxBulkDataSize);
+ byte[] putFileBulkData = getDataChunkWithSize(putFileBulkDataSize, this.inputStream);
+
+ final PutFile putFile = new PutFile(file.getName(), file.getType())
+ .setPersistentFile(file.isPersistent())
+ .setSystemFile(false)
+ .setOffset(currentOffset)
+ .setLength(putFileLength)
+ .setCRC(file.getFileData());
+ putFile.setBulkData(putFileBulkData);
+ putFile.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ PutFileResponse putFileResponse = (PutFileResponse) response;
+
+ // Check if the upload process has been cancelled by another packet. If so, stop the upload process.
+ if (getState() == Task.CANCELED) {
+ putFileGroup.leave();
+ return;
+ }
+
+ // If the SDL Core returned an error, cancel the upload the process in the future
+ if (!response.getSuccess() || getState() == Task.CANCELED) {
+ streamError = response.getInfo() + ": " + response.getResultCode();
+ putFileGroup.leave();
+ cancelTask();
+ return;
+ }
+
+ // If no errors, watch for a response containing the amount of storage left on the SDL Core
+ if (newHighestCorrelationID(correlationId, highestCorrelationIDReceived)) {
+ highestCorrelationIDReceived = correlationId;
+
+ // If spaceAvailable is null, set it to the max value
+ bytesAvailable = putFileResponse.getSpaceAvailable() != null ? putFileResponse.getSpaceAvailable() : BaseFileManager.SPACE_AVAILABLE_MAX_VALUE;
+ }
+
+ putFileGroup.leave();
+ }
+ });
+
+ currentOffset += putFileBulkDataSize;
+
+ if (internalInterface.get() != null) {
+ internalInterface.get().sendRPC(putFile);
+ }
+ }
+
+ putFileGroup.leave();
+ }
+
+ /**
+ * Close the input stream once all the data has been read
+ */
+ private void closeInputStream() {
+ if (this.inputStream == null) {
+ return;
+ }
+ try {
+ this.inputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Returns the max possible size for the JSON data in each of the PutFile pieces.
+ *
+ * @param file The file containing the data to be sent to the SDL Core
+ * @param fileSize The size of the file
+ * @return max possible size for the JSON data
+ */
+ private int getMaxJSONSize(@NonNull SdlFile file, int fileSize) {
+ int maxJSONSize = 0;
+
+ final PutFile putFile = new PutFile(file.getName(), file.getType())
+ .setPersistentFile(file.isPersistent())
+ .setSystemFile(false)
+ .setOffset(fileSize)
+ .setLength(fileSize)
+ .setCRC(file.getFileData());
+
+ if (putFile != null && putFile.getStore() != null) {
+ maxJSONSize = putFile.getStore().toString().getBytes().length;
+ }
+ return maxJSONSize;
+ }
+
+ /**
+ * Returns the max size of bulk data that we can load into each PutFile to guarantee that the
+ * packet size do not exceed the max MTU size allowed by the SDL Core.
+ *
+ * @param mtuSize The maximum packet size allowed
+ * @param file The file containing the data to be sent to the SDL Core
+ * @param fileSize The size of the file
+ * @return max size of bulk data that we can load into each PutFile
+ */
+ private int getMaxBulkDataSize(int mtuSize, @NonNull SdlFile file, int fileSize) {
+ // Each RPC packet contains : frame header + payload (binary header + JSON data + bulk data)
+ // To make sure that packets do not exceed MTU size, the bulk data size for each packet should not exceed:
+ // mtuSize - (frameHeaderSize + binaryHeaderSize + maxJSONSize)
+
+ int frameHeaderSize = SdlProtocolBase.V2_HEADER_SIZE;
+ int binaryHeaderSize = 12;
+ int maxJSONSize = getMaxJSONSize(file, fileSize);
+ return mtuSize - (frameHeaderSize + binaryHeaderSize + maxJSONSize);
+ }
+
+ /**
+ * Returns the length of the data being sent in the PutFile. The first PutFile's length is unique in
+ * that it sends the full size of the data. For the rest of the PutFiles, the length parameter is equal
+ * to the size of the chunk of data being sent in the PutFile.
+ *
+ * @param currentOffset The current position in the file
+ * @param fileSize The size of the file
+ * @param maxBulkDataSize The max size of bulk data that we can load into each PutFile
+ * @return The length of the data being sent in the PutFile
+ */
+ private int getPutFileLengthForOffset(int currentOffset, int fileSize, int maxBulkDataSize) {
+ int putFileLength;
+ if (currentOffset == 0) {
+ // The first PutFile sends the full file size
+ putFileLength = fileSize;
+ } else if ((fileSize - currentOffset) < maxBulkDataSize) {
+ // The last PutFile sends the size of the remaining data
+ putFileLength = fileSize - currentOffset;
+ } else {
+ // All other PutFiles send the maximum bulk data size
+ putFileLength = maxBulkDataSize;
+ }
+ return putFileLength;
+ }
+
+ /**
+ * Gets the size of the data to be sent in a packet.
+ * Packet size can not be greater than the max MTU size allowed by the SDL Core.
+ *
+ * @param currentOffset The position in the file where to start reading data
+ * @param fileSize he size of the file
+ * @param maxBulkDataSize The max size of bulk data that we can load into each PutFile
+ * @return The size of the data to be sent in the packet.
+ */
+ private int getDataSizeForOffset(int currentOffset, int fileSize, int maxBulkDataSize) {
+ int dataSize;
+ int fileSizeRemaining = fileSize - currentOffset;
+ if (fileSizeRemaining < maxBulkDataSize) {
+ dataSize = fileSizeRemaining;
+ } else {
+ dataSize = maxBulkDataSize;
+ }
+ return dataSize;
+ }
+
+ /**
+ * Reads a chunk of data from input stream.
+ *
+ * @param size The amount of data to read from the input stream
+ * @param inputStream The stream from which to read the data
+ * @return The data read from the socket
+ */
+ private byte[] getDataChunkWithSize(int size, InputStream inputStream) {
+ if (size < 0) {
+ return null;
+ }
+
+ int bytesRead = 0;
+ byte[] buffer = new byte[size];
+ try {
+ bytesRead = inputStream.read(buffer, 0, size);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ if (bytesRead > 0) {
+ return buffer;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * One of the responses returned by the SDL Core will contain the correct remaining free storage
+ * size on the SDL Core. Since communication with the SDL Core is asynchronous, there is no way
+ * to predict which response contains the correct bytes available other than to watch for the
+ * largest correlation id, since that will be the last response sent by the SDL Core.
+ *
+ * @param correlationID The correlationID for the newest response returned by the SDL Core for a PutFile
+ * @param highestCorrelationIDReceived The largest currently received correlation id
+ * @return Whether or not the newest request contains the highest correlationId
+ */
+ private boolean newHighestCorrelationID(int correlationID, int highestCorrelationIDReceived) {
+ return correlationID > highestCorrelationIDReceived;
+ }
+
+ /**
+ * Gets the size of the data.
+ *
+ * @return The size of the data.
+ */
+ private int getFileSizeFromInputStream(InputStream inputStream) {
+ int size = 0;
+ if (inputStream != null) {
+ try {
+ size = inputStream.available();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return size;
+ }
+
+ @Override
+ public String getName() {
+ return super.getName() + " - " + fileWrapper.getFile().getName();
+ }
+}
+
diff --git a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java
index 8a8487178..7073ad7ba 100644
--- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java
@@ -40,6 +40,7 @@ import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.SdlManager;
import com.smartdevicelink.managers.ServiceEncryptionListener;
+import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.marshal.JsonRPCMarshaller;
import com.smartdevicelink.protocol.ISdlServiceListener;
import com.smartdevicelink.protocol.ProtocolMessage;
@@ -87,6 +88,7 @@ import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.util.CorrelationIdGenerator;
import com.smartdevicelink.util.DebugTool;
import com.smartdevicelink.util.FileUtls;
+import com.smartdevicelink.util.SystemInfo;
import com.smartdevicelink.util.Version;
import java.util.HashMap;
@@ -97,7 +99,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
abstract class BaseLifecycleManager {
static final String TAG = "Lifecycle Manager";
- public static final Version MAX_SUPPORTED_RPC_VERSION = new Version(7, 0, 0);
+ public static final Version MAX_SUPPORTED_RPC_VERSION = new Version(7, 1, 0);
// Protected Correlation IDs
private final int REGISTER_APP_INTERFACE_CORRELATION_ID = 65529,
@@ -128,6 +130,7 @@ abstract class BaseLifecycleManager {
final Version minimumRPCVersion;
BaseTransportConfig _transportConfig;
private Taskmaster taskmaster;
+ private boolean didCheckSystemInfo = false;
BaseLifecycleManager(AppConfig appConfig, BaseTransportConfig config, LifecycleListener listener) {
this.appConfig = appConfig;
@@ -169,7 +172,12 @@ abstract class BaseLifecycleManager {
Taskmaster getTaskmaster() {
if (taskmaster == null) {
Taskmaster.Builder builder = new Taskmaster.Builder();
- builder.setThreadCount(2);
+ int threadCount = 2;
+ // Give NAVIGATION & PROJECTION apps an extra thread to handle audio/video streaming operations
+ if (appConfig != null && appConfig.appType != null && (appConfig.appType.contains(AppHMIType.NAVIGATION) || appConfig.appType.contains(AppHMIType.PROJECTION))) {
+ threadCount = 3;
+ }
+ builder.setThreadCount(threadCount);
builder.shouldBeDaemon(true);
taskmaster = builder.build();
taskmaster.start();
@@ -346,6 +354,10 @@ abstract class BaseLifecycleManager {
addRpcListener(FunctionID.ON_SYSTEM_REQUEST, rpcListener);
addRpcListener(FunctionID.ON_APP_INTERFACE_UNREGISTERED, rpcListener);
addRpcListener(FunctionID.UNREGISTER_APP_INTERFACE, rpcListener);
+
+ /* These are legacy and are necessary for older systems */
+ addRpcListener(FunctionID.ON_SYNC_P_DATA, rpcListener);
+ addRpcListener(FunctionID.ON_ENCODED_SYNC_P_DATA, rpcListener);
}
private final OnRPCListener rpcListener = new OnRPCListener() {
@@ -358,7 +370,7 @@ abstract class BaseLifecycleManager {
case REGISTER_APP_INTERFACE:
//We have begun
DebugTool.logInfo(TAG, "RAI Response");
- raiResponse = (RegisterAppInterfaceResponse) message;
+ BaseLifecycleManager.this.raiResponse = (RegisterAppInterfaceResponse) message;
SdlMsgVersion rpcVersion = ((RegisterAppInterfaceResponse) message).getSdlMsgVersion();
if (rpcVersion != null) {
BaseLifecycleManager.this.rpcSpecVersion = new Version(rpcVersion.getMajorVersion(), rpcVersion.getMinorVersion(), rpcVersion.getPatchVersion());
@@ -373,7 +385,25 @@ abstract class BaseLifecycleManager {
clean();
return;
}
- processRaiResponse(raiResponse);
+ if (!didCheckSystemInfo && lifecycleListener != null) {
+ didCheckSystemInfo = true;
+ VehicleType vehicleType = raiResponse.getVehicleType();
+ String systemSoftwareVersion = raiResponse.getSystemSoftwareVersion();
+ if (vehicleType != null || systemSoftwareVersion != null) {
+ SystemInfo systemInfo = new SystemInfo(vehicleType, systemSoftwareVersion, null);
+ boolean validSystemInfo = lifecycleListener.onSystemInfoReceived(systemInfo);
+ if (!validSystemInfo) {
+ DebugTool.logWarning(TAG, "Disconnecting from head unit, the system info was not accepted.");
+ UnregisterAppInterface msg = new UnregisterAppInterface();
+ msg.setCorrelationID(UNREGISTER_APP_INTERFACE_CORRELATION_ID);
+ sendRPCMessagePrivate(msg, true);
+ clean();
+ return;
+ }
+ }
+ //If the vehicle is acceptable and this is the first check, init security lib
+ setSecurityLibraryIfAvailable(vehicleType);
+ }
systemCapabilityManager.parseRAIResponse(raiResponse);
break;
case ON_HMI_STATUS:
@@ -387,42 +417,61 @@ abstract class BaseLifecycleManager {
case ON_HASH_CHANGE:
break;
case ON_SYSTEM_REQUEST:
+ case ON_ENCODED_SYNC_P_DATA:
+ case ON_SYNC_P_DATA:
+ if (functionID.equals(FunctionID.ON_ENCODED_SYNC_P_DATA) || functionID.equals(FunctionID.ON_SYNC_P_DATA)) {
+ DebugTool.logInfo(TAG, "Received legacy SYNC_P_DATA, handling it as OnSystemRequest");
+ } else {
+ DebugTool.logInfo(TAG, "Received OnSystemRequest");
+ }
+
final OnSystemRequest onSystemRequest = (OnSystemRequest) message;
- if ((onSystemRequest.getUrl() != null) &&
- (((onSystemRequest.getRequestType() == RequestType.PROPRIETARY) && (onSystemRequest.getFileType() == FileType.JSON))
- || ((onSystemRequest.getRequestType() == RequestType.HTTP) && (onSystemRequest.getFileType() == FileType.BINARY)))) {
- Thread handleOffboardTransmissionThread = new Thread() {
- @Override
- public void run() {
- RPCRequest request = PoliciesFetcher.fetchPolicies(onSystemRequest);
- if (request != null && isConnected()) {
- sendRPCMessagePrivate(request, true);
+ RequestType requestType = onSystemRequest.getRequestType();
+ FileType fileType = onSystemRequest.getFileType();
+
+ if (onSystemRequest.getUrl() != null) {
+ if ((requestType == RequestType.PROPRIETARY && fileType == FileType.JSON)
+ || (requestType == RequestType.HTTP && fileType == FileType.BINARY)
+ || functionID.equals(FunctionID.ON_ENCODED_SYNC_P_DATA)
+ || functionID.equals(FunctionID.ON_SYNC_P_DATA)) {
+ DebugTool.logInfo(TAG, "List of conditionals has passed");
+ Thread handleOffboardTransmissionThread = new Thread() {
+ @Override
+ public void run() {
+ DebugTool.logInfo(TAG, "Attempting to fetch policies");
+ RPCRequest request = PoliciesFetcher.fetchPolicies(onSystemRequest);
+ if (request != null && isConnected()) {
+ sendRPCMessagePrivate(request, true);
+ }
}
- }
- };
- handleOffboardTransmissionThread.start();
- } else if (onSystemRequest.getRequestType() == RequestType.ICON_URL && onSystemRequest.getUrl() != null) {
- //Download the icon file and send SystemRequest RPC
- Thread handleOffBoardTransmissionThread = new Thread() {
- @Override
- public void run() {
- final String urlHttps = onSystemRequest.getUrl().replaceFirst("http://", "https://");
- byte[] file = FileUtls.downloadFile(urlHttps);
- if (file != null) {
- SystemRequest systemRequest = new SystemRequest();
- systemRequest.setFileName(onSystemRequest.getUrl());
- systemRequest.setBulkData(file);
- systemRequest.setRequestType(RequestType.ICON_URL);
- if (isConnected()) {
- sendRPCMessagePrivate(systemRequest, true);
+ };
+ handleOffboardTransmissionThread.start();
+ return;
+ } else if (requestType == RequestType.ICON_URL) {
+ //Download the icon file and send SystemRequest RPC
+ Thread handleOffBoardTransmissionThread = new Thread() {
+ @Override
+ public void run() {
+ final String urlHttps = onSystemRequest.getUrl().replaceFirst("http://", "https://");
+ byte[] file = FileUtls.downloadFile(urlHttps);
+ if (file != null) {
+ SystemRequest systemRequest = new SystemRequest();
+ systemRequest.setFileName(onSystemRequest.getUrl());
+ systemRequest.setBulkData(file);
+ systemRequest.setRequestType(RequestType.ICON_URL);
+ if (isConnected()) {
+ sendRPCMessagePrivate(systemRequest, true);
+ }
+ } else {
+ DebugTool.logError(TAG, "File was null at: " + urlHttps);
}
- } else {
- DebugTool.logError(TAG, "File was null at: " + urlHttps);
}
- }
- };
- handleOffBoardTransmissionThread.start();
+ };
+ handleOffBoardTransmissionThread.start();
+ return;
+ }
}
+
break;
case ON_APP_INTERFACE_UNREGISTERED:
@@ -854,7 +903,7 @@ abstract class BaseLifecycleManager {
@Override
- public void onSessionStarted(int sessionID, Version version) {
+ public void onSessionStarted(int sessionID, Version version, SystemInfo systemInfo) {
DebugTool.logInfo(TAG, "on protocol session started");
if (minimumProtocolVersion != null && minimumProtocolVersion.isNewerThan(version) == 1) {
DebugTool.logWarning(TAG, String.format("Disconnecting from head unit, the configured minimum protocol version %s is greater than the supported protocol version %s", minimumProtocolVersion, getProtocolVersion()));
@@ -862,6 +911,20 @@ abstract class BaseLifecycleManager {
clean();
return;
}
+
+ if (systemInfo != null && lifecycleListener != null) {
+ didCheckSystemInfo = true;
+ boolean validSystemInfo = lifecycleListener.onSystemInfoReceived(systemInfo);
+ if (!validSystemInfo) {
+ DebugTool.logWarning(TAG, "Disconnecting from head unit, the system info was not accepted.");
+ session.endService(SessionType.RPC);
+ clean();
+ return;
+ }
+ //If the vehicle is acceptable, init security lib
+ setSecurityLibraryIfAvailable(systemInfo.getVehicleType());
+ }
+
if (appConfig != null) {
appConfig.prepare();
@@ -938,8 +1001,8 @@ abstract class BaseLifecycleManager {
}
@Override
- public void startVideoService(VideoStreamingParameters parameters, boolean encrypted) {
- BaseLifecycleManager.this.startVideoService(encrypted, parameters);
+ public void startVideoService(VideoStreamingParameters parameters, boolean encrypted, boolean afterPendingRestart) {
+ BaseLifecycleManager.this.startVideoService(encrypted, parameters, afterPendingRestart);
}
@Override
@@ -1019,6 +1082,11 @@ abstract class BaseLifecycleManager {
}
@Override
+ public long getMtu(SessionType serviceType) {
+ return BaseLifecycleManager.this.session.getMtu(serviceType);
+ }
+
+ @Override
public void startRPCEncryption() {
BaseLifecycleManager.this.startRPCEncryption();
}
@@ -1032,6 +1100,11 @@ abstract class BaseLifecycleManager {
public SystemCapabilityManager getSystemCapabilityManager() {
return BaseLifecycleManager.this.systemCapabilityManager;
}
+
+ @Override
+ public PermissionManager getPermissionManager() {
+ return null;
+ }
};
/* *******************************************************************************************************
@@ -1129,18 +1202,24 @@ abstract class BaseLifecycleManager {
this.encryptionLifecycleManager = new EncryptionLifecycleManager(internalInterface, listener);
}
- private void processRaiResponse(RegisterAppInterfaceResponse rai) {
- if (rai == null) return;
-
- this.raiResponse = rai;
-
- VehicleType vt = rai.getVehicleType();
- if (vt == null) return;
+ /**
+ * Using the vehicle type information, specifically the make, the library will attempt to init
+ * the security library that is associated with that OEM.
+ * @param vehicleType type of vehicle that is currently connected
+ */
+ private void setSecurityLibraryIfAvailable(VehicleType vehicleType) {
+ if (_secList == null) {
+ return;
+ }
- String make = vt.getMake();
- if (make == null) return;
+ if (vehicleType == null) {
+ return;
+ }
- if (_secList == null) return;
+ String make = vehicleType.getMake();
+ if (make == null) {
+ return;
+ }
setSdlSecurityStaticVars();
@@ -1185,7 +1264,7 @@ abstract class BaseLifecycleManager {
void onTransportDisconnected(String info, boolean availablePrimary, BaseTransportConfig transportConfig) {
}
- void startVideoService(boolean encrypted, VideoStreamingParameters parameters) {
+ void startVideoService(boolean encrypted, VideoStreamingParameters parameters, boolean afterPendingRestart) {
}
void startAudioService(boolean encrypted) {
@@ -1212,6 +1291,8 @@ abstract class BaseLifecycleManager {
void onServiceEnded(SessionType sessionType);
void onError(LifecycleManager lifeCycleManager, String info, Exception e);
+
+ boolean onSystemInfoReceived(SystemInfo systemInfo);
}
public static class AppConfig {
diff --git a/base/src/main/java/com/smartdevicelink/managers/lifecycle/RpcConverter.java b/base/src/main/java/com/smartdevicelink/managers/lifecycle/RpcConverter.java
index 9a67f3a97..6f8a955c2 100644
--- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/RpcConverter.java
+++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/RpcConverter.java
@@ -142,7 +142,20 @@ public class RpcConverter {
String functionName = (String) params.get(RPCMessage.KEY_FUNCTION_NAME);
if (FunctionID.SHOW_CONSTANT_TBT.toString().equals(functionName)) {
functionName = "ShowConstantTbt";
+ } else if (FunctionID.ON_ENCODED_SYNC_P_DATA.toString().equals(functionName)
+ || FunctionID.ON_SYNC_P_DATA.toString().equals(functionName)) {
+ //This will create an OnSystemRequest instance, but the function id in the RPC
+ //will remain FunctionID.ON_ENCODED_SYNC_P_DATA or FunctionID.ON_SYNC_P_DATA.
+ //This is desired behavior.
+ functionName = FunctionID.ON_SYSTEM_REQUEST.toString();
+ } else if (FunctionID.ENCODED_SYNC_P_DATA.toString().equals(functionName)
+ || FunctionID.SYNC_P_DATA.toString().equals(functionName)) {
+ //This will create an SystemRequest instance, but the function id in the RPC
+ //will remain FunctionID.ENCODED_SYNC_P_DATA or FunctionID.SYNC_P_DATA.
+ //This is desired behavior.
+ functionName = FunctionID.SYSTEM_REQUEST.toString();
}
+
rpcClassName.append(RPC_PACKAGE);
rpcClassName.append(functionName);
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/AlertAudioData.java b/base/src/main/java/com/smartdevicelink/managers/screen/AlertAudioData.java
new file mode 100644
index 000000000..c8c250ca5
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/AlertAudioData.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.screen;
+
+import androidx.annotation.NonNull;
+
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+import com.smartdevicelink.util.DebugTool;
+
+public class AlertAudioData extends AudioData implements Cloneable {
+ // Whether the alert tone should be played before the prompt (if any) is spoken. Defaults to false
+ private boolean playTone;
+
+ public AlertAudioData(@NonNull SdlFile audioFile) {
+ super(audioFile);
+ }
+
+ public AlertAudioData(@NonNull String spokenString) {
+ super(spokenString);
+ }
+
+ public AlertAudioData(@NonNull String phoneticString, @NonNull SpeechCapabilities phoneticType) {
+ super(phoneticString, phoneticType);
+ }
+
+ public boolean isPlayTone() {
+ return playTone;
+ }
+
+ public void setPlayTone(boolean playTone) {
+ this.playTone = playTone;
+ }
+
+ /**
+ * Creates a deep copy of the object
+ *
+ * @return deep copy of the object, null if an exception occurred
+ */
+ @Override
+ public AlertAudioData clone() {
+ try {
+ AlertAudioData alertAudioData = (AlertAudioData) super.clone();
+ return alertAudioData;
+ } catch (CloneNotSupportedException e) {
+ if (DebugTool.isDebugEnabled()) {
+ throw new RuntimeException("Clone not supported by super class");
+ }
+ }
+ return null;
+ }
+} \ No newline at end of file
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/AlertCanceledListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/AlertCanceledListener.java
new file mode 100644
index 000000000..ac65c2035
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/AlertCanceledListener.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.screen;
+
+public interface AlertCanceledListener {
+ /**
+ * Notifies the subscriber that the Alert should be cancelled.
+ */
+ void onAlertCanceled();
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/AlertView.java b/base/src/main/java/com/smartdevicelink/managers/screen/AlertView.java
new file mode 100644
index 000000000..f69011170
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/AlertView.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.screen;
+
+import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.util.DebugTool;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AlertView implements Cloneable {
+
+ private static final int TIMEOUT_DEFAULT = 0;
+ private static final int TIMEOUT_MIN = 3;
+ private static final int TIMEOUT_MAX = 10;
+ private static Integer defaultTimeout = 5;
+ private String text, secondaryText, tertiaryText;
+ private Integer timeout;
+ private AlertAudioData audio;
+ private boolean showWaitIndicator;
+ private List<SoftButtonObject> softButtons;
+ private SdlArtwork icon;
+ AlertCanceledListener canceledListener;
+
+
+ private AlertView() {
+ }
+
+ public static class Builder {
+
+ AlertView alertView;
+
+ public Builder() {
+ alertView = new AlertView();
+ if (alertView.timeout == null) {
+ alertView.timeout = TIMEOUT_DEFAULT;
+ }
+ }
+
+ /**
+ * The primary line of text for display on the alert. If fewer than three alert lines are available
+ * on the head unit, the screen manager will automatically concatenate some of the lines together.
+ */
+ public Builder setText(String text) {
+ alertView.text = text;
+ return this;
+ }
+
+ /**
+ * The secondary line of text for display on the alert. If fewer than three alert lines are available
+ * on the head unit, the screen manager will automatically concatenate some of the lines together.
+ */
+ public Builder setSecondaryText(String secondaryText) {
+ alertView.secondaryText = secondaryText;
+ return this;
+ }
+
+ /**
+ * The tertiary line of text for display on the alert. If fewer than three alert lines are available
+ * on the head unit, the screen manager will automatically concatenate some of the lines together.
+ */
+ public Builder setTertiaryText(String tertiaryText) {
+ alertView.tertiaryText = tertiaryText;
+ return this;
+ }
+
+ /**
+ * Timeout in seconds. Defaults to 0, which will use `defaultTimeout`. If this is set below the
+ * minimum, it will be capped at 3 seconds. Minimum 3 seconds, maximum 10 seconds. If this is
+ * set above the maximum, it will be capped at 10 seconds. Defaults to 0.
+ *
+ * Please note that if a button is added to the alert, the defaultTimeout and timeout values will be ignored.
+ */
+ public Builder setTimeout(Integer timeout) {
+ alertView.timeout = timeout;
+ return this;
+ }
+
+ /**
+ * If supported, the alert GUI will display some sort of indefinite waiting / refresh / loading
+ * indicator animation. Defaults to NO.
+ */
+ public Builder setShowWaitIndicator(boolean showWaitIndicator) {
+ alertView.showWaitIndicator = showWaitIndicator;
+ return this;
+ }
+
+ /**
+ * Soft buttons the user may select to perform actions. Only one `SoftButtonState` per object
+ * is supported; if any soft button object contains multiple states, an exception will be thrown.
+ */
+ public Builder setSoftButtons(List<SoftButtonObject> softButtons) {
+ alertView.setSoftButtons(softButtons);
+ return this;
+ }
+
+ /**
+ * Text spoken, file(s) played, and/or tone played when the alert appears
+ */
+ public Builder setAudio(AlertAudioData audio) {
+ alertView.audio = audio;
+ return this;
+ }
+
+ /**
+ * An artwork that will be displayed when the icon appears. This will be uploaded prior to the
+ * appearance of the alert if necessary. This will not be uploaded if the head unit does not
+ * declare support for alertIcon.
+ */
+ public Builder setIcon(SdlArtwork icon) {
+ alertView.icon = icon;
+ return this;
+ }
+
+ /**
+ * Set this to change the default timeout for all alerts. If a timeout is not set on an individual
+ * alert object (or if it is set to 0.0), then it will use this timeout instead. See `timeout`
+ * for more details. If this is not set by you, it will default to 5 seconds. The minimum is
+ * 3 seconds, the maximum is 10 seconds. If this is set below the minimum, it will be capped
+ * at 3 seconds. If this is set above the maximum, it will be capped at 10 seconds.
+ */
+ public Builder setDefaultTimeOut(int defaultTimeOut) {
+ alertView.setDefaultTimeout(defaultTimeOut);
+ return this;
+ }
+
+ public AlertView build() {
+ return alertView;
+ }
+ }
+
+ // Notifies the subscriber that the alert should be cancelled.
+ public void cancel() {
+ if (canceledListener == null) {
+ return;
+ }
+ canceledListener.onAlertCanceled();
+ }
+
+ public Integer getTimeout() {
+ if (timeout == TIMEOUT_DEFAULT) {
+ timeout = getDefaultTimeout();
+ } else if (timeout < TIMEOUT_MIN) {
+ return TIMEOUT_MIN;
+ } else if (timeout > TIMEOUT_MAX) {
+ return TIMEOUT_MAX;
+ }
+ return timeout;
+ }
+
+ public AlertAudioData getAudio() {
+ return audio;
+ }
+
+ public void setAudio(AlertAudioData audio) {
+ this.audio = audio;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public boolean isShowWaitIndicator() {
+ return showWaitIndicator;
+ }
+
+ public void setShowWaitIndicator(boolean showWaitIndicator) {
+ this.showWaitIndicator = showWaitIndicator;
+ }
+
+ public String getSecondaryText() {
+ return secondaryText;
+ }
+
+ public void setSecondaryText(String secondaryText) {
+ this.secondaryText = secondaryText;
+ }
+
+ public String getTertiaryText() {
+ return tertiaryText;
+ }
+
+ public void setTertiaryText(String tertiaryText) {
+ this.tertiaryText = tertiaryText;
+ }
+
+ public List<SoftButtonObject> getSoftButtons() {
+ return softButtons;
+ }
+
+ public void setSoftButtons(List<SoftButtonObject> softButtons) {
+ for (SoftButtonObject softButtonObject : softButtons) {
+ if (softButtonObject.getStates().size() != 1) {
+ throw new IllegalArgumentException("Attempting create a soft button for an Alert with more than one state. Alerts only support soft buttons with one state");
+ }
+ }
+ this.softButtons = softButtons;
+ }
+
+ public SdlArtwork getIcon() {
+ return icon;
+ }
+
+ public void setIcon(SdlArtwork icon) {
+ this.icon = icon;
+ }
+
+ public int getDefaultTimeout() {
+ if (defaultTimeout < TIMEOUT_MIN) {
+ return TIMEOUT_MIN;
+ } else if (defaultTimeout > TIMEOUT_MAX) {
+ return TIMEOUT_MAX;
+ }
+ return defaultTimeout;
+ }
+
+ public void setDefaultTimeout(int defaultTimeout) {
+ AlertView.defaultTimeout = defaultTimeout;
+ }
+
+ public void setTimeout(int timeout) {
+ this.timeout = timeout;
+ }
+
+ /**
+ * Creates a deep copy of the object
+ *
+ * @return deep copy of the object, null if an exception occurred
+ */
+ @Override
+ public AlertView clone() {
+ try {
+ AlertView alertView = (AlertView) super.clone();
+ if (alertView != null) {
+ if (alertView.getAudio() != null) {
+ alertView.audio = audio.clone();
+ }
+ if (alertView.getSoftButtons() != null) {
+ List<SoftButtonObject> softButtonObjectList = new ArrayList<>();
+ for (int i = 0; i < alertView.softButtons.size(); i++) {
+ SoftButtonObject cloneSoftButton = alertView.softButtons.get(i).clone();
+ softButtonObjectList.add(cloneSoftButton);
+ }
+ alertView.softButtons = softButtonObjectList;
+ }
+ if (alertView.icon != null) {
+ alertView.icon = icon.clone();
+ }
+ }
+ return alertView;
+ } catch (CloneNotSupportedException e) {
+ if (DebugTool.isDebugEnabled()) {
+ throw new RuntimeException("Clone not supported by super class");
+ }
+ }
+ return null;
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/AudioData.java b/base/src/main/java/com/smartdevicelink/managers/screen/AudioData.java
new file mode 100644
index 000000000..18aa110e3
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/AudioData.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.screen;
+
+import androidx.annotation.NonNull;
+
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.proxy.rpc.TTSChunk;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+public class AudioData {
+
+ // All audio data
+ private List<TTSChunk> audioData;
+
+ // The audio files that will be uploaded.
+ private HashMap<String, SdlFile> audioFiles;
+
+ public AudioData(@NonNull SdlFile audioFile) {
+ this.audioFiles = new HashMap<>();
+ this.audioData = new ArrayList<>();
+ audioFiles.put(audioFile.getName(), audioFile);
+ audioData.add(new TTSChunk(audioFile.getName(), SpeechCapabilities.FILE));
+ }
+
+ public AudioData(@NonNull String spokenString) {
+ this.audioData = new ArrayList<>();
+ audioData.add(new TTSChunk(spokenString, SpeechCapabilities.TEXT));
+ }
+
+ public AudioData(@NonNull String phoneticString, @NonNull SpeechCapabilities phoneticType) {
+ if (!isValidPhoneticType(phoneticType)) {
+ return;
+ }
+ this.audioData = new ArrayList<>();
+ audioData.add(new TTSChunk(phoneticString, phoneticType));
+ }
+
+ /**
+ * Checks if the phonetic type can be used to create a text-to-speech string.
+ *
+ * @param phoneticType The phonetic type of the text-to-speech string
+ * @return True if the phoneticType is of type `SAPI_PHONEMES`, `LHPLUS_PHONEMES`, `TEXT`, or `PRE_RECORDED`; false if not.
+ */
+ boolean isValidPhoneticType(SpeechCapabilities phoneticType) {
+ if (!(phoneticType.equals(SpeechCapabilities.SAPI_PHONEMES) || phoneticType.equals(SpeechCapabilities.LHPLUS_PHONEMES)
+ || phoneticType.equals(SpeechCapabilities.TEXT) || phoneticType.equals(SpeechCapabilities.PRE_RECORDED))) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Add additional SDLFiles holding data or pointing to a file on the file system. When this object
+ * is passed to an `Alert` or `Speak`, the file will be uploaded if it is not already, then played
+ * if the system supports that feature.
+ *
+ * @param audioFiles A list of audio file to be played by the system
+ */
+ public void addAudioFiles(@NonNull List<SdlFile> audioFiles) {
+ if (this.audioFiles == null) {
+ this.audioFiles = new HashMap<>();
+ }
+ if (this.audioData == null) {
+ this.audioData = new ArrayList<>();
+ }
+ for (SdlFile file : audioFiles) {
+ audioData.add(new TTSChunk(file.getName(), SpeechCapabilities.FILE));
+ this.audioFiles.put(file.getName(), file);
+ }
+ }
+
+ /**
+ * Create additional strings to be spoken by the system speech synthesizer.
+ *
+ * @param spokenString The strings to be spoken by the system speech synthesizer
+ */
+ public void addSpeechSynthesizerStrings(@NonNull List<String> spokenString) {
+ if (spokenString.size() == 0) {
+ return;
+ }
+ List<TTSChunk> newPrompts = new ArrayList<>();
+ for (String spoken : spokenString) {
+ if (spoken.length() == 0) {
+ continue;
+ }
+ newPrompts.add(new TTSChunk().setText(spoken).setType(SpeechCapabilities.TEXT));
+ }
+ if (newPrompts.size() == 0) {
+ return;
+ }
+ if (audioData == null) {
+ this.audioData = newPrompts;
+ return;
+ }
+ audioData.addAll(newPrompts);
+ }
+
+ /**
+ * Create additional strings to be spoken by the system speech synthesizer using a phonetic string.
+ *
+ * @param phoneticString The strings to be spoken by the system speech synthesizer
+ * @param phoneticType Must be one of `SAPI_PHONEMES`, `LHPLUS_PHONEMES`, `TEXT`, or `PRE_RECORDED` or no object will be created
+ */
+ public void addPhoneticSpeechSynthesizerStrings(@NonNull List<String> phoneticString, @NonNull SpeechCapabilities phoneticType) {
+ if (!(isValidPhoneticType(phoneticType)) || phoneticString.size() == 0) {
+ return;
+ }
+ List<TTSChunk> newPrompts = new ArrayList<>();
+ for (String phonetic : phoneticString) {
+ if (phonetic.length() == 0) {
+ continue;
+ }
+ newPrompts.add(new TTSChunk(phonetic, phoneticType));
+ }
+ if (newPrompts.size() == 0) {
+ return;
+ }
+ if (audioData == null) {
+ this.audioData = newPrompts;
+ return;
+ }
+ audioData.addAll(newPrompts);
+ }
+
+ HashMap<String, SdlFile> getAudioFiles() {
+ return audioFiles;
+ }
+
+ public List<TTSChunk> getAudioData() {
+ return audioData;
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java
new file mode 100644
index 000000000..56aec5c19
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.screen;
+
+import androidx.annotation.NonNull;
+
+import com.livio.taskmaster.Queue;
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.managers.AlertCompletionListener;
+import com.smartdevicelink.managers.BaseSubManager;
+import com.smartdevicelink.managers.CompletionListener;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.lifecycle.OnSystemCapabilityListener;
+import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
+import com.smartdevicelink.managers.permission.OnPermissionChangeListener;
+import com.smartdevicelink.managers.permission.PermissionElement;
+import com.smartdevicelink.managers.permission.PermissionStatus;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.RPCNotification;
+import com.smartdevicelink.proxy.rpc.DisplayCapability;
+import com.smartdevicelink.proxy.rpc.OnButtonEvent;
+import com.smartdevicelink.proxy.rpc.OnButtonPress;
+import com.smartdevicelink.proxy.rpc.WindowCapability;
+import com.smartdevicelink.proxy.rpc.enums.ButtonName;
+import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
+import com.smartdevicelink.util.DebugTool;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+
+abstract class BaseAlertManager extends BaseSubManager {
+
+ private static final String TAG = "BaseAlertManager";
+ Queue transactionQueue;
+ WindowCapability currentWindowCapability;
+ private OnSystemCapabilityListener onSpeechCapabilityListener, onDisplaysCapabilityListener;
+ List<SpeechCapabilities> speechCapabilities;
+ private UUID permissionListener;
+ boolean isAlertRPCAllowed = false;
+ private final WeakReference<FileManager> fileManager;
+ int nextCancelId;
+ private final int alertCancelIdMin = 1;
+ private final int alertCancelIdMax = 100;
+ private List<SoftButtonObject> softButtonObjects;
+ OnRPCNotificationListener onButtonPressListener, onButtonEventListener;
+
+
+ public BaseAlertManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
+ super(internalInterface);
+ this.transactionQueue = newTransactionQueue();
+ this.fileManager = new WeakReference<>(fileManager);
+ nextCancelId = 0;
+ this.softButtonObjects = new ArrayList<>();
+ addListeners();
+ }
+
+ /**
+ * Starts the manager
+ * @param listener CompletionListener that is called once the BaseSubManager's state is READY, LIMITED, or ERROR
+ */
+ @Override
+ public void start(CompletionListener listener) {
+ transitionToState(READY);
+ super.start(listener);
+ }
+
+ /**
+ * Clean up everything after the manager is no longer needed
+ */
+ @Override
+ public void dispose() {
+ currentWindowCapability = null;
+ speechCapabilities = null;
+ isAlertRPCAllowed = false;
+ softButtonObjects = null;
+
+ if (transactionQueue != null) {
+ transactionQueue.close();
+ transactionQueue = null;
+ }
+
+ // remove listeners
+ if (internalInterface.getSystemCapabilityManager() != null) {
+ internalInterface.getSystemCapabilityManager().removeOnSystemCapabilityListener(SystemCapabilityType.DISPLAYS, onDisplaysCapabilityListener);
+ internalInterface.getSystemCapabilityManager().removeOnSystemCapabilityListener(SystemCapabilityType.SPEECH, onSpeechCapabilityListener);
+ }
+ if (internalInterface.getPermissionManager() != null) {
+ internalInterface.getPermissionManager().removeListener(permissionListener);
+ }
+ internalInterface.removeOnRPCNotificationListener(FunctionID.ON_BUTTON_PRESS, onButtonPressListener);
+ internalInterface.removeOnRPCNotificationListener(FunctionID.ON_BUTTON_EVENT, onButtonEventListener);
+ super.dispose();
+ }
+
+ /**
+ * Creates a PresentAlertOperation and adds it to the transactionQueue
+ *
+ * @param alert - AlertView object that contains alert information
+ * @param listener - AlertCompletionListener that will notify the sender when Alert has completed
+ */
+ public void presentAlert(AlertView alert, AlertCompletionListener listener) {
+ if (getState() == ERROR) {
+ DebugTool.logWarning(TAG, "Alert Manager In Error State");
+ return;
+ }
+
+ // Check for softButtons and assign them ID's, Behavior mimic SoftButtonManager,
+ // as in if invalid ID's are set, Alert will not show up.
+ // It's best if ID's are not set custom and allow the screenManager to set them.
+ if (alert.getSoftButtons() != null) {
+ if (!BaseScreenManager.checkAndAssignButtonIds(alert.getSoftButtons(), BaseScreenManager.ManagerLocation.ALERT_MANAGER)) {
+ DebugTool.logError(TAG, "Attempted to set soft button objects for Alert, but multiple buttons had the same id.");
+ return;
+ }
+ softButtonObjects.addAll(alert.getSoftButtons());
+ }
+
+ if (nextCancelId >= alertCancelIdMax) {
+ nextCancelId = alertCancelIdMin;
+ } else {
+ nextCancelId++;
+ }
+
+ PresentAlertOperation operation = new PresentAlertOperation(internalInterface, alert, currentWindowCapability, speechCapabilities, fileManager.get(), nextCancelId, listener, new AlertSoftButtonClearListener() {
+ @Override
+ public void onButtonClear(List<SoftButtonObject> softButtonObjectList) {
+ // Stop keeping track of SoftButtonObject listeners as operation has finished
+ for (SoftButtonObject object : softButtonObjectList) {
+ softButtonObjects.remove(object);
+ }
+ }
+ });
+ transactionQueue.add(operation, false);
+ }
+
+ /**
+ * Interface that sends a list of SoftButtonObjects back from PresentAlertOperation, to allow BaseAlertManager
+ * to stop keeping track of them for their onButtonEventListener
+ */
+ interface AlertSoftButtonClearListener {
+ void onButtonClear(List<SoftButtonObject> softButtonObjects);
+ }
+
+
+ private Queue newTransactionQueue() {
+ Queue queue = internalInterface.getTaskmaster().createQueue("AlertManager", 6, false);
+ queue.pause();
+ return queue;
+ }
+
+ /**
+ * Get the soft button objects list
+ *
+ * @return a List<SoftButtonObject>
+ */
+ protected List<SoftButtonObject> getSoftButtonObjects() {
+ return softButtonObjects;
+ }
+
+ /**
+ * Get the SoftButtonObject that has the provided buttonId
+ *
+ * @param buttonId a int value that represents the id of the button
+ * @return a SoftButtonObject
+ */
+ protected SoftButtonObject getSoftButtonObjectById(int buttonId) {
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ if (softButtonObject.getButtonId() == buttonId) {
+ return softButtonObject;
+ }
+ }
+ return null;
+ }
+
+ // Suspend the queue if the WindowCapabilities are null
+ // OR if isAlertRPCAllowed is false
+ private void updateTransactionQueueSuspended() {
+ if (!isAlertRPCAllowed || currentWindowCapability == null) {
+ DebugTool.logInfo(TAG, String.format("Suspending the transaction queue. Current permission status is false: %b, window capabilities are null: %b", isAlertRPCAllowed, currentWindowCapability == null));
+ transactionQueue.pause();
+ } else {
+ DebugTool.logInfo(TAG, "Starting the transaction queue");
+ transactionQueue.resume();
+ }
+ }
+
+
+ private void addListeners() {
+ // Retrieves SpeechCapabilities of the system.
+ onSpeechCapabilityListener = new OnSystemCapabilityListener() {
+ @Override
+ public void onCapabilityRetrieved(Object capability) {
+ speechCapabilities = SystemCapabilityManager.convertToList(capability, SpeechCapabilities.class);
+ }
+
+ @Override
+ public void onError(String info) {
+
+ }
+ };
+ if (internalInterface.getSystemCapabilityManager() != null) {
+ this.internalInterface.getSystemCapabilityManager().getCapability(SystemCapabilityType.SPEECH, onSpeechCapabilityListener, false);
+ }
+ // Retrieves WindowCapability of the system, if WindowCapability are null, queue pauses
+ onDisplaysCapabilityListener = new OnSystemCapabilityListener() {
+ @Override
+ public void onCapabilityRetrieved(Object capability) {
+ // instead of using the parameter it's more safe to use the convenience method
+ List<DisplayCapability> capabilities = SystemCapabilityManager.convertToList(capability, DisplayCapability.class);
+ if (capabilities == null || capabilities.size() == 0) {
+ currentWindowCapability = null;
+ } else {
+ DisplayCapability display = capabilities.get(0);
+ for (WindowCapability windowCapability : display.getWindowCapabilities()) {
+ int currentWindowID = windowCapability.getWindowID() != null ? windowCapability.getWindowID() : PredefinedWindows.DEFAULT_WINDOW.getValue();
+ if (currentWindowID != PredefinedWindows.DEFAULT_WINDOW.getValue()) {
+ continue;
+ }
+ currentWindowCapability = windowCapability;
+ updatePendingOperationsWithNewWindowCapability();
+ break;
+ }
+ }
+ // Update the queue's suspend state
+ updateTransactionQueueSuspended();
+ }
+
+ @Override
+ public void onError(String info) {
+ DebugTool.logError(TAG, "Display Capability cannot be retrieved");
+ currentWindowCapability = null;
+ updateTransactionQueueSuspended();
+ }
+ };
+ if (internalInterface.getSystemCapabilityManager() != null) {
+ this.internalInterface.getSystemCapabilityManager().addOnSystemCapabilityListener(SystemCapabilityType.DISPLAYS, onDisplaysCapabilityListener);
+ }
+
+ // Subscribes to permission updates for the `Alert` RPC. If the alert is not allowed at the current HMI level, the queue is suspended.
+ // Any `Alert` RPCs added while the queue is suspended will be sent when the `Alert` RPC is allowed at the current HMI level and the queue is unsuspended.
+ PermissionElement alertPermissionElement = new PermissionElement(FunctionID.ALERT, null);
+ permissionListener = internalInterface.getPermissionManager().addListener(Collections.singletonList(alertPermissionElement), internalInterface.getPermissionManager().PERMISSION_GROUP_TYPE_ANY, new OnPermissionChangeListener() {
+ @Override
+ public void onPermissionsChange(@NonNull Map<FunctionID, PermissionStatus> allowedPermissions, int permissionGroupStatus) {
+ if (allowedPermissions.get(FunctionID.ALERT) != null) {
+ isAlertRPCAllowed = allowedPermissions.get(FunctionID.ALERT).getIsRPCAllowed();
+ } else {
+ isAlertRPCAllowed = false;
+ }
+ updateTransactionQueueSuspended();
+ }
+ });
+
+ this.onButtonPressListener = new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+ OnButtonPress onButtonPress = (OnButtonPress) notification;
+ if (onButtonPress != null && onButtonPress.getButtonName() == ButtonName.CUSTOM_BUTTON) {
+ Integer buttonId = onButtonPress.getCustomButtonID();
+ if (getSoftButtonObjects() != null) {
+ for (SoftButtonObject softButtonObject : getSoftButtonObjects()) {
+ if (softButtonObject.getButtonId() == buttonId && softButtonObject.getOnEventListener() != null) {
+ softButtonObject.getOnEventListener().onPress(getSoftButtonObjectById(buttonId), onButtonPress);
+ break;
+ }
+ }
+ }
+ }
+ }
+ };
+ this.internalInterface.addOnRPCNotificationListener(FunctionID.ON_BUTTON_PRESS, onButtonPressListener);
+
+ // Add OnButtonEventListener to notify SoftButtonObjects when there is a button event
+ this.onButtonEventListener = new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+ OnButtonEvent onButtonEvent = (OnButtonEvent) notification;
+ if (onButtonEvent != null && onButtonEvent.getButtonName() == ButtonName.CUSTOM_BUTTON) {
+ Integer buttonId = onButtonEvent.getCustomButtonID();
+ if (getSoftButtonObjects() != null) {
+ for (SoftButtonObject softButtonObject : getSoftButtonObjects()) {
+ if (softButtonObject.getButtonId() == buttonId && softButtonObject.getOnEventListener() != null) {
+ softButtonObject.getOnEventListener().onEvent(getSoftButtonObjectById(buttonId), onButtonEvent);
+ break;
+ }
+ }
+ }
+ }
+ }
+ };
+ this.internalInterface.addOnRPCNotificationListener(FunctionID.ON_BUTTON_EVENT, onButtonEventListener);
+ }
+
+ // Updates pending task with new DisplayCapabilities
+ void updatePendingOperationsWithNewWindowCapability() {
+ for (Task task : transactionQueue.getTasksAsList()) {
+ if (!(task instanceof PresentAlertOperation)) {
+ continue;
+ }
+ ((PresentAlertOperation) task).currentWindowCapability = currentWindowCapability;
+ }
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java
index 8b976b2af..4372cda9f 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java
@@ -31,10 +31,12 @@
*/
package com.smartdevicelink.managers.screen;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import com.smartdevicelink.managers.AlertCompletionListener;
import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.ISdl;
@@ -58,6 +60,8 @@ import com.smartdevicelink.proxy.rpc.enums.MetadataType;
import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
import com.smartdevicelink.util.DebugTool;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.List;
@@ -77,6 +81,13 @@ abstract class BaseScreenManager extends BaseSubManager {
private MenuManager menuManager;
private ChoiceSetManager choiceSetManager;
private SubscribeButtonManager subscribeButtonManager;
+ private AlertManager alertManager;
+
+ static final int SOFT_BUTTON_ID_NOT_SET_VALUE = -1;
+ static final int SOFT_BUTTON_ID_MIN_VALUE = 0;
+ static final int SOFT_BUTTON_ID_MAX_VALUE = 10000;
+ static HashSet<Integer> softButtonIDBySoftButtonManager;
+ static HashSet<Integer> softButtonIDByAlertManager;
// Sub manager listener
private final CompletionListener subManagerListener = new CompletionListener() {
@@ -85,15 +96,15 @@ abstract class BaseScreenManager extends BaseSubManager {
public synchronized void onComplete(boolean success) {
if (softButtonManager != null && textAndGraphicManager != null && voiceCommandManager != null && menuManager != null && choiceSetManager != null && subscribeButtonManager != null) {
if (softButtonManager.getState() == BaseSubManager.READY && textAndGraphicManager.getState() == BaseSubManager.READY && voiceCommandManager.getState() == BaseSubManager.READY && menuManager.getState() == BaseSubManager.READY
- && subscribeButtonManager.getState() == BaseSubManager.READY) {
+ && subscribeButtonManager.getState() == BaseSubManager.READY && alertManager.getState() == BaseSubManager.READY) {
DebugTool.logInfo(TAG, "Starting screen manager, all sub managers are in ready state");
transitionToState(READY);
} else if (softButtonManager.getState() == BaseSubManager.ERROR && textAndGraphicManager.getState() == BaseSubManager.ERROR && voiceCommandManager.getState() == BaseSubManager.ERROR && menuManager.getState() == BaseSubManager.ERROR
- && choiceSetManager.getState() == BaseSubManager.ERROR && subscribeButtonManager.getState() == BaseSubManager.ERROR) {
+ && choiceSetManager.getState() == BaseSubManager.ERROR && subscribeButtonManager.getState() == BaseSubManager.ERROR && alertManager.getState() == BaseSubManager.ERROR) {
DebugTool.logError(TAG, "ERROR starting screen manager, all sub managers are in error state");
transitionToState(ERROR);
} else if (textAndGraphicManager.getState() == BaseSubManager.SETTING_UP || softButtonManager.getState() == BaseSubManager.SETTING_UP || voiceCommandManager.getState() == BaseSubManager.SETTING_UP || menuManager.getState() == BaseSubManager.SETTING_UP
- || choiceSetManager.getState() == BaseSubManager.SETTING_UP || subscribeButtonManager.getState() == BaseSubManager.SETTING_UP) {
+ || choiceSetManager.getState() == BaseSubManager.SETTING_UP || subscribeButtonManager.getState() == BaseSubManager.SETTING_UP || alertManager.getState() == BaseSubManager.SETTING_UP) {
DebugTool.logInfo(TAG, "SETTING UP screen manager, at least one sub manager is still setting up");
transitionToState(SETTING_UP);
} else {
@@ -111,6 +122,8 @@ abstract class BaseScreenManager extends BaseSubManager {
BaseScreenManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
super(internalInterface);
this.fileManager = new WeakReference<>(fileManager);
+ softButtonIDBySoftButtonManager = new HashSet<>();
+ softButtonIDByAlertManager = new HashSet<>();
initialize();
}
@@ -124,6 +137,7 @@ abstract class BaseScreenManager extends BaseSubManager {
this.menuManager.start(subManagerListener);
this.choiceSetManager.start(subManagerListener);
this.subscribeButtonManager.start(subManagerListener);
+ this.alertManager.start(subManagerListener);
}
private void initialize() {
@@ -132,6 +146,7 @@ abstract class BaseScreenManager extends BaseSubManager {
this.textAndGraphicManager = new TextAndGraphicManager(internalInterface, fileManager.get(), softButtonManager);
this.menuManager = new MenuManager(internalInterface, fileManager.get());
this.choiceSetManager = new ChoiceSetManager(internalInterface, fileManager.get());
+ this.alertManager = new AlertManager(internalInterface, fileManager.get());
}
this.subscribeButtonManager = new SubscribeButtonManager(internalInterface);
this.voiceCommandManager = new VoiceCommandManager(internalInterface);
@@ -149,6 +164,10 @@ abstract class BaseScreenManager extends BaseSubManager {
menuManager.dispose();
choiceSetManager.dispose();
subscribeButtonManager.dispose();
+ alertManager.dispose();
+ softButtonIDByAlertManager = null;
+ softButtonIDBySoftButtonManager = null;
+
super.dispose();
}
@@ -703,4 +722,76 @@ abstract class BaseScreenManager extends BaseSubManager {
public void removeButtonListener(@NonNull ButtonName buttonName, @NonNull OnButtonListener listener) {
subscribeButtonManager.removeButtonListener(buttonName, listener);
}
+
+ public void presentAlert(AlertView alert, AlertCompletionListener listener) {
+ alertManager.presentAlert(alert, listener);
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ManagerLocation.SOFTBUTTON_MANAGER, ManagerLocation.ALERT_MANAGER})
+ @interface ManagerLocation {
+ int SOFTBUTTON_MANAGER = 0;
+ int ALERT_MANAGER = 1;
+ }
+
+ /**
+ * Used across managers to check and assign SoftButton ID's to prevent conflicts.
+ *
+ * @param softButtonObjects - list of SoftButtonObjects
+ * @param location - location from which the button came from, so if new buttons get set on screen, we can clear the old ID's out
+ * @return True if ButtonID's are good, False if not.
+ */
+ static boolean checkAndAssignButtonIds(List<SoftButtonObject> softButtonObjects, @ManagerLocation int location) {
+ HashSet<Integer> buttonIdsSetHashSet = new HashSet<>();
+ // Depending on location form which the softButtons came from, we will clear out the id list so they can be reset
+ if (location == ManagerLocation.ALERT_MANAGER) {
+ softButtonIDByAlertManager.clear();
+ buttonIdsSetHashSet = (HashSet) softButtonIDBySoftButtonManager.clone();
+ } else if (location == ManagerLocation.SOFTBUTTON_MANAGER) {
+ softButtonIDBySoftButtonManager.clear();
+ buttonIdsSetHashSet = (HashSet) softButtonIDByAlertManager.clone();
+ }
+ // Check if multiple soft button objects have the same id
+ int currentSoftButtonId, numberOfButtonIdsSet = buttonIdsSetHashSet.size(), maxButtonIdsSetByDev = SOFT_BUTTON_ID_MIN_VALUE;
+
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ currentSoftButtonId = softButtonObject.getButtonId();
+ if (currentSoftButtonId != SOFT_BUTTON_ID_NOT_SET_VALUE) {
+ if (softButtonIDByAlertManager.contains(currentSoftButtonId) || softButtonIDBySoftButtonManager.contains(currentSoftButtonId)) {
+ return false;
+ }
+ numberOfButtonIdsSet++;
+ if (currentSoftButtonId > maxButtonIdsSetByDev) {
+ maxButtonIdsSetByDev = currentSoftButtonId;
+ }
+ buttonIdsSetHashSet.add(softButtonObject.getButtonId());
+ }
+ }
+ if (numberOfButtonIdsSet != buttonIdsSetHashSet.size()) {
+ return false;
+ }
+
+ // Set ids for soft button objects
+ int generatedSoftButtonId = maxButtonIdsSetByDev;
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ // If the dev did not set the buttonId, the manager should set an id on the dev's behalf
+ currentSoftButtonId = softButtonObject.getButtonId();
+ if (currentSoftButtonId == SOFT_BUTTON_ID_NOT_SET_VALUE) {
+ do {
+ if (generatedSoftButtonId >= SOFT_BUTTON_ID_MAX_VALUE) {
+ generatedSoftButtonId = SOFT_BUTTON_ID_MIN_VALUE;
+ }
+ generatedSoftButtonId++;
+ } while (buttonIdsSetHashSet.contains(generatedSoftButtonId));
+ softButtonObject.setButtonId(generatedSoftButtonId);
+ buttonIdsSetHashSet.add(generatedSoftButtonId);
+ if (location == ManagerLocation.ALERT_MANAGER) {
+ softButtonIDByAlertManager.add(generatedSoftButtonId);
+ } else if (location == ManagerLocation.SOFTBUTTON_MANAGER) {
+ softButtonIDBySoftButtonManager.add(generatedSoftButtonId);
+ }
+ }
+ }
+ return true;
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java
index 10d24c765..4d93be501 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java
@@ -152,7 +152,7 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
// Auto-send an updated Show if we have new capabilities
if (softButtonObjects != null && !softButtonObjects.isEmpty() && softButtonCapabilities != null && !softButtonCapabilitiesEquals(oldSoftButtonCapabilities, softButtonCapabilities)) {
- SoftButtonReplaceOperation operation = new SoftButtonReplaceOperation(internalInterface, fileManager, softButtonCapabilities, softButtonObjects, currentMainField1);
+ SoftButtonReplaceOperation operation = new SoftButtonReplaceOperation(internalInterface, fileManager, softButtonCapabilities, softButtonObjects, getCurrentMainField1());
transactionQueue.add(operation, false);
}
}
@@ -298,7 +298,7 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
return;
}
- if (!checkAndAssignButtonIds(softButtonObjects)) {
+ if (!BaseScreenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER)) {
DebugTool.logError(TAG, "Attempted to set soft button objects, but multiple buttons had the same id");
return;
}
@@ -311,7 +311,7 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
this.softButtonObjects = softButtonObjects;
// We only need to pass the first softButtonCapabilities in the array due to the fact that all soft button capabilities are the same (i.e. there is no way to assign a softButtonCapabilities to a specific soft button).
- SoftButtonReplaceOperation operation = new SoftButtonReplaceOperation(internalInterface, fileManager.get(), softButtonCapabilities, softButtonObjects, currentMainField1);
+ SoftButtonReplaceOperation operation = new SoftButtonReplaceOperation(internalInterface, fileManager.get(), softButtonCapabilities, softButtonObjects, getCurrentMainField1());
if (batchUpdates) {
batchQueue.clear();
@@ -340,51 +340,8 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
return false;
}
- /**
- * Check if there is a collision in the ids provided by the developer and assign ids to the SoftButtonObjects that do not have ids
- *
- * @param softButtonObjects the list of the SoftButtonObject values that should be displayed on the head unit
- * @return boolean representing whether the ids are unique or not
- */
- boolean checkAndAssignButtonIds(List<SoftButtonObject> softButtonObjects) {
- // Check if multiple soft button objects have the same id
- HashSet<Integer> buttonIdsSetByDevHashSet = new HashSet<>();
- int currentSoftButtonId, numberOfButtonIdsSetByDev = 0, maxButtonIdsSetByDev = SoftButtonObject.SOFT_BUTTON_ID_MIN_VALUE;
- for (SoftButtonObject softButtonObject : softButtonObjects) {
- currentSoftButtonId = softButtonObject.getButtonId();
- if (currentSoftButtonId != SoftButtonObject.SOFT_BUTTON_ID_NOT_SET_VALUE) {
- numberOfButtonIdsSetByDev++;
- if (currentSoftButtonId > maxButtonIdsSetByDev) {
- maxButtonIdsSetByDev = currentSoftButtonId;
- }
- buttonIdsSetByDevHashSet.add(softButtonObject.getButtonId());
- }
- }
- if (numberOfButtonIdsSetByDev != buttonIdsSetByDevHashSet.size()) {
- return false;
- }
-
-
- // Set ids for soft button objects
- int generatedSoftButtonId = maxButtonIdsSetByDev;
- for (SoftButtonObject softButtonObject : softButtonObjects) {
- // If the dev did not set the buttonId, the manager should set an id on the dev's behalf
- currentSoftButtonId = softButtonObject.getButtonId();
- if (currentSoftButtonId == SoftButtonObject.SOFT_BUTTON_ID_NOT_SET_VALUE) {
- do {
- if (generatedSoftButtonId >= SoftButtonObject.SOFT_BUTTON_ID_MAX_VALUE) {
- generatedSoftButtonId = SoftButtonObject.SOFT_BUTTON_ID_MIN_VALUE;
- }
- generatedSoftButtonId++;
- } while (buttonIdsSetByDevHashSet.contains(generatedSoftButtonId));
- softButtonObject.setButtonId(generatedSoftButtonId);
- }
- }
- return true;
- }
-
private void transitionSoftButton() {
- SoftButtonTransitionOperation operation = new SoftButtonTransitionOperation(internalInterface, softButtonObjects, currentMainField1);
+ SoftButtonTransitionOperation operation = new SoftButtonTransitionOperation(internalInterface, softButtonObjects, getCurrentMainField1());
if (batchUpdates) {
for (Task task : batchQueue) {
@@ -467,10 +424,10 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
for (Task task : transactionQueue.getTasksAsList()) {
if (task instanceof SoftButtonReplaceOperation) {
SoftButtonReplaceOperation operation = (SoftButtonReplaceOperation) task;
- operation.setCurrentMainField1(currentMainField1);
+ operation.setCurrentMainField1(getCurrentMainField1());
} else if (task instanceof SoftButtonTransitionOperation) {
SoftButtonTransitionOperation operation = (SoftButtonTransitionOperation) task;
- operation.setCurrentMainField1(currentMainField1);
+ operation.setCurrentMainField1(getCurrentMainField1());
}
}
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/DispatchGroup.java b/base/src/main/java/com/smartdevicelink/managers/screen/DispatchGroup.java
new file mode 100644
index 000000000..f6dccb715
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/DispatchGroup.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.screen;
+
+/**
+ * Created by Julian Kast on 12/10/20.
+ */
+class DispatchGroup {
+ private int count;
+ private Runnable runnable;
+ DispatchGroup() {
+ count = 0;
+ }
+ synchronized void enter() {
+ count++;
+ }
+ synchronized void leave() {
+ count--;
+ run();
+ }
+ void notify(Runnable runnable) {
+ this.runnable = runnable;
+ run();
+ }
+ private void run() {
+ if (count <= 0 && runnable != null) {
+ runnable.run();
+ }
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/PresentAlertOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/PresentAlertOperation.java
new file mode 100644
index 000000000..9957ad309
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/PresentAlertOperation.java
@@ -0,0 +1,524 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.managers.screen;
+
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.managers.AlertCompletionListener;
+import com.smartdevicelink.managers.CompletionListener;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.ManagerUtility;
+import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.file.MultipleFileCompletionListener;
+import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.rpc.Alert;
+import com.smartdevicelink.proxy.rpc.AlertResponse;
+import com.smartdevicelink.proxy.rpc.CancelInteraction;
+import com.smartdevicelink.proxy.rpc.SoftButton;
+import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities;
+import com.smartdevicelink.proxy.rpc.TTSChunk;
+import com.smartdevicelink.proxy.rpc.WindowCapability;
+import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
+import com.smartdevicelink.util.DebugTool;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Operation that handles uploading images and audio data needed by the alert and, once the data uploads are complete, sending the alert.
+ *
+ * Created by Julian Kast on 12/10/20.
+ */
+public class PresentAlertOperation extends Task {
+ private static final String TAG = "PresentAlertOperation";
+ private AlertView alertView;
+ private AlertCompletionListener listener;
+ private final WeakReference<ISdl> internalInterface;
+ private final WeakReference<FileManager> fileManager;
+ WindowCapability currentWindowCapability;
+ private int cancelId;
+ private List<SpeechCapabilities> speechCapabilities;
+ boolean isAlertPresented;
+ static int SOFTBUTTON_COUNT = 4;
+ private BaseAlertManager.AlertSoftButtonClearListener alertSoftButtonClearListener;
+
+ public PresentAlertOperation(ISdl internalInterface, AlertView alertView, WindowCapability currentCapabilities, List<SpeechCapabilities> speechCapabilities, FileManager fileManager, Integer cancelId, AlertCompletionListener listener, BaseAlertManager.AlertSoftButtonClearListener alertSoftButtonClearListener) {
+ super("PresentAlertOperation");
+ this.internalInterface = new WeakReference<>(internalInterface);
+ this.fileManager = new WeakReference<>(fileManager);
+ this.currentWindowCapability = currentCapabilities;
+ this.speechCapabilities = speechCapabilities;
+ this.alertView = alertView.clone();
+ this.listener = listener;
+ this.cancelId = cancelId;
+ this.isAlertPresented = false;
+ this.alertSoftButtonClearListener = alertSoftButtonClearListener;
+
+ this.alertView.canceledListener = new AlertCanceledListener() {
+ @Override
+ public void onAlertCanceled() {
+ cancelAlert();
+ }
+ };
+ alertView.canceledListener = this.alertView.canceledListener;
+ }
+
+ @Override
+ public void onExecute() {
+ DebugTool.logInfo(TAG, "Alert Operation: Executing present Alert operation");
+ start();
+ }
+
+ private void start() {
+ if (getState() == Task.CANCELED) {
+ finishOperation(false, null);
+ return;
+ }
+ if (!isValidAlertViewData(alertView)) {
+ if (alertView.getAudio() != null && alertView.getAudio().getAudioData().size() > 0) {
+ DebugTool.logError(TAG, "The module does not support the use of only audio file data in an alert. " +
+ "The alert has no data and can not be sent to the module. " +
+ "The use of audio file data in an alert is only supported on modules supporting RPC Spec v5.0 or newer");
+ } else {
+ DebugTool.logError(TAG, "The alert data is invalid." +
+ " At least either text, secondaryText or audio needs to be provided. " +
+ "Make sure to set at least the text, secondaryText or audio properties on the AlertView");
+ }
+ finishOperation(false, null);
+ return;
+ }
+ final DispatchGroup uploadFilesTask = new DispatchGroup();
+
+ // Enter DispatchGroup twice for two tasks needing to be completed, One for uploading images and one for uploading audio files
+ uploadFilesTask.enter();
+ uploadFilesTask.enter();
+
+ // DispatchGroup notify when all tasks are done
+ uploadFilesTask.notify(new Runnable() {
+ @Override
+ public void run() {
+ presentAlert();
+ }
+ });
+
+ // DispatchGroup Task 1: uploading images
+ uploadImages(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ uploadFilesTask.leave();
+ }
+ });
+
+ // DispatchGroup Task 2: uploading audio files
+ uploadAudioFiles(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ uploadFilesTask.leave();
+ }
+ });
+ }
+
+ /**
+ * Checks the `AlertView` data to make sure it conforms to the RPC Spec, which says that at least either `alertText1`, `alertText2` or `TTSChunks` need to be provided.
+ * @param alertView - Alert data that needs to be presented
+ * @return true if AlertView data conforms to RPC Spec
+ */
+ private boolean isValidAlertViewData(AlertView alertView) {
+ if (alertView.getText() != null && alertView.getText().length() > 0) {
+ return true;
+ }
+ if (alertView.getSecondaryText() != null && alertView.getSecondaryText().length() > 0) {
+ return true;
+ }
+ if (alertView.getAudio() != null && getTTSChunksForAlert(alertView) != null) {
+ return true;
+ }
+ return false;
+ }
+
+ // Upload methods
+
+ /**
+ * Upload the alert audio files.
+ *
+ * @param listener - CompletionListener called when all audio files have been uploaded
+ */
+ private void uploadAudioFiles(final CompletionListener listener) {
+ if (!supportsAlertAudioFile()) {
+ DebugTool.logInfo(TAG, "Module does not support audio files for alerts, skipping upload of audio files");
+ listener.onComplete(true);
+ return;
+ }
+
+ if (alertView.getAudio() == null || alertView.getAudio().getAudioData() == null || alertView.getAudio().getAudioData().size() == 0) {
+ DebugTool.logInfo(TAG, "No audio files need to be uploaded for alert");
+ listener.onComplete(true);
+ return;
+ }
+
+ List<SdlFile> filesToBeUploaded = new ArrayList<>();
+ for (TTSChunk ttsChunk : alertView.getAudio().getAudioData()) {
+ if(ttsChunk.getType() != SpeechCapabilities.FILE){
+ continue;
+ }
+ SdlFile audioFile = alertView.getAudio().getAudioFiles().get(ttsChunk.getText());
+ if (fileManager.get() == null || !fileManager.get().fileNeedsUpload(audioFile)) {
+ continue;
+ }
+ filesToBeUploaded.add(audioFile);
+ }
+
+ if (filesToBeUploaded.size() == 0) {
+ DebugTool.logInfo(TAG, "No audio files need to be uploaded for alert");
+ listener.onComplete(true);
+ return;
+ }
+
+ DebugTool.logInfo(TAG, "Uploading audio files for alert");
+
+ if (fileManager.get() != null) {
+ fileManager.get().uploadFiles(filesToBeUploaded, new MultipleFileCompletionListener() {
+ @Override
+ public void onComplete(Map<String, String> errors) {
+ if (getState() == Task.CANCELED) {
+ finishOperation(false, null);
+ return;
+ }
+ if (errors != null) {
+ DebugTool.logError(TAG, "Error uploading alert audio files:" + errors.toString());
+ } else {
+ DebugTool.logInfo(TAG, "All alert audio files uploaded");
+ }
+ listener.onComplete(true);
+ }
+ });
+ }
+ }
+
+ /**
+ * Upload the alert icon and soft button images.
+ *
+ * @param listener - CompletionListener called when all images have been uploaded.
+ */
+ private void uploadImages(final CompletionListener listener) {
+ List<SdlArtwork> artworksToBeUploaded = new ArrayList<>();
+
+ if (supportsAlertIcon() && fileManager.get() != null && fileManager.get().fileNeedsUpload(alertView.getIcon())) {
+ artworksToBeUploaded.add(alertView.getIcon());
+ }
+
+ if (alertView.getSoftButtons() != null) {
+ for (int i = 0; i < getSoftButtonCount(); i++) {
+ SoftButtonObject object = alertView.getSoftButtons().get(i);
+ if (supportsSoftButtonImages() && object.getCurrentState() != null && fileManager.get() != null && fileManager.get().fileNeedsUpload(object.getCurrentState().getArtwork())) {
+ artworksToBeUploaded.add(object.getCurrentState().getArtwork());
+ }
+ }
+ }
+
+ if (artworksToBeUploaded.size() == 0) {
+ DebugTool.logInfo(TAG, "No Images to upload for alert");
+ listener.onComplete(true);
+ return;
+ }
+
+ DebugTool.logInfo(TAG, "Uploading images for alert");
+
+ if (fileManager.get() != null) {
+ fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() {
+ @Override
+ public void onComplete(Map<String, String> errors) {
+ if (getState() == Task.CANCELED) {
+ DebugTool.logInfo(TAG, "Operation canceled");
+ finishOperation(false, null);
+ return;
+ }
+
+ if (errors != null) {
+ DebugTool.logError(TAG, "AlertManager artwork failed to upload with error: " + errors.toString());
+ } else {
+ DebugTool.logInfo(TAG, "All alert images uploaded");
+ }
+ listener.onComplete(true);
+ }
+ });
+ }
+ }
+
+ /**
+ * Sends the alert RPC to the module. The operation is finished once a response has been received from the module.
+ */
+ private void presentAlert() {
+ if (getState() == Task.CANCELED) {
+ DebugTool.logInfo(TAG, "Operation canceled");
+ finishOperation(false, null);
+ return;
+ }
+
+ isAlertPresented = true;
+
+ Alert alert = alertRpc();
+
+ alert.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (!response.getSuccess()) {
+ DebugTool.logError(TAG, "There was an error presenting the alert: " + response.getInfo());
+ } else {
+ DebugTool.logInfo(TAG, "Alert finished presenting");
+ }
+ finishOperation(response.getSuccess(), ((AlertResponse) response).getTryAgainTime());
+ }
+ });
+ internalInterface.get().sendRPC(alert);
+ }
+
+ /**
+ * Cancels the alert. If the alert has not yet been sent to the module, it will not be sent.
+ * If the alert is already presented on the module, the alert will be immediately dismissed.
+ * Canceling an already presented alert will only work if connected to modules supporting RPC spec versions 6.0+.
+ * On older versions alert will not be dismissed.
+ */
+ private void cancelAlert() {
+ if (getState() == Task.FINISHED) {
+ DebugTool.logInfo(TAG, "This operation has already finished so it can not be canceled");
+ return;
+ } else if (getState() == Task.CANCELED) {
+ DebugTool.logInfo(TAG, "This operation has already been canceled. It will be finished at some point during the operation.");
+ return;
+ } else if (getState() == Task.IN_PROGRESS) {
+ if (internalInterface.get() != null && internalInterface.get().getSdlMsgVersion() != null && internalInterface.get().getSdlMsgVersion().getMajorVersion() < 6) {
+ DebugTool.logError(TAG, "Attempting to cancel this operation in-progress; if the alert is already presented on the module, it cannot be dismissed.");
+ this.cancelTask();
+ return;
+ } else if (!isAlertPresented) {
+ DebugTool.logError(TAG, "Alert has not yet been sent to the module. Alert will not be shown..");
+ this.cancelTask();
+ return;
+ }
+
+ DebugTool.logInfo(TAG, "Canceling the presented alert");
+
+ CancelInteraction cancelInteraction = new CancelInteraction(FunctionID.ALERT.getId(), cancelId);
+ cancelInteraction.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (!response.getSuccess()) {
+ DebugTool.logInfo(TAG, "Error canceling the presented alert: " + response.getInfo());
+ onFinished();
+ return;
+ }
+ DebugTool.logInfo(TAG, "The presented alert was canceled successfully");
+ onFinished();
+ }
+ });
+ internalInterface.get().sendRPC(cancelInteraction);
+ } else {
+ DebugTool.logInfo(TAG, "Cancelling an alert that has not yet started");
+ this.cancelTask();
+ }
+ }
+
+ // Getters / Setters
+
+ Alert alertRpc() {
+ Alert alert = new Alert();
+ alert = assembleAlertText(alert);
+ alert.setDuration(alertView.getTimeout() * 1000);
+
+ if (alertView.getIcon() != null && supportsAlertIcon() && !(fileManager.get().hasUploadedFile(alertView.getIcon()))) {
+ alert.setAlertIcon(alertView.getIcon().getImageRPC());
+ }
+
+ alert.setProgressIndicator(alertView.isShowWaitIndicator());
+ alert.setCancelID(this.cancelId);
+
+ if (alertView.getSoftButtons() != null) {
+ List<SoftButton> softButtons = new ArrayList<>();
+ for (int i = 0; i < getSoftButtonCount(); i++) {
+ softButtons.add(alertView.getSoftButtons().get(i).getCurrentStateSoftButton());
+ }
+ alert.setSoftButtons(softButtons);
+ }
+
+ if (alertView.getAudio() != null) {
+ alert.setPlayTone(alertView.getAudio().isPlayTone());
+ alert.setTtsChunks(getTTSChunksForAlert(alertView));
+ }
+ return alert;
+ }
+
+ /**
+ * Limits the number of SoftButtons that can be set in the AlertRPC to 4
+ *
+ * @return The maximum number of soft buttons that can be sent to the module
+ */
+ private int getSoftButtonCount() {
+ return alertView.getSoftButtons().size() <= 4 ? alertView.getSoftButtons().size() : SOFTBUTTON_COUNT;
+ }
+
+ /**
+ * Checks if AudioFiles are supported by module and removes them form audioData list if they are not
+ * @param alertView
+ * @return List of ttsChunks
+ */
+ private List<TTSChunk> getTTSChunksForAlert(AlertView alertView) {
+ AlertAudioData alertAudioData = alertView.getAudio();
+ List<TTSChunk> ttsChunks = new ArrayList<>();
+ for (TTSChunk chunk : alertAudioData.getAudioData()) {
+ if (chunk.getType() == SpeechCapabilities.FILE && !supportsAlertAudioFile()) {
+ continue;
+ }
+ ttsChunks.add(chunk);
+ }
+ return ttsChunks.size() > 0 ? ttsChunks : null;
+ }
+
+ /**
+ * Checks if the connected module or current template supports soft button images.
+ *
+ * @return True if soft button images are currently supported; false if not.
+ */
+ private boolean supportsSoftButtonImages() {
+ SoftButtonCapabilities softButtonCapabilities = currentWindowCapability.getSoftButtonCapabilities().get(0);
+ return softButtonCapabilities.getImageSupported().booleanValue();
+ }
+
+ /**
+ * Checks if the connected module supports audio files. Using an audio file in an alert will only work if connected to modules supporting RPC spec versions 5.0+.
+ * If the module does not return a speechCapabilities, assume that the module supports playing an audio file.
+ *
+ * @return True if the module supports playing audio files in an alert; false if not.
+ */
+ private boolean supportsAlertAudioFile() {
+ return (internalInterface.get() != null && internalInterface.get().getSdlMsgVersion() != null && internalInterface.get().getSdlMsgVersion().getMajorVersion() >= 5 && speechCapabilities != null && speechCapabilities.contains(SpeechCapabilities.FILE));
+ }
+
+ /**
+ * Checks if the connected module or current template supports alert icons.
+ *
+ * @return True if alert icons are currently supported; false if not.
+ */
+ private boolean supportsAlertIcon() {
+ return ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(currentWindowCapability, ImageFieldName.alertIcon);
+ }
+
+ // Text Helpers
+
+ private Alert assembleAlertText(Alert alert) {
+ List<String> nonNullFields = findNonNullTextFields();
+ if (nonNullFields.isEmpty()) {
+ return alert;
+ }
+ int numberOfLines = currentWindowCapability!= null ? ManagerUtility.WindowCapabilityUtility.getMaxNumberOfAlertFieldLines(currentWindowCapability) : 3;
+ switch (numberOfLines) {
+ case 1:
+ alert = assembleOneLineAlertText(alert, nonNullFields);
+ break;
+ case 2:
+ alert = assembleTwoLineAlertText(alert, nonNullFields);
+ break;
+ case 3:
+ alert = assembleThreeLineAlertText(alert, nonNullFields);
+ break;
+ }
+ return alert;
+ }
+
+ private List<String> findNonNullTextFields() {
+ List<String> list = new ArrayList<>();
+
+ if (alertView.getText() != null && alertView.getText().length() > 0) {
+ list.add(alertView.getText());
+ }
+
+ if (alertView.getSecondaryText() != null && alertView.getSecondaryText().length() > 0) {
+ list.add(alertView.getSecondaryText());
+ }
+
+ if (alertView.getTertiaryText() != null && alertView.getTertiaryText().length() > 0) {
+ list.add(alertView.getTertiaryText());
+ }
+
+ return list;
+ }
+
+ private Alert assembleOneLineAlertText(Alert alert, List<String> alertFields) {
+ StringBuilder alertString1 = new StringBuilder();
+ for (int i = 0; i < alertFields.size(); i++) {
+ if (i > 0) {
+ alertString1.append(" - ").append(alertFields.get(i));
+ } else {
+ alertString1.append(alertFields.get(i));
+ }
+ }
+ alert.setAlertText1(alertString1.toString());
+ return alert;
+ }
+
+ private Alert assembleTwoLineAlertText(Alert alert, List<String> alertFields) {
+ if (alertFields.size() <= 2) {
+ alert.setAlertText1(alertFields.size() > 0 ? alertFields.get(0) : null);
+ alert.setAlertText2(alertFields.size() > 1 ? alertFields.get(1) : null);
+ } else {
+ alert.setAlertText1(alertFields.size() > 0 ? alertFields.get(0) : null);
+ alert.setAlertText2(alertFields.get(1) + " - " + alertFields.get(2));
+ }
+ return alert;
+ }
+
+ private Alert assembleThreeLineAlertText(Alert alert, List<String> alertFields) {
+ alert.setAlertText1(alertFields.size() > 0 ? alertFields.get(0) : null);
+ alert.setAlertText2(alertFields.size() > 1 ? alertFields.get(1) : null);
+ alert.setAlertText3(alertFields.size() > 2 ? alertFields.get(2) : null);
+ return alert;
+ }
+
+ private void finishOperation(boolean success, Integer tryAgainTime) {
+ DebugTool.logInfo(TAG, "Finishing present alert operation");
+ if (listener != null) {
+ listener.onComplete(success, tryAgainTime);
+ }
+ // If alertView has SoftButtons, we need to clear out their references in BaseAlertManager
+ if (alertView.getSoftButtons() != null) {
+ alertSoftButtonClearListener.onButtonClear(alertView.getSoftButtons());
+ }
+ onFinished();
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java
index e401f8824..76301bc07 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java
@@ -50,7 +50,7 @@ import java.util.List;
*
* @see SoftButtonState
*/
-public class SoftButtonObject {
+public class SoftButtonObject implements Cloneable{
private static final String TAG = "SoftButtonObject";
static final int SOFT_BUTTON_ID_NOT_SET_VALUE = -1;
static final int SOFT_BUTTON_ID_MIN_VALUE = 0;
@@ -295,12 +295,14 @@ public class SoftButtonObject {
}
/**
+ * DO NOT USE! let the managers assign ID's. In next major version change this will be restricted to the library
* Sets the id of the SoftButtonObject <br>
* <strong>Note: If the developer did not set buttonId, the manager will automatically assign an id before the SoftButtons are sent to the head unit.
* Please note that the manager may reuse ids from previous batch of SoftButtons that were already sent to the head unit</strong>
*
* @param buttonId an int value that represents the id of the SoftButtonObject
*/
+ @Deprecated
public void setButtonId(int buttonId) {
if (buttonId < SOFT_BUTTON_ID_MIN_VALUE) {
DebugTool.logError(TAG, "buttonId has to be equal or more than " + SOFT_BUTTON_ID_MIN_VALUE);
@@ -375,4 +377,22 @@ public class SoftButtonObject {
// return comparison
return hashCode() == o.hashCode();
}
+
+ /**
+ * Creates a deep copy of the object
+ *
+ * @return deep copy of the object, null if an exception occurred
+ */
+ @Override
+ public SoftButtonObject clone() {
+ try {
+ SoftButtonObject softButtonObject = (SoftButtonObject) super.clone();
+ return softButtonObject;
+ } catch (CloneNotSupportedException e) {
+ if (DebugTool.isDebugEnabled()) {
+ throw new RuntimeException("Clone not supported by super class");
+ }
+ }
+ return null;
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java
index 73658b257..e2611a959 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java
@@ -116,7 +116,7 @@ class SoftButtonReplaceOperation extends Task {
List<SdlArtwork> initialStatesToBeUploaded = new ArrayList<>();
for (SoftButtonObject softButtonObject : softButtonObjects) {
SoftButtonState softButtonState = softButtonObject.getCurrentState();
- if (softButtonState != null && artworkNeedsUpload(softButtonState.getArtwork())) {
+ if (softButtonState != null && fileManager.get()!= null && fileManager.get().fileNeedsUpload(softButtonState.getArtwork()) && supportsSoftButtonImages()) {
initialStatesToBeUploaded.add(softButtonState.getArtwork());
}
}
@@ -163,7 +163,7 @@ class SoftButtonReplaceOperation extends Task {
if (softButtonState.getName().equals(softButtonObject.getCurrentState().getName())) {
continue;
}
- if (artworkNeedsUpload(softButtonState.getArtwork())) {
+ if (fileManager.get() != null && fileManager.get().fileNeedsUpload(softButtonState.getArtwork()) && supportsSoftButtonImages()) {
otherStatesToBeUploaded.add(softButtonState.getArtwork());
}
}
@@ -284,14 +284,6 @@ class SoftButtonReplaceOperation extends Task {
}
}
- private boolean artworkNeedsUpload(SdlArtwork artwork) {
- return (artwork != null
- && fileManager.get() != null
- && !fileManager.get().hasUploadedFile(artwork)
- && softButtonCapabilities.getImageSupported()
- && !artwork.isStaticIcon());
- }
-
private boolean currentStateHasImages() {
for (SoftButtonObject softButtonObject : softButtonObjects) {
if (softButtonObject.getCurrentState().getArtwork() != null) {
@@ -304,7 +296,7 @@ class SoftButtonReplaceOperation extends Task {
private boolean allCurrentStateImagesAreUploaded() {
for (SoftButtonObject softButtonObject : softButtonObjects) {
SdlArtwork artwork = softButtonObject.getCurrentState().getArtwork();
- if (artworkNeedsUpload(artwork)) {
+ if (fileManager.get() != null && fileManager.get().fileNeedsUpload(artwork) && supportsSoftButtonImages()) {
return false;
}
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/TextsAndGraphicsState.java b/base/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicState.java
index 3876ecaf4..3876ecaf4 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/TextsAndGraphicsState.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicState.java
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperation.java
index 2d0390e32..683820ee5 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicUpdateOperation.java
@@ -103,7 +103,7 @@ class TextAndGraphicUpdateOperation extends Task {
}
});
- } else if (!sdlArtworkNeedsUpload(updatedState.getPrimaryGraphic()) && !sdlArtworkNeedsUpload(updatedState.getSecondaryGraphic())) {
+ } else if (fileManager.get() != null && !fileManager.get().fileNeedsUpload(updatedState.getPrimaryGraphic()) && !fileManager.get().fileNeedsUpload(updatedState.getSecondaryGraphic())) {
DebugTool.logInfo(TAG, "Images already uploaded, sending full update");
// The files to be updated are already uploaded, send the full show immediately
sendShow(show, new CompletionListener() {
@@ -260,8 +260,8 @@ class TextAndGraphicUpdateOperation extends Task {
Show createImageOnlyShowWithPrimaryArtwork(SdlArtwork primaryArtwork, SdlArtwork secondaryArtwork) {
Show newShow = new Show();
- newShow.setGraphic((primaryArtwork != null && !(sdlArtworkNeedsUpload(primaryArtwork))) ? primaryArtwork.getImageRPC() : null);
- newShow.setSecondaryGraphic((secondaryArtwork != null && !(sdlArtworkNeedsUpload(secondaryArtwork))) ? secondaryArtwork.getImageRPC() : null);
+ newShow.setGraphic(shouldRPCIncludeImage(primaryArtwork) ? primaryArtwork.getImageRPC() : null);
+ newShow.setSecondaryGraphic(shouldRPCIncludeImage(secondaryArtwork) ? secondaryArtwork.getImageRPC() : null);
if (newShow.getGraphic() == null && newShow.getSecondaryGraphic() == null) {
DebugTool.logInfo(TAG, "No graphics to upload");
return null;
@@ -645,10 +645,9 @@ class TextAndGraphicUpdateOperation extends Task {
return array;
}
- @SuppressWarnings("BooleanMethodIsAlwaysInverted")
- private boolean sdlArtworkNeedsUpload(SdlArtwork artwork) {
- if (fileManager.get() != null) {
- return artwork != null && !fileManager.get().hasUploadedFile(artwork) && !artwork.isStaticIcon();
+ private boolean shouldRPCIncludeImage(SdlArtwork artwork) {
+ if (artwork != null) {
+ return artwork.isStaticIcon() || (fileManager.get() != null && fileManager.get().hasUploadedFile(artwork));
}
return false;
}
@@ -664,9 +663,10 @@ class TextAndGraphicUpdateOperation extends Task {
String currentScreenDataPrimaryGraphicName = (currentScreenData != null && currentScreenData.getPrimaryGraphic() != null) ? currentScreenData.getPrimaryGraphic().getName() : null;
String primaryGraphicName = updatedState.getPrimaryGraphic() != null ? updatedState.getPrimaryGraphic().getName() : null;
- boolean graphicMatchesExisting = CompareUtils.areStringsEqual(currentScreenDataPrimaryGraphicName, primaryGraphicName, true, true);
+ boolean graphicNameMatchesExisting = CompareUtils.areStringsEqual(currentScreenDataPrimaryGraphicName, primaryGraphicName, true, true);
+ boolean shouldOverwriteGraphic = updatedState.getPrimaryGraphic() != null && updatedState.getPrimaryGraphic().getOverwrite();
- return templateSupportsPrimaryArtwork && !graphicMatchesExisting;
+ return templateSupportsPrimaryArtwork && (shouldOverwriteGraphic || !graphicNameMatchesExisting);
}
/**
@@ -680,13 +680,14 @@ class TextAndGraphicUpdateOperation extends Task {
String currentScreenDataSecondaryGraphicName = (currentScreenData != null && currentScreenData.getSecondaryGraphic() != null) ? currentScreenData.getSecondaryGraphic().getName() : null;
String secondaryGraphicName = updatedState.getSecondaryGraphic() != null ? updatedState.getSecondaryGraphic().getName() : null;
- boolean graphicMatchesExisting = CompareUtils.areStringsEqual(currentScreenDataSecondaryGraphicName, secondaryGraphicName, true, true);
+ boolean graphicNameMatchesExisting = CompareUtils.areStringsEqual(currentScreenDataSecondaryGraphicName, secondaryGraphicName, true, true);
+ boolean shouldOverwriteGraphic = updatedState.getSecondaryGraphic() != null && updatedState.getSecondaryGraphic().getOverwrite();
// Cannot detect if there is a secondary image below v5.0, so we'll just try to detect if the primary image is allowed and allow the secondary image if it is.
if (internalInterface.get() != null && internalInterface.get().getSdlMsgVersion().getMajorVersion() >= 5) {
- return templateSupportsSecondaryArtwork && !graphicMatchesExisting;
+ return templateSupportsSecondaryArtwork && (shouldOverwriteGraphic || !graphicNameMatchesExisting);
} else {
- return templateSupportsImageField(ImageFieldName.graphic) && !graphicMatchesExisting;
+ return templateSupportsImageField(ImageFieldName.graphic) && (shouldOverwriteGraphic || !graphicNameMatchesExisting);
}
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java
index 70c9f2546..44303c4bd 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java
@@ -48,6 +48,8 @@ import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCNotification;
import com.smartdevicelink.proxy.rpc.DisplayCapability;
+import com.smartdevicelink.proxy.rpc.KeyboardCapabilities;
+import com.smartdevicelink.proxy.rpc.KeyboardLayoutCapability;
import com.smartdevicelink.proxy.rpc.KeyboardProperties;
import com.smartdevicelink.proxy.rpc.OnHMIStatus;
import com.smartdevicelink.proxy.rpc.WindowCapability;
@@ -64,8 +66,11 @@ import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
import com.smartdevicelink.util.DebugTool;
import java.lang.ref.WeakReference;
+import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
+import java.util.Locale;
/**
* <strong>ChoiceSetManager</strong> <br>
@@ -97,7 +102,8 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
int nextChoiceId;
int nextCancelId;
final int choiceCellIdMin = 1;
- final int choiceCellCancelIdMin = 1;
+ private final int choiceCellCancelIdMin = 101;
+ private final int choiceCellCancelIdMax = 200;
boolean isVROptional;
BaseChoiceSetManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
@@ -110,6 +116,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
// capabilities
currentSystemContext = SystemContext.SYSCTXT_MAIN;
currentHMILevel = HMILevel.HMI_NONE;
+ keyboardConfiguration = defaultKeyboardConfiguration();
addListeners();
// setting/instantiating class vars
@@ -119,7 +126,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
nextChoiceId = choiceCellIdMin;
nextCancelId = choiceCellCancelIdMin;
isVROptional = false;
- keyboardConfiguration = defaultKeyboardConfiguration();
currentlyPresentedKeyboardOperation = null;
}
@@ -193,9 +199,12 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
return;
}
- final HashSet<ChoiceCell> choicesToUpload = new HashSet<>(choices);
- choicesToUpload.removeAll(preloadedChoices);
- choicesToUpload.removeAll(pendingPreloadChoices);
+ LinkedHashSet<ChoiceCell> mutableChoicesToUpload = getChoicesToBeUploadedWithArray(choices);
+
+ mutableChoicesToUpload.removeAll(preloadedChoices);
+ mutableChoicesToUpload.removeAll(pendingPreloadChoices);
+
+ final LinkedHashSet<ChoiceCell> choicesToUpload = (LinkedHashSet<ChoiceCell>) mutableChoicesToUpload.clone();
if (choicesToUpload.size() == 0) {
if (listener != null) {
@@ -357,11 +366,11 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
if (keyboardListener == null) {
// Non-searchable choice set
DebugTool.logInfo(TAG, "Creating non-searchable choice set");
- presentOp = new PresentChoiceSetOperation(internalInterface, pendingPresentationSet, mode, null, null, privateChoiceListener, nextCancelId++);
+ presentOp = new PresentChoiceSetOperation(internalInterface, pendingPresentationSet, mode, null, null, privateChoiceListener, getNextCancelId());
} else {
// Searchable choice set
DebugTool.logInfo(TAG, "Creating searchable choice set");
- presentOp = new PresentChoiceSetOperation(internalInterface, pendingPresentationSet, mode, keyboardConfiguration, keyboardListener, privateChoiceListener, nextCancelId++);
+ presentOp = new PresentChoiceSetOperation(internalInterface, pendingPresentationSet, mode, keyboardConfiguration, keyboardListener, privateChoiceListener, getNextCancelId());
}
transactionQueue.add(presentOp, false);
@@ -393,6 +402,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
DebugTool.logWarning(TAG, "There is a current or pending choice set, cancelling and continuing.");
}
+ customKeyboardConfig = createValidKeyboardConfigurationBasedOnKeyboardCapabilitiesFromConfiguration(customKeyboardConfig);
if (customKeyboardConfig == null) {
if (this.keyboardConfiguration != null) {
customKeyboardConfig = this.keyboardConfiguration;
@@ -403,7 +413,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
// Present a keyboard with the choice set that we used to test VR's optional state
DebugTool.logInfo(TAG, "Presenting Keyboard - Choice Set Manager");
- Integer keyboardCancelID = nextCancelId++;
+ Integer keyboardCancelID = getNextCancelId();
PresentKeyboardOperation keyboardOp = new PresentKeyboardOperation(internalInterface, keyboardConfiguration, initialText, customKeyboardConfig, listener, keyboardCancelID);
currentlyPresentedKeyboardOperation = keyboardOp;
transactionQueue.add(keyboardOp, false);
@@ -452,19 +462,56 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
* @param keyboardConfiguration - the custom keyboard configuration to be used when the keyboard is displayed
*/
public void setKeyboardConfiguration(@Nullable KeyboardProperties keyboardConfiguration) {
- if (keyboardConfiguration == null) {
+ KeyboardProperties properties = createValidKeyboardConfigurationBasedOnKeyboardCapabilitiesFromConfiguration(keyboardConfiguration);
+ if (properties == null) {
this.keyboardConfiguration = defaultKeyboardConfiguration();
} else {
- KeyboardProperties properties = new KeyboardProperties();
- properties.setLanguage((keyboardConfiguration.getLanguage() == null ? Language.EN_US : keyboardConfiguration.getLanguage()));
- properties.setKeyboardLayout((keyboardConfiguration.getKeyboardLayout() == null ? KeyboardLayout.QWERTZ : keyboardConfiguration.getKeyboardLayout()));
- properties.setKeypressMode((keyboardConfiguration.getKeypressMode() == null ? KeypressMode.RESEND_CURRENT_ENTRY : keyboardConfiguration.getKeypressMode()));
- properties.setLimitedCharacterList(keyboardConfiguration.getLimitedCharacterList());
- properties.setAutoCompleteText(keyboardConfiguration.getAutoCompleteText());
this.keyboardConfiguration = properties;
}
}
+ // Takes a keyboard configuration (SDLKeyboardProperties) and creates a valid version of it, if possible, based on this object's internal keyboardCapabilities
+ private KeyboardProperties createValidKeyboardConfigurationBasedOnKeyboardCapabilitiesFromConfiguration(@Nullable KeyboardProperties keyboardConfiguration) {
+ KeyboardCapabilities keyboardCapabilities = defaultMainWindowCapability != null ? defaultMainWindowCapability.getKeyboardCapabilities() : null;
+
+ // If there are no keyboard capabilities, if there is no passed keyboard configuration, or if there is no layout to the passed keyboard configuration, just pass back the passed in configuration
+ if (keyboardCapabilities == null || keyboardConfiguration == null || keyboardConfiguration.getKeyboardLayout() == null) {
+ return keyboardConfiguration;
+ }
+
+ KeyboardLayoutCapability selectedLayoutCapability = null;
+ for (KeyboardLayoutCapability layoutCapability : keyboardCapabilities.getSupportedKeyboards()) {
+ if (layoutCapability.getKeyboardLayout().equals(keyboardConfiguration.getKeyboardLayout())) {
+ selectedLayoutCapability = layoutCapability;
+ break;
+ }
+ }
+
+ if (selectedLayoutCapability == null) {
+ DebugTool.logError(TAG, String.format("Configured keyboard layout is not supported: %s", keyboardConfiguration.getKeyboardLayout()));
+ return null;
+ }
+
+ KeyboardProperties modifiedKeyboardConfiguration = (KeyboardProperties) keyboardConfiguration.clone();
+
+ if (keyboardConfiguration.getCustomKeys() == null || keyboardConfiguration.getCustomKeys().isEmpty()) {
+ modifiedKeyboardConfiguration.setCustomKeys(null);
+ } else {
+ // If there are more custom keys than are allowed for the selected keyboard layout, we need to trim the number of keys to only use the first n number of custom keys, where n is the number of allowed custom keys for that layout.
+ int numConfigurableKeys = selectedLayoutCapability.getNumConfigurableKeys();
+ if (keyboardConfiguration.getCustomKeys().size() > numConfigurableKeys) {
+ modifiedKeyboardConfiguration.setCustomKeys(keyboardConfiguration.getCustomKeys().subList(0, numConfigurableKeys));
+ DebugTool.logWarning(TAG, String.format(Locale.US, "%d custom keys set, but the selected layout: %s only supports %d. Dropping the rest.", keyboardConfiguration.getCustomKeys().size(), keyboardConfiguration.getKeyboardLayout(), numConfigurableKeys));
+ }
+ }
+
+ // If the keyboard does not support masking input characters, we will remove it from the keyboard configuration
+ if (!keyboardCapabilities.getMaskInputCharactersSupported()) {
+ modifiedKeyboardConfiguration.setMaskInputCharacters(null);
+ }
+
+ return modifiedKeyboardConfiguration;
+ }
// GETTERS
/**
@@ -488,7 +535,40 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
return choicesSet;
}
- void updateIdsOnChoices(HashSet<ChoiceCell> choices) {
+ /**
+ * Checks if 2 or more cells have the same text/title. In case this condition is true, this function will handle the presented issue by adding "(count)".
+ * E.g. Choices param contains 2 cells with text/title "Address" will be handled by updating the uniqueText/uniqueTitle of the second cell to "Address (2)".
+ * @param choices The list of choiceCells to be uploaded.
+ */
+ void addUniqueNamesToCells(LinkedHashSet<ChoiceCell> choices) {
+ HashMap<String, Integer> dictCounter = new HashMap<>();
+
+ for (ChoiceCell cell : choices) {
+ String cellName = cell.getText();
+ Integer counter = dictCounter.get(cellName);
+
+ if (counter != null) {
+ dictCounter.put(cellName, ++counter);
+ cell.setUniqueText(cell.getText() + " (" + counter + ")");
+ } else {
+ dictCounter.put(cellName, 1);
+ }
+ }
+ }
+
+ private LinkedHashSet<ChoiceCell> getChoicesToBeUploadedWithArray(List<ChoiceCell> choices) {
+ LinkedHashSet<ChoiceCell> choiceSet = new LinkedHashSet<>(choices);
+ // If we're running on a connection < RPC 7.1, we need to de-duplicate cells because presenting them will fail if we have the same cell primary text.
+ if (choices != null && internalInterface.getSdlMsgVersion() != null
+ && (internalInterface.getSdlMsgVersion().getMajorVersion() < 7
+ || (internalInterface.getSdlMsgVersion().getMajorVersion() == 7 && internalInterface.getSdlMsgVersion().getMinorVersion() == 0))) {
+ addUniqueNamesToCells(choiceSet);
+ }
+ choiceSet.removeAll(preloadedChoices);
+ return choiceSet;
+ }
+
+ void updateIdsOnChoices(LinkedHashSet<ChoiceCell> choices) {
for (ChoiceCell cell : choices) {
cell.setChoiceId(this.nextChoiceId);
this.nextChoiceId++;
@@ -609,14 +689,14 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
}
}
- HashSet<String> choiceTextSet = new HashSet<>();
+ HashSet<ChoiceCell> uniqueChoiceCells = new HashSet<>();
HashSet<String> uniqueVoiceCommands = new HashSet<>();
int allVoiceCommandsCount = 0;
int choiceCellWithVoiceCommandCount = 0;
for (ChoiceCell cell : choices) {
- choiceTextSet.add(cell.getText());
+ uniqueChoiceCells.add(cell);
if (cell.getVoiceCommands() != null) {
uniqueVoiceCommands.addAll(cell.getVoiceCommands());
@@ -625,9 +705,8 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
}
}
- // Cell text MUST be unique
- if (choiceTextSet.size() < choices.size()) {
- DebugTool.logError(TAG, "Attempted to create a choice set with duplicate cell text. Cell text must be unique. The choice set will not be set.");
+ if (uniqueChoiceCells.size() != choices.size()) {
+ DebugTool.logError(TAG, "Attempted to create a choice set with a duplicate cell. Cell must have a unique value other than its primary text. The choice set will not be set.");
return false;
}
@@ -652,4 +731,18 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
defaultProperties.setKeypressMode(KeypressMode.RESEND_CURRENT_ENTRY);
return defaultProperties;
}
+
+ /**
+ * Checks and increments the cancelID to keep in in a defined range
+ *
+ * @return an integer for cancelId to be sent to operations.
+ */
+ private int getNextCancelId() {
+ if (nextCancelId >= choiceCellCancelIdMax) {
+ nextCancelId = choiceCellCancelIdMin;
+ } else {
+ nextCancelId++;
+ }
+ return nextCancelId;
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java
index 5e690fe0a..7d59bf555 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java
@@ -35,11 +35,13 @@ package com.smartdevicelink.managers.screen.choiceset;
import androidx.annotation.NonNull;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.util.DebugTool;
+import java.util.ArrayList;
import java.util.List;
-public class ChoiceCell {
- private String text, secondaryText, tertiaryText;
+public class ChoiceCell implements Cloneable{
+ private String text, secondaryText, tertiaryText, uniqueText;
private List<String> voiceCommands;
private SdlArtwork artwork, secondaryArtwork;
private Integer choiceId;
@@ -56,6 +58,7 @@ public class ChoiceCell {
*/
public ChoiceCell(@NonNull String text) {
setText(text);
+ setUniqueText(text);
setChoiceId(MAX_ID);
}
@@ -68,6 +71,7 @@ public class ChoiceCell {
*/
public ChoiceCell(@NonNull String text, List<String> voiceCommands, SdlArtwork artwork) {
setText(text);
+ setUniqueText(text);
setVoiceCommands(voiceCommands);
setArtwork(artwork);
setChoiceId(MAX_ID);
@@ -87,6 +91,7 @@ public class ChoiceCell {
setText(text);
setSecondaryText(secondaryText);
setTertiaryText(tertiaryText);
+ setUniqueText(text);
setVoiceCommands(voiceCommands);
setArtwork(artwork);
setSecondaryArtwork(secondaryArtwork);
@@ -226,6 +231,29 @@ public class ChoiceCell {
return choiceId;
}
+ /**
+ * NOTE: USED INTERNALLY
+ * Primary text of the cell to be displayed on the module. Used to distinguish cells with the
+ * same `text` but other fields are different. This is autogenerated by the screen manager.
+ * Attempting to use cells that are exactly the same (all text and artwork fields are the same)
+ * will not cause this to be used. This will not be used when connected to modules supporting RPC 7.1+.
+ *
+ * @param uniqueText - the uniqueText to be used in place of primaryText when core does not support identical names for ChoiceSets
+ */
+ void setUniqueText(String uniqueText) {
+ this.uniqueText = uniqueText;
+ }
+
+ /**
+ * NOTE: USED INTERNALLY
+ * Get the uniqueText that was used in place of primaryText
+ *
+ * @return the uniqueText for this Choice Cell
+ */
+ String getUniqueText() {
+ return uniqueText;
+ }
+
@Override
public int hashCode() {
int result = 1;
@@ -264,9 +292,37 @@ public class ChoiceCell {
@NonNull
public String toString() {
return "ChoiceCell: ID: " + this.choiceId + " Text: " + text + " - Secondary Text: " + secondaryText + " - Tertiary Text: " + tertiaryText + " " +
- "| Artwork Names: " + ((getArtwork() == null || getArtwork().getName() == null) ? "Primary Art null" : getArtwork().getName())
+ (text.equals(uniqueText) ? "" : "| Unique Text: " + uniqueText) + " | Artwork Names: " + ((getArtwork() == null || getArtwork().getName() == null) ? "Primary Art null" : getArtwork().getName())
+ " Secondary Art - " + ((getSecondaryArtwork() == null || getSecondaryArtwork().getName() == null) ? "Secondary Art null" : getSecondaryArtwork().getName()) +
" | Voice Commands Size: " + ((getVoiceCommands() == null) ? 0 : getVoiceCommands().size());
}
+ /**
+ * Creates a deep copy of the object
+ *
+ * @return deep copy of the object, null if an exception occurred
+ */
+ @Override
+ public ChoiceCell clone() {
+ try {
+ ChoiceCell clone = (ChoiceCell) super.clone();
+ if (this.artwork != null) {
+ clone.artwork = this.artwork.clone();
+ }
+ if (this.secondaryArtwork != null) {
+ clone.secondaryArtwork = this.secondaryArtwork.clone();
+ }
+ if (this.voiceCommands != null) {
+ clone.voiceCommands = new ArrayList<>(voiceCommands);
+ }
+
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ if (DebugTool.isDebugEnabled()) {
+ throw new RuntimeException("Clone not supported by super class");
+ }
+ }
+
+ return null;
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSet.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSet.java
index d3d938c7b..d38cf9713 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSet.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSet.java
@@ -59,13 +59,20 @@ public class ChoiceSet {
ChoiceSetCanceledListener canceledListener;
// defaults
- private final Integer defaultTimeout = 10;
+ private static final int TIMEOUT_DEFAULT = 0;
+ private static final int TIMEOUT_MIN = 5;
+ private static final int TIMEOUT_MAX = 100;
+ private static Integer defaultTimeout = 10;
private final ChoiceSetLayout defaultLayout = ChoiceSetLayout.CHOICE_SET_LAYOUT_LIST;
/**
* Initialize with a title, listener, and choices. It will use the default timeout and layout,
* all other properties (such as prompts) will be `null`.
*
+ * NOTE: If you display multiple cells with the same `text` with the only uniquing property between cells being different `vrCommands` or a feature that is not displayed on the head unit (e.g. if the head unit doesn't display `secondaryArtwork` and that's the only uniquing property between two cells) then the cells may appear to be the same to the user in `Manual` mode. This only applies to RPC connections >= 7.1.0.
+ *
+ * NOTE: On < 7.1.0 connections, the `text` cell will be automatically modified among cells that have the same `text` when they are preloaded, so they will always appear differently on-screen when they are displayed. `choiceCell.uniqueText` will be created by appending ` (2)`, ` (3)`, etc.
+ *
* @param title - The choice set's title
* @param listener - The choice set listener called after the user has interacted with your choice set
* @param choices - The choices to be displayed to the user for interaction
@@ -75,10 +82,10 @@ public class ChoiceSet {
setTitle(title);
setChoiceSetSelectionListener(listener);
setChoices(choices);
+ setTimeout(TIMEOUT_DEFAULT);
// defaults
setLayout(defaultLayout);
- setTimeout(defaultTimeout);
// things to do
checkChoiceSetParameters();
@@ -87,6 +94,10 @@ public class ChoiceSet {
/**
* Constructor with all possible properties.
*
+ * NOTE: If you display multiple cells with the same `text` with the only uniquing property between cells being different `vrCommands` or a feature that is not displayed on the head unit (e.g. if the head unit doesn't display `secondaryArtwork` and that's the only uniquing property between two cells) then the cells may appear to be the same to the user in `Manual` mode. This only applies to RPC connections >= 7.1.0.
+ *
+ * NOTE: On < 7.1.0 connections, the `text` cell will be automatically modified among cells that have the same `text` when they are preloaded, so they will always appear differently on-screen when they are displayed. `choiceCell.uniqueText` will be created by appending ` (2)`, ` (3)`, etc.
+ *
* @param title - The choice set's title
* @param listener - The choice set listener called after the user has interacted with your choice set
* @param layout - The layout of choice options (Manual/touch only)
@@ -128,6 +139,10 @@ public class ChoiceSet {
/**
* Constructor with all possible properties.
*
+ * NOTE: If you display multiple cells with the same `text` with the only uniquing property between cells being different `vrCommands` or a feature that is not displayed on the head unit (e.g. if the head unit doesn't display `secondaryArtwork` and that's the only uniquing property between two cells) then the cells may appear to be the same to the user in `Manual` mode. This only applies to RPC connections >= 7.1.0.
+ *
+ * NOTE: On < 7.1.0 connections, the `text` cell will be automatically modified among cells that have the same `text` when they are preloaded, so they will always appear differently on-screen when they are displayed. `choiceCell.uniqueText` will be created by appending ` (2)`, ` (3)`, etc.
+ *
* @param title - The choice set's title
* @param listener - The choice set listener called after the user has interacted with your choice set
* @param layout - The layout of choice options (Manual/touch only)
@@ -289,21 +304,43 @@ public class ChoiceSet {
* @return The Timeout
*/
public Integer getTimeout() {
+ if (timeout == TIMEOUT_DEFAULT) {
+ timeout = getDefaultTimeout();
+ } else if (timeout < TIMEOUT_MIN) {
+ return TIMEOUT_MIN;
+ } else if (timeout > TIMEOUT_MAX) {
+ return TIMEOUT_MAX;
+ }
return timeout;
}
/**
- * @param timeout - Maps to PerformInteraction.timeout. This applies only to a manual selection
- * (not a voice selection, which has its timeout handled by the system). Defaults to `defaultTimeout`.
- * <strong>This is set to seconds if using the screen manager.</strong>
+ * Maps to PerformInteraction.timeout. Timeout in seconds. Defaults to 0, which will use `defaultTimeout`.
+ * If this is set below the minimum, it will be capped at 5 seconds. Minimum 5 seconds, maximum 100 seconds.
+ * If this is set above the maximum, it will be capped at 100 seconds.
*/
public void setTimeout(Integer timeout) {
- if (timeout == null) {
- this.timeout = defaultTimeout;
- } else {
this.timeout = timeout;
+ }
+
+ public int getDefaultTimeout() {
+ if (defaultTimeout < TIMEOUT_MIN) {
+ return TIMEOUT_MIN;
+ } else if (defaultTimeout > TIMEOUT_MAX) {
+ return TIMEOUT_MAX;
}
- checkChoiceSetParameters();
+ return defaultTimeout;
+ }
+
+ /**
+ * Set this to change the default timeout for all ChoiceSets. If a timeout is not set on an individual
+ * ChoiceSet object (or if it is set to 0), then it will use this timeout instead. See `timeout`
+ * for more details. If this is not set by you, it will default to 10 seconds. The minimum is
+ * 5 seconds, the maximum is 100 seconds. If this is set below the minimum, it will be capped
+ * at 3 seconds. If this is set above the maximum, it will be capped at 10 seconds.
+ */
+ public void setDefaultTimeout(int defaultTimeout) {
+ this.defaultTimeout = defaultTimeout;
}
/**
@@ -376,11 +413,6 @@ public class ChoiceSet {
DebugTool.logWarning(TAG, "Attempted to create a choice set with a title of " + getTitle().length() + " length. Only 500 characters are supported.");
}
}
- if (getTimeout() != null) {
- if (getTimeout() < 5 || getTimeout() > 100) {
- DebugTool.logWarning(TAG, "Attempted to create a choice set with a " + getTimeout() + " second timeout; Only 5 - 100 seconds is valid");
- }
- }
if (getChoices() != null) {
if (getChoices().size() == 0 || getChoices().size() > 100) {
DebugTool.logWarning(TAG, "Attempted to create a choice set with " + getChoices().size() + " choices; Only 1 - 100 choices are valid");
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/KeyboardListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/KeyboardListener.java
index 175f39324..619f2c2f2 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/KeyboardListener.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/KeyboardListener.java
@@ -84,4 +84,6 @@ public interface KeyboardListener {
* @param currentInputText - The user's full current input text
*/
void onKeyboardDidSendEvent(KeyboardEvent event, String currentInputText);
+
+ void onKeyboardDidUpdateInputMask(KeyboardEvent event);
} \ No newline at end of file
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java
index 6bf0b7d25..8f47a2880 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java
@@ -59,6 +59,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -75,7 +76,7 @@ class PreloadChoicesOperation extends Task {
private boolean choiceError = false;
PreloadChoicesOperation(ISdl internalInterface, FileManager fileManager, String displayName, WindowCapability defaultMainWindowCapability,
- Boolean isVROptional, HashSet<ChoiceCell> cellsToPreload, CompletionListener listener) {
+ Boolean isVROptional, LinkedHashSet<ChoiceCell> cellsToPreload, CompletionListener listener) {
super("PreloadChoicesOperation");
this.internalInterface = new WeakReference<>(internalInterface);
this.fileManager = new WeakReference<>(fileManager);
@@ -195,7 +196,7 @@ class PreloadChoicesOperation extends Task {
vrCommands = cell.getVoiceCommands();
}
- String menuName = shouldSendChoiceText() ? cell.getText() : null;
+ String menuName = shouldSendChoiceText() ? cell.getUniqueText() : null;
if (menuName == null) {
DebugTool.logError(TAG, "Could not convert Choice Cell to CreateInteractionChoiceSet. It will not be shown. Cell: " + cell.toString());
@@ -261,20 +262,13 @@ class PreloadChoicesOperation extends Task {
List<SdlArtwork> artworksToUpload() {
List<SdlArtwork> artworksToUpload = new ArrayList<>();
for (ChoiceCell cell : cellsToUpload) {
- if (shouldSendChoicePrimaryImage() && artworkNeedsUpload(cell.getArtwork())) {
+ if (shouldSendChoicePrimaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getArtwork())) {
artworksToUpload.add(cell.getArtwork());
}
- if (shouldSendChoiceSecondaryImage() && artworkNeedsUpload(cell.getSecondaryArtwork())) {
+ if (shouldSendChoiceSecondaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork())) {
artworksToUpload.add(cell.getSecondaryArtwork());
}
}
return artworksToUpload;
}
-
- boolean artworkNeedsUpload(SdlArtwork artwork) {
- if (fileManager.get() != null) {
- return (artwork != null && !fileManager.get().hasUploadedFile(artwork) && !artwork.isStaticIcon());
- }
- return false;
- }
} \ No newline at end of file
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java
index 8a502e26c..b5d3945d1 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java
@@ -344,6 +344,7 @@ class PresentChoiceSetOperation extends Task {
@Override
public void onUpdatedAutoCompleteList(List<String> updatedAutoCompleteList) {
keyboardProperties.setAutoCompleteList(updatedAutoCompleteList != null ? updatedAutoCompleteList : new ArrayList<String>());
+ keyboardProperties.setAutoCompleteText(updatedAutoCompleteList != null && !updatedAutoCompleteList.isEmpty() ? updatedAutoCompleteList.get(0) : null);
updateKeyboardProperties(null);
}
});
@@ -358,6 +359,8 @@ class PresentChoiceSetOperation extends Task {
} else if (onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_ABORTED) || onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_CANCELLED)) {
// Notify of abort / Cancellation
keyboardListener.onKeyboardDidAbortWithReason(onKeyboard.getEvent());
+ } else if (onKeyboard.getEvent().equals(KeyboardEvent.INPUT_KEY_MASK_ENABLED) || onKeyboard.getEvent().equals(KeyboardEvent.INPUT_KEY_MASK_DISABLED)) {
+ keyboardListener.onKeyboardDidUpdateInputMask(onKeyboard.getEvent());
}
}
};
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java
index 54c26aaa6..2b13cf8ac 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java
@@ -305,6 +305,8 @@ class PresentKeyboardOperation extends Task {
} else if (onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_ABORTED) || onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_CANCELLED)) {
// Notify of abort / Cancellation
keyboardListener.onKeyboardDidAbortWithReason(onKeyboard.getEvent());
+ } else if (onKeyboard.getEvent().equals(KeyboardEvent.INPUT_KEY_MASK_ENABLED) || onKeyboard.getEvent().equals(KeyboardEvent.INPUT_KEY_MASK_DISABLED)) {
+ keyboardListener.onKeyboardDidUpdateInputMask(onKeyboard.getEvent());
}
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java
index dbf9c211c..f39423b1a 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java
@@ -65,6 +65,7 @@ import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows;
import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
import com.smartdevicelink.proxy.rpc.enums.SystemContext;
+import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
@@ -74,6 +75,7 @@ import org.json.JSONException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -174,6 +176,13 @@ abstract class BaseMenuManager extends BaseSubManager {
// Create a deep copy of the list so future changes by developers don't affect the algorithm logic
List<MenuCell> clonedCells = cloneMenuCellsList(cells);
+ // If we're running on a connection < RPC 7.1, we need to de-duplicate cells because presenting them will fail if we have the same cell primary text.
+ if (clonedCells != null && internalInterface.getSdlMsgVersion() != null
+ && (internalInterface.getSdlMsgVersion().getMajorVersion() < 7
+ || (internalInterface.getSdlMsgVersion().getMajorVersion() == 7 && internalInterface.getSdlMsgVersion().getMinorVersion() == 0))) {
+ addUniqueNamesToCells(clonedCells);
+ }
+
if (currentHMILevel == null || currentHMILevel.equals(HMILevel.HMI_NONE) || currentSystemContext.equals(SystemContext.SYSCTXT_MENU)) {
// We are in NONE or the menu is in use, bail out of here
waitingOnHMIUpdate = true;
@@ -196,27 +205,8 @@ abstract class BaseMenuManager extends BaseSubManager {
menuCells.addAll(clonedCells);
}
- // HashSet order doesnt matter / does not allow duplicates
- HashSet<String> titleCheckSet = new HashSet<>();
- HashSet<String> allMenuVoiceCommands = new HashSet<>();
- int voiceCommandCount = 0;
-
- for (MenuCell cell : menuCells) {
- titleCheckSet.add(cell.getTitle());
- if (cell.getVoiceCommands() != null) {
- allMenuVoiceCommands.addAll(cell.getVoiceCommands());
- voiceCommandCount += cell.getVoiceCommands().size();
- }
- }
- // Check for duplicate titles
- if (titleCheckSet.size() != menuCells.size()) {
- DebugTool.logError(TAG, "Not all cell titles are unique. The menu will not be set");
- return;
- }
-
- // Check for duplicate voice commands
- if (allMenuVoiceCommands.size() != voiceCommandCount) {
- DebugTool.logError(TAG, "Attempted to create a menu with duplicate voice commands. Voice commands must be unique. The menu will not be set");
+ // Check for cell lists with completely duplicate information, or any duplicate voiceCommands and return if it fails (logs are in the called method).
+ if (!menuCellsAreUnique(menuCells, new ArrayList<String>())) {
return;
}
@@ -751,17 +741,34 @@ abstract class BaseMenuManager extends BaseSubManager {
// ARTWORKS
+ /**
+ * Get an array of artwork that needs to be uploaded from a list of menu cells
+ *
+ * @param cells List of MenuCell's to check
+ * @return List of artwork that needs to be uploaded
+ */
private List<SdlArtwork> findAllArtworksToBeUploadedFromCells(List<MenuCell> cells) {
// Make sure we can use images in the menus
- if (!supportsImages()) {
+ if (!hasImageFieldOfName(ImageFieldName.cmdIcon)) {
return new ArrayList<>();
}
List<SdlArtwork> artworks = new ArrayList<>();
for (MenuCell cell : cells) {
- if (artworkNeedsUpload(cell.getIcon())) {
+ if (fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getIcon())) {
artworks.add(cell.getIcon());
}
+
+ if (cell.getSubCells() != null && cell.getSubCells().size() > 0 && hasImageFieldOfName(ImageFieldName.menuSubMenuSecondaryImage)) {
+ if (fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork())) {
+ artworks.add(cell.getSecondaryArtwork());
+ }
+ } else if ((cell.getSubCells() == null || cell.getSubCells().isEmpty()) && hasImageFieldOfName(ImageFieldName.menuCommandSecondaryImage)) {
+ if (fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork())) {
+ artworks.add(cell.getSecondaryArtwork());
+ }
+ }
+
if (cell.getSubCells() != null && cell.getSubCells().size() > 0) {
artworks.addAll(findAllArtworksToBeUploadedFromCells(cell.getSubCells()));
}
@@ -770,16 +777,45 @@ abstract class BaseMenuManager extends BaseSubManager {
return artworks;
}
- @SuppressWarnings("BooleanMethodIsAlwaysInverted")
- private boolean supportsImages() {
- return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.cmdIcon);
+ /**
+ * Determine if cells should or should not be uploaded to the head unit with artworks.
+ *
+ * No artworks will be uploaded if:
+ *
+ * 1. If any cell has a dynamic artwork that is not uploaded
+ * 2. If any cell contains a secondary artwork may be used on the head unit, and the cell has a dynamic secondary artwork that is not uploaded
+ * 3. If any cell's subcells fail check (1) or (2)
+ *
+ * @param cells List of MenuCell's to check
+ * @return True if the cells should be uploaded with artwork, false if they should not
+ */
+ private boolean shouldRPCsIncludeImages(List<MenuCell> cells) {
+ for (MenuCell cell : cells) {
+ SdlArtwork artwork = cell.getIcon();
+ SdlArtwork secondaryArtwork = cell.getSecondaryArtwork();
+ if (artwork != null && !artwork.isStaticIcon() && fileManager.get() != null && !fileManager.get().hasUploadedFile(artwork)) {
+ return false;
+ } else if (cell.getSubCells() != null && cell.getSubCells().size() > 0 && hasImageFieldOfName(ImageFieldName.menuSubMenuSecondaryImage)) {
+ if (secondaryArtwork != null && !secondaryArtwork.isStaticIcon() && fileManager.get() != null && !fileManager.get().hasUploadedFile(secondaryArtwork)) {
+ return false;
+ }
+ } else if ((cell.getSubCells() == null || cell.getSubCells().isEmpty()) && hasImageFieldOfName(ImageFieldName.menuCommandSecondaryImage)) {
+ if (secondaryArtwork != null && !secondaryArtwork.isStaticIcon() && fileManager.get() != null && !fileManager.get().hasUploadedFile(secondaryArtwork)) {
+ return false;
+ }
+ } else if (cell.getSubCells() != null && cell.getSubCells().size() > 0 && !shouldRPCsIncludeImages(cell.getSubCells())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean hasImageFieldOfName(ImageFieldName imageFieldName) {
+ return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, imageFieldName);
}
- private boolean artworkNeedsUpload(SdlArtwork artwork) {
- if (fileManager.get() != null) {
- return (artwork != null && !fileManager.get().hasUploadedFile(artwork) && !artwork.isStaticIcon());
- }
- return false;
+ private boolean hasTextFieldOfName(TextFieldName textFieldName) {
+ return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, textFieldName);
}
// IDs
@@ -828,6 +864,13 @@ abstract class BaseMenuManager extends BaseSubManager {
return null;
}
+
+ /**
+ * Assign cell ids on an array of menu cells given a parent id (or no parent id)
+ *
+ * @param cells List of menu cells to update
+ * @param parentId The parent id to assign if needed
+ */
private void updateIdsOnMenuCells(List<MenuCell> cells, int parentId) {
for (MenuCell cell : cells) {
int newId = ++lastMenuId;
@@ -867,6 +910,12 @@ abstract class BaseMenuManager extends BaseSubManager {
// DELETES
+ /**
+ * Create an array of DeleteCommand and DeleteSubMenu RPCs from an ArrayList of menu cells
+ *
+ * @param cells List of menu cells to use
+ * @return List of DeleteCommand and DeletedSubmenu RPCs
+ */
private List<RPCRequest> createDeleteRPCsForCells(List<MenuCell> cells) {
List<RPCRequest> deletes = new ArrayList<>();
for (MenuCell cell : cells) {
@@ -883,6 +932,14 @@ abstract class BaseMenuManager extends BaseSubManager {
// COMMANDS / SUBMENU RPCs
+ /**
+ * This method will receive the cells to be added. It will then build an array of add commands using the correct index to position the new items in the correct location.
+ * e.g. If the new menu array is [A, B, C, D] but only [C, D] are new we need to pass [A, B , C , D] so C and D can be added to index 2 and 3 respectively.
+ *
+ * @param cellsToAdd List of MenuCell's that will be added to the menu, this array must contain only cells that are not already in the menu.
+ * @param shouldHaveArtwork Boolean that indicates whether artwork should be applied to the RPC or not
+ * @return List of RPCRequest addCommands
+ */
private List<RPCRequest> mainMenuCommandsForCells(List<MenuCell> cellsToAdd, boolean shouldHaveArtwork) {
List<RPCRequest> builtCommands = new ArrayList<>();
@@ -904,6 +961,13 @@ abstract class BaseMenuManager extends BaseSubManager {
return builtCommands;
}
+ /**
+ * Creates AddSubMenu RPCs for the passed array of menu cells, AND all of those cells' subcell RPCs, both AddCommands and AddSubMenus
+ *
+ * @param cells List of MenuCell's to create RPCs for
+ * @param shouldHaveArtwork Boolean that indicates whether artwork should be applied to the RPC or not
+ * @return An List of RPCs of AddSubMenus and their associated subcell RPCs
+ */
private List<RPCRequest> subMenuCommandsForCells(List<MenuCell> cells, boolean shouldHaveArtwork) {
List<RPCRequest> builtCommands = new ArrayList<>();
for (MenuCell cell : cells) {
@@ -914,6 +978,13 @@ abstract class BaseMenuManager extends BaseSubManager {
return builtCommands;
}
+ /**
+ * Creates AddCommand and AddSubMenu RPCs for a passed array of cells, AND all of those cells' subcell RPCs, both AddCommands and AddSubmenus
+ *
+ * @param cells List of MenuCell's to create RPCs for
+ * @param shouldHaveArtwork Boolean that indicates whether artwork should be applied to the RPC or not
+ * @return An List of RPCs of AddCommand and AddSubMenus for the array of menu cells and their subcells, recursively
+ */
List<RPCRequest> allCommandsForCells(List<MenuCell> cells, boolean shouldHaveArtwork) {
List<RPCRequest> builtCommands = new ArrayList<>();
@@ -946,9 +1017,19 @@ abstract class BaseMenuManager extends BaseSubManager {
return builtCommands;
}
+ /**
+ * An individual AddCommand RPC for a given MenuCell
+ *
+ * @param cell MenuCell to create RPCs for
+ * @param shouldHaveArtwork Boolean that indicates whether artwork should be applied to the RPC or not
+ * @param position The position the AddCommand RPC should be given
+ * @return The AddCommand RPC
+ */
private AddCommand commandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) {
- MenuParams params = new MenuParams(cell.getTitle());
+ MenuParams params = new MenuParams(cell.getUniqueTitle());
+ params.setSecondaryText((cell.getSecondaryText() != null && cell.getSecondaryText().length() > 0 && hasTextFieldOfName(TextFieldName.menuCommandSecondaryText)) ? cell.getSecondaryText() : null);
+ params.setTertiaryText((cell.getTertiaryText() != null && cell.getTertiaryText().length() > 0 && hasTextFieldOfName(TextFieldName.menuCommandTertiaryText)) ? cell.getTertiaryText() : null);
params.setParentID(cell.getParentCellId() != MAX_ID ? cell.getParentCellId() : null);
params.setPosition(position);
@@ -960,12 +1041,23 @@ abstract class BaseMenuManager extends BaseSubManager {
command.setVrCommands(null);
}
command.setCmdIcon((cell.getIcon() != null && shouldHaveArtwork) ? cell.getIcon().getImageRPC() : null);
+ command.setSecondaryImage((cell.getSecondaryArtwork() != null && shouldHaveArtwork && !(fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork()))) ? cell.getSecondaryArtwork().getImageRPC() : null);
return command;
}
+ /**
+ * An individual AddSubMenu RPC for a given MenuCell
+ *
+ * @param cell The cell to create the RPC for
+ * @param shouldHaveArtwork Whether artwork should be applied to the RPC
+ * @param position The position the AddSubMenu RPC should be given
+ * @return The AddSubMenu RPC
+ */
private AddSubMenu subMenuCommandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) {
- AddSubMenu subMenu = new AddSubMenu(cell.getCellId(), cell.getTitle());
+ AddSubMenu subMenu = new AddSubMenu(cell.getCellId(), cell.getUniqueTitle());
+ subMenu.setSecondaryText((cell.getSecondaryText() != null && cell.getSecondaryText().length() > 0 && hasTextFieldOfName(TextFieldName.menuSubMenuSecondaryText)) ? cell.getSecondaryText() : null);
+ subMenu.setTertiaryText((cell.getTertiaryText() != null && cell.getTertiaryText().length() > 0 && hasTextFieldOfName(TextFieldName.menuSubMenuTertiaryText)) ? cell.getTertiaryText() : null);
subMenu.setPosition(position);
if (cell.getSubMenuLayout() != null) {
subMenu.setMenuLayout(cell.getSubMenuLayout());
@@ -973,11 +1065,20 @@ abstract class BaseMenuManager extends BaseSubManager {
subMenu.setMenuLayout(menuConfiguration.getSubMenuLayout());
}
subMenu.setMenuIcon((shouldHaveArtwork && (cell.getIcon() != null && cell.getIcon().getImageRPC() != null)) ? cell.getIcon().getImageRPC() : null);
+ subMenu.setSecondaryImage((shouldHaveArtwork && !(fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork())) && (cell.getSecondaryArtwork() != null && cell.getSecondaryArtwork().getImageRPC() != null)) ? cell.getSecondaryArtwork().getImageRPC() : null);
return subMenu;
}
// CELL COMMAND HANDLING
+
+ /**
+ * Call a listener for a currently displayed MenuCell based on the incoming OnCommand notification
+ *
+ * @param cells List of MenuCell's to check (including their subcells)
+ * @param command OnCommand notification retrieved
+ * @return True if the handler was found, false if it was not found
+ */
private boolean callListenerForCells(List<MenuCell> cells, OnCommand command) {
if (cells != null && cells.size() > 0 && command != null) {
for (MenuCell cell : cells) {
@@ -1132,7 +1233,7 @@ abstract class BaseMenuManager extends BaseSubManager {
List<RPCRequest> mainMenuCommands;
final List<RPCRequest> subMenuCommands;
- if (findAllArtworksToBeUploadedFromCells(menu).size() > 0 || !supportsImages()) {
+ if (!shouldRPCsIncludeImages(menu) || !hasImageFieldOfName(ImageFieldName.cmdIcon)) {
// Send artwork-less menu
mainMenuCommands = mainMenuCommandsForCells(menu, false);
subMenuCommands = subMenuCommandsForCells(menu, false);
@@ -1237,7 +1338,7 @@ abstract class BaseMenuManager extends BaseSubManager {
List<RPCRequest> mainMenuCommands;
- if (findAllArtworksToBeUploadedFromCells(adds).size() > 0 || !supportsImages()) {
+ if (!shouldRPCsIncludeImages(adds) || !hasImageFieldOfName(ImageFieldName.cmdIcon)) {
// Send artwork-less menu
mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, false);
} else {
@@ -1339,4 +1440,73 @@ abstract class BaseMenuManager extends BaseSubManager {
}
return clone;
}
+
+ private void addUniqueNamesToCells(List<MenuCell> cells) {
+ HashMap<String, Integer> dictCounter = new HashMap<>();
+
+ for (MenuCell cell : cells) {
+ String cellName = cell.getTitle();
+ Integer counter = dictCounter.get(cellName);
+
+ if (counter != null) {
+ dictCounter.put(cellName, ++counter);
+ cell.setUniqueTitle(cellName + " (" + counter + ")");
+ } else {
+ dictCounter.put(cellName, 1);
+ }
+
+ if (cell.getSubCells() != null && cell.getSubCells().size() > 0) {
+ addUniqueNamesToCells(cell.getSubCells());
+ }
+ }
+ }
+
+
+ /**
+ * Check for cell lists with completely duplicate information, or any duplicate voiceCommands
+ *
+ * @param cells List of MenuCell's you will be adding
+ * @param allVoiceCommands List of String's for VoiceCommands (Used for recursive calls to check voiceCommands of the cells)
+ * @return Boolean that indicates whether menuCells are unique or not
+ */
+ private boolean menuCellsAreUnique(List<MenuCell> cells, ArrayList<String> allVoiceCommands) {
+ //Check all voice commands for identical items and check each list of cells for identical cells
+ HashSet<MenuCell> identicalCellsCheckSet = new HashSet<>();
+
+ for (MenuCell cell : cells) {
+ identicalCellsCheckSet.add(cell);
+
+ // Recursively check the subcell lists to see if they are all unique as well. If anything is not, this will chain back up the list to return false.
+ if (cell.getSubCells() != null && cell.getSubCells().size() > 0) {
+ boolean subCellsAreUnique = menuCellsAreUnique(cell.getSubCells(), allVoiceCommands);
+
+ if (!subCellsAreUnique) {
+ DebugTool.logError(TAG, "Not all subCells are unique. The menu will not be set.");
+ return false;
+ }
+ }
+
+ // Voice commands have to be identical across all lists
+ if (cell.getVoiceCommands() == null) {
+ continue;
+ }
+ allVoiceCommands.addAll(cell.getVoiceCommands());
+ }
+
+
+ // Check for duplicate cells
+ if (identicalCellsCheckSet.size() != cells.size()) {
+ DebugTool.logError(TAG, "Not all cells are unique. The menu will not be set.");
+ return false;
+ }
+
+ // All the VR commands must be unique
+ HashSet<String> voiceCommandsSet = new HashSet<>(allVoiceCommands);
+ if (allVoiceCommands.size() != voiceCommandsSet.size()) {
+ DebugTool.logError(TAG, "Attempted to create a menu with duplicate voice commands. Voice commands must be unique. The menu will not be set");
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java
index 4c449ff61..3e5fa6dbe 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseVoiceCommandManager.java
@@ -34,47 +34,48 @@ package com.smartdevicelink.managers.screen.menu;
import androidx.annotation.NonNull;
+import com.livio.taskmaster.Queue;
+import com.livio.taskmaster.Task;
import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCNotification;
-import com.smartdevicelink.proxy.RPCResponse;
-import com.smartdevicelink.proxy.rpc.AddCommand;
-import com.smartdevicelink.proxy.rpc.DeleteCommand;
+import com.smartdevicelink.proxy.RPCRequest;
import com.smartdevicelink.proxy.rpc.OnCommand;
import com.smartdevicelink.proxy.rpc.OnHMIStatus;
import com.smartdevicelink.proxy.rpc.enums.HMILevel;
import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows;
-import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
import com.smartdevicelink.util.DebugTool;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
abstract class BaseVoiceCommandManager extends BaseSubManager {
private static final String TAG = "BaseVoiceCommandManager";
- List<VoiceCommand> voiceCommands, oldVoiceCommands;
-
- List<AddCommand> inProgressUpdate;
+ List<VoiceCommand> voiceCommands, currentVoiceCommands;
int lastVoiceCommandId;
private static final int voiceCommandIdMin = 1900000000;
- boolean waitingOnHMIUpdate;
- boolean hasQueuedUpdate;
-
HMILevel currentHMILevel;
OnRPCNotificationListener hmiListener;
OnRPCNotificationListener commandListener;
+ Queue transactionQueue;
+ VoiceCommandUpdateOperation updateOperation;
+
+
// CONSTRUCTORS
BaseVoiceCommandManager(@NonNull ISdl internalInterface) {
super(internalInterface);
- currentHMILevel = HMILevel.HMI_NONE;
+ this.transactionQueue = newTransactionQueue();
+
+ currentHMILevel = null;
addListeners();
lastVoiceCommandId = voiceCommandIdMin;
}
@@ -90,12 +91,16 @@ abstract class BaseVoiceCommandManager extends BaseSubManager {
lastVoiceCommandId = voiceCommandIdMin;
voiceCommands = null;
- oldVoiceCommands = null;
+ currentVoiceCommands = null;
- waitingOnHMIUpdate = false;
currentHMILevel = null;
- inProgressUpdate = null;
- hasQueuedUpdate = false;
+
+ if (transactionQueue != null) {
+ transactionQueue.close();
+ }
+ transactionQueue = null;
+
+ updateOperation = null;
// remove listeners
internalInterface.removeOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, hmiListener);
@@ -104,172 +109,75 @@ abstract class BaseVoiceCommandManager extends BaseSubManager {
super.dispose();
}
- // SETTERS
-
- public void setVoiceCommands(List<VoiceCommand> voiceCommands) {
-
- // we actually need voice commands to set.
- if (voiceCommands == null || voiceCommands.size() == 0) {
- DebugTool.logInfo(TAG, "Trying to set empty list of voice commands, returning");
- return;
- }
-
- // make sure hmi is not none
- if (currentHMILevel == null || currentHMILevel == HMILevel.HMI_NONE) {
- // Trying to send on HMI_NONE, waiting for full
- this.voiceCommands = new ArrayList<>(voiceCommands);
- waitingOnHMIUpdate = true;
- return;
- }
-
- waitingOnHMIUpdate = false;
- lastVoiceCommandId = voiceCommandIdMin;
- updateIdsOnVoiceCommands(voiceCommands);
- this.oldVoiceCommands = new ArrayList<>();
- if (this.voiceCommands != null && !this.voiceCommands.isEmpty()) {
- this.oldVoiceCommands.addAll(this.voiceCommands);
- }
- this.voiceCommands = new ArrayList<>(voiceCommands);
-
- update();
- }
-
- public List<VoiceCommand> getVoiceCommands() {
- return voiceCommands;
+ private Queue newTransactionQueue() {
+ Queue queue = internalInterface.getTaskmaster().createQueue("VoiceCommandManager", 4, false);
+ queue.pause();
+ return queue;
}
- // UPDATING SYSTEM
-
- private void update() {
-
- if (currentHMILevel == null || currentHMILevel.equals(HMILevel.HMI_NONE)) {
- waitingOnHMIUpdate = true;
- return;
+ // If the HMI level is NONE since we want to delay sending RPCs until we're in non-NONE
+ private void updateTransactionQueueSuspended() {
+ if (HMILevel.HMI_NONE.equals(currentHMILevel)) {
+ DebugTool.logInfo(TAG, "Suspending the transaction queue. Current HMI level is NONE");
+ transactionQueue.pause();
+ } else {
+ DebugTool.logInfo(TAG, "Starting the transaction queue");
+ transactionQueue.resume();
}
-
- if (inProgressUpdate != null) {
- // There's an in-progress update, put this on hold
- hasQueuedUpdate = true;
- return;
- }
-
- sendDeleteCurrentVoiceCommands(new CompletionListener() {
- @Override
- public void onComplete(boolean success) {
- // we don't care about errors from deleting, send new add commands
- sendCurrentVoiceCommands(new CompletionListener() {
- @Override
- public void onComplete(boolean success2) {
- inProgressUpdate = null;
-
- if (hasQueuedUpdate) {
- update();
- hasQueuedUpdate = false;
- }
-
- if (!success2) {
- DebugTool.logError(TAG, "Error sending voice commands");
- }
- }
- });
- }
- });
-
}
- // DELETING OLD MENU ITEMS
+ // SETTERS
- private void sendDeleteCurrentVoiceCommands(final CompletionListener listener) {
+ public void setVoiceCommands(List<VoiceCommand> voiceCommands) {
- if (oldVoiceCommands == null || oldVoiceCommands.size() == 0) {
- if (listener != null) {
- listener.onComplete(true);
- }
+ // we actually need voice commands to set.
+ if (voiceCommands == null || voiceCommands.equals(this.voiceCommands)) {
+ DebugTool.logInfo(TAG, "Voice commands list was null or matches the current voice commands");
return;
}
- List<DeleteCommand> deleteVoiceCommands = deleteCommandsForVoiceCommands(oldVoiceCommands);
- oldVoiceCommands.clear();
- internalInterface.sendRPCs(deleteVoiceCommands, new OnMultipleRequestListener() {
- @Override
- public void onUpdate(int remainingRequests) {
-
- }
+ updateIdsOnVoiceCommands(voiceCommands);
+ this.voiceCommands = new ArrayList<>(voiceCommands);
+ cleanTransactionQueue();
+ updateOperation = new VoiceCommandUpdateOperation(internalInterface, currentVoiceCommands, voiceCommands, new VoiceCommandUpdateOperation.VoiceCommandChangesListener() {
@Override
- public void onFinished() {
- DebugTool.logInfo(TAG, "Successfully deleted old voice commands");
- if (listener != null) {
- listener.onComplete(true);
+ public void updateVoiceCommands(List<VoiceCommand> newCurrentVoiceCommands, HashMap<RPCRequest, String> errorObject) {
+ DebugTool.logInfo(TAG, "The updated list of VoiceCommands: " + newCurrentVoiceCommands);
+ if (!errorObject.isEmpty()) {
+ DebugTool.logError(TAG, "The failed Add and Delete Commands: " + errorObject);
}
- }
-
- @Override
- public void onResponse(int correlationId, RPCResponse response) {
+ currentVoiceCommands = newCurrentVoiceCommands;
+ updatePendingOperations(newCurrentVoiceCommands);
+ updateOperation = null;
}
});
-
+ transactionQueue.add(updateOperation, false);
}
- // SEND NEW MENU ITEMS
-
- private void sendCurrentVoiceCommands(final CompletionListener listener) {
-
- if (voiceCommands == null || voiceCommands.size() == 0) {
- if (listener != null) {
- listener.onComplete(true); // no voice commands to send doesnt mean that its an error
- }
- return;
- }
-
- inProgressUpdate = addCommandsForVoiceCommands(voiceCommands);
-
- internalInterface.sendRPCs(inProgressUpdate, new OnMultipleRequestListener() {
- @Override
- public void onUpdate(int remainingRequests) {
-
- }
-
- @Override
- public void onFinished() {
- DebugTool.logInfo(TAG, "Sending Voice Commands Complete");
- if (listener != null) {
- listener.onComplete(true);
- }
- oldVoiceCommands = voiceCommands;
- }
-
- @Override
- public void onResponse(int correlationId, RPCResponse response) {
- }
- });
+ public List<VoiceCommand> getVoiceCommands() {
+ return voiceCommands;
}
- // DELETES
-
- List<DeleteCommand> deleteCommandsForVoiceCommands(List<VoiceCommand> voiceCommands) {
- List<DeleteCommand> deleteCommandList = new ArrayList<>();
- for (VoiceCommand command : voiceCommands) {
- DeleteCommand delete = new DeleteCommand(command.getCommandId());
- deleteCommandList.add(delete);
+ private void cleanTransactionQueue() {
+ if (transactionQueue != null) {
+ transactionQueue.clear();
}
- return deleteCommandList;
- }
-
- // COMMANDS
- List<AddCommand> addCommandsForVoiceCommands(List<VoiceCommand> voiceCommands) {
- List<AddCommand> addCommandList = new ArrayList<>();
- for (VoiceCommand command : voiceCommands) {
- addCommandList.add(commandForVoiceCommand(command));
+ if (updateOperation != null) {
+ updateOperation.cancelTask();
+ updateOperation = null;
}
- return addCommandList;
}
- private AddCommand commandForVoiceCommand(VoiceCommand voiceCommand) {
- AddCommand command = new AddCommand(voiceCommand.getCommandId());
- command.setVrCommands(voiceCommand.getVoiceCommands());
- return command;
+ private void updatePendingOperations(List<VoiceCommand> newCurrentVoiceCommands) {
+ for (Task operation : transactionQueue.getTasksAsList()) {
+ if (operation.getState() == Task.IN_PROGRESS) {
+ continue;
+ }
+ VoiceCommandUpdateOperation vcOperation = (VoiceCommandUpdateOperation) operation;
+ vcOperation.oldVoiceCommands = newCurrentVoiceCommands;
+ }
}
// HELPERS
@@ -292,14 +200,8 @@ abstract class BaseVoiceCommandManager extends BaseSubManager {
if (onHMIStatus.getWindowID() != null && onHMIStatus.getWindowID() != PredefinedWindows.DEFAULT_WINDOW.getValue()) {
return;
}
- HMILevel oldHMILevel = currentHMILevel;
currentHMILevel = onHMIStatus.getHmiLevel();
- // Auto-send an update if we were in NONE and now we are not
- if (oldHMILevel == HMILevel.HMI_NONE && currentHMILevel != HMILevel.HMI_NONE) {
- if (waitingOnHMIUpdate) {
- setVoiceCommands(voiceCommands);
- }
- }
+ updateTransactionQueueSuspended();
}
};
internalInterface.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, hmiListener);
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java
index 83e265f8e..652891181 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java
@@ -90,6 +90,23 @@ public class MenuCell implements Cloneable {
*/
private static final int MAX_ID = 2000000000;
+ /**
+ * secondaryText and tertiaryText - additional text to be displayed
+ */
+ private String secondaryText, tertiaryText;
+
+ /**
+ * secondaryArtwork - a secondary icon to be displayed
+ */
+ private SdlArtwork secondaryArtwork;
+
+ /**
+ * Primary text of the cell to be displayed on the module. Used to distinguish cells with the
+ * same `title` but other fields are different. This is autogenerated by the screen manager.
+ * This will not be used when connected to modules supporting RPC 7.1+.
+ */
+ private String uniqueTitle;
+
// CONSTRUCTORS
// SINGLE MENU ITEM CONSTRUCTORS
@@ -102,9 +119,35 @@ public class MenuCell implements Cloneable {
* @param voiceCommands Voice commands that will activate the menu cell
* @param listener Calls the code that will be run when the menu cell is selected
*/
+ @Deprecated
public MenuCell(@NonNull String title, @Nullable SdlArtwork icon, @Nullable List<String> voiceCommands, @Nullable MenuSelectionListener listener) {
setTitle(title); // title is the only required param
+ setUniqueTitle(title);
+ setIcon(icon);
+ setVoiceCommands(voiceCommands);
+ setMenuSelectionListener(listener);
+ setCellId(MAX_ID);
+ setParentCellId(MAX_ID);
+ }
+
+ /**
+ * Creates a new MenuCell Object with multiple parameters and optional fields set
+ *
+ * @param title The cell's primary text
+ * @param secondaryText The cell's secondary text
+ * @param tertiaryText The cell's tertiary text
+ * @param icon The cell's image
+ * @param secondaryArtwork The cell's secondary image
+ * @param voiceCommands Voice commands that will activate the menu cell
+ * @param listener Calls the code that will be run when the menu cell is selected
+ */
+ public MenuCell(@NonNull String title, @Nullable String secondaryText, @Nullable String tertiaryText, @Nullable SdlArtwork icon, @Nullable SdlArtwork secondaryArtwork, @Nullable List<String> voiceCommands, @Nullable MenuSelectionListener listener) {
+ setTitle(title); // title is the only required param
+ setUniqueTitle(title);
+ setSecondaryText(secondaryText);
+ setTertiaryText(tertiaryText);
setIcon(icon);
+ setSecondaryArtwork(secondaryArtwork);
setVoiceCommands(voiceCommands);
setMenuSelectionListener(listener);
setCellId(MAX_ID);
@@ -123,8 +166,10 @@ public class MenuCell implements Cloneable {
* @param icon The cell's image
* @param subCells The sub-cells for the sub menu that will appear when the cell is selected
*/
+ @Deprecated
public MenuCell(@NonNull String title, @Nullable MenuLayout subMenuLayout, @Nullable SdlArtwork icon, @Nullable List<MenuCell> subCells) {
setTitle(title); // title is the only required param
+ setUniqueTitle(title);
setSubMenuLayout(subMenuLayout);
setIcon(icon);
setSubCells(subCells);
@@ -132,6 +177,32 @@ public class MenuCell implements Cloneable {
setParentCellId(MAX_ID);
}
+ /**
+ * Creates a new MenuCell Object with multiple parameters and optional fields set
+ * <strong>NOTE: because this has sub-cells, there does not need to be a listener</strong>
+ *
+ * @param title The cell's primary text
+ * @param secondaryText The cell's secondary text
+ * @param tertiaryText The cell's tertiary text
+ * @param subMenuLayout The submenu's layout that the subCells will be shown in. If `null`, the
+ * default submenu layout in the screen manager's `MenuConfiguration` will be used.
+ * @param icon The cell's image
+ * @param secondaryArtwork The cell's secondary image
+ * @param subCells The sub-cells for the sub menu that will appear when the cell is selected
+ */
+ public MenuCell(@NonNull String title, @Nullable String secondaryText, @Nullable String tertiaryText, @Nullable MenuLayout subMenuLayout, @Nullable SdlArtwork icon, @Nullable SdlArtwork secondaryArtwork, @Nullable List<MenuCell> subCells) {
+ setTitle(title); // title is the only required param
+ setUniqueTitle(title);
+ setSecondaryText(secondaryText);
+ setTertiaryText(tertiaryText);
+ setSubMenuLayout(subMenuLayout);
+ setIcon(icon);
+ setSecondaryArtwork(secondaryArtwork);
+ setSubCells(subCells);
+ setCellId(MAX_ID);
+ setParentCellId(MAX_ID);
+ }
+
// SETTERS / GETTERS
// PUBLIC METHODS
@@ -284,6 +355,81 @@ public class MenuCell implements Cloneable {
return parentCellId;
}
+ /**
+ * Sets the secondaryText
+ *
+ * @param secondaryText the cell's secondaryText
+ */
+ public void setSecondaryText(String secondaryText) {
+ this.secondaryText = secondaryText;
+ }
+
+ /**
+ * Get the cell's secondaryText
+ *
+ * @return the cell's secondaryText
+ */
+ public String getSecondaryText() {
+ return secondaryText;
+ }
+
+ /**
+ * Sets the tertiaryText
+ *
+ * @param tertiaryText the cell's tertiaryText
+ */
+ public void setTertiaryText(String tertiaryText) {
+ this.tertiaryText = tertiaryText;
+ }
+
+
+ /**
+ * Get the cell's tertiaryText
+ *
+ * @return the cell's tertiaryText
+ */
+ public String getTertiaryText(){
+ return tertiaryText;
+ }
+
+ /**
+ * Sets the secondaryArtwork
+ *
+ * @param secondaryArtwork the cell's secondaryArtwork
+ */
+ public void setSecondaryArtwork(SdlArtwork secondaryArtwork) {
+ this.secondaryArtwork = secondaryArtwork;
+ }
+
+ /**
+ * Get the cell's secondaryArtwork
+ *
+ * @return the cell's secondaryArtwork
+ */
+ public SdlArtwork getSecondaryArtwork(){
+ return secondaryArtwork;
+ }
+
+ /**
+ * NOTE: USED INTERNALLY
+ * Set the uniqueTitle.
+ *
+ * @param uniqueTitle - the uniqueTitle to be used in place of primary title when core does not support identical names for MenuCells
+ */
+ void setUniqueTitle(String uniqueTitle) {
+ this.uniqueTitle = uniqueTitle;
+ }
+
+ /**
+ * NOTE: USED INTERNALLY
+ * Get the uniqueTitle that was used in place of the primary title
+ *
+ * @return the uniqueTitle for this MenuCell
+ */
+ String getUniqueTitle() {
+ return uniqueTitle;
+ }
+
// HELPER
/**
@@ -303,6 +449,9 @@ public class MenuCell implements Cloneable {
result += ((getIcon() == null) ? 0 : Integer.rotateLeft(getIcon().hashCode(), 2));
result += ((getVoiceCommands() == null) ? 0 : Integer.rotateLeft(getVoiceCommands().hashCode(), 3));
result += ((getSubCells() == null) ? 0 : Integer.rotateLeft(1, 4));
+ result += ((getSecondaryText() == null) ? 0 : Integer.rotateLeft(getSecondaryText().hashCode(), 1));
+ result += ((getTertiaryText() == null) ? 0 : Integer.rotateLeft(getTertiaryText().hashCode(), 1));
+ result += ((getSecondaryArtwork() == null) ? 0 : Integer.rotateLeft(getSecondaryArtwork().hashCode(), 2));
return result;
}
@@ -337,6 +486,9 @@ public class MenuCell implements Cloneable {
if (this.icon != null) {
clone.icon = this.icon.clone();
}
+ if (this.secondaryArtwork != null) {
+ clone.secondaryArtwork = this.secondaryArtwork.clone();
+ }
if (this.subCells != null) {
ArrayList<MenuCell> cloneSubCells = new ArrayList<>();
for (MenuCell subCell : subCells) {
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommand.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommand.java
index 2d1b875cc..15d66b09e 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommand.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommand.java
@@ -35,6 +35,7 @@ package com.smartdevicelink.managers.screen.menu;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+
import java.util.List;
public class VoiceCommand {
@@ -124,4 +125,36 @@ public class VoiceCommand {
int getCommandId() {
return commandId;
}
+
+ /**
+ * Used to compile hashcode for VoiceCommand for use to compare in equals method
+ *
+ * @return Custom hashcode of VoiceCommand variables
+ */
+ @Override
+ public int hashCode() {
+ int result = 1;
+ result += Integer.rotateLeft(getCommandId(), 1);
+ for (int i = 0; i < this.getVoiceCommands().size(); i++) {
+ result += ((getVoiceCommands().get(i) == null) ? 0 : Integer.rotateLeft(getVoiceCommands().get(i).hashCode(), i + 2));
+ }
+ return result;
+ }
+
+ /**
+ * Uses our custom hashCode for VoiceCommand objects
+ *
+ * @param o - The object to compare
+ * @return boolean of whether the objects are the same or not
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) return false;
+ // if this is the same memory address, it's the same
+ if (this == o) return true;
+ // if this is not an instance of SoftButtonObject, not the same
+ if (!(o instanceof VoiceCommand)) return false;
+ // return comparison
+ return hashCode() == o.hashCode();
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java
new file mode 100644
index 000000000..b755abd7d
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java
@@ -0,0 +1,230 @@
+package com.smartdevicelink.managers.screen.menu;
+
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.managers.CompletionListener;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.rpc.AddCommand;
+import com.smartdevicelink.proxy.rpc.DeleteCommand;
+import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
+import com.smartdevicelink.util.DebugTool;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+class VoiceCommandUpdateOperation extends Task {
+ private static final String TAG = "VoiceCommandReplaceOperation";
+ private final WeakReference<ISdl> internalInterface;
+ List<VoiceCommand> oldVoiceCommands;
+ private List<VoiceCommand> pendingVoiceCommands;
+ private List<DeleteCommand> deleteVoiceCommands;
+ private List<AddCommand> addCommandsToSend;
+ private VoiceCommandChangesListener voiceCommandListener;
+ private List<VoiceCommand> currentVoiceCommands;
+ private HashMap<RPCRequest, String> errorObject;
+
+ interface VoiceCommandChangesListener {
+ void updateVoiceCommands(List<VoiceCommand> newCurrentVoiceCommands, HashMap<RPCRequest, String> errorObject);
+ }
+
+ VoiceCommandUpdateOperation(ISdl internalInterface, List<VoiceCommand> oldVoiceCommands, List<VoiceCommand> pendingVoiceCommands, VoiceCommandChangesListener voiceCommandListener) {
+ super("VoiceCommandReplaceOperation");
+ this.internalInterface = new WeakReference<>(internalInterface);
+ this.oldVoiceCommands = oldVoiceCommands;
+ this.pendingVoiceCommands = pendingVoiceCommands;
+ this.currentVoiceCommands = new ArrayList<>();
+ if (oldVoiceCommands != null) {
+ this.currentVoiceCommands.addAll(oldVoiceCommands);
+ }
+ this.voiceCommandListener = voiceCommandListener;
+ this.errorObject = new HashMap<>();
+ }
+
+ @Override
+ public void onExecute() {
+ start();
+ }
+
+ private void start() {
+ if (getState() == Task.CANCELED) {
+ onFinished();
+ return;
+ }
+
+ sendDeleteCurrentVoiceCommands(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ if (getState() == Task.CANCELED) {
+ onFinished();
+ return;
+ }
+ // we don't care about errors from deleting, send new add commands
+ sendCurrentVoiceCommands(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success2) {
+ if (!success2) {
+ DebugTool.logError(TAG, "Error sending voice commands");
+ }
+ onFinished();
+ if (voiceCommandListener != null) {
+ voiceCommandListener.updateVoiceCommands(currentVoiceCommands, errorObject);
+ }
+ }
+ });
+ }
+ });
+ }
+
+ // Send DeleteCommandList
+
+ private void sendDeleteCurrentVoiceCommands(final CompletionListener listener) {
+
+ if (oldVoiceCommands == null || oldVoiceCommands.isEmpty()) {
+ if (listener != null) {
+ listener.onComplete(true);
+ }
+ return;
+ }
+
+ deleteVoiceCommands = deleteCommandsForVoiceCommands(oldVoiceCommands);
+
+ internalInterface.get().sendRPCs(deleteVoiceCommands, new OnMultipleRequestListener() {
+ @Override
+ public void onUpdate(int remainingRequests) {
+ }
+
+ @Override
+ public void onFinished() {
+ if (listener != null) {
+ if (errorObject.isEmpty()) {
+ DebugTool.logInfo(TAG, "Successfully deleted old voice commands");
+ listener.onComplete(true);
+ } else {
+ DebugTool.logInfo(TAG, "Unable to deleted some old voice commands");
+ listener.onComplete(false);
+ }
+ }
+ }
+
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ DeleteCommand foundDeleteCommand = null;
+ for (DeleteCommand deleteCommand : deleteVoiceCommands) {
+ if (correlationId == deleteCommand.getCorrelationID()) {
+ foundDeleteCommand = deleteCommand;
+ break;
+ }
+ }
+
+ if (!response.getSuccess()) {
+ errorObject.put(foundDeleteCommand, response.getInfo());
+ } else {
+ if (foundDeleteCommand == null) {
+ return;
+ }
+ removeCurrentVoiceCommand(foundDeleteCommand.getCmdID());
+ }
+ }
+ });
+
+ }
+
+ // Create DeleteCommand List
+
+ List<DeleteCommand> deleteCommandsForVoiceCommands(List<VoiceCommand> voiceCommands) {
+ List<DeleteCommand> deleteCommandList = new ArrayList<>();
+ for (VoiceCommand command : voiceCommands) {
+ DeleteCommand delete = new DeleteCommand(command.getCommandId());
+ deleteCommandList.add(delete);
+ }
+ return deleteCommandList;
+ }
+
+ private void removeCurrentVoiceCommand(Integer commandId) {
+ for (VoiceCommand voiceCommand : oldVoiceCommands) {
+ if (commandId == voiceCommand.getCommandId()) {
+ currentVoiceCommands.remove(voiceCommand);
+ return;
+ }
+ }
+ }
+
+ // SEND NEW MENU ITEMS
+
+ private void sendCurrentVoiceCommands(final CompletionListener listener) {
+
+ if (pendingVoiceCommands == null || pendingVoiceCommands.size() == 0) {
+ if (listener != null) {
+ listener.onComplete(true); // no voice commands to send doesnt mean that its an error
+ }
+ return;
+ }
+
+ addCommandsToSend = addCommandsForVoiceCommands(pendingVoiceCommands);
+
+ internalInterface.get().sendRPCs(addCommandsToSend, new OnMultipleRequestListener() {
+ @Override
+ public void onUpdate(int remainingRequests) {
+ }
+
+ @Override
+ public void onFinished() {
+ if (listener != null) {
+ if (errorObject.isEmpty()) {
+ DebugTool.logInfo(TAG, "Sending Voice Commands Complete");
+ listener.onComplete(true);
+ } else {
+ DebugTool.logInfo(TAG, "Sending Voice Commands Complete with errors");
+ listener.onComplete(false);
+ }
+ }
+ }
+
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ AddCommand foundAddCommand = null;
+ for (AddCommand addCommand : addCommandsToSend) {
+ if (correlationId == addCommand.getCorrelationID()) {
+ foundAddCommand = addCommand;
+ break;
+ }
+ }
+ if (!response.getSuccess()) {
+ errorObject.put(foundAddCommand, response.getInfo());
+ } else {
+ if (foundAddCommand == null) {
+ return;
+ }
+ VoiceCommand foundVoiceCommand = pendingVoiceCommand(foundAddCommand.getCmdID());
+ if (foundVoiceCommand != null) {
+ currentVoiceCommands.add(foundVoiceCommand);
+ }
+ }
+ }
+ });
+ }
+
+ // Create AddCommand List
+
+ List<AddCommand> addCommandsForVoiceCommands(List<VoiceCommand> voiceCommands) {
+ List<AddCommand> addCommandList = new ArrayList<>();
+ for (VoiceCommand command : voiceCommands) {
+ AddCommand addCommand = new AddCommand(command.getCommandId());
+ addCommand.setVrCommands(command.getVoiceCommands());
+ addCommandList.add(addCommand);
+ }
+ return addCommandList;
+ }
+
+ private VoiceCommand pendingVoiceCommand(Integer commandId) {
+ for (VoiceCommand voiceCommand : pendingVoiceCommands) {
+ if (commandId == voiceCommand.getCommandId()) {
+ return voiceCommand;
+ }
+ }
+ return null;
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java
index bb5e9cb31..5cac1302c 100644
--- a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java
+++ b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java
@@ -71,6 +71,10 @@ public class SdlProtocolBase {
private final static String FailurePropagating_Msg = "Failure propagating ";
private static final int TLS_MAX_RECORD_SIZE = 16384;
+ private final static int TLS_RECORD_HEADER_SIZE = 5;
+ private final static int TLS_RECORD_MES_AUTH_CDE_SIZE = 32;
+ private final static int TLS_MAX_RECORD_PADDING_SIZE = 256;
+ private final static int TLS_MAX_DATA_TO_ENCRYPT_SIZE = TLS_MAX_RECORD_SIZE - TLS_RECORD_HEADER_SIZE - TLS_RECORD_MES_AUTH_CDE_SIZE - TLS_MAX_RECORD_PADDING_SIZE;
private static final int PRIMARY_TRANSPORT_ID = 1;
private static final int SECONDARY_TRANSPORT_ID = 2;
@@ -85,7 +89,7 @@ public class SdlProtocolBase {
public static final int V2_HEADER_SIZE = 12;
//If increasing MAX PROTOCOL VERSION major version, make sure to alter it in SdlPsm
- private static final Version MAX_PROTOCOL_VERSION = new Version(5, 3, 0);
+ private static final Version MAX_PROTOCOL_VERSION = new Version(5, 4, 0);
public static final int V1_V2_MTU_SIZE = 1500;
public static final int V3_V4_MTU_SIZE = 131072;
@@ -103,6 +107,7 @@ public class SdlProtocolBase {
private final Hashtable<Byte, Object> _messageLocks = new Hashtable<>();
private final HashMap<SessionType, Long> mtus = new HashMap<>();
private final HashMap<SessionType, TransportRecord> activeTransports = new HashMap<>();
+ private final HashMap<SessionType, Boolean> serviceStartedOnTransport = new HashMap<>();
private final Map<TransportType, List<ISecondaryTransportListener>> secondaryTransportListeners = new HashMap<>();
@@ -212,6 +217,7 @@ public class SdlProtocolBase {
messageID = 0;
headerSize = V1_HEADER_SIZE;
this.activeTransports.clear();
+ this.serviceStartedOnTransport.clear();
this.mtus.clear();
mtus.put(SessionType.RPC, (long) (V1_V2_MTU_SIZE - headerSize));
this.secondaryTransportParams = null;
@@ -559,7 +565,8 @@ public class SdlProtocolBase {
public void sendMessage(ProtocolMessage protocolMsg) {
SessionType sessionType = protocolMsg.getSessionType();
byte sessionID = protocolMsg.getSessionID();
-
+ boolean requiresEncryption = protocolMsg.getPayloadProtected();
+ SdlSecurityBase sdlSec = null;
byte[] data;
if (protocolVersion.getMajor() > 1 && sessionType != SessionType.NAV && sessionType != SessionType.PCM) {
if (sessionType.eq(SessionType.CONTROL)) {
@@ -588,21 +595,15 @@ public class SdlProtocolBase {
data = protocolMsg.getData();
}
- if (iSdlProtocol != null && protocolMsg.getPayloadProtected()) {
-
- if (data != null && data.length > 0) {
- byte[] dataToRead = new byte[TLS_MAX_RECORD_SIZE];
- SdlSecurityBase sdlSec = iSdlProtocol.getSdlSecurity();
- if (sdlSec == null)
- return;
-
- Integer iNumBytes = sdlSec.encryptData(data, dataToRead);
- if ((iNumBytes == null) || (iNumBytes <= 0))
- return;
-
- byte[] encryptedData = new byte[iNumBytes];
- System.arraycopy(dataToRead, 0, encryptedData, 0, iNumBytes);
- data = encryptedData;
+ if (requiresEncryption) {
+ if (iSdlProtocol == null) {
+ DebugTool.logError(TAG, "Unable to encrypt packet, protocol callback was null");
+ return;
+ }
+ sdlSec = iSdlProtocol.getSdlSecurity();
+ if (sdlSec == null) {
+ DebugTool.logError(TAG, "Unable to encrypt packet, security library not found");
+ return;
}
}
@@ -614,24 +615,26 @@ public class SdlProtocolBase {
return;
}
- synchronized (messageLock) {
- if (data != null && data.length > getMtu(sessionType)) {
+ //Set the MTU according to service MTU provided by the IVI .
+ //If encryption is required the MTU will be set to lowest value between the max data size for encryption or service MTU
+ final Long mtu = requiresEncryption ? Math.min(TLS_MAX_DATA_TO_ENCRYPT_SIZE, getMtu(sessionType)) : getMtu(sessionType);
+ synchronized (messageLock) {
+ if (data != null && data.length > mtu) {
+ //Since the packet is larger than the mtu size, it will be sent as a multi-frame packet
messageID++;
// Assemble first frame.
- Long mtu = getMtu(sessionType);
- int frameCount = Long.valueOf(data.length / mtu).intValue();
- if (data.length % mtu > 0) {
- frameCount++;
- }
+ int frameCount = (int) Math.ceil(data.length / (double) mtu);
+
byte[] firstFrameData = new byte[8];
// First four bytes are data size.
System.arraycopy(BitConverter.intToByteArray(data.length), 0, firstFrameData, 0, 4);
// Second four bytes are frame count.
System.arraycopy(BitConverter.intToByteArray(frameCount), 0, firstFrameData, 4, 4);
- SdlPacket firstHeader = SdlPacketFactory.createMultiSendDataFirst(sessionType, sessionID, messageID, (byte) protocolVersion.getMajor(), firstFrameData, protocolMsg.getPayloadProtected());
+ //NOTE: First frames cannot be encrypted because their payloads need to be exactly 8 bytes
+ SdlPacket firstHeader = SdlPacketFactory.createMultiSendDataFirst(sessionType, sessionID, messageID, (byte) protocolVersion.getMajor(), firstFrameData, false);
firstHeader.setPriorityCoefficient(1 + protocolMsg.priorityCoefficient);
firstHeader.setTransportRecord(activeTransports.get(sessionType));
//Send the first frame
@@ -640,33 +643,65 @@ public class SdlProtocolBase {
int currentOffset = 0;
byte frameSequenceNumber = 0;
+ byte[] dataBuffer, encryptedData;
for (int i = 0; i < frameCount; i++) {
- if (i < (frameCount - 1)) {
+
+ frameSequenceNumber++;
+
+ if (frameSequenceNumber == SdlPacket.FRAME_INFO_FINAL_CONNESCUTIVE_FRAME) {
+ //If sequence numbers roll over to 0, increment again to avoid
+ //using the reserved sequence value for the final frame
++frameSequenceNumber;
- if (frameSequenceNumber ==
- SdlPacket.FRAME_INFO_FINAL_CONNESCUTIVE_FRAME) {
- // we can't use 0x00 as frameSequenceNumber, because
- // it's reserved for the last frame
- ++frameSequenceNumber;
- }
- } else {
+ }
+
+ if (i == frameCount - 1) {
frameSequenceNumber = SdlPacket.FRAME_INFO_FINAL_CONNESCUTIVE_FRAME;
- } // end-if
+ }
int bytesToWrite = data.length - currentOffset;
if (bytesToWrite > mtu) {
bytesToWrite = mtu.intValue();
}
- SdlPacket consecHeader = SdlPacketFactory.createMultiSendDataRest(sessionType, sessionID, bytesToWrite, frameSequenceNumber, messageID, (byte) protocolVersion.getMajor(), data, currentOffset, bytesToWrite, protocolMsg.getPayloadProtected());
- consecHeader.setTransportRecord(activeTransports.get(sessionType));
- consecHeader.setPriorityCoefficient(i + 2 + protocolMsg.priorityCoefficient);
- handlePacketToSend(consecHeader);
+
+ SdlPacket consecutiveFrame;
+ if (requiresEncryption) {
+ //Retrieve a chunk of the data into a temporary buffer to be encrypted
+ dataBuffer = new byte[bytesToWrite];
+ System.arraycopy(data, currentOffset, dataBuffer, 0, bytesToWrite);
+
+ encryptedData = new byte[TLS_MAX_RECORD_SIZE];
+ Integer numberOfBytesEncrypted = sdlSec.encryptData(dataBuffer, encryptedData);
+ if (numberOfBytesEncrypted == null || (numberOfBytesEncrypted <= 0)) {
+ DebugTool.logError(TAG, "Unable to encrypt data");
+ return;
+ }
+
+ consecutiveFrame = SdlPacketFactory.createMultiSendDataRest(sessionType, sessionID, numberOfBytesEncrypted, frameSequenceNumber, messageID, (byte) protocolVersion.getMajor(), encryptedData, 0, numberOfBytesEncrypted, true);
+ } else {
+ consecutiveFrame = SdlPacketFactory.createMultiSendDataRest(sessionType, sessionID, bytesToWrite, frameSequenceNumber, messageID, (byte) protocolVersion.getMajor(), data, currentOffset, bytesToWrite, false);
+ }
+
+ consecutiveFrame.setTransportRecord(activeTransports.get(sessionType));
+ consecutiveFrame.setPriorityCoefficient(i + 2 + protocolMsg.priorityCoefficient);
+ handlePacketToSend(consecutiveFrame);
currentOffset += bytesToWrite;
}
} else {
messageID++;
+ if (requiresEncryption && data != null && data.length > 0) {
+ //Encrypt the data before sending
+ byte[] encryptedData = new byte[TLS_MAX_RECORD_SIZE];
+ Integer numberOfBytesEncrypted = sdlSec.encryptData(data, encryptedData);
+ if (numberOfBytesEncrypted == null || (numberOfBytesEncrypted <= 0)) {
+ DebugTool.logError(TAG, "Unable to encrypt data");
+ return;
+ }
+ //Put the encrypted bytes back into the data array
+ data = new byte[numberOfBytesEncrypted];
+ System.arraycopy(encryptedData, 0, data, 0, numberOfBytesEncrypted);
+ }
int dataLength = data != null ? data.length : 0;
- SdlPacket header = SdlPacketFactory.createSingleSendData(sessionType, sessionID, dataLength, messageID, (byte) protocolVersion.getMajor(), data, protocolMsg.getPayloadProtected());
+ SdlPacket header = SdlPacketFactory.createSingleSendData(sessionType, sessionID, dataLength, messageID, (byte) protocolVersion.getMajor(), data, requiresEncryption);
header.setPriorityCoefficient(protocolMsg.priorityCoefficient);
header.setTransportRecord(activeTransports.get(sessionType));
handlePacketToSend(header);
@@ -708,6 +743,7 @@ public class SdlProtocolBase {
public void startService(SessionType serviceType, byte sessionID, boolean isEncrypted) {
final SdlPacket header = SdlPacketFactory.createStartSession(serviceType, 0x00, (byte) protocolVersion.getMajor(), sessionID, isEncrypted);
+ serviceStartedOnTransport.put(serviceType, true);
if (SessionType.RPC.equals(serviceType)) {
if (connectedPrimaryTransport != null) {
header.setTransportRecord(connectedPrimaryTransport);
@@ -1193,12 +1229,18 @@ public class SdlProtocolBase {
//a single transport record per transport.
//TransportType type = disconnectedTransport.getType();
if (getTransportForSession(SessionType.NAV) != null && disconnectedTransport.equals(getTransportForSession(SessionType.NAV))) {
- iSdlProtocol.onServiceError(null, SessionType.NAV, iSdlProtocol.getSessionId(), "Transport disconnected");
- activeTransports.remove(SessionType.NAV);
+ if (serviceStartedOnTransport.get(SessionType.NAV) != null && serviceStartedOnTransport.get(SessionType.NAV)) {
+ iSdlProtocol.onServiceError(null, SessionType.NAV, iSdlProtocol.getSessionId(), "Transport disconnected");
+ activeTransports.remove(SessionType.NAV);
+ serviceStartedOnTransport.remove(SessionType.NAV);
+ }
}
if (getTransportForSession(SessionType.PCM) != null && disconnectedTransport.equals(getTransportForSession(SessionType.PCM))) {
- iSdlProtocol.onServiceError(null, SessionType.PCM, iSdlProtocol.getSessionId(), "Transport disconnected");
- activeTransports.remove(SessionType.PCM);
+ if (serviceStartedOnTransport.get(SessionType.PCM) != null && serviceStartedOnTransport.get(SessionType.PCM)) {
+ iSdlProtocol.onServiceError(null, SessionType.PCM, iSdlProtocol.getSessionId(), "Transport disconnected");
+ activeTransports.remove(SessionType.PCM);
+ serviceStartedOnTransport.remove(SessionType.PCM);
+ }
}
if ((getTransportForSession(SessionType.RPC) != null && disconnectedTransport.equals(getTransportForSession(SessionType.RPC))) || disconnectedTransport.equals(connectedPrimaryTransport)) {
@@ -1255,6 +1297,7 @@ public class SdlProtocolBase {
requestedSession = false;
activeTransports.clear();
+ serviceStartedOnTransport.clear();
iSdlProtocol.onTransportDisconnected(info, primaryTransportAvailable, transportConfig);
@@ -1364,16 +1407,17 @@ public class SdlProtocolBase {
if (packet.getPayload() != null && packet.getDataSize() > 0 && packet.isEncrypted()) {
SdlSecurityBase sdlSec = iSdlProtocol.getSdlSecurity();
- byte[] dataToRead = new byte[4096];
+ byte[] dataToRead = new byte[TLS_MAX_RECORD_SIZE];
- Integer iNumBytes = sdlSec.decryptData(packet.getPayload(), dataToRead);
- if ((iNumBytes == null) || (iNumBytes <= 0)) {
+ Integer numberOfDecryptedBytes = sdlSec.decryptData(packet.getPayload(), dataToRead);
+ if ((numberOfDecryptedBytes == null) || (numberOfDecryptedBytes <= 0)) {
return;
}
- byte[] decryptedData = new byte[iNumBytes];
- System.arraycopy(dataToRead, 0, decryptedData, 0, iNumBytes);
+ byte[] decryptedData = new byte[numberOfDecryptedBytes];
+ System.arraycopy(dataToRead, 0, decryptedData, 0, numberOfDecryptedBytes);
packet.payload = decryptedData;
+ packet.dataSize = numberOfDecryptedBytes;
}
if (packet.getFrameType().equals(FrameType.Control)) {
diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/ControlFrameTags.java b/base/src/main/java/com/smartdevicelink/protocol/enums/ControlFrameTags.java
index 992dccfb3..a6656fa09 100644
--- a/base/src/main/java/com/smartdevicelink/protocol/enums/ControlFrameTags.java
+++ b/base/src/main/java/com/smartdevicelink/protocol/enums/ControlFrameTags.java
@@ -85,6 +85,19 @@ public class ControlFrameTags {
* Auth token to be used for log in into services
**/
public static final String AUTH_TOKEN = "authToken";
+ /**
+ * Vehicle info to describe connected device
+ */
+ public static final String MAKE = "make";
+ public static final String MODEL = "model";
+ public static final String MODEL_YEAR = "modelYear";
+ public static final String TRIM = "trim";
+
+ /**
+ * System specifics for hardware and software versions of connected device
+ */
+ public static final String SYSTEM_SOFTWARE_VERSION = "systemSoftwareVersion";
+ public static final String SYSTEM_HARDWARE_VERSION = "systemHardwareVersion";
}
diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/FunctionID.java b/base/src/main/java/com/smartdevicelink/protocol/enums/FunctionID.java
index 2b80f4d51..e75722d93 100644
--- a/base/src/main/java/com/smartdevicelink/protocol/enums/FunctionID.java
+++ b/base/src/main/java/com/smartdevicelink/protocol/enums/FunctionID.java
@@ -134,6 +134,7 @@ public enum FunctionID {
ON_SUBTLE_ALERT_PRESSED(32788, "OnSubtleAlertPressed"),
ON_UPDATE_FILE(32789, "OnUpdateFile"),
ON_UPDATE_SUB_MENU(32790, "OnUpdateSubMenu"),
+ ON_APP_CAPABILITY_UPDATED(32791, "OnAppCapabilityUpdated"),
;
public static final int INVALID_ID = -1;
diff --git a/base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java b/base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java
index e99eba1d7..c0dd495dd 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java
@@ -31,7 +31,10 @@
*/
package com.smartdevicelink.proxy;
+import androidx.annotation.Nullable;
+
import com.smartdevicelink.marshal.JsonRPCMarshaller;
+import com.smartdevicelink.util.DebugTool;
import com.smartdevicelink.util.SdlDataTypeConverter;
import com.smartdevicelink.util.Version;
@@ -46,7 +49,7 @@ import java.util.Hashtable;
import java.util.List;
import java.util.Set;
-public class RPCStruct {
+public class RPCStruct implements Cloneable {
public static final String KEY_BULK_DATA = "bulkData";
public static final String KEY_PROTECTED = "protected";
@@ -375,4 +378,60 @@ public class RPCStruct {
}
return null;
}
+
+ /**
+ * Creates a deep copy of the object
+ *
+ * @return deep copy of the object, null if an exception occurred
+ */
+ @Override
+ public RPCStruct clone() {
+ try {
+ RPCStruct clone = (RPCStruct) super.clone();
+ clone.setPayloadProtected(protectedPayload);
+ clone.setBulkData(_bulkData);
+ clone.store = (Hashtable) store.clone();
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ DebugTool.logError("RPCStruct", "Failed to clone: " + e);
+ return null;
+ }
+ }
+
+ /**
+ * Uses the RPCStruct store for RPCStruct objects
+ *
+ * @param obj - The object to compare
+ * @return boolean of whether the objects are the same or not
+ */
+ @Override
+ public boolean equals(@Nullable Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ // if this is the same memory address, its the same
+ if (this == obj) {
+ return true;
+ }
+ // if this is not an instance of the same class, not the same
+ if (obj.getClass() != getClass()) {
+ return false;
+ }
+ // return comparison of store
+ return isEqualToRPC((RPCStruct) obj);
+ }
+
+ private boolean isEqualToRPC(RPCStruct rpc) {
+ return store.equals(rpc.store);
+ }
+
+ /**
+ * Used to compile hashcode for RPCStruct
+ *
+ * @return Custom hashcode of RPCStruct
+ */
+ @Override
+ public int hashCode() {
+ return store.hashCode();
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/AddCommand.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/AddCommand.java
index 1af9f8238..3458e361a 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/AddCommand.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/AddCommand.java
@@ -107,6 +107,16 @@ import java.util.List;
* <td></td>
* <td>SmartDeviceLink 1.0</td>
* </tr>
+ * <tr>
+ * <td>secondaryImage</td>
+ * <td>Image</td>
+ * <td>Optional secondary image struct for menu cell</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
* </table>
* <p> <b>Response</b></p><p>Indicates that the corresponding request has failed or succeeded, if the response returns with a SUCCESS result code, this means a command was added to the Command Menu successfully.</p>
*
@@ -123,6 +133,7 @@ public class AddCommand extends RPCRequest {
public static final String KEY_MENU_PARAMS = "menuParams";
public static final String KEY_CMD_ID = "cmdID";
public static final String KEY_VR_COMMANDS = "vrCommands";
+ public static final String KEY_SECONDARY_IMAGE = "secondaryImage";
/**
* Constructs a new AddCommand object
@@ -263,4 +274,25 @@ public class AddCommand extends RPCRequest {
setParameters(KEY_CMD_ICON, cmdIcon);
return this;
}
+
+ /**
+ * Sets the secondaryImage.
+ *
+ * @param secondaryImage Optional secondary image struct for menu cell
+ * @since SmartDeviceLink 7.1.0
+ */
+ public AddCommand setSecondaryImage(Image secondaryImage) {
+ setParameters(KEY_SECONDARY_IMAGE, secondaryImage);
+ return this;
+ }
+
+ /**
+ * Gets the secondaryImage.
+ *
+ * @return Image Optional secondary image struct for menu cell
+ * @since SmartDeviceLink 7.1.0
+ */
+ public Image getSecondaryImage() {
+ return (Image) getObject(Image.class, KEY_SECONDARY_IMAGE);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/AddSubMenu.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/AddSubMenu.java
index 38eed2ed1..93c5f0719 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/AddSubMenu.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/AddSubMenu.java
@@ -106,6 +106,36 @@ import java.util.Hashtable;
* <td>Min Value: 0 <p>Max Value: 2000000000</p> </td>
* <td>SmartDeviceLink 7.0.0</td>
* </tr>
+ * <tr>
+ * <td>secondaryText</td>
+ * <td>String</td>
+ * <td>Optional secondary text to display</td>
+ * <td>N</td>
+ * <td>{"string_min_length": 1, "string_max_length": 500}</td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>tertiaryText</td>
+ * <td>String</td>
+ * <td>Optional tertiary text to display</td>
+ * <td>N</td>
+ * <td>{"string_min_length": 1, "string_max_length": 500}</td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>secondaryImage</td>
+ * <td>Image</td>
+ * <td>Optional secondary image struct for sub-menu cell</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
* </table>
* <b>Response</b>
* <p>Indicates that the corresponding request either failed or succeeded. If the response returns with a SUCCESS result code, this means the SubMenu was added to the Command Menu successfully</p>
@@ -126,6 +156,10 @@ public class AddSubMenu extends RPCRequest {
public static final String KEY_MENU_ICON = "menuIcon";
public static final String KEY_MENU_LAYOUT = "menuLayout";
public static final String KEY_PARENT_ID = "parentID";
+ public static final String KEY_SECONDARY_TEXT = "secondaryText";
+ public static final String KEY_TERTIARY_TEXT = "tertiaryText";
+ public static final String KEY_SECONDARY_IMAGE = "secondaryImage";
+
/**
* Constructs a new AddSubMenu object
@@ -293,4 +327,71 @@ public class AddSubMenu extends RPCRequest {
public Integer getParentID() {
return getInteger(KEY_PARENT_ID);
}
+
+ /**
+ * Sets the secondaryText.
+ *
+ * @param secondaryText Optional secondary text to display
+ * {"string_min_length": 1, "string_max_length": 500}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public AddSubMenu setSecondaryText(String secondaryText) {
+ setParameters(KEY_SECONDARY_TEXT, secondaryText);
+ return this;
+ }
+
+ /**
+ * Gets the secondaryText.
+ *
+ * @return String Optional secondary text to display
+ * {"string_min_length": 1, "string_max_length": 500}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public String getSecondaryText() {
+ return getString(KEY_SECONDARY_TEXT);
+ }
+
+ /**
+ * Sets the tertiaryText.
+ *
+ * @param tertiaryText Optional tertiary text to display
+ * {"string_min_length": 1, "string_max_length": 500}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public AddSubMenu setTertiaryText(String tertiaryText) {
+ setParameters(KEY_TERTIARY_TEXT, tertiaryText);
+ return this;
+ }
+
+ /**
+ * Gets the tertiaryText.
+ *
+ * @return String Optional tertiary text to display
+ * {"string_min_length": 1, "string_max_length": 500}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public String getTertiaryText() {
+ return getString(KEY_TERTIARY_TEXT);
+ }
+
+ /**
+ * Sets the secondaryImage.
+ *
+ * @param secondaryImage Optional secondary image struct for sub-menu cell
+ * @since SmartDeviceLink 7.1.0
+ */
+ public AddSubMenu setSecondaryImage(Image secondaryImage) {
+ setParameters(KEY_SECONDARY_IMAGE, secondaryImage);
+ return this;
+ }
+
+ /**
+ * Gets the secondaryImage.
+ *
+ * @return Image Optional secondary image struct for sub-menu cell
+ * @since SmartDeviceLink 7.1.0
+ */
+ public Image getSecondaryImage() {
+ return (Image) getObject(Image.class, KEY_SECONDARY_IMAGE);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/AppCapability.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/AppCapability.java
new file mode 100644
index 000000000..213a4a758
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/AppCapability.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2017 - 2021, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc;
+
+import androidx.annotation.NonNull;
+
+import com.smartdevicelink.proxy.RPCStruct;
+import com.smartdevicelink.proxy.rpc.enums.AppCapabilityType;
+
+import java.util.Hashtable;
+
+/**
+ *
+ * <p><b>Parameter List</b></p>
+ *
+ * <table border="1" rules="all">
+ * <tr>
+ * <th>Param Name</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * <th>Required</th>
+ * <th>Notes</th>
+ * <th>Version Available</th>
+ * </tr>
+ * <tr>
+ * <td>appCapabilityType</td>
+ * <td>AppCapabilityType</td>
+ * <td>Used as a descriptor of what data to expect in this struct. The corresponding param to this enum should be included and the only other param included.</td>
+ * <td>Y</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>videoStreamingCapability</td>
+ * <td>VideoStreamingCapability</td>
+ * <td>Describes supported capabilities for video streaming</td>
+ * <td>N</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * </table>
+ * @since SmartDeviceLink 7.1.0
+ */
+public class AppCapability extends RPCStruct {
+ public static final String KEY_APP_CAPABILITY_TYPE = "appCapabilityType";
+ public static final String KEY_VIDEO_STREAMING_CAPABILITY = "videoStreamingCapability";
+
+ /**
+ * Constructs a new AppCapability object
+ */
+ public AppCapability() { }
+
+ /**
+ * Constructs a new AppCapability object indicated by the Hashtable parameter
+ *
+ * @param hash The Hashtable to use
+ */
+ public AppCapability(Hashtable<String, Object> hash) {
+ super(hash);
+ }
+
+ /**
+ * Constructs a new AppCapability object
+ *
+ * @param appCapabilityType Used as a descriptor of what data to expect in this struct. The corresponding param to
+ * this enum should be included and the only other param included.
+ */
+ public AppCapability(@NonNull AppCapabilityType appCapabilityType) {
+ this();
+ setAppCapabilityType(appCapabilityType);
+ }
+
+ /**
+ * Sets the appCapabilityType.
+ *
+ * @param appCapabilityType Used as a descriptor of what data to expect in this struct. The corresponding param to
+ * this enum should be included and the only other param included.
+ */
+ public AppCapability setAppCapabilityType(@NonNull AppCapabilityType appCapabilityType) {
+ setValue(KEY_APP_CAPABILITY_TYPE, appCapabilityType);
+ return this;
+ }
+
+ /**
+ * Gets the appCapabilityType.
+ *
+ * @return AppCapabilityType Used as a descriptor of what data to expect in this struct. The corresponding param to
+ * this enum should be included and the only other param included.
+ */
+ public AppCapabilityType getAppCapabilityType() {
+ return (AppCapabilityType) getObject(AppCapabilityType.class, KEY_APP_CAPABILITY_TYPE);
+ }
+
+ /**
+ * Sets the videoStreamingCapability.
+ *
+ * @param videoStreamingCapability Describes supported capabilities for video streaming
+ */
+ public AppCapability setVideoStreamingCapability(VideoStreamingCapability videoStreamingCapability) {
+ setValue(KEY_VIDEO_STREAMING_CAPABILITY, videoStreamingCapability);
+ return this;
+ }
+
+ /**
+ * Gets the videoStreamingCapability.
+ *
+ * @return VideoStreamingCapability Describes supported capabilities for video streaming
+ */
+ public VideoStreamingCapability getVideoStreamingCapability() {
+ return (VideoStreamingCapability) getObject(VideoStreamingCapability.class, KEY_VIDEO_STREAMING_CAPABILITY);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/AppInfo.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/AppInfo.java
index 7d33b9b50..b47227915 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/AppInfo.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/AppInfo.java
@@ -37,8 +37,57 @@ import com.smartdevicelink.proxy.RPCStruct;
import java.util.Hashtable;
+/**
+ * Contains detailed information about the registered application.
+ *
+ * <p><b>Parameter List</b></p>
+ *
+ * <table border="1" rules="all">
+ * <tr>
+ * <th>Param Name</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * <th>Required</th>
+ * <th>Notes</th>
+ * <th>Version Available</th>
+ * </tr>
+ * <tr>
+ * <td>appDisplayName</td>
+ * <td>String</td>
+ * <td>The name displayed for the mobile application on the mobile device (can differ from the app name set in the initial RAI request).</td>
+ * <td>Y</td>
+ * <td>{"string_min_length": 1, "string_max_length": 100}</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>appBundleID</td>
+ * <td>String</td>
+ * <td>The AppBundleID of an iOS application or package name of the Android application. This supports App Launch strategies for each platform.</td>
+ * <td>Y</td>
+ * <td>{"string_min_length": 1, "string_max_length": 256}</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>appVersion</td>
+ * <td>String</td>
+ * <td>Represents the build version number of this particular mobile app.</td>
+ * <td>Y</td>
+ * <td>{"string_min_length": 1, "string_max_length": 256}</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>appIcon</td>
+ * <td>String</td>
+ * <td>A file reference to the icon utilized by this app (simplifies the process of setting an app icon during app registration).</td>
+ * <td>N</td>
+ * <td>{"string_min_length": 1, "string_max_length": 500}</td>
+ * <td></td>
+ * </tr>
+ * </table>
+ *
+ * @since SmartDeviceLink 4.2.0
+ */
public class AppInfo extends RPCStruct {
-
public static final String KEY_APP_DISPLAY_NAME = "appDisplayName";
public static final String KEY_APP_BUNDLE_ID = "appBundleID";
public static final String KEY_APP_VERSION = "appVersion";
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/BodyInformation.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/BodyInformation.java
index fcb5c8d70..62a006abe 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/BodyInformation.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/BodyInformation.java
@@ -38,6 +38,7 @@ import com.smartdevicelink.proxy.rpc.enums.IgnitionStableStatus;
import com.smartdevicelink.proxy.rpc.enums.IgnitionStatus;
import java.util.Hashtable;
+import java.util.List;
/**
* The body information including power modes.
@@ -86,30 +87,71 @@ import java.util.Hashtable;
* <td>Boolean</td>
* <td>true</td>
* <td>The information about the park brake: - true, if active - false if not.</td>
- * <td>SmartDeviceLink 2.0</td>
+ * <td>
+ * @since SmartDeviceLink 2.0.0
+ * @property-deprecated in SmartDeviceLink 7.1.0
+ * </td>
* </tr>
* <tr>
* <td>passengerDoorAjar</td>
* <td>Boolean</td>
* <td>true</td>
* <td>The information about the park brake: - true, if active - false if not.</td>
- * <td>SmartDeviceLink 2.0</td>
+ * <td>
+ * @since SmartDeviceLink 2.0.0
+ * @property-deprecated in SmartDeviceLink 7.1.0
+ * </td>
* </tr>
* <tr>
* <td>rearLeftDoorAjar</td>
* <td>Boolean</td>
* <td>true</td>
* <td>The information about the park brake: - true, if active - false if not.</td>
- * <td>SmartDeviceLink 2.0</td>
+ * <td>
+ * @since SmartDeviceLink 2.0.0
+ * @property-deprecated in SmartDeviceLink 7.1.0
+ * </td>
* </tr>
* <tr>
* <td>rearRightDoorAjar</td>
* <td>Boolean</td>
* <td>true</td>
* <td>References signal "DrStatRr_B_Actl".</td>
- * <td>SmartDeviceLink 2.0</td>
+ * <td>
+ * @since SmartDeviceLink 2.0.0
+ * @property-deprecated in SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>doorStatuses</td>
+ * <td>List<DoorStatus></td>
+ * <td>Provides status for doors if Ajar/Closed/Locked</td>
+ * <td>N</td>
+ * <td>{"array_min_size": 0, "array_max_size": 100}</td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>gateStatuses</td>
+ * <td>List<GateStatus></td>
+ * <td>Provides status for trunk/hood/etc. if Ajar/Closed/Locked</td>
+ * <td>N</td>
+ * <td>{"array_min_size": 0, "array_max_size": 100}</td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>roofStatuses</td>
+ * <td>List<RoofStatus></td>
+ * <td>Provides status for roof/convertible roof/sunroof/moonroof etc., if Closed/Ajar/Removedetc.</td>
+ * <td>N</td>
+ * <td>{"array_min_size": 0, "array_max_size": 100}</td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
* </tr>
- *
* </table>
*
* @see SubscribeVehicleData
@@ -122,10 +164,42 @@ public class BodyInformation extends RPCStruct {
public static final String KEY_PARK_BRAKE_ACTIVE = "parkBrakeActive";
public static final String KEY_IGNITION_STABLE_STATUS = "ignitionStableStatus";
public static final String KEY_IGNITION_STATUS = "ignitionStatus";
+ /**
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public static final String KEY_DRIVER_DOOR_AJAR = "driverDoorAjar";
+ /**
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public static final String KEY_PASSENGER_DOOR_AJAR = "passengerDoorAjar";
+ /**
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public static final String KEY_REAR_LEFT_DOOR_AJAR = "rearLeftDoorAjar";
+ /**
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public static final String KEY_REAR_RIGHT_DOOR_AJAR = "rearRightDoorAjar";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_DOOR_STATUSES = "doorStatuses";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_GATE_STATUSES = "gateStatuses";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_ROOF_STATUSES = "roofStatuses";
public BodyInformation() {
}
@@ -175,41 +249,177 @@ public class BodyInformation extends RPCStruct {
return (IgnitionStatus) getObject(IgnitionStatus.class, KEY_IGNITION_STATUS);
}
+ /**
+ * Sets the driverDoorAjar.
+ *
+ * @param driverDoorAjar References signal "DrStatDrv_B_Actl". Deprecated starting with RPC Spec 7.1.0.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public BodyInformation setDriverDoorAjar(Boolean driverDoorAjar) {
setValue(KEY_DRIVER_DOOR_AJAR, driverDoorAjar);
return this;
}
+ /**
+ * Gets the driverDoorAjar.
+ *
+ * @return Boolean References signal "DrStatDrv_B_Actl". Deprecated starting with RPC Spec 7.1.0.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public Boolean getDriverDoorAjar() {
return getBoolean(KEY_DRIVER_DOOR_AJAR);
}
-
+ /**
+ * Sets the passengerDoorAjar.
+ *
+ * @param passengerDoorAjar References signal "DrStatPsngr_B_Actl". Deprecated starting with RPC Spec 7.1.0.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public BodyInformation setPassengerDoorAjar(Boolean passengerDoorAjar) {
setValue(KEY_PASSENGER_DOOR_AJAR, passengerDoorAjar);
return this;
}
+ /**
+ * Gets the passengerDoorAjar.
+ *
+ * @return Boolean References signal "DrStatPsngr_B_Actl". Deprecated starting with RPC Spec 7.1.0.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public Boolean getPassengerDoorAjar() {
return getBoolean(KEY_PASSENGER_DOOR_AJAR);
}
+ /**
+ * Sets the rearLeftDoorAjar.
+ *
+ * @param rearLeftDoorAjar References signal "DrStatRl_B_Actl". Deprecated starting with RPC Spec 7.1.0.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public BodyInformation setRearLeftDoorAjar(Boolean rearLeftDoorAjar) {
setValue(KEY_REAR_LEFT_DOOR_AJAR, rearLeftDoorAjar);
return this;
}
+ /**
+ * Gets the rearLeftDoorAjar.
+ *
+ * @return Boolean References signal "DrStatRl_B_Actl". Deprecated starting with RPC Spec 7.1.0.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public Boolean getRearLeftDoorAjar() {
return getBoolean(KEY_REAR_LEFT_DOOR_AJAR);
}
+ /**
+ * Sets the rearRightDoorAjar.
+ *
+ * @param rearRightDoorAjar References signal "DrStatRr_B_Actl". Deprecated starting with RPC Spec 7.1.0.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public BodyInformation setRearRightDoorAjar(Boolean rearRightDoorAjar) {
setValue(KEY_REAR_RIGHT_DOOR_AJAR, rearRightDoorAjar);
return this;
}
+ /**
+ * Gets the rearRightDoorAjar.
+ *
+ * @return Boolean References signal "DrStatRr_B_Actl". Deprecated starting with RPC Spec 7.1.0.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public Boolean getRearRightDoorAjar() {
return getBoolean(KEY_REAR_RIGHT_DOOR_AJAR);
}
+ /**
+ * Sets the doorStatuses.
+ *
+ * @param doorStatuses Provides status for doors if Ajar/Closed/Locked
+ * {"array_min_size": 0, "array_max_size": 100}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public BodyInformation setDoorStatuses(List<DoorStatus> doorStatuses) {
+ setValue(KEY_DOOR_STATUSES, doorStatuses);
+ return this;
+ }
+
+ /**
+ * Gets the doorStatuses.
+ *
+ * @return List<DoorStatus> Provides status for doors if Ajar/Closed/Locked
+ * {"array_min_size": 0, "array_max_size": 100}
+ * @since SmartDeviceLink 7.1.0
+ */
+ @SuppressWarnings("unchecked")
+ public List<DoorStatus> getDoorStatuses() {
+ return (List<DoorStatus>) getObject(DoorStatus.class, KEY_DOOR_STATUSES);
+ }
+
+ /**
+ * Sets the gateStatuses.
+ *
+ * @param gateStatuses Provides status for trunk/hood/etc. if Ajar/Closed/Locked
+ * {"array_min_size": 0, "array_max_size": 100}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public BodyInformation setGateStatuses(List<GateStatus> gateStatuses) {
+ setValue(KEY_GATE_STATUSES, gateStatuses);
+ return this;
+ }
+
+ /**
+ * Gets the gateStatuses.
+ *
+ * @return List<GateStatus> Provides status for trunk/hood/etc. if Ajar/Closed/Locked
+ * {"array_min_size": 0, "array_max_size": 100}
+ * @since SmartDeviceLink 7.1.0
+ */
+ @SuppressWarnings("unchecked")
+ public List<GateStatus> getGateStatuses() {
+ return (List<GateStatus>) getObject(GateStatus.class, KEY_GATE_STATUSES);
+ }
+
+ /**
+ * Sets the roofStatuses.
+ *
+ * @param roofStatuses Provides status for roof/convertible roof/sunroof/moonroof etc., if Closed/Ajar/Removed
+ * etc.
+ * {"array_min_size": 0, "array_max_size": 100}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public BodyInformation setRoofStatuses(List<RoofStatus> roofStatuses) {
+ setValue(KEY_ROOF_STATUSES, roofStatuses);
+ return this;
+ }
+
+ /**
+ * Gets the roofStatuses.
+ *
+ * @return List<RoofStatus> Provides status for roof/convertible roof/sunroof/moonroof etc., if Closed/Ajar/Removed
+ * etc.
+ * {"array_min_size": 0, "array_max_size": 100}
+ * @since SmartDeviceLink 7.1.0
+ */
+ @SuppressWarnings("unchecked")
+ public List<RoofStatus> getRoofStatuses() {
+ return (List<RoofStatus>) getObject(RoofStatus.class, KEY_ROOF_STATUSES);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/ClimateData.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/ClimateData.java
new file mode 100644
index 000000000..e44635163
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/ClimateData.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2017 - 2020, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc;
+
+import com.smartdevicelink.proxy.RPCStruct;
+import com.smartdevicelink.util.SdlDataTypeConverter;
+
+import java.util.Hashtable;
+
+/**
+ *
+ * <p><b>Parameter List</b></p>
+ *
+ * <table border="1" rules="all">
+ * <tr>
+ * <th>Param Name</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * <th>Required</th>
+ * <th>Notes</th>
+ * <th>Version Available</th>
+ * </tr>
+ * <tr>
+ * <td>externalTemperature</td>
+ * <td>Temperature</td>
+ * <td>The external temperature in degrees celsius</td>
+ * <td>N</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>cabinTemperature</td>
+ * <td>Temperature</td>
+ * <td>Internal ambient cabin temperature in degrees celsius</td>
+ * <td>N</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>atmosphericPressure</td>
+ * <td>Float</td>
+ * <td>Current atmospheric pressure in mBar</td>
+ * <td>N</td>
+ * <td>{"num_min_value": 0.0, "num_max_value": 2000.0}</td>
+ * <td></td>
+ * </tr>
+ * </table>
+ * @since SmartDeviceLink 7.1.0
+ */
+public class ClimateData extends RPCStruct {
+ public static final String KEY_EXTERNAL_TEMPERATURE = "externalTemperature";
+ public static final String KEY_CABIN_TEMPERATURE = "cabinTemperature";
+ public static final String KEY_ATMOSPHERIC_PRESSURE = "atmosphericPressure";
+
+ /**
+ * Constructs a new ClimateData object
+ */
+ public ClimateData() { }
+
+ /**
+ * Constructs a new ClimateData object indicated by the Hashtable parameter
+ *
+ * @param hash The Hashtable to use
+ */
+ public ClimateData(Hashtable<String, Object> hash) {
+ super(hash);
+ }
+
+ /**
+ * Sets the externalTemperature.
+ *
+ * @param externalTemperature The external temperature in degrees celsius
+ */
+ public ClimateData setExternalTemperature(Temperature externalTemperature) {
+ setValue(KEY_EXTERNAL_TEMPERATURE, externalTemperature);
+ return this;
+ }
+
+ /**
+ * Gets the externalTemperature.
+ *
+ * @return Temperature The external temperature in degrees celsius
+ */
+ public Temperature getExternalTemperature() {
+ return (Temperature) getObject(Temperature.class, KEY_EXTERNAL_TEMPERATURE);
+ }
+
+ /**
+ * Sets the cabinTemperature.
+ *
+ * @param cabinTemperature Internal ambient cabin temperature in degrees celsius
+ */
+ public ClimateData setCabinTemperature(Temperature cabinTemperature) {
+ setValue(KEY_CABIN_TEMPERATURE, cabinTemperature);
+ return this;
+ }
+
+ /**
+ * Gets the cabinTemperature.
+ *
+ * @return Temperature Internal ambient cabin temperature in degrees celsius
+ */
+ public Temperature getCabinTemperature() {
+ return (Temperature) getObject(Temperature.class, KEY_CABIN_TEMPERATURE);
+ }
+
+ /**
+ * Sets the atmosphericPressure.
+ *
+ * @param atmosphericPressure Current atmospheric pressure in mBar
+ * {"num_min_value": 0.0, "num_max_value": 2000.0}
+ */
+ public ClimateData setAtmosphericPressure(Float atmosphericPressure) {
+ setValue(KEY_ATMOSPHERIC_PRESSURE, atmosphericPressure);
+ return this;
+ }
+
+ /**
+ * Gets the atmosphericPressure.
+ *
+ * @return Float Current atmospheric pressure in mBar
+ * {"num_min_value": 0.0, "num_max_value": 2000.0}
+ */
+ public Float getAtmosphericPressure() {
+ Object object = getValue(KEY_ATMOSPHERIC_PRESSURE);
+ return SdlDataTypeConverter.objectToFloat(object);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/DisplayCapabilities.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/DisplayCapabilities.java
index e5757c3d2..05a49adc0 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/DisplayCapabilities.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/DisplayCapabilities.java
@@ -52,7 +52,7 @@ import java.util.List;
* <th>SmartDeviceLink Ver. Available</th>
* </tr>
* <tr>
- * <td>@Deprecated <s>displayType</s></td>
+ * <td>@property-deprecated <s>displayType</s></td>
* <td><s>DisplayType</s></td>
* <td><s>The type of display</s>
* </td>
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/DoorStatus.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/DoorStatus.java
new file mode 100644
index 000000000..777542085
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/DoorStatus.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017 - 2020, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc;
+
+import androidx.annotation.NonNull;
+
+import com.smartdevicelink.proxy.RPCStruct;
+import com.smartdevicelink.proxy.rpc.enums.DoorStatusType;
+
+import java.util.Hashtable;
+
+/**
+ * Describes the status of a parameter of door.
+ *
+ * <p><b>Parameter List</b></p>
+ *
+ * <table border="1" rules="all">
+ * <tr>
+ * <th>Param Name</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * <th>Required</th>
+ * <th>Notes</th>
+ * <th>Version Available</th>
+ * </tr>
+ * <tr>
+ * <td>location</td>
+ * <td>Grid</td>
+ * <td></td>
+ * <td>Y</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>status</td>
+ * <td>DoorStatusType</td>
+ * <td></td>
+ * <td>Y</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * </table>
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+public class DoorStatus extends RPCStruct {
+ public static final String KEY_LOCATION = "location";
+ public static final String KEY_STATUS = "status";
+
+ /**
+ * Constructs a new DoorStatus object
+ */
+ public DoorStatus() { }
+
+ /**
+ * Constructs a new DoorStatus object indicated by the Hashtable parameter
+ *
+ * @param hash The Hashtable to use
+ */
+ public DoorStatus(Hashtable<String, Object> hash) {
+ super(hash);
+ }
+
+ /**
+ * Constructs a new DoorStatus object
+ *
+ * @param location
+ * @param status
+ */
+ public DoorStatus(@NonNull Grid location, @NonNull DoorStatusType status) {
+ this();
+ setLocation(location);
+ setStatus(status);
+ }
+
+ /**
+ * Sets the location.
+ *
+ * @param location
+ */
+ public DoorStatus setLocation(@NonNull Grid location) {
+ setValue(KEY_LOCATION, location);
+ return this;
+ }
+
+ /**
+ * Gets the location.
+ *
+ * @return Grid
+ */
+ public Grid getLocation() {
+ return (Grid) getObject(Grid.class, KEY_LOCATION);
+ }
+
+ /**
+ * Sets the status.
+ *
+ * @param status
+ */
+ public DoorStatus setStatus(@NonNull DoorStatusType status) {
+ setValue(KEY_STATUS, status);
+ return this;
+ }
+
+ /**
+ * Gets the status.
+ *
+ * @return DoorStatusType
+ */
+ public DoorStatusType getStatus() {
+ return (DoorStatusType) getObject(DoorStatusType.class, KEY_STATUS);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/GateStatus.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/GateStatus.java
new file mode 100644
index 000000000..df000ecbc
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/GateStatus.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2017 - 2020, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc;
+
+import androidx.annotation.NonNull;
+
+import com.smartdevicelink.proxy.RPCStruct;
+import com.smartdevicelink.proxy.rpc.enums.DoorStatusType;
+
+import java.util.Hashtable;
+
+/**
+ * Describes the status of a parameter of trunk/hood/etc.
+ *
+ * <p><b>Parameter List</b></p>
+ *
+ * <table border="1" rules="all">
+ * <tr>
+ * <th>Param Name</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * <th>Required</th>
+ * <th>Notes</th>
+ * <th>Version Available</th>
+ * </tr>
+ * <tr>
+ * <td>location</td>
+ * <td>Grid</td>
+ * <td></td>
+ * <td>Y</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>status</td>
+ * <td>DoorStatusType</td>
+ * <td></td>
+ * <td>Y</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * </table>
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+public class GateStatus extends RPCStruct {
+ public static final String KEY_LOCATION = "location";
+ public static final String KEY_STATUS = "status";
+
+ /**
+ * Constructs a new GateStatus object
+ */
+ public GateStatus() { }
+
+ /**
+ * Constructs a new GateStatus object indicated by the Hashtable parameter
+ *
+ * @param hash The Hashtable to use
+ */
+ public GateStatus(Hashtable<String, Object> hash) {
+ super(hash);
+ }
+
+ /**
+ * Constructs a new GateStatus object
+ *
+ * @param location
+ * @param status
+ */
+ public GateStatus(@NonNull Grid location, @NonNull DoorStatusType status) {
+ this();
+ setLocation(location);
+ setStatus(status);
+ }
+
+ /**
+ * Sets the location.
+ *
+ * @param location
+ */
+ public GateStatus setLocation(@NonNull Grid location) {
+ setValue(KEY_LOCATION, location);
+ return this;
+ }
+
+ /**
+ * Gets the location.
+ *
+ * @return Grid
+ */
+ public Grid getLocation() {
+ return (Grid) getObject(Grid.class, KEY_LOCATION);
+ }
+
+ /**
+ * Sets the status.
+ *
+ * @param status
+ */
+ public GateStatus setStatus(@NonNull DoorStatusType status) {
+ setValue(KEY_STATUS, status);
+ return this;
+ }
+
+ /**
+ * Gets the status.
+ *
+ * @return DoorStatusType
+ */
+ public DoorStatusType getStatus() {
+ return (DoorStatusType) getObject(DoorStatusType.class, KEY_STATUS);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleData.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleData.java
index 37545d140..b723f08b9 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleData.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleData.java
@@ -108,10 +108,13 @@ import java.util.Hashtable;
* <tr>
* <td>externalTemperature</td>
* <td>Boolean</td>
- * <td>The external temperature in degrees celsius</td>
+ * <td>The external temperature in degrees celsius. This parameter is deprecated starting RPCSpec 7.1.0, please see climateData.</td>
* <td>N</td>
* <td>Subscribable</td>
- * <td>SmartDeviceLink 2.0</td>
+ * <td>
+ * @since SmartDeviceLink 2.0.0
+ * @property-deprecated in SmartDeviceLink 7.1.0
+ * </td>
* </tr>
* <tr>
* <td>vin</td>
@@ -269,6 +272,26 @@ import java.util.Hashtable;
* <td>N</td>
* <td>SmartDeviceLink 7.0.0</td>
* </tr>
+ * <tr>
+ * <td>climateData</td>
+ * <td>Boolean</td>
+ * <td>See ClimateData</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>seatOccupancy</td>
+ * <td>Boolean</td>
+ * <td>See SeatOccupancy</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
* </table>
*
*
@@ -293,6 +316,11 @@ import java.util.Hashtable;
public class GetVehicleData extends RPCRequest {
public static final String KEY_SPEED = "speed";
public static final String KEY_RPM = "rpm";
+ /**
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public static final String KEY_EXTERNAL_TEMPERATURE = "externalTemperature";
public static final String KEY_VIN = "vin";
public static final String KEY_PRNDL = "prndl";
@@ -334,8 +362,16 @@ public class GetVehicleData extends RPCRequest {
@Deprecated
public static final String KEY_FUEL_LEVEL_STATE = "fuelLevel_State";
public static final String KEY_STABILITY_CONTROLS_STATUS = "stabilityControlsStatus";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_CLIMATE_DATA = "climateData";
/**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_SEAT_OCCUPANCY = "seatOccupancy";
+ /**
* Constructs a new GetVehicleData object
*/
@@ -435,11 +471,29 @@ public class GetVehicleData extends RPCRequest {
return getBoolean(KEY_FUEL_RANGE);
}
+ /**
+ * Sets the externalTemperature.
+ *
+ * @param externalTemperature The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public GetVehicleData setExternalTemperature(Boolean externalTemperature) {
setParameters(KEY_EXTERNAL_TEMPERATURE, externalTemperature);
return this;
}
+ /**
+ * Gets the externalTemperature.
+ *
+ * @return Boolean The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public Boolean getExternalTemperature() {
return getBoolean(KEY_EXTERNAL_TEMPERATURE);
}
@@ -790,4 +844,46 @@ public class GetVehicleData extends RPCRequest {
public Boolean getStabilityControlsStatus() {
return getBoolean(KEY_STABILITY_CONTROLS_STATUS);
}
+
+ /**
+ * Sets the climateData.
+ *
+ * @param climateData See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public GetVehicleData setClimateData(Boolean climateData) {
+ setParameters(KEY_CLIMATE_DATA, climateData);
+ return this;
+ }
+
+ /**
+ * Gets the climateData.
+ *
+ * @return Boolean See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public Boolean getClimateData() {
+ return getBoolean(KEY_CLIMATE_DATA);
+ }
+
+ /**
+ * Sets the seatOccupancy.
+ *
+ * @param seatOccupancy See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public GetVehicleData setSeatOccupancy(Boolean seatOccupancy) {
+ setParameters(KEY_SEAT_OCCUPANCY, seatOccupancy);
+ return this;
+ }
+
+ /**
+ * Gets the seatOccupancy.
+ *
+ * @return Boolean See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public Boolean getSeatOccupancy() {
+ return getBoolean(KEY_SEAT_OCCUPANCY);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleDataResponse.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleDataResponse.java
index 1ce840819..33ebf324f 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleDataResponse.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/GetVehicleDataResponse.java
@@ -69,6 +69,11 @@ public class GetVehicleDataResponse extends RPCResponse {
public static final String KEY_FUEL_LEVEL_STATE = "fuelLevel_State";
public static final String KEY_INSTANT_FUEL_CONSUMPTION = "instantFuelConsumption";
public static final String KEY_FUEL_RANGE = "fuelRange";
+ /**
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public static final String KEY_EXTERNAL_TEMPERATURE = "externalTemperature";
public static final String KEY_TURN_SIGNAL = "turnSignal";
public static final String KEY_VIN = "vin";
@@ -96,8 +101,16 @@ public class GetVehicleDataResponse extends RPCResponse {
public static final String KEY_GEAR_STATUS = "gearStatus";
public static final String KEY_HANDS_OFF_STEERING = "handsOffSteering";
public static final String KEY_STABILITY_CONTROLS_STATUS = "stabilityControlsStatus";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_CLIMATE_DATA = "climateData";
/**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_SEAT_OCCUPANCY = "seatOccupancy";
+ /**
* Constructs a new GetVehicleDataResponse object
*/
@@ -206,11 +219,31 @@ public class GetVehicleDataResponse extends RPCResponse {
return SdlDataTypeConverter.objectToDouble(object);
}
+ /**
+ * Sets the externalTemperature.
+ *
+ * @param externalTemperature The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * {"num_min_value": -40.0, "num_max_value": 100.0}
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public GetVehicleDataResponse setExternalTemperature(Double externalTemperature) {
setParameters(KEY_EXTERNAL_TEMPERATURE, externalTemperature);
return this;
}
+ /**
+ * Gets the externalTemperature.
+ *
+ * @return Double The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * {"num_min_value": -40.0, "num_max_value": 100.0}
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public Double getExternalTemperature() {
Object object = getParameters(KEY_EXTERNAL_TEMPERATURE);
return SdlDataTypeConverter.objectToDouble(object);
@@ -594,4 +627,45 @@ public class GetVehicleDataResponse extends RPCResponse {
setParameters(KEY_STABILITY_CONTROLS_STATUS, stabilityControlsStatus);
return this;
}
+
+ /**
+ * Sets the climateData.
+ *
+ * @param climateData See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public GetVehicleDataResponse setClimateData(ClimateData climateData) {
+ setParameters(KEY_CLIMATE_DATA, climateData);
+ return this;
+ }
+
+ /**
+ * Gets the climateData.
+ *
+ * @return ClimateData See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public ClimateData getClimateData() {
+ return (ClimateData) getObject(ClimateData.class, KEY_CLIMATE_DATA);
+ }
+ /**
+ * Sets the seatOccupancy.
+ *
+ * @param seatOccupancy See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public GetVehicleDataResponse setSeatOccupancy(SeatOccupancy seatOccupancy) {
+ setParameters(KEY_SEAT_OCCUPANCY, seatOccupancy);
+ return this;
+ }
+
+ /**
+ * Gets the seatOccupancy.
+ *
+ * @return SeatOccupancy See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public SeatOccupancy getSeatOccupancy() {
+ return (SeatOccupancy) getObject(SeatOccupancy.class, KEY_SEAT_OCCUPANCY);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardCapabilities.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardCapabilities.java
new file mode 100644
index 000000000..bc2fb4301
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardCapabilities.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2017 - 2021, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc;
+
+import com.smartdevicelink.proxy.RPCStruct;
+
+import java.util.Hashtable;
+import java.util.List;
+
+/**
+ *
+ * <p><b>Parameter List</b></p>
+ *
+ * <table border="1" rules="all">
+ * <tr>
+ * <th>Param Name</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * <th>Required</th>
+ * <th>Notes</th>
+ * <th>Version Available</th>
+ * </tr>
+ * <tr>
+ * <td>maskInputCharactersSupported</td>
+ * <td>Boolean</td>
+ * <td>Availability of capability to mask input characters using keyboard. True: Available,False: Not Available</td>
+ * <td>N</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>supportedKeyboards</td>
+ * <td>List<KeyboardLayoutCapability></td>
+ * <td>Capabilities of supported keyboard layouts by HMI.</td>
+ * <td>N</td>
+ * <td>{"array_max_size": 1000, "array_min_size": 1}</td>
+ * <td></td>
+ * </tr>
+ * </table>
+ * @since SmartDeviceLink 7.1.0
+ */
+public class KeyboardCapabilities extends RPCStruct {
+
+ public static final String KEY_MASK_INPUT_CHARACTERS_SUPPORTED = "maskInputCharactersSupported";
+ public static final String KEY_SUPPORTED_KEYBOARDS = "supportedKeyboards";
+
+ /**
+ * Constructs a new KeyboardCapabilities object
+ */
+ public KeyboardCapabilities() { }
+
+ /**
+ * Constructs a new KeyboardCapabilities object indicated by the Hashtable parameter
+ *
+ * @param hash The Hashtable to use
+ */
+ public KeyboardCapabilities(Hashtable<String, Object> hash) {
+ super(hash);
+ }
+
+ /**
+ * Sets the maskInputCharactersSupported.
+ *
+ * @param maskInputCharactersSupported Availability of capability to mask input characters using keyboard. True: Available,
+ * False: Not Available
+ */
+ public KeyboardCapabilities setMaskInputCharactersSupported(Boolean maskInputCharactersSupported) {
+ setValue(KEY_MASK_INPUT_CHARACTERS_SUPPORTED, maskInputCharactersSupported);
+ return this;
+ }
+
+ /**
+ * Gets the maskInputCharactersSupported.
+ *
+ * @return Boolean Availability of capability to mask input characters using keyboard. True: Available,
+ * False: Not Available
+ */
+ public Boolean getMaskInputCharactersSupported() {
+ return getBoolean(KEY_MASK_INPUT_CHARACTERS_SUPPORTED);
+ }
+
+ /**
+ * Sets the supportedKeyboards.
+ *
+ * @param supportedKeyboards Capabilities of supported keyboard layouts by HMI.
+ * {"array_max_size": 1000, "array_min_size": 1}
+ */
+ public KeyboardCapabilities setSupportedKeyboards(List<KeyboardLayoutCapability> supportedKeyboards) {
+ setValue(KEY_SUPPORTED_KEYBOARDS, supportedKeyboards);
+ return this;
+ }
+
+ /**
+ * Gets the supportedKeyboards.
+ *
+ * @return List<KeyboardLayoutCapability> Capabilities of supported keyboard layouts by HMI.
+ * {"array_max_size": 1000, "array_min_size": 1}
+ */
+ @SuppressWarnings("unchecked")
+ public List<KeyboardLayoutCapability> getSupportedKeyboards() {
+ return (List<KeyboardLayoutCapability>) getObject(KeyboardLayoutCapability.class, KEY_SUPPORTED_KEYBOARDS);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardLayoutCapability.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardLayoutCapability.java
new file mode 100644
index 000000000..cf3f13b9c
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardLayoutCapability.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2017 - 2021, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc;
+
+import androidx.annotation.NonNull;
+
+import com.smartdevicelink.proxy.RPCStruct;
+import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout;
+
+import java.util.Hashtable;
+
+/**
+ * Describes the capabilities of a single keyboard layout.
+ *
+ * <p><b>Parameter List</b></p>
+ *
+ * <table border="1" rules="all">
+ * <tr>
+ * <th>Param Name</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * <th>Required</th>
+ * <th>Notes</th>
+ * <th>Version Available</th>
+ * </tr>
+ * <tr>
+ * <td>keyboardLayout</td>
+ * <td>KeyboardLayout</td>
+ * <td></td>
+ * <td>Y</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>numConfigurableKeys</td>
+ * <td>Integer</td>
+ * <td>Number of keys available for special characters, App can customize as per their needs.</td>
+ * <td>Y</td>
+ * <td>{"num_max_value": 10, "num_min_value": 0}</td>
+ * <td></td>
+ * </tr>
+ * </table>
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+public class KeyboardLayoutCapability extends RPCStruct {
+ public static final String KEY_KEYBOARD_LAYOUT = "keyboardLayout";
+ public static final String KEY_NUM_CONFIGURABLE_KEYS = "numConfigurableKeys";
+
+ /**
+ * Constructs a new KeyboardLayoutCapability object
+ */
+ public KeyboardLayoutCapability() { }
+
+ /**
+ * Constructs a new KeyboardLayoutCapability object indicated by the Hashtable parameter
+ *
+ * @param hash The Hashtable to use
+ */
+ public KeyboardLayoutCapability(Hashtable<String, Object> hash) {
+ super(hash);
+ }
+
+ /**
+ * Constructs a new KeyboardLayoutCapability object
+ *
+ * @param keyboardLayout
+ * @param numConfigurableKeys Number of keys available for special characters, App can customize as per their needs.
+ * {"num_max_value": 10, "num_min_value": 0}
+ */
+ public KeyboardLayoutCapability(@NonNull KeyboardLayout keyboardLayout, @NonNull Integer numConfigurableKeys) {
+ this();
+ setKeyboardLayout(keyboardLayout);
+ setNumConfigurableKeys(numConfigurableKeys);
+ }
+
+ /**
+ * Sets the keyboardLayout.
+ *
+ * @param keyboardLayout
+ */
+ public KeyboardLayoutCapability setKeyboardLayout(@NonNull KeyboardLayout keyboardLayout) {
+ setValue(KEY_KEYBOARD_LAYOUT, keyboardLayout);
+ return this;
+ }
+
+ /**
+ * Gets the keyboardLayout.
+ *
+ * @return KeyboardLayout
+ */
+ public KeyboardLayout getKeyboardLayout() {
+ return (KeyboardLayout) getObject(KeyboardLayout.class, KEY_KEYBOARD_LAYOUT);
+ }
+
+ /**
+ * Sets the numConfigurableKeys.
+ *
+ * @param numConfigurableKeys Number of keys available for special characters, App can customize as per their needs.
+ * {"num_max_value": 10, "num_min_value": 0}
+ */
+ public KeyboardLayoutCapability setNumConfigurableKeys(@NonNull Integer numConfigurableKeys) {
+ setValue(KEY_NUM_CONFIGURABLE_KEYS, numConfigurableKeys);
+ return this;
+ }
+
+ /**
+ * Gets the numConfigurableKeys.
+ *
+ * @return Integer Number of keys available for special characters, App can customize as per their needs.
+ * {"num_max_value": 10, "num_min_value": 0}
+ */
+ public Integer getNumConfigurableKeys() {
+ return getInteger(KEY_NUM_CONFIGURABLE_KEYS);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardProperties.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardProperties.java
index cfd9b0dae..a59623720 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardProperties.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/KeyboardProperties.java
@@ -32,6 +32,7 @@
package com.smartdevicelink.proxy.rpc;
import com.smartdevicelink.proxy.RPCStruct;
+import com.smartdevicelink.proxy.rpc.enums.KeyboardInputMask;
import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout;
import com.smartdevicelink.proxy.rpc.enums.KeypressMode;
import com.smartdevicelink.proxy.rpc.enums.Language;
@@ -101,6 +102,27 @@ import java.util.List;
* <td>Array = true maxlength = 1000 minsize = 0 maxsize = 100</td>
* <td>Allows an app to prepopulate the text field with a list of suggested or completed entry as the user types. Set to an empty array to remove the auto-complete list from the screen</td>
* </tr>
+ * <tr>
+ * <td>maskInputCharacters</td>
+ * <td>KeyboardInputMask</td>
+ * <td>false</td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * <td>Allows an app to mask entered characters on HMI</td>
+ * </tr>
+ * <tr>
+ * <td>customKeys</td>
+ * <td>List<String></td>
+ * <td>false</td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * <td>
+ * Array of special characters to show in customizable keys. If omitted, keyboard will show default special characters
+ * {"string_max_length": 1, "string_min_length": 1, "array_max_size": 10, "array_min_size": 1}
+ * </td>
+ * </tr>
* </table>
*
* @since SmartDeviceLink 3.0
@@ -113,6 +135,8 @@ public class KeyboardProperties extends RPCStruct {
@Deprecated
public static final String KEY_AUTO_COMPLETE_TEXT = "autoCompleteText";
public static final String KEY_AUTO_COMPLETE_LIST = "autoCompleteList";
+ public static final String KEY_MASK_INPUT_CHARACTERS = "maskInputCharacters";
+ public static final String KEY_CUSTOM_KEYS = "customKeys";
public static final String KEY_LANGUAGE = "language";
private static final KeypressMode KEYPRESS_MODE_DEFAULT = KeypressMode.RESEND_CURRENT_ENTRY;
@@ -201,6 +225,7 @@ public class KeyboardProperties extends RPCStruct {
*
* @return List<String> representing the suggestions list
*/
+ @SuppressWarnings("unchecked")
public List<String> getAutoCompleteList() {
return (List<String>) getObject(String.class, KEY_AUTO_COMPLETE_LIST);
}
@@ -215,4 +240,51 @@ public class KeyboardProperties extends RPCStruct {
setValue(KEY_AUTO_COMPLETE_LIST, autoCompleteList);
return this;
}
+
+ /**
+ * Sets the maskInputCharacters.
+ *
+ * @param maskInputCharacters Allows an app to mask entered characters on HMI
+ * @since SmartDeviceLink 7.1.0
+ */
+ public KeyboardProperties setMaskInputCharacters(KeyboardInputMask maskInputCharacters) {
+ setValue(KEY_MASK_INPUT_CHARACTERS, maskInputCharacters);
+ return this;
+ }
+
+ /**
+ * Gets the maskInputCharacters.
+ *
+ * @return KeyboardInputMask Allows an app to mask entered characters on HMI
+ * @since SmartDeviceLink 7.1.0
+ */
+ public KeyboardInputMask getMaskInputCharacters() {
+ return (KeyboardInputMask) getObject(KeyboardInputMask.class, KEY_MASK_INPUT_CHARACTERS);
+ }
+
+ /**
+ * Sets the customKeys.
+ *
+ * @param customKeys Array of special characters to show in customizable keys. If omitted, keyboard will show
+ * default special characters
+ * {"string_max_length": 1, "string_min_length": 1, "array_max_size": 10, "array_min_size": 1}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public KeyboardProperties setCustomKeys(List<String> customKeys) {
+ setValue(KEY_CUSTOM_KEYS, customKeys);
+ return this;
+ }
+
+ /**
+ * Gets the customKeys.
+ *
+ * @return List<String> Array of special characters to show in customizable keys. If omitted, keyboard will show
+ * default special characters
+ * {"string_max_length": 1, "string_min_length": 1, "array_max_size": 10, "array_min_size": 1}
+ * @since SmartDeviceLink 7.1.0
+ */
+ @SuppressWarnings("unchecked")
+ public List<String> getCustomKeys() {
+ return (List<String>) getObject(String.class, KEY_CUSTOM_KEYS);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/MenuParams.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/MenuParams.java
index 9c6635427..4801930f7 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/MenuParams.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/MenuParams.java
@@ -84,6 +84,26 @@ import java.util.Hashtable;
* </td>
* <td>SmartDeviceLink 1.0</td>
* </tr>
+ * <tr>
+ * <td>secondaryText</td>
+ * <td>String</td>
+ * <td>Optional secondary text to display</td>
+ * <td>N</td>
+ * <td>{"string_min_length": 1, "string_max_length": 500}</td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>tertiaryText</td>
+ * <td>String</td>
+ * <td>Optional tertiary text to display</td>
+ * <td>N</td>
+ * <td>{"string_min_length": 1, "string_max_length": 500}</td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
* </table>
*
* @see AddCommand
@@ -94,6 +114,14 @@ public class MenuParams extends RPCStruct {
public static final String KEY_PARENT_ID = "parentID";
public static final String KEY_POSITION = "position";
public static final String KEY_MENU_NAME = "menuName";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_SECONDARY_TEXT = "secondaryText";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_TERTIARY_TEXT = "tertiaryText";
/**
* Constructs a newly allocated MenuParams object
@@ -202,4 +230,51 @@ public class MenuParams extends RPCStruct {
setValue(KEY_MENU_NAME, menuName);
return this;
}
+
+
+ /**
+ * Sets the secondaryText.
+ *
+ * @param secondaryText Optional secondary text to display
+ * {"string_min_length": 1, "string_max_length": 500}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public MenuParams setSecondaryText(String secondaryText) {
+ setValue(KEY_SECONDARY_TEXT, secondaryText);
+ return this;
+ }
+
+ /**
+ * Gets the secondaryText.
+ *
+ * @return String Optional secondary text to display
+ * {"string_min_length": 1, "string_max_length": 500}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public String getSecondaryText() {
+ return getString(KEY_SECONDARY_TEXT);
+ }
+
+ /**
+ * Sets the tertiaryText.
+ *
+ * @param tertiaryText Optional tertiary text to display
+ * {"string_min_length": 1, "string_max_length": 500}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public MenuParams setTertiaryText(String tertiaryText) {
+ setValue(KEY_TERTIARY_TEXT, tertiaryText);
+ return this;
+ }
+
+ /**
+ * Gets the tertiaryText.
+ *
+ * @return String Optional tertiary text to display
+ * {"string_min_length": 1, "string_max_length": 500}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public String getTertiaryText() {
+ return getString(KEY_TERTIARY_TEXT);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/OnAppCapabilityUpdated.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/OnAppCapabilityUpdated.java
new file mode 100644
index 000000000..00ff6509c
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/OnAppCapabilityUpdated.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2017 - 2021, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc;
+
+import androidx.annotation.NonNull;
+
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.RPCNotification;
+
+import java.util.Hashtable;
+
+/**
+ * A notification to inform SDL Core that a specific app capability has changed.
+ *
+ * <p><b>Parameter List</b></p>
+ *
+ * <table border="1" rules="all">
+ * <tr>
+ * <th>Param Name</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * <th>Required</th>
+ * <th>Notes</th>
+ * <th>Version Available</th>
+ * </tr>
+ * <tr>
+ * <td>appCapability</td>
+ * <td>AppCapability</td>
+ * <td>The app capability that has been updated</td>
+ * <td>Y</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * </table>
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+public class OnAppCapabilityUpdated extends RPCNotification {
+ public static final String KEY_APP_CAPABILITY = "appCapability";
+
+ /**
+ * Constructs a new OnAppCapabilityUpdated object
+ */
+ public OnAppCapabilityUpdated() {
+ super(FunctionID.ON_APP_CAPABILITY_UPDATED.toString());
+ }
+
+ /**
+ * Constructs a new OnAppCapabilityUpdated object indicated by the Hashtable parameter
+ *
+ * @param hash The Hashtable to use
+ */
+ public OnAppCapabilityUpdated(Hashtable<String, Object> hash) {
+ super(hash);
+ }
+
+ /**
+ * Constructs a new OnAppCapabilityUpdated object
+ *
+ * @param appCapability The app capability that has been updated
+ */
+ public OnAppCapabilityUpdated(@NonNull AppCapability appCapability) {
+ this();
+ setAppCapability(appCapability);
+ }
+
+ /**
+ * Sets the appCapability.
+ *
+ * @param appCapability The app capability that has been updated
+ */
+ public OnAppCapabilityUpdated setAppCapability(@NonNull AppCapability appCapability) {
+ setParameters(KEY_APP_CAPABILITY, appCapability);
+ return this;
+ }
+
+ /**
+ * Gets the appCapability.
+ *
+ * @return AppCapability The app capability that has been updated
+ */
+ public AppCapability getAppCapability() {
+ return (AppCapability) getObject(AppCapability.class, KEY_APP_CAPABILITY);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/OnVehicleData.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/OnVehicleData.java
index e8f5b57ca..8d87f6d30 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/OnVehicleData.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/OnVehicleData.java
@@ -320,12 +320,31 @@ import java.util.List;
* <td>SmartDeviceLink 7.0.0</td>
* </tr>
* <tr>
+ * <td>climateData</td>
+ * <td>ClimateData</td>
+ * <td>See ClimateData</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
+ * <tr>
* <td>stabilityControlsStatus</td>
* <td>StabilityControlsStatus</td>
* <td>See StabilityControlsStatus</td>
* <td>N</td>
* <td>SmartDeviceLink 7.0.0</td>
* </tr>
+ * <tr>
+ * <td>seatOccupancy</td>
+ * <td>SeatOccupancy</td>
+ * <td>See SeatOccupancy</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
* </table>
*
* @see SubscribeVehicleData
@@ -335,6 +354,11 @@ import java.util.List;
public class OnVehicleData extends RPCNotification {
public static final String KEY_SPEED = "speed";
public static final String KEY_RPM = "rpm";
+ /**
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public static final String KEY_EXTERNAL_TEMPERATURE = "externalTemperature";
public static final String KEY_VIN = "vin";
public static final String KEY_PRNDL = "prndl";
@@ -365,6 +389,10 @@ public class OnVehicleData extends RPCNotification {
public static final String KEY_WINDOW_STATUS = "windowStatus";
public static final String KEY_GEAR_STATUS = "gearStatus";
/**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_CLIMATE_DATA = "climateData";
+ /**
* @deprecated
*/
@Deprecated
@@ -377,6 +405,10 @@ public class OnVehicleData extends RPCNotification {
public static final String KEY_STABILITY_CONTROLS_STATUS = "stabilityControlsStatus";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_SEAT_OCCUPANCY = "seatOccupancy";
public OnVehicleData() {
super(FunctionID.ON_VEHICLE_DATA.toString());
}
@@ -458,11 +490,31 @@ public class OnVehicleData extends RPCNotification {
return SdlDataTypeConverter.objectToDouble(object);
}
+ /**
+ * Sets the externalTemperature.
+ *
+ * @param externalTemperature The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * {"num_min_value": -40.0, "num_max_value": 100.0}
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public OnVehicleData setExternalTemperature(Double externalTemperature) {
setParameters(KEY_EXTERNAL_TEMPERATURE, externalTemperature);
return this;
}
+ /**
+ * Gets the externalTemperature.
+ *
+ * @return Double The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * {"num_min_value": -40.0, "num_max_value": 100.0}
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public Double getExternalTemperature() {
Object object = getParameters(KEY_EXTERNAL_TEMPERATURE);
return SdlDataTypeConverter.objectToDouble(object);
@@ -849,4 +901,46 @@ public class OnVehicleData extends RPCNotification {
public GearStatus getGearStatus() {
return (GearStatus) getObject(GearStatus.class, KEY_GEAR_STATUS);
}
+
+
+ /**
+ * Sets the climateData.
+ *
+ * @param climateData See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public OnVehicleData setClimateData(ClimateData climateData) {
+ setParameters(KEY_CLIMATE_DATA, climateData);
+ return this;
+ }
+
+ /**
+ * Gets the climateData.
+ *
+ * @return ClimateData See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public ClimateData getClimateData() {
+ return (ClimateData) getObject(ClimateData.class, KEY_CLIMATE_DATA);
+ }
+ /**
+ * Sets the seatOccupancy.
+ *
+ * @param seatOccupancy See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public OnVehicleData setSeatOccupancy(SeatOccupancy seatOccupancy) {
+ setParameters(KEY_SEAT_OCCUPANCY, seatOccupancy);
+ return this;
+ }
+
+ /**
+ * Gets the seatOccupancy.
+ *
+ * @return SeatOccupancy See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public SeatOccupancy getSeatOccupancy() {
+ return (SeatOccupancy) getObject(SeatOccupancy.class, KEY_SEAT_OCCUPANCY);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/RegisterAppInterface.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/RegisterAppInterface.java
index 206b74f79..311095cea 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/RegisterAppInterface.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/RegisterAppInterface.java
@@ -200,6 +200,14 @@ import java.util.List;
* <td>SmartDeviceLink 2.3.1 </td>
* </tr>
* <tr>
+ * <td>appInfo</td>
+ * <td>AppInfo</td>
+ * <td>See AppInfo.</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>@since SmartDeviceLink 4.2.0 </td>
+ * </tr>
+ * <tr>
* <td>deviceInfo</td>
* <td>DeviceInfo</td>
* <td>Various information about connecting device.</td>
@@ -288,6 +296,9 @@ public class RegisterAppInterface extends RPCRequest {
public static final String KEY_VR_SYNONYMS = "vrSynonyms";
public static final String KEY_SDL_MSG_VERSION = "syncMsgVersion";
public static final String KEY_HASH_ID = "hashID";
+ /**
+ * @since SmartDeviceLink 4.2.0
+ */
public static final String KEY_APP_INFO = "appInfo";
public static final String KEY_DAY_COLOR_SCHEME = "dayColorScheme";
public static final String KEY_NIGHT_COLOR_SCHEME = "nightColorScheme";
@@ -631,18 +642,20 @@ public class RegisterAppInterface extends RPCRequest {
}
/**
- * Gets the detailed information about the registered application
+ * Gets the appInfo.
*
- * @return appInfo - detailed information about the registered application
+ * @return AppInfo See AppInfo.
+ * @since SmartDeviceLink 4.2.0
*/
public AppInfo getAppInfo() {
return (AppInfo) getObject(AppInfo.class, KEY_APP_INFO);
}
/**
- * Sets detailed information about the registered application
+ * Sets the appInfo.
*
- * @param appInfo - detailed information about the registered application
+ * @param appInfo See AppInfo.
+ * @since SmartDeviceLink 4.2.0
*/
public RegisterAppInterface setAppInfo(AppInfo appInfo) {
setParameters(KEY_APP_INFO, appInfo);
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/RegisterAppInterfaceResponse.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/RegisterAppInterfaceResponse.java
index c18cbd38b..d4ae5a706 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/RegisterAppInterfaceResponse.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/RegisterAppInterfaceResponse.java
@@ -53,6 +53,7 @@ import java.util.List;
* @since SmartDeviceLink 1.0
*/
public class RegisterAppInterfaceResponse extends RPCResponse {
+ @Deprecated
public static final String KEY_VEHICLE_TYPE = "vehicleType";
public static final String KEY_SPEECH_CAPABILITIES = "speechCapabilities";
public static final String KEY_VR_CAPABILITIES = "vrCapabilities";
@@ -73,6 +74,7 @@ public class RegisterAppInterfaceResponse extends RPCResponse {
public static final String KEY_PRESET_BANK_CAPABILITIES = "presetBankCapabilities";
public static final String KEY_HMI_CAPABILITIES = "hmiCapabilities"; //As of v4.0
public static final String KEY_SDL_VERSION = "sdlVersion"; //As of v4.0
+ @Deprecated
public static final String KEY_SYSTEM_SOFTWARE_VERSION = "systemSoftwareVersion"; //As of v4.0
public static final String KEY_ICON_RESUMED = "iconResumed";
public static final String KEY_PCM_STREAM_CAPABILITIES = "pcmStreamCapabilities";
@@ -382,18 +384,20 @@ public class RegisterAppInterfaceResponse extends RPCResponse {
/**
* Gets getVehicleType set when application interface is registered.
- *
+ * @deprecated in SmartDeviceLink 7.1.0
* @return vehicleType
*/
+ @Deprecated
public VehicleType getVehicleType() {
return (VehicleType) getObject(VehicleType.class, KEY_VEHICLE_TYPE);
}
/**
* Sets vehicleType
- *
+ * @deprecated in SmartDeviceLink 7.1.0
* @param vehicleType
*/
+ @Deprecated
public RegisterAppInterfaceResponse setVehicleType(VehicleType vehicleType) {
setParameters(KEY_VEHICLE_TYPE, vehicleType);
return this;
@@ -466,11 +470,24 @@ public class RegisterAppInterfaceResponse extends RPCResponse {
return getString(KEY_SDL_VERSION);
}
+ /**
+ * The software version of the connected device.
+ * @param systemSoftwareVersion the version of software on the connected device
+ * @return RegisterAppInterface
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public RegisterAppInterfaceResponse setSystemSoftwareVersion(String systemSoftwareVersion) {
setParameters(KEY_SYSTEM_SOFTWARE_VERSION, systemSoftwareVersion);
return this;
}
+ /**
+ * The software version of the connected device.
+ * @return String the version of software on the connected device
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public String getSystemSoftwareVersion() {
return getString(KEY_SYSTEM_SOFTWARE_VERSION);
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/RoofStatus.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/RoofStatus.java
new file mode 100644
index 000000000..1bc28d7cf
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/RoofStatus.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2017 - 2020, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc;
+
+import androidx.annotation.NonNull;
+
+import com.smartdevicelink.proxy.RPCStruct;
+import com.smartdevicelink.proxy.rpc.enums.DoorStatusType;
+
+import java.util.Hashtable;
+
+/**
+ * Describes the status of a parameter of roof/convertible roof/sunroof/moonroof etc. If roof
+ * is open (AJAR), state will determine percentage of roof open.
+ *
+ * <p><b>Parameter List</b></p>
+ *
+ * <table border="1" rules="all">
+ * <tr>
+ * <th>Param Name</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * <th>Required</th>
+ * <th>Notes</th>
+ * <th>Version Available</th>
+ * </tr>
+ * <tr>
+ * <td>location</td>
+ * <td>Grid</td>
+ * <td></td>
+ * <td>Y</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>status</td>
+ * <td>DoorStatusType</td>
+ * <td></td>
+ * <td>Y</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>state</td>
+ * <td>WindowState</td>
+ * <td></td>
+ * <td>N</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * </table>
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+public class RoofStatus extends RPCStruct {
+ public static final String KEY_LOCATION = "location";
+ public static final String KEY_STATUS = "status";
+ public static final String KEY_STATE = "state";
+
+ /**
+ * Constructs a new RoofStatus object
+ */
+ public RoofStatus() { }
+
+ /**
+ * Constructs a new RoofStatus object indicated by the Hashtable parameter
+ *
+ * @param hash The Hashtable to use
+ */
+ public RoofStatus(Hashtable<String, Object> hash) {
+ super(hash);
+ }
+
+ /**
+ * Constructs a new RoofStatus object
+ *
+ * @param location
+ * @param status
+ */
+ public RoofStatus(@NonNull Grid location, @NonNull DoorStatusType status) {
+ this();
+ setLocation(location);
+ setStatus(status);
+ }
+
+ /**
+ * Sets the location.
+ *
+ * @param location
+ */
+ public RoofStatus setLocation(@NonNull Grid location) {
+ setValue(KEY_LOCATION, location);
+ return this;
+ }
+
+ /**
+ * Gets the location.
+ *
+ * @return Grid
+ */
+ public Grid getLocation() {
+ return (Grid) getObject(Grid.class, KEY_LOCATION);
+ }
+
+ /**
+ * Sets the status.
+ *
+ * @param status
+ */
+ public RoofStatus setStatus(@NonNull DoorStatusType status) {
+ setValue(KEY_STATUS, status);
+ return this;
+ }
+
+ /**
+ * Gets the status.
+ *
+ * @return DoorStatusType
+ */
+ public DoorStatusType getStatus() {
+ return (DoorStatusType) getObject(DoorStatusType.class, KEY_STATUS);
+ }
+
+ /**
+ * Sets the state.
+ *
+ * @param state
+ */
+ public RoofStatus setState(WindowState state) {
+ setValue(KEY_STATE, state);
+ return this;
+ }
+
+ /**
+ * Gets the state.
+ *
+ * @return WindowState
+ */
+ public WindowState getState() {
+ return (WindowState) getObject(WindowState.class, KEY_STATE);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatOccupancy.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatOccupancy.java
new file mode 100644
index 000000000..c54ae0ef0
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatOccupancy.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2017 - 2020, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc;
+
+import com.smartdevicelink.proxy.RPCStruct;
+
+import java.util.Hashtable;
+import java.util.List;
+
+/**
+ *
+ * <p><b>Parameter List</b></p>
+ *
+ * <table border="1" rules="all">
+ * <tr>
+ * <th>Param Name</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * <th>Required</th>
+ * <th>Notes</th>
+ * <th>Version Available</th>
+ * </tr>
+ * <tr>
+ * <td>seatsOccupied</td>
+ * <td>List<SeatStatus></td>
+ * <td>Seat status array containing location and whether the seats are occupied.</td>
+ * <td>N</td>
+ * <td>{"array_min_size": 0, "array_max_size": 100}</td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>seatsBelted</td>
+ * <td>List<SeatStatus></td>
+ * <td>Seat status array containing location and whether the seats are belted.</td>
+ * <td>N</td>
+ * <td>{"array_min_size": 0, "array_max_size": 100}</td>
+ * <td></td>
+ * </tr>
+ * </table>
+ * @since SmartDeviceLink 7.1.0
+ */
+public class SeatOccupancy extends RPCStruct {
+ public static final String KEY_SEATS_OCCUPIED = "seatsOccupied";
+ public static final String KEY_SEATS_BELTED = "seatsBelted";
+
+ /**
+ * Constructs a new SeatOccupancy object
+ */
+ public SeatOccupancy() { }
+
+ /**
+ * Constructs a new SeatOccupancy object indicated by the Hashtable parameter
+ *
+ * @param hash The Hashtable to use
+ */
+ public SeatOccupancy(Hashtable<String, Object> hash) {
+ super(hash);
+ }
+
+ /**
+ * Sets the seatsOccupied.
+ *
+ * @param seatsOccupied Seat status array containing location and whether the seats are occupied.
+ * {"array_min_size": 0, "array_max_size": 100}
+ */
+ public SeatOccupancy setSeatsOccupied(List<SeatStatus> seatsOccupied) {
+ setValue(KEY_SEATS_OCCUPIED, seatsOccupied);
+ return this;
+ }
+
+ /**
+ * Gets the seatsOccupied.
+ *
+ * @return List<SeatStatus> Seat status array containing location and whether the seats are occupied.
+ * {"array_min_size": 0, "array_max_size": 100}
+ */
+ @SuppressWarnings("unchecked")
+ public List<SeatStatus> getSeatsOccupied() {
+ return (List<SeatStatus>) getObject(SeatStatus.class, KEY_SEATS_OCCUPIED);
+ }
+
+ /**
+ * Sets the seatsBelted.
+ *
+ * @param seatsBelted Seat status array containing location and whether the seats are belted.
+ * {"array_min_size": 0, "array_max_size": 100}
+ */
+ public SeatOccupancy setSeatsBelted(List<SeatStatus> seatsBelted) {
+ setValue(KEY_SEATS_BELTED, seatsBelted);
+ return this;
+ }
+
+ /**
+ * Gets the seatsBelted.
+ *
+ * @return List<SeatStatus> Seat status array containing location and whether the seats are belted.
+ * {"array_min_size": 0, "array_max_size": 100}
+ */
+ @SuppressWarnings("unchecked")
+ public List<SeatStatus> getSeatsBelted() {
+ return (List<SeatStatus>) getObject(SeatStatus.class, KEY_SEATS_BELTED);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatStatus.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatStatus.java
new file mode 100644
index 000000000..65f60c8ff
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatStatus.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2017 - 2020, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc;
+
+import androidx.annotation.NonNull;
+
+import com.smartdevicelink.proxy.RPCStruct;
+
+import java.util.Hashtable;
+
+/**
+ * Describes the status of a parameter of seat.
+ *
+ * <p><b>Parameter List</b></p>
+ *
+ * <table border="1" rules="all">
+ * <tr>
+ * <th>Param Name</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * <th>Required</th>
+ * <th>Notes</th>
+ * <th>Version Available</th>
+ * </tr>
+ * <tr>
+ * <td>seatLocation</td>
+ * <td>SeatLocation</td>
+ * <td></td>
+ * <td>Y</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>conditionActive</td>
+ * <td>Boolean</td>
+ * <td></td>
+ * <td>Y</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * </table>
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+public class SeatStatus extends RPCStruct {
+ public static final String KEY_SEAT_LOCATION = "seatLocation";
+ public static final String KEY_CONDITION_ACTIVE = "conditionActive";
+
+ /**
+ * Constructs a new SeatStatus object
+ */
+ public SeatStatus() { }
+
+ /**
+ * Constructs a new SeatStatus object indicated by the Hashtable parameter
+ *
+ * @param hash The Hashtable to use
+ */
+ public SeatStatus(Hashtable<String, Object> hash) {
+ super(hash);
+ }
+
+ /**
+ * Constructs a new SeatStatus object
+ *
+ * @param seatLocation
+ * @param conditionActive
+ */
+ public SeatStatus(@NonNull SeatLocation seatLocation, @NonNull Boolean conditionActive) {
+ this();
+ setSeatLocation(seatLocation);
+ setConditionActive(conditionActive);
+ }
+
+ /**
+ * Sets the seatLocation.
+ *
+ * @param seatLocation
+ */
+ public SeatStatus setSeatLocation(@NonNull SeatLocation seatLocation) {
+ setValue(KEY_SEAT_LOCATION, seatLocation);
+ return this;
+ }
+
+ /**
+ * Gets the seatLocation.
+ *
+ * @return SeatLocation
+ */
+ public SeatLocation getSeatLocation() {
+ return (SeatLocation) getObject(SeatLocation.class, KEY_SEAT_LOCATION);
+ }
+
+ /**
+ * Sets the conditionActive.
+ *
+ * @param conditionActive
+ */
+ public SeatStatus setConditionActive(@NonNull Boolean conditionActive) {
+ setValue(KEY_CONDITION_ACTIVE, conditionActive);
+ return this;
+ }
+
+ /**
+ * Gets the conditionActive.
+ *
+ * @return Boolean
+ */
+ public Boolean getConditionActive() {
+ return getBoolean(KEY_CONDITION_ACTIVE);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/SeekStreamingIndicator.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/SeekStreamingIndicator.java
new file mode 100644
index 000000000..576543a07
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/SeekStreamingIndicator.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2017 - 2020, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc;
+
+import androidx.annotation.NonNull;
+
+import com.smartdevicelink.proxy.RPCStruct;
+import com.smartdevicelink.proxy.rpc.enums.SeekIndicatorType;
+
+import java.util.Hashtable;
+
+/**
+ * The seek next / skip previous subscription buttons' content
+ *
+ * <p><b>Parameter List</b></p>
+ *
+ * <table border="1" rules="all">
+ * <tr>
+ * <th>Param Name</th>
+ * <th>Type</th>
+ * <th>Description</th>
+ * <th>Required</th>
+ * <th>Notes</th>
+ * <th>Version Available</th>
+ * </tr>
+ * <tr>
+ * <td>type</td>
+ * <td>SeekIndicatorType</td>
+ * <td></td>
+ * <td>Y</td>
+ * <td></td>
+ * <td></td>
+ * </tr>
+ * <tr>
+ * <td>seekTime</td>
+ * <td>Integer</td>
+ * <td>If the type is TIME, this number of seconds may be present alongside the skip indicator.It will indicate the number of seconds that the currently playing media will skip forward or backward.</td>
+ * <td>N</td>
+ * <td>{"num_min_value": 1, "num_max_value": 99}</td>
+ * <td></td>
+ * </tr>
+ * </table>
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+public class SeekStreamingIndicator extends RPCStruct {
+ public static final String KEY_TYPE = "type";
+ public static final String KEY_SEEK_TIME = "seekTime";
+
+ /**
+ * Constructs a new SeekStreamingIndicator object
+ */
+ public SeekStreamingIndicator() { }
+
+ /**
+ * Constructs a new SeekStreamingIndicator object indicated by the Hashtable parameter
+ *
+ * @param hash The Hashtable to use
+ */
+ public SeekStreamingIndicator(Hashtable<String, Object> hash) {
+ super(hash);
+ }
+
+ /**
+ * Constructs a new SeekStreamingIndicator object
+ *
+ * @param type
+ */
+ public SeekStreamingIndicator(@NonNull SeekIndicatorType type) {
+ this();
+ setType(type);
+ }
+
+ /**
+ * Sets the type.
+ *
+ * @param type
+ */
+ public SeekStreamingIndicator setType(@NonNull SeekIndicatorType type) {
+ setValue(KEY_TYPE, type);
+ return this;
+ }
+
+ /**
+ * Gets the type.
+ *
+ * @return SeekIndicatorType
+ */
+ public SeekIndicatorType getType() {
+ return (SeekIndicatorType) getObject(SeekIndicatorType.class, KEY_TYPE);
+ }
+
+ /**
+ * Sets the seekTime.
+ *
+ * @param seekTime If the type is TIME, this number of seconds may be present alongside the skip indicator.
+ * It will indicate the number of seconds that the currently playing media will skip forward
+ * or backward.
+ * {"num_min_value": 1, "num_max_value": 99}
+ */
+ public SeekStreamingIndicator setSeekTime(Integer seekTime) {
+ setValue(KEY_SEEK_TIME, seekTime);
+ return this;
+ }
+
+ /**
+ * Gets the seekTime.
+ *
+ * @return Integer If the type is TIME, this number of seconds may be present alongside the skip indicator.
+ * It will indicate the number of seconds that the currently playing media will skip forward
+ * or backward.
+ * {"num_min_value": 1, "num_max_value": 99}
+ */
+ public Integer getSeekTime() {
+ return getInteger(KEY_SEEK_TIME);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/SetMediaClockTimer.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/SetMediaClockTimer.java
index cba5d0445..60b9c524f 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/SetMediaClockTimer.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/SetMediaClockTimer.java
@@ -38,6 +38,7 @@ import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCRequest;
import com.smartdevicelink.proxy.rpc.enums.AudioStreamingIndicator;
import com.smartdevicelink.proxy.rpc.enums.UpdateMode;
+import com.smartdevicelink.util.SdlDataTypeConverter;
import java.util.Hashtable;
@@ -94,6 +95,37 @@ import java.util.Hashtable;
* <td></td>
* <td>SmartDeviceLink 5.0</td>
* </tr>
+ * <tr>
+ * <td>countRate</td>
+ * <td>Float</td>
+ * <td>The value of this parameter is the amount that the media clock timer will advance per 1.0 seconds of real time. Values less than 1.0 will therefore advance the timer slower than real-time, while values greater than 1.0 will advance the timer faster than real-time.e.g. If this parameter is set to `0.5`, the timer will advance one second per two seconds real-time, or at 50% speed. If this parameter is set to `2.0`, the timer will advance two seconds per one second real-time, or at 200% speed.</td>
+
+ * <td>N</td>
+ * <td>{"num_min_value": 0.1, "num_max_value": 100.0}</td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>forwardSeekIndicator</td>
+ * <td>SeekStreamingIndicator</td>
+ * <td>Used to control the forward seek button to either skip forward a set amount of time or to the next track.</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>backSeekIndicator</td>
+ * <td>SeekStreamingIndicator</td>
+ * <td>Used to control the forward seek button to either skip back a set amount of time or to the previous track.</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
*
* </table>
*
@@ -117,6 +149,9 @@ public class SetMediaClockTimer extends RPCRequest {
public static final String KEY_END_TIME = "endTime";
public static final String KEY_UPDATE_MODE = "updateMode";
public static final String KEY_AUDIO_STREAMING_INDICATOR = "audioStreamingIndicator";
+ public static final String KEY_COUNT_RATE = "countRate";
+ public static final String KEY_FORWARD_SEEK_INDICATOR = "forwardSeekIndicator";
+ public static final String KEY_BACK_SEEK_INDICATOR = "backSeekIndicator";
/**
* Constructs a new SetMediaClockTimer object
@@ -350,4 +385,84 @@ public class SetMediaClockTimer extends RPCRequest {
setParameters(KEY_AUDIO_STREAMING_INDICATOR, audioStreamingIndicator);
return this;
}
+
+ /**
+ * Sets the countRate.
+ *
+ * @param countRate The value of this parameter is the amount that the media clock timer will advance per 1.0
+ * seconds of real time. Values less than 1.0 will therefore advance the timer slower than
+ * real-time, while values greater than 1.0 will advance the timer faster than real-time.
+ * e.g. If this parameter is set to `0.5`, the timer will advance one second per two seconds
+ * real-time, or at 50% speed. If this parameter is set to `2.0`, the timer will advance two
+ * seconds per one second real-time, or at 200% speed.
+ * {"num_min_value": 0.1, "num_max_value": 100.0}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public SetMediaClockTimer setCountRate(Float countRate) {
+ setParameters(KEY_COUNT_RATE, countRate);
+ return this;
+ }
+
+ /**
+ * Gets the countRate.
+ *
+ * @return Float The value of this parameter is the amount that the media clock timer will advance per 1.0
+ * seconds of real time. Values less than 1.0 will therefore advance the timer slower than
+ * real-time, while values greater than 1.0 will advance the timer faster than real-time.
+ * e.g. If this parameter is set to `0.5`, the timer will advance one second per two seconds
+ * real-time, or at 50% speed. If this parameter is set to `2.0`, the timer will advance two
+ * seconds per one second real-time, or at 200% speed.
+ * {"num_min_value": 0.1, "num_max_value": 100.0}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public Float getCountRate() {
+ Object object = getParameters(KEY_COUNT_RATE);
+ return SdlDataTypeConverter.objectToFloat(object);
+ }
+
+ /**
+ * Sets the forwardSeekIndicator.
+ *
+ * @param forwardSeekIndicator Used to control the forward seek button to either skip forward a set amount of time or to
+ * the next track.
+ * @since SmartDeviceLink 7.1.0
+ */
+ public SetMediaClockTimer setForwardSeekIndicator(SeekStreamingIndicator forwardSeekIndicator) {
+ setParameters(KEY_FORWARD_SEEK_INDICATOR, forwardSeekIndicator);
+ return this;
+ }
+
+ /**
+ * Gets the forwardSeekIndicator.
+ *
+ * @return SeekStreamingIndicator Used to control the forward seek button to either skip forward a set amount of time or to
+ * the next track.
+ * @since SmartDeviceLink 7.1.0
+ */
+ public SeekStreamingIndicator getForwardSeekIndicator() {
+ return (SeekStreamingIndicator) getObject(SeekStreamingIndicator.class, KEY_FORWARD_SEEK_INDICATOR);
+ }
+
+ /**
+ * Sets the backSeekIndicator.
+ *
+ * @param backSeekIndicator Used to control the forward seek button to either skip back a set amount of time or to the
+ * previous track.
+ * @since SmartDeviceLink 7.1.0
+ */
+ public SetMediaClockTimer setBackSeekIndicator(SeekStreamingIndicator backSeekIndicator) {
+ setParameters(KEY_BACK_SEEK_INDICATOR, backSeekIndicator);
+ return this;
+ }
+
+ /**
+ * Gets the backSeekIndicator.
+ *
+ * @return SeekStreamingIndicator Used to control the forward seek button to either skip back a set amount of time or to the
+ * previous track.
+ * @since SmartDeviceLink 7.1.0
+ */
+ public SeekStreamingIndicator getBackSeekIndicator() {
+ return (SeekStreamingIndicator) getObject(SeekStreamingIndicator.class, KEY_BACK_SEEK_INDICATOR);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/Show.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/Show.java
index e11ac0009..5962a04fd 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/Show.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/Show.java
@@ -117,10 +117,13 @@ import java.util.List;
* <tr>
* <td>mediaClock</td>
* <td>String</td>
- * <td><p>Text value for MediaClock field.</p> <p>Has to be properly formatted by Mobile App according to SDL capabilities.</p>If this text is set, any automatic media clock updates previously set with SetMediaClockTimer will be stopped.</td>
+ * <td>Text value for MediaClock field. Has to be properly formatted by Mobile App according to the module's capabilities. If this text is set, any automatic media clock updates previously set with SetMediaClockTimer will be stopped.</td>
* <td>N</td>
- * <td><p>Must be properly formatted as described in the MediaClockFormat enumeration. </p><p>If a value of five spaces is provided, this will clear that field on the display (i.e. the media clock timer field will not display anything) </p>Maxlength = 500</td>
- * <td>SmartDeviceLink 1.0</td>
+ * <td>{"string_min_length": 0, "string_max_length": 500}</td>
+ * <td>
+ * @since SmartDeviceLink 1.0.0
+ * @property-deprecated in SmartDeviceLink 7.1.0
+ * </td>
* </tr>
* <tr>
* <td>mediaTrack</td>
@@ -198,6 +201,10 @@ public class Show extends RPCRequest {
public static final String KEY_MAIN_FIELD_3 = "mainField3";
public static final String KEY_MAIN_FIELD_4 = "mainField4";
public static final String KEY_STATUS_BAR = "statusBar";
+ /**
+ * @since SmartDeviceLink 1.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
@Deprecated
public static final String KEY_MEDIA_CLOCK = "mediaClock";
public static final String KEY_ALIGNMENT = "alignment";
@@ -421,9 +428,14 @@ public class Show extends RPCRequest {
}
/**
- * Gets the String value of the MediaClock
+ * Gets the mediaClock.
*
- * @return String -a String value of the MediaClock
+ * @return String Text value for MediaClock field. Has to be properly formatted by Mobile App according to
+ * the module's capabilities. If this text is set, any automatic media clock updates
+ * previously set with SetMediaClockTimer will be stopped.
+ * {"string_min_length": 0, "string_max_length": 500}
+ * @since SmartDeviceLink 1.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
*/
@Deprecated
public String getMediaClock() {
@@ -431,19 +443,14 @@ public class Show extends RPCRequest {
}
/**
- * Sets the value for the MediaClock field using a format described in the
- * MediaClockFormat enumeration
+ * Sets the mediaClock.
*
- * @param mediaClock a String value for the MediaClock
- * <p></p>
- * <b>Notes: </b>
- * <ul>
- * <li>Must be properly formatted as described in the
- * MediaClockFormat enumeration</li>
- * <li>If a value of five spaces is provided, this will clear
- * that field on the display (i.e. the media clock timer field
- * will not display anything)</li>
- * </ul>
+ * @param mediaClock Text value for MediaClock field. Has to be properly formatted by Mobile App according to
+ * the module's capabilities. If this text is set, any automatic media clock updates
+ * previously set with SetMediaClockTimer will be stopped.
+ * {"string_min_length": 0, "string_max_length": 500}
+ * @since SmartDeviceLink 1.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
*/
@Deprecated
public Show setMediaClock(String mediaClock) {
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/ShowConstantTbt.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/ShowConstantTbt.java
index d0bbd0222..22b85b5ff 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/ShowConstantTbt.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/ShowConstantTbt.java
@@ -203,11 +203,11 @@ public class ShowConstantTbt extends RPCRequest {
}
/**
- * Sets a Fraction of distance till next maneuver
+ * Sets the distanceToManeuver.
*
- * @param distanceToManeuver a Double value representing a Fraction of distance till next maneuver
- * <p></p>
- * <b>Notes: </b>Minvalue=0; Maxvalue=1000000000
+ * @param distanceToManeuver Distance (in meters) until next maneuver. May be used to calculate progress bar.
+ * {"num_min_value": 0.0, "num_max_value": 1000000000.0}
+ * @since SmartDeviceLink 2.0.0
*/
public ShowConstantTbt setDistanceToManeuver(Double distanceToManeuver) {
setParameters(KEY_MANEUVER_DISTANCE, distanceToManeuver);
@@ -215,20 +215,23 @@ public class ShowConstantTbt extends RPCRequest {
}
/**
- * Gets a Fraction of distance till next maneuver
+ * Gets the distanceToManeuver.
*
- * @return Double -a Double value representing a Fraction of distance till next maneuver
+ * @return Float Distance (in meters) until next maneuver. May be used to calculate progress bar.
+ * {"num_min_value": 0.0, "num_max_value": 1000000000.0}
+ * @since SmartDeviceLink 2.0.0
*/
public Double getDistanceToManeuver() {
return getDouble(KEY_MANEUVER_DISTANCE);
}
/**
- * Sets a Distance till next maneuver (starting from) from previous maneuver
+ * Sets the distanceToManeuverScale.
*
- * @param distanceToManeuverScale a Double value representing a Distance till next maneuver (starting from) from previous maneuver
- * <p></p>
- * <b>Notes: </b>Minvalue=0; Maxvalue=1000000000
+ * @param distanceToManeuverScale Distance (in meters) from previous maneuver to next maneuver. May be used to calculate
+ * progress bar.
+ * {"num_min_value": 0.0, "num_max_value": 1000000000.0}
+ * @since SmartDeviceLink 2.0.0
*/
public ShowConstantTbt setDistanceToManeuverScale(Double distanceToManeuverScale) {
setParameters(KEY_MANEUVER_DISTANCE_SCALE, distanceToManeuverScale);
@@ -236,9 +239,12 @@ public class ShowConstantTbt extends RPCRequest {
}
/**
- * Gets a Distance till next maneuver (starting from) from previous maneuver
+ * Gets the distanceToManeuverScale.
*
- * @return Double -a Double value representing a Distance till next maneuver (starting from) from previous maneuver
+ * @return Float Distance (in meters) from previous maneuver to next maneuver. May be used to calculate
+ * progress bar.
+ * {"num_min_value": 0.0, "num_max_value": 1000000000.0}
+ * @since SmartDeviceLink 2.0.0
*/
public Double getDistanceToManeuverScale() {
return getDouble(KEY_MANEUVER_DISTANCE_SCALE);
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleData.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleData.java
index 99e881202..a779c7288 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleData.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleData.java
@@ -116,10 +116,13 @@ import java.util.Hashtable;
* <tr>
* <td>externalTemperature</td>
* <td>Boolean</td>
- * <td>The external temperature in degrees celsius</td>
+ * <td>The external temperature in degrees celsius. This parameter is deprecated starting RPCSpec 7.1.0, please see climateData.</td>
* <td>N</td>
* <td>Subscribable</td>
- * <td>SmartDeviceLink 2.0 </td>
+ * <td>
+ * @since SmartDeviceLink 2.0.0
+ * @property-deprecated in SmartDeviceLink 7.1.0
+ * </td>
* </tr>
* <tr>
* <td>gearStatus</td>
@@ -308,6 +311,26 @@ import java.util.Hashtable;
* <td>N</td>
* <td>SmartDeviceLink 7.0.0</td>
* </tr>
+ * <tr>
+ * <td>climateData</td>
+ * <td>Boolean</td>
+ * <td>See ClimateData</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>seatOccupancy</td>
+ * <td>Boolean</td>
+ * <td>See SeatOccupancy</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
* </table>
*
* <p> <b>Response</b></p>
@@ -329,6 +352,11 @@ import java.util.Hashtable;
*/
public class SubscribeVehicleData extends RPCRequest {
public static final String KEY_RPM = "rpm";
+ /**
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public static final String KEY_EXTERNAL_TEMPERATURE = "externalTemperature";
public static final String KEY_PRNDL = "prndl";
public static final String KEY_TIRE_PRESSURE = "tirePressure";
@@ -369,6 +397,14 @@ public class SubscribeVehicleData extends RPCRequest {
@Deprecated
public static final String KEY_FUEL_LEVEL_STATE = "fuelLevel_State";
public static final String KEY_STABILITY_CONTROLS_STATUS = "stabilityControlsStatus";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_CLIMATE_DATA = "climateData";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_SEAT_OCCUPANCY = "seatOccupancy";
/**
* Constructs a new SubscribeVehicleData object
@@ -515,22 +551,28 @@ public class SubscribeVehicleData extends RPCRequest {
}
/**
- * Sets a boolean value. If true, subscribes externalTemperature data
+ * Sets the externalTemperature.
*
- * @param externalTemperature a boolean value
+ * @param externalTemperature The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
*/
+ @Deprecated
public SubscribeVehicleData setExternalTemperature(Boolean externalTemperature) {
setParameters(KEY_EXTERNAL_TEMPERATURE, externalTemperature);
return this;
}
/**
- * Gets a boolean value. If true, means the externalTemperature data has been
- * subscribed.
+ * Gets the externalTemperature.
*
- * @return Boolean -a Boolean value. If true, means the externalTemperature data
- * has been subscribed.
+ * @return Boolean The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
*/
+ @Deprecated
public Boolean getExternalTemperature() {
return getBoolean(KEY_EXTERNAL_TEMPERATURE);
}
@@ -1026,4 +1068,46 @@ public class SubscribeVehicleData extends RPCRequest {
setParameters(KEY_STABILITY_CONTROLS_STATUS, stabilityControlsStatus);
return this;
}
+
+ /**
+ * Sets the climateData.
+ *
+ * @param climateData See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public SubscribeVehicleData setClimateData(Boolean climateData) {
+ setParameters(KEY_CLIMATE_DATA, climateData);
+ return this;
+ }
+
+ /**
+ * Gets the climateData.
+ *
+ * @return Boolean See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public Boolean getClimateData() {
+ return getBoolean(KEY_CLIMATE_DATA);
+ }
+
+ /**
+ * Sets the seatOccupancy.
+ *
+ * @param seatOccupancy See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public SubscribeVehicleData setSeatOccupancy(Boolean seatOccupancy) {
+ setParameters(KEY_SEAT_OCCUPANCY, seatOccupancy);
+ return this;
+ }
+
+ /**
+ * Gets the seatOccupancy.
+ *
+ * @return Boolean See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public Boolean getSeatOccupancy() {
+ return getBoolean(KEY_SEAT_OCCUPANCY);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleDataResponse.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleDataResponse.java
index 9d82940d5..7a268f0f3 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleDataResponse.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/SubscribeVehicleDataResponse.java
@@ -47,6 +47,11 @@ import java.util.Hashtable;
public class SubscribeVehicleDataResponse extends RPCResponse {
public static final String KEY_SPEED = "speed";
public static final String KEY_RPM = "rpm";
+ /**
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public static final String KEY_EXTERNAL_TEMPERATURE = "externalTemperature";
public static final String KEY_PRNDL = "prndl";
public static final String KEY_TIRE_PRESSURE = "tirePressure";
@@ -88,8 +93,16 @@ public class SubscribeVehicleDataResponse extends RPCResponse {
@Deprecated
public static final String KEY_FUEL_LEVEL_STATE = "fuelLevel_State";
public static final String KEY_STABILITY_CONTROLS_STATUS = "stabilityControlsStatus";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_CLIMATE_DATA = "climateData";
/**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_SEAT_OCCUPANCY = "seatOccupancy";
+ /**
* Constructs a new SubscribeVehicleDataResponse object
*/
public SubscribeVehicleDataResponse() {
@@ -239,20 +252,28 @@ public class SubscribeVehicleDataResponse extends RPCResponse {
}
/**
- * Sets External Temperature
+ * Sets the externalTemperature.
*
- * @param externalTemperature a VehicleDataResult related to external temperature
+ * @param externalTemperature The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
*/
+ @Deprecated
public SubscribeVehicleDataResponse setExternalTemperature(VehicleDataResult externalTemperature) {
setParameters(KEY_EXTERNAL_TEMPERATURE, externalTemperature);
return this;
}
/**
- * Gets External Temperature
+ * Gets the externalTemperature.
*
- * @return a VehicleDataResult related to external temperature
+ * @return VehicleDataResult The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
*/
+ @Deprecated
public VehicleDataResult getExternalTemperature() {
return (VehicleDataResult) getObject(VehicleDataResult.class, KEY_EXTERNAL_TEMPERATURE);
}
@@ -752,4 +773,46 @@ public class SubscribeVehicleDataResponse extends RPCResponse {
setParameters(KEY_STABILITY_CONTROLS_STATUS, stabilityControlsStatus);
return this;
}
+
+ /**
+ * Sets the climateData.
+ *
+ * @param climateData See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public SubscribeVehicleDataResponse setClimateData(VehicleDataResult climateData) {
+ setParameters(KEY_CLIMATE_DATA, climateData);
+ return this;
+ }
+
+ /**
+ * Gets the climateData.
+ *
+ * @return VehicleDataResult See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public VehicleDataResult getClimateData() {
+ return (VehicleDataResult) getObject(VehicleDataResult.class, KEY_CLIMATE_DATA);
+ }
+
+ /**
+ * Sets the seatOccupancy.
+ *
+ * @param seatOccupancy See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public SubscribeVehicleDataResponse setSeatOccupancy(VehicleDataResult seatOccupancy) {
+ setParameters(KEY_SEAT_OCCUPANCY, seatOccupancy);
+ return this;
+ }
+
+ /**
+ * Gets the seatOccupancy.
+ *
+ * @return VehicleDataResult See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public VehicleDataResult getSeatOccupancy() {
+ return (VehicleDataResult) getObject(VehicleDataResult.class, KEY_SEAT_OCCUPANCY);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleData.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleData.java
index 226499364..3f3dff1bf 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleData.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleData.java
@@ -111,10 +111,13 @@ import java.util.Hashtable;
* <tr>
* <td>externalTemperature</td>
* <td>Boolean</td>
- * <td>The external temperature in degrees celsius</td>
+ * <td>The external temperature in degrees celsius. This parameter is deprecated starting RPCSpec 7.1.0, please see climateData.</td>
* <td>N</td>
* <td>Subscribable</td>
- * <td>SmartDeviceLink 2.0 </td>
+ * <td>
+ * @since SmartDeviceLink 2.0.0
+ * @property-deprecated in SmartDeviceLink 7.1.0
+ * </td>
* </tr>
* <tr>
* <td>gearStatus</td>
@@ -295,6 +298,26 @@ import java.util.Hashtable;
* <td>N</td>
* <td>SmartDeviceLink 7.0.0</td>
* </tr>
+ * <tr>
+ * <td>climateData</td>
+ * <td>Boolean</td>
+ * <td>See ClimateData</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>seatOccupancy</td>
+ * <td>Boolean</td>
+ * <td>See SeatOccupancy</td>
+ * <td>N</td>
+ * <td></td>
+ * <td>
+ * @since SmartDeviceLink 7.1.0
+ * </td>
+ * </tr>
*
*
* </table>
@@ -317,6 +340,11 @@ import java.util.Hashtable;
public class UnsubscribeVehicleData extends RPCRequest {
public static final String KEY_SPEED = "speed";
public static final String KEY_RPM = "rpm";
+ /**
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public static final String KEY_EXTERNAL_TEMPERATURE = "externalTemperature";
public static final String KEY_PRNDL = "prndl";
public static final String KEY_TIRE_PRESSURE = "tirePressure";
@@ -356,6 +384,14 @@ public class UnsubscribeVehicleData extends RPCRequest {
@Deprecated
public static final String KEY_FUEL_LEVEL_STATE = "fuelLevel_State";
public static final String KEY_STABILITY_CONTROLS_STATUS = "stabilityControlsStatus";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_SEAT_OCCUPANCY = "seatOccupancy";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_CLIMATE_DATA = "climateData";
/**
* Constructs a new UnsubscribeVehicleData object
@@ -505,22 +541,28 @@ public class UnsubscribeVehicleData extends RPCRequest {
}
/**
- * Sets a boolean value. If true, unsubscribes from externalTemperature data
+ * Sets the externalTemperature.
*
- * @param externalTemperature a boolean value
+ * @param externalTemperature The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
*/
+ @Deprecated
public UnsubscribeVehicleData setExternalTemperature(Boolean externalTemperature) {
setParameters(KEY_EXTERNAL_TEMPERATURE, externalTemperature);
return this;
}
/**
- * Gets a boolean value. If true, means the externalTemperature data has been
- * unsubscribed.
+ * Gets the externalTemperature.
*
- * @return Boolean -a Boolean value. If true, means the externalTemperature data
- * has been unsubscribed.
+ * @return Boolean The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
*/
+ @Deprecated
public Boolean getExternalTemperature() {
return getBoolean(KEY_EXTERNAL_TEMPERATURE);
}
@@ -1015,4 +1057,46 @@ public class UnsubscribeVehicleData extends RPCRequest {
public Boolean getStabilityControlsStatus() {
return getBoolean(KEY_STABILITY_CONTROLS_STATUS);
}
+
+ /**
+ * Sets the climateData.
+ *
+ * @param climateData See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public UnsubscribeVehicleData setClimateData(Boolean climateData) {
+ setParameters(KEY_CLIMATE_DATA, climateData);
+ return this;
+ }
+
+ /**
+ * Gets the climateData.
+ *
+ * @return Boolean See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public Boolean getClimateData() {
+ return getBoolean(KEY_CLIMATE_DATA);
+ }
+
+ /**
+ * Sets the seatOccupancy.
+ *
+ * @param seatOccupancy See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public UnsubscribeVehicleData setSeatOccupancy(Boolean seatOccupancy) {
+ setParameters(KEY_SEAT_OCCUPANCY, seatOccupancy);
+ return this;
+ }
+
+ /**
+ * Gets the seatOccupancy.
+ *
+ * @return Boolean See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public Boolean getSeatOccupancy() {
+ return getBoolean(KEY_SEAT_OCCUPANCY);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleDataResponse.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleDataResponse.java
index a9b59ce9b..114a1971a 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleDataResponse.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/UnsubscribeVehicleDataResponse.java
@@ -47,6 +47,11 @@ import java.util.Hashtable;
public class UnsubscribeVehicleDataResponse extends RPCResponse {
public static final String KEY_SPEED = "speed";
public static final String KEY_RPM = "rpm";
+ /**
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
+ */
+ @Deprecated
public static final String KEY_EXTERNAL_TEMPERATURE = "externalTemperature";
public static final String KEY_PRNDL = "prndl";
public static final String KEY_TIRE_PRESSURE = "tirePressure";
@@ -88,8 +93,16 @@ public class UnsubscribeVehicleDataResponse extends RPCResponse {
@Deprecated
public static final String KEY_FUEL_LEVEL_STATE = "fuelLevel_State";
public static final String KEY_STABILITY_CONTROLS_STATUS = "stabilityControlsStatus";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_CLIMATE_DATA = "climateData";
/**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_SEAT_OCCUPANCY = "seatOccupancy";
+ /**
* Constructs a new UnsubscribeVehicleDataResponse object
*/
public UnsubscribeVehicleDataResponse() {
@@ -240,20 +253,28 @@ public class UnsubscribeVehicleDataResponse extends RPCResponse {
}
/**
- * Sets External Temperature
+ * Sets the externalTemperature.
*
- * @param externalTemperature a VehicleDataResult related to external temperature
+ * @param externalTemperature The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
*/
+ @Deprecated
public UnsubscribeVehicleDataResponse setExternalTemperature(VehicleDataResult externalTemperature) {
setParameters(KEY_EXTERNAL_TEMPERATURE, externalTemperature);
return this;
}
/**
- * Gets External Temperature
+ * Gets the externalTemperature.
*
- * @return a VehicleDataResult related to external temperature
+ * @return VehicleDataResult The external temperature in degrees celsius. This parameter is deprecated starting RPC
+ * Spec 7.1.0, please see climateData.
+ * @since SmartDeviceLink 2.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
*/
+ @Deprecated
public VehicleDataResult getExternalTemperature() {
return (VehicleDataResult) getObject(VehicleDataResult.class, KEY_EXTERNAL_TEMPERATURE);
}
@@ -753,4 +774,46 @@ public class UnsubscribeVehicleDataResponse extends RPCResponse {
setParameters(KEY_STABILITY_CONTROLS_STATUS, stabilityControlsStatus);
return this;
}
+
+ /**
+ * Sets the climateData.
+ *
+ * @param climateData See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public UnsubscribeVehicleDataResponse setClimateData(VehicleDataResult climateData) {
+ setParameters(KEY_CLIMATE_DATA, climateData);
+ return this;
+ }
+
+ /**
+ * Gets the climateData.
+ *
+ * @return VehicleDataResult See ClimateData
+ * @since SmartDeviceLink 7.1.0
+ */
+ public VehicleDataResult getClimateData() {
+ return (VehicleDataResult) getObject(VehicleDataResult.class, KEY_CLIMATE_DATA);
+ }
+
+ /**
+ * Sets the seatOccupancy.
+ *
+ * @param seatOccupancy See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public UnsubscribeVehicleDataResponse setSeatOccupancy(VehicleDataResult seatOccupancy) {
+ setParameters(KEY_SEAT_OCCUPANCY, seatOccupancy);
+ return this;
+ }
+
+ /**
+ * Gets the seatOccupancy.
+ *
+ * @return VehicleDataResult See SeatOccupancy
+ * @since SmartDeviceLink 7.1.0
+ */
+ public VehicleDataResult getSeatOccupancy() {
+ return (VehicleDataResult) getObject(VehicleDataResult.class, KEY_SEAT_OCCUPANCY);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/VideoStreamingCapability.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/VideoStreamingCapability.java
index 88b18b802..6e1b6abbf 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/VideoStreamingCapability.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/VideoStreamingCapability.java
@@ -33,7 +33,9 @@ package com.smartdevicelink.proxy.rpc;
import com.smartdevicelink.proxy.RPCStruct;
import com.smartdevicelink.util.SdlDataTypeConverter;
+import com.smartdevicelink.util.Version;
+import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
@@ -49,6 +51,11 @@ public class VideoStreamingCapability extends RPCStruct {
public static final String KEY_DIAGONAL_SCREEN_SIZE = "diagonalScreenSize";
public static final String KEY_PIXEL_PER_INCH = "pixelPerInch";
public static final String KEY_SCALE = "scale";
+ public static final String KEY_PREFERRED_FPS = "preferredFPS";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_ADDITIONAL_VIDEO_STREAMING_CAPABILITIES = "additionalVideoStreamingCapabilities";
public VideoStreamingCapability() {
}
@@ -180,4 +187,71 @@ public class VideoStreamingCapability extends RPCStruct {
setValue(KEY_SCALE, scale);
return this;
}
+
+
+ /**
+ * Gets the additionalVideoStreamingCapabilities.
+ *
+ * @return List
+ * {"array_min_size": 1, "array_max_size": 100}
+ * @since SmartDeviceLink 7.1.0
+ */
+ @SuppressWarnings("unchecked")
+ public List<VideoStreamingCapability> getAdditionalVideoStreamingCapabilities(){
+ return (List<VideoStreamingCapability>) getObject(VideoStreamingCapability.class, KEY_ADDITIONAL_VIDEO_STREAMING_CAPABILITIES);
+ }
+
+ /**
+ * Sets the additionalVideoStreamingCapabilities.
+ *
+ * @param additionalVideoStreamingCapabilities
+ * {"array_min_size": 1, "array_max_size": 100}
+ * @since SmartDeviceLink 7.1.0
+ */
+ public VideoStreamingCapability setAdditionalVideoStreamingCapabilities(List<VideoStreamingCapability> additionalVideoStreamingCapabilities) {
+ setValue(KEY_ADDITIONAL_VIDEO_STREAMING_CAPABILITIES, additionalVideoStreamingCapabilities);
+ return this;
+ }
+
+
+ /**
+ * @return the preferred frame rate per second (FPS) specified by head unit.
+ */
+ public Integer getPreferredFPS() {
+ return getInteger(KEY_PREFERRED_FPS);
+ }
+
+ /**
+ * @param preferredFPS preferred frame rate per second
+ */
+ public VideoStreamingCapability setPreferredFPS(Integer preferredFPS) {
+ setValue(KEY_PREFERRED_FPS, preferredFPS);
+ return this;
+ }
+
+ @Override
+ public void format(Version rpcVersion, boolean formatParams) {
+ if (getAdditionalVideoStreamingCapabilities() != null && getAdditionalVideoStreamingCapabilities().contains(this)) {
+ List<VideoStreamingCapability> copyList = new ArrayList<>(getAdditionalVideoStreamingCapabilities());
+
+ while (copyList.contains(this)) {
+ copyList.remove(this);
+ }
+
+ VideoStreamingCapability copyRootVideoStreamCapability = new VideoStreamingCapability();
+ copyRootVideoStreamCapability.setPreferredResolution(this.getPreferredResolution());
+ copyRootVideoStreamCapability.setMaxBitrate(this.getMaxBitrate());
+ copyRootVideoStreamCapability.setSupportedFormats(this.getSupportedFormats());
+ copyRootVideoStreamCapability.setHapticSpatialDataSupported(this.isHapticSpatialDataSupported());
+ copyRootVideoStreamCapability.setDiagonalScreenSize(this.getDiagonalScreenSize());
+ copyRootVideoStreamCapability.setPixelPerInch(this.getPixelPerInch());
+ copyRootVideoStreamCapability.setScale(this.getScale());
+ copyRootVideoStreamCapability.setPreferredFPS(this.getPreferredFPS());
+
+ copyList.add(copyRootVideoStreamCapability);
+ this.setAdditionalVideoStreamingCapabilities(copyList);
+ }
+
+ super.format(rpcVersion, formatParams);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/WindowCapability.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/WindowCapability.java
index 28a5b0ccb..4de9d153c 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/WindowCapability.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/WindowCapability.java
@@ -21,6 +21,10 @@ public class WindowCapability extends RPCStruct {
public static final String KEY_SOFT_BUTTON_CAPABILITIES = "softButtonCapabilities";
public static final String KEY_MENU_LAYOUTS_AVAILABLE = "menuLayoutsAvailable";
public static final String KEY_DYNAMIC_UPDATE_CAPABILITIES = "dynamicUpdateCapabilities";
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ public static final String KEY_KEYBOARD_CAPABILITIES = "keyboardCapabilities";
public WindowCapability() {
}
@@ -240,4 +244,25 @@ public class WindowCapability extends RPCStruct {
public DynamicUpdateCapabilities getDynamicUpdateCapabilities() {
return (DynamicUpdateCapabilities) getObject(DynamicUpdateCapabilities.class, KEY_DYNAMIC_UPDATE_CAPABILITIES);
}
+
+ /**
+ * Sets the keyboardCapabilities.
+ *
+ * @param keyboardCapabilities See KeyboardCapabilities
+ * @since SmartDeviceLink 7.1.0
+ */
+ public WindowCapability setKeyboardCapabilities(KeyboardCapabilities keyboardCapabilities) {
+ setValue(KEY_KEYBOARD_CAPABILITIES, keyboardCapabilities);
+ return this;
+ }
+
+ /**
+ * Gets the keyboardCapabilities.
+ *
+ * @return KeyboardCapabilities See KeyboardCapabilities
+ * @since SmartDeviceLink 7.1.0
+ */
+ public KeyboardCapabilities getKeyboardCapabilities() {
+ return (KeyboardCapabilities) getObject(KeyboardCapabilities.class, KEY_KEYBOARD_CAPABILITIES);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/AppCapabilityType.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/AppCapabilityType.java
new file mode 100644
index 000000000..79969528c
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/AppCapabilityType.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2021 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.proxy.rpc.enums;
+
+/**
+ * @since SmartDeviceLink 7.1.0
+ * Enumerations of all available app capability types
+ */
+public enum AppCapabilityType {
+ VIDEO_STREAMING,
+ ;
+
+ /**
+ * Convert String to AppCapabilityType
+ * @param value String
+ * @return AppCapabilityType
+ */
+ public static AppCapabilityType valueForString(String value) {
+ try{
+ return valueOf(value);
+ }catch(Exception e){
+ return null;
+ }
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/DoorStatusType.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/DoorStatusType.java
new file mode 100644
index 000000000..f19c66699
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/DoorStatusType.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 - 2020, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc.enums;
+
+/**
+ * @since SmartDeviceLink 7.1.0
+ */
+public enum DoorStatusType {
+ CLOSED,
+ LOCKED,
+ AJAR,
+ REMOVED;
+
+ /**
+ * Convert String to DoorStatusType
+ *
+ * @param value String
+ * @return DoorStatusType
+ */
+ public static DoorStatusType valueForString(String value) {
+ try {
+ return valueOf(value);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/ImageFieldName.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/ImageFieldName.java
index d6a0d7fa3..4421e2a97 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/ImageFieldName.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/ImageFieldName.java
@@ -121,7 +121,19 @@ public enum ImageFieldName {
*
* @since SmartDeviceLink 7.0.0
*/
- subMenuIcon;
+ subMenuIcon,
+ /**
+ * The secondary image field for AddCommand
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+ menuCommandSecondaryImage,
+ /**
+ * The secondary image field for AddSubMenu
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+ menuSubMenuSecondaryImage;
/**
* Convert String to ImageFieldName
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardEvent.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardEvent.java
index 2e3b45c1b..a54d247a6 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardEvent.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardEvent.java
@@ -58,7 +58,14 @@ public enum KeyboardEvent {
* @since SmartDeviceLink 4.0
*/
ENTRY_VOICE,
- ;
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ INPUT_KEY_MASK_ENABLED,
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ INPUT_KEY_MASK_DISABLED;
/**
* Convert String to KeyboardEvent
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardInputMask.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardInputMask.java
new file mode 100644
index 000000000..391947a87
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardInputMask.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2017 - 2020, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc.enums;
+
+/**
+ * Enumeration listing possible input character masking.
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+public enum KeyboardInputMask {
+ ENABLE_INPUT_KEY_MASK,
+ DISABLE_INPUT_KEY_MASK,
+ USER_CHOICE_INPUT_KEY_MASK;
+
+ /**
+ * Convert String to KeyboardInputMask
+ *
+ * @param value String
+ * @return KeyboardInputMask
+ */
+ public static KeyboardInputMask valueForString(String value) {
+ try {
+ return valueOf(value);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardLayout.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardLayout.java
index fac69ac4d..0e263c413 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardLayout.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/KeyboardLayout.java
@@ -39,7 +39,11 @@ package com.smartdevicelink.proxy.rpc.enums;
public enum KeyboardLayout {
QWERTY,
QWERTZ,
- AZERTY;
+ AZERTY,
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ NUMERIC;
public static KeyboardLayout valueForString(String value) {
try {
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/SeekIndicatorType.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/SeekIndicatorType.java
new file mode 100644
index 000000000..0efad637a
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/SeekIndicatorType.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2017 - 2020, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.proxy.rpc.enums;
+
+/**
+ * @since SmartDeviceLink 7.1.0
+ */
+public enum SeekIndicatorType {
+ TRACK,
+ TIME;
+
+ /**
+ * Convert String to SeekIndicatorType
+ *
+ * @param value String
+ * @return SeekIndicatorType
+ */
+ public static SeekIndicatorType valueForString(String value) {
+ try {
+ return valueOf(value);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/StaticIconName.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/StaticIconName.java
index 7b136c11f..c8bf94b67 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/StaticIconName.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/StaticIconName.java
@@ -76,10 +76,13 @@ public enum StaticIconName {
*/
AUDIOBOOK_NARRATOR("0x82"),
+ @Deprecated
+ AUXILLARY_AUDIO("0x45"),
+
/**
- * Icon Name auxillary audio
+ * Icon Name auxiliary audio
*/
- AUXILLARY_AUDIO("0x45"),
+ AUXILIARY_AUDIO("0x45"),
/**
* Icon Name back / return
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/TextFieldName.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/TextFieldName.java
index 53f77428d..5658e3e17 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/TextFieldName.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/TextFieldName.java
@@ -65,8 +65,11 @@ public enum TextFieldName {
statusBar,
/**
* Text value for MediaClock field; applies to "Show"
+ *
+ * @since SmartDeviceLink 1.0.0
+ * @deprecated in SmartDeviceLink 7.1.0
*/
-
+ @Deprecated
mediaClock,
/**
* The track field of NGN and GEN1.1 MFD displays. This field is only available for media applications; applies to "Show"
@@ -182,6 +185,18 @@ public enum TextFieldName {
*/
phoneNumber,
/**
+ * Optional time to destination field for ShowConstantTBT
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+ timeToDestination,
+ /**
+ * Turn text for turnList parameter of UpdateTurnList
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+ turnText,
+ /**
* Optional title of the template that will be displayed
*
* @since SmartDeviceLink 6.0
@@ -204,7 +219,31 @@ public enum TextFieldName {
*
* @since SmartDeviceLink 7.0.0
*/
- subtleAlertSoftButtonText;
+ subtleAlertSoftButtonText,
+ /**
+ * Secondary text for AddCommand
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+ menuCommandSecondaryText,
+ /**
+ * Tertiary text for AddCommand
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+ menuCommandTertiaryText,
+ /**
+ * Secondary text for AddSubMenu
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+ menuSubMenuSecondaryText,
+ /**
+ * Tertiary text for AddSubMenu
+ *
+ * @since SmartDeviceLink 7.1.0
+ */
+ menuSubMenuTertiaryText;
/**
* Convert String to TextFieldName
diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/VehicleDataType.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/VehicleDataType.java
index 7d7219453..050a0169c 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/VehicleDataType.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/VehicleDataType.java
@@ -62,8 +62,14 @@ public enum VehicleDataType {
*/
VEHICLEDATA_FUELCONSUMPTION,
/**
+ * @since SmartDeviceLink 7.1.0
+ */
+ VEHICLEDATA_CLIMATEDATA,
+ /**
* Notifies EXTERNTEMPData may be subscribed
+ * @deprecated since SmartDeviceLink 7.1.0
*/
+ @Deprecated
VEHICLEDATA_EXTERNTEMP,
/**
* Notifies VINData may be subscribed
@@ -185,7 +191,11 @@ public enum VehicleDataType {
/**
* @since SmartDeviceLink 7.0.0
*/
- VEHICLEDATA_GEARSTATUS;
+ VEHICLEDATA_GEARSTATUS,
+ /**
+ * @since SmartDeviceLink 7.1.0
+ */
+ VEHICLEDATA_SEATOCCUPANCY;
/**
* Convert String to VehicleDataType
diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java
index ed4957b78..8b35541b4 100644
--- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java
+++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java
@@ -41,8 +41,10 @@ import com.smartdevicelink.protocol.ISdlServiceListener;
import com.smartdevicelink.protocol.ProtocolMessage;
import com.smartdevicelink.protocol.SdlPacket;
import com.smartdevicelink.protocol.SdlProtocolBase;
+import com.smartdevicelink.protocol.enums.ControlFrameTags;
import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.rpc.VehicleType;
import com.smartdevicelink.proxy.rpc.VideoStreamingFormat;
import com.smartdevicelink.proxy.rpc.enums.VideoStreamingProtocol;
import com.smartdevicelink.security.ISecurityInitializedListener;
@@ -51,6 +53,7 @@ import com.smartdevicelink.streaming.video.VideoStreamingParameters;
import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.transport.enums.TransportType;
import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.SystemInfo;
import com.smartdevicelink.util.Version;
import java.util.HashMap;
@@ -209,6 +212,38 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ
sendMessage(protocolMessage);
}
+ /**
+ * Extracts the SystemInfo out of a packet
+ * @param packet should be a StartServiceACK for the RPC service
+ * @return an instance of SystemInfo if the information is available, null otherwise
+ */
+ protected SystemInfo extractSystemInfo(SdlPacket packet) {
+ SystemInfo systemInfo = null;
+ if (packet != null && packet.getFrameInfo() == SdlPacket.FRAME_INFO_START_SERVICE_ACK) {
+ VehicleType vehicleType = null;
+
+ String make = (String) packet.getTag(ControlFrameTags.RPC.StartServiceACK.MAKE);
+ String model = (String) packet.getTag(ControlFrameTags.RPC.StartServiceACK.MODEL);
+ String modelYear = (String) packet.getTag(ControlFrameTags.RPC.StartServiceACK.MODEL_YEAR);
+ String trim = (String) packet.getTag(ControlFrameTags.RPC.StartServiceACK.TRIM);
+ String softwareVersion = (String) packet.getTag(ControlFrameTags.RPC.StartServiceACK.SYSTEM_SOFTWARE_VERSION);
+ String hardwareVersion = (String) packet.getTag(ControlFrameTags.RPC.StartServiceACK.SYSTEM_HARDWARE_VERSION);
+
+ if (make != null || model != null || modelYear != null || trim != null) {
+ vehicleType = new VehicleType()
+ .setMake(make)
+ .setModel(model)
+ .setModelYear(modelYear)
+ .setTrim(trim);
+ }
+
+ if (vehicleType != null || softwareVersion != null || hardwareVersion != null) {
+ systemInfo = new SystemInfo(vehicleType, softwareVersion, hardwareVersion);
+ }
+ }
+
+ return systemInfo;
+ }
public boolean isServiceProtected(SessionType sType) {
return encryptedServices.contains(sType);
diff --git a/base/src/main/java/com/smartdevicelink/session/ISdlSessionListener.java b/base/src/main/java/com/smartdevicelink/session/ISdlSessionListener.java
index 180452997..2f754a30e 100644
--- a/base/src/main/java/com/smartdevicelink/session/ISdlSessionListener.java
+++ b/base/src/main/java/com/smartdevicelink/session/ISdlSessionListener.java
@@ -35,6 +35,7 @@ import androidx.annotation.RestrictTo;
import com.smartdevicelink.proxy.RPCMessage;
import com.smartdevicelink.transport.BaseTransportConfig;
+import com.smartdevicelink.util.SystemInfo;
import com.smartdevicelink.util.Version;
@RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -63,8 +64,9 @@ public interface ISdlSessionListener {
*
* @param sessionID session ID associated with the session that was established
* @param version the protocol version that has been negotiated for this session
+ * @param systemInfo info about the device that this service is started
*/
- void onSessionStarted(int sessionID, Version version);
+ void onSessionStarted(int sessionID, Version version, SystemInfo systemInfo);
/**
* Called to indicate that the session that was previously established has now ended. This means
diff --git a/base/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java b/base/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java
index 51c635154..7a2fe7b78 100644
--- a/base/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java
+++ b/base/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java
@@ -41,6 +41,7 @@ import com.smartdevicelink.proxy.rpc.enums.VideoStreamingCodec;
import com.smartdevicelink.proxy.rpc.enums.VideoStreamingProtocol;
import com.smartdevicelink.util.DebugTool;
+import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("FieldCanBeLocal")
@@ -64,8 +65,12 @@ public class VideoStreamingParameters {
private int frameRate;
private int bitrate;
private int interval;
+ private double preferredDiagonal;
+ private double scale = DEFAULT_SCALE;
private ImageResolution resolution;
private VideoStreamingFormat format;
+ private List<VideoStreamingCapability> additionalCapabilities = new ArrayList<>(1);
+ private boolean stableFrameRate;
public VideoStreamingParameters() {
displayDensity = DEFAULT_DENSITY;
@@ -78,16 +83,49 @@ public class VideoStreamingParameters {
format = new VideoStreamingFormat();
format.setProtocol(DEFAULT_PROTOCOL);
format.setCodec(DEFAULT_CODEC);
+ stableFrameRate = true;
}
+ /**
+ * deprecated constructor of VideoStreamingParameters. This constructor will be removed in the future version.
+ * @param displayDensity
+ * @param frameRate
+ * @param bitrate
+ * @param interval
+ * @param resolution
+ * @param format
+ */
+ @Deprecated
public VideoStreamingParameters(int displayDensity, int frameRate, int bitrate, int interval,
ImageResolution resolution, VideoStreamingFormat format) {
+ this.displayDensity = displayDensity;
+ this.frameRate = frameRate;
+ this.bitrate = bitrate;
+ this.interval = interval;
+ this.resolution = resolution;
+ this.format = format;
+ this.stableFrameRate = true;
+ }
+
+ /**
+ * new constructor of VideoStreamingParameters, which now has stableFrameRate param.
+ * @param displayDensity
+ * @param frameRate
+ * @param bitrate
+ * @param interval
+ * @param resolution
+ * @param format
+ * @param stableFrameRate
+ */
+ public VideoStreamingParameters(int displayDensity, int frameRate, int bitrate, int interval,
+ ImageResolution resolution, VideoStreamingFormat format, boolean stableFrameRate) {
this.displayDensity = displayDensity;
this.frameRate = frameRate;
this.bitrate = bitrate;
this.interval = interval;
this.resolution = resolution;
this.format = format;
+ this.stableFrameRate = stableFrameRate;
}
/**
@@ -119,6 +157,9 @@ public class VideoStreamingParameters {
this.interval = params.interval;
}
if (params.resolution != null) {
+ if (this.resolution == null) {
+ this.resolution = new ImageResolution(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ }
if (params.resolution.getResolutionHeight() != null && params.resolution.getResolutionHeight() > 0) {
this.resolution.setResolutionHeight(params.resolution.getResolutionHeight());
}
@@ -129,6 +170,15 @@ public class VideoStreamingParameters {
if (params.format != null) {
this.format = params.format;
}
+
+ if (params.preferredDiagonal != 0.0) {
+ this.preferredDiagonal = params.preferredDiagonal;
+ }
+
+ if (!params.additionalCapabilities.isEmpty()) {
+ this.additionalCapabilities = params.additionalCapabilities;
+ }
+ this.stableFrameRate = params.stableFrameRate;
}
}
@@ -143,15 +193,28 @@ public class VideoStreamingParameters {
*/
public void update(VideoStreamingCapability capability, String vehicleMake) {
if (capability.getMaxBitrate() != null) {
- this.bitrate = capability.getMaxBitrate() * 1000;
+ // Taking lower value as per SDL 0323 :
+ // https://github.com/smartdevicelink/sdl_evolution/blob/master/proposals/0323-align-VideoStreamingParameter-with-capability.md
+ int capableBitrateInKb = Math.min(Integer.MAX_VALUE / 1000, capability.getMaxBitrate());
+ this.bitrate = Math.min(this.bitrate, capableBitrateInKb * 1000);
} // NOTE: the unit of maxBitrate in getSystemCapability is kbps.
- double scale = DEFAULT_SCALE;
if (capability.getScale() != null) {
scale = capability.getScale();
}
+ if (capability.getDiagonalScreenSize() != null) {
+ preferredDiagonal = capability.getDiagonalScreenSize();
+ }
ImageResolution resolution = capability.getPreferredResolution();
+ if (capability.getAdditionalVideoStreamingCapabilities() != null &&
+ !capability.getAdditionalVideoStreamingCapabilities().isEmpty()) {
+ this.additionalCapabilities = capability.getAdditionalVideoStreamingCapabilities();
+ }
if (resolution != null) {
+ if (this.resolution == null) {
+ this.resolution = new ImageResolution(DEFAULT_WIDTH, DEFAULT_HEIGHT);
+ }
+
if (vehicleMake != null) {
if ((vehicleMake.contains("Ford") || vehicleMake.contains("Lincoln")) && ((resolution.getResolutionHeight() != null && resolution.getResolutionHeight() > 800) || (resolution.getResolutionWidth() != null && resolution.getResolutionWidth() > 800))) {
scale = 1.0 / 0.75;
@@ -165,10 +228,18 @@ public class VideoStreamingParameters {
this.resolution.setResolutionWidth((int) (resolution.getResolutionWidth() / scale));
}
}
+ if (capability.getPreferredFPS() != null) {
+ // Taking lower value as per SDL 0323
+ this.frameRate = Math.min(this.frameRate, capability.getPreferredFPS());
+ }
// This should be the last call as it will return out once a suitable format is found
final List<VideoStreamingFormat> formats = capability.getSupportedFormats();
+
if (formats != null && formats.size() > 0) {
+ if (this.format != null && formats.contains(this.format)) {
+ return; // given format is supported, so no need to change.
+ }
for (VideoStreamingFormat format : formats) {
for (VideoStreamingFormat currentlySupportedFormat : currentlySupportedFormats) {
if (currentlySupportedFormat.equals(format)) {
@@ -233,6 +304,18 @@ public class VideoStreamingParameters {
return resolution;
}
+ public boolean isStableFrameRate() {
+ return stableFrameRate;
+ }
+
+ public void setStableFrameRate(boolean isStable) {
+ stableFrameRate = isStable;
+ }
+
+ public double getScale() { return scale; }
+
+ public double getPreferredDiagonal() { return preferredDiagonal; }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
@@ -250,6 +333,8 @@ public class VideoStreamingParameters {
builder.append(bitrate);
builder.append("}, IFrame interval{ ");
builder.append(interval);
+ builder.append("}, stableFrameRate{");
+ builder.append(stableFrameRate);
builder.append("}");
return builder.toString();
}
diff --git a/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java b/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java
index a142cebad..99510b061 100644
--- a/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java
+++ b/base/src/main/java/com/smartdevicelink/transport/SdlPsm.java
@@ -61,14 +61,14 @@ public class SdlPsm {
private static final byte FIRST_FRAME_DATA_SIZE = 0x08;
private static final int VERSION_MASK = 0xF0; //4 highest bits
- private static final int COMPRESSION_MASK = 0x08; //4th lowest bit
+ private static final int ENCRYPTION_MASK = 0x08; //4th lowest bit
private static final int FRAME_TYPE_MASK = 0x07; //3 lowest bits
int state;
int version;
- boolean compression;
+ boolean encrypted;
int frameType;
int serviceType;
int controlFrameInfo;
@@ -97,7 +97,7 @@ public class SdlPsm {
if (version == 0) { //It should never be 0
return ERROR_STATE;
}
- compression = (1 == ((rawByte & (byte) COMPRESSION_MASK) >> 3));
+ encrypted = (1 == ((rawByte & (byte) ENCRYPTION_MASK) >> 3));
frameType = rawByte & (byte) FRAME_TYPE_MASK;
@@ -194,7 +194,10 @@ public class SdlPsm {
break;
case SdlPacket.FRAME_TYPE_FIRST:
- if (dataLength == FIRST_FRAME_DATA_SIZE) {
+ if (dataLength == FIRST_FRAME_DATA_SIZE || this.encrypted) {
+ //In a few production releases of core the first frame could be
+ //encrypted. Therefore it is not an error state if the first frame data
+ //length is greater than 8 in that case alone.
break;
}
default:
@@ -263,7 +266,7 @@ public class SdlPsm {
public SdlPacket getFormedPacket() {
if (state == FINISHED_STATE) {
//Log.trace(TAG, "Finished packet.");
- return new SdlPacket(version, compression, frameType,
+ return new SdlPacket(version, encrypted, frameType,
serviceType, controlFrameInfo, sessionId,
dataLength, messageId, payload);
} else {
diff --git a/base/src/main/java/com/smartdevicelink/util/BitConverter.java b/base/src/main/java/com/smartdevicelink/util/BitConverter.java
index c3b55ebf3..94695802f 100644
--- a/base/src/main/java/com/smartdevicelink/util/BitConverter.java
+++ b/base/src/main/java/com/smartdevicelink/util/BitConverter.java
@@ -47,12 +47,19 @@ public class BitConverter {
* @param bytes byte array that will be converted to hex
* @param offset int representing the offset to begin conversion at
* @param length int representing number of bytes in array to convert
- * @return the String containing converted hex values or null if byte array is null
+ * @return the String containing converted hex values or null if byte array is null,
+ * a null value if byte array is null,
+ * and an empty string if the offset is greater than the length
*/
public static String bytesToHex(byte[] bytes, int offset, int length) {
if (bytes == null) {
return null;
}
+
+ if (offset > bytes.length) {
+ return "";
+ }
+
final char[] HexDigits = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
byte b;
char[] hexChars = new char[2 * length];
@@ -100,12 +107,20 @@ public class BitConverter {
/**
* @param sizeBuf byte array that will be converted to int
- * @return int converted from byte array or -1 if byte array is null
+ * @param offset The position indicating where to begin converting.
+ * @return int converted from byte array,
+ * -1 if byte array is null,
+ * and 0 if the offset is greater than the length of the byte array
*/
public static int intFromByteArray(byte[] sizeBuf, int offset) {
if (sizeBuf == null) {
return -1;
}
+
+ if (offset > sizeBuf.length) {
+ return 0;
+ }
+
int ret = 0;
for (int i = offset; i < offset + 4; i++) {
ret <<= 8;
@@ -122,12 +137,20 @@ public class BitConverter {
/**
* @param sizeBuf byte array that will be converted to short
- * @return short converted from byte array or -1 if byte array is null
+ * @param offset The position indicating where to begin converting.
+ * @return short converted from byte array,
+ * -1 if byte array is null,
+ * and 0 if the offset is greater than the length of the byte array
*/
public static short shortFromByteArray(byte[] sizeBuf, int offset) {
if (sizeBuf == null) {
return -1;
}
+
+ if (offset > sizeBuf.length) {
+ return 0;
+ }
+
short ret = 0;
for (int i = offset; i < offset + 2; i++) {
ret <<= 8;
diff --git a/base/src/main/java/com/smartdevicelink/util/SystemInfo.java b/base/src/main/java/com/smartdevicelink/util/SystemInfo.java
new file mode 100644
index 000000000..e622be04d
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/util/SystemInfo.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2021 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package com.smartdevicelink.util;
+
+import androidx.annotation.Nullable;
+
+import com.smartdevicelink.proxy.rpc.VehicleType;
+
+public class SystemInfo {
+
+ @Nullable
+ private VehicleType vehicleType;
+ @Nullable
+ private String systemSoftwareVersion;
+ @Nullable
+ private String systemHardwareVersion;
+
+ public SystemInfo(@Nullable VehicleType vehicleType, @Nullable String systemSoftwareVersion, @Nullable String systemHardwareVersion) {
+ this.vehicleType = vehicleType;
+ this.systemSoftwareVersion = systemSoftwareVersion;
+ this.systemHardwareVersion = systemHardwareVersion;
+ }
+
+ @Nullable
+ public VehicleType getVehicleType() {
+ return vehicleType;
+ }
+
+ @Nullable
+ public String getSystemSoftwareVersion() {
+ return systemSoftwareVersion;
+ }
+
+ @Nullable
+ public String getSystemHardwareVersion() {
+ return systemHardwareVersion;
+ }
+}
diff --git a/generator/rpc_spec b/generator/rpc_spec
-Subproject 762489ca140f246923e8bb8f54e96364e9d0101
+Subproject 72632f946941d63a57ee5e99896e3eae3627f7d
diff --git a/generator/templates/base_template.java b/generator/templates/base_template.java
index 29f171872..e2f44a2cf 100644
--- a/generator/templates/base_template.java
+++ b/generator/templates/base_template.java
@@ -63,7 +63,7 @@ import {{i}};{{ '\n' if loop.last }}
* <td>{%- for d in param.description %}{{d}}{%- endfor %}</td>
* <td>{%- if param.mandatory is eq true %}Y{%- else %}N{%- endif %}</td>
* <td>{%- for k in param.values %}{{ '{' if loop.first}}"{{k}}": {{param.values[k]}}{{ ', ' if not loop.last else '}'}}{%- endfor %}</td>
- {%- if param.since is defined and param.since is not none %}{% set see, deprecated, since, history, spacing = param.see, param.deprecated, param.since, param.history, ' * ' %}
+ {%- if param.since is defined and param.since is not none %}{% set see, deprecated, since, history, spacing, headerParam = param.see, param.deprecated, param.since, param.history, ' * ', 'true' %}
* <td>
{%- include "javadoc_version_info.java" %}
* </td>
diff --git a/generator/templates/javadoc_version_info.java b/generator/templates/javadoc_version_info.java
index a6c34467c..0eabb1bd2 100644
--- a/generator/templates/javadoc_version_info.java
+++ b/generator/templates/javadoc_version_info.java
@@ -4,7 +4,11 @@
{%- endif %}
{%- if deprecated is defined and deprecated is not none %}
{{spacing}}{{prefix}}@since SmartDeviceLink {{history[0].since}}
+{%- if headerParam is defined and headerParam is not none %}
+{{spacing}}{{prefix}}@property-deprecated in SmartDeviceLink {{since}}
+{%- else %}
{{spacing}}{{prefix}}@deprecated in SmartDeviceLink {{since}}
+{%- endif %}
{%- elif history is defined and history is not none %}
{{spacing}}{{prefix}}@since SmartDeviceLink {{history[0].since}}
{%- else %}
diff --git a/javaEE/hello_sdl_java_ee/src/main/java/com/smartdevicelink/SdlService.java b/javaEE/hello_sdl_java_ee/src/main/java/com/smartdevicelink/SdlService.java
index 87bbf4c67..c261230a6 100644
--- a/javaEE/hello_sdl_java_ee/src/main/java/com/smartdevicelink/SdlService.java
+++ b/javaEE/hello_sdl_java_ee/src/main/java/com/smartdevicelink/SdlService.java
@@ -32,12 +32,13 @@
package com.smartdevicelink;
-import android.util.Log;
+import com.smartdevicelink.managers.AlertCompletionListener;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.SdlManager;
import com.smartdevicelink.managers.SdlManagerListener;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.managers.lifecycle.LifecycleConfigurationUpdate;
+import com.smartdevicelink.managers.screen.AlertView;
import com.smartdevicelink.managers.screen.choiceset.ChoiceCell;
import com.smartdevicelink.managers.screen.choiceset.ChoiceSet;
import com.smartdevicelink.managers.screen.choiceset.ChoiceSetSelectionListener;
@@ -47,14 +48,15 @@ import com.smartdevicelink.managers.screen.menu.VoiceCommand;
import com.smartdevicelink.managers.screen.menu.VoiceCommandSelectionListener;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCNotification;
-import com.smartdevicelink.proxy.TTSChunkFactory;
-import com.smartdevicelink.proxy.rpc.Alert;
import com.smartdevicelink.proxy.rpc.OnHMIStatus;
import com.smartdevicelink.proxy.rpc.Speak;
+import com.smartdevicelink.proxy.rpc.TTSChunk;
import com.smartdevicelink.proxy.rpc.enums.*;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.SystemInfo;
+
import java.util.*;
@@ -92,7 +94,7 @@ public class SdlService {
}
public void start() {
- DebugTool.logInfo("SdlService start() ");
+ DebugTool.logInfo(TAG, "SdlService start() ");
if (sdlManager != null) {
sdlManager.start();
}
@@ -110,7 +112,7 @@ public class SdlService {
// Build flavors are selected by the "build variants" tab typically located in the bottom left of Android Studio
// Typically in your app, you will only set one of these.
if (sdlManager == null) {
- DebugTool.logInfo("Creating SDL Manager");
+ DebugTool.logInfo(TAG, "Creating SDL Manager");
//FIXME add the transport type
// The app type to be used
@@ -122,12 +124,12 @@ public class SdlService {
SdlManagerListener listener = new SdlManagerListener() {
@Override
public void onStart(SdlManager sdlManager) {
- DebugTool.logInfo("SdlManager onStart");
+ DebugTool.logInfo(TAG, "SdlManager onStart");
}
@Override
public void onDestroy(SdlManager sdlManager) {
- DebugTool.logInfo("SdlManager onDestroy ");
+ DebugTool.logInfo(TAG, "SdlManager onDestroy ");
SdlService.this.sdlManager = null;
if (SdlService.this.callback != null) {
SdlService.this.callback.onEnd();
@@ -139,20 +141,46 @@ public class SdlService {
}
@Override
- public LifecycleConfigurationUpdate managerShouldUpdateLifecycle(Language language) {
- String appName;
+ public LifecycleConfigurationUpdate managerShouldUpdateLifecycle(Language language, Language hmiLanguage) {
+ boolean isNeedUpdate = false;
+ String appName = APP_NAME;
+ String ttsName = APP_NAME;
switch (language) {
case ES_MX:
+ isNeedUpdate = true;
+ ttsName = APP_NAME_ES;
+ break;
+ case FR_CA:
+ isNeedUpdate = true;
+ ttsName = APP_NAME_FR;
+ break;
+ default:
+ break;
+ }
+ switch (hmiLanguage) {
+ case ES_MX:
+ isNeedUpdate = true;
appName = APP_NAME_ES;
break;
case FR_CA:
+ isNeedUpdate = true;
appName = APP_NAME_FR;
break;
default:
- return null;
+ break;
+ }
+ if (isNeedUpdate) {
+ Vector<TTSChunk> chunks = new Vector<>(Collections.singletonList(new TTSChunk(ttsName, SpeechCapabilities.TEXT)));
+ return new LifecycleConfigurationUpdate(appName, null, chunks, null);
+ } else {
+ return null;
}
+ }
- return new LifecycleConfigurationUpdate(appName, null, TTSChunkFactory.createSimpleTTSChunks(appName), null);
+ @Override
+ public boolean onSystemInfoReceived(SystemInfo systemInfo) {
+ //Check the SystemInfo object to ensure that the connection to the device should continue
+ return true;
}
};
@@ -196,14 +224,14 @@ public class SdlService {
VoiceCommand voiceCommand1 = new VoiceCommand(list1, new VoiceCommandSelectionListener() {
@Override
public void onVoiceCommandSelected() {
- Log.i(TAG, "Voice Command 1 triggered");
+ DebugTool.logInfo(TAG, "Voice Command 1 triggered");
}
});
VoiceCommand voiceCommand2 = new VoiceCommand(list2, new VoiceCommandSelectionListener() {
@Override
public void onVoiceCommandSelected() {
- Log.i(TAG, "Voice Command 2 triggered");
+ DebugTool.logInfo(TAG, "Voice Command 2 triggered");
}
});
@@ -221,51 +249,51 @@ public class SdlService {
// some voice commands
List<String> voice2 = Collections.singletonList("Cell two");
- MenuCell mainCell1 = new MenuCell("Test Cell 1 (speak)", livio, null, new MenuSelectionListener() {
+ MenuCell mainCell1 = new MenuCell("Test Cell 1 (speak)", "Secondary Text", "Tertiary Text", livio, livio, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
- Log.i(TAG, "Test cell 1 triggered. Source: " + trigger.toString());
+ DebugTool.logInfo(TAG, "Test cell 1 triggered. Source: " + trigger.toString());
showTest();
}
});
- MenuCell mainCell2 = new MenuCell("Test Cell 2", null, voice2, new MenuSelectionListener() {
+ MenuCell mainCell2 = new MenuCell("Test Cell 2", "Secondary Text", null, null, null, voice2, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
- Log.i(TAG, "Test cell 2 triggered. Source: " + trigger.toString());
+ DebugTool.logInfo(TAG, "Test cell 2 triggered. Source: " + trigger.toString());
}
});
// SUB MENU
- MenuCell subCell1 = new MenuCell("SubCell 1", null, null, new MenuSelectionListener() {
+ MenuCell subCell1 = new MenuCell("SubCell 1", null, null, null, null, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
- Log.i(TAG, "Sub cell 1 triggered. Source: " + trigger.toString());
+ DebugTool.logInfo(TAG, "Sub cell 1 triggered. Source: " + trigger.toString());
}
});
- MenuCell subCell2 = new MenuCell("SubCell 2", null, null, new MenuSelectionListener() {
+ MenuCell subCell2 = new MenuCell("SubCell 2", null, null, null, null, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
- Log.i(TAG, "Sub cell 2 triggered. Source: " + trigger.toString());
+ DebugTool.logInfo(TAG, "Sub cell 2 triggered. Source: " + trigger.toString());
}
});
// sub menu parent cell
- MenuCell mainCell3 = new MenuCell("Test Cell 3 (sub menu)", null, Arrays.asList(subCell1, subCell2));
+ MenuCell mainCell3 = new MenuCell("Test Cell 3 (sub menu)", null, null, MenuLayout.LIST, null, null, Arrays.asList(subCell1, subCell2));
- MenuCell mainCell4 = new MenuCell("Show Perform Interaction", null, null, new MenuSelectionListener() {
+ MenuCell mainCell4 = new MenuCell("Show Perform Interaction", null, null, null, null, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
showPerformInteraction();
}
});
- MenuCell mainCell5 = new MenuCell("Clear the menu", null, null, new MenuSelectionListener() {
+ MenuCell mainCell5 = new MenuCell("Clear the menu", null, null, null,null, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
- Log.i(TAG, "Clearing Menu. Source: " + trigger.toString());
+ DebugTool.logInfo(TAG, "Clearing Menu. Source: " + trigger.toString());
// Clear this thing
sdlManager.getScreenManager().setMenu(Collections.<MenuCell>emptyList());
showAlert("Menu Cleared");
@@ -280,7 +308,8 @@ public class SdlService {
* Will speak a sample welcome message
*/
private void performWelcomeSpeak() {
- sdlManager.sendRPC(new Speak(TTSChunkFactory.createSimpleTTSChunks(WELCOME_SPEAK)));
+ List<TTSChunk> chunks = Collections.singletonList(new TTSChunk(WELCOME_SPEAK, SpeechCapabilities.TEXT));
+ sdlManager.sendRPC(new Speak(chunks));
}
/**
@@ -297,7 +326,7 @@ public class SdlService {
@Override
public void onComplete(boolean success) {
if (success) {
- Log.i(TAG, "welcome show successful");
+ DebugTool.logInfo(TAG, "welcome show successful");
}
}
});
@@ -312,14 +341,21 @@ public class SdlService {
sdlManager.getScreenManager().setTextField2("");
sdlManager.getScreenManager().commit(null);
- sdlManager.sendRPC(new Speak(TTSChunkFactory.createSimpleTTSChunks(TEST_COMMAND_NAME)));
+ List<TTSChunk> chunks = Collections.singletonList(new TTSChunk(TEST_COMMAND_NAME, SpeechCapabilities.TEXT));
+ sdlManager.sendRPC(new Speak(chunks));
}
private void showAlert(String text) {
- Alert alert = new Alert();
- alert.setAlertText1(text);
- alert.setDuration(5000);
- sdlManager.sendRPC(alert);
+ AlertView.Builder builder = new AlertView.Builder();
+ builder.setText(text);
+ builder.setTimeout(5);
+ AlertView alertView = builder.build();
+ sdlManager.getScreenManager().presentAlert(alertView, new AlertCompletionListener() {
+ @Override
+ public void onComplete(boolean success, Integer tryAgainTime) {
+ DebugTool.logInfo(TAG, "Alert presented: "+ success);
+ }
+ });
}
public interface SdlServiceCallback {
@@ -346,7 +382,7 @@ public class SdlService {
@Override
public void onError(String error) {
- Log.e(TAG, "There was an error showing the perform interaction: " + error);
+ DebugTool.logError(TAG, "There was an error showing the perform interaction: " + error);
}
});
sdlManager.getScreenManager().presentChoiceSet(choiceSet, InteractionMode.MANUAL_ONLY);
diff --git a/javaEE/javaEE/README.md b/javaEE/javaEE/README.md
index 8e7c3699d..24f921bc4 100644
--- a/javaEE/javaEE/README.md
+++ b/javaEE/javaEE/README.md
@@ -8,10 +8,10 @@ To compile with the latest release of SDL JavaSE, include the following in your
```sh
repositories {
- jcenter()
+ mavenCentral()
}
dependencies {
- implementation 'com.smartdevicelink:sdl_java_se:4.+'
+ implementation 'com.smartdevicelink:sdl_java_se:5.1.0'
}
```
@@ -22,4 +22,4 @@ If you prefer making a JAR, simply call:
```sh
gradle build
```
-from within the project and a JAR should be generated in the `build/libs` folder \ No newline at end of file
+from within the project and a JAR should be generated in the `build/libs` folder
diff --git a/javaEE/javaEE/build.gradle b/javaEE/javaEE/build.gradle
index d2c90a1b4..1efaa324e 100644
--- a/javaEE/javaEE/build.gradle
+++ b/javaEE/javaEE/build.gradle
@@ -31,7 +31,7 @@ dependencies {
extraLibs 'org.mongodb:bson:4.0.5'
extraLibs 'androidx.annotation:annotation:1.1.0'
extraLibs 'org.java-websocket:Java-WebSocket:1.3.9'
- extraLibs 'com.livio.taskmaster:taskmaster:0.3.0'
+ //extraLibs 'com.livio.taskmaster:taskmaster:0.4.0'
configurations.api.extendsFrom(configurations.extraLibs)
}
diff --git a/javaEE/javaEE/libs/TaskMaster-0.5.jar b/javaEE/javaEE/libs/TaskMaster-0.5.jar
new file mode 100644
index 000000000..a7881051b
--- /dev/null
+++ b/javaEE/javaEE/libs/TaskMaster-0.5.jar
Binary files differ
diff --git a/javaSE/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java b/javaSE/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java
index 176d6010e..c106b03fa 100644
--- a/javaSE/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java
+++ b/javaSE/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java
@@ -32,11 +32,13 @@
package com.smartdevicelink.java;
+import com.smartdevicelink.managers.AlertCompletionListener;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.SdlManager;
import com.smartdevicelink.managers.SdlManagerListener;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.managers.lifecycle.LifecycleConfigurationUpdate;
+import com.smartdevicelink.managers.screen.AlertView;
import com.smartdevicelink.managers.screen.OnButtonListener;
import com.smartdevicelink.managers.screen.choiceset.ChoiceCell;
import com.smartdevicelink.managers.screen.choiceset.ChoiceSet;
@@ -52,6 +54,7 @@ import com.smartdevicelink.proxy.rpc.enums.*;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.SystemInfo;
import java.util.*;
@@ -170,6 +173,12 @@ public class SdlService {
return null;
}
}
+
+ @Override
+ public boolean onSystemInfoReceived(SystemInfo systemInfo) {
+ //Check the SystemInfo object to ensure that the connection to the device should continue
+ return true;
+ }
};
@@ -238,7 +247,7 @@ public class SdlService {
// some voice commands
List<String> voice2 = Collections.singletonList("Cell two");
- MenuCell mainCell1 = new MenuCell("Test Cell 1 (speak)", livio, null, new MenuSelectionListener() {
+ MenuCell mainCell1 = new MenuCell("Test Cell 1 (speak)", "Secondary Text", "Tertiary Text", livio, livio, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
DebugTool.logInfo(TAG, "Test cell 1 triggered. Source: " + trigger.toString());
@@ -246,7 +255,7 @@ public class SdlService {
}
});
- MenuCell mainCell2 = new MenuCell("Test Cell 2", null, voice2, new MenuSelectionListener() {
+ MenuCell mainCell2 = new MenuCell("Test Cell 2", "Secondary Text", null, null, null, voice2, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
DebugTool.logInfo(TAG, "Test cell 2 triggered. Source: " + trigger.toString());
@@ -255,14 +264,14 @@ public class SdlService {
// SUB MENU
- MenuCell subCell1 = new MenuCell("SubCell 1", null, null, new MenuSelectionListener() {
+ MenuCell subCell1 = new MenuCell("SubCell 1", null, null, null, null, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
DebugTool.logInfo(TAG, "Sub cell 1 triggered. Source: " + trigger.toString());
}
});
- MenuCell subCell2 = new MenuCell("SubCell 2", null, null, new MenuSelectionListener() {
+ MenuCell subCell2 = new MenuCell("SubCell 2", null, null, null, null, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
DebugTool.logInfo(TAG, "Sub cell 2 triggered. Source: " + trigger.toString());
@@ -270,16 +279,16 @@ public class SdlService {
});
// sub menu parent cell
- MenuCell mainCell3 = new MenuCell("Test Cell 3 (sub menu)", MenuLayout.LIST, null, Arrays.asList(subCell1, subCell2));
+ MenuCell mainCell3 = new MenuCell("Test Cell 3 (sub menu)", null, null, MenuLayout.LIST, null, null, Arrays.asList(subCell1, subCell2));
- MenuCell mainCell4 = new MenuCell("Show Perform Interaction", null, null, new MenuSelectionListener() {
+ MenuCell mainCell4 = new MenuCell("Show Perform Interaction", null, null, null, null, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
showPerformInteraction();
}
});
- MenuCell mainCell5 = new MenuCell("Clear the menu", null, null, new MenuSelectionListener() {
+ MenuCell mainCell5 = new MenuCell("Clear the menu", null, null, null,null, null, new MenuSelectionListener() {
@Override
public void onTriggered(TriggerSource trigger) {
DebugTool.logInfo(TAG, "Clearing Menu. Source: " + trigger.toString());
@@ -366,10 +375,16 @@ public class SdlService {
}
private void showAlert(String text) {
- Alert alert = new Alert();
- alert.setAlertText1(text);
- alert.setDuration(5000);
- sdlManager.sendRPC(alert);
+ AlertView.Builder builder = new AlertView.Builder();
+ builder.setText(text);
+ builder.setTimeout(5);
+ AlertView alertView = builder.build();
+ sdlManager.getScreenManager().presentAlert(alertView, new AlertCompletionListener() {
+ @Override
+ public void onComplete(boolean success, Integer tryAgainTime) {
+ DebugTool.logInfo(TAG, "Alert presented: "+ success);
+ }
+ });
}
public interface SdlServiceCallback {
diff --git a/javaSE/javaSE/README.md b/javaSE/javaSE/README.md
index e40e9ca4a..8198c8d77 100644
--- a/javaSE/javaSE/README.md
+++ b/javaSE/javaSE/README.md
@@ -8,10 +8,10 @@ To compile with the latest release of SDL JavaSE, include the following in your
```sh
repositories {
- jcenter()
+ mavenCentral()
}
dependencies {
- implementation 'com.smartdevicelink:sdl_java_se:4.+'
+ implementation 'com.smartdevicelink:sdl_java_se:5.1.0'
}
```
diff --git a/javaSE/javaSE/build.gradle b/javaSE/javaSE/build.gradle
index 09da6d5a6..ef93e9aad 100644
--- a/javaSE/javaSE/build.gradle
+++ b/javaSE/javaSE/build.gradle
@@ -32,7 +32,7 @@ dependencies {
extraLibs 'org.mongodb:bson:4.0.5'
extraLibs 'androidx.annotation:annotation:1.1.0'
extraLibs 'org.java-websocket:Java-WebSocket:1.3.9'
- extraLibs 'com.livio.taskmaster:taskmaster:0.3.0'
+ //extraLibs 'com.livio.taskmaster:taskmaster:0.4.0'
configurations.api.extendsFrom(configurations.extraLibs)
}
diff --git a/javaSE/javaSE/libs/TaskMaster-0.5.jar b/javaSE/javaSE/libs/TaskMaster-0.5.jar
new file mode 100644
index 000000000..a7881051b
--- /dev/null
+++ b/javaSE/javaSE/libs/TaskMaster-0.5.jar
Binary files differ
diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java
index 82bb06001..d3770a95c 100644
--- a/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java
+++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java
@@ -32,5 +32,5 @@ package com.smartdevicelink;
// THIS FILE IS AUTO GENERATED, DO NOT MODIFY!!
public final class BuildConfig {
- public static final String VERSION_NAME = "5.0.0";
+ public static final String VERSION_NAME = "5.1.0";
} \ No newline at end of file
diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java
index 09a495355..f95639ca6 100644
--- a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java
+++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java
@@ -34,11 +34,27 @@ package com.smartdevicelink.managers;
import androidx.annotation.NonNull;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.managers.screen.ScreenManager;
+import com.smartdevicelink.protocol.ISdlServiceListener;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.protocol.enums.SessionType;
+import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.rpc.RegisterAppInterfaceResponse;
+import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
+import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCRequestListener;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
import com.smartdevicelink.transport.enums.TransportType;
import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.Version;
+
+import java.util.List;
/**
* <strong>SDLManager</strong> <br>
@@ -178,4 +194,132 @@ public class SdlManager extends BaseSdlManager {
super(appId, appName, listener);
}
}
+ private ISdl _internalInterface = new ISdl() {
+ @Override
+ public void start() {
+ lifecycleManager.getInternalInterface(SdlManager.this).start();
+ }
+
+ @Override
+ public void stop() {
+ lifecycleManager.getInternalInterface(SdlManager.this).start();
+ }
+
+ @Override
+ public boolean isConnected() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).isConnected();
+ }
+
+ @Override
+ public void addServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addServiceListener(serviceType, sdlServiceListener);
+ }
+
+ @Override
+ public void removeServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).removeServiceListener(serviceType, sdlServiceListener);
+ }
+
+ @Override
+ public void startVideoService(VideoStreamingParameters parameters, boolean encrypted, boolean withPendingRestart) {
+ lifecycleManager.getInternalInterface(SdlManager.this).startVideoService(parameters, encrypted, withPendingRestart);
+ }
+
+ @Override
+ public void startAudioService(boolean encrypted) {
+ lifecycleManager.getInternalInterface(SdlManager.this).startAudioService(encrypted);
+ }
+
+ @Override
+ public void sendRPC(RPCMessage message) {
+ lifecycleManager.getInternalInterface(SdlManager.this).sendRPC(message);
+ }
+
+ @Override
+ public void sendRPCs(List<? extends RPCMessage> rpcs, OnMultipleRequestListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).sendRPCs(rpcs, listener);
+ }
+
+ @Override
+ public void sendSequentialRPCs(List<? extends RPCMessage> rpcs, OnMultipleRequestListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).sendSequentialRPCs(rpcs, listener);
+ }
+
+ @Override
+ public void addOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addOnRPCNotificationListener(notificationId, listener);
+ }
+
+ @Override
+ public boolean removeOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCNotificationListener(notificationId, listener);
+ }
+
+ @Override
+ public void addOnRPCRequestListener(FunctionID functionID, OnRPCRequestListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addOnRPCRequestListener(functionID, listener);
+ }
+
+ @Override
+ public boolean removeOnRPCRequestListener(FunctionID functionID, OnRPCRequestListener listener) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCRequestListener(functionID, listener);
+ }
+
+ @Override
+ public void addOnRPCListener(FunctionID responseId, OnRPCListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addOnRPCListener(responseId, listener);
+ }
+
+ @Override
+ public boolean removeOnRPCListener(FunctionID responseId, OnRPCListener listener) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCListener(responseId, listener);
+ }
+
+ @Override
+ public RegisterAppInterfaceResponse getRegisterAppInterfaceResponse() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getRegisterAppInterfaceResponse();
+ }
+
+ @Override
+ public boolean isTransportForServiceAvailable(SessionType serviceType) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).isTransportForServiceAvailable(serviceType);
+ }
+
+ @NonNull
+ @Override
+ public SdlMsgVersion getSdlMsgVersion() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getSdlMsgVersion();
+ }
+
+ @NonNull
+ @Override
+ public Version getProtocolVersion() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getProtocolVersion();
+ }
+
+ @Override
+ public long getMtu(SessionType serviceType) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getMtu(serviceType);
+ }
+
+ @Override
+ public void startRPCEncryption() {
+ lifecycleManager.getInternalInterface(SdlManager.this).startRPCEncryption();
+ }
+
+ @Override
+ public Taskmaster getTaskmaster() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getTaskmaster();
+ }
+
+ @Override
+ public SystemCapabilityManager getSystemCapabilityManager() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getSystemCapabilityManager();
+ }
+
+ @Override
+ public PermissionManager getPermissionManager() {
+ return permissionManager;
+ }
+ };
}
diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManagerListener.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManagerListener.java
index cf3e7d04c..fbe236f08 100644
--- a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManagerListener.java
+++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManagerListener.java
@@ -33,6 +33,7 @@ package com.smartdevicelink.managers;
import com.smartdevicelink.managers.lifecycle.LifecycleConfigurationUpdate;
import com.smartdevicelink.proxy.rpc.enums.Language;
+import com.smartdevicelink.util.SystemInfo;
public interface SdlManagerListener extends BaseSdlManagerListener {
@@ -67,4 +68,12 @@ public interface SdlManagerListener extends BaseSdlManagerListener {
* otherwise null to indicate that the language is not supported.
*/
LifecycleConfigurationUpdate managerShouldUpdateLifecycle(Language language, Language hmiLanguage);
+
+ /**
+ * A way to determine if this SDL session should continue to be active while
+ * connected to the determined system information of the vehicle.
+ * @param systemInfo systemInfo - the system information of the vehicle that this session is currently active on.
+ * @return Return true if this session should continue, false if the session should end
+ */
+ boolean onSystemInfoReceived(SystemInfo systemInfo);
}
diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/FileManager.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/FileManager.java
index a2a464c11..b53a734d5 100644
--- a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/FileManager.java
+++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/FileManager.java
@@ -34,31 +34,16 @@ package com.smartdevicelink.managers.file;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
-
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.file.filetypes.SdlFile;
-import com.smartdevicelink.proxy.rpc.PutFile;
import com.smartdevicelink.util.DebugTool;
-import com.smartdevicelink.util.FileUtls;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URI;
+import java.io.*;
/**
* <strong>FileManager</strong> <br>
- * <p>
* Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br>
- * <p>
- * The SDLFileManager uploads files and keeps track of all the uploaded files names during a session. <br>
- * <p>
- * We need to add the following struct: SDLFile<br>
- * <p>
- * It is broken down to these areas: <br>
- * <p>
- * 1. Getters <br>
- * 2. Deletion methods <br>
- * 3. Uploading Files / Artwork
+ * The FileManager uploads files and keeps track of all the uploaded files names during a session. <br>
*/
public class FileManager extends BaseFileManager {
@@ -74,76 +59,28 @@ public class FileManager extends BaseFileManager {
super(internalInterface, fileManagerConfig);
}
- /**
- * Creates and returns a PutFile request that would upload a given SdlFile
- *
- * @param file SdlFile with fileName and one of A) fileData, B) Uri, or C) resourceID set
- * @return a valid PutFile request if SdlFile contained a fileName and sufficient data
- */
@Override
- PutFile createPutFile(@NonNull final SdlFile file) {
- PutFile putFile = new PutFile();
- if (file.getName() == null) {
- throw new IllegalArgumentException("You must specify an file name in the SdlFile");
- } else {
- putFile.setSdlFileName(file.getName());
- }
+ InputStream openInputStreamWithFile(@NonNull SdlFile file) {
+ InputStream inputStream = null;
if (file.getFilePath() != null) {
- //Attempt to access the file via a path
- byte[] data = FileUtls.getFileData(file.getFilePath());
- if (data != null) {
- putFile.setFileData(data);
- } else {
- throw new IllegalArgumentException("File at path was empty");
+ try {
+ inputStream = new FileInputStream(file.getFilePath());
+ } catch (FileNotFoundException e) {
+ DebugTool.logError(TAG, String.format("File at %s cannot be found.", file.getFilePath()));
}
} else if (file.getURI() != null) {
- // Use URI to upload file
- byte[] data = contentsOfUri(file.getURI());
- if (data != null) {
- putFile.setFileData(data);
- } else {
- throw new IllegalArgumentException("Uri was empty");
+ try {
+ inputStream = file.getURI().toURL().openStream();
+ } catch (IOException e) {
+ DebugTool.logError(TAG, String.format("File at %s cannot be found.", file.getURI()));
}
} else if (file.getFileData() != null) {
- // Use file data (raw bytes) to upload file
- putFile.setFileData(file.getFileData());
+ inputStream = new ByteArrayInputStream(file.getFileData());
} else {
- throw new IllegalArgumentException("The SdlFile to upload does " +
- "not specify its resourceId, Uri, or file data");
- }
-
- if (file.getType() != null) {
- putFile.setFileType(file.getType());
+ DebugTool.logError(TAG, "The SdlFile to upload does not specify its path, URI, or file data");
}
- putFile.setPersistentFile(file.isPersistent());
- return putFile;
- }
-
-
- /**
- * Helper method to take Uri and turn it into byte array
- *
- * @param uri Uri for desired file
- * @return Resulting byte array
- */
- private byte[] contentsOfUri(URI uri) {
- InputStream is = null;
- try {
- is = uri.toURL().openStream();
- return contentsOfInputStream(is);
- } catch (IOException e) {
- DebugTool.logError(TAG, "Can't read from URI", e);
- return null;
- } finally {
- if (is != null) {
- try {
- is.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
+ return inputStream;
}
}
diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java
new file mode 100644
index 000000000..addcee13d
--- /dev/null
+++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java
@@ -0,0 +1,18 @@
+package com.smartdevicelink.managers.screen;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.file.FileManager;
+/**
+ * <strong>AlertManager</strong> <br>
+ * <p>
+ * Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br>
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class AlertManager extends BaseAlertManager {
+
+ public AlertManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
+ super(internalInterface, fileManager);
+ }
+}
diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicManager.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicManager.java
index b479e3ba5..575809538 100644
--- a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicManager.java
+++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/TextAndGraphicManager.java
@@ -54,10 +54,7 @@ class TextAndGraphicManager extends BaseTextAndGraphicManager {
@Override
SdlArtwork getBlankArtwork() {
if (blankArtwork == null) {
- blankArtwork = new SdlArtwork();
- blankArtwork.setType(FileType.GRAPHIC_PNG);
- blankArtwork.setName("blankArtwork");
- blankArtwork.setFileData(new byte[50]);
+ blankArtwork = new SdlArtwork("blankArtwork", FileType.GRAPHIC_PNG, new byte[50], true);
}
return blankArtwork;
}
diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/session/SdlSession.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/session/SdlSession.java
index 3fa84ed27..91df403f9 100644
--- a/javaSE/javaSE/src/main/java/com/smartdevicelink/session/SdlSession.java
+++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/session/SdlSession.java
@@ -42,6 +42,7 @@ import com.smartdevicelink.protocol.SdlProtocolBase;
import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.SystemInfo;
import com.smartdevicelink.util.Version;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -67,7 +68,11 @@ public class SdlSession extends BaseSdlSession {
if (serviceType != null && serviceType.eq(SessionType.RPC) && this.sessionId == -1) {
this.sessionId = sessionID;
- this.sessionListener.onSessionStarted(sessionID, version);
+ SystemInfo systemInfo = null;
+ if (version != null && version.isNewerThan(new Version(5, 4, 0)) >= 0) {
+ systemInfo = extractSystemInfo(packet);
+ }
+ this.sessionListener.onSessionStarted(sessionID, version, systemInfo);
}
if (isEncrypted) {
@@ -109,4 +114,4 @@ public class SdlSession extends BaseSdlSession {
}
}
-} \ No newline at end of file
+}
diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/util/Log.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/util/Log.java
index 2de00692c..b20dc4076 100644
--- a/javaSE/javaSE/src/main/java/com/smartdevicelink/util/Log.java
+++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/util/Log.java
@@ -2,18 +2,18 @@ package com.smartdevicelink.util;
public class Log {
public static void i(String tag, String message) {
- System.out.print("\r\nINFO: " + tag + " - " + message);
+ System.out.print("\r\nI: " + tag + " - " + message);
}
public static void w(String tag, String message) {
- System.out.print("\r\nWARN: " + tag + " - " + message);
+ System.out.print("\r\nW: " + tag + " - " + message);
}
public static void e(String tag, String message, Throwable t) {
if (t != null) {
- System.out.print("\r\nERROR: " + tag + " - " + message + " - " + t.getMessage());
+ System.err.print("\r\nE: " + tag + " - " + message + " - " + t.getMessage());
} else {
- System.out.print("\r\nERROR: " + tag + " - " + message);
+ System.err.print("\r\nE: " + tag + " - " + message);
}
}
} \ No newline at end of file