diff options
author | Joey Grover <joeygrover@gmail.com> | 2018-09-27 14:59:08 -0400 |
---|---|---|
committer | Joey Grover <joeygrover@gmail.com> | 2018-09-27 14:59:08 -0400 |
commit | 0b894d7e2beb4c9f82f4b609b9e65620039f010c (patch) | |
tree | 02d2e308671d39b50286bdedd45f156bdc2e4c11 | |
parent | 92d5b306c210987a0dfe8eb49dcc9475c2faded8 (diff) | |
parent | 101d980aece2639713331c5f441faa0efeb6efe1 (diff) | |
download | sdl_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
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 Binary files differnew file mode 100644 index 000000000..f21003d3a --- /dev/null +++ b/sdl_android/src/main/res/drawable-mdpi/transparent.png |