diff options
author | Henigan <rheniga1@MGC12Z921DLVCG.fbpld77.ford.com> | 2021-02-15 14:54:25 -0500 |
---|---|---|
committer | Henigan <rheniga1@MGC12Z921DLVCG.fbpld77.ford.com> | 2021-02-15 14:54:25 -0500 |
commit | 4362a69962530f41f3e043172b907497962d145f (patch) | |
tree | ba949e49b65fd1d5b6021fcb78952fe390542fcd /android/sdl_android | |
parent | 181f98ed9849e46c65cb39ec1db7d60f4e052d2b (diff) | |
parent | 3a84cea88ad75f1fa72152ac513f7198c758ab06 (diff) | |
download | sdl_android-4362a69962530f41f3e043172b907497962d145f.tar.gz |
Merge branch 'develop' into feature/issue_1246
Diffstat (limited to 'android/sdl_android')
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; |