summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoey Grover <joeygrover@gmail.com>2018-09-27 14:59:08 -0400
committerJoey Grover <joeygrover@gmail.com>2018-09-27 14:59:08 -0400
commit0b894d7e2beb4c9f82f4b609b9e65620039f010c (patch)
tree02d2e308671d39b50286bdedd45f156bdc2e4c11
parent92d5b306c210987a0dfe8eb49dcc9475c2faded8 (diff)
parent101d980aece2639713331c5f441faa0efeb6efe1 (diff)
downloadsdl_android-feature/issue_654_audio_stream_review_fix.tar.gz
Merge branch 'feature/issue_782_Android_Managers' of https://github.com/smartdevicelink/sdl_android into feature/issue_654_audio_stream_review_fixfeature/issue_654_audio_stream_review_fix
# Conflicts: # sdl_android/src/main/java/com/smartdevicelink/api/SdlManager.java
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/api/SdlManagerTests.java2
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/api/screen/ScreenManagerTests.java122
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/api/screen/SoftButtonManagerTests.java239
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/api/screen/TextAndGraphicManagerTests.java545
-rw-r--r--sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java55
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/api/BaseSubManager.java2
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/api/SdlManager.java58
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/api/datatypes/SdlArtwork.java15
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/api/datatypes/SdlFile.java23
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/api/screen/ScreenManager.java361
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/api/screen/SoftButtonManager.java570
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/api/screen/SoftButtonObject.java271
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/api/screen/SoftButtonState.java97
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/api/screen/TextAndGraphicManager.java894
-rw-r--r--sdl_android/src/main/java/com/smartdevicelink/proxy/rpc/listeners/OnMultipleRequestListener.java3
-rw-r--r--sdl_android/src/main/res/drawable-mdpi/transparent.pngbin0 -> 980 bytes
16 files changed, 3219 insertions, 38 deletions
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/api/SdlManagerTests.java b/sdl_android/src/androidTest/java/com/smartdevicelink/api/SdlManagerTests.java
index af9d38b38..e5de44bbd 100644
--- a/sdl_android/src/androidTest/java/com/smartdevicelink/api/SdlManagerTests.java
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/api/SdlManagerTests.java
@@ -170,7 +170,7 @@ public class SdlManagerTests extends AndroidTestCase {
sdlManager.getAudioStreamManager().transitionToState(BaseSubManager.READY);
// manager.getVideoStreamingManager().transitionToState(BaseSubManager.READY);
// manager.getLockScreenManager().transitionToState(BaseSubManager.READY);
- // manager.getScreenManager().transitionToState(BaseSubManager.READY);
+ sdlManager.getScreenManager().transitionToState(BaseSubManager.READY);
sdlManager.getPermissionManager().transitionToState(BaseSubManager.READY);
sdlManager.getFileManager().transitionToState(BaseSubManager.READY);
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/api/screen/ScreenManagerTests.java b/sdl_android/src/androidTest/java/com/smartdevicelink/api/screen/ScreenManagerTests.java
new file mode 100644
index 000000000..553a57544
--- /dev/null
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/api/screen/ScreenManagerTests.java
@@ -0,0 +1,122 @@
+package com.smartdevicelink.api.screen;
+
+import com.smartdevicelink.api.BaseSubManager;
+import com.smartdevicelink.api.FileManager;
+import com.smartdevicelink.api.datatypes.SdlArtwork;
+import com.smartdevicelink.proxy.interfaces.ISdl;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.MetadataType;
+import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
+import com.smartdevicelink.test.utl.AndroidToolsTests;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.mockito.Mockito.mock;
+
+/**
+ * This is a unit test class for the SmartDeviceLink library manager class :
+ * {@link ScreenManager}
+ */
+public class ScreenManagerTests extends AndroidToolsTests{
+ private ScreenManager screenManager;
+ private SdlArtwork testArtwork;
+
+ @Override
+ public void setUp() throws Exception{
+ super.setUp();
+
+ ISdl internalInterface = mock(ISdl.class);
+ FileManager fileManager = mock(FileManager.class);
+ screenManager = new ScreenManager(internalInterface, fileManager);
+
+
+ testArtwork = new SdlArtwork("testFile", FileType.GRAPHIC_PNG, 1, false);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testInstantiation(){
+ assertNull(screenManager.getTextField1());
+ assertNull(screenManager.getTextField2());
+ assertNull(screenManager.getTextField3());
+ assertNull(screenManager.getTextField4());
+ assertNull(screenManager.getMediaTrackTextField());
+ assertNull(screenManager.getPrimaryGraphic());
+ assertNull(screenManager.getSecondaryGraphic());
+ assertEquals(screenManager.getTextAlignment(), TextAlignment.CENTERED);
+ assertNull(screenManager.getTextField1Type());
+ assertNull(screenManager.getTextField2Type());
+ assertNull(screenManager.getTextField3Type());
+ assertNull(screenManager.getTextField4Type());
+ assertTrue(screenManager.getSoftButtonObjects().isEmpty());
+ assertNull(screenManager.getSoftButtonObjectByName("test"));
+ assertNull(screenManager.getSoftButtonObjectById(1));
+ assertEquals(screenManager.getState(), BaseSubManager.READY);
+ }
+
+ public void testSetTextField() {
+ screenManager.setTextField1("It is");
+ screenManager.setTextField2("Wednesday");
+ screenManager.setTextField3("My");
+ screenManager.setTextField4("Dudes");
+ assertEquals(screenManager.getTextField1(), "It is");
+ assertEquals(screenManager.getTextField2(), "Wednesday");
+ assertEquals(screenManager.getTextField3(), "My");
+ assertEquals(screenManager.getTextField4(), "Dudes");
+ }
+
+ public void testMediaTrackTextFields() {
+ String songTitle = "Wild For The Night";
+ screenManager.setMediaTrackTextField(songTitle);
+ assertEquals(screenManager.getMediaTrackTextField(), songTitle);
+ }
+
+ public void testSetPrimaryGraphic() {
+ screenManager.setPrimaryGraphic(testArtwork);
+ assertEquals(screenManager.getPrimaryGraphic(), testArtwork);
+ }
+
+ public void testSetSecondaryGraphic() {
+ screenManager.setSecondaryGraphic(testArtwork);
+ assertEquals(screenManager.getSecondaryGraphic(), testArtwork);
+ }
+
+ public void testAlignment() {
+ screenManager.setTextAlignment(TextAlignment.LEFT_ALIGNED);
+ assertEquals(screenManager.getTextAlignment(), TextAlignment.LEFT_ALIGNED);
+ }
+
+ public void testSetTextFieldTypes() {
+ screenManager.setTextField1Type(MetadataType.MEDIA_TITLE);
+ screenManager.setTextField2Type(MetadataType.MEDIA_ALBUM);
+ screenManager.setTextField3Type(MetadataType.MEDIA_ARTIST);
+ screenManager.setTextField4Type(MetadataType.MEDIA_GENRE);
+ assertEquals(screenManager.getTextField1Type(), MetadataType.MEDIA_TITLE);
+ assertEquals(screenManager.getTextField2Type(), MetadataType.MEDIA_ALBUM);
+ assertEquals(screenManager.getTextField3Type(), MetadataType.MEDIA_ARTIST);
+ assertEquals(screenManager.getTextField4Type(), MetadataType.MEDIA_GENRE);
+ }
+
+ public void testSetSoftButtonObjects(){
+ // Create softButtonObject1
+ SoftButtonState softButtonState1 = new SoftButtonState("object1-state1", "it is", testArtwork);
+ SoftButtonState softButtonState2 = new SoftButtonState("object1-state2", "Wed", testArtwork);
+ SoftButtonObject softButtonObject1 = new SoftButtonObject("object1", Arrays.asList(softButtonState1, softButtonState2), softButtonState1.getName(),null);
+
+ // Create softButtonObject2
+ SoftButtonState softButtonState3 = new SoftButtonState("object2-state1", "my", testArtwork);
+ SoftButtonState softButtonState4 = new SoftButtonState("object2-state2", "dudes!", null);
+ SoftButtonObject softButtonObject2 = new SoftButtonObject("object2", Arrays.asList(softButtonState3, softButtonState4), softButtonState3.getName(), null);
+
+ List<SoftButtonObject> softButtonObjects = Arrays.asList(softButtonObject1, softButtonObject2);
+ screenManager.setSoftButtonObjects(Arrays.asList(softButtonObject1, softButtonObject2));
+ assertEquals(screenManager.getSoftButtonObjects(), softButtonObjects);
+ assertEquals(screenManager.getSoftButtonObjectByName("object2"), softButtonObject2);
+ assertEquals(screenManager.getSoftButtonObjectById(100), softButtonObject2);
+ }
+
+}
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/api/screen/SoftButtonManagerTests.java b/sdl_android/src/androidTest/java/com/smartdevicelink/api/screen/SoftButtonManagerTests.java
new file mode 100644
index 000000000..b6b1bb9ef
--- /dev/null
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/api/screen/SoftButtonManagerTests.java
@@ -0,0 +1,239 @@
+package com.smartdevicelink.api.screen;
+
+import android.test.AndroidTestCase;
+
+import com.smartdevicelink.api.CompletionListener;
+import com.smartdevicelink.api.FileManager;
+import com.smartdevicelink.api.MultipleFileCompletionListener;
+import com.smartdevicelink.api.datatypes.SdlArtwork;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.interfaces.ISdl;
+import com.smartdevicelink.proxy.rpc.Image;
+import com.smartdevicelink.proxy.rpc.OnHMIStatus;
+import com.smartdevicelink.proxy.rpc.Show;
+import com.smartdevicelink.proxy.rpc.SoftButton;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.HMILevel;
+import com.smartdevicelink.proxy.rpc.enums.ImageType;
+import com.smartdevicelink.proxy.rpc.enums.SoftButtonType;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
+import com.smartdevicelink.test.Validator;
+
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+
+/**
+ * This is a unit test class for the SmartDeviceLink library manager class :
+ * {@link SoftButtonManager}
+ */
+public class SoftButtonManagerTests extends AndroidTestCase {
+
+ private SoftButtonManager softButtonManager;
+ private boolean fileManagerUploadArtworksGotCalled;
+ private boolean internalInterfaceSendRPCRequestGotCalled;
+ private boolean softButtonMangerUpdateCompleted;
+ private SoftButtonObject softButtonObject1, softButtonObject2;
+ private SoftButtonState softButtonState1, softButtonState2, softButtonState3, softButtonState4;
+
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // When internalInterface.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, OnRPCNotificationListener) is called
+ // inside SoftButtonManager, respond with a fake HMILevel.HMI_FULL response to let the SoftButtonManager continue working.
+ ISdl internalInterface = mock(ISdl.class);
+ Answer<Void> onHMIStatusAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ OnRPCNotificationListener onHMIStatusListener = (OnRPCNotificationListener) args[1];
+ OnHMIStatus onHMIStatusFakeNotification = new OnHMIStatus();
+ onHMIStatusFakeNotification.setHmiLevel(HMILevel.HMI_FULL);
+ onHMIStatusListener.onNotified(onHMIStatusFakeNotification);
+ return null;
+ }
+ };
+ doAnswer(onHMIStatusAnswer).when(internalInterface).addOnRPCNotificationListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCNotificationListener.class));
+
+
+ // When fileManager.uploadArtworks() is called inside the SoftButtonManager, respond with
+ // a fake onComplete() callback to let the SoftButtonManager continue working.
+ FileManager fileManager = mock(FileManager.class);
+ Answer<Void> onFileManagerUploadAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ fileManagerUploadArtworksGotCalled = true;
+ Object[] args = invocation.getArguments();
+ MultipleFileCompletionListener multipleFileCompletionListener = (MultipleFileCompletionListener) args[1];
+ multipleFileCompletionListener.onComplete(null);
+ return null;
+ }
+ };
+ doAnswer(onFileManagerUploadAnswer).when(fileManager).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+
+
+ // Create softButtonManager
+ softButtonManager = new SoftButtonManager(internalInterface, fileManager);
+
+
+ // When internalInterface.sendRPCRequest() is called inside SoftButtonManager:
+ // 1) respond with a fake onResponse() callback to let the SoftButtonManager continue working
+ // 2) assert that the Show RPC values (ie: MainField1 & SoftButtons) that are created by the SoftButtonManager, match the ones that are provided by the developer
+ Answer<Void> onSendShowRPCAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ internalInterfaceSendRPCRequestGotCalled = true;
+ Object[] args = invocation.getArguments();
+ Show show = (Show) args[0];
+
+ show.getOnRPCResponseListener().onResponse(0, null);
+
+ assertEquals(show.getMainField1(), softButtonManager.getCurrentMainField1());
+ assertEquals(show.getSoftButtons().size(), softButtonManager.createSoftButtonsForCurrentState().size());
+
+ return null;
+ }
+ };
+ doAnswer(onSendShowRPCAnswer).when(internalInterface).sendRPCRequest(any(Show.class));
+
+
+ // Create soft button objects
+ softButtonState1 = new SoftButtonState("object1-state1", "o1s1", new SdlArtwork("image1", FileType.GRAPHIC_PNG, 1, true));
+ softButtonState2 = new SoftButtonState("object1-state2", "o1s2", new SdlArtwork("image2", FileType.GRAPHIC_PNG, 2, true));
+ softButtonObject1 = new SoftButtonObject("object1", Arrays.asList(softButtonState1, softButtonState2), softButtonState1.getName(), null);
+ softButtonState3 = new SoftButtonState("object2-state1", "o2s1", null);
+ softButtonState4 = new SoftButtonState("object2-state2", "o2s2", new SdlArtwork("image3", FileType.GRAPHIC_PNG, 3, true));
+ softButtonObject2 = new SoftButtonObject("object2", Arrays.asList(softButtonState3, softButtonState4), softButtonState3.getName(), null);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testSoftButtonManagerUpdate() {
+ // Reset the boolean variables
+ fileManagerUploadArtworksGotCalled = false;
+ internalInterfaceSendRPCRequestGotCalled = false;
+ softButtonMangerUpdateCompleted = false;
+
+
+ // Test batch update
+ softButtonManager.setBatchUpdates(true);
+ List<SoftButtonObject> softButtonObjects = Arrays.asList(softButtonObject1, softButtonObject2);
+ softButtonManager.setSoftButtonObjects(Arrays.asList(softButtonObject1, softButtonObject2));
+ softButtonManager.setBatchUpdates(false);
+ softButtonManager.update(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ softButtonMangerUpdateCompleted = true;
+ }
+ });
+
+
+ // Test single update, setCurrentMainField1, and transitionToNextState
+ softButtonManager.setCurrentMainField1("It is Wednesday my dudes!");
+ softButtonObject1.transitionToNextState();
+
+
+ // Check that everything got called as expected
+ assertTrue("FileManager.uploadArtworks() did not get called", fileManagerUploadArtworksGotCalled);
+ assertTrue("InternalInterface.sendRPCRequest() did not get called", internalInterfaceSendRPCRequestGotCalled);
+ assertTrue("SoftButtonManger update onComplete() did not get called", softButtonMangerUpdateCompleted);
+
+
+ // Test getSoftButtonObjects
+ assertEquals("Returned softButtonObjects value doesn't match the expected value", softButtonObjects, softButtonManager.getSoftButtonObjects());
+ }
+
+ public void testSoftButtonManagerGetSoftButtonObject() {
+ softButtonManager.setSoftButtonObjects(Arrays.asList(softButtonObject1, softButtonObject2));
+
+
+ // Test get by valid name
+ assertEquals("Returned SoftButtonObject doesn't match the expected value", softButtonObject2, softButtonManager.getSoftButtonObjectByName("object2"));
+
+
+ // Test get by invalid name
+ assertNull("Returned SoftButtonObject doesn't match the expected value", softButtonManager.getSoftButtonObjectByName("object300"));
+
+
+ // Test get by valid id
+ assertEquals("Returned SoftButtonObject doesn't match the expected value", softButtonObject2, softButtonManager.getSoftButtonObjectById(100));
+
+
+ // Test get by invalid id
+ assertNull("Returned SoftButtonObject doesn't match the expected value", softButtonManager.getSoftButtonObjectById(500));
+ }
+
+ public void testSoftButtonState(){
+ // Test SoftButtonState.getName()
+ String nameExpectedValue = "object1-state1";
+ assertEquals("Returned state name doesn't match the expected value", nameExpectedValue, softButtonState1.getName());
+
+
+ // Test SoftButtonState.getArtwork()
+ SdlArtwork artworkExpectedValue = new SdlArtwork("image1", FileType.GRAPHIC_PNG, 1, true);
+ assertTrue("Returned SdlArtwork doesn't match the expected value", Validator.validateSdlFile(artworkExpectedValue, softButtonState1.getArtwork()));
+
+
+ // Test SoftButtonState.getSoftButton()
+ SoftButton softButtonExpectedValue = new SoftButton(SoftButtonType.SBT_BOTH, 0);
+ softButtonExpectedValue.setText("o1s1");
+ softButtonExpectedValue.setImage(new Image(artworkExpectedValue.getName(), ImageType.DYNAMIC));
+ assertTrue("Returned SoftButton doesn't match the expected value", Validator.validateSoftButton(softButtonExpectedValue, softButtonState1.getSoftButton()));
+ }
+
+ public void testSoftButtonObject(){
+ // Test SoftButtonObject.getName()
+ assertEquals("Returned object name doesn't match the expected value", "object1", softButtonObject1.getName());
+
+
+ // Test SoftButtonObject.getCurrentState()
+ assertEquals("Returned current state doesn't match the expected value", softButtonState1, softButtonObject1.getCurrentState());
+
+
+ // Test SoftButtonObject.getCurrentStateName()
+ assertEquals("Returned current state name doesn't match the expected value", softButtonState1.getName(), softButtonObject1.getCurrentStateName());
+
+
+ // Test SoftButtonObject.getButtonId()
+ assertEquals("Returned button Id doesn't match the expected value", 0, softButtonObject1.getButtonId());
+
+
+ // Test SoftButtonObject.getCurrentStateSoftButton()
+ SoftButton softButtonExpectedValue = new SoftButton(SoftButtonType.SBT_TEXT, 0);
+ softButtonExpectedValue.setText("o2s1");
+ assertTrue("Returned current state SoftButton doesn't match the expected value", Validator.validateSoftButton(softButtonExpectedValue, softButtonObject2.getCurrentStateSoftButton()));
+
+
+ // Test SoftButtonObject.getStates()
+ assertEquals("Returned object states doesn't match the expected value", Arrays.asList(softButtonState1, softButtonState2), softButtonObject1.getStates());
+
+
+ // Test SoftButtonObject.transitionToNextState()
+ assertEquals(softButtonState1, softButtonObject1.getCurrentState());
+ softButtonObject1.transitionToNextState();
+ assertEquals(softButtonState2, softButtonObject1.getCurrentState());
+
+
+ // Test SoftButtonObject.transitionToStateByName() - transitioning to a none existing state
+ boolean success = softButtonObject1.transitionToStateByName("none existing name");
+ assertFalse(success);
+
+
+ // Test SoftButtonObject.transitionToStateByName() - transitioning to an existing state
+ success = softButtonObject1.transitionToStateByName("object1-state1");
+ assertTrue(success);
+ assertEquals(softButtonState1, softButtonObject1.getCurrentState());
+ }
+}
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/api/screen/TextAndGraphicManagerTests.java b/sdl_android/src/androidTest/java/com/smartdevicelink/api/screen/TextAndGraphicManagerTests.java
new file mode 100644
index 000000000..fc6162952
--- /dev/null
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/api/screen/TextAndGraphicManagerTests.java
@@ -0,0 +1,545 @@
+package com.smartdevicelink.api.screen;
+
+import android.content.Context;
+import android.net.Uri;
+
+import com.smartdevicelink.api.BaseSubManager;
+import com.smartdevicelink.api.FileManager;
+import com.smartdevicelink.api.datatypes.SdlArtwork;
+import com.smartdevicelink.proxy.interfaces.ISdl;
+import com.smartdevicelink.proxy.rpc.DisplayCapabilities;
+import com.smartdevicelink.proxy.rpc.MetadataTags;
+import com.smartdevicelink.proxy.rpc.Show;
+import com.smartdevicelink.proxy.rpc.TextField;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.HMILevel;
+import com.smartdevicelink.proxy.rpc.enums.MetadataType;
+import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
+import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
+import com.smartdevicelink.test.utl.AndroidToolsTests;
+
+import org.json.JSONException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.mockito.Mockito.mock;
+
+/**
+ * This is a unit test class for the SmartDeviceLink library manager class :
+ * {@link com.smartdevicelink.api.screen.TextAndGraphicManager}
+ */
+public class TextAndGraphicManagerTests extends AndroidToolsTests{
+
+ // SETUP / HELPERS
+ private TextAndGraphicManager textAndGraphicManager;
+ private SdlArtwork testArtwork;
+
+ @Override
+ public void setUp() throws Exception{
+ super.setUp();
+ Context mTestContext = this.getContext();
+ // mock things
+ ISdl internalInterface = mock(ISdl.class);
+ FileManager fileManager = mock(FileManager.class);
+ SoftButtonManager softButtonManager = mock(SoftButtonManager.class);
+
+ testArtwork = new SdlArtwork();
+ testArtwork.setName("testFile");
+ Uri uri = Uri.parse("android.resource://" + mTestContext.getPackageName() + "/drawable/ic_sdl");
+ testArtwork.setUri(uri);
+ testArtwork.setType(FileType.GRAPHIC_PNG);
+
+ textAndGraphicManager = new TextAndGraphicManager(internalInterface, fileManager, softButtonManager);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private DisplayCapabilities getDisplayCapability(int numberOfMainFields){
+
+ TextField mainField1 = new TextField();
+ mainField1.setName(TextFieldName.mainField1);
+ TextField mainField2 = new TextField();
+ mainField2.setName(TextFieldName.mainField2);
+ TextField mainField3 = new TextField();
+ mainField3.setName(TextFieldName.mainField3);
+ TextField mainField4 = new TextField();
+ mainField4.setName(TextFieldName.mainField4);
+
+ List<TextField> textFieldList = new ArrayList<>();
+
+ textFieldList.add(mainField1);
+ textFieldList.add(mainField2);
+ textFieldList.add(mainField3);
+ textFieldList.add(mainField4);
+
+ List<TextField> returnList = new ArrayList<>();
+
+ if (numberOfMainFields > 0){
+ for (int i = 0; i < numberOfMainFields; i++) {
+ returnList.add(textFieldList.get(i));
+ }
+ }
+
+ DisplayCapabilities displayCapabilities = new DisplayCapabilities();
+ displayCapabilities.setTextFields(returnList);
+
+ return displayCapabilities;
+ }
+
+ public void testInstantiation(){
+
+ assertNull(textAndGraphicManager.getTextField1());
+ assertNull(textAndGraphicManager.getTextField2());
+ assertNull(textAndGraphicManager.getTextField3());
+ assertNull(textAndGraphicManager.getTextField4());
+ assertNull(textAndGraphicManager.getMediaTrackTextField());
+ assertNull(textAndGraphicManager.getPrimaryGraphic());
+ assertNull(textAndGraphicManager.getSecondaryGraphic());
+ assertEquals(textAndGraphicManager.getTextAlignment(), TextAlignment.CENTERED);
+ assertNull(textAndGraphicManager.getTextField1Type());
+ assertNull(textAndGraphicManager.getTextField2Type());
+ assertNull(textAndGraphicManager.getTextField3Type());
+ assertNull(textAndGraphicManager.getTextField4Type());
+
+ assertNotNull(textAndGraphicManager.currentScreenData);
+ assertNull(textAndGraphicManager.inProgressUpdate);
+ assertNull(textAndGraphicManager.queuedImageUpdate);
+ assertFalse(textAndGraphicManager.hasQueuedUpdate);
+ assertNull(textAndGraphicManager.displayCapabilities);
+ assertEquals(textAndGraphicManager.currentHMILevel, HMILevel.HMI_NONE);
+ assertFalse(textAndGraphicManager.isDirty);
+ assertEquals(textAndGraphicManager.getState(), BaseSubManager.SETTING_UP);
+ }
+
+ public void testGetMainLines(){
+
+ // We want to test that the looping works. By default, it will return 4 if display cap is null
+
+ // Null test
+ assertEquals(textAndGraphicManager.getNumberOfLines(), 4);
+
+ // The tests.java class has an example of this, but we must build it to do what
+ // we need it to do. Build display cap w/ 3 main fields and test that it returns 3
+ textAndGraphicManager.displayCapabilities = getDisplayCapability(3);
+ assertEquals(textAndGraphicManager.getNumberOfLines(), 3);
+ }
+
+ public void testAssemble1Line(){
+
+ Show inputShow = new Show();
+
+ // Force it to return display with support for only 1 line of text
+ textAndGraphicManager.displayCapabilities = getDisplayCapability(1);
+
+ textAndGraphicManager.setTextField1("It is");
+ textAndGraphicManager.setTextField1Type(MetadataType.HUMIDITY);
+
+ Show assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is");
+
+ // test tags (just 1)
+ MetadataTags tags = assembledShow.getMetadataTags();
+ List<MetadataType> tagsList = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ assertEquals(tags.getMainField1(), tagsList);
+
+ textAndGraphicManager.setTextField2("Wednesday");
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is - Wednesday");
+
+ textAndGraphicManager.setTextField3("My");
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is - Wednesday - My");
+
+ textAndGraphicManager.setTextField4("Dudes");
+ textAndGraphicManager.setTextField4Type(MetadataType.CURRENT_TEMPERATURE);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is - Wednesday - My - Dudes");
+
+ // test tags
+ tags = assembledShow.getMetadataTags();
+ tagsList = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ tagsList.add(MetadataType.CURRENT_TEMPERATURE);
+ assertEquals(tags.getMainField1(), tagsList);
+
+ // For some obscurity, lets try setting just fields 2 and 4 for a 1 line display
+ textAndGraphicManager.setTextField1(null);
+ textAndGraphicManager.setTextField3(null);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "Wednesday - Dudes");
+ }
+
+ public void testAssemble2Lines() {
+
+ Show inputShow = new Show();
+
+ // Force it to return display with support for only 2 lines of text
+ textAndGraphicManager.displayCapabilities = getDisplayCapability(2);
+
+ textAndGraphicManager.setTextField1("It is");
+ textAndGraphicManager.setTextField1Type(MetadataType.HUMIDITY);
+
+ Show assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is");
+
+ // test tags
+ MetadataTags tags = assembledShow.getMetadataTags();
+ List<MetadataType> tagsList = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ assertEquals(tags.getMainField1(), tagsList);
+
+ textAndGraphicManager.setTextField2("Wednesday");
+ textAndGraphicManager.setTextField2Type(MetadataType.CURRENT_TEMPERATURE);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is");
+ assertEquals(assembledShow.getMainField2(), "Wednesday");
+
+ // test tags
+ tags = assembledShow.getMetadataTags();
+ tagsList = new ArrayList<>();
+ List<MetadataType> tagsList2 = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ tagsList2.add(MetadataType.CURRENT_TEMPERATURE);
+ assertEquals(tags.getMainField1(), tagsList);
+ assertEquals(tags.getMainField2(), tagsList2);
+
+ textAndGraphicManager.setTextField3("My");
+ textAndGraphicManager.setTextField3Type(MetadataType.MEDIA_ALBUM);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is - Wednesday");
+ assertEquals(assembledShow.getMainField2(), "My");
+
+ // test tags
+ tags = assembledShow.getMetadataTags();
+ tagsList = new ArrayList<>();
+ tagsList2 = new ArrayList<>();
+ tagsList.add(MetadataType.CURRENT_TEMPERATURE);
+ tagsList.add(MetadataType.HUMIDITY);
+ tagsList2.add(MetadataType.MEDIA_ALBUM);
+ assertEquals(tags.getMainField1(), tagsList);
+ assertEquals(tags.getMainField2(), tagsList2);
+
+ textAndGraphicManager.setTextField4("Dudes");
+ textAndGraphicManager.setTextField4Type(MetadataType.MEDIA_STATION);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is - Wednesday");
+ assertEquals(assembledShow.getMainField2(), "My - Dudes");
+
+ // test tags
+ tags = assembledShow.getMetadataTags();
+ tagsList = new ArrayList<>();
+ tagsList2 = new ArrayList<>();
+ tagsList.add(MetadataType.CURRENT_TEMPERATURE);
+ tagsList.add(MetadataType.HUMIDITY);
+ tagsList2.add(MetadataType.MEDIA_STATION);
+ tagsList2.add(MetadataType.MEDIA_ALBUM);
+ assertEquals(tags.getMainField1(), tagsList);
+ assertEquals(tags.getMainField2(), tagsList2);
+
+ // For some obscurity, lets try setting just fields 2 and 4 for a 2 line display
+ textAndGraphicManager.setTextField1(null);
+ textAndGraphicManager.setTextField3(null);
+ textAndGraphicManager.setTextField1Type(null);
+ textAndGraphicManager.setTextField3Type(null);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "Wednesday");
+ assertEquals(assembledShow.getMainField2(), "Dudes");
+
+ // And 3 fields without setting 1
+ textAndGraphicManager.setTextField3("My");
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "Wednesday");
+ assertEquals(assembledShow.getMainField2(), "My - Dudes");
+
+ // test tags
+ tags = assembledShow.getMetadataTags();
+ tagsList = new ArrayList<>();
+ tagsList2 = new ArrayList<>();
+ tagsList.add(MetadataType.CURRENT_TEMPERATURE);
+ tagsList2.add(MetadataType.MEDIA_STATION);
+ assertEquals(tags.getMainField1(), tagsList);
+ assertEquals(tags.getMainField2(), tagsList2);
+ }
+
+ public void testAssemble3Lines() {
+
+ Show inputShow = new Show();
+
+ // Force it to return display with support for only 3 lines of text
+ textAndGraphicManager.displayCapabilities = getDisplayCapability(3);
+
+ textAndGraphicManager.setTextField1("It is");
+ textAndGraphicManager.setTextField1Type(MetadataType.HUMIDITY);
+
+ Show assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is");
+ assertEquals(assembledShow.getMainField2(), "");
+ assertEquals(assembledShow.getMainField3(), "");
+
+ // test tags
+ MetadataTags tags = assembledShow.getMetadataTags();
+ List<MetadataType> tagsList = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ assertEquals(tags.getMainField1(), tagsList);
+
+ textAndGraphicManager.setTextField2("Wednesday");
+ textAndGraphicManager.setTextField2Type(MetadataType.CURRENT_TEMPERATURE);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is");
+ assertEquals(assembledShow.getMainField2(), "Wednesday");
+ assertEquals(assembledShow.getMainField3(), "");
+
+ // test tags
+ tags = assembledShow.getMetadataTags();
+ tagsList = new ArrayList<>();
+ List<MetadataType> tagsList2 = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ tagsList2.add(MetadataType.CURRENT_TEMPERATURE);
+ assertEquals(tags.getMainField1(), tagsList);
+ assertEquals(tags.getMainField2(), tagsList2);
+
+ textAndGraphicManager.setTextField3("My");
+ textAndGraphicManager.setTextField3Type(MetadataType.MEDIA_ALBUM);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is");
+ assertEquals(assembledShow.getMainField2(), "Wednesday");
+ assertEquals(assembledShow.getMainField3(), "My");
+
+ // test tags
+ tags = assembledShow.getMetadataTags();
+ tagsList = new ArrayList<>();
+ tagsList2 = new ArrayList<>();
+ List<MetadataType> tagsList3 = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ tagsList2.add(MetadataType.CURRENT_TEMPERATURE);
+ tagsList3.add(MetadataType.MEDIA_ALBUM);
+ assertEquals(tags.getMainField1(), tagsList);
+ assertEquals(tags.getMainField2(), tagsList2);
+ assertEquals(tags.getMainField3(), tagsList3);
+
+ textAndGraphicManager.setTextField4("Dudes");
+ textAndGraphicManager.setTextField4Type(MetadataType.MEDIA_STATION);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is");
+ assertEquals(assembledShow.getMainField2(), "Wednesday");
+ assertEquals(assembledShow.getMainField3(), "My - Dudes");
+
+ // test tags
+ tags = assembledShow.getMetadataTags();
+ tagsList = new ArrayList<>();
+ tagsList2 = new ArrayList<>();
+ tagsList3 = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ tagsList2.add(MetadataType.CURRENT_TEMPERATURE);
+ tagsList3.add(MetadataType.MEDIA_ALBUM);
+ tagsList3.add(MetadataType.MEDIA_STATION);
+ assertEquals(tags.getMainField1(), tagsList);
+ assertEquals(tags.getMainField2(), tagsList2);
+ assertEquals(tags.getMainField3(), tagsList3);
+
+ // Someone might not want to set the fields in order? We should handle that
+ textAndGraphicManager.setTextField1(null);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ try {
+ System.out.println(assembledShow.serializeJSON().toString());
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ assertEquals(assembledShow.getMainField2(), "Wednesday");
+ assertEquals(assembledShow.getMainField3(), "My - Dudes");
+ }
+
+ public void testAssemble4Lines() {
+
+ Show inputShow = new Show();
+
+ // Force it to return display with support for only 4 lines of text
+ textAndGraphicManager.displayCapabilities = getDisplayCapability(4);
+
+ textAndGraphicManager.setTextField1("It is");
+ textAndGraphicManager.setTextField1Type(MetadataType.HUMIDITY);
+
+ Show assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+
+ assertEquals(assembledShow.getMainField1(), "It is");
+ assertEquals(assembledShow.getMainField2(), "");
+ assertEquals(assembledShow.getMainField3(), "");
+ assertEquals(assembledShow.getMainField4(), "");
+
+ // test tags
+ MetadataTags tags = assembledShow.getMetadataTags();
+ List<MetadataType> tagsList = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ assertEquals(tags.getMainField1(), tagsList);
+
+ textAndGraphicManager.setTextField2("Wednesday");
+ textAndGraphicManager.setTextField2Type(MetadataType.CURRENT_TEMPERATURE);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is");
+ assertEquals(assembledShow.getMainField2(), "Wednesday");
+ assertEquals(assembledShow.getMainField3(), "");
+ assertEquals(assembledShow.getMainField4(), "");
+
+ // test tags
+ tags = assembledShow.getMetadataTags();
+ tagsList = new ArrayList<>();
+ List<MetadataType> tagsList2 = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ tagsList2.add(MetadataType.CURRENT_TEMPERATURE);
+ assertEquals(tags.getMainField1(), tagsList);
+ assertEquals(tags.getMainField2(), tagsList2);
+
+ textAndGraphicManager.setTextField3("My");
+ textAndGraphicManager.setTextField3Type(MetadataType.MEDIA_ALBUM);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is");
+ assertEquals(assembledShow.getMainField2(), "Wednesday");
+ assertEquals(assembledShow.getMainField3(), "My");
+ assertEquals(assembledShow.getMainField4(), "");
+
+ // test tags
+ tags = assembledShow.getMetadataTags();
+ tagsList = new ArrayList<>();
+ tagsList2 = new ArrayList<>();
+ List<MetadataType> tagsList3 = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ tagsList2.add(MetadataType.CURRENT_TEMPERATURE);
+ tagsList3.add(MetadataType.MEDIA_ALBUM);
+ assertEquals(tags.getMainField1(), tagsList);
+ assertEquals(tags.getMainField2(), tagsList2);
+ assertEquals(tags.getMainField3(), tagsList3);
+
+ textAndGraphicManager.setTextField4("Dudes");
+ textAndGraphicManager.setTextField4Type(MetadataType.MEDIA_STATION);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is");
+ assertEquals(assembledShow.getMainField2(), "Wednesday");
+ assertEquals(assembledShow.getMainField3(), "My");
+ assertEquals(assembledShow.getMainField4(), "Dudes");
+
+ // test tags
+ tags = assembledShow.getMetadataTags();
+ tagsList = new ArrayList<>();
+ tagsList2 = new ArrayList<>();
+ tagsList3 = new ArrayList<>();
+ List<MetadataType> tagsList4 = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ tagsList2.add(MetadataType.CURRENT_TEMPERATURE);
+ tagsList3.add(MetadataType.MEDIA_ALBUM);
+ tagsList4.add(MetadataType.MEDIA_STATION);
+ assertEquals(tags.getMainField1(), tagsList);
+ assertEquals(tags.getMainField2(), tagsList2);
+ assertEquals(tags.getMainField3(), tagsList3);
+ assertEquals(tags.getMainField4(), tagsList4);
+
+ // try just setting line 1 and 4
+ textAndGraphicManager.setTextField2(null);
+ textAndGraphicManager.setTextField3(null);
+ textAndGraphicManager.setTextField2Type(null);
+ textAndGraphicManager.setTextField3Type(null);
+
+ assembledShow = textAndGraphicManager.assembleShowText(inputShow);
+ assertEquals(assembledShow.getMainField1(), "It is");
+ assertEquals(assembledShow.getMainField2(), "");
+ assertEquals(assembledShow.getMainField3(), "");
+ assertEquals(assembledShow.getMainField4(), "Dudes");
+
+ // test tags
+ tags = assembledShow.getMetadataTags();
+ tagsList = new ArrayList<>();
+ tagsList4 = new ArrayList<>();
+ tagsList.add(MetadataType.HUMIDITY);
+ tagsList4.add(MetadataType.MEDIA_STATION);
+ assertEquals(tags.getMainField1(), tagsList);
+ assertEquals(tags.getMainField4(), tagsList4);
+ }
+
+ public void testMediaTrackTextField() {
+
+ String songTitle = "Wild For The Night";
+ textAndGraphicManager.setMediaTrackTextField(songTitle);
+ assertEquals(textAndGraphicManager.getMediaTrackTextField(), songTitle);
+ }
+
+ public void testAlignment() {
+
+ textAndGraphicManager.setTextAlignment(TextAlignment.LEFT_ALIGNED);
+ assertEquals(textAndGraphicManager.getTextAlignment(), TextAlignment.LEFT_ALIGNED);
+ }
+
+ public void testExtractTextFromShow(){
+
+ Show mainShow = new Show();
+ mainShow.setMainField1("test");
+ mainShow.setMainField3("Sauce");
+ mainShow.setMainField4("");
+
+ Show newShow = textAndGraphicManager.extractTextFromShow(mainShow);
+
+ assertEquals(newShow.getMainField1(), "test");
+ assertEquals(newShow.getMainField3(), "Sauce");
+ assertEquals(newShow.getMainField4(), "");
+ assertNull(newShow.getMainField2());
+ }
+
+ // TEST IMAGES
+
+ public void testSetPrimaryGraphic() {
+ textAndGraphicManager.setPrimaryGraphic(testArtwork);
+ assertEquals(textAndGraphicManager.getPrimaryGraphic(), testArtwork);
+ }
+
+ public void testSetSecondaryGraphic() {
+ textAndGraphicManager.setSecondaryGraphic(testArtwork);
+ assertEquals(textAndGraphicManager.getSecondaryGraphic(), testArtwork);
+ }
+
+ // TEST DISPOSE
+
+ public void testDispose() {
+ textAndGraphicManager.dispose();
+
+ assertNull(textAndGraphicManager.getTextField1());
+ assertNull(textAndGraphicManager.getTextField2());
+ assertNull(textAndGraphicManager.getTextField3());
+ assertNull(textAndGraphicManager.getTextField4());
+ assertNull(textAndGraphicManager.getMediaTrackTextField());
+ assertNull(textAndGraphicManager.getPrimaryGraphic());
+ assertNull(textAndGraphicManager.getSecondaryGraphic());
+ assertNull(textAndGraphicManager.getTextAlignment());
+ assertNull(textAndGraphicManager.getTextField1Type());
+ assertNull(textAndGraphicManager.getTextField2Type());
+ assertNull(textAndGraphicManager.getTextField3Type());
+ assertNull(textAndGraphicManager.getTextField4Type());
+ assertNull(textAndGraphicManager.getBlankArtwork());
+ assertNull(textAndGraphicManager.currentScreenData);
+ assertNull(textAndGraphicManager.inProgressUpdate);
+ assertNull(textAndGraphicManager.queuedImageUpdate);
+ assertFalse(textAndGraphicManager.hasQueuedUpdate);
+ assertNull(textAndGraphicManager.displayCapabilities);
+ assertFalse(textAndGraphicManager.isDirty);
+ assertEquals(textAndGraphicManager.getState(), BaseSubManager.SHUTDOWN);
+ }
+}
diff --git a/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java b/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java
index 7c9c665ca..98208090a 100644
--- a/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java
+++ b/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java
@@ -1,5 +1,9 @@
package com.smartdevicelink.test;
+import java.util.Iterator;
+import java.util.List;
+
+import com.smartdevicelink.api.datatypes.SdlFile;
import com.smartdevicelink.protocol.enums.FrameData;
import com.smartdevicelink.protocol.enums.FrameDataControlFrameType;
import com.smartdevicelink.protocol.enums.FrameType;
@@ -259,6 +263,42 @@ public class Validator{
return true;
}
+ public static boolean validateSdlFile(SdlFile sdlFile1, SdlFile sdlFile2){
+ if(sdlFile1 == null){
+ return ( sdlFile2 == null );
+ }
+ if(sdlFile2 == null){
+ return ( sdlFile1 == null );
+ }
+
+ if(!( sdlFile1.getName().equals(sdlFile2.getName()) )){
+ log("validateSdlFile",
+ "sdlFile1 name \"" + sdlFile1.getName() + "\" didn't match sdlFile2 name \"" + sdlFile2.getName() + "\".");
+ return false;
+ }
+
+ if(sdlFile1.getResourceId() != sdlFile2.getResourceId() ){
+ log("validateSdlFile",
+ "sdlFile1 resourceId \"" + sdlFile1.getName() + "\" didn't match sdlFile2 resourceId \"" + sdlFile2.getName() + "\".");
+ return false;
+ }
+
+ if(!( sdlFile1.getType().equals(sdlFile2.getType()) )){
+ log("validateSdlFile",
+ "sdlFile1 type \"" + sdlFile1.getType() + "\" didn't match sdlFile2 type \"" + sdlFile2.getType() + "\".");
+ return false;
+ }
+
+ if( (sdlFile1.getUri() != sdlFile2.getUri()) && !( sdlFile1.getUri().equals(sdlFile2.getUri()) )){
+ log("validateSdlFile",
+ "sdlFile1 uri \"" + sdlFile1.getUri() + "\" didn't match sdlFile2 uri \"" + sdlFile2.getUri() + "\".");
+ return false;
+ }
+
+
+ return true;
+ }
+
public static boolean validateStringList(List<String> vrCommands1, List<String> vrCommands2){
if(vrCommands1 == null){
return ( vrCommands2 == null );
@@ -310,6 +350,15 @@ public class Validator{
return true;
}
+ public static boolean validateSoftButton(SoftButton button1, SoftButton button2){
+ return validateImage(button1.getImage(), button2.getImage())
+ && validateText(button1.getText(), button2.getText())
+ && button1.getIsHighlighted() == button2.getIsHighlighted()
+ && button1.getSoftButtonID() == button2.getSoftButtonID()
+ && button1.getSystemAction() == button2.getSystemAction()
+ && button1.getType() == button2.getType();
+ }
+
public static boolean validateSoftButtons(List<SoftButton> list1, List<SoftButton> list2){
if(list1 == null){
return ( list2 == null );
@@ -325,11 +374,7 @@ public class Validator{
SoftButton button1 = iterator1.next();
SoftButton button2 = iterator2.next();
- if(!validateImage(button1.getImage(), button2.getImage())
- || !validateText(button1.getText(), button2.getText())
- || button1.getIsHighlighted() != button2.getIsHighlighted()
- || button1.getSoftButtonID() != button2.getSoftButtonID()
- || button1.getSystemAction() != button2.getSystemAction() || button1.getType() != button2.getType()){
+ if(!validateSoftButton(button1, button2)){
return false;
}
}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/api/BaseSubManager.java b/sdl_android/src/main/java/com/smartdevicelink/api/BaseSubManager.java
index 66c7f309a..bf1582b30 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/api/BaseSubManager.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/api/BaseSubManager.java
@@ -18,7 +18,7 @@ public abstract class BaseSubManager {
// states - if this gets more complicated we can move elsewhere
private int state;
private final Object STATE_LOCK = new Object();
- public static final int SETTING_UP = 0x00, READY = 0x30, SHUTDOWN = 0x60, ERROR = 0x90;
+ public static final int SETTING_UP = 0x00, READY = 0x30, LIMITED = 0x50, SHUTDOWN = 0x80, ERROR = 0xC0;
protected final ISdl internalInterface;
private CompletionListener completionListener;
diff --git a/sdl_android/src/main/java/com/smartdevicelink/api/SdlManager.java b/sdl_android/src/main/java/com/smartdevicelink/api/SdlManager.java
index 3a332e8dd..2237db309 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/api/SdlManager.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/api/SdlManager.java
@@ -3,6 +3,7 @@ package com.smartdevicelink.api;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.Log;
import com.smartdevicelink.api.PermissionManager.PermissionManager;
@@ -10,6 +11,7 @@ import com.smartdevicelink.api.audio.AudioStreamManager;
import com.smartdevicelink.api.datatypes.SdlArtwork;
import com.smartdevicelink.api.lockscreen.LockScreenConfig;
import com.smartdevicelink.api.lockscreen.LockScreenManager;
+import com.smartdevicelink.api.screen.ScreenManager;
import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.protocol.enums.SessionType;
@@ -39,7 +41,6 @@ import com.smartdevicelink.streaming.audio.AudioStreamingCodec;
import com.smartdevicelink.streaming.audio.AudioStreamingParams;
import com.smartdevicelink.streaming.video.VideoStreamingParameters;
import com.smartdevicelink.transport.BaseTransportConfig;
-import com.smartdevicelink.util.Version;
import java.util.ArrayList;
import java.util.List;
@@ -79,14 +80,12 @@ public class SdlManager{
// Managers
private PermissionManager permissionManager;
- private VideoStreamingManager videoStreamingManager;
private FileManager fileManager;
- private AudioStreamManager audioStreamManager;
private LockScreenManager lockScreenManager;
-
- /*
private ScreenManager screenManager;
- */
+ private VideoStreamingManager videoStreamingManager;
+ private AudioStreamManager audioStreamManager;
+
// Initialize proxyBridge with anonymous lifecycleListener
private final ProxyBridge proxyBridge= new ProxyBridge(new ProxyBridge.LifecycleListener() {
@@ -129,10 +128,9 @@ public class SdlManager{
fileManager != null && fileManager.getState() != BaseSubManager.SETTING_UP &&
audioStreamManager != null && audioStreamManager.getState() != BaseSubManager.SETTING_UP &&
(videoStreamingManager == null || (videoStreamingManager != null && videoStreamingManager.getState() != BaseSubManager.SETTING_UP)) &&
- lockScreenManager != null && lockScreenManager.getState() != BaseSubManager.SETTING_UP
- /*
+ lockScreenManager != null && lockScreenManager.getState() != BaseSubManager.SETTING_UP &&
screenManager != null && screenManager.getState() != BaseSubManager.SETTING_UP
- */
+
){
state = BaseSubManager.READY;
if(managerListener != null){
@@ -166,13 +164,24 @@ public class SdlManager{
this.permissionManager = new PermissionManager(_internalInterface);
this.permissionManager.start(subManagerListener);
+ this.fileManager = new FileManager(_internalInterface, context);
+ this.fileManager.start(subManagerListener);
+
+ if (lockScreenConfig.isEnabled()) {
+ this.lockScreenManager = new LockScreenManager(lockScreenConfig, context, _internalInterface);
+ this.lockScreenManager.start(subManagerListener);
+ }
+
+ this.screenManager = new ScreenManager(_internalInterface, this.fileManager);
+ this.screenManager.start(subManagerListener);
+
+
if(getAppTypes().contains(AppHMIType.NAVIGATION) || getAppTypes().contains(AppHMIType.PROJECTION)){
this.videoStreamingManager = new VideoStreamingManager(_internalInterface);
this.videoStreamingManager.start(subManagerListener);
}
- this.fileManager = new FileManager(_internalInterface, context);
- this.fileManager.start(subManagerListener);
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
this.audioStreamManager = new AudioStreamManager(_internalInterface, context);
this.audioStreamManager.start(subManagerListener);
@@ -180,36 +189,29 @@ public class SdlManager{
this.audioStreamManager = null;
}
- if (lockScreenConfig.isEnabled()) {
- this.lockScreenManager = new LockScreenManager(lockScreenConfig, context, _internalInterface);
- this.lockScreenManager.start(subManagerListener);
- }
-
- /*
- this.screenManager = new ScreenManager(_internalInterface, this.fileManager);
- this.screenManager.start(subManagerListener);
- */
}
public void dispose() {
this.permissionManager.dispose();
- if(this.videoStreamingManager != null) {
- this.videoStreamingManager.dispose();
- }
+
this.fileManager.dispose();
if (this.lockScreenManager != null) {
this.lockScreenManager.dispose();
}
+
+ this.screenManager.dispose();
+
+ if(this.videoStreamingManager != null) {
+ this.videoStreamingManager.dispose();
+ }
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
if (this.audioStreamManager != null) {
this.audioStreamManager.dispose();
}
}
- /*
- this.screenManager.dispose();
- */
if(managerListener != null){
managerListener.onDestroy();
managerListener = null;
@@ -461,7 +463,7 @@ public class SdlManager{
* <strong>Note: AudioStreamManager should be used only after SdlManager.start() CompletionListener callback is completed successfully.</strong>
* @return a AudioStreamManager object
*/
- public AudioStreamManager getAudioStreamManager() {
+ public @Nullable AudioStreamManager getAudioStreamManager() {
checkSdlManagerState();
return audioStreamManager;
}
@@ -471,12 +473,10 @@ public class SdlManager{
* <strong>Note: ScreenManager should be used only after SdlManager.start() CompletionListener callback is completed successfully.</strong>
* @return a ScreenManager object
*/
- /*
public ScreenManager getScreenManager() {
checkSdlManagerState();
return screenManager;
}
- */
/**
* Gets the LockScreenManager. <br>
diff --git a/sdl_android/src/main/java/com/smartdevicelink/api/datatypes/SdlArtwork.java b/sdl_android/src/main/java/com/smartdevicelink/api/datatypes/SdlArtwork.java
index 7f6718288..b64ca6c6f 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/api/datatypes/SdlArtwork.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/api/datatypes/SdlArtwork.java
@@ -1,5 +1,6 @@
package com.smartdevicelink.api.datatypes;
+import android.net.Uri;
import android.support.annotation.NonNull;
import com.smartdevicelink.proxy.rpc.enums.FileType;
@@ -8,6 +9,20 @@ import com.smartdevicelink.proxy.rpc.enums.FileType;
* A class that extends SdlFile, representing artwork (JPEG, PNG, or BMP) to be uploaded to core
*/
public class SdlArtwork extends SdlFile {
+ public SdlArtwork(){}
+
+ public SdlArtwork(@NonNull String fileName, @NonNull FileType fileType, int id, boolean persistentFile) {
+ super(fileName, fileType, id, persistentFile);
+ }
+
+ public SdlArtwork(@NonNull String fileName, @NonNull FileType fileType, Uri uri, boolean persistentFile) {
+ super(fileName, fileType, uri, persistentFile);
+ }
+
+ public SdlArtwork(@NonNull String fileName, @NonNull FileType fileType, byte[] data, boolean persistentFile) {
+ super(fileName, fileType, data, persistentFile);
+ }
+
@Override
public void setType(@NonNull FileType fileType) {
if(fileType.equals(FileType.GRAPHIC_JPEG) || fileType.equals(FileType.GRAPHIC_PNG)
diff --git a/sdl_android/src/main/java/com/smartdevicelink/api/datatypes/SdlFile.java b/sdl_android/src/main/java/com/smartdevicelink/api/datatypes/SdlFile.java
index 1f70b0758..3cb634fc9 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/api/datatypes/SdlFile.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/api/datatypes/SdlFile.java
@@ -16,6 +16,29 @@ public class SdlFile{
private FileType fileType;
private boolean persistentFile;
+ public SdlFile(){}
+
+ public SdlFile(@NonNull String fileName, @NonNull FileType fileType, int id, boolean persistentFile){
+ this.fileName = fileName;
+ this.fileType = fileType;
+ this.id = id;
+ this.persistentFile = persistentFile;
+ }
+
+ public SdlFile(@NonNull String fileName, @NonNull FileType fileType, Uri uri, boolean persistentFile){
+ this.fileName = fileName;
+ this.fileType = fileType;
+ this.uri = uri;
+ this.persistentFile = persistentFile;
+ }
+
+ public SdlFile(@NonNull String fileName, @NonNull FileType fileType, byte[] data, boolean persistentFile){
+ this.fileName = fileName;
+ this.fileType = fileType;
+ this.fileData = data;
+ this.persistentFile = persistentFile;
+ }
+
public void setName(@NonNull String fileName){
this.fileName = fileName;
}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/api/screen/ScreenManager.java b/sdl_android/src/main/java/com/smartdevicelink/api/screen/ScreenManager.java
new file mode 100644
index 000000000..ca46dfe28
--- /dev/null
+++ b/sdl_android/src/main/java/com/smartdevicelink/api/screen/ScreenManager.java
@@ -0,0 +1,361 @@
+package com.smartdevicelink.api.screen;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import com.smartdevicelink.api.BaseSubManager;
+import com.smartdevicelink.api.CompletionListener;
+import com.smartdevicelink.api.FileManager;
+import com.smartdevicelink.api.datatypes.SdlArtwork;
+import com.smartdevicelink.proxy.interfaces.ISdl;
+import com.smartdevicelink.proxy.rpc.SetDisplayLayout;
+import com.smartdevicelink.proxy.rpc.enums.MetadataType;
+import com.smartdevicelink.proxy.rpc.enums.PredefinedLayout;
+import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * <strong>ScreenManager</strong> <br>
+ *
+ * Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br>
+*/
+public class ScreenManager extends BaseSubManager {
+
+ private static String TAG = "ScreenManager";
+ private final WeakReference<FileManager> fileManager;
+ private SoftButtonManager softButtonManager;
+ private TextAndGraphicManager textAndGraphicManager;
+
+ // Sub manager listener
+ private final CompletionListener subManagerListener = new CompletionListener() {
+ @Override
+ public synchronized void onComplete(boolean success) {
+ if (softButtonManager != null && textAndGraphicManager != null) {
+ if (softButtonManager.getState() == BaseSubManager.READY && textAndGraphicManager.getState() == BaseSubManager.READY) {
+ transitionToState(READY);
+ }else if (softButtonManager.getState() == BaseSubManager.ERROR && textAndGraphicManager.getState() == BaseSubManager.ERROR){
+ Log.e(TAG, "ERROR starting screen manager, both sub managers in error state");
+ transitionToState(ERROR);
+ } else if (softButtonManager.getState() == BaseSubManager.ERROR || textAndGraphicManager.getState() == BaseSubManager.ERROR) {
+ Log.e(TAG, "ERROR starting screen manager, one sub manager in error state");
+ transitionToState(LIMITED);
+ }
+ } else if (softButtonManager == null || textAndGraphicManager == null) {
+ // We should never be here, but somehow one of the sub-sub managers is null
+ Log.e(TAG, "ERROR one of the screen sub managers is null");
+ transitionToState(LIMITED);
+ } else {
+ Log.e(TAG, "ERROR both of the screen sub managers are null");
+ // We should never be here, but somehow both of the sub-sub managers is null
+ transitionToState(ERROR);
+ }
+ }
+ };
+
+ public ScreenManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
+ super(internalInterface);
+ transitionToState(SETTING_UP);
+ this.fileManager = new WeakReference<>(fileManager);
+ initialize();
+ }
+
+ private void initialize(){
+ if (fileManager.get() != null) {
+ this.softButtonManager = new SoftButtonManager(internalInterface, fileManager.get());
+ this.textAndGraphicManager = new TextAndGraphicManager(internalInterface, fileManager.get(), softButtonManager);
+ this.softButtonManager.start(subManagerListener);
+ this.textAndGraphicManager.start(subManagerListener);
+ }
+ }
+
+ /**
+ * <p>Called when manager is being torn down</p>
+ */
+ public void dispose(){
+ softButtonManager.dispose();
+ textAndGraphicManager.dispose();
+ super.dispose();
+ }
+
+ /**
+ * Set the textField1 on the head unit screen
+ * Sending an empty String "" will clear the field
+ * @param textField1 String value represents the textField1
+ */
+ public void setTextField1(String textField1) {
+ this.softButtonManager.setCurrentMainField1(textField1);
+ this.textAndGraphicManager.setTextField1(textField1);
+ }
+
+ /**
+ * Get the current textField1 value
+ * @return a String value represents the current textField1 value
+ */
+ public String getTextField1() {
+ return this.textAndGraphicManager.getTextField1();
+ }
+
+ /**
+ * Set the textField2 on the head unit screen
+ * Sending an empty String "" will clear the field
+ * @param textField2 String value represents the textField1
+ */
+ public void setTextField2(String textField2) {
+ this.textAndGraphicManager.setTextField2(textField2);
+ }
+
+ /**
+ * Get the current textField2 value
+ * @return a String value represents the current textField2 value
+ */
+ public String getTextField2() {
+ return this.textAndGraphicManager.getTextField2();
+ }
+
+ /**
+ * Set the textField3 on the head unit screen
+ * Sending an empty String "" will clear the field
+ * @param textField3 String value represents the textField1
+ */
+ public void setTextField3(String textField3) {
+ this.textAndGraphicManager.setTextField3(textField3);
+ }
+
+ /**
+ * Get the current textField3 value
+ * @return a String value represents the current textField3 value
+ */
+ public String getTextField3() {
+ return this.textAndGraphicManager.getTextField3();
+ }
+
+ /**
+ * Set the textField4 on the head unit screen
+ * Sending an empty String "" will clear the field
+ * @param textField4 String value represents the textField1
+ */
+ public void setTextField4(String textField4) {
+ this.textAndGraphicManager.setTextField4(textField4);
+ }
+
+ /**
+ * Get the current textField4 value
+ * @return a String value represents the current textField4 value
+ */
+ public String getTextField4() {
+ return this.textAndGraphicManager.getTextField4();
+ }
+
+ /**
+ * Set the mediaTrackTextField on the head unit screen
+ * @param mediaTrackTextField String value represents the mediaTrackTextField
+ */
+ public void setMediaTrackTextField(String mediaTrackTextField) {
+ this.textAndGraphicManager.setMediaTrackTextField(mediaTrackTextField);
+ }
+
+ /**
+ * Get the current mediaTrackTextField value
+ * @return a String value represents the current mediaTrackTextField
+ */
+ public String getMediaTrackTextField() {
+ return this.textAndGraphicManager.getMediaTrackTextField();
+ }
+
+ /**
+ * Set the primaryGraphic on the head unit screen
+ * @param primaryGraphic an SdlArtwork object represents the primaryGraphic
+ */
+ public void setPrimaryGraphic(SdlArtwork primaryGraphic) {
+ if (primaryGraphic == null){
+ primaryGraphic = textAndGraphicManager.getBlankArtwork();
+ }
+ this.textAndGraphicManager.setPrimaryGraphic(primaryGraphic);
+ }
+
+ /**
+ * Get the current primaryGraphic value
+ * @return an SdlArtwork object represents the current primaryGraphic
+ */
+ public SdlArtwork getPrimaryGraphic() {
+ return this.textAndGraphicManager.getPrimaryGraphic();
+ }
+
+ /**
+ * Set the secondaryGraphic on the head unit screen
+ * @param secondaryGraphic an SdlArtwork object represents the secondaryGraphic
+ */
+ public void setSecondaryGraphic(SdlArtwork secondaryGraphic) {
+ if (secondaryGraphic == null){
+ secondaryGraphic = textAndGraphicManager.getBlankArtwork();
+ }
+ this.textAndGraphicManager.setSecondaryGraphic(secondaryGraphic);
+ }
+
+ /**
+ * Get the current secondaryGraphic value
+ * @return an SdlArtwork object represents the current secondaryGraphic
+ */
+ public SdlArtwork getSecondaryGraphic() {
+ return this.textAndGraphicManager.getSecondaryGraphic();
+ }
+
+ /**
+ * Set the alignment for the text fields
+ * @param textAlignment TextAlignment value represents the alignment for the text fields
+ */
+ public void setTextAlignment(TextAlignment textAlignment) {
+ this.textAndGraphicManager.setTextAlignment(textAlignment);
+ }
+
+ /**
+ * Get the alignment for the text fields
+ * @return a TextAlignment value represents the alignment for the text fields
+ */
+ public TextAlignment getTextAlignment() {
+ return this.textAndGraphicManager.getTextAlignment();
+ }
+
+ /**
+ * Set the metadata type for the textField1
+ * @param textField1Type a MetadataType value represents the metadata for textField1
+ */
+ public void setTextField1Type(MetadataType textField1Type) {
+ this.textAndGraphicManager.setTextField1Type(textField1Type);
+ }
+
+ /**
+ * Get the metadata type for textField1
+ * @return a MetadataType value represents the metadata for textField1
+ */
+ public MetadataType getTextField1Type() {
+ return this.textAndGraphicManager.getTextField1Type();
+ }
+
+ /**
+ * Set the metadata type for the textField2
+ * @param textField2Type a MetadataType value represents the metadata for textField2
+ */
+ public void setTextField2Type(MetadataType textField2Type) {
+ this.textAndGraphicManager.setTextField2Type(textField2Type);
+ }
+
+ /**
+ * Get the metadata type for textField2
+ * @return a MetadataType value represents the metadata for textField2
+ */
+ public MetadataType getTextField2Type() {
+ return this.textAndGraphicManager.getTextField2Type();
+ }
+
+ /**
+ * Set the metadata type for the textField3
+ * @param textField3Type a MetadataType value represents the metadata for textField3
+ */
+ public void setTextField3Type(MetadataType textField3Type) {
+ this.textAndGraphicManager.setTextField3Type(textField3Type);
+ }
+
+ /**
+ * Get the metadata type for textField3
+ * @return a MetadataType value represents the metadata for textField3
+ */
+ public MetadataType getTextField3Type() {
+ return this.textAndGraphicManager.getTextField3Type();
+ }
+
+ /**
+ * Set the metadata type for the textField4
+ * @param textField4Type a MetadataType value represents the metadata for textField4
+ */
+ public void setTextField4Type(MetadataType textField4Type) {
+ this.textAndGraphicManager.setTextField4Type(textField4Type);
+ }
+
+ /**
+ * Get the metadata type for textField4
+ * @return a MetadataType value represents the metadata for textField4
+ */
+ public MetadataType getTextField4Type() {
+ return this.textAndGraphicManager.getTextField4Type();
+ }
+
+ /**
+ * Set softButtonObjects list and upload the images to the head unit
+ * @param softButtonObjects the list of the SoftButtonObject values that should be displayed on the head unit
+ */
+ public void setSoftButtonObjects(@NonNull List<SoftButtonObject> softButtonObjects) {
+ softButtonManager.setSoftButtonObjects(softButtonObjects);
+ }
+
+ /**
+ * Get the soft button objects list
+ * @return a List<SoftButtonObject>
+ */
+ public List<SoftButtonObject> getSoftButtonObjects() {
+ return softButtonManager.getSoftButtonObjects();
+ }
+
+ /**
+ * Get the SoftButtonObject that has the provided name
+ * @param name a String value that represents the name
+ * @return a SoftButtonObject
+ */
+ public SoftButtonObject getSoftButtonObjectByName(@NonNull String name){
+ return softButtonManager.getSoftButtonObjectByName(name);
+ }
+
+ /**
+ * Get the SoftButtonObject that has the provided buttonId
+ * @param buttonId a int value that represents the id of the button
+ * @return a SoftButtonObject
+ */
+ public SoftButtonObject getSoftButtonObjectById(int buttonId){
+ return softButtonManager.getSoftButtonObjectById(buttonId);
+ }
+
+ public void setDisplayLayout(PredefinedLayout layout){
+ SetDisplayLayout setDisplayLayoutRequest = new SetDisplayLayout();
+ setDisplayLayoutRequest.setDisplayLayout(layout.toString());
+ internalInterface.sendRPCRequest(setDisplayLayoutRequest);
+ }
+
+ /**
+ * Begin a multiple updates transaction. The updates will be applied when commit() is called<br>
+ * Note: if we don't use beginTransaction & commit, every update will be sent individually.
+ */
+ public void beginTransaction(){
+ softButtonManager.setBatchUpdates(true);
+ textAndGraphicManager.setBatchUpdates(true);
+ }
+
+ /**
+ * Send the updates that were started after beginning the transaction
+ * @param listener a CompletionListener that has a callback that will be called when the updates are finished
+ */
+ public void commit(final CompletionListener listener){
+ softButtonManager.setBatchUpdates(false);
+ softButtonManager.update(new CompletionListener() {
+ boolean updateSuccessful = true;
+ @Override
+ public void onComplete(boolean success) {
+ if (!success){
+ updateSuccessful = false;
+ }
+ textAndGraphicManager.setBatchUpdates(false);
+ textAndGraphicManager.update(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ if (!success){
+ updateSuccessful = false;
+ }
+ listener.onComplete(updateSuccessful);
+ }
+ });
+ }
+ });
+ }
+
+}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/api/screen/SoftButtonManager.java b/sdl_android/src/main/java/com/smartdevicelink/api/screen/SoftButtonManager.java
new file mode 100644
index 000000000..a8369f7d3
--- /dev/null
+++ b/sdl_android/src/main/java/com/smartdevicelink/api/screen/SoftButtonManager.java
@@ -0,0 +1,570 @@
+package com.smartdevicelink.api.screen;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import com.smartdevicelink.api.BaseSubManager;
+import com.smartdevicelink.api.CompletionListener;
+import com.smartdevicelink.api.FileManager;
+import com.smartdevicelink.api.MultipleFileCompletionListener;
+import com.smartdevicelink.api.datatypes.SdlArtwork;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.RPCNotification;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.interfaces.ISdl;
+import com.smartdevicelink.proxy.interfaces.OnSystemCapabilityListener;
+import com.smartdevicelink.proxy.rpc.DisplayCapabilities;
+import com.smartdevicelink.proxy.rpc.OnButtonEvent;
+import com.smartdevicelink.proxy.rpc.OnButtonPress;
+import com.smartdevicelink.proxy.rpc.OnHMIStatus;
+import com.smartdevicelink.proxy.rpc.Show;
+import com.smartdevicelink.proxy.rpc.SoftButton;
+import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities;
+import com.smartdevicelink.proxy.rpc.enums.ButtonName;
+import com.smartdevicelink.proxy.rpc.enums.HMILevel;
+import com.smartdevicelink.proxy.rpc.enums.Result;
+import com.smartdevicelink.proxy.rpc.enums.SoftButtonType;
+import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * <strong>SoftButtonManager</strong> <br>
+ * SoftButtonManager gives the developer the ability to control how soft buttons are displayed on the head unit.<br>
+ * Note: This class must be accessed through the SdlManager->ScreenManager. Do not instantiate it by itself.<br>
+ */
+class SoftButtonManager extends BaseSubManager {
+
+ private static final String TAG = "SoftButtonManager";
+ private WeakReference<FileManager> fileManager;
+ private DisplayCapabilities displayCapabilities;
+ private SoftButtonCapabilities softButtonCapabilities;
+ private CopyOnWriteArrayList<SoftButtonObject> softButtonObjects;
+ private HMILevel currentHMILevel;
+ private Show inProgressShowRPC;
+ private CompletionListener inProgressListener, queuedUpdateListener, cachedListener;
+ private boolean hasQueuedUpdate, batchUpdates, waitingOnHMILevelUpdateToSetButtons;
+ private final OnSystemCapabilityListener onSoftButtonCapabilitiesListener, onDisplayCapabilitiesListener;
+ private final OnRPCNotificationListener onHMIStatusListener, onButtonPressListener, onButtonEventListener;
+ private final SoftButtonObject.UpdateListener updateListener;
+
+ /**
+ * HAX: This is necessary due to a Ford Sync 3 bug that doesn't like Show requests without a main field being set (it will accept them, but with a GENERIC_ERROR, and 10-15 seconds late...)
+ */
+ private String currentMainField1;
+
+
+ /**
+ * Creates a new instance of the SoftButtonManager
+ * @param internalInterface
+ * @param fileManager
+ */
+ SoftButtonManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
+ super(internalInterface);
+ transitionToState(BaseSubManager.SETTING_UP);
+ this.fileManager = new WeakReference<>(fileManager);
+ this.softButtonObjects = new CopyOnWriteArrayList<>();
+ this.currentHMILevel = HMILevel.HMI_NONE; // Assume NONE until we get something else
+ this.waitingOnHMILevelUpdateToSetButtons = false;
+ this.updateListener = new SoftButtonObject.UpdateListener() {
+ @Override
+ public void onUpdate() {
+ update(null);
+ }
+ };
+
+
+ // Add OnSoftButtonCapabilitiesListener to keep softButtonCapabilities updated
+ onSoftButtonCapabilitiesListener = new OnSystemCapabilityListener() {
+ @Override
+ public void onCapabilityRetrieved(Object capability) {
+ List<SoftButtonCapabilities> softButtonCapabilitiesList = (List<SoftButtonCapabilities>) capability;
+ if (softButtonCapabilitiesList != null && !softButtonCapabilitiesList.isEmpty()) {
+ softButtonCapabilities = softButtonCapabilitiesList.get(0);
+ } else {
+ softButtonCapabilities = null;
+ }
+ }
+
+ @Override
+ public void onError(String info) {
+ Log.w(TAG, "SoftButton Capability cannot be retrieved:");
+ softButtonCapabilities = null;
+ }
+ };
+ this.internalInterface.addOnSystemCapabilityListener(SystemCapabilityType.SOFTBUTTON, onSoftButtonCapabilitiesListener);
+
+
+ // Add OnDisplayCapabilitiesListener to keep displayCapabilities updated
+ onDisplayCapabilitiesListener = new OnSystemCapabilityListener() {
+ @Override
+ public void onCapabilityRetrieved(Object capability) {
+ displayCapabilities = (DisplayCapabilities) capability;
+ }
+
+ @Override
+ public void onError(String info) {
+ Log.w(TAG, "Display Capability cannot be retrieved:");
+ displayCapabilities = null;
+ }
+ };
+ this.internalInterface.addOnSystemCapabilityListener(SystemCapabilityType.DISPLAY, onDisplayCapabilitiesListener);
+
+
+ // Add OnHMIStatusListener to keep currentHMILevel updated
+ this.onHMIStatusListener = new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+
+ OnHMIStatus onHMIStatus = (OnHMIStatus) notification;
+ HMILevel oldHmiLevel = currentHMILevel;
+ currentHMILevel = onHMIStatus.getHmiLevel();
+
+
+ // Auto-send an updated show if we were in NONE and now we are not
+ if (oldHmiLevel == HMILevel.HMI_NONE && currentHMILevel != HMILevel.HMI_NONE) {
+ if (waitingOnHMILevelUpdateToSetButtons) {
+ setSoftButtonObjects(softButtonObjects);
+ } else {
+ update(cachedListener);
+ }
+ }
+ }
+ };
+ this.internalInterface.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, onHMIStatusListener);
+
+
+ // Add OnButtonPressListener to notify SoftButtonObjects when there is a button press
+ this.onButtonPressListener = new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+ OnButtonPress onButtonPress = (OnButtonPress) notification;
+ if (onButtonPress!= null && onButtonPress.getButtonName() == ButtonName.CUSTOM_BUTTON) {
+ Integer buttonId = onButtonPress.getCustomButtonName();
+ if (getSoftButtonObjects() != null) {
+ for (SoftButtonObject softButtonObject : getSoftButtonObjects()) {
+ if (softButtonObject.getButtonId() == buttonId && softButtonObject.getOnEventListener() != null) {
+ softButtonObject.getOnEventListener().onPress(getSoftButtonObjectById(buttonId), onButtonPress);
+ break;
+ }
+ }
+ }
+ }
+ }
+ };
+ this.internalInterface.addOnRPCNotificationListener(FunctionID.ON_BUTTON_PRESS, onButtonPressListener);
+
+
+ // Add OnButtonEventListener to notify SoftButtonObjects when there is a button event
+ this.onButtonEventListener = new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+ OnButtonEvent onButtonEvent = (OnButtonEvent) notification;
+ if (onButtonEvent!= null && onButtonEvent.getButtonName() == ButtonName.CUSTOM_BUTTON) {
+ Integer buttonId = onButtonEvent.getCustomButtonID();
+ if (getSoftButtonObjects() != null) {
+ for (SoftButtonObject softButtonObject : getSoftButtonObjects()) {
+ if (softButtonObject.getButtonId() == buttonId && softButtonObject.getOnEventListener() != null) {
+ softButtonObject.getOnEventListener().onEvent(getSoftButtonObjectById(buttonId), onButtonEvent);
+ break;
+ }
+ }
+ }
+ }
+ }
+ };
+ this.internalInterface.addOnRPCNotificationListener(FunctionID.ON_BUTTON_EVENT, onButtonEventListener);
+ }
+
+ @Override
+ public void start(CompletionListener listener) {
+ transitionToState(READY);
+ super.start(listener);
+ }
+
+ /**
+ * Get the SoftButtonObject that has the provided name
+ * @param name a String value that represents the name
+ * @return a SoftButtonObject
+ */
+ protected SoftButtonObject getSoftButtonObjectByName(String name) {
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ if (softButtonObject.getName().equals(name)) {
+ return softButtonObject;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the SoftButtonObject that has the provided buttonId
+ * @param buttonId a int value that represents the id of the button
+ * @return a SoftButtonObject
+ */
+ protected SoftButtonObject getSoftButtonObjectById(int buttonId) {
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ if (softButtonObject.getButtonId() == buttonId) {
+ return softButtonObject;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the soft button objects list
+ * @return a List<SoftButtonObject>
+ */
+ protected List<SoftButtonObject> getSoftButtonObjects() {
+ return softButtonObjects;
+ }
+
+ /**
+ * Set softButtonObjects list and upload the images to the head unit
+ * @param list the list of the SoftButtonObject values that should be displayed on the head unit
+ */
+ protected void setSoftButtonObjects(@NonNull List<SoftButtonObject> list) {
+ // Convert the List to CopyOnWriteArrayList
+ CopyOnWriteArrayList<SoftButtonObject> softButtonObjects;
+ if(list instanceof CopyOnWriteArrayList){
+ softButtonObjects = (CopyOnWriteArrayList<SoftButtonObject>) list;
+ }else{
+ softButtonObjects = new CopyOnWriteArrayList<>(list);
+ }
+
+
+ if (hasTwoSoftButtonObjectsOfSameName(softButtonObjects)) {
+ this.softButtonObjects = new CopyOnWriteArrayList<>();
+ Log.e(TAG, "Attempted to set soft button objects, but two buttons had the same name");
+ return;
+ }
+
+ // Set ids and updateListeners for soft button objects
+ for (int i = 0; i < softButtonObjects.size(); i++) {
+ softButtonObjects.get(i).setButtonId(i * 100);
+ softButtonObjects.get(i).setUpdateListener(updateListener);
+ }
+ this.softButtonObjects = softButtonObjects;
+
+
+ if (currentHMILevel == null || currentHMILevel == HMILevel.HMI_NONE) {
+ waitingOnHMILevelUpdateToSetButtons = true;
+ return;
+ }
+
+
+ // End any in-progress update
+ inProgressShowRPC = null;
+ if (inProgressListener != null) {
+ inProgressListener.onComplete(false);
+ inProgressListener = null;
+ }
+
+
+ // End any queued update
+ hasQueuedUpdate = false;
+ if (queuedUpdateListener != null) {
+ queuedUpdateListener.onComplete(false);
+ queuedUpdateListener = null;
+ }
+
+
+ // Prepare soft button images to be uploaded to the head unit.
+ // we will prepare a list for initial state images and another list for other state images
+ // so we can upload the initial state images first, then the other states images.
+ List<SdlArtwork> initialStatesToBeUploaded = new ArrayList<>();
+ List<SdlArtwork> otherStatesToBeUploaded = new ArrayList<>();
+ if (softButtonImagesSupported() && fileManager.get() != null) {
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ SoftButtonState initialState = null;
+ if (softButtonObject != null) {
+ initialState = softButtonObject.getCurrentState();
+ }
+ if (initialState != null && softButtonObject.getStates() != null) {
+ for (SoftButtonState softButtonState : softButtonObject.getStates()) {
+ if (softButtonState != null && softButtonState.getName() != null && softButtonState.getArtwork() != null && !fileManager.get().hasUploadedFile(softButtonState.getArtwork())) {
+ if (softButtonState.getName().equals(initialState.getName())) {
+ initialStatesToBeUploaded.add(softButtonObject.getCurrentState().getArtwork());
+ } else{
+ otherStatesToBeUploaded.add(softButtonState.getArtwork());
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ // Upload initial state images
+ if (initialStatesToBeUploaded.size() > 0 && fileManager.get() != null) {
+ Log.v(TAG, "Uploading soft button initial state artworks");
+ fileManager.get().uploadArtworks(initialStatesToBeUploaded, new MultipleFileCompletionListener() {
+ @Override
+ public void onComplete(Map<String, String> errors) {
+ if (errors != null && errors.size() > 0) {
+ Log.e(TAG, "Error uploading soft button artworks");
+ }
+ Log.d(TAG, "Soft button initial artworks uploaded");
+ update(cachedListener);
+ }
+ });
+ }
+
+
+ // Upload other state images
+ if (otherStatesToBeUploaded.size() > 0 && fileManager.get() != null) {
+ Log.v(TAG, "Uploading soft button other state artworks");
+ fileManager.get().uploadArtworks(otherStatesToBeUploaded, new MultipleFileCompletionListener() {
+ @Override
+ public void onComplete(Map<String, String> errors) {
+ if (errors != null && errors.size() > 0) {
+ Log.e(TAG, "Error uploading soft button artworks");
+ }
+ Log.d(TAG, "Soft button other state artworks uploaded");
+ // In case our soft button states have changed in the meantime
+ update(cachedListener);
+ }
+ });
+ }
+
+ // This is necessary because there may be no images needed to be uploaded
+ update(cachedListener);
+ }
+
+ /**
+ * Update the SoftButtonManger by sending a new Show RPC to reflect the changes
+ * @param listener a CompletionListener
+ */
+ protected void update(CompletionListener listener) {
+ cachedListener = listener;
+
+ if (batchUpdates) {
+ return;
+ }
+
+ // Don't send if we're in HMI NONE
+ if (currentHMILevel == null || currentHMILevel == HMILevel.HMI_NONE) {
+ return;
+ }
+
+ Log.v(TAG, "Updating soft buttons");
+
+ cachedListener = null;
+
+
+ // Check if we have update already in progress
+ if (inProgressShowRPC != null) {
+ Log.d(TAG, "In progress update exists, queueing update");
+ // If we already have a pending update, we're going to tell the old listener that it was superseded by a new update and then return
+ if (queuedUpdateListener != null) {
+ Log.d(TAG, "Queued update already exists, superseding previous queued update");
+ queuedUpdateListener.onComplete(false);
+ queuedUpdateListener = null;
+ }
+
+ // Note: the queued update will be started after the in-progress one finishes
+ if (listener != null) {
+ queuedUpdateListener = listener;
+ }
+ hasQueuedUpdate = true;
+ return;
+ }
+
+
+ // Send Show RPC with soft buttons representing the current state for the soft button objects
+ inProgressListener = listener;
+ inProgressShowRPC = new Show();
+ inProgressShowRPC.setMainField1(getCurrentMainField1());
+ if (softButtonObjects == null) {
+ Log.d(TAG, "Soft button objects are null, sending an empty array");
+ inProgressShowRPC.setSoftButtons(new ArrayList<SoftButton>());
+ } else if ((currentStateHasImages() && !allCurrentStateImagesAreUploaded()) || !softButtonImagesSupported()) {
+ // The images don't yet exist on the head unit, or we cannot use images, send a text update if possible, otherwise, don't send anything yet
+ List<SoftButton> textOnlySoftButtons = createTextSoftButtonsForCurrentState();
+ if (textOnlySoftButtons != null) {
+ Log.d(TAG, "Soft button images unavailable, sending text buttons");
+ inProgressShowRPC.setSoftButtons(textOnlySoftButtons);
+
+ } else {
+ Log.d(TAG, "Soft button images unavailable, text buttons unavailable");
+ inProgressShowRPC = null;
+ return;
+ }
+
+ } else {
+ Log.d(TAG, "Sending soft buttons with images");
+ inProgressShowRPC.setSoftButtons(createSoftButtonsForCurrentState());
+ }
+
+
+ inProgressShowRPC.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ Log.i(TAG, "Soft button update completed");
+ handleResponse(true);
+ }
+
+ @Override
+ public void onError(int correlationId, Result resultCode, String info) {
+ super.onError(correlationId, resultCode, info);
+
+ Log.e(TAG, "Soft button update error");
+ handleResponse(false);
+
+ }
+
+ private void handleResponse(boolean success){
+
+ inProgressShowRPC = null;
+ CompletionListener currentListener;
+ if (inProgressListener != null) {
+ currentListener = inProgressListener;
+ inProgressListener = null;
+ currentListener.onComplete(success);
+ }
+
+
+ if (hasQueuedUpdate) {
+ Log.d(TAG, "Queued update exists, sending another update");
+ currentListener = queuedUpdateListener;
+ queuedUpdateListener = null;
+ hasQueuedUpdate = false;
+ update(currentListener);
+ }
+ }
+ });
+
+
+ internalInterface.sendRPCRequest(inProgressShowRPC);
+ }
+
+ private boolean softButtonImagesSupported(){
+ return (displayCapabilities == null || displayCapabilities.getGraphicSupported()) && (softButtonCapabilities == null || softButtonCapabilities.getImageSupported());
+ }
+
+ /**
+ * Check if two SoftButtonObject have the same name
+ * @param softButtonObjects
+ * @return a boolean value
+ */
+ private boolean hasTwoSoftButtonObjectsOfSameName(List<SoftButtonObject> softButtonObjects) {
+ for (int i = 0; i < softButtonObjects.size(); i++) {
+ String buttonName = softButtonObjects.get(i).getName();
+ for (int j = (i + 1); j < softButtonObjects.size(); j++) {
+ if (softButtonObjects.get(j).getName().equals(buttonName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get the TextField1
+ * @return currentMainField1
+ */
+ protected String getCurrentMainField1() {
+ if (currentMainField1 == null){
+ return "";
+ }
+ return currentMainField1;
+ }
+
+ /**
+ * Set the TextField1
+ * @param currentMainField1
+ */
+ protected void setCurrentMainField1(String currentMainField1) {
+ this.currentMainField1 = currentMainField1;
+ }
+
+ /**
+ * Set the batchUpdates flag that represents whether the manager should wait until commit() is called to send the updated show RPC
+ * @param batchUpdates
+ */
+ protected void setBatchUpdates(boolean batchUpdates) {
+ this.batchUpdates = batchUpdates;
+ }
+
+ /**
+ * Clean up everything after the manager is no longer needed
+ */
+ @Override
+ public void dispose() {
+ super.dispose();
+
+ transitionToState(SHUTDOWN);
+
+ // Remove listeners
+ internalInterface.removeOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, onHMIStatusListener);
+ internalInterface.removeOnRPCNotificationListener(FunctionID.ON_BUTTON_PRESS, onButtonPressListener);
+ internalInterface.removeOnRPCNotificationListener(FunctionID.ON_BUTTON_EVENT, onButtonEventListener);
+ internalInterface.removeOnSystemCapabilityListener(SystemCapabilityType.SOFTBUTTON, onSoftButtonCapabilitiesListener);
+ internalInterface.removeOnSystemCapabilityListener(SystemCapabilityType.DISPLAY, onDisplayCapabilitiesListener);
+ }
+
+ /**
+ * Check if the current state for any SoftButtonObject has images
+ * @return a boolean value
+ */
+ private boolean currentStateHasImages() {
+ for (SoftButtonObject softButtonObject : this.softButtonObjects) {
+ if (softButtonObject.getCurrentState() != null && softButtonObject.getCurrentState().getArtwork() != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Check if the current state for any SoftButtonObject has images that are not uploaded yet
+ * @return a boolean value
+ */
+ private boolean allCurrentStateImagesAreUploaded() {
+ if (fileManager.get() != null) {
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ SoftButtonState currentState = softButtonObject.getCurrentState();
+ if (currentState != null && currentState.getArtwork() != null && !fileManager.get().hasUploadedFile(currentState.getArtwork())) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns text soft buttons representing the initial states of the button objects, or null if _any_ of the buttons' current states are image only buttons.
+ * @return The text soft buttons
+ */
+ private List<SoftButton> createTextSoftButtonsForCurrentState() {
+ List<SoftButton> textButtons = new ArrayList<>();
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ SoftButton softButton = softButtonObject.getCurrentStateSoftButton();
+ if (softButton.getText() == null) {
+ return null;
+ }
+ // We should create a new softButtonObject rather than modifying the original one
+ SoftButton textOnlySoftButton = new SoftButton(SoftButtonType.SBT_TEXT, softButton.getSoftButtonID());
+ textOnlySoftButton.setText(softButton.getText());
+ textButtons.add(textOnlySoftButton);
+ }
+ return textButtons;
+ }
+
+ /**
+ * Returns a list of the SoftButton for the SoftButtonObjects' current state
+ * @return a List<SoftButton>
+ */
+ protected List<SoftButton> createSoftButtonsForCurrentState() {
+ List<SoftButton> softButtons = new ArrayList<>();
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ softButtons.add(softButtonObject.getCurrentStateSoftButton());
+ }
+ return softButtons;
+ }
+}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/api/screen/SoftButtonObject.java b/sdl_android/src/main/java/com/smartdevicelink/api/screen/SoftButtonObject.java
new file mode 100644
index 000000000..ca734193f
--- /dev/null
+++ b/sdl_android/src/main/java/com/smartdevicelink/api/screen/SoftButtonObject.java
@@ -0,0 +1,271 @@
+package com.smartdevicelink.api.screen;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import com.smartdevicelink.proxy.rpc.OnButtonEvent;
+import com.smartdevicelink.proxy.rpc.OnButtonPress;
+import com.smartdevicelink.proxy.rpc.SoftButton;
+
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * <strong>SoftButtonObject</strong> <br>
+ * SoftButtonObject define a button that can have multiple SoftButtonState values.<br>
+ * The states of SoftButtonObject allow the developer to not have to manage multiple SoftButtons that have very similar functionality.<br>
+ * For example, a repeat button in a music app can be thought of as one SoftButtonObject with three typical states: repeat off, repeat 1, and repeat on.<br>
+ * @see SoftButtonState
+ */
+public class SoftButtonObject {
+
+ private static final String TAG = "SoftButtonObject";
+ private String name;
+ private List<SoftButtonState> states;
+ private String currentStateName;
+ private int buttonId;
+ private OnEventListener onEventListener;
+ private UpdateListener updateListener;
+
+ /**
+ * Create a new instance of the SoftButtonObject with multiple states
+ * @param name a String value represents name of the object
+ * @param states a list of SoftButtonState represents the SoftButtonState values for the object
+ * @param initialStateName a String value represents the name for the initial state
+ * @param onEventListener a listener that has a callback that will be triggered when a button event happens
+ * Note: the initialStateName should match exactly the name of one of the states for the object. Otherwise an exception will be thrown.
+ */
+ public SoftButtonObject(@NonNull String name, @NonNull List<SoftButtonState> states, @NonNull String initialStateName, OnEventListener onEventListener) {
+
+ // Make sure there aren't two states with the same name
+ if (hasTwoStatesOfSameName(states)) {
+ Log.e(TAG, "Two states have the same name in states list for soft button object");
+ return;
+ }
+
+ this.name = name;
+ this.states = states;
+ currentStateName = initialStateName;
+ this.buttonId = 0;
+ this.onEventListener = onEventListener;
+ }
+
+ /**
+ * Create a new instance of the SoftButtonObject with one state
+ * @param name a String value represents name of the object
+ * @param state a SoftButtonState represents state for the object
+ * @param onEventListener a listener that has a callback that will be triggered when a button event happens
+ */
+ public SoftButtonObject(@NonNull String name, @NonNull SoftButtonState state, OnEventListener onEventListener) {
+ this(name, Collections.singletonList(state), state.getName(), onEventListener);
+ }
+
+ /**
+ * Transition the SoftButtonObject to a specific state
+ * @param newStateName a String value represents the name fo the state that we want to transition the SoftButtonObject to
+ * @return a boolean value that represents whether the transition succeeded or failed
+ */
+ public boolean transitionToStateByName(@NonNull String newStateName) {
+ SoftButtonState newState = getStateByName(newStateName);
+ if (newState == null) {
+ Log.e(TAG, String.format("Attempted to transition to state: %s on soft button object: %s but no state with that name was found", newStateName, this.name));
+ return false;
+ }
+ Log.i(TAG, String.format("Transitioning soft button object %s to state %s", this.name, newStateName));
+ currentStateName = newStateName;
+
+ // Send a new Show RPC because the state has changed which means the actual SoftButton has changed
+ if (updateListener != null) {
+ updateListener.onUpdate();
+ } else {
+ Log.e(TAG, String.format("SoftButtonManager is not set for soft button object: %s. Update cannot be triggered", this.name));
+ }
+
+ return true;
+ }
+
+ /**
+ * Transition the SoftButtonObject to the next state
+ */
+ public void transitionToNextState() {
+ String nextStateName = null;
+ for (int i = 0; i < states.size(); i++) {
+ if (states.get(i).getName().equals(currentStateName)) {
+ if (i == (states.size() - 1)) {
+ nextStateName = states.get(0).getName();
+ } else {
+ nextStateName = states.get(i + 1).getName();
+ }
+ break;
+ }
+ }
+ if (nextStateName == null) {
+ Log.e(TAG, String.format("Current state name : %s cannot be found for soft button object %s", currentStateName, this.name));
+ return;
+ }
+ transitionToStateByName(nextStateName);
+ }
+
+ /**
+ * Get the current state for the SoftButtonObject
+ * @return a SoftButtonState represents the current state
+ */
+ public SoftButtonState getCurrentState() {
+ SoftButtonState state = getStateByName(currentStateName);
+ if (state == null) {
+ Log.e(TAG, String.format("Current state name : %s cannot be found for soft button object %s", currentStateName, this.name));
+ }
+ return state;
+ }
+
+ /**
+ * Get the SoftButton object for the current state
+ * @return a SoftButton object that is associated with the current state
+ */
+ public SoftButton getCurrentStateSoftButton() {
+ SoftButtonState currentState = getCurrentState();
+ if (currentState == null || currentState.getSoftButton() == null) {
+ return null;
+ }
+
+ SoftButton softButton = currentState.getSoftButton();
+ softButton.setSoftButtonID(this.buttonId);
+ return softButton;
+ }
+
+ /**
+ * Find and get the SoftButtonState that has the provided name
+ * @param stateName a String value that represents the name of the state
+ * @return a SoftButtonState object that represents the state that has the provided name
+ */
+ private SoftButtonState getStateByName(String stateName) {
+ if (stateName != null && states != null) {
+ for (SoftButtonState state : states) {
+ if (state.getName().equals(stateName)) {
+ return state;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Check if two SoftButtonState have the same name
+ * @param states a list of SoftButtonState
+ * @return a boolean value that represents whether we have two states with the same name
+ */
+ private boolean hasTwoStatesOfSameName(List<SoftButtonState> states) {
+ for (int i = 0; i < states.size(); i++) {
+ String stateName = states.get(i).getName();
+ for (int j = (i + 1); j < states.size(); j++) {
+ if (states.get(j).getName().equals(stateName)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Set the SoftButtonManager's update listener
+ * @param updateListener the SoftButtonManager.UpdateListener object
+ */
+ protected void setUpdateListener(UpdateListener updateListener) {
+ this.updateListener = updateListener;
+ }
+
+ /**
+ * Get the name of the SoftButtonObject
+ * @return a String that represents the name of the SoftButtonObject
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the name of the SoftButtonObject
+ * @param name a String that represents the name of the SoftButtonObject
+ */
+ public void setName(@NonNull String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the SoftButtonState list
+ * @return a list of the object's soft button states
+ */
+ public List<SoftButtonState> getStates() {
+ return states;
+ }
+
+ /**
+ * Set the the SoftButtonState list
+ * @param states a list of the object's soft button states
+ */
+ public void setStates(@NonNull List<SoftButtonState> states) {
+ this.states = states;
+ }
+
+ /**
+ * Get the name of the current state
+ * @return a String that represents the name of the current state
+ */
+ public String getCurrentStateName() {
+ return currentStateName;
+ }
+
+ /**
+ * Set the name of the current state
+ * @param currentStateName a String that represents the name of the current state
+ */
+ public void setCurrentStateName(@NonNull String currentStateName) {
+ this.currentStateName = currentStateName;
+ }
+
+ /**
+ * Get the dd of the SoftButtonObject
+ * @return an int value that represents the id of the SoftButtonObject
+ */
+ public int getButtonId() {
+ return buttonId;
+ }
+
+ /**
+ * Set the id of the SoftButtonObject
+ * @param buttonId an int value that represents the id of the SoftButtonObject
+ */
+ public void setButtonId(int buttonId) {
+ this.buttonId = buttonId;
+ }
+
+ /**
+ * Get the event listener for the SoftButtonObject
+ * @return OnEventListener
+ */
+ public OnEventListener getOnEventListener() {
+ return onEventListener;
+ }
+
+ /**
+ * Set the event listener for the SoftButtonObject
+ * @param onEventListener a listener that has a callback that will be triggered when a button event happens
+ */
+ public void setOnEventListener(OnEventListener onEventListener) {
+ this.onEventListener = onEventListener;
+ }
+
+ public interface OnEventListener{
+ void onPress(SoftButtonObject softButtonObject, OnButtonPress onButtonPress);
+ void onEvent(SoftButtonObject softButtonObject, OnButtonEvent onButtonEvent);
+ }
+
+ /**
+ * A listener interface that is used by SoftButtonObject to request an update from SoftButtonManager
+ */
+ interface UpdateListener{
+ /**
+ * Requests an update from SoftButtonManager
+ */
+ void onUpdate();
+ }
+}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/api/screen/SoftButtonState.java b/sdl_android/src/main/java/com/smartdevicelink/api/screen/SoftButtonState.java
new file mode 100644
index 000000000..da9f23298
--- /dev/null
+++ b/sdl_android/src/main/java/com/smartdevicelink/api/screen/SoftButtonState.java
@@ -0,0 +1,97 @@
+package com.smartdevicelink.api.screen;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import com.smartdevicelink.api.datatypes.SdlArtwork;
+import com.smartdevicelink.proxy.rpc.Image;
+import com.smartdevicelink.proxy.rpc.SoftButton;
+import com.smartdevicelink.proxy.rpc.enums.ImageType;
+import com.smartdevicelink.proxy.rpc.enums.SoftButtonType;
+
+/**
+ * <strong>SoftButtonState</strong> <br>
+ * Defines an individual state for SoftButtonObject.<br>
+ * The states of SoftButtonObject allow the developer to not have to manage multiple SoftButtons that have very similar functionality.<br>
+ * For example, a repeat button in a music app can be thought of as one SoftButtonObject with three typical states: repeat off, repeat 1, and repeat on.<br>
+ * @see SoftButtonObject
+ */
+public class SoftButtonState {
+
+ private static final String TAG = "SoftButtonState";
+ private String name;
+ private SdlArtwork artwork;
+ private final SoftButton softButton;
+
+ /**
+ * Creates a new instance of SoftButtonState
+ * Note: state names should be different for each SoftButtonObject
+ * @param name a String value represents name of the state
+ * @param text a String represents the text for the state
+ * @param artwork an SdlArtwork represents the artwork for the state
+ */
+ public SoftButtonState(@NonNull String name, String text, SdlArtwork artwork) {
+ if (text == null && artwork == null) {
+ Log.e(TAG, "Attempted to create an invalid soft button state: text and artwork are both null");
+ softButton = null;
+ return;
+ }
+ this.name = name;
+ this.artwork = artwork;
+
+
+ // Create a SoftButton and set its Type
+ SoftButtonType type;
+ if (artwork != null && text != null) {
+ type = SoftButtonType.SBT_BOTH;
+ } else if (artwork != null) {
+ type = SoftButtonType.SBT_IMAGE;
+ } else {
+ type = SoftButtonType.SBT_TEXT;
+ }
+ this.softButton = new SoftButton(type, 0);
+
+
+ // Set the SoftButton's image
+ if (artwork != null) {
+ softButton.setImage(new Image(artwork.getName(), ImageType.DYNAMIC));
+ }
+
+ // Set the SoftButton's text
+ if (text != null) {
+ softButton.setText(text);
+ }
+ }
+
+ /**
+ * Get the state name
+ * @return a String value represents the name of the state
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Set the state name
+ * @param name a String value represents the name of the state
+ */
+ public void setName(@NonNull String name) {
+ this.name = name;
+ }
+
+ /**
+ * Get the SoftButton for the state
+ * @return a SoftButton object represents the SoftButton for the state
+ */
+ public SoftButton getSoftButton() {
+ return softButton;
+ }
+
+ /**
+ * Get the Artwork for the state
+ * @return an SdlArtwork object represents the artwork for the state
+ */
+ public SdlArtwork getArtwork() {
+ return artwork;
+ }
+}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/api/screen/TextAndGraphicManager.java b/sdl_android/src/main/java/com/smartdevicelink/api/screen/TextAndGraphicManager.java
new file mode 100644
index 000000000..cabc02067
--- /dev/null
+++ b/sdl_android/src/main/java/com/smartdevicelink/api/screen/TextAndGraphicManager.java
@@ -0,0 +1,894 @@
+package com.smartdevicelink.api.screen;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import com.smartdevicelink.R;
+import com.smartdevicelink.api.BaseSubManager;
+import com.smartdevicelink.api.CompletionListener;
+import com.smartdevicelink.api.FileManager;
+import com.smartdevicelink.api.MultipleFileCompletionListener;
+import com.smartdevicelink.api.datatypes.SdlArtwork;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.RPCNotification;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.interfaces.ISdl;
+import com.smartdevicelink.proxy.interfaces.OnSystemCapabilityListener;
+import com.smartdevicelink.proxy.rpc.DisplayCapabilities;
+import com.smartdevicelink.proxy.rpc.Image;
+import com.smartdevicelink.proxy.rpc.MetadataTags;
+import com.smartdevicelink.proxy.rpc.OnHMIStatus;
+import com.smartdevicelink.proxy.rpc.Show;
+import com.smartdevicelink.proxy.rpc.TextField;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.HMILevel;
+import com.smartdevicelink.proxy.rpc.enums.ImageType;
+import com.smartdevicelink.proxy.rpc.enums.MetadataType;
+import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
+import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
+import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import static com.smartdevicelink.proxy.rpc.enums.TextAlignment.CENTERED;
+
+/**
+ * <strong>TextAndGraphicManager</strong> <br>
+ *
+ * Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br>
+ *
+ */
+class TextAndGraphicManager extends BaseSubManager {
+
+ private static final String TAG = "TextAndGraphicManager";
+
+ boolean isDirty, hasQueuedUpdate;
+ volatile Show inProgressUpdate;
+ Show currentScreenData, queuedImageUpdate;
+ HMILevel currentHMILevel;
+ protected DisplayCapabilities displayCapabilities;
+ private boolean pendingHMIFull, batchingUpdates;
+ private final WeakReference<FileManager> fileManager;
+ private final WeakReference<SoftButtonManager> softButtonManager;
+ private CompletionListener queuedUpdateListener, inProgressListener, pendingHMIListener;
+ private SdlArtwork blankArtwork;
+ private OnRPCNotificationListener hmiListener;
+ private OnSystemCapabilityListener onDisplayCapabilitiesListener;
+ private SdlArtwork primaryGraphic, secondaryGraphic;
+ private TextAlignment textAlignment;
+ private String textField1, textField2, textField3, textField4, mediaTrackTextField;
+ private MetadataType textField1Type, textField2Type, textField3Type, textField4Type;
+
+ //Constructors
+
+ TextAndGraphicManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager, @NonNull SoftButtonManager softButtonManager) {
+ // set class vars
+ super(internalInterface);
+ this.fileManager = new WeakReference<>(fileManager);
+ this.softButtonManager = new WeakReference<>(softButtonManager);
+ batchingUpdates = false;
+ isDirty = false;
+ pendingHMIFull = false;
+ textAlignment = CENTERED;
+ currentHMILevel = HMILevel.HMI_NONE;
+ currentScreenData = new Show();
+ addListeners();
+ getBlankArtwork();
+ }
+
+ @Override
+ public void start(CompletionListener listener) {
+ transitionToState(READY);
+ super.start(listener);
+ }
+
+ @Override
+ public void dispose(){
+
+ textField1 = null;
+ textField1Type = null;
+ textField2 = null;
+ textField2Type = null;
+ textField3 = null;
+ textField3Type = null;
+ textField4 = null;
+ textField4Type = null;
+ mediaTrackTextField = null;
+ textAlignment = null;
+ primaryGraphic = null;
+ secondaryGraphic = null;
+ blankArtwork = null;
+ displayCapabilities = null;
+ inProgressUpdate = null;
+ queuedImageUpdate = null;
+ currentScreenData = null;
+ queuedUpdateListener = null;
+ pendingHMIListener = null;
+ inProgressListener = null;
+ hasQueuedUpdate = false;
+ isDirty = false;
+ pendingHMIFull = false;
+
+ // remove listeners
+ internalInterface.removeOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, hmiListener);
+ internalInterface.removeOnSystemCapabilityListener(SystemCapabilityType.DISPLAY, onDisplayCapabilitiesListener);
+
+ super.dispose();
+ }
+
+ private void addListeners() {
+ // add listener
+ hmiListener = new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+ currentHMILevel = ((OnHMIStatus)notification).getHmiLevel();
+ if (currentHMILevel == HMILevel.HMI_FULL){
+ if (pendingHMIFull){
+ Log.v(TAG, "Acquired HMI_FULL with pending update. Sending now");
+ pendingHMIFull = false;
+ sdlUpdate(pendingHMIListener);
+ pendingHMIListener = null;
+ }
+ }
+ }
+ };
+ internalInterface.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, hmiListener);
+
+ // Add OnDisplayCapabilitiesListener to keep displayCapabilities updated
+ onDisplayCapabilitiesListener = new OnSystemCapabilityListener() {
+ @Override
+ public void onCapabilityRetrieved(Object capability) {
+ displayCapabilities = (DisplayCapabilities)capability;
+ }
+
+ @Override
+ public void onError(String info) {
+ Log.e(TAG, "DISPLAY Capability cannot be retrieved:");
+ displayCapabilities = null;
+ }
+ };
+ this.internalInterface.addOnSystemCapabilityListener(SystemCapabilityType.DISPLAY, onDisplayCapabilitiesListener);
+ }
+
+ // Upload / Send
+
+ protected void update(CompletionListener listener) {
+
+ // check if is batch update
+ if (batchingUpdates) {
+ return;
+ }
+
+ if (isDirty){
+ isDirty = false;
+ sdlUpdate(listener);
+ }
+ }
+
+ private synchronized void sdlUpdate(CompletionListener listener){
+
+ // make sure hmi is not none
+ if (currentHMILevel == null || currentHMILevel == HMILevel.HMI_NONE){
+ //Trying to send show on HMI_NONE, waiting for full
+ pendingHMIFull = true;
+ if (listener != null){
+ pendingHMIListener = listener;
+ }
+ return;
+ }
+
+ //Updating Text and Graphics
+ if (inProgressUpdate != null){
+
+ //In progress update exists, queueing update
+ if (queuedUpdateListener != null){
+
+ //Queued update already exists, superseding previous queued update
+ queuedUpdateListener.onComplete(false);
+ queuedUpdateListener = null;
+ }
+
+ if (listener != null){
+ queuedUpdateListener = listener;
+ }else{
+ hasQueuedUpdate = true;
+ }
+ return;
+ }
+
+ Show fullShow = new Show();
+ fullShow.setAlignment(textAlignment);
+ fullShow = assembleShowText(fullShow);
+ fullShow = assembleShowImages(fullShow);
+
+ inProgressListener = listener;
+
+ if (!shouldUpdatePrimaryImage() && !shouldUpdateSecondaryImage()){
+
+ //No Images to send, only sending text
+ inProgressUpdate = extractTextFromShow(fullShow);
+ sendShow();
+
+ }else if (isArtworkUploadedOrDoesntExist(primaryGraphic) && ( secondaryGraphic == blankArtwork || isArtworkUploadedOrDoesntExist(secondaryGraphic))){
+
+ //Images already uploaded, sending full update
+ // The files to be updated are already uploaded, send the full show immediately
+ inProgressUpdate = fullShow;
+ sendShow();
+ } else{
+
+ // Images need to be uploaded, sending text and uploading images
+ inProgressUpdate = fullShow;
+ final Show thisUpdate = fullShow;
+
+ uploadImages(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ if (!success){
+ Log.e(TAG, "Error uploading image");
+ inProgressUpdate = extractTextFromShow(inProgressUpdate);
+ sendShow();
+ }
+ // Check if queued image update still matches our images (there could have been a new Show in the meantime)
+ // and send a new update if it does. Since the images will already be on the head unit, the whole show will be sent
+ if (thisUpdate.getGraphic() != null && thisUpdate.getGraphic().equals(queuedImageUpdate.getGraphic()) ||
+ (thisUpdate.getSecondaryGraphic() != null && queuedImageUpdate.getSecondaryGraphic() != null) && thisUpdate.getSecondaryGraphic().equals(queuedImageUpdate.getSecondaryGraphic())){
+ // Queued image update matches the images we need, sending update
+ sendShow();
+ }
+ // Else, Queued image update does not match the images we need, skipping update
+ }
+ });
+ queuedImageUpdate = fullShow;
+ }
+ }
+
+ private void sendShow(){
+ inProgressUpdate.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (response.getSuccess()){
+ updateCurrentScreenDataState(inProgressUpdate);
+ }
+
+ inProgressUpdate = null;
+ if (inProgressListener != null){
+ inProgressListener.onComplete(true);
+ inProgressListener = null;
+ }
+
+ if (hasQueuedUpdate){
+ //Queued update exists, sending another update
+ hasQueuedUpdate = false;
+ CompletionListener temp = queuedUpdateListener;
+ queuedUpdateListener = null;
+ sdlUpdate(temp);
+ }
+ }
+ });
+
+ if (this.softButtonManager.get() != null) {
+ this.softButtonManager.get().setCurrentMainField1(inProgressUpdate.getMainField1());
+ }
+ internalInterface.sendRPCRequest(inProgressUpdate);
+ }
+
+ // Images
+
+ private void uploadImages(final CompletionListener listener) {
+
+ List<SdlArtwork> artworksToUpload = new ArrayList<>();
+
+ // add primary image
+ if (shouldUpdatePrimaryImage()){
+ artworksToUpload.add(primaryGraphic);
+ }
+
+ // add secondary image
+ if (shouldUpdateSecondaryImage()){
+ artworksToUpload.add(secondaryGraphic);
+ }
+
+ // use file manager to upload art
+ if (fileManager.get() != null) {
+ fileManager.get().uploadArtworks(artworksToUpload, new MultipleFileCompletionListener() {
+ @Override
+ public void onComplete(Map<String, String> errors) {
+ if (errors != null) {
+ Log.e(TAG, "Error Uploading Artworks. Error: " + errors.toString());
+ listener.onComplete(false);
+ } else {
+ listener.onComplete(true);
+ }
+ }
+ });
+ }
+ }
+
+ private Show assembleShowImages(Show show){
+
+ if (shouldUpdatePrimaryImage()){
+ Image primaryImage = new Image();
+ primaryImage.setImageType(ImageType.DYNAMIC);
+ primaryImage.setValue(primaryGraphic.getName());
+ show.setGraphic(primaryImage);
+ }
+
+ if (shouldUpdateSecondaryImage()){
+ Image secondaryImage = new Image();
+ secondaryImage.setImageType(ImageType.DYNAMIC);
+ secondaryImage.setValue(secondaryGraphic.getName());
+ show.setSecondaryGraphic(secondaryImage);
+ }
+
+ return show;
+ }
+
+ // Text
+
+ Show assembleShowText(Show show){
+
+ show = setBlankTextFields(show);
+
+ if (mediaTrackTextField != null){
+ show.setMediaTrack(mediaTrackTextField);
+ }
+
+ List<String> nonNullFields = findValidMainTextFields();
+ if (nonNullFields.isEmpty()){
+ return show;
+ }
+
+ int numberOfLines = getNumberOfLines();
+
+ switch (numberOfLines) {
+ case 1: show = assembleOneLineShowText(show, nonNullFields);
+ break;
+ case 2: show = assembleTwoLineShowText(show);
+ break;
+ case 3: show = assembleThreeLineShowText(show);
+ break;
+ case 4: show = assembleFourLineShowText(show);
+ break;
+ }
+
+ return show;
+ }
+
+ private Show assembleOneLineShowText(Show show, List<String> showFields){
+
+ StringBuilder showString1 = new StringBuilder();
+ for (int i = 0; i < showFields.size(); i++) {
+ if (i > 0) {
+ showString1.append(" - ").append(showFields.get(i));
+ }else{
+ showString1.append(showFields.get(i));
+ }
+ }
+ show.setMainField1(showString1.toString());
+
+ MetadataTags tags = new MetadataTags();
+ tags.setMainField1(findNonNullMetadataFields());
+
+ show.setMetadataTags(tags);
+
+ return show;
+ }
+
+ private Show assembleTwoLineShowText(Show show){
+
+ StringBuilder tempString = new StringBuilder();
+ MetadataTags tags = new MetadataTags();
+
+ if (textField1 != null && textField1.length() > 0) {
+ tempString.append(textField1);
+ if (textField1Type != null){
+ tags.setMainField1(textField1Type);
+ }
+ }
+
+ if (textField2 != null && textField2.length() > 0) {
+ if (( textField3 == null || !(textField3.length() > 0)) && (textField4 == null || !(textField4.length() > 0))){
+ // text does not exist in slots 3 or 4, put text2 in slot 2
+ show.setMainField2(textField2);
+ if (textField2Type != null){
+ tags.setMainField2(textField2Type);
+ }
+ } else if (textField1 != null && textField1.length() > 0) {
+ // If text 1 exists, put it in slot 1 formatted
+ tempString.append(" - ").append(textField2);
+ if (textField2Type != null){
+ List<MetadataType> typeList = new ArrayList<>();
+ typeList.add(textField2Type);
+ if (textField1Type != null){
+ typeList.add(textField1Type);
+ }
+ tags.setMainField1(typeList);
+ }
+ }else {
+ // If text 1 does not exist, put it in slot 1 unformatted
+ tempString.append(textField2);
+ if (textField2Type != null){
+ tags.setMainField1(textField2Type);
+ }
+ }
+ }
+
+ // set mainfield 1
+ show.setMainField1(tempString.toString());
+
+ // new stringbuilder object
+ tempString = new StringBuilder();
+
+ if (textField3 != null && textField3.length() > 0){
+ // If text 3 exists, put it in slot 2
+ tempString.append(textField3);
+ if (textField3Type != null){
+ List<MetadataType> typeList = new ArrayList<>();
+ typeList.add(textField3Type);
+ tags.setMainField2(typeList);
+ }
+ }
+
+ if (textField4 != null && textField4.length() > 0){
+ if (textField3 != null && textField3.length() > 0){
+ // If text 3 exists, put it in slot 2 formatted
+ tempString.append(" - ").append(textField4);
+ if (textField4Type != null){
+ List<MetadataType> typeList = new ArrayList<>();
+ typeList.add(textField4Type);
+ if (textField3Type != null){
+ typeList.add(textField3Type);
+ }
+ tags.setMainField2(typeList);
+ }
+ } else {
+ // If text 3 does not exist, put it in slot 3 unformatted
+ tempString.append(textField4);
+ if (textField4Type != null){
+ tags.setMainField2(textField4Type);
+ }
+ }
+ }
+
+ if (tempString.toString().length() > 0){
+ show.setMainField2(tempString.toString());
+ }
+
+ show.setMetadataTags(tags);
+ return show;
+ }
+
+ private Show assembleThreeLineShowText(Show show){
+
+ MetadataTags tags = new MetadataTags();
+
+ if (textField1 != null && textField1.length() > 0) {
+ show.setMainField1(textField1);
+ if (textField1Type != null){
+ tags.setMainField1(textField1Type);
+ }
+ }
+
+ if (textField2 != null && textField2.length() > 0) {
+ show.setMainField2(textField2);
+ if (textField2Type != null){
+ tags.setMainField2(textField2Type);
+ }
+ }
+
+ StringBuilder tempString = new StringBuilder();
+
+ if (textField3 != null && textField3.length() > 0){
+ tempString.append(textField3);
+ if (textField3Type != null){
+ tags.setMainField3(textField3Type);
+ }
+ }
+
+ if (textField4 != null && textField4.length() > 0) {
+ if (textField3 != null && textField3.length() > 0) {
+ // If text 3 exists, put it in slot 3 formatted
+ tempString.append(" - ").append(textField4);
+ if (textField4Type != null){
+ List<MetadataType> tags4 = new ArrayList<>();
+ if (textField3Type != null){
+ tags4.add(textField3Type);
+ }
+ tags4.add(textField4Type);
+ tags.setMainField3(tags4);
+ }
+ } else {
+ // If text 3 does not exist, put it in slot 3 formatted
+ tempString.append(textField4);
+ if (textField4Type != null){
+ tags.setMainField3(textField4Type);
+ }
+ }
+ }
+
+ show.setMainField3(tempString.toString());
+ show.setMetadataTags(tags);
+ return show;
+ }
+
+ private Show assembleFourLineShowText(Show show){
+
+ MetadataTags tags = new MetadataTags();
+
+ if (textField1 != null && textField1.length() > 0) {
+ show.setMainField1(textField1);
+ if (textField1Type != null){
+ tags.setMainField1(textField1Type);
+ }
+ }
+
+ if (textField2 != null && textField2.length() > 0) {
+ show.setMainField2(textField2);
+ if (textField2Type != null){
+ tags.setMainField2(textField2Type);
+ }
+ }
+
+ if (textField3 != null && textField3.length() > 0) {
+ show.setMainField3(textField3);
+ if (textField3Type != null){
+ tags.setMainField3(textField3Type);
+ }
+ }
+
+ if (textField4 != null && textField4.length() > 0) {
+ show.setMainField4(textField4);
+ if (textField4Type != null){
+ tags.setMainField4(textField4Type);
+ }
+ }
+
+ show.setMetadataTags(tags);
+ return show;
+ }
+
+ // Extraction
+
+ Show extractTextFromShow(Show show){
+
+ Show newShow = new Show();
+ newShow.setMainField1(show.getMainField1());
+ newShow.setMainField2(show.getMainField2());
+ newShow.setMainField3(show.getMainField3());
+ newShow.setMainField4(show.getMainField4());
+
+ return newShow;
+ }
+
+ private Show setBlankTextFields(Show newShow){
+
+ newShow.setMainField1("");
+ newShow.setMainField2("");
+ newShow.setMainField3("");
+ newShow.setMainField4("");
+ newShow.setMediaTrack("");
+
+ return newShow;
+ }
+
+ private void updateCurrentScreenDataState(Show show){
+
+ if (show == null){
+ Log.e(TAG, "can not updateCurrentScreenDataFromShow from null show");
+ return;
+ }
+
+ // If the items are null, they were not updated, so we can't just set it directly
+ if (show.getMainField1() != null){
+ currentScreenData.setMainField1(show.getMainField1());
+ }
+ if (show.getMainField2() != null){
+ currentScreenData.setMainField2(show.getMainField2());
+ }
+ if (show.getMainField3() != null){
+ currentScreenData.setMainField3(show.getMainField3());
+ }
+ if (show.getMainField4() != null){
+ currentScreenData.setMainField4(show.getMainField4());
+ }
+ if (show.getMediaTrack() != null){
+ currentScreenData.setMediaTrack(show.getMediaTrack());
+ }
+ if (show.getMetadataTags() != null){
+ currentScreenData.setMetadataTags(show.getMetadataTags());
+ }
+ if (show.getAlignment() != null){
+ currentScreenData.setAlignment(show.getAlignment());
+ }
+ if (show.getGraphic() != null){
+ currentScreenData.setGraphic(show.getGraphic());
+ }
+ if (show.getSecondaryGraphic() != null){
+ currentScreenData.setSecondaryGraphic(show.getSecondaryGraphic());
+ }
+ }
+
+ // Helpers
+
+ private List<String> findValidMainTextFields(){
+ List<String> array = new ArrayList<>();
+
+ if (textField1 != null && textField1.length() > 0) {
+ array.add(textField1);
+ }
+
+ if (textField2 != null && textField2.length() > 0) {
+ array.add(textField2);
+ }
+
+ if (textField3 != null && textField3.length() > 0) {
+ array.add(textField3);
+ }
+
+ if (textField4 != null && textField4.length() > 0) {
+ array.add(textField4);
+ }
+
+ return array;
+ }
+
+
+ private List<MetadataType> findNonNullMetadataFields(){
+ List<MetadataType> array = new ArrayList<>();
+
+ if (textField1Type != null) {
+ array.add(textField1Type);
+ }
+
+ if (textField2Type != null) {
+ array.add(textField2Type);
+ }
+
+ if (textField3Type != null) {
+ array.add(textField3Type);
+ }
+
+ if (textField4Type != null) {
+ array.add(textField4Type);
+ }
+
+ return array;
+ }
+
+ SdlArtwork getBlankArtwork(){
+
+ if (blankArtwork != null){
+ blankArtwork = new SdlArtwork();
+ blankArtwork.setType(FileType.GRAPHIC_PNG);
+ blankArtwork.setName("blankArtwork");
+ blankArtwork.setResourceId(R.drawable.transparent);
+ }
+ return blankArtwork;
+ }
+
+ private boolean isArtworkUploadedOrDoesntExist(SdlArtwork artwork){
+
+ if (fileManager.get() != null){
+ return artwork != null && fileManager.get().hasUploadedFile(artwork);
+ }
+
+ return false;
+ }
+
+ private boolean shouldUpdatePrimaryImage() {
+ if (displayCapabilities == null || displayCapabilities.getGraphicSupported()) {
+ if (currentScreenData.getGraphic() == null && primaryGraphic != null) {
+ return true;
+ } else if (currentScreenData.getGraphic() == null && primaryGraphic == null) {
+ return false;
+ }
+ return currentScreenData != null && (primaryGraphic != null && !currentScreenData.getGraphic().getValue().equalsIgnoreCase(primaryGraphic.getName()));
+ }
+ return false;
+ }
+
+ private boolean shouldUpdateSecondaryImage() {
+ // Cannot detect if there is a secondary image, so we'll just try to detect if there's a primary image and allow it if there is.
+ if (displayCapabilities == null || displayCapabilities.getGraphicSupported()) {
+ if (currentScreenData.getGraphic() == null && secondaryGraphic != null) {
+ return true;
+ } else if (currentScreenData.getGraphic() == null && secondaryGraphic == null) {
+ return false;
+ }
+ return currentScreenData != null && (secondaryGraphic != null && !currentScreenData.getGraphic().getValue().equalsIgnoreCase(secondaryGraphic.getName()));
+ }
+ return false;
+ }
+
+ int getNumberOfLines() {
+
+ if (displayCapabilities == null){
+ return 4;
+ }
+
+ int linesFound = 0;
+
+ List<TextField> textFields = displayCapabilities.getTextFields();
+ TextFieldName name;
+ for (TextField field : textFields) {
+ if (field.getName() != null) {
+ name = field.getName();
+ if (name == TextFieldName.mainField1 || name == TextFieldName.mainField2 || name == TextFieldName.mainField3 || name == TextFieldName.mainField4) {
+ linesFound += 1;
+ }
+ }
+ }
+
+ return linesFound;
+ }
+
+ // SCREEN ITEM SETTERS AND GETTERS
+
+ void setTextAlignment(TextAlignment textAlignment){
+ this.textAlignment = textAlignment;
+ // If we aren't batching, send the update immediately, if we are, set ourselves as dirty (so we know we should send an update after the batch ends)
+ if (!batchingUpdates){
+ sdlUpdate(null);
+ }else{
+ isDirty = true;
+ }
+ }
+
+ TextAlignment getTextAlignment(){
+ return textAlignment;
+ }
+
+ void setMediaTrackTextField(String mediaTrackTextField){
+ this.mediaTrackTextField = mediaTrackTextField;
+ if (!batchingUpdates){
+ sdlUpdate(null);
+ }else{
+ isDirty = true;
+ }
+ }
+
+ String getMediaTrackTextField(){
+ return mediaTrackTextField;
+ }
+
+ void setTextField1(String textField1){
+ this.textField1 = textField1;
+ if (!batchingUpdates){
+ sdlUpdate(null);
+ }else{
+ isDirty = true;
+ }
+ }
+
+ String getTextField1(){
+ return textField1;
+ }
+
+ void setTextField2(String textField2){
+ this.textField2 = textField2;
+ if (!batchingUpdates){
+ sdlUpdate(null);
+ }else{
+ isDirty = true;
+ }
+ }
+
+ String getTextField2(){
+ return textField2;
+ }
+
+ void setTextField3(String textField3){
+ this.textField3 = textField3;
+ if (!batchingUpdates){
+ sdlUpdate(null);
+ }else{
+ isDirty = true;
+ }
+ }
+
+ String getTextField3(){
+ return textField3;
+ }
+
+ void setTextField4(String textField4){
+ this.textField4 = textField4;
+ if (!batchingUpdates){
+ sdlUpdate(null);
+ }else{
+ isDirty = true;
+ }
+ }
+
+ String getTextField4(){
+ return textField4;
+ }
+
+ void setTextField1Type(MetadataType textField1Type){
+ this.textField1Type = textField1Type;
+ if (!batchingUpdates){
+ sdlUpdate(null);
+ }else{
+ isDirty = true;
+ }
+ }
+
+ MetadataType getTextField1Type(){
+ return textField1Type;
+ }
+
+ void setTextField2Type(MetadataType textField2Type){
+ this.textField2Type = textField2Type;
+ if (!batchingUpdates){
+ sdlUpdate(null);
+ }else{
+ isDirty = true;
+ }
+ }
+
+ MetadataType getTextField2Type(){
+ return textField2Type;
+ }
+
+ void setTextField3Type(MetadataType textField3Type){
+ this.textField3Type = textField3Type;
+ if (!batchingUpdates){
+ sdlUpdate(null);
+ }else{
+ isDirty = true;
+ }
+ }
+
+ MetadataType getTextField3Type(){
+ return textField3Type;
+ }
+
+ void setTextField4Type(MetadataType textField4Type){
+ this.textField4Type = textField4Type;
+ if (!batchingUpdates){
+ sdlUpdate(null);
+ }else{
+ isDirty = true;
+ }
+ }
+
+ MetadataType getTextField4Type(){
+ return textField4Type;
+ }
+
+ void setPrimaryGraphic(SdlArtwork primaryGraphic){
+ this.primaryGraphic = primaryGraphic;
+ if (!batchingUpdates){
+ sdlUpdate(null);
+ }else{
+ isDirty = true;
+ }
+ }
+
+ SdlArtwork getPrimaryGraphic(){
+ return primaryGraphic;
+ }
+
+ void setSecondaryGraphic(SdlArtwork secondaryGraphic){
+ this.secondaryGraphic = secondaryGraphic;
+ if (!batchingUpdates){
+ sdlUpdate(null);
+ }else{
+ isDirty = true;
+ }
+ }
+
+ SdlArtwork getSecondaryGraphic(){
+ return secondaryGraphic;
+ }
+
+ void setBatchUpdates(boolean batching){
+ this.batchingUpdates = batching;
+ }
+
+}
diff --git a/sdl_android/src/main/java/com/smartdevicelink/proxy/rpc/listeners/OnMultipleRequestListener.java b/sdl_android/src/main/java/com/smartdevicelink/proxy/rpc/listeners/OnMultipleRequestListener.java
index 2e60108ed..88f119bdd 100644
--- a/sdl_android/src/main/java/com/smartdevicelink/proxy/rpc/listeners/OnMultipleRequestListener.java
+++ b/sdl_android/src/main/java/com/smartdevicelink/proxy/rpc/listeners/OnMultipleRequestListener.java
@@ -5,8 +5,6 @@ import android.util.Log;
import com.smartdevicelink.proxy.RPCResponse;
import com.smartdevicelink.proxy.rpc.enums.Result;
-import org.json.JSONException;
-
import java.util.Vector;
/**
@@ -25,6 +23,7 @@ public abstract class OnMultipleRequestListener extends OnRPCResponseListener {
rpcResponseListener = new OnRPCResponseListener() {
@Override
public void onResponse(int correlationId, RPCResponse response) {
+ OnMultipleRequestListener.this.onResponse(correlationId, response);
update(correlationId);
}
diff --git a/sdl_android/src/main/res/drawable-mdpi/transparent.png b/sdl_android/src/main/res/drawable-mdpi/transparent.png
new file mode 100644
index 000000000..f21003d3a
--- /dev/null
+++ b/sdl_android/src/main/res/drawable-mdpi/transparent.png
Binary files differ