summaryrefslogtreecommitdiff
path: root/android/sdl_android
diff options
context:
space:
mode:
authorHenigan <rheniga1@MGC12Z921DLVCG.fbpld77.ford.com>2021-02-15 14:54:25 -0500
committerHenigan <rheniga1@MGC12Z921DLVCG.fbpld77.ford.com>2021-02-15 14:54:25 -0500
commit4362a69962530f41f3e043172b907497962d145f (patch)
treeba949e49b65fd1d5b6021fcb78952fe390542fcd /android/sdl_android
parent181f98ed9849e46c65cb39ec1db7d60f4e052d2b (diff)
parent3a84cea88ad75f1fa72152ac513f7198c758ab06 (diff)
downloadsdl_android-4362a69962530f41f3e043172b907497962d145f.tar.gz
Merge branch 'develop' into feature/issue_1246
Diffstat (limited to 'android/sdl_android')
-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
59 files changed, 3991 insertions, 787 deletions
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;