summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xandroid/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java7
-rw-r--r--android/sdl_android/build.gradle2
-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/managers/SdlManagerTests.java6
-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.java9
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java5
-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/PreloadChoicesOperationTests.java14
-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/test/TestValues.java183
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java111
-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/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/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.java7
-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/TextFieldNameTests.java2
-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/OnVehicleDataTests.java48
-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/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/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/screen/TextAndGraphicManager.java5
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java55
-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.java15
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/util/IntegrationValidator.java17
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java12
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/ISdl.java7
-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.java377
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java49
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java10
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java14
-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/PreloadChoicesOperation.java11
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java1
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java25
-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/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.java20
-rw-r--r--base/src/main/java/com/smartdevicelink/protocol/enums/ControlFrameTags.java13
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/RPCStruct.java61
-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/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/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.java17
-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/SeekIndicatorType.java54
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/enums/TextFieldName.java17
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/rpc/enums/VehicleDataType.java11
-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.java67
-rw-r--r--base/src/main/java/com/smartdevicelink/util/SystemInfo.java68
-rw-r--r--javaEE/hello_sdl_java_ee/src/main/java/com/smartdevicelink/SdlService.java8
-rw-r--r--javaEE/javaEE/build.gradle2
-rw-r--r--javaSE/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java7
-rw-r--r--javaSE/javaSE/build.gradle2
-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/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
121 files changed, 8055 insertions, 1447 deletions
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..8767b70fa 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
@@ -47,6 +47,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 +240,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
diff --git a/android/sdl_android/build.gradle b/android/sdl_android/build.gradle
index 7419e64dd..378273a7a 100644
--- a/android/sdl_android/build.gradle
+++ b/android/sdl_android/build.gradle
@@ -43,7 +43,7 @@ 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 '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/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/managers/SdlManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java
index c263db59d..ca941fab6 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
@@ -23,6 +23,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 +115,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
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 52eac0063..95977e857 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
@@ -98,6 +98,7 @@ public class SystemCapabilityManagerTests {
videoStreamingCapability.setMaxBitrate(TestValues.GENERAL_INT);
videoStreamingCapability.setPreferredResolution(TestValues.GENERAL_IMAGERESOLUTION);
videoStreamingCapability.setSupportedFormats(TestValues.GENERAL_VIDEOSTREAMINGFORMAT_LIST);
+ videoStreamingCapability.setPreferredFPS(TestValues.GENERAL_INTEGER);
systemCapability.setCapabilityForType(SystemCapabilityType.VIDEO_STREAMING, videoStreamingCapability);
}
@@ -204,7 +205,7 @@ 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(), 36);
+ assertEquals(displayCapabilityList.get(0).getWindowCapabilities().get(0).getTextFields().size(), 38);
assertEquals(displayCapabilityList.get(0).getWindowCapabilities().get(0).getImageFields().size(), 18);
}
@@ -214,6 +215,7 @@ public class SystemCapabilityManagerTests {
vsCapability.setMaxBitrate(TestValues.GENERAL_INT);
vsCapability.setPreferredResolution(TestValues.GENERAL_IMAGERESOLUTION);
vsCapability.setSupportedFormats(TestValues.GENERAL_VIDEOSTREAMINGFORMAT_LIST);
+ vsCapability.setPreferredFPS(TestValues.GENERAL_INTEGER);
SystemCapability cap = new SystemCapability();
cap.setSystemCapabilityType(SystemCapabilityType.VIDEO_STREAMING);
@@ -1038,6 +1040,11 @@ public class SystemCapabilityManagerTests {
}
@Override
+ public long getMtu(SessionType serviceType) {
+ return 0;
+ }
+
+ @Override
public boolean isTransportForServiceAvailable(SessionType serviceType) {
return false;
}
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..7719e2996 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();
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/PreloadChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java
index db592dfb6..b8ab310ca 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;
@@ -63,7 +64,9 @@ 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 {
@@ -101,6 +104,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);
}
@@ -147,13 +154,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/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/test/TestValues.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java
index edf446b3c..eeafa2edb 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,104 +16,7 @@ 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.*;
import com.smartdevicelink.proxy.rpc.enums.AmbientLightStatus;
import com.smartdevicelink.proxy.rpc.enums.AppHMIType;
import com.smartdevicelink.proxy.rpc.enums.AppInterfaceUnregisteredReason;
@@ -137,6 +40,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;
@@ -181,6 +85,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;
@@ -366,6 +271,7 @@ 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 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";
@@ -405,6 +311,7 @@ public class TestValues {
public static final LightStatus GENERAL_LIGHTSTATUS = LightStatus.OFF;
public static final RadioBand GENERAL_RADIOBAND = RadioBand.AM;
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();
@@ -427,6 +334,20 @@ public class TestValues {
public static final DynamicUpdateCapabilities GENERAL_DYNAMICUPDATECAPABILITIES = new DynamicUpdateCapabilities();
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_SEAT_LOCATION = new SeatLocation();
+ public static final DoorStatusType GENERAL_DOOR_STATUS_TYPE = 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 +367,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;
@@ -591,6 +511,17 @@ public class TestValues {
public static final JSONObject JSON_DISPLAYCAPABILITY = new JSONObject();
public static final JSONArray JSON_DISPLAYCAPABILITY_LIST = new JSONArray();
public static final JSONObject JSON_DYNAMICUPDATECAPABILITIES = 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);
@@ -1154,7 +1085,46 @@ public class TestValues {
GENERAL_STABILITY_CONTROL_STATUS.setEscSystem(GENERAL_ESC_SYSTEM);
GENERAL_STABILITY_CONTROL_STATUS.setTrailerSwayControl(GENERAL_S_WAY_CONTROL);
+ // SEAT_OCCUPANCY
+ GENERAL_SEAT_LOCATION.setGrid(GENERAL_LOCATION_GRID);
+
+ GENERAL_SEAT_STATUS.setConditionActive(GENERAL_BOOLEAN);
+ GENERAL_SEAT_STATUS.setSeatLocation(GENERAL_SEAT_LOCATION);
+
+ 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_DOOR_STATUS_TYPE);
+ GENERAL_ROOF_STATUS_LIST.add(GENERAL_ROOF_STATUS);
+
+ GENERAL_GATE_STATUS.setLocation(GENERAL_GRID);
+ GENERAL_GATE_STATUS.setStatus(GENERAL_DOOR_STATUS_TYPE);
+ GENERAL_GATE_STATUS_LIST.add(GENERAL_GATE_STATUS);
+
+ GENERAL_DOOR_STATUS.setLocation(GENERAL_GRID);
+ GENERAL_DOOR_STATUS.setStatus(GENERAL_DOOR_STATUS_TYPE);
+ 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 {
+
+ JSON_OBJECT_GENERAL_SEATS_OCCUPIED.put(SeatStatus.KEY_SEAT_LOCATION, GENERAL_SEAT_LOCATION.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_SEAT_LOCATION.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 +1355,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_DOOR_STATUS_TYPE);
+ 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_DOOR_STATUS_TYPE);
+ JSON_DOOR_STATUS.put(DoorStatus.KEY_LOCATION, JSON_GRID);
+
+ JSON_GATE_STATUS.put(GateStatus.KEY_STATUS, GENERAL_DOOR_STATUS_TYPE);
+ 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..6db76934f 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
@@ -3910,6 +3910,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 +3946,98 @@ public class Validator {
}
return status1.getDataType().equals(status2.getDataType()) && status1.getResultCode().equals(status2.getResultCode());
}
+
+ 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/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..cae1001c3
--- /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_DOOR_STATUS_TYPE);
+ }
+
+ 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_DOOR_STATUS_TYPE, status);
+ }
+
+ public void testJson() {
+ JSONObject reference = new JSONObject();
+
+ try {
+ reference.put(RoofStatus.KEY_STATUS, TestValues.GENERAL_DOOR_STATUS_TYPE);
+ 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..7bb8da021
--- /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_DOOR_STATUS_TYPE);
+ }
+
+ 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_DOOR_STATUS_TYPE, status);
+ }
+
+ public void testJson() {
+ JSONObject reference = new JSONObject();
+
+ try {
+ reference.put(RoofStatus.KEY_STATUS, TestValues.GENERAL_DOOR_STATUS_TYPE);
+ 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/RoofStatusTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/RoofStatusTests.java
new file mode 100644
index 000000000..5d30cdb15
--- /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_DOOR_STATUS_TYPE);
+ 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_DOOR_STATUS_TYPE, status);
+ assertEquals(TestValues.MATCH, TestValues.GENERAL_WINDOW_STATE, windowState);
+ }
+
+ public void testJson() {
+ JSONObject reference = new JSONObject();
+
+ try {
+ reference.put(RoofStatus.KEY_STATUS, TestValues.GENERAL_DOOR_STATUS_TYPE);
+ 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..dddf3b2c0
--- /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_SEAT_LOCATION);
+ 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_SEAT_LOCATION, 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_SEAT_LOCATION.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..0799645b6 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
@@ -4,6 +4,7 @@ import com.smartdevicelink.marshal.JsonRPCMarshaller;
import com.smartdevicelink.proxy.rpc.ImageResolution;
import com.smartdevicelink.proxy.rpc.VideoStreamingCapability;
import com.smartdevicelink.proxy.rpc.VideoStreamingFormat;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
import com.smartdevicelink.test.JsonUtils;
import com.smartdevicelink.test.TestValues;
import com.smartdevicelink.test.Validator;
@@ -33,6 +34,7 @@ public class VideoStreamingCapabilityTests extends TestCase {
msg.setDiagonalScreenSize(TestValues.GENERAL_DOUBLE);
msg.setPixelPerInch(TestValues.GENERAL_DOUBLE);
msg.setScale(TestValues.GENERAL_DOUBLE);
+ msg.setPreferredFPS(TestValues.GENERAL_INTEGER);
}
/**
@@ -47,6 +49,7 @@ public class VideoStreamingCapabilityTests extends TestCase {
Double diagonalScreenSize = msg.getDiagonalScreenSize();
Double pixelPerInch = msg.getPixelPerInch();
Double scale = msg.getScale();
+ Integer preferredFPS = msg.getPreferredFPS();
// Valid Tests
assertEquals(TestValues.MATCH, (List<VideoStreamingFormat>) TestValues.GENERAL_VIDEOSTREAMINGFORMAT_LIST, format);
@@ -68,6 +71,7 @@ public class VideoStreamingCapabilityTests extends TestCase {
assertNull(TestValues.NULL, msg.getDiagonalScreenSize());
assertNull(TestValues.NULL, msg.getPixelPerInch());
assertNull(TestValues.NULL, msg.getScale());
+ assertNull(TestValues.NULL, msg.getPreferredFPS());
}
public void testJson() {
@@ -81,6 +85,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 +94,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);
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/TextFieldNameTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/enums/TextFieldNameTests.java
index 339ce371f..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
@@ -184,6 +184,8 @@ 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);
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/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/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/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/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/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..a7ed506ab 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
@@ -294,38 +294,35 @@ public class VideoStreamManager extends BaseVideoStreamManager {
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);
- }
+ // regardless of VideoStreamingParameters are specified or not, we should refer to VideoStreamingCapability.
+ if (majorProtocolVersion >= 5) {
+ if (internalInterface.getSystemCapabilityManager() != null) {
+ final VideoStreamingParameters params = ( parameters == null) ? new VideoStreamingParameters() : new VideoStreamingParameters(parameters);
+ internalInterface.getSystemCapabilityManager().getCapability(SystemCapabilityType.VIDEO_STREAMING, new OnSystemCapabilityListener() {
+ @Override
+ public void onCapabilityRetrieved(Object capability) {
+ params.update((VideoStreamingCapability) capability, vehicleMake); //Streaming parameters are ready time to stream
+ startStreaming(params, encrypted);
+ }
- @Override
- public void onError(String info) {
- stateMachine.transitionToState(StreamingStateMachine.ERROR);
- DebugTool.logError(TAG, "Error retrieving video streaming capability: " + info);
- }
- }, 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);
+ @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
+ VideoStreamingParameters params = (parameters == null) ? new VideoStreamingParameters() : new VideoStreamingParameters(parameters);
+ 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);
}
}
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..a8238323b 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
@@ -49,6 +49,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 +68,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 +93,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 +110,6 @@ public abstract class SdlRemoteDisplay extends Presentation {
if (mainView != null) {
mainView.invalidate();
}
-
- handler.postDelayed(this, REFRESH_RATE_MS);
}
};
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..44e6bca2f 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
@@ -52,6 +52,9 @@ public class IntegrationValidator {
private static final char CHECK_MARK = 0x2713;
private static final char FAIL_MARK = 0x2715;
+ //FIXME When the CI is stable with API 30 use Manifest.permission.QUERY_ALL_PACKAGES instead
+ private static final String QUERY_ALL_PACKAGES = "android.permission.QUERY_ALL_PACKAGES";
+
public static final int FLAG_SKIP_ROUTER_SERVICE_CHECK = 0x01;
@@ -62,7 +65,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 +79,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) {
@@ -107,6 +117,9 @@ public class IntegrationValidator {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
permissionList.add(Manifest.permission.FOREGROUND_SERVICE);
}
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) {
+ permissionList.add(QUERY_ALL_PACKAGES);
+ }
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
String[] permissionInfos = packageInfo.requestedPermissions;
diff --git a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
index 0348cc1e7..6730ee3eb 100644
--- a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
@@ -63,6 +63,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 +78,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 +150,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
diff --git a/base/src/main/java/com/smartdevicelink/managers/ISdl.java b/base/src/main/java/com/smartdevicelink/managers/ISdl.java
index 6d9a3c50f..e12880561 100644
--- a/base/src/main/java/com/smartdevicelink/managers/ISdl.java
+++ b/base/src/main/java/com/smartdevicelink/managers/ISdl.java
@@ -213,6 +213,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();
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..c05aac5d0
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java
@@ -0,0 +1,377 @@
+/*
+ * 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);
+ 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);
+
+ 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 e63bda22c..e3b7a90a2 100644
--- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java
@@ -87,6 +87,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 +98,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 +129,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 +171,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();
@@ -377,6 +384,23 @@ abstract class BaseLifecycleManager {
clean();
return;
}
+ 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;
+ }
+ }
+ }
processRaiResponse(raiResponse);
systemCapabilityManager.parseRAIResponse(raiResponse);
break;
@@ -877,7 +901,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()));
@@ -885,6 +909,18 @@ 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 (appConfig != null) {
appConfig.prepare();
@@ -1042,6 +1078,11 @@ abstract class BaseLifecycleManager {
}
@Override
+ public long getMtu(SessionType serviceType) {
+ return BaseLifecycleManager.this.session.getMtu(serviceType);
+ }
+
+ @Override
public void startRPCEncryption() {
BaseLifecycleManager.this.startRPCEncryption();
}
@@ -1235,6 +1276,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/screen/BaseSoftButtonManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java
index 10d24c765..c2c9cc52e 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);
}
}
@@ -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();
@@ -384,7 +384,7 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
}
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 +467,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/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/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/PreloadChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java
index 6bf0b7d25..c857f0878 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
@@ -261,20 +261,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..d0fb36bd1 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);
}
});
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 0cd09dfb1..e05bbc441 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
@@ -759,7 +759,7 @@ abstract class BaseMenuManager extends BaseSubManager {
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) {
@@ -770,18 +770,23 @@ abstract class BaseMenuManager extends BaseSubManager {
return artworks;
}
+ private boolean shouldRPCsIncludeImages(List<MenuCell> cells) {
+ for (MenuCell cell : cells) {
+ SdlArtwork artwork = cell.getIcon();
+ if (artwork != null && !artwork.isStaticIcon() && fileManager.get() != null && !fileManager.get().hasUploadedFile(artwork)) {
+ return false;
+ } else if (cell.getSubCells() != null && cell.getSubCells().size() > 0) {
+ return shouldRPCsIncludeImages(cell.getSubCells());
+ }
+ }
+ return true;
+ }
+
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean supportsImages() {
return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.cmdIcon);
}
- private boolean artworkNeedsUpload(SdlArtwork artwork) {
- if (fileManager.get() != null) {
- return (artwork != null && !fileManager.get().hasUploadedFile(artwork) && !artwork.isStaticIcon());
- }
- return false;
- }
-
// IDs
private void updateIdsOnDynamicCells(List<MenuCell> dynamicCells) {
@@ -1138,7 +1143,7 @@ abstract class BaseMenuManager extends BaseSubManager {
List<RPCRequest> mainMenuCommands;
final List<RPCRequest> subMenuCommands;
- if (findAllArtworksToBeUploadedFromCells(menu).size() > 0 || !supportsImages()) {
+ if (!shouldRPCsIncludeImages(menu) || !supportsImages()) {
// Send artwork-less menu
mainMenuCommands = mainMenuCommandsForCells(menu, false);
subMenuCommands = subMenuCommandsForCells(menu, false);
@@ -1243,7 +1248,7 @@ abstract class BaseMenuManager extends BaseSubManager {
List<RPCRequest> mainMenuCommands;
- if (findAllArtworksToBeUploadedFromCells(adds).size() > 0 || !supportsImages()) {
+ if (!shouldRPCsIncludeImages(adds) || !supportsImages()) {
// Send artwork-less menu
mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, false);
} else {
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/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..d9e437548 100644
--- a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java
+++ b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java
@@ -85,7 +85,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 +103,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 +213,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;
@@ -708,6 +710,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 +1196,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 +1264,7 @@ public class SdlProtocolBase {
requestedSession = false;
activeTransports.clear();
+ serviceStartedOnTransport.clear();
iSdlProtocol.onTransportDisconnected(info, primaryTransportAvailable, transportConfig);
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/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/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..c040af0cd 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
+ * @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
+ * @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
+ * @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
+ * @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/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..b7744a3e3 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
+ * @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/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..1263a47e6 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
+ * @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..7ea19c858 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
+ * @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..e607711d0 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
+ * @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..199bd3999 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/rpc/VideoStreamingCapability.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/VideoStreamingCapability.java
@@ -49,6 +49,7 @@ 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";
public VideoStreamingCapability() {
}
@@ -180,4 +181,20 @@ public class VideoStreamingCapability extends RPCStruct {
setValue(KEY_SCALE, scale);
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;
+ }
}
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/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/TextFieldName.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/enums/TextFieldName.java
index d4fa18ab1..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
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..ad619e46a 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,7 +62,12 @@ public enum VehicleDataType {
*/
VEHICLEDATA_FUELCONSUMPTION,
/**
+ * @since SmartDeviceLink 7.1.0
+ */
+ VEHICLEDATA_CLIMATEDATA,
+ /**
* Notifies EXTERNTEMPData may be subscribed
+ * @deprecated since SmartDeviceLink 7.1.0
*/
VEHICLEDATA_EXTERNTEMP,
/**
@@ -185,7 +190,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..0424639c5 100644
--- a/base/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java
+++ b/base/src/main/java/com/smartdevicelink/streaming/video/VideoStreamingParameters.java
@@ -66,6 +66,7 @@ public class VideoStreamingParameters {
private int interval;
private ImageResolution resolution;
private VideoStreamingFormat format;
+ private boolean stableFrameRate;
public VideoStreamingParameters() {
displayDensity = DEFAULT_DENSITY;
@@ -78,16 +79,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 +153,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 +166,7 @@ public class VideoStreamingParameters {
if (params.format != null) {
this.format = params.format;
}
+ this.stableFrameRate = params.stableFrameRate;
}
}
@@ -143,15 +181,23 @@ 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;
+ // For resolution and scale, the capability values should be taken rather than parameters specified by developers.
if (capability.getScale() != null) {
scale = capability.getScale();
}
ImageResolution resolution = capability.getPreferredResolution();
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 +211,17 @@ 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,7 +286,15 @@ public class VideoStreamingParameters {
return resolution;
}
- @Override
+ public boolean isStableFrameRate() {
+ return stableFrameRate;
+ }
+
+ public void setStableFrameRate(boolean isStable) {
+ stableFrameRate = isStable;
+ }
+
+ @Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("VideoStreamingParams - format: {");
@@ -250,6 +311,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/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/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..3e1b5ba0a 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
@@ -55,6 +55,8 @@ 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.*;
@@ -154,6 +156,12 @@ public class SdlService {
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;
+ }
};
diff --git a/javaEE/javaEE/build.gradle b/javaEE/javaEE/build.gradle
index d2c90a1b4..436ff8f9f 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/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..d4eef253b 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
@@ -52,6 +52,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 +171,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;
+ }
};
diff --git a/javaSE/javaSE/build.gradle b/javaSE/javaSE/build.gradle
index 09da6d5a6..3bf55e8c9 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/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/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