summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRHenigan <heniganr1@gmail.com>2020-06-30 16:02:14 -0400
committerRHenigan <heniganr1@gmail.com>2020-06-30 16:02:14 -0400
commit9e191ac60d7b71194f1f57dc71a96f2a94aebca3 (patch)
treea9ef447558c50c5e4c8a96076add028eb15c4e63
parent8aa7102194e86bb467bf6f5b03f619722e9d3c07 (diff)
parent7a2f581925c213a07fbbe0fac2eb7aa6a40e738d (diff)
downloadsdl_android-9e191ac60d7b71194f1f57dc71a96f2a94aebca3.tar.gz
Merge branch 'develop' into bugfix/issue_1385bugfix/issue_1385
-rwxr-xr-xandroid/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java35
-rw-r--r--android/sdl_android/build.gradle1
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java3
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java4
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java115
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SubscribeButtonManagerTest.java172
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java19
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java97
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperationTests.java88
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java6
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java15
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java201
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java17
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/protocol/SdlPacket.java115
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java21
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java5
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java180
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java10
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java102
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java13
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java278
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java56
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/util/HttpRequestTask.java (renamed from base/src/main/java/com/smartdevicelink/util/HttpRequestTask.java)0
-rw-r--r--android/sdl_android/src/main/res/values/sdl.xml2
-rw-r--r--base/src/main/java/android/os/AsyncTask.java1
-rw-r--r--base/src/main/java/android/os/Parcel.java1
-rw-r--r--base/src/main/java/android/os/Parcelable.java1
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java25
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java432
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java43
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java544
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/BaseSubscribeButtonManager.java193
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/OnButtonListener.java32
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java47
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java338
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonState.java11
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonTransitionOperation.java82
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/AsynchronousOperation.java122
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java54
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/CheckChoiceVROptionalOperation.java17
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java13
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PausableThreadPoolExecutor.java72
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java13
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java29
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java29
-rw-r--r--base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java (renamed from base/src/main/java/com/smartdevicelink/protocol/SdlPacket.java)108
-rw-r--r--base/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java3
-rw-r--r--base/src/main/java/com/smartdevicelink/transport/TransportConstants.java1
-rw-r--r--base/src/main/java/com/smartdevicelink/transport/utl/BaseTransportRecord.java (renamed from base/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java)60
l---------baseAndroid/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java1
l---------baseAndroid/src/main/java/com/smartdevicelink/protocol/SdlPacket.java1
l---------baseAndroid/src/main/java/com/smartdevicelink/transport/utl/BaseTransportRecord.java1
l---------baseAndroid/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java1
l---------baseAndroid/src/main/java/com/smartdevicelink/util/HttpRequestTask.java1
-rw-r--r--hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java37
-rw-r--r--javaEE/build.gradle1
-rw-r--r--javaSE/build.gradle1
-rw-r--r--javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java8
-rw-r--r--javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java20
-rw-r--r--javaSE/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java11
-rw-r--r--javaSE/src/main/java/com/smartdevicelink/protocol/SdlPacket.java82
-rw-r--r--javaSE/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java77
-rw-r--r--javaSE/src/main/java/com/smartdevicelink/util/HttpRequestTask.java184
63 files changed, 2765 insertions, 1487 deletions
diff --git a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java
index ec8afa812..893525b97 100755
--- a/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java
+++ b/android/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java
@@ -12,6 +12,7 @@ import android.os.IBinder;
import android.util.Log;
import com.smartdevicelink.managers.CompletionListener;
+import com.smartdevicelink.managers.screen.OnButtonListener;
import com.smartdevicelink.managers.SdlManager;
import com.smartdevicelink.managers.SdlManagerListener;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
@@ -27,9 +28,12 @@ import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCNotification;
import com.smartdevicelink.proxy.TTSChunkFactory;
import com.smartdevicelink.proxy.rpc.Alert;
+import com.smartdevicelink.proxy.rpc.OnButtonEvent;
+import com.smartdevicelink.proxy.rpc.OnButtonPress;
import com.smartdevicelink.proxy.rpc.OnHMIStatus;
import com.smartdevicelink.proxy.rpc.Speak;
import com.smartdevicelink.proxy.rpc.enums.AppHMIType;
+import com.smartdevicelink.proxy.rpc.enums.ButtonName;
import com.smartdevicelink.proxy.rpc.enums.FileType;
import com.smartdevicelink.proxy.rpc.enums.HMILevel;
import com.smartdevicelink.proxy.rpc.enums.InteractionMode;
@@ -183,6 +187,7 @@ public class SdlService extends Service {
performWelcomeSpeak();
performWelcomeShow();
preloadChoices();
+ subscribeToButtons();
}
}
});
@@ -371,6 +376,36 @@ public class SdlService extends Service {
}
/**
+ * Attempts to Subscribe to all preset buttons
+ */
+ private void subscribeToButtons() {
+ ButtonName[] buttonNames = {ButtonName.PLAY_PAUSE, ButtonName.SEEKLEFT, ButtonName.SEEKRIGHT, ButtonName.AC_MAX, ButtonName.AC, ButtonName.RECIRCULATE,
+ ButtonName.FAN_UP, ButtonName.FAN_DOWN, ButtonName.TEMP_UP, ButtonName.TEMP_DOWN, ButtonName.FAN_DOWN, ButtonName.DEFROST_MAX, ButtonName.DEFROST_REAR, ButtonName.DEFROST,
+ ButtonName.UPPER_VENT, ButtonName.LOWER_VENT, ButtonName.VOLUME_UP, ButtonName.VOLUME_DOWN, ButtonName.EJECT, ButtonName.SOURCE, ButtonName.SHUFFLE, ButtonName.REPEAT};
+
+ OnButtonListener onButtonListener = new OnButtonListener() {
+ @Override
+ public void onPress(ButtonName buttonName, OnButtonPress buttonPress) {
+ sdlManager.getScreenManager().setTextField1(buttonName + " pressed");
+ }
+
+ @Override
+ public void onEvent(ButtonName buttonName, OnButtonEvent buttonEvent) {
+ sdlManager.getScreenManager().setTextField2(buttonName + " " + buttonEvent.getButtonEventMode());
+ }
+
+ @Override
+ public void onError(String info) {
+ Log.i(TAG, "onError: " + info);
+ }
+ };
+
+ for (ButtonName buttonName : buttonNames) {
+ sdlManager.getScreenManager().addButtonListener(buttonName, onButtonListener);
+ }
+ }
+
+ /**
* Will show a sample test message on screen as well as speak a sample test message
*/
private void showTest(){
diff --git a/android/sdl_android/build.gradle b/android/sdl_android/build.gradle
index 7d45586e7..551ff10c3 100644
--- a/android/sdl_android/build.gradle
+++ b/android/sdl_android/build.gradle
@@ -56,6 +56,7 @@ task buildWindowSymLinks(type:Exec){
dependencies {
api fileTree(dir: 'libs', include: ['*.jar'])
api 'com.smartdevicelink:bson_java_port:1.2.0'
+ api 'com.livio.taskmaster:taskmaster:0.3.0'
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java
index 118ea1e9b..c1c636c50 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java
@@ -3,6 +3,7 @@ package com.smartdevicelink.managers;
import android.content.Context;
import android.support.test.runner.AndroidJUnit4;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.lifecycle.LifecycleConfigurationUpdate;
import com.smartdevicelink.managers.lockscreen.LockScreenConfig;
import com.smartdevicelink.protocol.enums.FunctionID;
@@ -42,6 +43,7 @@ import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
/**
* This is a unit test class for the SmartDeviceLink library manager class :
@@ -138,6 +140,7 @@ public class SdlManagerTests {
// mock internalInterface and set it manually
internalInterface = mock(ISdl.class);
+ when(internalInterface.getTaskmaster()).thenReturn(new Taskmaster.Builder().build());
manager._internalInterface = internalInterface;
return manager;
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java
index e19f10980..a91a68b3e 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java
@@ -1,5 +1,6 @@
package com.smartdevicelink.managers.screen;
+import com.livio.taskmaster.Taskmaster;
import android.support.test.runner.AndroidJUnit4;
import com.smartdevicelink.managers.BaseSubManager;
@@ -24,6 +25,7 @@ import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
/**
* This is a unit test class for the SmartDeviceLink library manager class :
@@ -37,7 +39,9 @@ public class ScreenManagerTests {
@Before
public void setUp() throws Exception {
+
ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getTaskmaster()).thenReturn(new Taskmaster.Builder().build());
FileManager fileManager = mock(FileManager.class);
screenManager = new ScreenManager(internalInterface, fileManager);
screenManager.start(null);
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java
index fd5b1a71a..efea2bf1d 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java
@@ -4,24 +4,31 @@ package com.smartdevicelink.managers.screen;
import android.support.test.runner.AndroidJUnit4;
import com.smartdevicelink.managers.CompletionListener;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.file.FileManager;
import com.smartdevicelink.managers.file.MultipleFileCompletionListener;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.interfaces.ISdl;
+import com.smartdevicelink.proxy.interfaces.OnSystemCapabilityListener;
+import com.smartdevicelink.proxy.rpc.DisplayCapability;
import com.smartdevicelink.proxy.rpc.Image;
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.ShowResponse;
import com.smartdevicelink.proxy.rpc.SoftButton;
import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities;
import com.smartdevicelink.proxy.rpc.WindowCapability;
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.Result;
import com.smartdevicelink.proxy.rpc.enums.SoftButtonType;
import com.smartdevicelink.proxy.rpc.enums.StaticIconName;
+import com.smartdevicelink.proxy.rpc.enums.SystemAction;
+import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
import com.smartdevicelink.test.Validator;
@@ -40,10 +47,12 @@ import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
/**
* This is a unit test class for the SmartDeviceLink library manager class :
@@ -53,9 +62,8 @@ import static org.mockito.Mockito.mock;
public class SoftButtonManagerTests {
private SoftButtonManager softButtonManager;
- private boolean fileManagerUploadArtworksGotCalled;
- private boolean internalInterfaceSendRPCGotCalled;
- private boolean softButtonMangerUpdateCompleted;
+ private int fileManagerUploadArtworksListenerCalledCounter;
+ private int internalInterfaceSendRPCListenerCalledCounter;
private int softButtonObject1Id = 1000, softButtonObject2Id = 2000;
private SoftButtonObject softButtonObject1, softButtonObject2;
private SoftButtonState softButtonState1, softButtonState2, softButtonState3, softButtonState4;
@@ -81,13 +89,35 @@ public class SoftButtonManagerTests {
doAnswer(onHMIStatusAnswer).when(internalInterface).addOnRPCNotificationListener(eq(FunctionID.ON_HMI_STATUS), any(OnRPCNotificationListener.class));
+ // When internalInterface.addOnSystemCapabilityListener(SystemCapabilityType.DISPLAYS, onSystemCapabilityListener) is called
+ // inside SoftButtonManager, respond with a fake response to let the SoftButtonManager continue working.
+ Answer<Void> onSystemCapabilityAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ OnSystemCapabilityListener onSystemCapabilityListener = (OnSystemCapabilityListener) args[1];
+ SoftButtonCapabilities softButtonCapabilities = new SoftButtonCapabilities();
+ softButtonCapabilities.setImageSupported(true);
+ softButtonCapabilities.setTextSupported(true);
+ WindowCapability windowCapability = new WindowCapability();
+ windowCapability.setSoftButtonCapabilities(Collections.singletonList(softButtonCapabilities));
+ DisplayCapability displayCapability = new DisplayCapability();
+ displayCapability.setWindowCapabilities(Collections.singletonList(windowCapability));
+ List<DisplayCapability> capabilities = Collections.singletonList(displayCapability);
+ onSystemCapabilityListener.onCapabilityRetrieved(capabilities);
+ return null;
+ }
+ };
+ doAnswer(onSystemCapabilityAnswer).when(internalInterface).addOnSystemCapabilityListener(eq(SystemCapabilityType.DISPLAYS), any(OnSystemCapabilityListener.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;
+ fileManagerUploadArtworksListenerCalledCounter++;
Object[] args = invocation.getArguments();
MultipleFileCompletionListener multipleFileCompletionListener = (MultipleFileCompletionListener) args[1];
multipleFileCompletionListener.onComplete(null);
@@ -98,6 +128,9 @@ public class SoftButtonManagerTests {
// Create softButtonManager
+ Taskmaster taskmaster = new Taskmaster.Builder().build();
+ taskmaster.start();
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
softButtonManager = new SoftButtonManager(internalInterface, fileManager);
@@ -107,14 +140,14 @@ public class SoftButtonManagerTests {
Answer<Void> onSendShowRPCAnswer = new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) {
- internalInterfaceSendRPCGotCalled = true;
+ internalInterfaceSendRPCListenerCalledCounter++;
Object[] args = invocation.getArguments();
Show show = (Show) args[0];
- show.getOnRPCResponseListener().onResponse(0, null);
+ show.getOnRPCResponseListener().onResponse(0, new ShowResponse(true, Result.SUCCESS));
assertEquals(show.getMainField1(), softButtonManager.getCurrentMainField1());
- assertEquals(show.getSoftButtons().size(), softButtonManager.createSoftButtonsForCurrentState().size());
+ assertEquals(show.getSoftButtons().size(), softButtonManager.getSoftButtonObjects().size());
return null;
}
@@ -133,18 +166,20 @@ public class SoftButtonManagerTests {
softButtonObject2.setButtonId(softButtonObject2Id);
}
+
+ private void sleep() {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
@Test
public void testSoftButtonManagerUpdate() {
// Reset the boolean variables
- fileManagerUploadArtworksGotCalled = false;
- internalInterfaceSendRPCGotCalled = false;
- softButtonMangerUpdateCompleted = false;
-
- SoftButtonCapabilities softCap = new SoftButtonCapabilities();
- softCap.setImageSupported(true);
- WindowCapability defaultCap = new WindowCapability();
- defaultCap.setSoftButtonCapabilities(Collections.singletonList(softCap));
- softButtonManager.defaultMainWindowCapability = defaultCap;
+ fileManagerUploadArtworksListenerCalledCounter = 0;
+ internalInterfaceSendRPCListenerCalledCounter = 0;
// Test batch update
@@ -152,12 +187,6 @@ public class SoftButtonManagerTests {
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
@@ -165,11 +194,13 @@ public class SoftButtonManagerTests {
softButtonObject1.transitionToNextState();
- // Check that everything got called as expected
+ // Sleep to give time to Taskmaster to run the operations
+ sleep();
- assertTrue("FileManager.uploadArtworks() did get called", fileManagerUploadArtworksGotCalled);
- assertTrue("InternalInterface.sendRPC() did not get called", internalInterfaceSendRPCGotCalled);
- assertTrue("SoftButtonManger update onComplete() did not get called", softButtonMangerUpdateCompleted);
+
+ // Check that everything got called as expected
+ assertEquals("FileManager.uploadArtworks() did not get called correctly", 1, fileManagerUploadArtworksListenerCalledCounter);
+ assertEquals("InternalInterface.sendRPC() did not get called correctly",2, internalInterfaceSendRPCListenerCalledCounter);
// Test getSoftButtonObjects
@@ -215,8 +246,9 @@ public class SoftButtonManagerTests {
SoftButton softButtonExpectedValue = new SoftButton(SoftButtonType.SBT_BOTH, SoftButtonObject.SOFT_BUTTON_ID_NOT_SET_VALUE);
softButtonExpectedValue.setText("o1s1");
softButtonExpectedValue.setImage(new Image(artworkExpectedValue.getName(), ImageType.DYNAMIC));
+ softButtonExpectedValue.setSystemAction(SystemAction.DEFAULT_ACTION);
SoftButton actual = softButtonState1.getSoftButton();
- assertTrue("Returned SoftButton doesn't match the expected value", Validator.validateSoftButton(softButtonExpectedValue, softButtonState1.getSoftButton()));
+ assertTrue("Returned SoftButton doesn't match the expected value", Validator.validateSoftButton(softButtonExpectedValue, actual));
}
@Test
@@ -240,6 +272,7 @@ public class SoftButtonManagerTests {
// Test SoftButtonObject.getCurrentStateSoftButton()
SoftButton softButtonExpectedValue = new SoftButton(SoftButtonType.SBT_TEXT, softButtonObject2Id);
softButtonExpectedValue.setText("o2s1");
+ softButtonExpectedValue.setSystemAction(SystemAction.DEFAULT_ACTION);
assertTrue("Returned current state SoftButton doesn't match the expected value", Validator.validateSoftButton(softButtonExpectedValue, softButtonObject2.getCurrentStateSoftButton()));
@@ -348,14 +381,14 @@ public class SoftButtonManagerTests {
// Case 1: object is null, assertFalse
softButtonObject1 = new SoftButtonObject("test", softButtonState1, null);
softButtonObject2 = null;
- assertFalse(softButtonObject1.equals(softButtonObject2));
+ assertNotEquals(softButtonObject1, softButtonObject2);
// Case 2 SoftButtonObjects are the same, assertTrue
- assertTrue(softButtonObject1.equals(softButtonObject1));
+ assertEquals(softButtonObject1, softButtonObject1);
// Case 3: object is not an instance of SoftButtonObject assertFalse
SdlArtwork artwork = new SdlArtwork("image1", FileType.GRAPHIC_PNG, 1, true);
- assertFalse(softButtonObject1.equals(artwork));
+ assertNotEquals(softButtonObject1, artwork);
// Case 4: SoftButtonObjectState List are not same size, assertFalse
List<SoftButtonState> softButtonStateList = new ArrayList<>();
@@ -365,22 +398,22 @@ public class SoftButtonManagerTests {
softButtonStateList2.add(softButtonState2);
softButtonObject1 = new SoftButtonObject("hi", softButtonStateList, "Hi", null);
softButtonObject2 = new SoftButtonObject("hi", softButtonStateList2, "Hi", null);
- assertFalse(softButtonObject1.equals(softButtonObject2));
+ assertNotEquals(softButtonObject1, softButtonObject2);
// Case 5: SoftButtonStates are not the same, assertFalse
softButtonObject1 = new SoftButtonObject("test", softButtonState1, null);
softButtonObject2 = new SoftButtonObject("test", softButtonState2, null);
- assertFalse(softButtonObject1.equals(softButtonObject2));
+ assertNotEquals(softButtonObject1, softButtonObject2);
// Case 6: SoftButtonObject names are not same, assertFalse
softButtonObject1 = new SoftButtonObject("test", softButtonState1, null);
softButtonObject2 = new SoftButtonObject("test23123", softButtonState1, null);
- assertFalse(softButtonObject1.equals(softButtonObject2));
+ assertNotEquals(softButtonObject1, softButtonObject2);
// Case 7: SoftButtonObject currentStateName not same, assertFalse
softButtonObject1 = new SoftButtonObject("hi", softButtonStateList, "Hi", null);
softButtonObject2 = new SoftButtonObject("hi", softButtonStateList, "Hi2", null);
- assertFalse(softButtonObject1.equals(softButtonObject2));
+ assertNotEquals(softButtonObject1, softButtonObject2);
}
/**
@@ -388,31 +421,31 @@ public class SoftButtonManagerTests {
*/
@Test
public void testSoftButtonStateEquals() {
- assertFalse(softButtonState1.equals(softButtonState2));
+ assertNotEquals(softButtonState1, softButtonState2);
SdlArtwork artwork1 = new SdlArtwork("image1", FileType.GRAPHIC_PNG, 1, true);
SdlArtwork artwork2 = new SdlArtwork("image2", FileType.GRAPHIC_PNG, 1, true);
// Case 1: object is null, assertFalse
softButtonState1 = new SoftButtonState("object1-state1", "o1s1", artwork1);
softButtonState2 = null;
- assertFalse(softButtonState1.equals(softButtonState2));
+ assertNotEquals(softButtonState1, softButtonState2);
// Case 2 SoftButtonObjects are the same, assertTrue
- assertTrue(softButtonState1.equals(softButtonState1));
+ assertEquals(softButtonState1, softButtonState1);
// Case 3: object is not an instance of SoftButtonState, assertFalse
- assertFalse(softButtonState1.equals(artwork1));
+ assertNotEquals(softButtonState1, artwork1);
// Case 4: different artwork, assertFalse
softButtonState2 = new SoftButtonState("object1-state1", "o1s1", artwork2);
- assertFalse(softButtonState1.equals(softButtonState2));
+ assertNotEquals(softButtonState1, softButtonState2);
// Case 5: different name, assertFalse
softButtonState2 = new SoftButtonState("object1-state1 different name", "o1s1", artwork1);
- assertFalse(softButtonState1.equals(softButtonState2));
+ assertNotEquals(softButtonState1, softButtonState2);
// Case 6 they are equal, assertTrue
softButtonState2 = new SoftButtonState("object1-state1", "o1s1", artwork1);
- assertTrue(softButtonState1.equals(softButtonState2));
+ assertEquals(softButtonState1, softButtonState2);
}
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SubscribeButtonManagerTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SubscribeButtonManagerTest.java
new file mode 100644
index 000000000..b1da6b052
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SubscribeButtonManagerTest.java
@@ -0,0 +1,172 @@
+package com.smartdevicelink.managers.screen;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import com.smartdevicelink.managers.BaseSubManager;
+import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.interfaces.ISdl;
+import com.smartdevicelink.proxy.rpc.OnButtonEvent;
+import com.smartdevicelink.proxy.rpc.OnButtonPress;
+import com.smartdevicelink.proxy.rpc.SubscribeButton;
+import com.smartdevicelink.proxy.rpc.SubscribeButtonResponse;
+import com.smartdevicelink.proxy.rpc.UnsubscribeButton;
+import com.smartdevicelink.proxy.rpc.UnsubscribeButtonResponse;
+import com.smartdevicelink.proxy.rpc.enums.ButtonName;
+import com.smartdevicelink.proxy.rpc.enums.Result;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNotNull;
+import static junit.framework.TestCase.assertNull;
+import static junit.framework.TestCase.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+
+@RunWith(AndroidJUnit4.class)
+public class SubscribeButtonManagerTest {
+ private SubscribeButtonManager subscribeButtonManager;
+ private ISdl internalInterface;
+
+ private Answer<Void> onSubscribe_UnsubscribeSuccess = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ RPCRequest message = (RPCRequest) args[0];
+ if(message instanceof SubscribeButton){
+ SubscribeButtonResponse subscribeButtonResponse = new SubscribeButtonResponse();
+ subscribeButtonResponse.setSuccess(true);
+ message.getOnRPCResponseListener().onResponse(message.getCorrelationID(),subscribeButtonResponse);
+ }
+ if(message instanceof UnsubscribeButton) {
+ UnsubscribeButtonResponse unsubscribeButtonResponse = new UnsubscribeButtonResponse();
+ unsubscribeButtonResponse.setSuccess(true);
+ message.getOnRPCResponseListener().onResponse(message.getCorrelationID(), unsubscribeButtonResponse);
+ }
+ return null;
+ }
+ };
+
+
+ private Answer<Void> onSubscribeFail = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ RPCRequest message = (RPCRequest) args[0];
+ if(message instanceof SubscribeButton){
+ SubscribeButtonResponse subscribeButtonResponse = new SubscribeButtonResponse();
+ subscribeButtonResponse.setSuccess(false);
+ message.getOnRPCResponseListener().onError(message.getCorrelationID(), Result.GENERIC_ERROR, "Fail");
+ }
+ return null;
+ }
+ };
+ private OnButtonListener listener = new OnButtonListener() {
+ @Override
+ public void onPress(ButtonName buttonName, OnButtonPress buttonPress) {
+
+ }
+
+ @Override
+ public void onEvent(ButtonName buttonName, OnButtonEvent buttonEvent) {
+
+ }
+
+ @Override
+ public void onError(String info) {
+
+ }
+ };
+
+ private OnButtonListener listener2 = new OnButtonListener() {
+ @Override
+ public void onPress(ButtonName buttonName, OnButtonPress buttonPress) {
+
+ }
+
+ @Override
+ public void onEvent(ButtonName buttonName, OnButtonEvent buttonEvent) {
+
+ }
+
+ @Override
+ public void onError(String info) {
+
+ }
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ internalInterface = mock(ISdl.class);
+ subscribeButtonManager = new SubscribeButtonManager(internalInterface);
+ }
+
+ @Test
+ public void testInstantiation(){
+ assertNotNull(subscribeButtonManager.onButtonListeners);
+ assertEquals(subscribeButtonManager.getState(), BaseSubManager.SETTING_UP);
+ }
+
+ @Test
+ public void testDispose() {
+ subscribeButtonManager.addButtonListener(ButtonName.VOLUME_UP, listener);
+ subscribeButtonManager.dispose();
+ assertTrue(subscribeButtonManager.onButtonListeners.size() == 0);
+ }
+
+ @Test
+ public void testAddButtonListener() {
+ doAnswer(onSubscribe_UnsubscribeSuccess).when(internalInterface).sendRPC(any(RPCMessage.class));
+
+ subscribeButtonManager.addButtonListener(null, null);
+ assertTrue(subscribeButtonManager.onButtonListeners.size() == 0);
+
+ subscribeButtonManager.addButtonListener(null, listener);
+ assertTrue(subscribeButtonManager.onButtonListeners.size() == 0);
+
+ subscribeButtonManager.addButtonListener(ButtonName.VOLUME_UP, listener);
+ assertTrue(subscribeButtonManager.onButtonListeners.containsKey(ButtonName.VOLUME_UP));
+
+ }
+
+ @Test
+ public void testAddButtonListenerError(){
+ doAnswer(onSubscribeFail).when(internalInterface).sendRPC(any(RPCMessage.class));
+ subscribeButtonManager.addButtonListener(ButtonName.VOLUME_UP, listener);
+ assertFalse(subscribeButtonManager.onButtonListeners.containsKey(ButtonName.VOLUME_UP));
+ }
+
+ @Test
+ public void testRemoveButtonListener() {
+ doAnswer(onSubscribe_UnsubscribeSuccess).when(internalInterface).sendRPC(any(RPCMessage.class));
+
+ subscribeButtonManager.removeButtonListener(ButtonName.VOLUME_DOWN, listener);
+ assertFalse(subscribeButtonManager.onButtonListeners.containsKey(ButtonName.VOLUME_DOWN));
+
+ subscribeButtonManager.addButtonListener(ButtonName.VOLUME_UP, listener);
+ assertTrue(subscribeButtonManager.onButtonListeners.get(ButtonName.VOLUME_UP).size() == 1);
+
+ subscribeButtonManager.removeButtonListener(ButtonName.VOLUME_UP, listener2);
+ assertTrue(subscribeButtonManager.onButtonListeners.get(ButtonName.VOLUME_UP).size() == 1);
+
+ subscribeButtonManager.addButtonListener(ButtonName.VOLUME_UP, listener);
+ assertTrue(subscribeButtonManager.onButtonListeners.get(ButtonName.VOLUME_UP).size() == 1);
+
+ subscribeButtonManager.addButtonListener(ButtonName.VOLUME_UP, listener2);
+ assertTrue(subscribeButtonManager.onButtonListeners.get(ButtonName.VOLUME_UP).size() == 2);
+
+
+ subscribeButtonManager.removeButtonListener(ButtonName.VOLUME_UP, listener);
+ assertTrue(subscribeButtonManager.onButtonListeners.get(ButtonName.VOLUME_UP).size() == 1);
+
+ subscribeButtonManager.removeButtonListener(ButtonName.VOLUME_UP, listener2);
+ assertNull(subscribeButtonManager.onButtonListeners.get(ButtonName.VOLUME_UP));
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java
index f483ea3dd..78dd07cce 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java
@@ -37,6 +37,7 @@ package com.smartdevicelink.managers.screen.choiceset;
import android.support.test.runner.AndroidJUnit4;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.file.FileManager;
import com.smartdevicelink.proxy.interfaces.ISdl;
@@ -58,7 +59,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
-import java.util.concurrent.LinkedBlockingQueue;
import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse;
@@ -77,12 +77,15 @@ import static org.mockito.Mockito.when;
public class ChoiceSetManagerTests {
private ChoiceSetManager csm;
+ Taskmaster taskmaster;
@Before
public void setUp() throws Exception{
ISdl internalInterface = mock(ISdl.class);
FileManager fileManager = mock(FileManager.class);
+ taskmaster = new Taskmaster.Builder().build();
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
csm = new ChoiceSetManager(internalInterface, fileManager);
assertEquals(csm.getState(), BaseSubManager.SETTING_UP);
@@ -94,8 +97,7 @@ public class ChoiceSetManagerTests {
assertNotNull(csm.fileManager);
assertNotNull(csm.preloadedChoices);
assertNotNull(csm.pendingPreloadChoices);
- assertNotNull(csm.operationQueue);
- assertNotNull(csm.executor);
+ assertNotNull(csm.transactionQueue);
assertNotNull(csm.hmiListener);
assertNotNull(csm.onDisplayCapabilityListener);
assertNull(csm.pendingPresentOperation);
@@ -112,10 +114,9 @@ public class ChoiceSetManagerTests {
assertNull(csm.pendingPresentationSet);
assertNull(csm.pendingPresentOperation);
- assertEquals(csm.operationQueue.size(), 0);
+ assertEquals(csm.transactionQueue.getTasksAsList().size(), 0);
assertEquals(csm.nextChoiceId, 1);
- assertTrue(csm.executor.isShutdown());
assertFalse(csm.isVROptional);
assertEquals(csm.getState(), BaseSubManager.SHUTDOWN);
@@ -260,6 +261,7 @@ public class ChoiceSetManagerTests {
@Test
public void testPresentingKeyboardShouldReturnCancelIDIfKeyboardCanBeSent() {
ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
FileManager fileManager = mock(FileManager.class);
ChoiceSetManager newCSM = new ChoiceSetManager(internalInterface, fileManager);
@@ -273,6 +275,7 @@ public class ChoiceSetManagerTests {
@Test
public void testPresentingKeyboardShouldNotReturnCancelIDIfKeyboardCannotBeSent() {
ISdl internalInterface = mock(ISdl.class);
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
FileManager fileManager = mock(FileManager.class);
ChoiceSetManager newCSM = new ChoiceSetManager(internalInterface, fileManager);
@@ -299,17 +302,13 @@ public class ChoiceSetManagerTests {
// Currently executing operation
PresentKeyboardOperation testKeyboardOp = mock(PresentKeyboardOperation.class);
- doReturn(true).when(testKeyboardOp).isExecuting();
doReturn(96).when(testKeyboardOp).getCancelID();
csm.currentlyPresentedKeyboardOperation = testKeyboardOp;
// Queued operations
PresentKeyboardOperation testKeyboardOp2 = mock(PresentKeyboardOperation.class);
- doReturn(true).when(testKeyboardOp2).isExecuting();
doReturn(testCancelID).when(testKeyboardOp2).getCancelID();
- LinkedBlockingQueue<Runnable> testOperationQueue = new LinkedBlockingQueue<>();
- testOperationQueue.add(testKeyboardOp2);
- csm.operationQueue = testOperationQueue;
+ csm.currentlyPresentedKeyboardOperation = testKeyboardOp2;
// Queued operation should be canceled
csm.dismissKeyboard(testCancelID);
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java
index 8e2df6181..1f4984706 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java
@@ -37,6 +37,9 @@ package com.smartdevicelink.managers.screen.choiceset;
import android.support.test.runner.AndroidJUnit4;
+import com.livio.taskmaster.Queue;
+import com.livio.taskmaster.Task;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCResponse;
import com.smartdevicelink.proxy.interfaces.ISdl;
@@ -58,14 +61,9 @@ import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.Collections;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
import static junit.framework.TestCase.assertEquals;
-import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNull;
-import static junit.framework.TestCase.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
@@ -83,7 +81,8 @@ public class PresentChoiceSetOperationTests {
private KeyboardListener keyboardListener;
private ChoiceSetSelectionListener choiceSetSelectionListener;
- private ExecutorService executor;
+ private Taskmaster taskmaster;
+ private Queue queue;
@Before
public void setUp() throws Exception{
@@ -97,7 +96,9 @@ public class PresentChoiceSetOperationTests {
cell1.setChoiceId(0);
choiceSet = new ChoiceSet("Test", Collections.singletonList(cell1), choiceSetSelectionListener);
- executor = Executors.newCachedThreadPool();
+ taskmaster = new Taskmaster.Builder().build();
+ queue = taskmaster.createQueue("test", 100, false);
+ taskmaster.start();
}
@@ -142,19 +143,23 @@ public class PresentChoiceSetOperationTests {
assertEquals(presentChoiceSetOperation.selectedCellRow, Integer.valueOf(0));
}
+ private void sleep() {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
@Test
public void testCancelingChoiceSetSuccessfullyIfThreadIsRunning(){
when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
- executor.execute(presentChoiceSetOperation);
+ queue.add(presentChoiceSetOperation, false);
- try {
- executor.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {}
+ sleep();
- assertTrue(presentChoiceSetOperation.isExecuting());
- assertFalse(presentChoiceSetOperation.isFinished());
- assertFalse(presentChoiceSetOperation.isCancelled());
+ assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState());
choiceSet.cancel();
Answer<Void> cancelInteractionAnswer = new Answer<Void>() {
@@ -178,23 +183,17 @@ public class PresentChoiceSetOperationTests {
verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class));
verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class));
- assertTrue(presentChoiceSetOperation.isExecuting());
- assertFalse(presentChoiceSetOperation.isFinished());
- assertFalse(presentChoiceSetOperation.isCancelled());
+ assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState());
}
@Test
public void testCancelingChoiceSetUnsuccessfullyIfThreadIsRunning(){
when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
- executor.execute(presentChoiceSetOperation);
- try {
- executor.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {}
+ queue.add(presentChoiceSetOperation, false);
+ sleep();
- assertTrue(presentChoiceSetOperation.isExecuting());
- assertFalse(presentChoiceSetOperation.isFinished());
- assertFalse(presentChoiceSetOperation.isCancelled());
+ assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState());
choiceSet.cancel();
Answer<Void> cancelInteractionAnswer = new Answer<Void>() {
@@ -218,9 +217,7 @@ public class PresentChoiceSetOperationTests {
verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class));
verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class));
- assertTrue(presentChoiceSetOperation.isExecuting());
- assertFalse(presentChoiceSetOperation.isFinished());
- assertFalse(presentChoiceSetOperation.isCancelled());
+ assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState());
}
@Test
@@ -229,16 +226,12 @@ public class PresentChoiceSetOperationTests {
presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
presentChoiceSetOperation.finishOperation();
- assertFalse(presentChoiceSetOperation.isExecuting());
- assertTrue(presentChoiceSetOperation.isFinished());
- assertFalse(presentChoiceSetOperation.isCancelled());
+ assertEquals(Task.FINISHED, presentChoiceSetOperation.getState());
choiceSet.cancel();
verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
- assertFalse(presentChoiceSetOperation.isExecuting());
- assertTrue(presentChoiceSetOperation.isFinished());
- assertFalse(presentChoiceSetOperation.isCancelled());
+ assertEquals(Task.FINISHED, presentChoiceSetOperation.getState());
}
@Test
@@ -246,21 +239,15 @@ public class PresentChoiceSetOperationTests {
when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
- assertFalse(presentChoiceSetOperation.isExecuting());
- assertFalse(presentChoiceSetOperation.isFinished());
- assertFalse(presentChoiceSetOperation.isCancelled());
+ assertEquals(Task.BLOCKED, presentChoiceSetOperation.getState());
choiceSet.cancel();
// Once the operation has started
- executor.execute(presentChoiceSetOperation);
- try {
- executor.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {}
+ queue.add(presentChoiceSetOperation, false);
+ sleep();
- assertFalse(presentChoiceSetOperation.isExecuting());
- assertTrue(presentChoiceSetOperation.isFinished());
- assertFalse(presentChoiceSetOperation.isCancelled());
+ assertEquals(Task.CANCELED, presentChoiceSetOperation.getState());
// Make sure neither a `CancelInteraction` or `PerformInteraction` RPC is ever sent
verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
@@ -272,14 +259,10 @@ public class PresentChoiceSetOperationTests {
// Cancel Interaction is only supported on RPC specs v.6.0.0+
when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3));
presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
- executor.execute(presentChoiceSetOperation);
- try {
- executor.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {}
+ queue.add(presentChoiceSetOperation, false);
+ sleep();
- assertTrue(presentChoiceSetOperation.isExecuting());
- assertFalse(presentChoiceSetOperation.isFinished());
- assertFalse(presentChoiceSetOperation.isCancelled());
+ assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState());
choiceSet.cancel();
@@ -293,23 +276,17 @@ public class PresentChoiceSetOperationTests {
when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3));
presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER);
- assertFalse(presentChoiceSetOperation.isExecuting());
- assertFalse(presentChoiceSetOperation.isFinished());
- assertFalse(presentChoiceSetOperation.isCancelled());
+ assertEquals(Task.BLOCKED, presentChoiceSetOperation.getState());
choiceSet.cancel();
verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
// Once the operation has started
- executor.execute(presentChoiceSetOperation);
- try {
- executor.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {}
+ queue.add(presentChoiceSetOperation, false);
+ sleep();
- assertFalse(presentChoiceSetOperation.isExecuting());
- assertTrue(presentChoiceSetOperation.isFinished());
- assertFalse(presentChoiceSetOperation.isCancelled());
+ assertEquals(Task.CANCELED, presentChoiceSetOperation.getState());
// Make sure neither a `CancelInteraction` or `PerformInteraction` RPC is ever sent
verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperationTests.java
index 8316a79a5..16992a522 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperationTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperationTests.java
@@ -37,6 +37,9 @@ package com.smartdevicelink.managers.screen.choiceset;
import android.support.test.runner.AndroidJUnit4;
+import com.livio.taskmaster.Queue;
+import com.livio.taskmaster.Task;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCResponse;
import com.smartdevicelink.proxy.interfaces.ISdl;
@@ -80,7 +83,8 @@ public class PresentKeyboardOperationTests {
private KeyboardListener keyboardListener;
private ISdl internalInterface;
- private ExecutorService executor;
+ private Taskmaster taskmaster;
+ private Queue queue;
@Before
public void setUp() throws Exception{
@@ -103,7 +107,9 @@ public class PresentKeyboardOperationTests {
};
doAnswer(setGlobalPropertiesAnswer).when(internalInterface).sendRPC(any(SetGlobalProperties.class));
- executor = Executors.newCachedThreadPool();
+ taskmaster = new Taskmaster.Builder().build();
+ queue = taskmaster.createQueue("test", 100, false);
+ taskmaster.start();
}
private KeyboardProperties getKeyBoardProperties(){
@@ -127,18 +133,22 @@ public class PresentKeyboardOperationTests {
assertEquals(pi.getCancelID(), TestValues.GENERAL_INTEGER);
}
+ private void sleep() {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
@Test
public void testCancelingKeyboardSuccessfullyIfThreadIsRunning(){
when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
presentKeyboardOperation = new PresentKeyboardOperation(internalInterface, null, "Test", null, null, TestValues.GENERAL_INTEGER);
- executor.execute(presentKeyboardOperation);
- try {
- executor.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {}
+ queue.add(presentKeyboardOperation, false);
+ sleep();
- assertTrue(presentKeyboardOperation.isExecuting());
- assertFalse(presentKeyboardOperation.isFinished());
- assertFalse(presentKeyboardOperation.isCancelled());
+ assertEquals(Task.IN_PROGRESS, presentKeyboardOperation.getState());
presentKeyboardOperation.dismissKeyboard();
Answer<Void> cancelInteractionAnswer = new Answer<Void>() {
@@ -162,19 +172,15 @@ public class PresentKeyboardOperationTests {
verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class));
verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class));
- assertTrue(presentKeyboardOperation.isExecuting());
- assertFalse(presentKeyboardOperation.isFinished());
- assertFalse(presentKeyboardOperation.isCancelled());
+ assertEquals(Task.IN_PROGRESS, presentKeyboardOperation.getState());
}
@Test
public void testCancelingKeyboardUnsuccessfullyIfThreadIsRunning(){
when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
presentKeyboardOperation = new PresentKeyboardOperation(internalInterface, null, "Test", null, null, TestValues.GENERAL_INTEGER);
- executor.execute(presentKeyboardOperation);
- try {
- executor.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {}
+ queue.add(presentKeyboardOperation, false);
+ sleep();
presentKeyboardOperation.dismissKeyboard();
Answer<Void> cancelInteractionAnswer = new Answer<Void>() {
@@ -198,9 +204,7 @@ public class PresentKeyboardOperationTests {
verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class));
verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class));
- assertTrue(presentKeyboardOperation.isExecuting());
- assertFalse(presentKeyboardOperation.isFinished());
- assertFalse(presentKeyboardOperation.isCancelled());
+ assertEquals(Task.IN_PROGRESS, presentKeyboardOperation.getState());
}
@Test
@@ -209,16 +213,12 @@ public class PresentKeyboardOperationTests {
presentKeyboardOperation = new PresentKeyboardOperation(internalInterface, null, "Test", null, null, TestValues.GENERAL_INTEGER);
presentKeyboardOperation.finishOperation();
- assertFalse(presentKeyboardOperation.isExecuting());
- assertTrue(presentKeyboardOperation.isFinished());
- assertFalse(presentKeyboardOperation.isCancelled());
+ assertEquals(Task.FINISHED, presentKeyboardOperation.getState());
presentKeyboardOperation.dismissKeyboard();
verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
- assertFalse(presentKeyboardOperation.isExecuting());
- assertTrue(presentKeyboardOperation.isFinished());
- assertFalse(presentKeyboardOperation.isCancelled());
+ assertEquals(Task.FINISHED, presentKeyboardOperation.getState());
}
@Test
@@ -226,21 +226,15 @@ public class PresentKeyboardOperationTests {
when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
presentKeyboardOperation = new PresentKeyboardOperation(internalInterface, null, "Test", null, null, TestValues.GENERAL_INTEGER);
- assertFalse(presentKeyboardOperation.isExecuting());
- assertFalse(presentKeyboardOperation.isFinished());
- assertFalse(presentKeyboardOperation.isCancelled());
+ assertEquals(Task.BLOCKED, presentKeyboardOperation.getState());
presentKeyboardOperation.dismissKeyboard();
// Once the operation has started
- executor.execute(presentKeyboardOperation);
- try {
- executor.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {}
+ queue.add(presentKeyboardOperation, false);
+ sleep();
- assertFalse(presentKeyboardOperation.isExecuting());
- assertTrue(presentKeyboardOperation.isFinished());
- assertFalse(presentKeyboardOperation.isCancelled());
+ assertEquals(Task.CANCELED, presentKeyboardOperation.getState());
// Make sure neither a `CancelInteraction` or `PerformInteraction` RPC is ever sent
verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
@@ -252,14 +246,10 @@ public class PresentKeyboardOperationTests {
// Cancel Interaction is only supported on RPC specs v.6.0.0+
when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3));
presentKeyboardOperation = new PresentKeyboardOperation(internalInterface, null, "Test", null, null, TestValues.GENERAL_INTEGER);
- executor.execute(presentKeyboardOperation);
- try {
- executor.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {}
+ queue.add(presentKeyboardOperation, false);
+ sleep();
- assertTrue(presentKeyboardOperation.isExecuting());
- assertFalse(presentKeyboardOperation.isFinished());
- assertFalse(presentKeyboardOperation.isCancelled());
+ assertEquals(Task.IN_PROGRESS, presentKeyboardOperation.getState());
presentKeyboardOperation.dismissKeyboard();
@@ -273,23 +263,17 @@ public class PresentKeyboardOperationTests {
when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3));
presentKeyboardOperation = new PresentKeyboardOperation(internalInterface, null, "Test", null, null, TestValues.GENERAL_INTEGER);
- assertFalse(presentKeyboardOperation.isExecuting());
- assertFalse(presentKeyboardOperation.isFinished());
- assertFalse(presentKeyboardOperation.isCancelled());
+ assertEquals(Task.BLOCKED, presentKeyboardOperation.getState());
presentKeyboardOperation.dismissKeyboard();
verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
// Once the operation has started
- executor.execute(presentKeyboardOperation);
- try {
- executor.awaitTermination(1, TimeUnit.SECONDS);
- } catch (InterruptedException e) {}
+ queue.add(presentKeyboardOperation, false);
+ sleep();
- assertFalse(presentKeyboardOperation.isExecuting());
- assertTrue(presentKeyboardOperation.isFinished());
- assertFalse(presentKeyboardOperation.isCancelled());
+ assertEquals(Task.CANCELED, presentKeyboardOperation.getState());
// Make sure neither a `CancelInteraction` or `PerformInteraction` RPC is ever sent
verify(internalInterface, never()).sendRPC(any(CancelInteraction.class));
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java
index 579772654..75b45797f 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/proxy/SystemCapabilityManagerTests.java
@@ -3,6 +3,7 @@ package com.smartdevicelink.test.proxy;
import android.support.test.runner.AndroidJUnit4;
import android.util.SparseArray;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.ManagerUtility;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.protocol.enums.SessionType;
@@ -1091,5 +1092,10 @@ public class SystemCapabilityManagerTests {
@Override
public void startRPCEncryption() {}
+
+ @Override
+ public Taskmaster getTaskmaster() {
+ return null;
+ }
}
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java
index 2a34dc62e..b993724ba 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java
@@ -49,7 +49,6 @@ import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.managers.screen.ScreenManager;
import com.smartdevicelink.managers.video.VideoStreamManager;
import com.smartdevicelink.proxy.rpc.enums.AppHMIType;
-import com.smartdevicelink.proxy.rpc.enums.SdlDisconnectedReason;
import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.transport.MultiplexTransportConfig;
import com.smartdevicelink.transport.enums.TransportType;
@@ -211,25 +210,13 @@ public class SdlManager extends BaseSdlManager {
}
}
- @Override
- void onProxyClosed(SdlDisconnectedReason reason) {
- Log.i(TAG, "Proxy is closed.");
- if (managerListener != null) {
- managerListener.onDestroy();
- }
-
- if (reason == null || !reason.equals(SdlDisconnectedReason.LANGUAGE_CHANGE)) {
- dispose();
- }
- }
-
/**
* Dispose SdlManager and clean its resources
* <strong>Note: new instance of SdlManager should be created on every connection. SdlManager cannot be reused after getting disposed.</strong>
*/
@SuppressLint("NewApi")
@Override
- public void dispose() {
+ public synchronized void dispose() {
if (this.permissionManager != null) {
this.permissionManager.dispose();
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java
index 777e29f1b..38c8e4c26 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2019 Livio, Inc.
+ * Copyright (c) 2019-2020 Livio, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -54,38 +54,29 @@ import com.smartdevicelink.transport.USBTransportConfig;
import com.smartdevicelink.transport.enums.TransportType;
import com.smartdevicelink.util.DebugTool;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
-import java.util.concurrent.Callable;
-import java.util.concurrent.Executors;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.ScheduledExecutorService;
/**
* The lifecycle manager creates a central point for all SDL session logic to converge. It should only be used by
* the library itself. Usage outside the library is not permitted and will not be protected for in the future.
*
- * @author Bilal Alsharifi.
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
public class LifecycleManager extends BaseLifecycleManager {
- private static final int RESPONSE_WAIT_TIME = 2000;
- private ISdlServiceListener navServiceListener;
- private boolean navServiceStartResponseReceived = false;
- private boolean navServiceStartResponse = false;
- private boolean navServiceEndResponseReceived = false;
- private boolean navServiceEndResponse = false;
- private boolean pcmServiceEndResponseReceived = false;
- private boolean pcmServiceEndResponse = false;
- private Context context;
+ private ISdlServiceListener videoServiceListener;
+ private boolean videoServiceStartResponseReceived = false;
+ private boolean videoServiceStartResponse = false;
+ private WeakReference<Context> contextWeakReference;
public LifecycleManager(AppConfig appConfig, BaseTransportConfig config, LifecycleListener listener) {
super(appConfig, config, listener);
}
@Override
- void initializeProxy() {
- super.initializeProxy();
+ void initialize() {
+ super.initialize();
//Handle legacy USB connections
if (_transportConfig != null && TransportType.USB.equals(_transportConfig.getTransportType())) {
@@ -112,30 +103,38 @@ public class LifecycleManager extends BaseLifecycleManager {
}
}
- private void cycleProxy(SdlDisconnectedReason disconnectedReason) {
- cleanProxy();
- initializeProxy();
+ @Override
+ void cycle(SdlDisconnectedReason disconnectedReason) {
+ clean();
+ initialize();
if (!SdlDisconnectedReason.LEGACY_BLUETOOTH_MODE_ENABLED.equals(disconnectedReason) && !SdlDisconnectedReason.PRIMARY_TRANSPORT_CYCLE_REQUEST.equals(disconnectedReason)) {
//We don't want to alert higher if we are just cycling for legacy bluetooth
- onClose("Sdl Proxy Cycled", new SdlException("Sdl Proxy Cycled", SdlExceptionCause.SDL_PROXY_CYCLED));
+ onClose("Sdl Proxy Cycled", new SdlException("Sdl Proxy Cycled", SdlExceptionCause.SDL_PROXY_CYCLED), disconnectedReason);
}
- try {
- session.startSession();
- } catch (SdlException e) {
- e.printStackTrace();
+ if (session != null) {
+ try {
+ session.startSession();
+ } catch (SdlException e) {
+ e.printStackTrace();
+ }
}
}
@RestrictTo(RestrictTo.Scope.LIBRARY)
public void setContext(Context context) {
- this.context = context;
+ this.contextWeakReference = new WeakReference<>(context);
}
@Override
void setSdlSecurityStaticVars() {
super.setSdlSecurityStaticVars();
+ Context context = null;
Service service = null;
+
+ if(this.contextWeakReference != null){
+ context = contextWeakReference.get();
+ }
if (context != null && context instanceof Service) {
service = (Service) context;
}
@@ -144,11 +143,11 @@ public class LifecycleManager extends BaseLifecycleManager {
}
@Override
- void onProtocolSessionStarted(SessionType sessionType) {
- super.onProtocolSessionStarted(sessionType);
+ void onServiceStarted(SessionType sessionType) {
+ super.onServiceStarted(sessionType);
if (sessionType.eq(SessionType.NAV)) {
- navServiceStartResponseReceived = true;
- navServiceStartResponse = true;
+ videoServiceStartResponseReceived = true;
+ videoServiceStartResponse = true;
}
}
@@ -158,42 +157,18 @@ public class LifecycleManager extends BaseLifecycleManager {
if (availablePrimary) {
_transportConfig = transportConfig;
Log.d(TAG, "notifying RPC session ended, but potential primary transport available");
- cycleProxy(SdlDisconnectedReason.PRIMARY_TRANSPORT_CYCLE_REQUEST);
+ cycle(SdlDisconnectedReason.PRIMARY_TRANSPORT_CYCLE_REQUEST);
} else {
- onClose(info, null);
- }
- }
-
- @Override
- void onProtocolSessionStartedNACKed(SessionType sessionType) {
- super.onProtocolSessionStartedNACKed(sessionType);
- if (sessionType.eq(SessionType.NAV)) {
- navServiceStartResponseReceived = true;
- navServiceStartResponse = false;
+ onClose(info, null, null);
}
}
@Override
- void onProtocolSessionEnded(SessionType sessionType) {
- super.onProtocolSessionEnded(sessionType);
+ void onStartServiceNACKed(SessionType sessionType) {
+ super.onStartServiceNACKed(sessionType);
if (sessionType.eq(SessionType.NAV)) {
- navServiceEndResponseReceived = true;
- navServiceEndResponse = true;
- } else if (sessionType.eq(SessionType.PCM)) {
- pcmServiceEndResponseReceived = true;
- pcmServiceEndResponse = true;
- }
- }
-
- @Override
- void onProtocolSessionEndedNACKed(SessionType sessionType) {
- super.onProtocolSessionEndedNACKed(sessionType);
- if (sessionType.eq(SessionType.NAV)) {
- navServiceEndResponseReceived = true;
- navServiceEndResponse = false;
- } else if (sessionType.eq(SessionType.PCM)) {
- pcmServiceEndResponseReceived = true;
- pcmServiceEndResponse = false;
+ videoServiceStartResponseReceived = true;
+ videoServiceStartResponse = false;
}
}
@@ -230,72 +205,60 @@ public class LifecycleManager extends BaseLifecycleManager {
* mode (i.e. without any negotiation) then an instance of VideoStreamingParams is
* returned. If the service was not opened then null is returned.
*/
- private VideoStreamingParameters tryStartVideoStream(boolean isEncrypted, VideoStreamingParameters parameters) {
+ private void tryStartVideoStream(boolean isEncrypted, VideoStreamingParameters parameters) {
if (session == null) {
DebugTool.logWarning("SdlSession is not created yet.");
- return null;
+ return;
}
if (getProtocolVersion() != null && getProtocolVersion().getMajor() >= 5 && !systemCapabilityManager.isCapabilitySupported(SystemCapabilityType.VIDEO_STREAMING)) {
DebugTool.logWarning("Module doesn't support video streaming.");
- return null;
+ return;
}
if (parameters == null) {
DebugTool.logWarning("Video parameters were not supplied.");
- return null;
+ return;
}
- if (!navServiceStartResponseReceived || !navServiceStartResponse //If we haven't started the service before
- || (navServiceStartResponse && isEncrypted && !session.isServiceProtected(SessionType.NAV))) { //Or the service has been started but we'd like to start an encrypted one
+
+ if (!videoServiceStartResponseReceived || !videoServiceStartResponse //If we haven't started the service before
+ || (videoServiceStartResponse && isEncrypted && !session.isServiceProtected(SessionType.NAV))) { //Or the service has been started but we'd like to start an encrypted one
session.setDesiredVideoParams(parameters);
- navServiceStartResponseReceived = false;
- navServiceStartResponse = false;
+ videoServiceStartResponseReceived = false;
+ videoServiceStartResponse = false;
+ addVideoServiceListener();
session.startService(SessionType.NAV, session.getSessionId(), isEncrypted);
- addNavListener();
- FutureTask<Void> fTask = new FutureTask<>(new CallableMethod(RESPONSE_WAIT_TIME));
- ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
- scheduler.execute(fTask);
-
- //noinspection StatementWithEmptyBody
- while (!navServiceStartResponseReceived && !fTask.isDone()) ;
- scheduler.shutdown();
- }
- if (navServiceStartResponse) {
- if (getProtocolVersion() != null && getProtocolVersion().getMajor() < 5) { //Versions 1-4 do not support streaming parameter negotiations
- session.setAcceptedVideoParams(parameters);
- }
- return session.getAcceptedVideoParams();
}
-
- return null;
}
- private void addNavListener() {
+ private void addVideoServiceListener() {
// videos may be started and stopped. Only add this once
- if (navServiceListener == null) {
+ if (videoServiceListener == null) {
- navServiceListener = new ISdlServiceListener() {
+ videoServiceListener = new ISdlServiceListener() {
@Override
public void onServiceStarted(SdlSession session, SessionType type, boolean isEncrypted) {
+ videoServiceStartResponseReceived = true;
+ videoServiceStartResponse = true;
}
@Override
public void onServiceEnded(SdlSession session, SessionType type) {
// reset nav flags so nav can start upon the next transport connection
- navServiceStartResponseReceived = false;
- navServiceStartResponse = false;
+ videoServiceStartResponseReceived = false;
+ videoServiceStartResponse = false;
}
@Override
public void onServiceError(SdlSession session, SessionType type, String reason) {
// if there is an error reset the flags so that there is a chance to restart streaming
- navServiceStartResponseReceived = false;
- navServiceStartResponse = false;
+ videoServiceStartResponseReceived = false;
+ videoServiceStartResponse = false;
}
};
- session.addServiceListener(SessionType.NAV, navServiceListener);
+ session.addServiceListener(SessionType.NAV, videoServiceListener);
}
}
@@ -305,29 +268,17 @@ public class LifecycleManager extends BaseLifecycleManager {
* @return true if the video service is closed successfully, return false otherwise
*/
@Override
- boolean endVideoStream() {
+ void endVideoStream() {
if (session == null) {
DebugTool.logWarning("SdlSession is not created yet.");
- return false;
+ return;
}
if (!session.getIsConnected()) {
DebugTool.logWarning("Connection is not available.");
- return false;
+ return;
}
- navServiceEndResponseReceived = false;
- navServiceEndResponse = false;
session.stopVideoStream();
-
- FutureTask<Void> fTask = new FutureTask<>(new CallableMethod(RESPONSE_WAIT_TIME));
- ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
- scheduler.execute(fTask);
-
- //noinspection StatementWithEmptyBody
- while (!navServiceEndResponseReceived && !fTask.isDone()) ;
- scheduler.shutdown();
-
- return navServiceEndResponse;
}
@Override
@@ -349,46 +300,16 @@ public class LifecycleManager extends BaseLifecycleManager {
* @return true if the audio service is closed successfully, return false otherwise
*/
@Override
- boolean endAudioStream() {
+ void endAudioStream() {
if (session == null) {
DebugTool.logWarning("SdlSession is not created yet.");
- return false;
+ return;
}
if (!session.getIsConnected()) {
DebugTool.logWarning("Connection is not available.");
- return false;
+ return;
}
- pcmServiceEndResponseReceived = false;
- pcmServiceEndResponse = false;
session.stopAudioStream();
-
- FutureTask<Void> fTask = new FutureTask<>(new CallableMethod(RESPONSE_WAIT_TIME));
- ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
- scheduler.execute(fTask);
-
- //noinspection StatementWithEmptyBody
- while (!pcmServiceEndResponseReceived && !fTask.isDone()) ;
- scheduler.shutdown();
-
- return pcmServiceEndResponse;
- }
-
- private class CallableMethod implements Callable<Void> {
- private final long waitTime;
-
- public CallableMethod(int timeInMillis) {
- this.waitTime = timeInMillis;
- }
-
- @Override
- public Void call() {
- try {
- Thread.sleep(waitTime);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- return null;
- }
}
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java
new file mode 100644
index 000000000..f38cf79d0
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java
@@ -0,0 +1,17 @@
+package com.smartdevicelink.managers.screen;
+
+import android.support.annotation.NonNull;
+import com.smartdevicelink.proxy.interfaces.ISdl;
+
+/**
+ * <strong>SubscribeButtonManager</strong> <br>
+ *
+ * Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br>
+ *
+ */
+class SubscribeButtonManager extends BaseSubscribeButtonManager {
+
+ SubscribeButtonManager(@NonNull ISdl internalInterface) {
+ super(internalInterface);
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/protocol/SdlPacket.java b/android/sdl_android/src/main/java/com/smartdevicelink/protocol/SdlPacket.java
new file mode 100644
index 000000000..7a6292505
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/protocol/SdlPacket.java
@@ -0,0 +1,115 @@
+package com.smartdevicelink.protocol;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.smartdevicelink.transport.utl.TransportRecord;
+import com.smartdevicelink.util.DebugTool;
+
+public class SdlPacket extends BaseSdlPacket implements Parcelable {
+ private static final int EXTRA_PARCEL_DATA_LENGTH = 24;
+
+ public SdlPacket(int version, boolean encryption, int frameType,
+ int serviceType, int frameInfo, int sessionId,
+ int dataSize, int messageId, byte[] payload) {
+ super(version, encryption, frameType, serviceType, frameInfo, sessionId, dataSize, messageId, payload);
+ }
+
+ public SdlPacket(int version, boolean encryption, int frameType,
+ int serviceType, int frameInfo, int sessionId,
+ int dataSize, int messageId, byte[] payload, int offset, int bytesToWrite) {
+ super(version, encryption, frameType, serviceType, frameInfo, sessionId, dataSize, messageId, payload, offset, bytesToWrite);
+ }
+
+ protected SdlPacket() {
+ super();
+ }
+
+ protected SdlPacket(BaseSdlPacket packet) {
+ super(packet);
+ }
+
+ /* ***************************************************************************************************************************************************
+ * *********************************************************** Parceable Overrides *****************************************************************
+ *****************************************************************************************************************************************************/
+
+
+
+ //I think this is FIFO...right?
+ public SdlPacket(Parcel p) {
+ this.version = p.readInt();
+ this.encryption = (p.readInt() == 0) ? false : true;
+ this.frameType = p.readInt();
+ this.serviceType = p.readInt();
+ this.frameInfo = p.readInt();
+ this.sessionId = p.readInt();
+ this.dataSize = p.readInt();
+ this.messageId = p.readInt();
+ if(p.readInt() == 1){ //We should have a payload attached
+ payload = new byte[dataSize];
+ p.readByteArray(payload);
+ }
+
+ this.priorityCoefficient = p.readInt();
+
+ if(p.dataAvail() > EXTRA_PARCEL_DATA_LENGTH) { //See note on constant for why not 0
+ try {
+ messagingVersion = p.readInt();
+ if (messagingVersion >= 2) {
+ if (p.readInt() == 1) { //We should have a transport type attached
+ this.transportRecord = (TransportRecord) p.readParcelable(TransportRecord.class.getClassLoader());
+ }
+ }
+ }catch (RuntimeException e){
+ DebugTool.logError("Error creating packet from parcel", e);
+ }
+ }
+ }
+
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+
+ dest.writeInt(version);
+ dest.writeInt(encryption? 1 : 0);
+ dest.writeInt(frameType);
+ dest.writeInt(serviceType);
+ dest.writeInt(frameInfo);
+ dest.writeInt(sessionId);
+ dest.writeInt(dataSize);
+ dest.writeInt(messageId);
+ dest.writeInt(payload!=null? 1 : 0);
+ if(payload!=null){
+ dest.writeByteArray(payload);
+ }
+ dest.writeInt(priorityCoefficient);
+
+ ///Additions after initial creation
+ if(messagingVersion > 1){
+ dest.writeInt(messagingVersion);
+
+ dest.writeInt(transportRecord!=null? 1 : 0);
+ if(transportRecord != null){
+ dest.writeParcelable(transportRecord,0);
+ }
+ }
+
+ }
+
+ public static final Parcelable.Creator<SdlPacket> CREATOR = new Parcelable.Creator<SdlPacket>() {
+ public SdlPacket createFromParcel(Parcel in) {
+ return new SdlPacket(in);
+ }
+
+ @Override
+ public SdlPacket[] newArray(int size) {
+ return new SdlPacket[size];
+ }
+
+ };
+}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java b/android/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java
index b0bda2d26..b42360019 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/proxy/SdlProxyBase.java
@@ -50,6 +50,7 @@ import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.Surface;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.BuildConfig;
import com.smartdevicelink.Dispatcher.IDispatchingStrategy;
import com.smartdevicelink.Dispatcher.ProxyMessageDispatcher;
@@ -313,6 +314,7 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
private Set<String> encryptionRequiredRPCs = new HashSet<>();
private boolean rpcSecuredServiceStarted;
private ServiceEncryptionListener serviceEncryptionListener;
+ private Taskmaster taskmaster;
// Interface broker
private SdlInterfaceBroker _interfaceBroker = null;
@@ -537,7 +539,23 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
public void startRPCEncryption() {
SdlProxyBase.this.startProtectedRPCService();
}
+
+ @Override
+ public Taskmaster getTaskmaster() {
+ return SdlProxyBase.this.getTaskmaster();
+ }
};
+
+ Taskmaster getTaskmaster() {
+ if (taskmaster == null) {
+ Taskmaster.Builder builder = new Taskmaster.Builder();
+ builder.setThreadCount(2);
+ builder.shouldBeDaemon(false);
+ taskmaster = builder.build();
+ taskmaster.start();
+ }
+ return taskmaster;
+ }
private void notifyPutFileStreamError(Exception e, String info)
{
@@ -1832,6 +1850,9 @@ public abstract class SdlProxyBase<proxyListenerType extends IProxyListenerBase>
public void dispose() throws SdlException {
SdlTrace.logProxyEvent("Application called dispose() method.", SDL_LIB_TRACE_KEY);
disposeInternal(SdlDisconnectedReason.APPLICATION_REQUESTED_DISCONNECT);
+ if (taskmaster != null) {
+ taskmaster.shutdown();
+ }
}
/**
* Terminates the App's Interface Registration, closes the transport connection, ends the protocol session, and frees any resources used by the proxy.
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java
index cc7b030c7..ba34a3f58 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java
@@ -35,6 +35,7 @@ import android.util.Log;
import com.smartdevicelink.protocol.SdlPacket;
import com.smartdevicelink.transport.enums.TransportType;
import com.smartdevicelink.transport.utl.TransportRecord;
+import com.smartdevicelink.util.DebugTool;
import java.io.IOException;
import java.io.InputStream;
@@ -815,7 +816,9 @@ public class MultiplexBluetoothTransport extends MultiplexBaseTransport{
}
} catch (IOException|NullPointerException e) { // NPE is ONLY to catch error on mmInStream
Log.e(TAG, "Lost connection in the Connected Thread");
- e.printStackTrace();
+ if(DebugTool.isDebugEnabled()){
+ e.printStackTrace();
+ }
connectionLost();
break;
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java
index ba1cc80a0..3baabc74a 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java
@@ -53,6 +53,7 @@ import android.util.Log;
import com.smartdevicelink.R;
import com.smartdevicelink.transport.RouterServiceValidator.TrustedListCallback;
import com.smartdevicelink.transport.enums.TransportType;
+import com.smartdevicelink.transport.utl.SdlDeviceListener;
import com.smartdevicelink.util.AndroidTools;
import com.smartdevicelink.util.DebugTool;
import com.smartdevicelink.util.SdlAppInfo;
@@ -85,6 +86,8 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
private static final Object QUEUED_SERVICE_LOCK = new Object();
private static ComponentName queuedService = null;
private static Thread.UncaughtExceptionHandler foregroundExceptionHandler = null;
+ private static final Object DEVICE_LISTENER_LOCK = new Object();
+ private static SdlDeviceListener sdlDeviceListener;
public int getRouterServiceVersion(){
return SdlRouterService.ROUTER_SERVICE_VERSION_NUMBER;
@@ -190,9 +193,6 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
});
}
- }else{
- //This was previously not hooked up, so let's leave it commented out
- //onSdlDisabled(context);
}
return;
}else if(intent.getBooleanExtra(TransportConstants.PING_ROUTER_SERVICE_EXTRA, false)){
@@ -223,8 +223,57 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
}
}
+ /**
+ * This method will attempt to start the router service.
+ * @param context to be used to start the service and send broadcasts
+ * @param componentName the router service that should be started
+ * @param altTransportWake if the alt transport flag should be set. Only used in debug
+ * @param device the connected bluetooth device
+ */
+ private static void startRouterService(Context context, ComponentName componentName, boolean altTransportWake, BluetoothDevice device, boolean confirmedDevice) {
+ if (componentName == null) {
+ return;
+ }
+
+ Intent serviceIntent = new Intent();
+ serviceIntent.setComponent(componentName);
+
+ if (altTransportWake) {
+ serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
+ }
+
+ if (device != null) {
+ serviceIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
+ }
+
+ if (confirmedDevice) {
+ serviceIntent.putExtra(TransportConstants.CONFIRMED_SDL_DEVICE, confirmedDevice);
+ }
+
+ try {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
+ context.startService(serviceIntent);
+ } else {
+ serviceIntent.putExtra(FOREGROUND_EXTRA, true);
+ DebugTool.logInfo("Attempting to startForegroundService - " + System.currentTimeMillis());
+ setForegroundExceptionHandler(); //Prevent ANR in case the OS takes too long to start the service
+ context.startForegroundService(serviceIntent);
+
+ }
+ //Make sure to send this out for old apps to close down
+ SdlRouterService.LocalRouterService self = SdlRouterService.getLocalRouterService(serviceIntent, serviceIntent.getComponent());
+ Intent restart = new Intent(SdlRouterService.REGISTER_NEWER_SERVER_INSTANCE_ACTION);
+ restart.putExtra(LOCAL_ROUTER_SERVICE_EXTRA, self);
+ restart.putExtra(LOCAL_ROUTER_SERVICE_DID_START_OWN, true);
+ context.sendBroadcast(restart);
+
+ } catch (SecurityException e) {
+ Log.e(TAG, "Security exception, process is bad");
+ }
+ }
+
private boolean wakeUpRouterService(final Context context, final boolean ping, final boolean altTransportWake, final BluetoothDevice device){
- new ServiceFinder(context, context.getPackageName(), new ServiceFinder.ServiceFinderCallback() {
+ new ServiceFinder(context, context.getPackageName(), new ServiceFinder.ServiceFinderCallback() {
@Override
public void onComplete(Vector<ComponentName> routerServices) {
runningBluetoothServicePackage = new Vector<ComponentName>();
@@ -232,43 +281,43 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
if (runningBluetoothServicePackage.isEmpty()) {
//If there isn't a service running we should try to start one
//We will try to sort the SDL enabled apps and find the one that's been installed the longest
- Intent serviceIntent;
- List<SdlAppInfo> sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator());
+ final List<SdlAppInfo> sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator());
+ synchronized (DEVICE_LISTENER_LOCK) {
+ final boolean sdlDeviceListenerEnabled = SdlDeviceListener.isFeatureSupported(sdlAppInfoList);
+ if (sdlDeviceListenerEnabled) {
+ String myPackage = context.getPackageName();
+ String routerServicePackage = null;
+ if (sdlAppInfoList != null && !sdlAppInfoList.isEmpty() && sdlAppInfoList.get(0).getRouterServiceComponentName() != null) {
+ routerServicePackage = sdlAppInfoList.get(0).getRouterServiceComponentName().getPackageName();
+ }
+ DebugTool.logInfo(TAG + ": This app's package: " + myPackage);
+ DebugTool.logInfo(TAG + ": Router service app's package: " + routerServicePackage);
+ if (myPackage != null && myPackage.equalsIgnoreCase(routerServicePackage)) {
+ SdlDeviceListener sdlDeviceListener = getSdlDeviceListener(context, device);
+ if (!sdlDeviceListener.isRunning()) {
+ sdlDeviceListener.start();
+ }
+ } else {
+ DebugTool.logInfo(TAG + ": Not the app to start the router service nor device listener");
+ }
+ return;
+ }
+ }
+
if (sdlAppInfoList != null && !sdlAppInfoList.isEmpty()) {
- serviceIntent = new Intent();
- serviceIntent.setComponent(sdlAppInfoList.get(0).getRouterServiceComponentName());
+ startRouterService(context, sdlAppInfoList.get(0).getRouterServiceComponentName(), altTransportWake, device, false);
} else{
Log.d(TAG, "No SDL Router Services found");
Log.d(TAG, "WARNING: This application has not specified its SdlRouterService correctly in the manifest. THIS WILL THROW AN EXCEPTION IN FUTURE RELEASES!!");
return;
}
- if (altTransportWake) {
- serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
- }
- if(device != null){
- serviceIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
- }
- try {
- if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
- context.startService(serviceIntent);
- }else {
- serviceIntent.putExtra(FOREGROUND_EXTRA, true);
- DebugTool.logInfo("Attempting to startForegroundService - " + System.currentTimeMillis());
- setForegroundExceptionHandler(); //Prevent ANR in case the OS takes too long to start the service
- context.startForegroundService(serviceIntent);
+ } else { //There are currently running services
+ if(DebugTool.isDebugEnabled()){
+ for(ComponentName service : runningBluetoothServicePackage){
+ DebugTool.logInfo("Currently running router service: " + service.getPackageName());
}
- //Make sure to send this out for old apps to close down
- SdlRouterService.LocalRouterService self = SdlRouterService.getLocalRouterService(serviceIntent, serviceIntent.getComponent());
- Intent restart = new Intent(SdlRouterService.REGISTER_NEWER_SERVER_INSTANCE_ACTION);
- restart.putExtra(LOCAL_ROUTER_SERVICE_EXTRA, self);
- restart.putExtra(LOCAL_ROUTER_SERVICE_DID_START_OWN, true);
- context.sendBroadcast(restart);
-
- } catch (SecurityException e) {
- Log.e(TAG, "Security exception, process is bad");
}
- } else {
if (altTransportWake) {
wakeRouterServiceAltTransport(context);
return;
@@ -281,7 +330,7 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
}
}
});
- return true;
+ return true;
}
private void wakeRouterServiceAltTransport(Context context){
@@ -289,7 +338,11 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
serviceIntent.setAction(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT);
for (ComponentName compName : runningBluetoothServicePackage) {
serviceIntent.setComponent(compName);
- context.startService(serviceIntent);
+ try{
+ context.startService(serviceIntent);
+ } catch (Exception e){
+ DebugTool.logError("Can't start router service for alt transport");
+ }
}
}
@@ -329,10 +382,9 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
* Determines if an instance of the Router Service is currently running on the device.<p>
* <b>Note:</b> This method no longer works on Android Oreo or newer
* @param context A context to access Android system services through.
- * @param pingService Set this to true if you want to make sure the service is up and listening to bluetooth
* @return True if a SDL Router Service is currently running, false otherwise.
*/
- private static boolean isRouterServiceRunning(Context context, boolean pingService){
+ private static boolean isRouterServiceRunning(Context context){
if(context == null){
Log.e(TAG, "Can't look for router service, context supplied was null");
return false;
@@ -356,9 +408,6 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
//Log.d(TAG, "Found Service: "+ service.service.getClassName());
if ((service.service.getClassName()).toLowerCase(Locale.US).contains(SDL_ROUTER_SERVICE_CLASS_NAME) && AndroidTools.isServiceExported(context, service.service)) {
runningBluetoothServicePackage.add(service.service); //Store which instance is running
- if (pingService) {
- pingRouterService(context, service.service.getPackageName(), service.service.getClassName());
- }
}
}
return runningBluetoothServicePackage.size() > 0;
@@ -366,7 +415,8 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
}
/**
- * Attempts to ping a running router service
+ * Attempts to ping a running router service. It does call startForegroundService so it is
+ * important to only call this as a ping if the service is already started.
* @param context A context to access Android system services through.
* @param packageName Package name for service to ping
* @param className Class name for service to ping
@@ -431,7 +481,7 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
}
return;
}
- if((!lookForServices || isRouterServiceRunning(context,false)) && !runningBluetoothServicePackage.isEmpty()){ //So there is a service up, let's see if it's connected
+ if((!lookForServices || isRouterServiceRunning(context)) && !runningBluetoothServicePackage.isEmpty()){ //So there is a service up, let's see if it's connected
final ConcurrentLinkedQueue<ComponentName> list = new ConcurrentLinkedQueue<ComponentName>(runningBluetoothServicePackage);
final SdlRouterStatusProvider.ConnectedStatusCallback sdlBrCallback = new SdlRouterStatusProvider.ConnectedStatusCallback() {
@@ -471,12 +521,13 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
}else{
Log.w(TAG, "Router service isn't running, returning false.");
if(isBluetoothConnected()){
- Log.d(TAG, "Bluetooth is connected. Attempting to start Router Service");
+ Log.d(TAG, "Bluetooth is connected. Attempting to ping Router Service");
Intent serviceIntent = new Intent();
serviceIntent.setAction(TransportConstants.START_ROUTER_SERVICE_ACTION);
serviceIntent.putExtra(TransportConstants.PING_ROUTER_SERVICE_EXTRA, true);
- AndroidTools.sendExplicitBroadcast(context,serviceIntent,null);
+ AndroidTools.sendExplicitBroadcast(context, serviceIntent,null);
}
+
if(callback!=null){
callback.onConnectionStatusUpdate(false, null,context);
}
@@ -500,6 +551,49 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver{
return false;
}
+
+ private static SdlDeviceListener getSdlDeviceListener(Context context, BluetoothDevice bluetoothDevice){
+
+ synchronized (DEVICE_LISTENER_LOCK){
+ if (sdlDeviceListener == null){
+ sdlDeviceListener = new SdlDeviceListener(context, bluetoothDevice, new SdlDeviceListener.Callback() {
+ @Override
+ public boolean onTransportConnected(Context context, BluetoothDevice bluetoothDevice) {
+
+ synchronized (DEVICE_LISTENER_LOCK){
+ sdlDeviceListener = null;
+ if(context != null) {
+ final List<SdlAppInfo> sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator());
+ if(sdlAppInfoList != null && !sdlAppInfoList.isEmpty()) {
+ ComponentName routerService = sdlAppInfoList.get(0).getRouterServiceComponentName();
+ startRouterService(context, routerService, false, bluetoothDevice, true);
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onTransportDisconnected(BluetoothDevice bluetoothDevice) {
+ synchronized (DEVICE_LISTENER_LOCK){
+ sdlDeviceListener = null;
+ }
+ }
+
+ @Override
+ public void onTransportError(BluetoothDevice bluetoothDevice) {
+ synchronized (DEVICE_LISTENER_LOCK){
+ sdlDeviceListener = null;
+ }
+ }
+ });
+ }
+ }
+
+ return sdlDeviceListener;
+ }
+
public static ComponentName consumeQueuedRouterService(){
synchronized(QUEUED_SERVICE_LOCK){
ComponentName retVal = queuedService;
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java
index 4edc639e8..2e3317661 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java
@@ -140,7 +140,7 @@ public class SdlRouterService extends Service{
/**
* <b> NOTE: DO NOT MODIFY THIS UNLESS YOU KNOW WHAT YOU'RE DOING.</b>
*/
- protected static final int ROUTER_SERVICE_VERSION_NUMBER = 12;
+ protected static final int ROUTER_SERVICE_VERSION_NUMBER = 13;
private static final String ROUTER_SERVICE_PROCESS = "com.smartdevicelink.router";
@@ -1259,7 +1259,9 @@ public class SdlRouterService extends Service{
address = device.getAddress();
}
}
- int timeout = getNotificationTimeout(address);
+ boolean confirmedDevice = intent.getBooleanExtra(TransportConstants.CONFIRMED_SDL_DEVICE, false);
+ int timeout = getNotificationTimeout(address, confirmedDevice);
+
enterForeground("Waiting for connection...", timeout, false);
resetForegroundTimeOut(timeout);
} else {
@@ -1383,9 +1385,9 @@ public class SdlRouterService extends Service{
* @return the amount of time for a timeout handler to remove the notification.
*/
@SuppressLint("MissingPermission")
- private int getNotificationTimeout(String address){
+ private int getNotificationTimeout(String address, boolean confirmedDevice){
if(address != null){
- if(hasSDLConnected(address)){
+ if(confirmedDevice || hasSDLConnected(address)){
return FOREGROUND_TIMEOUT * 2;
}else if(this.isFirstStatusCheck(address)) {
// If this is the first time the service has ever connected to this device we want
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java
index 710e9d610..003556109 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportBroker.java
@@ -86,8 +86,7 @@ public class TransportBroker {
private Context currentContext = null;
private final Object INIT_LOCK = new Object();
-
- private TransportType queuedOnTransportConnect = null;
+ private final Object MESSAGE_SEND_LOCK = new Object();
Messenger routerServiceMessenger = null;
final Messenger clientMessenger;
@@ -126,59 +125,55 @@ public class TransportBroker {
};
}
- protected synchronized boolean sendMessageToRouterService(Message message) {
+ protected boolean sendMessageToRouterService(Message message) {
return sendMessageToRouterService(message, 0);
}
- protected synchronized boolean sendMessageToRouterService(Message message, int retryCount) {
- if (message == null) {
- Log.w(TAG, "Attempted to send null message");
- return false;
- }
- //Log.i(TAG, "Attempting to send message type - " + message.what);
- if (isBound && routerServiceMessenger != null) {
- if (registeredWithRouterService
- || message.what == TransportConstants.ROUTER_REGISTER_CLIENT) { //We can send a message if we are registered or are attempting to register
- try {
- routerServiceMessenger.send(message);
- return true;
- } catch (RemoteException e) {
- e.printStackTrace();
- //Let's check to see if we should retry
- if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 && e instanceof TransactionTooLargeException )
- || (retryCount < 5 && routerServiceMessenger.getBinder().isBinderAlive() && routerServiceMessenger.getBinder().pingBinder())) { //We probably just failed on a small transaction =\
- try {
- Thread.sleep(100);
- } catch (InterruptedException e1) {
- e1.printStackTrace();
+ protected boolean sendMessageToRouterService(Message message, int retryCount) {
+ synchronized (MESSAGE_SEND_LOCK) {
+ if (message == null) {
+ Log.w(TAG, "Attempted to send null message");
+ return false;
+ }
+ //Log.i(TAG, "Attempting to send message type - " + message.what);
+ if (isBound && routerServiceMessenger != null && routerServiceMessenger.getBinder() != null && routerServiceMessenger.getBinder().isBinderAlive()) {
+ if (registeredWithRouterService
+ || message.what == TransportConstants.ROUTER_REGISTER_CLIENT) { //We can send a message if we are registered or are attempting to register
+ try {
+ routerServiceMessenger.send(message);
+ return true;
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ //Let's check to see if we should retry
+ if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1 && e instanceof TransactionTooLargeException)
+ || (retryCount < 5 && routerServiceMessenger.getBinder().isBinderAlive() && routerServiceMessenger.getBinder().pingBinder())) { //We probably just failed on a small transaction =\
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e1) {
+ e1.printStackTrace();
+ }
+ return sendMessageToRouterService(message, retryCount++);
+ } else {
+ //DeadObject, time to kill our connection
+ Log.d(TAG, "Dead object while attempting to send packet");
+ stop();
+ onHardwareDisconnected(null, null);
+ return false;
}
- return sendMessageToRouterService(message, retryCount++);
- } else {
- //DeadObject, time to kill our connection
- Log.d(TAG, "Dead object while attempting to send packet");
- routerServiceMessenger = null;
- registeredWithRouterService = false;
- unBindFromRouterService();
- isBound = false;
+ } catch (NullPointerException e) {
+ Log.d(TAG, "Null messenger while attempting to send packet"); // NPE, routerServiceMessenger is null
+ stop();
onHardwareDisconnected(null, null);
return false;
}
- } catch (NullPointerException e) {
- Log.d(TAG, "Null messenger while attempting to send packet"); // NPE, routerServiceMessenger is null
- routerServiceMessenger = null;
- registeredWithRouterService = false;
- unBindFromRouterService();
- isBound = false;
- onHardwareDisconnected(null, null);
+ } else {
+ Log.e(TAG, "Unable to send message to router service. Not registered.");
return false;
}
} else {
- Log.e(TAG, "Unable to send message to router service. Not registered.");
+ Log.e(TAG, "Unable to send message to router service. Not bound.");
return false;
}
- } else {
- Log.e(TAG, "Unable to send message to router service. Not bound.");
- return false;
}
}
@@ -416,7 +411,6 @@ public class TransportBroker {
}
//this.appId = appId.concat(timeStamp);
this.appId = appId;
- queuedOnTransportConnect = null;
currentContext = context;
//Log.d(TAG, "Registering our reply receiver: " + whereToReply);
this.routerService = service;
@@ -448,7 +442,6 @@ public class TransportBroker {
synchronized (INIT_LOCK) {
unregisterWithRouterService();
routerServiceMessenger = null;
- queuedOnTransportConnect = null;
unBindFromRouterService();
isBound = false;
}
@@ -463,13 +456,12 @@ public class TransportBroker {
unregisterWithRouterService();
unBindFromRouterService();
routerServiceMessenger = null;
- queuedOnTransportConnect = null;
currentContext = null;
}
}
- private synchronized void unBindFromRouterService() {
+ private void unBindFromRouterService() {
try {
getContext().unbindService(routerConnection);
@@ -487,27 +479,17 @@ public class TransportBroker {
public void onServiceUnregsiteredFromRouterService(int unregisterCode) {
- queuedOnTransportConnect = null;
}
@Deprecated
public void onHardwareDisconnected(TransportType type) {
- routerServiceDisconnect();
+ stop();
}
public void onHardwareDisconnected(TransportRecord record, List<TransportRecord> connectedTransports) {
}
- private void routerServiceDisconnect() {
- synchronized (INIT_LOCK) {
- unBindFromRouterService();
- routerServiceMessenger = null;
- routerConnection = null;
- queuedOnTransportConnect = null;
- }
- }
-
/**
* WILL NO LONGER BE CALLED
*
@@ -518,7 +500,6 @@ public class TransportBroker {
public boolean onHardwareConnected(TransportType type) {
synchronized (INIT_LOCK) {
if (routerServiceMessenger == null) {
- queuedOnTransportConnect = type;
return false;
}
return true;
@@ -528,7 +509,6 @@ public class TransportBroker {
public boolean onHardwareConnected(List<TransportRecord> transports) {
synchronized (INIT_LOCK) {
if (routerServiceMessenger == null && transports != null && transports.size() > 0) {
- queuedOnTransportConnect = transports.get(transports.size() - 1).getType();
return false;
}
return true;
@@ -616,7 +596,7 @@ public class TransportBroker {
ByteArrayMessageSpliter splitter = new ByteArrayMessageSpliter(appId, TransportConstants.ROUTER_SEND_PACKET, bytes, packet.getPrioirtyCoefficient());
splitter.setRouterServiceVersion(routerServiceVersion);
splitter.setTransportRecord(packet.getTransportRecord());
- while (splitter.isActive()) {
+ while (splitter.isActive() && routerServiceMessenger != null) {
sendMessageToRouterService(splitter.nextMessage());
}
return splitter.close();
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java
index 1a0951503..459a221f9 100644
--- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java
@@ -49,6 +49,7 @@ import android.util.Log;
import com.smartdevicelink.protocol.SdlPacket;
import com.smartdevicelink.protocol.enums.ControlFrameTags;
import com.smartdevicelink.transport.enums.TransportType;
+import com.smartdevicelink.transport.utl.SdlDeviceListener;
import com.smartdevicelink.transport.utl.TransportRecord;
import com.smartdevicelink.util.DebugTool;
@@ -100,7 +101,7 @@ public class TransportManager extends TransportManagerBase{
if (valid) {
mConfig.service = name;
transport = new TransportBrokerImpl(contextWeakReference.get(), mConfig.appId, mConfig.service);
- DebugTool.logInfo("TransportManager start got called; transport=" + transport);
+ DebugTool.logInfo("TransportManager start was called; transport = " + transport);
if(transport != null){
transport.start();
}
@@ -302,6 +303,16 @@ public class TransportManager extends TransportManagerBase{
transportStatus.clear();
transportStatus.addAll(transports);
}
+ //If a bluetooth device has connected, make sure to save the mac address in the case
+ //this app is asked to host the router service, the app knows to do so immediately on connection.
+ if(transports != null && transports.size() > 0) {
+ for (TransportRecord record : transports) {
+ if(record != null && TransportType.BLUETOOTH.equals(record.getType())) {
+ SdlDeviceListener.setSDLConnectedStatus(contextWeakReference.get(), record.getAddress(),true);
+ }
+ }
+ }
+
transportListener.onTransportConnected(transports);
return true;
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java
new file mode 100644
index 000000000..a396d186e
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 2020 Livio, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the Livio Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+package com.smartdevicelink.transport.utl;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.support.annotation.NonNull;
+
+import com.smartdevicelink.transport.MultiplexBaseTransport;
+import com.smartdevicelink.transport.MultiplexBluetoothTransport;
+import com.smartdevicelink.transport.SdlRouterService;
+import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.SdlAppInfo;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+
+public class SdlDeviceListener {
+
+ private static final String TAG = "SdlListener";
+ private static final int MIN_VERSION_REQUIRED = 13;
+ private static final String SDL_DEVICE_STATUS_SHARED_PREFS = "sdl.device.status";
+ private static final Object LOCK = new Object(), RUNNING_LOCK = new Object();
+
+ private final WeakReference<Context> contextWeakReference;
+ private final Callback callback;
+ private final BluetoothDevice connectedDevice;
+ private MultiplexBluetoothTransport bluetoothTransport;
+ private TransportHandler bluetoothHandler;
+ private Handler timeoutHandler;
+ private Runnable timeoutRunner;
+ private boolean isRunning = false;
+
+
+ public SdlDeviceListener(Context context, BluetoothDevice device, Callback callback) {
+ this.contextWeakReference = new WeakReference<>(context);
+ this.connectedDevice = device;
+ this.callback = callback;
+ }
+
+ /**
+ * This will start the SDL Device Listener with two paths. The first path will be a check
+ * against the supplied bluetooth device to see if it has already successfully connected as an
+ * SDL device. If it has, the supplied callback will be called immediately. If the device hasn't
+ * connected as an SDL device before, the SDL Device Listener will then open up an RFCOMM channel
+ * using the SDL UUID and await a potential connection. A timeout is used to ensure this only
+ * listens for a finite amount of time. If this is the first time the device has been seen, this
+ * will listen for 30 seconds, if it is not, this will listen for 15 seconds instead.
+ */
+ public void start() {
+ if(connectedDevice == null) {
+ DebugTool.logInfo(TAG + ": No supplied bluetooth device");
+ if(callback != null){
+ callback.onTransportError(null);
+ }
+ return;
+ }
+
+ if (hasSDLConnected(contextWeakReference.get(), connectedDevice.getAddress())) {
+ DebugTool.logInfo(TAG + ": Confirmed SDL device, should start router service");
+ //This device has connected to SDL previously, it is ok to start the RS right now
+ callback.onTransportConnected(contextWeakReference.get(), connectedDevice);
+ return;
+ }
+ synchronized (RUNNING_LOCK) {
+ isRunning = true;
+ // set timeout = if first time seeing BT device, 30s, if not 15s
+ int timeout = isFirstStatusCheck(connectedDevice.getAddress()) ? 30000 : 15000;
+ //Set our preference as false for this device for now
+ setSDLConnectedStatus(contextWeakReference.get(), connectedDevice.getAddress(), false);
+ bluetoothHandler = new TransportHandler(this);
+ bluetoothTransport = new MultiplexBluetoothTransport(bluetoothHandler);
+ bluetoothTransport.start();
+ timeoutRunner = new Runnable() {
+ @Override
+ public void run() {
+ if (bluetoothTransport != null) {
+ int state = bluetoothTransport.getState();
+ if (state != MultiplexBluetoothTransport.STATE_CONNECTED) {
+ DebugTool.logInfo(TAG + ": No bluetooth connection made");
+ bluetoothTransport.stop();
+ } //else BT is connected; it will close itself through callbacks
+ }
+ }
+ };
+ timeoutHandler = new Handler(Looper.getMainLooper());
+ timeoutHandler.postDelayed(timeoutRunner, timeout);
+ }
+ }
+
+ /**
+ * Check to see if this instance is in the middle of running or not
+ *
+ * @return if this is already in the process of running
+ */
+ public boolean isRunning() {
+ synchronized (RUNNING_LOCK) {
+ return isRunning;
+ }
+ }
+
+ private static class TransportHandler extends Handler {
+
+ final WeakReference<SdlDeviceListener> provider;
+
+ TransportHandler(SdlDeviceListener provider) {
+ this.provider = new WeakReference<>(provider);
+ }
+
+ @Override
+ public void handleMessage(@NonNull Message msg) {
+ if (this.provider.get() == null) {
+ return;
+ }
+ SdlDeviceListener sdlListener = this.provider.get();
+ switch (msg.what) {
+
+ case SdlRouterService.MESSAGE_STATE_CHANGE:
+ switch (msg.arg1) {
+ case MultiplexBaseTransport.STATE_CONNECTED:
+ sdlListener.setSDLConnectedStatus(sdlListener.contextWeakReference.get(), sdlListener.connectedDevice.getAddress(), true);
+ boolean keepConnectionOpen = sdlListener.callback.onTransportConnected(sdlListener.contextWeakReference.get(), sdlListener.connectedDevice);
+ if (!keepConnectionOpen) {
+ sdlListener.bluetoothTransport.stop();
+ sdlListener.bluetoothTransport = null;
+ sdlListener.timeoutHandler.removeCallbacks(sdlListener.timeoutRunner);
+ }
+ break;
+ case MultiplexBaseTransport.STATE_NONE:
+ // We've just lost the connection
+ sdlListener.callback.onTransportDisconnected(sdlListener.connectedDevice);
+ break;
+ case MultiplexBaseTransport.STATE_ERROR:
+ sdlListener.callback.onTransportError(sdlListener.connectedDevice);
+ break;
+ }
+ break;
+
+ case com.smartdevicelink.transport.SdlRouterService.MESSAGE_READ:
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * Set the connection establishment status of the particular device
+ *
+ * @param address address of the device in question
+ * @param hasSDLConnected true if a connection has been established, false if not
+ */
+ public static void setSDLConnectedStatus(Context context, String address, boolean hasSDLConnected) {
+ synchronized (LOCK) {
+ if (context != null) {
+ DebugTool.logInfo(TAG + ": Saving connected status - " + address + " : " + hasSDLConnected);
+ SharedPreferences preferences = context.getSharedPreferences(SDL_DEVICE_STATUS_SHARED_PREFS, Context.MODE_PRIVATE);
+ if (preferences.contains(address) && hasSDLConnected == preferences.getBoolean(address, false)) {
+ //The same key/value exists in our shared preferences. No reason to write again.
+ return;
+ }
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.putBoolean(address, hasSDLConnected);
+ editor.commit();
+ }
+ }
+ }
+
+ /**
+ * Checks to see if a device address has connected to SDL before.
+ *
+ * @param address the mac address of the device in question
+ * @return if this is the first status check of this device
+ */
+ private boolean isFirstStatusCheck(String address) {
+ synchronized (LOCK) {
+ Context context = contextWeakReference.get();
+ if (context != null) {
+ SharedPreferences preferences = context.getSharedPreferences(SDL_DEVICE_STATUS_SHARED_PREFS, Context.MODE_PRIVATE);
+ return !preferences.contains(address);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Checks to see if a device address has connected to SDL before.
+ *
+ * @param address the mac address of the device in question
+ * @return if an SDL connection has ever been established with this device
+ */
+ public static boolean hasSDLConnected(Context context, String address) {
+ synchronized (LOCK) {
+ if (context != null) {
+ SharedPreferences preferences = context.getSharedPreferences(SDL_DEVICE_STATUS_SHARED_PREFS, Context.MODE_PRIVATE);
+ return preferences.contains(address) && preferences.getBoolean(address, false);
+ }
+ return false;
+ }
+ }
+
+ /**
+ * This method will check the current device and list of SDL enabled apps to derive if the
+ * feature can be supported. Due to older libraries sending their intents to start the router
+ * service right at the bluetooth A2DP/HFS connections, this feature can't be used until all
+ * applications are updated to the point they include the feature.
+ *
+ * @param sdlAppInfoList current list of SDL enabled applications on the device
+ * @return if this feature is supported or not. If it is not, the caller should follow the
+ * previously used flow, ie start the router service.
+ */
+ public static boolean isFeatureSupported(List<SdlAppInfo> sdlAppInfoList) {
+
+ SdlAppInfo appInfo;
+ for (int i = sdlAppInfoList.size() - 1; i >= 0; i--) {
+ appInfo = sdlAppInfoList.get(i);
+ if (appInfo != null
+ && !appInfo.isCustomRouterService()
+ && appInfo.getRouterServiceVersion() < MIN_VERSION_REQUIRED) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Callback for the SdlDeviceListener. It will return if the supplied device makes a bluetooth
+ * connection on the SDL UUID RFCOMM chanel or not. Most of the time the only callback that
+ * matters will be the onTransportConnected.
+ */
+ public interface Callback {
+ /**
+ * @param bluetoothDevice the BT device that successfully connected to SDL's UUID
+ * @return if the RFCOMM connection should stay open. In most cases this should be false
+ */
+ boolean onTransportConnected(Context context, BluetoothDevice bluetoothDevice);
+
+ void onTransportDisconnected(BluetoothDevice bluetoothDevice);
+
+ void onTransportError(BluetoothDevice bluetoothDevice);
+ }
+}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java
new file mode 100644
index 000000000..bd31e746e
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java
@@ -0,0 +1,56 @@
+package com.smartdevicelink.transport.utl;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.smartdevicelink.transport.enums.TransportType;
+
+public class TransportRecord extends BaseTransportRecord implements Parcelable {
+
+ public TransportRecord(TransportType transportType, String address) {
+ super(transportType, address);
+ }
+
+ public TransportRecord(Parcel p) {
+ if (p.readInt() == 1) { //We should have a transport type attached
+ String transportName = p.readString();
+ if(transportName != null){
+ this.type = TransportType.valueOf(transportName);
+ }
+ }
+
+ if (p.readInt() == 1) { //We should have a transport address attached
+ address = p.readString();
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(type!=null? 1 : 0);
+ if(type != null){
+ dest.writeString(type.name());
+ }
+
+ dest.writeInt(address !=null? 1 : 0);
+ if(address != null){
+ dest.writeString(address);
+ }
+ }
+
+ public static final Parcelable.Creator<TransportRecord> CREATOR = new Parcelable.Creator<TransportRecord>() {
+ public TransportRecord createFromParcel(Parcel in) {
+ return new TransportRecord(in);
+ }
+
+ @Override
+ public TransportRecord[] newArray(int size) {
+ return new TransportRecord[size];
+ }
+
+ };
+}
diff --git a/base/src/main/java/com/smartdevicelink/util/HttpRequestTask.java b/android/sdl_android/src/main/java/com/smartdevicelink/util/HttpRequestTask.java
index 3eee85a05..3eee85a05 100644
--- a/base/src/main/java/com/smartdevicelink/util/HttpRequestTask.java
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/util/HttpRequestTask.java
diff --git a/android/sdl_android/src/main/res/values/sdl.xml b/android/sdl_android/src/main/res/values/sdl.xml
index e2a20bff8..bc83d733e 100644
--- a/android/sdl_android/src/main/res/values/sdl.xml
+++ b/android/sdl_android/src/main/res/values/sdl.xml
@@ -2,7 +2,7 @@
<resources>
<string name="sdl_router_service_version_name" translatable="false">sdl_router_version</string>
- <integer name="sdl_router_service_version_value">12</integer>
+ <integer name="sdl_router_service_version_value">13</integer>
<string name="sdl_router_service_is_custom_name" translatable="false">sdl_custom_router</string>
</resources>
diff --git a/base/src/main/java/android/os/AsyncTask.java b/base/src/main/java/android/os/AsyncTask.java
index f3953f16a..db6b69a8e 100644
--- a/base/src/main/java/android/os/AsyncTask.java
+++ b/base/src/main/java/android/os/AsyncTask.java
@@ -18,6 +18,7 @@
package android.os;
+@Deprecated
public abstract class AsyncTask<Params, Progress, Result> {
diff --git a/base/src/main/java/android/os/Parcel.java b/base/src/main/java/android/os/Parcel.java
index 4dbf6b7ce..b99dbc671 100644
--- a/base/src/main/java/android/os/Parcel.java
+++ b/base/src/main/java/android/os/Parcel.java
@@ -18,6 +18,7 @@
package android.os;
+@Deprecated
public class Parcel {
public void writeInt(int data){
diff --git a/base/src/main/java/android/os/Parcelable.java b/base/src/main/java/android/os/Parcelable.java
index 76499a99c..e09a1754c 100644
--- a/base/src/main/java/android/os/Parcelable.java
+++ b/base/src/main/java/android/os/Parcelable.java
@@ -17,6 +17,7 @@
*/
package android.os;
+@Deprecated
public interface Parcelable {
int describeContents();
diff --git a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
index c52f27b2d..234cbb22e 100644
--- a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
@@ -83,7 +83,7 @@ abstract class BaseSdlManager {
static final String TAG = "BaseSubManager";
final Object STATE_LOCK = new Object();
int state = -1;
- String appId, appName, shortAppName;
+ String appId, appName, shortAppName, resumeHash;
boolean isMediaApp;
Language hmiLanguage;
Language language;
@@ -121,7 +121,7 @@ abstract class BaseSdlManager {
// Initialize with anonymous lifecycleListener
final LifecycleManager.LifecycleListener lifecycleListener = new LifecycleManager.LifecycleListener() {
@Override
- public void onProxyConnected(LifecycleManager lifeCycleManager) {
+ public void onConnected(LifecycleManager lifeCycleManager) {
Log.i(TAG, "Proxy is connected. Now initializing.");
synchronized (this) {
changeRegistrationRetry = 0;
@@ -141,8 +141,11 @@ abstract class BaseSdlManager {
}
@Override
- public void onProxyClosed(LifecycleManager lifeCycleManager, String info, Exception e, SdlDisconnectedReason reason) {
- BaseSdlManager.this.onProxyClosed(reason);
+ public void onClosed(LifecycleManager lifeCycleManager, String info, Exception e, SdlDisconnectedReason reason) {
+ Log.i(TAG, "Proxy is closed.");
+ if (reason == null || !reason.equals(SdlDisconnectedReason.LANGUAGE_CHANGE)) {
+ dispose();
+ }
}
@Override
@@ -165,8 +168,6 @@ abstract class BaseSdlManager {
// ABSTRACT METHODS
abstract void retryChangeRegistration();
- abstract void onProxyClosed(SdlDisconnectedReason reason);
-
abstract void checkState();
abstract void initialize();
@@ -310,7 +311,6 @@ abstract class BaseSdlManager {
//Set variables to null that are no longer needed
queuedNotifications = null;
queuedNotificationListener = null;
- onRPCNotificationListeners = null;
}
}
@@ -333,6 +333,7 @@ abstract class BaseSdlManager {
appConfig.setAppID(appId);
appConfig.setMinimumProtocolVersion(minimumProtocolVersion);
appConfig.setMinimumRPCVersion(minimumRPCVersion);
+ appConfig.setResumeHash(resumeHash);
lifecycleManager = new LifecycleManager(appConfig, transport, lifecycleListener);
_internalInterface = lifecycleManager.getInternalInterface((SdlManager) BaseSdlManager.this);
@@ -630,6 +631,16 @@ abstract class BaseSdlManager {
}
/**
+ * Sets the Resumption Hash ID
+ *
+ * @param resumeHash String representation of the Hash ID Used to resume the application
+ */
+ public Builder setResumeHash(final String resumeHash) {
+ sdlManager.resumeHash = resumeHash;
+ return this;
+ }
+
+ /**
* Sets the Application Name
*
* @param appName String that will be associated as the app's name
diff --git a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java
index 0054b7810..2934e5736 100644
--- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java
@@ -36,6 +36,7 @@ import android.support.annotation.NonNull;
import android.support.annotation.RestrictTo;
import android.util.Log;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.SdlConnection.ISdlConnectionListener;
import com.smartdevicelink.SdlConnection.SdlSession;
import com.smartdevicelink.exception.SdlException;
@@ -134,6 +135,7 @@ abstract class BaseLifecycleManager {
Version minimumProtocolVersion;
Version minimumRPCVersion;
BaseTransportConfig _transportConfig;
+ private Taskmaster taskmaster;
BaseLifecycleManager(AppConfig appConfig, BaseTransportConfig config, LifecycleListener listener) {
this.appConfig = appConfig;
@@ -141,7 +143,7 @@ abstract class BaseLifecycleManager {
this.lifecycleListener = listener;
this.minimumProtocolVersion = appConfig.getMinimumProtocolVersion();
this.minimumRPCVersion = appConfig.getMinimumRPCVersion();
- initializeProxy();
+ initialize();
}
public void start() {
@@ -161,8 +163,25 @@ abstract class BaseLifecycleManager {
}
}
- public void stop() {
- session.close();
+ public synchronized void stop() {
+ if(session != null) {
+ session.close();
+ session = null;
+ }
+ if (taskmaster != null) {
+ taskmaster.shutdown();
+ }
+ }
+
+ Taskmaster getTaskmaster() {
+ if (taskmaster == null) {
+ Taskmaster.Builder builder = new Taskmaster.Builder();
+ builder.setThreadCount(2);
+ builder.shouldBeDaemon(true);
+ taskmaster = builder.build();
+ taskmaster.start();
+ }
+ return taskmaster;
}
Version getProtocolVersion() {
@@ -326,10 +345,10 @@ abstract class BaseLifecycleManager {
return currentHMIStatus;
}
- void onClose(String info, Exception e) {
+ void onClose(String info, Exception e, SdlDisconnectedReason reason) {
Log.i(TAG, "onClose");
if (lifecycleListener != null) {
- lifecycleListener.onProxyClosed((LifecycleManager) this, info, e, null);
+ lifecycleListener.onClosed((LifecycleManager) this, info, e, reason);
}
}
@@ -383,7 +402,7 @@ abstract class BaseLifecycleManager {
UnregisterAppInterface msg = new UnregisterAppInterface();
msg.setCorrelationID(UNREGISTER_APP_INTERFACE_CORRELATION_ID);
sendRPCMessagePrivate(msg, true);
- cleanProxy();
+ clean();
return;
}
processRaiResponse(raiResponse);
@@ -394,7 +413,7 @@ abstract class BaseLifecycleManager {
boolean shouldInit = currentHMIStatus == null;
currentHMIStatus = (OnHMIStatus) message;
if (lifecycleListener != null && shouldInit) {
- lifecycleListener.onProxyConnected((LifecycleManager) BaseLifecycleManager.this);
+ lifecycleListener.onConnected((LifecycleManager) BaseLifecycleManager.this);
}
break;
case ON_HASH_CHANGE:
@@ -443,15 +462,15 @@ abstract class BaseLifecycleManager {
if (!onAppInterfaceUnregistered.getReason().equals(AppInterfaceUnregisteredReason.LANGUAGE_CHANGE)) {
Log.v(TAG, "on app interface unregistered");
- cleanProxy();
+ clean();
} else {
Log.v(TAG, "re-registering for language change");
- processLanguageChange();
+ cycle(SdlDisconnectedReason.LANGUAGE_CHANGE);
}
break;
case UNREGISTER_APP_INTERFACE:
Log.v(TAG, "unregister app interface");
- cleanProxy();
+ clean();
break;
}
}
@@ -460,19 +479,6 @@ abstract class BaseLifecycleManager {
};
- private void processLanguageChange() {
- if (session != null) {
- if (session.getIsConnected()) {
- session.close();
- }
- try {
- session.startSession();
- } catch (SdlException e) {
- e.printStackTrace();
- }
- }
- }
-
/* *******************************************************************************************************
********************************** INTERNAL - RPC LISTENERS !! END !! *********************************
*********************************************************************************************************/
@@ -856,7 +862,7 @@ abstract class BaseLifecycleManager {
final ISdlConnectionListener sdlConnectionListener = new ISdlConnectionListener() {
@Override
public void onTransportDisconnected(String info) {
- onClose(info, null);
+ onClose(info, null, null);
}
@@ -868,7 +874,7 @@ abstract class BaseLifecycleManager {
@Override
public void onTransportError(String info, Exception e) {
- onClose(info, e);
+ onClose(info, e, null);
}
@@ -916,24 +922,24 @@ abstract class BaseLifecycleManager {
@Override
public void onProtocolSessionStartedNACKed(SessionType sessionType, byte sessionID, byte version, String correlationID, List<String> rejectedParams) {
- Log.w(TAG, sessionType + " onProtocolSessionStartedNACKed " + sessionID + " RejectedParams: " + rejectedParams);
- BaseLifecycleManager.this.onProtocolSessionStartedNACKed(sessionType);
+ Log.w(TAG, sessionType.getName() + " onProtocolSessionStartedNACKed " + sessionID + " RejectedParams: " + rejectedParams);
+ BaseLifecycleManager.this.onStartServiceNACKed(sessionType);
}
@Override
public void onProtocolSessionStarted(SessionType sessionType, byte sessionID, byte version, String correlationID, int hashID, boolean isEncrypted) {
Log.i(TAG, "on protocol session started");
- BaseLifecycleManager.this.onProtocolSessionStarted(sessionType);
+ BaseLifecycleManager.this.onServiceStarted(sessionType);
}
@Override
public void onProtocolSessionEnded(SessionType sessionType, byte sessionID, String correlationID) {
- BaseLifecycleManager.this.onProtocolSessionEnded(sessionType);
+ //Currently not necessary
}
@Override
public void onProtocolSessionEndedNACKed(SessionType sessionType, byte sessionID, String correlationID) {
- BaseLifecycleManager.this.onProtocolSessionEndedNACKed(sessionType);
+ //Currently not necessary
}
@Override
@@ -1159,172 +1165,17 @@ abstract class BaseLifecycleManager {
public void startRPCEncryption() {
BaseLifecycleManager.this.startRPCEncryption();
}
+
+ @Override
+ public Taskmaster getTaskmaster() {
+ return BaseLifecycleManager.this.getTaskmaster();
+ }
};
/* *******************************************************************************************************
********************************************* ISdl - END ************************************************
*********************************************************************************************************/
- public interface LifecycleListener {
- void onProxyConnected(LifecycleManager lifeCycleManager);
-
- void onProxyClosed(LifecycleManager lifeCycleManager, String info, Exception e, SdlDisconnectedReason reason);
-
- void onServiceStarted(SessionType sessionType);
-
- void onServiceEnded(SessionType sessionType);
-
- void onError(LifecycleManager lifeCycleManager, String info, Exception e);
- }
-
- public static class AppConfig {
- private String appID, appName, ngnMediaScreenAppName;
- private Vector<TTSChunk> ttsName;
- private Vector<String> vrSynonyms;
- private boolean isMediaApp = false;
- private Language languageDesired, hmiDisplayLanguageDesired;
- private Vector<AppHMIType> appType;
- private TemplateColorScheme dayColorScheme, nightColorScheme;
- private Version minimumProtocolVersion;
- private Version minimumRPCVersion;
-
- private void prepare() {
- if (getNgnMediaScreenAppName() == null) {
- setNgnMediaScreenAppName(getAppName());
- }
-
- if (getLanguageDesired() == null) {
- setLanguageDesired(Language.EN_US);
- }
-
- if (getHmiDisplayLanguageDesired() == null) {
- setHmiDisplayLanguageDesired(Language.EN_US);
- }
-
- if (getVrSynonyms() == null) {
- setVrSynonyms(new Vector<String>());
- getVrSynonyms().add(getAppName());
- }
- }
-
- public String getAppID() {
- return appID;
- }
-
- public void setAppID(String appID) {
- this.appID = appID;
- }
-
- public String getAppName() {
- return appName;
- }
-
- public void setAppName(String appName) {
- this.appName = appName;
- }
-
- public String getNgnMediaScreenAppName() {
- return ngnMediaScreenAppName;
- }
-
- public void setNgnMediaScreenAppName(String ngnMediaScreenAppName) {
- this.ngnMediaScreenAppName = ngnMediaScreenAppName;
- }
-
- public Vector<TTSChunk> getTtsName() {
- return ttsName;
- }
-
- public void setTtsName(Vector<TTSChunk> ttsName) {
- this.ttsName = ttsName;
- }
-
- public Vector<String> getVrSynonyms() {
- return vrSynonyms;
- }
-
- public void setVrSynonyms(Vector<String> vrSynonyms) {
- this.vrSynonyms = vrSynonyms;
- }
-
- public boolean isMediaApp() {
- return isMediaApp;
- }
-
- public void setMediaApp(boolean mediaApp) {
- isMediaApp = mediaApp;
- }
-
- public Language getLanguageDesired() {
- return languageDesired;
- }
-
- public void setLanguageDesired(Language languageDesired) {
- this.languageDesired = languageDesired;
- }
-
- public Language getHmiDisplayLanguageDesired() {
- return hmiDisplayLanguageDesired;
- }
-
- public void setHmiDisplayLanguageDesired(Language hmiDisplayLanguageDesired) {
- this.hmiDisplayLanguageDesired = hmiDisplayLanguageDesired;
- }
-
- public Vector<AppHMIType> getAppType() {
- return appType;
- }
-
- public void setAppType(Vector<AppHMIType> appType) {
- this.appType = appType;
- }
-
- public TemplateColorScheme getDayColorScheme() {
- return dayColorScheme;
- }
-
- public void setDayColorScheme(TemplateColorScheme dayColorScheme) {
- this.dayColorScheme = dayColorScheme;
- }
-
- public TemplateColorScheme getNightColorScheme() {
- return nightColorScheme;
- }
-
- public void setNightColorScheme(TemplateColorScheme nightColorScheme) {
- this.nightColorScheme = nightColorScheme;
- }
-
- public Version getMinimumProtocolVersion() {
- return minimumProtocolVersion;
- }
-
- /**
- * Sets the minimum protocol version that will be permitted to connect.
- * If the protocol version of the head unit connected is below this version,
- * the app will disconnect with an EndService protocol message and will not register.
- *
- * @param minimumProtocolVersion a Version object with the minimally accepted Protocol version
- */
- public void setMinimumProtocolVersion(Version minimumProtocolVersion) {
- this.minimumProtocolVersion = minimumProtocolVersion;
- }
-
- public Version getMinimumRPCVersion() {
- return minimumRPCVersion;
- }
-
- /**
- * The minimum RPC version that will be permitted to connect.
- * If the RPC version of the head unit connected is below this version, an UnregisterAppInterface will be sent.
- *
- * @param minimumRPCVersion a Version object with the minimally accepted RPC spec version
- */
- public void setMinimumRPCVersion(Version minimumRPCVersion) {
- this.minimumRPCVersion = minimumRPCVersion;
- }
- }
-
/**
* Temporary method to bridge the new PLAY_PAUSE and OKAY button functionality with the old
* OK button name. This should be removed during the next major release
@@ -1378,7 +1229,7 @@ abstract class BaseLifecycleManager {
return null;
}
- void cleanProxy() {
+ void clean() {
firstTimeFull = true;
currentHMIStatus = null;
if (rpcListeners != null) {
@@ -1458,7 +1309,7 @@ abstract class BaseLifecycleManager {
********************************** Platform specific methods - START *************************************
*********************************************************************************************************/
- void initializeProxy() {
+ void initialize() {
this.rpcListeners = new HashMap<>();
this.rpcResponseListeners = new HashMap<>();
this.rpcNotificationListeners = new HashMap<>();
@@ -1467,12 +1318,12 @@ abstract class BaseLifecycleManager {
setupInternalRpcListeners();
}
- void onProtocolSessionStarted(SessionType sessionType) {
+ void onServiceStarted(SessionType sessionType) {
if (sessionType != null) {
if (minimumProtocolVersion != null && minimumProtocolVersion.isNewerThan(getProtocolVersion()) == 1) {
Log.w(TAG, String.format("Disconnecting from head unit, the configured minimum protocol version %s is greater than the supported protocol version %s", minimumProtocolVersion, getProtocolVersion()));
session.endService(sessionType, session.getSessionId());
- cleanProxy();
+ clean();
return;
}
@@ -1497,9 +1348,9 @@ abstract class BaseLifecycleManager {
rai.setAppHMIType(appConfig.getAppType());
rai.setDayColorScheme(appConfig.getDayColorScheme());
rai.setNightColorScheme(appConfig.getNightColorScheme());
+ rai.setHashID(appConfig.getResumeHash());
//Add device/system info in the future
- //TODO attach previous hash id
sendRPCMessagePrivate(rai, true);
} else {
@@ -1509,30 +1360,25 @@ abstract class BaseLifecycleManager {
}
}
- void onTransportDisconnected(String info, boolean availablePrimary, BaseTransportConfig transportConfig) {
- }
+ abstract void cycle(SdlDisconnectedReason disconnectedReason);
- void onProtocolSessionStartedNACKed(SessionType sessionType) {
+ void onTransportDisconnected(String info, boolean availablePrimary, BaseTransportConfig transportConfig) {
}
- void onProtocolSessionEnded(SessionType sessionType) {
+ void onStartServiceNACKed(SessionType sessionType) {
}
- void onProtocolSessionEndedNACKed(SessionType sessionType) {
- }
void startVideoService(boolean encrypted, VideoStreamingParameters parameters) {
}
- boolean endVideoStream() {
- return false;
+ void endVideoStream() {
}
void startAudioService(boolean encrypted) {
}
- boolean endAudioStream() {
- return false;
+ void endAudioStream() {
}
void setSdlSecurityStaticVars() {
@@ -1541,4 +1387,180 @@ abstract class BaseLifecycleManager {
/* *******************************************************************************************************
********************************** Platform specific methods - End *************************************
*********************************************************************************************************/
+
+ /* *******************************************************************************************************
+ ****************************** Inner Classes and Interfaces - Start *************************************
+ *********************************************************************************************************/
+
+ public interface LifecycleListener {
+ void onConnected(LifecycleManager lifeCycleManager);
+
+ void onClosed(LifecycleManager lifeCycleManager, String info, Exception e, SdlDisconnectedReason reason);
+
+ void onServiceStarted(SessionType sessionType);
+
+ void onServiceEnded(SessionType sessionType);
+
+ void onError(LifecycleManager lifeCycleManager, String info, Exception e);
+ }
+
+ public static class AppConfig {
+ private String appID, appName, ngnMediaScreenAppName, resumeHash;
+ private Vector<TTSChunk> ttsName;
+ private Vector<String> vrSynonyms;
+ private boolean isMediaApp = false;
+ private Language languageDesired, hmiDisplayLanguageDesired;
+ private Vector<AppHMIType> appType;
+ private TemplateColorScheme dayColorScheme, nightColorScheme;
+ private Version minimumProtocolVersion;
+ private Version minimumRPCVersion;
+
+ private void prepare() {
+ if (getNgnMediaScreenAppName() == null) {
+ setNgnMediaScreenAppName(getAppName());
+ }
+
+ if (getLanguageDesired() == null) {
+ setLanguageDesired(Language.EN_US);
+ }
+
+ if (getHmiDisplayLanguageDesired() == null) {
+ setHmiDisplayLanguageDesired(Language.EN_US);
+ }
+
+ if (getVrSynonyms() == null) {
+ setVrSynonyms(new Vector<String>());
+ getVrSynonyms().add(getAppName());
+ }
+ }
+
+ public String getAppID() {
+ return appID;
+ }
+
+ public void setAppID(String appID) {
+ this.appID = appID;
+ }
+
+ public String getAppName() {
+ return appName;
+ }
+
+ public void setAppName(String appName) {
+ this.appName = appName;
+ }
+
+ public String getNgnMediaScreenAppName() {
+ return ngnMediaScreenAppName;
+ }
+
+ public void setNgnMediaScreenAppName(String ngnMediaScreenAppName) {
+ this.ngnMediaScreenAppName = ngnMediaScreenAppName;
+ }
+
+ public Vector<TTSChunk> getTtsName() {
+ return ttsName;
+ }
+
+ public void setTtsName(Vector<TTSChunk> ttsName) {
+ this.ttsName = ttsName;
+ }
+
+ public Vector<String> getVrSynonyms() {
+ return vrSynonyms;
+ }
+
+ public void setVrSynonyms(Vector<String> vrSynonyms) {
+ this.vrSynonyms = vrSynonyms;
+ }
+
+ public boolean isMediaApp() {
+ return isMediaApp;
+ }
+
+ public void setMediaApp(boolean mediaApp) {
+ isMediaApp = mediaApp;
+ }
+
+ public Language getLanguageDesired() {
+ return languageDesired;
+ }
+
+ public void setLanguageDesired(Language languageDesired) {
+ this.languageDesired = languageDesired;
+ }
+
+ public Language getHmiDisplayLanguageDesired() {
+ return hmiDisplayLanguageDesired;
+ }
+
+ public void setHmiDisplayLanguageDesired(Language hmiDisplayLanguageDesired) {
+ this.hmiDisplayLanguageDesired = hmiDisplayLanguageDesired;
+ }
+
+ public Vector<AppHMIType> getAppType() {
+ return appType;
+ }
+
+ public void setAppType(Vector<AppHMIType> appType) {
+ this.appType = appType;
+ }
+
+ public String getResumeHash() {
+ return this.resumeHash;
+ }
+
+ public void setResumeHash(String resumeHash) {
+ this.resumeHash = resumeHash;
+ }
+
+ public TemplateColorScheme getDayColorScheme() {
+ return dayColorScheme;
+ }
+
+ public void setDayColorScheme(TemplateColorScheme dayColorScheme) {
+ this.dayColorScheme = dayColorScheme;
+ }
+
+ public TemplateColorScheme getNightColorScheme() {
+ return nightColorScheme;
+ }
+
+ public void setNightColorScheme(TemplateColorScheme nightColorScheme) {
+ this.nightColorScheme = nightColorScheme;
+ }
+
+ public Version getMinimumProtocolVersion() {
+ return minimumProtocolVersion;
+ }
+
+ /**
+ * Sets the minimum protocol version that will be permitted to connect.
+ * If the protocol version of the head unit connected is below this version,
+ * the app will disconnect with an EndService protocol message and will not register.
+ *
+ * @param minimumProtocolVersion a Version object with the minimally accepted Protocol version
+ */
+ public void setMinimumProtocolVersion(Version minimumProtocolVersion) {
+ this.minimumProtocolVersion = minimumProtocolVersion;
+ }
+
+ public Version getMinimumRPCVersion() {
+ return minimumRPCVersion;
+ }
+
+ /**
+ * The minimum RPC version that will be permitted to connect.
+ * If the RPC version of the head unit connected is below this version, an UnregisterAppInterface will be sent.
+ *
+ * @param minimumRPCVersion a Version object with the minimally accepted RPC spec version
+ */
+ public void setMinimumRPCVersion(Version minimumRPCVersion) {
+ this.minimumRPCVersion = minimumRPCVersion;
+ }
+ }
+
+ /* *******************************************************************************************************
+ ****************************** Inner Classes and Interfaces - End ***************************************
+ *********************************************************************************************************/
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java
index 82e4c4131..8ab2caecd 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java
@@ -51,6 +51,7 @@ import com.smartdevicelink.managers.screen.menu.VoiceCommand;
import com.smartdevicelink.managers.screen.menu.VoiceCommandManager;
import com.smartdevicelink.proxy.interfaces.ISdl;
import com.smartdevicelink.proxy.rpc.KeyboardProperties;
+import com.smartdevicelink.proxy.rpc.enums.ButtonName;
import com.smartdevicelink.proxy.rpc.enums.InteractionMode;
import com.smartdevicelink.proxy.rpc.enums.MetadataType;
import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
@@ -74,19 +75,23 @@ abstract class BaseScreenManager extends BaseSubManager {
private VoiceCommandManager voiceCommandManager;
private MenuManager menuManager;
private ChoiceSetManager choiceSetManager;
+ private SubscribeButtonManager subscribeButtonManager;
// Sub manager listener
private final CompletionListener subManagerListener = new CompletionListener() {
@Override
public synchronized void onComplete(boolean success) {
- if (softButtonManager != null && textAndGraphicManager != null && voiceCommandManager != null && menuManager != null && choiceSetManager != null) {
- if (softButtonManager.getState() == BaseSubManager.READY && textAndGraphicManager.getState() == BaseSubManager.READY && voiceCommandManager.getState() == BaseSubManager.READY && menuManager.getState() == BaseSubManager.READY) {
+ if (softButtonManager != null && textAndGraphicManager != null && voiceCommandManager != null && menuManager != null && choiceSetManager != null && subscribeButtonManager != null) {
+ if (softButtonManager.getState() == BaseSubManager.READY && textAndGraphicManager.getState() == BaseSubManager.READY && voiceCommandManager.getState() == BaseSubManager.READY && menuManager.getState() == BaseSubManager.READY
+ && subscribeButtonManager.getState() == BaseSubManager.READY) {
DebugTool.logInfo("Starting screen manager, all sub managers are in ready state");
transitionToState(READY);
- } else if (softButtonManager.getState() == BaseSubManager.ERROR && textAndGraphicManager.getState() == BaseSubManager.ERROR && voiceCommandManager.getState() == BaseSubManager.ERROR && menuManager.getState() == BaseSubManager.ERROR && choiceSetManager.getState() == BaseSubManager.ERROR) {
+ } else if (softButtonManager.getState() == BaseSubManager.ERROR && textAndGraphicManager.getState() == BaseSubManager.ERROR && voiceCommandManager.getState() == BaseSubManager.ERROR && menuManager.getState() == BaseSubManager.ERROR
+ && choiceSetManager.getState() == BaseSubManager.ERROR && subscribeButtonManager.getState() == BaseSubManager.ERROR) {
Log.e(TAG, "ERROR starting screen manager, all sub managers are in error state");
transitionToState(ERROR);
- } else if (textAndGraphicManager.getState() == BaseSubManager.SETTING_UP || softButtonManager.getState() == BaseSubManager.SETTING_UP || voiceCommandManager.getState() == BaseSubManager.SETTING_UP || menuManager.getState() == BaseSubManager.SETTING_UP || choiceSetManager.getState() == BaseSubManager.SETTING_UP) {
+ } else if (textAndGraphicManager.getState() == BaseSubManager.SETTING_UP || softButtonManager.getState() == BaseSubManager.SETTING_UP || voiceCommandManager.getState() == BaseSubManager.SETTING_UP || menuManager.getState() == BaseSubManager.SETTING_UP
+ || choiceSetManager.getState() == BaseSubManager.SETTING_UP || subscribeButtonManager.getState() == BaseSubManager.SETTING_UP) {
DebugTool.logInfo("SETTING UP screen manager, at least one sub manager is still setting up");
transitionToState(SETTING_UP);
} else {
@@ -115,6 +120,7 @@ abstract class BaseScreenManager extends BaseSubManager {
this.voiceCommandManager.start(subManagerListener);
this.menuManager.start(subManagerListener);
this.choiceSetManager.start(subManagerListener);
+ this.subscribeButtonManager.start(subManagerListener);
}
private void initialize(){
@@ -124,6 +130,7 @@ abstract class BaseScreenManager extends BaseSubManager {
this.menuManager = new MenuManager(internalInterface, fileManager.get());
this.choiceSetManager = new ChoiceSetManager(internalInterface, fileManager.get());
}
+ this.subscribeButtonManager = new SubscribeButtonManager(internalInterface);
this.voiceCommandManager = new VoiceCommandManager(internalInterface);
}
@@ -137,6 +144,7 @@ abstract class BaseScreenManager extends BaseSubManager {
voiceCommandManager.dispose();
menuManager.dispose();
choiceSetManager.dispose();
+ subscribeButtonManager.dispose();
super.dispose();
}
@@ -568,26 +576,21 @@ abstract class BaseScreenManager extends BaseSubManager {
*/
public void commit(final CompletionListener listener){
softButtonManager.setBatchUpdates(false);
- softButtonManager.update(new CompletionListener() {
- boolean updateSuccessful = true;
+ textAndGraphicManager.setBatchUpdates(false);
+ textAndGraphicManager.update(new CompletionListener() {
@Override
public void onComplete(boolean success) {
- if (!success){
- updateSuccessful = false;
+ if (listener != null) {
+ listener.onComplete(success);
}
- textAndGraphicManager.setBatchUpdates(false);
- textAndGraphicManager.update(new CompletionListener() {
- @Override
- public void onComplete(boolean success) {
- if (!success){
- updateSuccessful = false;
- }
- if (listener != null) {
- listener.onComplete(updateSuccessful);
- }
- }
- });
}
});
}
+
+ public void addButtonListener(@NonNull ButtonName buttonName, @NonNull OnButtonListener listener){
+ subscribeButtonManager.addButtonListener(buttonName,listener);
+ }
+ public void removeButtonListener(@NonNull ButtonName buttonName, @NonNull OnButtonListener listener){
+ subscribeButtonManager.removeButtonListener(buttonName, listener);
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java
index 6ba777ee4..cd61a31f0 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java
@@ -32,16 +32,14 @@
package com.smartdevicelink.managers.screen;
import android.support.annotation.NonNull;
-import android.util.Log;
+import com.livio.taskmaster.Queue;
+import com.livio.taskmaster.Task;
import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.file.FileManager;
-import com.smartdevicelink.managers.file.MultipleFileCompletionListener;
-import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCNotification;
-import com.smartdevicelink.proxy.RPCResponse;
import com.smartdevicelink.proxy.SystemCapabilityManager;
import com.smartdevicelink.proxy.interfaces.ISdl;
import com.smartdevicelink.proxy.interfaces.OnSystemCapabilityListener;
@@ -49,24 +47,19 @@ import com.smartdevicelink.proxy.rpc.DisplayCapability;
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.WindowCapability;
import com.smartdevicelink.proxy.rpc.enums.ButtonName;
import com.smartdevicelink.proxy.rpc.enums.HMILevel;
import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows;
-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 com.smartdevicelink.util.DebugTool;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
/**
@@ -76,16 +69,15 @@ import java.util.concurrent.CopyOnWriteArrayList;
*/
abstract class BaseSoftButtonManager extends BaseSubManager {
- private static final String TAG = "SoftButtonManager";
- private WeakReference<FileManager> fileManager;
- WindowCapability defaultMainWindowCapability;
+ private final WeakReference<FileManager> fileManager;
+ 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 onDisplayCapabilityListener;
private final OnRPCNotificationListener onHMIStatusListener, onButtonPressListener, onButtonEventListener;
+ private Queue transactionQueue;
+ private final List<Task> batchQueue;
+ private boolean batchUpdates;
private final SoftButtonObject.UpdateListener updateListener;
/**
@@ -93,84 +85,90 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
*/
private String currentMainField1;
-
/**
* Creates a new instance of the SoftButtonManager
* @param internalInterface an instance of the ISdl interface that can be used for common SDL operations (sendRpc, addRpcListener, etc)
* @param fileManager an instance of the FileManager so that button graphics can be sent
*/
- BaseSoftButtonManager(@NonNull final ISdl internalInterface, @NonNull FileManager fileManager) {
+ BaseSoftButtonManager(@NonNull final ISdl internalInterface, @NonNull final FileManager fileManager) {
super(internalInterface);
+
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.currentHMILevel = null;
+ this.transactionQueue = newTransactionQueue();
+ this.batchQueue = new ArrayList<>();
+
this.updateListener = new SoftButtonObject.UpdateListener() {
@Override
public void onUpdate() {
- update(null);
+ transitionSoftButton();
}
};
+ // Add OnHMIStatusListener to keep currentHMILevel updated
+ this.onHMIStatusListener = new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+ OnHMIStatus onHMIStatus = (OnHMIStatus) notification;
+ if (onHMIStatus.getWindowID() != null && onHMIStatus.getWindowID() != PredefinedWindows.DEFAULT_WINDOW.getValue()) {
+ return;
+ }
+ currentHMILevel = onHMIStatus.getHmiLevel();
+ updateTransactionQueueSuspended();
+ }
+ };
+ this.internalInterface.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, onHMIStatusListener);
// Add OnSoftButtonCapabilitiesListener to keep softButtonCapabilities updated
onDisplayCapabilityListener = new OnSystemCapabilityListener() {
@Override
public void onCapabilityRetrieved(Object capability) {
+ SoftButtonCapabilities oldSoftButtonCapabilities = softButtonCapabilities;
+
+ // Extract and update the capabilities
// instead of using the parameter it's more safe to use the convenience method
List<DisplayCapability> capabilities = SystemCapabilityManager.convertToList(capability, DisplayCapability.class);
if (capabilities == null || capabilities.size() == 0) {
- DebugTool.logError("SoftButton Manager - Capabilities sent here are null or empty");
- }else {
- DisplayCapability display = capabilities.get(0);
- for (WindowCapability windowCapability : display.getWindowCapabilities()) {
+ softButtonCapabilities = null;
+ } else {
+ DisplayCapability mainDisplay = capabilities.get(0);
+ for (WindowCapability windowCapability : mainDisplay.getWindowCapabilities()) {
int currentWindowID = windowCapability.getWindowID() != null ? windowCapability.getWindowID() : PredefinedWindows.DEFAULT_WINDOW.getValue();
if (currentWindowID == PredefinedWindows.DEFAULT_WINDOW.getValue()) {
- defaultMainWindowCapability = windowCapability;
+ softButtonCapabilities = windowCapability.getSoftButtonCapabilities().get(0);
+ break;
}
}
}
+
+ // Update the queue's suspend state
+ updateTransactionQueueSuspended();
+
+ // Auto-send an updated Show if we have new capabilities
+ if (softButtonObjects != null && !softButtonObjects.isEmpty() && softButtonCapabilities != null && !softButtonCapabilitiesEquals(oldSoftButtonCapabilities, softButtonCapabilities)) {
+ SoftButtonReplaceOperation operation = new SoftButtonReplaceOperation(internalInterface, fileManager, softButtonCapabilities, softButtonObjects, currentMainField1);
+ transactionQueue.add(operation, false);
+ }
}
@Override
public void onError(String info) {
DebugTool.logError("Display Capability cannot be retrieved");
- defaultMainWindowCapability = null;
- }
- };
- this.internalInterface.addOnSystemCapabilityListener(SystemCapabilityType.DISPLAYS, onDisplayCapabilityListener);
-
- // Add OnHMIStatusListener to keep currentHMILevel updated
- this.onHMIStatusListener = new OnRPCNotificationListener() {
- @Override
- public void onNotified(RPCNotification notification) {
- OnHMIStatus onHMIStatus = (OnHMIStatus)notification;
- if (onHMIStatus.getWindowID() != null && onHMIStatus.getWindowID() != PredefinedWindows.DEFAULT_WINDOW.getValue()) {
- return;
- }
- HMILevel oldHmiLevel = currentHMILevel;
- currentHMILevel = onHMIStatus.getHmiLevel();
-
+ softButtonCapabilities = null;
- // 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);
- }
- }
+ // Update the queue's suspend state
+ updateTransactionQueueSuspended();
}
};
- this.internalInterface.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, onHMIStatusListener);
-
+ this.internalInterface.addOnSystemCapabilityListener(SystemCapabilityType.DISPLAYS, onDisplayCapabilityListener);
// 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) {
+ if (onButtonPress != null && onButtonPress.getButtonName() == ButtonName.CUSTOM_BUTTON) {
Integer buttonId = onButtonPress.getCustomButtonID();
if (getSoftButtonObjects() != null) {
for (SoftButtonObject softButtonObject : getSoftButtonObjects()) {
@@ -185,13 +183,12 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
};
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) {
+ if (onButtonEvent != null && onButtonEvent.getButtonName() == ButtonName.CUSTOM_BUTTON) {
Integer buttonId = onButtonEvent.getCustomButtonID();
if (getSoftButtonObjects() != null) {
for (SoftButtonObject softButtonObject : getSoftButtonObjects()) {
@@ -214,31 +211,46 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
}
/**
- * Get the SoftButtonObject that has the provided name
- * @param name a String value that represents the name
- * @return a SoftButtonObject
+ * Clean up everything after the manager is no longer needed
*/
- protected SoftButtonObject getSoftButtonObjectByName(String name) {
- for (SoftButtonObject softButtonObject : softButtonObjects) {
- if (softButtonObject.getName().equals(name)) {
- return softButtonObject;
- }
+ @Override
+ public void dispose() {
+ super.dispose();
+
+ softButtonObjects = new CopyOnWriteArrayList<>();
+ currentMainField1 = null;
+ currentHMILevel = null;
+ softButtonCapabilities = null;
+
+ // Cancel the operations
+ if (transactionQueue != null) {
+ transactionQueue.close();
+ transactionQueue = null;
}
- return null;
+
+ // 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.DISPLAYS, onDisplayCapabilityListener);
}
- /**
- * 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;
- }
+ private Queue newTransactionQueue() {
+ Queue queue = internalInterface.getTaskmaster().createQueue("SoftButtonManager", 2, false);
+ queue.pause();
+ return queue;
+ }
+
+ // Suspend the queue if the soft button capabilities are null (we assume that soft buttons are not supported)
+ // OR if the HMI level is NONE since we want to delay sending RPCs until we're in non-NONE
+ private void updateTransactionQueueSuspended() {
+ if (softButtonCapabilities == null || HMILevel.HMI_NONE.equals(currentHMILevel)) {
+ DebugTool.logInfo(String.format("Suspending the transaction queue. Current HMI level is NONE: %b, soft button capabilities are null: %b", HMILevel.HMI_NONE.equals(currentHMILevel), softButtonCapabilities == null));
+ transactionQueue.pause();
+ } else {
+ DebugTool.logInfo("Starting the transaction queue");
+ transactionQueue.resume();
}
- return null;
}
/**
@@ -256,118 +268,64 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
protected void setSoftButtonObjects(@NonNull List<SoftButtonObject> list) {
// Convert the List to CopyOnWriteArrayList
CopyOnWriteArrayList<SoftButtonObject> softButtonObjects;
- if(list instanceof CopyOnWriteArrayList){
+ if (list instanceof CopyOnWriteArrayList) {
softButtonObjects = (CopyOnWriteArrayList<SoftButtonObject>) list;
- }else{
+ } else {
softButtonObjects = new CopyOnWriteArrayList<>(list);
}
+ // Only update if something changed. This prevents, for example, an empty array being reset
+ if (softButtonObjects.equals(this.softButtonObjects)) {
+ DebugTool.logInfo("New soft button objects are equivalent to existing soft button objects, skipping...");
+ return;
+ }
// Check if two soft button objects have the same name
if (hasTwoSoftButtonObjectsOfSameName(softButtonObjects)) {
this.softButtonObjects = new CopyOnWriteArrayList<>();
- Log.e(TAG, "Attempted to set soft button objects, but two buttons had the same name");
+ DebugTool.logError("Attempted to set soft button objects, but two buttons had the same name");
return;
}
+ if (!checkAndAssignButtonIds(softButtonObjects)) {
+ DebugTool.logError("Attempted to set soft button objects, but multiple buttons had the same id");
+ return;
+ }
// Set updateListeners for soft button objects
for (SoftButtonObject softButtonObject : softButtonObjects) {
softButtonObject.setUpdateListener(updateListener);
}
-
- if (!checkAndAssignButtonIds(softButtonObjects)) {
- Log.e(TAG, "Attempted to set soft button objects, but multiple buttons had the same id");
- return;
- }
-
-
this.softButtonObjects = softButtonObjects;
+ // We only need to pass the first softButtonCapabilities in the array due to the fact that all soft button capabilities are the same (i.e. there is no way to assign a softButtonCapabilities to a specific soft button).
+ SoftButtonReplaceOperation operation = new SoftButtonReplaceOperation(internalInterface, fileManager.get(), softButtonCapabilities, softButtonObjects, currentMainField1);
- 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;
+ if (batchUpdates) {
+ batchQueue.clear();
+ batchQueue.add(operation);
+ } else {
+ transactionQueue.clear();
+ transactionQueue.add(operation, false);
}
+ }
-
- // 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 && sdlArtworkNeedsUpload(softButtonState.getArtwork())) {
- if (softButtonState.getName().equals(initialState.getName())) {
- initialStatesToBeUploaded.add(softButtonObject.getCurrentState().getArtwork());
- } else{
- otherStatesToBeUploaded.add(softButtonState.getArtwork());
- }
- }
- }
+ /**
+ * Check if two SoftButtonObject have the same name
+ * @param softButtonObjects a list of SoftButton objects that will be iterated through
+ * @return true if two buttons exist that are the same in the list, false if not
+ */
+ 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;
}
}
}
-
-
- // Upload initial state images
- if (initialStatesToBeUploaded.size() > 0 && fileManager.get() != null) {
- DebugTool.logInfo( "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");
- }
- DebugTool.logInfo( "Soft button initial artworks uploaded");
- update(cachedListener);
- }
- });
- }
-
-
- // Upload other state images
- if (otherStatesToBeUploaded.size() > 0 && fileManager.get() != null) {
- DebugTool.logInfo("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");
- }
- DebugTool.logInfo("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);
+ return false;
}
/**
@@ -383,13 +341,13 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
currentSoftButtonId = softButtonObject.getButtonId();
if (currentSoftButtonId != SoftButtonObject.SOFT_BUTTON_ID_NOT_SET_VALUE) {
numberOfButtonIdsSetByDev++;
- if (currentSoftButtonId > maxButtonIdsSetByDev){
+ if (currentSoftButtonId > maxButtonIdsSetByDev) {
maxButtonIdsSetByDev = currentSoftButtonId;
}
buttonIdsSetByDevHashSet.add(softButtonObject.getButtonId());
}
}
- if (numberOfButtonIdsSetByDev != buttonIdsSetByDevHashSet.size()){
+ if (numberOfButtonIdsSetByDev != buttonIdsSetByDevHashSet.size()) {
return false;
}
@@ -399,9 +357,9 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
for (SoftButtonObject softButtonObject : softButtonObjects) {
// If the dev did not set the buttonId, the manager should set an id on the dev's behalf
currentSoftButtonId = softButtonObject.getButtonId();
- if (currentSoftButtonId == SoftButtonObject.SOFT_BUTTON_ID_NOT_SET_VALUE){
+ if (currentSoftButtonId == SoftButtonObject.SOFT_BUTTON_ID_NOT_SET_VALUE) {
do {
- if (generatedSoftButtonId >= SoftButtonObject.SOFT_BUTTON_ID_MAX_VALUE){
+ if (generatedSoftButtonId >= SoftButtonObject.SOFT_BUTTON_ID_MAX_VALUE) {
generatedSoftButtonId = SoftButtonObject.SOFT_BUTTON_ID_MIN_VALUE;
}
generatedSoftButtonId++;
@@ -412,133 +370,62 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
return true;
}
- /**
- * Update the SoftButtonManger by sending a new Show RPC to reflect the changes
- * @param listener a CompletionListener
- */
- protected void update(CompletionListener listener) {
- cachedListener = listener;
+ private void transitionSoftButton() {
+ SoftButtonTransitionOperation operation = new SoftButtonTransitionOperation(internalInterface, softButtonObjects, currentMainField1);
if (batchUpdates) {
- return;
- }
-
- // Don't send if we're in HMI NONE
- if (currentHMILevel == null || currentHMILevel == HMILevel.HMI_NONE) {
- return;
- }
-
- DebugTool.logInfo("Updating soft buttons");
-
- cachedListener = null;
-
-
- // Check if we have update already in progress
- if (inProgressShowRPC != null) {
- DebugTool.logInfo("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) {
- DebugTool.logInfo("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) {
- DebugTool.logInfo("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) {
- DebugTool.logInfo( "Soft button images unavailable, sending text buttons");
- inProgressShowRPC.setSoftButtons(textOnlySoftButtons);
-
- } else {
- DebugTool.logInfo( "Soft button images unavailable, text buttons unavailable");
- inProgressShowRPC = null;
- return;
+ for (Task task : batchQueue) {
+ if (task instanceof SoftButtonTransitionOperation) {
+ batchQueue.remove(task);
+ }
}
-
+ batchQueue.add(operation);
} else {
- DebugTool.logInfo( "Sending soft buttons with images");
- inProgressShowRPC.setSoftButtons(createSoftButtonsForCurrentState());
+ transactionQueue.add(operation, false);
}
+ }
-
- inProgressShowRPC.setOnRPCResponseListener(new OnRPCResponseListener() {
- @Override
- public void onResponse(int correlationId, RPCResponse response) {
- DebugTool.logInfo("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. resultCode: " + resultCode + ". info: " + info);
- handleResponse(false);
- }
-
- private void handleResponse(boolean success){
-
- inProgressShowRPC = null;
- CompletionListener currentListener;
- if (inProgressListener != null) {
- currentListener = inProgressListener;
- inProgressListener = null;
- currentListener.onComplete(success);
- }
-
-
- if (hasQueuedUpdate) {
- DebugTool.logInfo("Queued update exists, sending another update");
- currentListener = queuedUpdateListener;
- queuedUpdateListener = null;
- hasQueuedUpdate = false;
- update(currentListener);
- }
+ /**
+ * 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;
}
- });
-
-
- internalInterface.sendRPC(inProgressShowRPC);
+ }
+ return null;
}
- private boolean softButtonImagesSupported() {
- return defaultMainWindowCapability == null
- || defaultMainWindowCapability.getSoftButtonCapabilities() == null
- || (!defaultMainWindowCapability.getSoftButtonCapabilities().isEmpty() && defaultMainWindowCapability.getSoftButtonCapabilities().get(0).getImageSupported());
+ /**
+ * 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;
}
/**
- * Check if two SoftButtonObject have the same name
- * @param softButtonObjects a list of SoftButton objects that will be iterated through
- * @return true if two buttons exist that are the same in the list, false if not
+ * Set the batchUpdates flag that represents whether the manager should wait until commit() is called to send the updated show RPC
+ * @param batchUpdates Set true if the manager should batch updates together, or false if it should send them as soon as they happen
*/
- 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;
- }
+ protected void setBatchUpdates(boolean batchUpdates) {
+ this.batchUpdates = batchUpdates;
+
+ if (!this.batchUpdates) {
+ for (Task task : batchQueue) {
+ transactionQueue.add(task, false);
}
+ batchQueue.clear();
}
- return false;
}
/**
@@ -546,7 +433,7 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
* @return the string that is currently used for MainField1
*/
protected String getCurrentMainField1() {
- if (currentMainField1 == null){
+ if (currentMainField1 == null) {
return "";
}
return currentMainField1;
@@ -558,95 +445,28 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
*/
protected void setCurrentMainField1(String currentMainField1) {
this.currentMainField1 = currentMainField1;
- }
-
- /**
- * Sets the batchUpdates flag that represents whether the manager should wait until commit() is called to send the updated show RPC
- * @param batchUpdates Set true if the manager should batch updates together, or false if it should send them as soon
- * as they happen
- */
- protected void setBatchUpdates(boolean batchUpdates) {
- this.batchUpdates = batchUpdates;
- }
-
- /**
- * Clean up everything after the manager is no longer needed
- */
- @Override
- public void dispose() {
- super.dispose();
-
- // 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.DISPLAYS, onDisplayCapabilityListener);
- }
- /**
- * 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;
+ for (Task task : transactionQueue.getTasksAsList()) {
+ if (task instanceof SoftButtonReplaceOperation) {
+ SoftButtonReplaceOperation operation = (SoftButtonReplaceOperation) task;
+ operation.setCurrentMainField1(currentMainField1);
+ } else if (task instanceof SoftButtonTransitionOperation) {
+ SoftButtonTransitionOperation operation = (SoftButtonTransitionOperation) task;
+ operation.setCurrentMainField1(currentMainField1);
}
}
- 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 && sdlArtworkNeedsUpload(currentState.getArtwork())) {
- return false;
- }
- }
+ private boolean softButtonCapabilitiesEquals(SoftButtonCapabilities capabilities1, SoftButtonCapabilities capabilities2) {
+ if (capabilities1 == capabilities2) {
+ return true;
+ } else if (capabilities1 == null || capabilities2 == null) {
+ return false;
+ } else if (capabilities1.getImageSupported() != capabilities2.getImageSupported()) {
+ return false;
+ } else if (capabilities1.getTextSupported() != capabilities2.getTextSupported()) {
+ return false;
}
return true;
}
-
- private boolean sdlArtworkNeedsUpload(SdlArtwork artwork){
- if (fileManager.get() != null) {
- return artwork != null && !fileManager.get().hasUploadedFile(artwork) && !artwork.isStaticIcon();
- }
- return false;
- }
-
- /**
- * 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/base/src/main/java/com/smartdevicelink/managers/screen/BaseSubscribeButtonManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseSubscribeButtonManager.java
new file mode 100644
index 000000000..70996db4c
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseSubscribeButtonManager.java
@@ -0,0 +1,193 @@
+package com.smartdevicelink.managers.screen;
+
+import android.support.annotation.NonNull;
+import android.util.Log;
+
+import com.smartdevicelink.managers.BaseSubManager;
+import com.smartdevicelink.managers.CompletionListener;
+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.rpc.OnButtonEvent;
+import com.smartdevicelink.proxy.rpc.OnButtonPress;
+import com.smartdevicelink.proxy.rpc.SubscribeButton;
+import com.smartdevicelink.proxy.rpc.UnsubscribeButton;
+import com.smartdevicelink.proxy.rpc.enums.ButtonName;
+import com.smartdevicelink.proxy.rpc.enums.Result;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
+
+import java.util.HashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * <strong>SubscribeButtonManager</strong> <br>
+ * <p>
+ * Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br>
+ */
+abstract class BaseSubscribeButtonManager extends BaseSubManager {
+
+ private static final String TAG = "SubscribeButtonManager";
+ HashMap<ButtonName, CopyOnWriteArrayList<OnButtonListener>> onButtonListeners;
+ private OnRPCNotificationListener onButtonPressListener;
+ private OnRPCNotificationListener onButtonEventListener;
+
+ BaseSubscribeButtonManager(@NonNull ISdl internalInterface) {
+ super(internalInterface);
+ setRpcNotificationListeners();
+ onButtonListeners = new HashMap<>();
+ }
+
+ @Override
+ public void start(CompletionListener listener) {
+ transitionToState(READY);
+ super.start(listener);
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ if (onButtonListeners != null) {
+ onButtonListeners.clear();
+ }
+ internalInterface.removeOnRPCNotificationListener(FunctionID.ON_BUTTON_PRESS, onButtonPressListener);
+ internalInterface.removeOnRPCNotificationListener(FunctionID.ON_BUTTON_EVENT, onButtonEventListener);
+ }
+
+ /***
+ * Checks to see if Button is already subscribed and adds listener to hashmap.
+ * If button is not already subscribed to, it call method:
+ * subscribeButtonRequest to send RPC request
+ * @param buttonName - Is the button that the developer wants to subscribe to
+ * @param listener - Is the listener that was sent by developer
+ */
+ void addButtonListener(ButtonName buttonName, OnButtonListener listener) {
+ if (listener == null) {
+ Log.e(TAG, "OnButtonListener cannot be null");
+ return;
+ }
+ if (buttonName == null) {
+ listener.onError("ButtonName cannot be null");
+ return;
+ }
+
+ if (onButtonListeners.get(buttonName) == null) {
+ subscribeButtonRequest(buttonName, listener);
+ return;
+ }
+
+ if (onButtonListeners.get(buttonName).contains(listener)) {
+ Log.w(TAG, "Already subscribed to button named: " + buttonName);
+ return;
+ }
+ onButtonListeners.get(buttonName).add(listener);
+ }
+
+ /**
+ * Unsubscribe form button and/or listener sent by developer
+ *
+ * @param buttonName Is the button that the developer wants to unsubscribe from
+ * @param listener - the listener that was sent by developer
+ */
+ void removeButtonListener(final ButtonName buttonName, final OnButtonListener listener) {
+ if (listener == null) {
+ Log.e(TAG, "OnButtonListener cannot be null: ");
+ return;
+ }
+
+ if (buttonName == null) {
+ listener.onError("ButtonName cannot be null");
+ return;
+ }
+
+ if (onButtonListeners.get(buttonName) == null || !onButtonListeners.get(buttonName).contains(listener)) {
+ listener.onError("Attempting to unsubscribe to the " + buttonName + " button failed because it is not currently subscribed");
+ return;
+ }
+
+ if (onButtonListeners.get(buttonName).size() > 1) {
+ onButtonListeners.get(buttonName).remove(listener);
+ return;
+ }
+ unsubscribeButtonRequest(buttonName, listener);
+ }
+
+ /**
+ * Send the UnsubscribeButton RPC
+ *
+ * @param buttonName - ButtonName - name of button
+ * @param listener - OnButtonListener - listener to get notified
+ */
+ private void unsubscribeButtonRequest(final ButtonName buttonName, final OnButtonListener listener) {
+ UnsubscribeButton unsubscribeButtonRequest = new UnsubscribeButton(buttonName);
+ unsubscribeButtonRequest.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ onButtonListeners.remove(buttonName);
+ }
+
+ @Override
+ public void onError(int correlationId, Result resultCode, String info) {
+ listener.onError("Attempt to unsubscribe to button named " + buttonName + " Failed. ResultCode: " + resultCode + " info: " + info);
+ }
+ });
+ internalInterface.sendRPC(unsubscribeButtonRequest);
+ }
+
+ /**
+ * Send the SubscribeButton RPC
+ *
+ * @param buttonName - ButtonName - name of button
+ * @param listener - OnButtonListener - listener to get notified
+ */
+ private void subscribeButtonRequest(final ButtonName buttonName, final OnButtonListener listener) {
+ SubscribeButton subscribeButtonRequest = new SubscribeButton(buttonName);
+ subscribeButtonRequest.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ onButtonListeners.put(buttonName, new CopyOnWriteArrayList<OnButtonListener>());
+ onButtonListeners.get(buttonName).add(listener);
+ }
+
+ @Override
+ public void onError(int correlationId, Result resultCode, String info) {
+ listener.onError("Attempt to subscribe to button named " + buttonName + " Failed . ResultCode: " + resultCode + " info: " + info);
+ }
+ });
+ internalInterface.sendRPC(subscribeButtonRequest);
+ }
+
+ /**
+ * Sets up RpcNotificationListeners for button presses and events, is setup when manager is created
+ */
+ private void setRpcNotificationListeners() {
+ onButtonPressListener = new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+ OnButtonPress onButtonPressNotification = (OnButtonPress) notification;
+ CopyOnWriteArrayList<OnButtonListener> listeners = onButtonListeners.get(onButtonPressNotification.getButtonName());
+ if (listeners != null && listeners.size() > 0) {
+ for (OnButtonListener listener : listeners) {
+ listener.onPress(onButtonPressNotification.getButtonName(), onButtonPressNotification);
+ }
+ }
+ }
+ };
+ internalInterface.addOnRPCNotificationListener(FunctionID.ON_BUTTON_PRESS, onButtonPressListener);
+
+ onButtonEventListener = new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+ OnButtonEvent onButtonEvent = (OnButtonEvent) notification;
+ CopyOnWriteArrayList<OnButtonListener> listeners = onButtonListeners.get(onButtonEvent.getButtonName());
+ if (listeners != null && listeners.size() > 0) {
+ for (OnButtonListener listener : listeners) {
+ listener.onEvent(onButtonEvent.getButtonName(), onButtonEvent);
+ }
+ }
+ }
+ };
+ internalInterface.addOnRPCNotificationListener(FunctionID.ON_BUTTON_EVENT, onButtonEventListener);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/OnButtonListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/OnButtonListener.java
new file mode 100644
index 000000000..ba2910307
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/OnButtonListener.java
@@ -0,0 +1,32 @@
+package com.smartdevicelink.managers.screen;
+
+import com.smartdevicelink.proxy.rpc.OnButtonEvent;
+import com.smartdevicelink.proxy.rpc.OnButtonPress;
+import com.smartdevicelink.proxy.rpc.enums.ButtonName;
+
+/**
+ * OnButtonListener is a listener used for notifying when events have happened with SubscribeButtons
+ */
+public interface OnButtonListener {
+
+ /**
+ * Returns when a Subscribed button is pressed
+ * @param buttonName - Name of Button
+ * @param buttonPress - OnButtonPress
+ */
+ void onPress(ButtonName buttonName, OnButtonPress buttonPress);
+
+ /**
+ * Returns when a Subscribed button Event has occurred
+ * @param buttonName - Name of Button
+ * @param buttonEvent - OnButtonEvent
+ */
+ void onEvent(ButtonName buttonName, OnButtonEvent buttonEvent);
+
+ /**
+ * Returns when there is an error with subscribing to button
+ * @param info - Error info
+ */
+ void onError(String info);
+}
+
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java
index 76eb98b1e..8a040b967 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java
@@ -32,8 +32,8 @@
package com.smartdevicelink.managers.screen;
import android.support.annotation.NonNull;
-import android.util.Log;
+import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.proxy.rpc.OnButtonEvent;
import com.smartdevicelink.proxy.rpc.OnButtonPress;
import com.smartdevicelink.proxy.rpc.SoftButton;
@@ -44,14 +44,13 @@ import java.util.List;
/**
* <strong>SoftButtonObject</strong> <br>
- * SoftButtonObject define a button that can have multiple SoftButtonState values.<br>
+ * SoftButtonObject defines 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";
static int SOFT_BUTTON_ID_NOT_SET_VALUE = -1;
static int SOFT_BUTTON_ID_MIN_VALUE = 0;
static int SOFT_BUTTON_ID_MAX_VALUE = 65535;
@@ -74,13 +73,13 @@ public class SoftButtonObject {
// 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");
+ DebugTool.logError("Two states have the same name in states list for soft button object");
return;
}
this.name = name;
this.states = states;
- currentStateName = initialStateName;
+ this.currentStateName = initialStateName;
this.buttonId = SOFT_BUTTON_ID_NOT_SET_VALUE;
this.onEventListener = onEventListener;
}
@@ -96,6 +95,16 @@ public class SoftButtonObject {
}
/**
+ * Create a new instance of the SoftButtonObject with one state
+ * @param name a String value represents name of the object
+ * @param artwork a SdlArtwork to be displayed on the button
+ * @param onEventListener a listener that has a callback that will be triggered when a button event happens
+ */
+ public SoftButtonObject(@NonNull String name, @NonNull String text, SdlArtwork artwork, OnEventListener onEventListener) {
+ this(name, Collections.singletonList(new SoftButtonState(name, text, artwork)), name, 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
@@ -103,9 +112,15 @@ public class SoftButtonObject {
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));
+ DebugTool.logError(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;
+ }
+
+ if (states.size() == 1) {
+ DebugTool.logWarning("There's only one state, so no transitioning is possible!");
return false;
}
+
DebugTool.logInfo(String.format("Transitioning soft button object %s to state %s", this.name, newStateName));
currentStateName = newStateName;
@@ -113,7 +128,7 @@ public class SoftButtonObject {
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));
+ DebugTool.logError(String.format("SoftButtonManager is not set for soft button object: %s. Update cannot be triggered", this.name));
}
return true;
@@ -126,16 +141,13 @@ public class SoftButtonObject {
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();
- }
+ int nextStateNumber = (i == states.size() - 1) ? 0 : (i + 1);
+ nextStateName = states.get(nextStateNumber).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));
+ DebugTool.logError(String.format("Current state name : %s cannot be found for soft button object %s", currentStateName, this.name));
return;
}
transitionToStateByName(nextStateName);
@@ -148,7 +160,7 @@ public class SoftButtonObject {
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));
+ DebugTool.logError(String.format("Current state name : %s cannot be found for soft button object %s", currentStateName, this.name));
}
return state;
}
@@ -273,7 +285,7 @@ public class SoftButtonObject {
*/
public void setButtonId(int buttonId) {
if (buttonId < SOFT_BUTTON_ID_MIN_VALUE){
- Log.e(TAG, "buttonId has to be equal or more than " + SOFT_BUTTON_ID_MIN_VALUE);
+ DebugTool.logError("buttonId has to be equal or more than " + SOFT_BUTTON_ID_MIN_VALUE);
return;
}
this.buttonId = buttonId;
@@ -303,7 +315,7 @@ public class SoftButtonObject {
/**
* A listener interface that is used by SoftButtonObject to request an update from SoftButtonManager
*/
- interface UpdateListener{
+ interface UpdateListener {
/**
* Requests an update from SoftButtonManager
*/
@@ -319,9 +331,8 @@ public class SoftButtonObject {
int result = 1;
result += ((getName() == null) ? 0 : Integer.rotateLeft(getName().hashCode(), 1));
result += ((getCurrentStateName() == null) ? 0 : Integer.rotateLeft(getCurrentStateName().hashCode(), 2));
- result += Integer.rotateLeft(Integer.valueOf(getButtonId()).hashCode(), 3);
for (int i = 0; i < this.states.size(); i++) {
- result += ((getStates().get(i) == null) ? 0 : Integer.rotateLeft(getStates().get(i).hashCode(), i + 4));
+ result += ((getStates().get(i) == null) ? 0 : Integer.rotateLeft(getStates().get(i).hashCode(), i + 3));
}
return result;
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java
new file mode 100644
index 000000000..0aecbc616
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonReplaceOperation.java
@@ -0,0 +1,338 @@
+package com.smartdevicelink.managers.screen;
+
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.managers.CompletionListener;
+import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.file.MultipleFileCompletionListener;
+import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.interfaces.ISdl;
+import com.smartdevicelink.proxy.rpc.Show;
+import com.smartdevicelink.proxy.rpc.SoftButton;
+import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities;
+import com.smartdevicelink.proxy.rpc.enums.Result;
+import com.smartdevicelink.proxy.rpc.enums.SoftButtonType;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
+import com.smartdevicelink.util.DebugTool;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Created by Bilal Alsharifi on 6/15/20.
+ */
+class SoftButtonReplaceOperation extends Task {
+
+ private final WeakReference<ISdl> internalInterface;
+ private final WeakReference<FileManager> fileManager;
+ private final SoftButtonCapabilities softButtonCapabilities;
+ private final CopyOnWriteArrayList<SoftButtonObject> softButtonObjects;
+ private String currentMainField1;
+
+ SoftButtonReplaceOperation(ISdl internalInterface, FileManager fileManager, SoftButtonCapabilities softButtonCapabilities, CopyOnWriteArrayList<SoftButtonObject> softButtonObjects, String currentMainField1) {
+ super("SoftButtonReplaceOperation");
+ this.internalInterface = new WeakReference<>(internalInterface);
+ this.fileManager = new WeakReference<>(fileManager);
+ this.softButtonCapabilities = softButtonCapabilities;
+ this.softButtonObjects = softButtonObjects;
+ this.currentMainField1 = currentMainField1;
+ }
+
+ @Override
+ public void onExecute() {
+ start();
+ }
+
+ private void start() {
+ if (getState() == Task.CANCELED) {
+ return;
+ }
+
+ // Check the state of our images
+ if (!supportsSoftButtonImages()) {
+ // We don't support images at all
+ DebugTool.logWarning("Soft button images are not supported. Attempting to send text-only soft buttons. If any button does not contain text, no buttons will be sent.");
+
+ // Send text buttons if all the soft buttons have text
+ sendCurrentStateTextOnlySoftButtons(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ if (!success) {
+ DebugTool.logError("Head unit does not support images and some of the soft buttons do not have text, so none of the buttons will be sent.");
+ }
+ onFinished();
+ }
+ });
+ } else if (currentStateHasImages() && !allCurrentStateImagesAreUploaded()) {
+ // If there are images that aren't uploaded
+ // Send text buttons if all the soft buttons have text
+ sendCurrentStateTextOnlySoftButtons(null);
+
+ // Upload initial images
+ uploadInitialStateImages(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ // Send initial soft buttons w/ images
+ sendCurrentStateSoftButtons(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ // Upload other images
+ uploadOtherStateImages(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ DebugTool.logInfo("Finished sending other images for soft buttons");
+ onFinished();
+ }
+ });
+ }
+ });
+ }
+ });
+ } else {
+ // All the images are already uploaded. Send initial soft buttons w/ images.
+ sendCurrentStateSoftButtons(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ DebugTool.logInfo("Finished sending soft buttons with images");
+ // Upload other images
+ uploadOtherStateImages(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ if (success) {
+ DebugTool.logInfo("Finished sending other images for soft buttons");
+ }
+ onFinished();
+ }
+ });
+ }
+ });
+ }
+ }
+
+ private void uploadInitialStateImages(final CompletionListener completionListener) {
+ // Upload all soft button images, the initial state images first, then the other states. We need to send updates when the initial state is ready.
+ List<SdlArtwork> initialStatesToBeUploaded = new ArrayList<>();
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ SoftButtonState softButtonState = softButtonObject.getCurrentState();
+ if (softButtonState != null && artworkNeedsUpload(softButtonState.getArtwork())) {
+ initialStatesToBeUploaded.add(softButtonState.getArtwork());
+ }
+ }
+
+ if (initialStatesToBeUploaded.isEmpty()) {
+ DebugTool.logInfo("No initial state artworks to upload");
+ if (completionListener != null) {
+ completionListener.onComplete(false);
+ }
+ return;
+ }
+
+ DebugTool.logInfo("Uploading soft button initial artworks");
+ if (fileManager.get() != null) {
+ fileManager.get().uploadArtworks(initialStatesToBeUploaded, new MultipleFileCompletionListener() {
+ @Override
+ public void onComplete(Map<String, String> errors) {
+ if (errors != null) {
+ DebugTool.logError("Error uploading soft button artworks: " + errors.keySet());
+ } else {
+ DebugTool.logInfo("Soft button initial state artworks uploaded");
+ }
+
+ if (getState() == Task.CANCELED) {
+ onFinished();
+ if (completionListener != null) {
+ completionListener.onComplete(false);
+ }
+ return;
+ }
+ if (completionListener != null) {
+ completionListener.onComplete(true);
+ }
+ }
+ });
+ }
+ }
+
+ private void uploadOtherStateImages(final CompletionListener completionListener) {
+ // Upload all soft button images, the initial state images first, then the other states. We need to send updates when the initial state is ready.
+ List<SdlArtwork> otherStatesToBeUploaded = new ArrayList<>();
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ for (SoftButtonState softButtonState : softButtonObject.getStates()) {
+ if (softButtonState.getName().equals(softButtonObject.getCurrentState().getName())) {
+ continue;
+ }
+ if (artworkNeedsUpload(softButtonState.getArtwork())) {
+ otherStatesToBeUploaded.add(softButtonState.getArtwork());
+ }
+ }
+ }
+
+ if (otherStatesToBeUploaded.isEmpty()) {
+ DebugTool.logInfo("No other state artworks to upload");
+ if (completionListener != null) {
+ completionListener.onComplete(false);
+ }
+ return;
+ }
+
+ DebugTool.logInfo("Uploading soft button other state artworks");
+ if (fileManager.get() != null) {
+ fileManager.get().uploadArtworks(otherStatesToBeUploaded, new MultipleFileCompletionListener() {
+ @Override
+ public void onComplete(Map<String, String> errors) {
+ if (errors != null) {
+ DebugTool.logError("Error uploading soft button artworks: " + errors.keySet());
+ } else {
+ DebugTool.logInfo("Soft button other state artworks uploaded");
+ }
+
+ if (getState() == Task.CANCELED) {
+ onFinished();
+ if (completionListener != null) {
+ completionListener.onComplete(false);
+ }
+ return;
+ }
+
+ if (completionListener != null) {
+ completionListener.onComplete(true);
+ }
+ }
+ });
+ }
+ }
+
+ private void sendCurrentStateSoftButtons(final CompletionListener completionListener) {
+ if (getState() == Task.CANCELED) {
+ onFinished();
+ }
+
+ DebugTool.logInfo("Preparing to send full soft buttons");
+ List<SoftButton> softButtons = new ArrayList<>();
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ softButtons.add(softButtonObject.getCurrentStateSoftButton());
+ }
+
+ Show show = new Show();
+ show.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (response.getSuccess()) {
+ DebugTool.logInfo("Finished sending text only soft buttons");
+ } else {
+ DebugTool.logWarning("Failed to update soft buttons with text buttons");
+ }
+ if (completionListener != null) {
+ completionListener.onComplete(response.getSuccess());
+ }
+ }
+
+ @Override
+ public void onError(int correlationId, Result resultCode, String info) {
+ DebugTool.logWarning("Failed to update soft buttons with text buttons");
+ if (completionListener != null) {
+ completionListener.onComplete(false);
+ }
+ }
+ });
+ show.setMainField1(currentMainField1);
+ show.setSoftButtons(softButtons);
+ if (internalInterface.get() != null) {
+ internalInterface.get().sendRPC(show);
+ }
+ }
+
+
+ // Returns text soft buttons representing the current states of the button objects, or returns if _any_ of the buttons' current states are image only buttons.
+ private void sendCurrentStateTextOnlySoftButtons(final CompletionListener completionListener) {
+ if (getState() == Task.CANCELED) {
+ onFinished();
+ }
+
+ DebugTool.logInfo("Preparing to send text-only soft buttons");
+ List<SoftButton> textButtons = new ArrayList<>();
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ SoftButton softButton = softButtonObject.getCurrentStateSoftButton();
+ if (softButton.getText() == null) {
+ DebugTool.logWarning("Attempted to create text buttons, but some buttons don't support text, so no text-only soft buttons will be sent");
+ if (completionListener != null) {
+ completionListener.onComplete(false);
+ }
+ return;
+ }
+
+ // 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());
+ textOnlySoftButton.setSystemAction(softButton.getSystemAction());
+ textOnlySoftButton.setIsHighlighted(softButton.getIsHighlighted());
+ textButtons.add(textOnlySoftButton);
+ }
+
+ Show show = new Show();
+ show.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (response.getSuccess()) {
+ DebugTool.logInfo("Finished sending text only soft buttons");
+ } else {
+ DebugTool.logWarning("Failed to update soft buttons with text buttons");
+ }
+ if (completionListener != null) {
+ completionListener.onComplete(response.getSuccess());
+ }
+ }
+
+ @Override
+ public void onError(int correlationId, Result resultCode, String info) {
+ DebugTool.logWarning("Failed to update soft buttons with text buttons");
+ if (completionListener != null) {
+ completionListener.onComplete(false);
+ }
+ }
+ });
+ show.setMainField1(currentMainField1);
+ show.setSoftButtons(textButtons);
+ if (internalInterface.get() != null) {
+ internalInterface.get().sendRPC(show);
+ }
+ }
+
+ private boolean artworkNeedsUpload(SdlArtwork artwork) {
+ return (artwork != null
+ && fileManager.get() != null
+ && !fileManager.get().hasUploadedFile(artwork)
+ && softButtonCapabilities.getImageSupported()
+ && !artwork.isStaticIcon());
+ }
+
+ private boolean currentStateHasImages() {
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ if (softButtonObject.getCurrentState().getArtwork() != null) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean allCurrentStateImagesAreUploaded() {
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ SdlArtwork artwork = softButtonObject.getCurrentState().getArtwork();
+ if (artworkNeedsUpload(artwork)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean supportsSoftButtonImages() {
+ return softButtonCapabilities.getImageSupported();
+ }
+
+ void setCurrentMainField1(String currentMainField1) {
+ this.currentMainField1 = currentMainField1;
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonState.java b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonState.java
index bd6f104d1..33ead8136 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonState.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonState.java
@@ -32,12 +32,12 @@
package com.smartdevicelink.managers.screen;
import android.support.annotation.NonNull;
-import android.util.Log;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.proxy.rpc.SoftButton;
import com.smartdevicelink.proxy.rpc.enums.SoftButtonType;
import com.smartdevicelink.proxy.rpc.enums.SystemAction;
+import com.smartdevicelink.util.DebugTool;
/**
* <strong>SoftButtonState</strong> <br>
@@ -48,7 +48,6 @@ import com.smartdevicelink.proxy.rpc.enums.SystemAction;
*/
public class SoftButtonState {
- private static final String TAG = "SoftButtonState";
private String name;
private SdlArtwork artwork;
private final SoftButton softButton;
@@ -62,7 +61,7 @@ public class SoftButtonState {
*/
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");
+ DebugTool.logError("Attempted to create an invalid soft button state: text and artwork are both null");
softButton = null;
return;
}
@@ -84,13 +83,15 @@ public class SoftButtonState {
// Set the SoftButton's image
if (artwork != null) {
- softButton.setImage(artwork.getImageRPC());
+ this.softButton.setImage(artwork.getImageRPC());
}
// Set the SoftButton's text
if (text != null) {
- softButton.setText(text);
+ this.softButton.setText(text);
}
+
+ this.softButton.setSystemAction(SystemAction.DEFAULT_ACTION);
}
/**
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonTransitionOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonTransitionOperation.java
new file mode 100644
index 000000000..cd414bc73
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonTransitionOperation.java
@@ -0,0 +1,82 @@
+package com.smartdevicelink.managers.screen;
+
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.interfaces.ISdl;
+import com.smartdevicelink.proxy.rpc.Show;
+import com.smartdevicelink.proxy.rpc.SoftButton;
+import com.smartdevicelink.proxy.rpc.enums.Result;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener;
+import com.smartdevicelink.util.DebugTool;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Created by Bilal Alsharifi on 6/15/20.
+ */
+class SoftButtonTransitionOperation extends Task {
+
+ private final WeakReference<ISdl> internalInterface;
+ private final CopyOnWriteArrayList<SoftButtonObject> softButtonObjects;
+ private String currentMainField1;
+
+ SoftButtonTransitionOperation(ISdl internalInterface, CopyOnWriteArrayList<SoftButtonObject> softButtonObjects, String currentMainField1) {
+ super("SoftButtonReplaceOperation");
+ this.internalInterface = new WeakReference<>(internalInterface);
+ this.softButtonObjects = softButtonObjects;
+ this.currentMainField1 = currentMainField1;
+ }
+
+ @Override
+ public void onExecute() {
+ start();
+ }
+
+ private void start() {
+ if (getState() == Task.CANCELED) {
+ return;
+ }
+
+ sendNewSoftButtons();
+ }
+
+ private void sendNewSoftButtons() {
+ Show show = new Show();
+ show.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (!response.getSuccess()) {
+ DebugTool.logWarning("Failed to transition soft button to new state");
+ }
+ onFinished();
+ }
+
+ @Override
+ public void onError(int correlationId, Result resultCode, String info) {
+ super.onError(correlationId, resultCode, info);
+ DebugTool.logWarning("Failed to transition soft button to new state. " + info);
+ onFinished();
+ }
+ });
+ show.setMainField1(currentMainField1);
+ show.setSoftButtons(currentStateSoftButtonsForObjects(softButtonObjects));
+ if (internalInterface.get() != null) {
+ internalInterface.get().sendRPC(show);
+ }
+ }
+
+ private List<SoftButton> currentStateSoftButtonsForObjects(List<SoftButtonObject> softButtonObjects) {
+ List<SoftButton> softButtons = new ArrayList<>();
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ softButtons.add(softButtonObject.getCurrentStateSoftButton());
+ }
+ return softButtons;
+ }
+
+ void setCurrentMainField1(String currentMainField1) {
+ this.currentMainField1 = currentMainField1;
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/AsynchronousOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/AsynchronousOperation.java
deleted file mode 100644
index c0a1dbb25..000000000
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/AsynchronousOperation.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * Copyright (c) 2019 Livio, Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following
- * disclaimer in the documentation and/or other materials provided with the
- * distribution.
- *
- * Neither the name of the Livio Inc. nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * Created by Nicole Yarroch on 7/25/19 8:43 AM
- */
-
-package com.smartdevicelink.managers.screen.choiceset;
-
-import com.smartdevicelink.util.DebugTool;
-
-class AsynchronousOperation implements Runnable {
- private static final String TAG = "AsynchronousOperation - ";
- private Thread thread;
- private final Object lock;
- private boolean blocked;
- private boolean executing;
- private boolean finished;
- private boolean cancelled;
-
- AsynchronousOperation() {
- lock = new Object();
- blocked = false;
- executing = false;
- finished = false;
- cancelled = false;
- }
-
- @Override
- public void run() {
- thread = Thread.currentThread();
- DebugTool.logInfo(TAG + "Starting: " + toString());
- if (isCancelled()) {
- finished = true;
- DebugTool.logInfo(TAG + "Operation was cancelled: " + toString());
- return;
- }
-
- executing = true;
- }
-
- void finishOperation() {
- unblock();
- executing = false;
- finished = true;
- cancelled = false;
- DebugTool.logInfo(TAG + "Finishing: " + toString());
- }
-
- boolean isExecuting() {
- return executing;
- }
-
- boolean isFinished() {
- return finished;
- }
-
- void cancel(){
- cancelled = true;
- }
-
- boolean isCancelled() {
- return cancelled;
- }
-
- void block(){
- if (!blocked && !finished) {
- blocked = true;
- DebugTool.logInfo(TAG + "Blocking: " + toString());
- try {
- synchronized (lock) {
- lock.wait();
- }
- } catch (InterruptedException e) {
- DebugTool.logWarning(TAG + "InterruptedException: " + toString());
- finishOperation();
- }
- }
- }
-
- void unblock(){
- if (blocked) {
- blocked = false;
- DebugTool.logInfo(TAG + "Unblocking: " + toString());
- synchronized (lock) {
- lock.notify();
- }
- }
- }
-
- @Override
- public String toString() {
- return this.getClass().getSimpleName() + " (OpId: " + System.identityHashCode(this) + ", OpThread:" + (thread != null ? thread.getName() : "no operating thread") + ", currentThread:" + Thread.currentThread().getName() + ", blocked:" + blocked + ", executing:" + executing + ", finished:" + finished + ", cancelled:" + cancelled + ")";
- }
-}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java
index 2e590f406..a06334fc2 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java
@@ -37,6 +37,8 @@ package com.smartdevicelink.managers.screen.choiceset;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
+import com.livio.taskmaster.Queue;
+import com.livio.taskmaster.Task;
import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.file.FileManager;
@@ -64,9 +66,6 @@ import com.smartdevicelink.util.DebugTool;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.List;
-import java.util.concurrent.Future;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
/**
* <strong>ChoiceSetManager</strong> <br>
@@ -90,9 +89,8 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
ChoiceSet pendingPresentationSet;
// We will pass operations into this to be completed
- PausableThreadPoolExecutor executor;
- LinkedBlockingQueue<Runnable> operationQueue;
- Future pendingPresentOperation;
+ Queue transactionQueue;
+ Task pendingPresentOperation;
PresentKeyboardOperation currentlyPresentedKeyboardOperation;
@@ -105,6 +103,10 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
BaseChoiceSetManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
super(internalInterface);
+ // prepare operations queue
+ transactionQueue = internalInterface.getTaskmaster().createQueue("ChoiceSetManagerQueue", 1, false);
+ transactionQueue.pause(); // pause until HMI ready
+
// capabilities
currentSystemContext = SystemContext.SYSCTXT_MAIN;
currentHMILevel = HMILevel.HMI_NONE;
@@ -118,9 +120,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
nextCancelId = choiceCellCancelIdMin;
isVROptional = false;
keyboardConfiguration = defaultKeyboardConfiguration();
- operationQueue = new LinkedBlockingQueue<>();
- executor = new PausableThreadPoolExecutor(1, Runtime.getRuntime().availableProcessors(), 10, TimeUnit.SECONDS, operationQueue);
- executor.pause(); // pause until HMI ready
currentlyPresentedKeyboardOperation = null;
}
@@ -136,8 +135,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
public void dispose(){
// cancel the operations
- operationQueue.clear();
- executor.shutdownNow();
+ transactionQueue.close();
currentHMILevel = null;
currentSystemContext = null;
@@ -175,10 +173,10 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
// checking VR will always be first in the queue.
// If pre-load operations were added while this was in progress
// clear it from the queue onError.
- operationQueue.clear();
+ transactionQueue.clear();
}
});
- executor.submit(checkChoiceVR);
+ transactionQueue.add(checkChoiceVR, false);
}
/**
@@ -227,7 +225,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
}
});
- executor.submit(preloadChoicesOperation);
+ transactionQueue.add(preloadChoicesOperation, false);
} else {
DebugTool.logError("File Manager was null in preload choice operation");
}
@@ -253,15 +251,15 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
pendingPresentationChoices.addAll(pendingPresentationSet.getChoices());
}
- if (pendingPresentOperation != null && !pendingPresentOperation.isCancelled() && !pendingPresentOperation.isDone() && (cellsToBeDeleted.retainAll(pendingPresentationChoices) || cellsToBeRemovedFromPending.retainAll(pendingPresentationChoices))){
- pendingPresentOperation.cancel(false);
+ if (pendingPresentOperation != null && pendingPresentOperation.getState() != Task.CANCELED && pendingPresentOperation.getState() != Task.FINISHED && (cellsToBeDeleted.retainAll(pendingPresentationChoices) || cellsToBeRemovedFromPending.retainAll(pendingPresentationChoices))){
+ pendingPresentOperation.cancelTask();
DebugTool.logWarning("Attempting to delete choice cells while there is a pending presentation operation. Pending presentation cancelled.");
pendingPresentOperation = null;
}
// Remove cells from pending and delete choices
pendingPresentationChoices.removeAll(cellsToBeRemovedFromPending);
- for (Runnable operation : operationQueue){
+ for (Task operation : transactionQueue.getTasksAsList()){
if (!(operation instanceof PreloadChoicesOperation)){ continue; }
((PreloadChoicesOperation) operation).removeChoicesFromUpload(cellsToBeRemovedFromPending);
}
@@ -283,7 +281,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
preloadedChoices.removeAll(cellsToBeDeleted);
}
});
- executor.submit(deleteChoicesOperation);
+ transactionQueue.add(deleteChoicesOperation, false);
}
/**
@@ -303,7 +301,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
if (!setUpChoiceSet(choiceSet)){ return; }
if (this.pendingPresentationSet != null && pendingPresentOperation != null){
- pendingPresentOperation.cancel(false);
+ pendingPresentOperation.cancelTask();
DebugTool.logWarning("Presenting a choice set while one is currently presented. Cancelling previous and continuing");
}
@@ -357,7 +355,8 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
presentOp = new PresentChoiceSetOperation(internalInterface, pendingPresentationSet, mode, keyboardConfiguration, keyboardListener, privateChoiceListener, nextCancelId++);
}
- pendingPresentOperation = executor.submit(presentOp);
+ transactionQueue.add(presentOp, false);
+ pendingPresentOperation = presentOp;
}
/**
@@ -379,7 +378,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
}
if (pendingPresentationSet != null && pendingPresentOperation != null){
- pendingPresentOperation.cancel(false);
+ pendingPresentOperation.cancelTask();
pendingPresentationSet = null;
DebugTool.logWarning("There is a current or pending choice set, cancelling and continuing.");
}
@@ -397,7 +396,8 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
Integer keyboardCancelID = nextCancelId++;
PresentKeyboardOperation keyboardOp = new PresentKeyboardOperation(internalInterface, keyboardConfiguration, initialText, customKeyboardConfig, listener, keyboardCancelID);
currentlyPresentedKeyboardOperation = keyboardOp;
- pendingPresentOperation = executor.submit(keyboardOp);
+ transactionQueue.add(keyboardOp, false);
+ pendingPresentOperation = keyboardOp;
return keyboardCancelID;
}
@@ -421,7 +421,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
}
// Next, attempt to cancel keyboard operations that have not yet started
- for (Runnable operation : operationQueue){
+ for (Task operation : transactionQueue.getTasksAsList()){
if (!(operation instanceof PresentKeyboardOperation)){ continue; }
PresentKeyboardOperation pendingOp = (PresentKeyboardOperation) operation;
@@ -551,21 +551,21 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
currentHMILevel = onHMIStatus.getHmiLevel();
if (currentHMILevel == HMILevel.HMI_NONE){
- executor.pause();
+ transactionQueue.pause();
}
if (oldHMILevel == HMILevel.HMI_NONE && currentHMILevel != HMILevel.HMI_NONE){
- executor.resume();
+ transactionQueue.resume();
}
currentSystemContext = onHMIStatus.getSystemContext();
if (currentSystemContext == SystemContext.SYSCTXT_HMI_OBSCURED || currentSystemContext == SystemContext.SYSCTXT_ALERT){
- executor.pause();
+ transactionQueue.pause();
}
if (currentSystemContext == SystemContext.SYSCTXT_MAIN && currentHMILevel != HMILevel.HMI_NONE){
- executor.resume();
+ transactionQueue.resume();
}
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/CheckChoiceVROptionalOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/CheckChoiceVROptionalOperation.java
index 1c427c16b..9eaba87cd 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/CheckChoiceVROptionalOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/CheckChoiceVROptionalOperation.java
@@ -35,6 +35,7 @@
package com.smartdevicelink.managers.screen.choiceset;
+import com.livio.taskmaster.Task;
import com.smartdevicelink.proxy.RPCResponse;
import com.smartdevicelink.proxy.interfaces.ISdl;
import com.smartdevicelink.proxy.rpc.Choice;
@@ -47,24 +48,22 @@ import com.smartdevicelink.util.DebugTool;
import java.lang.ref.WeakReference;
import java.util.Collections;
-class CheckChoiceVROptionalOperation extends AsynchronousOperation {
+class CheckChoiceVROptionalOperation extends Task {
private CheckChoiceVROptionalInterface checkChoiceVROptionalInterface;
private WeakReference<ISdl> internalInterface;
private boolean isVROptional;
CheckChoiceVROptionalOperation(ISdl internalInterface, CheckChoiceVROptionalInterface checkChoiceVROptionalInterface){
- super();
+ super("CheckChoiceVROptionalOperation");
this.internalInterface = new WeakReference<>(internalInterface);
this.checkChoiceVROptionalInterface = checkChoiceVROptionalInterface;
}
@Override
- public void run() {
- CheckChoiceVROptionalOperation.super.run();
+ public void onExecute() {
DebugTool.logInfo("Choice Operation: Executing check vr optional operation");
sendTestChoiceNoVR();
- block();
}
/**
@@ -121,7 +120,7 @@ class CheckChoiceVROptionalOperation extends AsynchronousOperation {
checkChoiceVROptionalInterface.onError(response.getInfo());
}
- CheckChoiceVROptionalOperation.super.finishOperation();
+ CheckChoiceVROptionalOperation.super.onFinished();
}
}
@@ -133,7 +132,7 @@ class CheckChoiceVROptionalOperation extends AsynchronousOperation {
checkChoiceVROptionalInterface.onError(info);
}
- CheckChoiceVROptionalOperation.super.finishOperation();
+ CheckChoiceVROptionalOperation.super.onFinished();
}
});
@@ -155,7 +154,7 @@ class CheckChoiceVROptionalOperation extends AsynchronousOperation {
checkChoiceVROptionalInterface.onCheckChoiceVROperationComplete(isVROptional);
}
- CheckChoiceVROptionalOperation.super.finishOperation();
+ CheckChoiceVROptionalOperation.super.onFinished();
}
@Override
@@ -165,7 +164,7 @@ class CheckChoiceVROptionalOperation extends AsynchronousOperation {
checkChoiceVROptionalInterface.onError(info);
}
- CheckChoiceVROptionalOperation.super.finishOperation();
+ CheckChoiceVROptionalOperation.super.onFinished();
}
});
if (internalInterface.get() != null){
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java
index 2fc6c314d..c12178da5 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java
@@ -35,6 +35,7 @@
package com.smartdevicelink.managers.screen.choiceset;
+import com.livio.taskmaster.Task;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.proxy.RPCResponse;
import com.smartdevicelink.proxy.interfaces.ISdl;
@@ -48,25 +49,23 @@ import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
-class DeleteChoicesOperation extends AsynchronousOperation {
+class DeleteChoicesOperation extends Task {
private WeakReference<ISdl> internalInterface;
private HashSet<ChoiceCell> cellsToDelete;
private CompletionListener completionListener;
DeleteChoicesOperation(ISdl internalInterface, HashSet<ChoiceCell> cellsToDelete, CompletionListener completionListener){
- super();
+ super("DeleteChoicesOperation");
this.internalInterface = new WeakReference<>(internalInterface);
this.cellsToDelete = cellsToDelete;
this.completionListener = completionListener;
}
@Override
- public void run() {
- DeleteChoicesOperation.super.run();
+ public void onExecute() {
DebugTool.logInfo("Choice Operation: Executing delete choices operation");
sendDeletions();
- block();
}
private void sendDeletions(){
@@ -88,7 +87,7 @@ class DeleteChoicesOperation extends AsynchronousOperation {
}
DebugTool.logInfo("Successfully deleted choices");
- DeleteChoicesOperation.super.finishOperation();
+ DeleteChoicesOperation.super.onFinished();
}
@Override
@@ -98,7 +97,7 @@ class DeleteChoicesOperation extends AsynchronousOperation {
}
DebugTool.logError("Failed to delete choice: " + info + " | Corr ID: " + correlationId);
- DeleteChoicesOperation.super.finishOperation();
+ DeleteChoicesOperation.super.onFinished();
}
@Override
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PausableThreadPoolExecutor.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PausableThreadPoolExecutor.java
deleted file mode 100644
index 74f1cd85a..000000000
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PausableThreadPoolExecutor.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- *
- * Note: This file has been modified from its original form.
- * Site with example code: https://developer.android.com/reference/java/util/concurrent/ThreadPoolExecutor
- */
-
-package com.smartdevicelink.managers.screen.choiceset;
-
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.ReentrantLock;
-
-class PausableThreadPoolExecutor extends ThreadPoolExecutor {
-
- private boolean isPaused;
- private ReentrantLock threadLock;
- private Condition condition;
-
- PausableThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
- super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
- threadLock = new ReentrantLock();
- condition = threadLock.newCondition();
- }
-
- protected void beforeExecute(Thread t, Runnable r) {
- super.beforeExecute(t, r);
- threadLock.lock();
- try {
- while (isPaused) condition.await();
- } catch (InterruptedException ie) {
- t.interrupt();
- } finally {
- threadLock.unlock();
- }
- }
-
- void pause() {
- threadLock.lock();
- try {
- isPaused = true;
- } finally {
- threadLock.unlock();
- }
- }
-
- void resume() {
- threadLock.lock();
- try {
- isPaused = false;
- condition.signalAll();
- } finally {
- threadLock.unlock();
- }
- }
-
-}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java
index 04f0b7d7f..d94b88ecb 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java
@@ -37,6 +37,7 @@ package com.smartdevicelink.managers.screen.choiceset;
import android.support.annotation.NonNull;
+import com.livio.taskmaster.Task;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.ManagerUtility;
import com.smartdevicelink.managers.file.FileManager;
@@ -62,7 +63,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
-class PreloadChoicesOperation extends AsynchronousOperation {
+class PreloadChoicesOperation extends Task {
private WeakReference<ISdl> internalInterface;
private WeakReference<FileManager> fileManager;
@@ -75,7 +76,7 @@ class PreloadChoicesOperation extends AsynchronousOperation {
PreloadChoicesOperation(ISdl internalInterface, FileManager fileManager, String displayName, WindowCapability defaultMainWindowCapability,
Boolean isVROptional, HashSet<ChoiceCell> cellsToPreload, CompletionListener listener){
- super();
+ super("PreloadChoicesOperation");
this.internalInterface = new WeakReference<>(internalInterface);
this.fileManager = new WeakReference<>(fileManager);
this.displayName = displayName;
@@ -86,8 +87,7 @@ class PreloadChoicesOperation extends AsynchronousOperation {
}
@Override
- public void run() {
- PreloadChoicesOperation.super.run();
+ public void onExecute() {
DebugTool.logInfo("Choice Operation: Executing preload choices operation");
preloadCellArtworks(new CompletionListener() {
@Override
@@ -95,7 +95,6 @@ class PreloadChoicesOperation extends AsynchronousOperation {
preloadCells();
}
});
- block();
}
void removeChoicesFromUpload(HashSet<ChoiceCell> choices){
@@ -167,14 +166,14 @@ class PreloadChoicesOperation extends AsynchronousOperation {
DebugTool.logInfo("Finished pre loading choice cells");
completionListener.onComplete(true);
- PreloadChoicesOperation.super.finishOperation();
+ PreloadChoicesOperation.super.onFinished();
}
@Override
public void onError(int correlationId, Result resultCode, String info) {
DebugTool.logError("There was an error uploading a choice cell: "+ info + " resultCode: " + resultCode);
- PreloadChoicesOperation.super.finishOperation();
+ PreloadChoicesOperation.super.onFinished();
}
@Override
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java
index 10137b68b..a94f33ec0 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java
@@ -35,6 +35,7 @@
package com.smartdevicelink.managers.screen.choiceset;
+import com.livio.taskmaster.Task;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCNotification;
@@ -60,7 +61,7 @@ import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
-class PresentChoiceSetOperation extends AsynchronousOperation {
+class PresentChoiceSetOperation extends Task {
private WeakReference<ISdl> internalInterface;
private ChoiceSet choiceSet;
@@ -78,7 +79,7 @@ class PresentChoiceSetOperation extends AsynchronousOperation {
PresentChoiceSetOperation(ISdl internalInterface, ChoiceSet choiceSet, InteractionMode mode,
KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, ChoiceSetSelectionListener choiceSetSelectionListener, Integer cancelID){
- super();
+ super("PresentChoiceSetOperation");
this.internalInterface = new WeakReference<>(internalInterface);
this.keyboardListener = keyboardListener;
this.choiceSet = choiceSet;
@@ -98,16 +99,14 @@ class PresentChoiceSetOperation extends AsynchronousOperation {
}
@Override
- public void run() {
- PresentChoiceSetOperation.super.run();
+ public void onExecute() {
DebugTool.logInfo("Choice Operation: Executing present choice set operation");
addListeners();
start();
- block();
}
private void start(){
- if (isCancelled()) {
+ if (getState() == Task.CANCELED) {
finishOperation();
return;
}
@@ -121,7 +120,7 @@ class PresentChoiceSetOperation extends AsynchronousOperation {
updateKeyboardProperties(new CompletionListener() {
@Override
public void onComplete(boolean success) {
- if (isCancelled()) {
+ if (getState() == Task.CANCELED) {
finishOperation();
return;
}
@@ -228,13 +227,13 @@ class PresentChoiceSetOperation extends AsynchronousOperation {
public void onResponse(int correlationId, RPCResponse response) {
updatedKeyboardProperties = false;
DebugTool.logInfo("Successfully reset choice keyboard properties to original config");
- PresentChoiceSetOperation.super.finishOperation();
+ PresentChoiceSetOperation.super.onFinished();
}
@Override
public void onError(int correlationId, Result resultCode, String info) {
DebugTool.logError("Failed to reset choice keyboard properties to original config " + resultCode + ", " + info);
- PresentChoiceSetOperation.super.finishOperation();
+ PresentChoiceSetOperation.super.onFinished();
}
});
@@ -245,7 +244,7 @@ class PresentChoiceSetOperation extends AsynchronousOperation {
DebugTool.logError("Internal Interface null when finishing choice keyboard reset");
}
} else {
- PresentChoiceSetOperation.super.finishOperation();
+ PresentChoiceSetOperation.super.onFinished();
}
}
@@ -253,13 +252,13 @@ class PresentChoiceSetOperation extends AsynchronousOperation {
* Cancels the choice set. If the choice set has not yet been sent to Core, it will not be sent. If the choice set is already presented on Core, the choice set will be dismissed using the `CancelInteraction` RPC.
*/
private void cancelInteraction() {
- if (isFinished()) {
+ if ((getState() == Task.FINISHED)) {
DebugTool.logInfo("This operation has already finished so it can not be canceled.");
return;
- } else if (isCancelled()) {
+ } else if (getState() == Task.CANCELED) {
DebugTool.logInfo("This operation has already been canceled. It will be finished at some point during the operation.");
return;
- } else if (isExecuting()) {
+ } else if ((getState() == Task.IN_PROGRESS)) {
if (sdlMsgVersion.getMajorVersion() < 6){
DebugTool.logWarning("Canceling a presented choice set is not supported on this head unit");
return;
@@ -287,7 +286,7 @@ class PresentChoiceSetOperation extends AsynchronousOperation {
}
} else {
DebugTool.logInfo("Canceling a choice set that has not yet been sent to Core");
- this.cancel();
+ this.cancelTask();
}
}
@@ -346,7 +345,7 @@ class PresentChoiceSetOperation extends AsynchronousOperation {
keyboardRPCListener = new OnRPCNotificationListener() {
@Override
public void onNotified(RPCNotification notification) {
- if (isCancelled()) {
+ if (getState() == Task.CANCELED) {
finishOperation();
return;
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java
index 5089d0c92..45420b881 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentKeyboardOperation.java
@@ -35,6 +35,7 @@
package com.smartdevicelink.managers.screen.choiceset;
+import com.livio.taskmaster.Task;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCNotification;
@@ -59,7 +60,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-class PresentKeyboardOperation extends AsynchronousOperation {
+class PresentKeyboardOperation extends Task {
private WeakReference<ISdl> internalInterface;
private KeyboardListener keyboardListener;
@@ -71,7 +72,7 @@ class PresentKeyboardOperation extends AsynchronousOperation {
SdlMsgVersion sdlMsgVersion;
PresentKeyboardOperation(ISdl internalInterface, KeyboardProperties originalKeyboardProperties, String initialText, KeyboardProperties customConfig, KeyboardListener keyboardListener, Integer cancelID){
- super();
+ super("PresentKeyboardOperation");
this.internalInterface = new WeakReference<>(internalInterface);
this.keyboardListener = keyboardListener;
this.originalKeyboardProperties = originalKeyboardProperties;
@@ -83,18 +84,16 @@ class PresentKeyboardOperation extends AsynchronousOperation {
}
@Override
- public void run() {
- PresentKeyboardOperation.super.run();
+ public void onExecute() {
DebugTool.logInfo("Keyboard Operation: Executing present keyboard operation");
addListeners();
start();
- block();
}
private void start(){
DebugTool.logInfo("Choice Operation: Executing present keyboard operation");
- if (isCancelled()) {
+ if (getState() == Task.CANCELED) {
finishOperation();
return;
}
@@ -107,7 +106,7 @@ class PresentKeyboardOperation extends AsynchronousOperation {
updateKeyboardProperties(new CompletionListener() {
@Override
public void onComplete(boolean success) {
- if (isCancelled()) {
+ if (getState() == Task.CANCELED) {
finishOperation();
return;
}
@@ -148,13 +147,13 @@ class PresentKeyboardOperation extends AsynchronousOperation {
* This will only dismiss an already presented keyboard if connected to head units running SDL 6.0+.
*/
void dismissKeyboard() {
- if (isFinished()) {
+ if ((getState() == Task.FINISHED)) {
DebugTool.logInfo("This operation has already finished so it can not be canceled.");
return;
- } else if (isCancelled()) {
+ } else if (getState() == Task.CANCELED) {
DebugTool.logInfo("This operation has already been canceled. It will be finished at some point during the operation.");
return;
- } else if (isExecuting()) {
+ } else if (getState() == Task.IN_PROGRESS) {
if (sdlMsgVersion.getMajorVersion() < 6){
DebugTool.logWarning("Canceling a keyboard is not supported on this head unit");
return;
@@ -181,7 +180,7 @@ class PresentKeyboardOperation extends AsynchronousOperation {
}
} else {
DebugTool.logInfo("Canceling a keyboard that has not yet been sent to Core.");
- this.cancel();
+ this.cancelTask();
}
}
@@ -242,13 +241,13 @@ class PresentKeyboardOperation extends AsynchronousOperation {
public void onResponse(int correlationId, RPCResponse response) {
updatedKeyboardProperties = false;
DebugTool.logInfo("Successfully reset choice keyboard properties to original config");
- PresentKeyboardOperation.super.finishOperation();
+ PresentKeyboardOperation.super.onFinished();
}
@Override
public void onError(int correlationId, Result resultCode, String info) {
DebugTool.logError("Failed to reset choice keyboard properties to original config " + resultCode + ", " + info);
- PresentKeyboardOperation.super.finishOperation();
+ PresentKeyboardOperation.super.onFinished();
}
});
@@ -259,7 +258,7 @@ class PresentKeyboardOperation extends AsynchronousOperation {
DebugTool.logError("Internal Interface null when finishing choice keyboard reset");
}
} else {
- PresentKeyboardOperation.super.finishOperation();
+ PresentKeyboardOperation.super.onFinished();
}
}
@@ -286,7 +285,7 @@ class PresentKeyboardOperation extends AsynchronousOperation {
keyboardRPCListener = new OnRPCNotificationListener() {
@Override
public void onNotified(RPCNotification notification) {
- if (isCancelled()) {
+ if (getState() == Task.CANCELED) {
finishOperation();
return;
}
diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlPacket.java b/base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java
index edc860a42..21362b53e 100644
--- a/base/src/main/java/com/smartdevicelink/protocol/SdlPacket.java
+++ b/base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java
@@ -31,13 +31,9 @@
*/
package com.smartdevicelink.protocol;
-import android.os.Parcel;
-import android.os.Parcelable;
-
import com.livio.BSON.BsonEncoder;
import com.smartdevicelink.protocol.enums.FrameType;
import com.smartdevicelink.transport.utl.TransportRecord;
-import com.smartdevicelink.util.DebugTool;
import java.nio.ByteBuffer;
import java.util.HashMap;
@@ -47,7 +43,7 @@ import java.util.HashMap;
* Any other binder transactions must include an additional int flag into their bundle or the parsing
* of this object will fail.
*/
-public class SdlPacket implements Parcelable{
+class BaseSdlPacket {
/**
* This is the amount of bytes added to the bundle from the router service for a specific int
@@ -122,9 +118,9 @@ public class SdlPacket implements Parcelable{
int messagingVersion = 1;
TransportRecord transportRecord;
- public SdlPacket(int version, boolean encryption, int frameType,
- int serviceType, int frameInfo, int sessionId,
- int dataSize, int messageId, byte[] payload) {
+ BaseSdlPacket(int version, boolean encryption, int frameType,
+ int serviceType, int frameInfo, int sessionId,
+ int dataSize, int messageId, byte[] payload) {
this.version = version;
this.encryption = encryption;
this.frameType = frameType;
@@ -140,9 +136,9 @@ public class SdlPacket implements Parcelable{
}
}
- public SdlPacket(int version, boolean encryption, int frameType,
- int serviceType, int frameInfo, int sessionId,
- int dataSize, int messageId, byte[] payload, int offset,int bytesToWrite) {
+ BaseSdlPacket(int version, boolean encryption, int frameType,
+ int serviceType, int frameInfo, int sessionId,
+ int dataSize, int messageId, byte[] payload, int offset, int bytesToWrite) {
this.version = version;
this.encryption = encryption;
this.frameType = frameType;
@@ -166,7 +162,7 @@ public class SdlPacket implements Parcelable{
* <p>Frame Info
* <p>
*/
- protected SdlPacket(){
+ protected BaseSdlPacket(){
//Package only empty constructor
this.version = 1;
this.encryption = false;
@@ -183,7 +179,7 @@ public class SdlPacket implements Parcelable{
* Creates a new packet based on previous packet definitions. Will not copy payload.
* @param packet an instance of the packet that should be copied.
*/
- protected SdlPacket(SdlPacket packet){
+ protected BaseSdlPacket(BaseSdlPacket packet){
this.version = packet.version;
this.encryption = packet.encryption;
this.frameType = packet.frameType;
@@ -359,92 +355,6 @@ public class SdlPacket implements Parcelable{
public void setMessagingVersion(int version){
this.messagingVersion = version;
}
-
-
-
- /* ***************************************************************************************************************************************************
- * *********************************************************** Parceable Overrides *****************************************************************
- *****************************************************************************************************************************************************/
-
-
-
- //I think this is FIFO...right?
- public SdlPacket(Parcel p) {
- this.version = p.readInt();
- this.encryption = (p.readInt() == 0) ? false : true;
- this.frameType = p.readInt();
- this.serviceType = p.readInt();
- this.frameInfo = p.readInt();
- this.sessionId = p.readInt();
- this.dataSize = p.readInt();
- this.messageId = p.readInt();
- if(p.readInt() == 1){ //We should have a payload attached
- payload = new byte[dataSize];
- p.readByteArray(payload);
- }
-
- this.priorityCoefficient = p.readInt();
-
- if(p.dataAvail() > EXTRA_PARCEL_DATA_LENGTH) { //See note on constant for why not 0
- try {
- messagingVersion = p.readInt();
- if (messagingVersion >= 2) {
- if (p.readInt() == 1) { //We should have a transport type attached
- this.transportRecord = (TransportRecord) p.readParcelable(TransportRecord.class.getClassLoader());
- }
- }
- }catch (RuntimeException e){
- DebugTool.logError("Error creating packet from parcel", e);
- }
- }
- }
-
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
-
- dest.writeInt(version);
- dest.writeInt(encryption? 1 : 0);
- dest.writeInt(frameType);
- dest.writeInt(serviceType);
- dest.writeInt(frameInfo);
- dest.writeInt(sessionId);
- dest.writeInt(dataSize);
- dest.writeInt(messageId);
- dest.writeInt(payload!=null? 1 : 0);
- if(payload!=null){
- dest.writeByteArray(payload);
- }
- dest.writeInt(priorityCoefficient);
-
- ///Additions after initial creation
- if(messagingVersion > 1){
- dest.writeInt(messagingVersion);
-
- dest.writeInt(transportRecord!=null? 1 : 0);
- if(transportRecord != null){
- dest.writeParcelable(transportRecord,0);
- }
- }
-
- }
-
- public static final Parcelable.Creator<SdlPacket> CREATOR = new Parcelable.Creator<SdlPacket>() {
- public SdlPacket createFromParcel(Parcel in) {
- return new SdlPacket(in);
- }
-
- @Override
- public SdlPacket[] newArray(int size) {
- return new SdlPacket[size];
- }
-
- };
public void putTag(String tag, Object data){
if(bsonPayload == null){
diff --git a/base/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java b/base/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java
index 9464cc296..a3e1da573 100644
--- a/base/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java
+++ b/base/src/main/java/com/smartdevicelink/proxy/interfaces/ISdl.java
@@ -2,6 +2,7 @@ package com.smartdevicelink.proxy.interfaces;
import android.support.annotation.NonNull;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.protocol.enums.SessionType;
import com.smartdevicelink.proxy.RPCMessage;
@@ -302,4 +303,6 @@ public interface ISdl {
* Start encrypted RPC service
*/
void startRPCEncryption();
+
+ Taskmaster getTaskmaster();
}
diff --git a/base/src/main/java/com/smartdevicelink/transport/TransportConstants.java b/base/src/main/java/com/smartdevicelink/transport/TransportConstants.java
index cd34d626a..9b2eba9ed 100644
--- a/base/src/main/java/com/smartdevicelink/transport/TransportConstants.java
+++ b/base/src/main/java/com/smartdevicelink/transport/TransportConstants.java
@@ -43,6 +43,7 @@ public class TransportConstants {
public static final String START_ROUTER_SERVICE_ACTION ="sdl.router.startservice";
public static final String ROUTER_SERVICE_ACTION = "com.smartdevicelink.router.service";
public static final String FOREGROUND_EXTRA = "foreground";
+ public static final String CONFIRMED_SDL_DEVICE = "confirmed_sdl_device";
public static final String BIND_LOCATION_PACKAGE_NAME_EXTRA = "BIND_LOCATION_PACKAGE_NAME_EXTRA";
public static final String BIND_LOCATION_CLASS_NAME_EXTRA = "BIND_LOCATION_CLASS_NAME_EXTRA";
diff --git a/base/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java b/base/src/main/java/com/smartdevicelink/transport/utl/BaseTransportRecord.java
index bcfc2e373..b0099581e 100644
--- a/base/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java
+++ b/base/src/main/java/com/smartdevicelink/transport/utl/BaseTransportRecord.java
@@ -32,17 +32,16 @@
package com.smartdevicelink.transport.utl;
-import android.os.Parcel;
-import android.os.Parcelable;
-
import com.smartdevicelink.transport.enums.TransportType;
-public class TransportRecord implements Parcelable{
+class BaseTransportRecord{
+
+ protected TransportType type;
+ protected String address;
- private TransportType type;
- private String address;
+ BaseTransportRecord(){}
- public TransportRecord(TransportType transportType, String address){
+ BaseTransportRecord(TransportType transportType, String address){
this.type = transportType;
this.address = address;
}
@@ -61,8 +60,8 @@ public class TransportRecord implements Parcelable{
return false;
}
- if (obj instanceof TransportRecord) {
- TransportRecord record = (TransportRecord) obj;
+ if (obj instanceof BaseTransportRecord) {
+ BaseTransportRecord record = (BaseTransportRecord) obj;
return record.type != null && record.type.equals(type) //Transport type is the same
&& ((record.address == null && address == null) //Both addresses are null
|| (record.address != null && record.address.equals(address))); //Or they match
@@ -80,47 +79,4 @@ public class TransportRecord implements Parcelable{
builder.append(address);
return builder.toString();
}
-
- public TransportRecord(Parcel p){
- if (p.readInt() == 1) { //We should have a transport type attached
- String transportName = p.readString();
- if(transportName != null){
- this.type = TransportType.valueOf(transportName);
- }
- }
-
- if (p.readInt() == 1) { //We should have a transport address attached
- address = p.readString();
- }
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(type!=null? 1 : 0);
- if(type != null){
- dest.writeString(type.name());
- }
-
- dest.writeInt(address !=null? 1 : 0);
- if(address != null){
- dest.writeString(address);
- }
- }
-
- public static final Parcelable.Creator<TransportRecord> CREATOR = new Parcelable.Creator<TransportRecord>() {
- public TransportRecord createFromParcel(Parcel in) {
- return new TransportRecord(in);
- }
-
- @Override
- public TransportRecord[] newArray(int size) {
- return new TransportRecord[size];
- }
-
- };
}
diff --git a/baseAndroid/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java b/baseAndroid/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java
new file mode 120000
index 000000000..2fec10c84
--- /dev/null
+++ b/baseAndroid/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java
@@ -0,0 +1 @@
+../../../../../../../base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java \ No newline at end of file
diff --git a/baseAndroid/src/main/java/com/smartdevicelink/protocol/SdlPacket.java b/baseAndroid/src/main/java/com/smartdevicelink/protocol/SdlPacket.java
deleted file mode 120000
index 8408c2728..000000000
--- a/baseAndroid/src/main/java/com/smartdevicelink/protocol/SdlPacket.java
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../base/src/main/java/com/smartdevicelink/protocol/SdlPacket.java \ No newline at end of file
diff --git a/baseAndroid/src/main/java/com/smartdevicelink/transport/utl/BaseTransportRecord.java b/baseAndroid/src/main/java/com/smartdevicelink/transport/utl/BaseTransportRecord.java
new file mode 120000
index 000000000..067c6a17a
--- /dev/null
+++ b/baseAndroid/src/main/java/com/smartdevicelink/transport/utl/BaseTransportRecord.java
@@ -0,0 +1 @@
+../../../../../../../../base/src/main/java/com/smartdevicelink/transport/utl/BaseTransportRecord.java \ No newline at end of file
diff --git a/baseAndroid/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java b/baseAndroid/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java
deleted file mode 120000
index 6fec751ec..000000000
--- a/baseAndroid/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../../base/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java \ No newline at end of file
diff --git a/baseAndroid/src/main/java/com/smartdevicelink/util/HttpRequestTask.java b/baseAndroid/src/main/java/com/smartdevicelink/util/HttpRequestTask.java
deleted file mode 120000
index 1733992ae..000000000
--- a/baseAndroid/src/main/java/com/smartdevicelink/util/HttpRequestTask.java
+++ /dev/null
@@ -1 +0,0 @@
-../../../../../../../base/src/main/java/com/smartdevicelink/util/HttpRequestTask.java \ No newline at end of file
diff --git a/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java b/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java
index d4611e8b3..d53b64b9c 100644
--- a/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java
+++ b/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java
@@ -34,6 +34,7 @@ package com.smartdevicelink.java;
import android.util.Log;
import com.smartdevicelink.managers.CompletionListener;
+import com.smartdevicelink.managers.screen.OnButtonListener;
import com.smartdevicelink.managers.SdlManager;
import com.smartdevicelink.managers.SdlManagerListener;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
@@ -76,15 +77,12 @@ public class SdlService {
private static final String IMAGE_DIR = "assets/images/";
-
-
// variable to create and call functions of the SyncProxy
private SdlManager sdlManager = null;
private List<ChoiceCell> choiceCellList;
private SdlServiceCallback callback;
-
public SdlService(BaseTransportConfig config, SdlServiceCallback callback){
this.callback = callback;
buildSdlManager(config);
@@ -130,6 +128,7 @@ public class SdlService {
SdlService.this.sdlManager = null;
if(SdlService.this.callback != null){
SdlService.this.callback.onEnd();
+ stop();
}
}
@@ -191,6 +190,7 @@ public class SdlService {
performWelcomeSpeak();
performWelcomeShow();
preloadChoices();
+ subscribeToButtons();
}
}
});
@@ -327,6 +327,37 @@ public class SdlService {
}
/**
+ * Attempts to Subscribe to all preset buttons
+ */
+ private void subscribeToButtons() {
+ ButtonName[] buttonNames = {ButtonName.PLAY_PAUSE, ButtonName.SEEKLEFT, ButtonName.SEEKRIGHT, ButtonName.AC_MAX, ButtonName.AC, ButtonName.RECIRCULATE,
+ ButtonName.FAN_UP, ButtonName.FAN_DOWN, ButtonName.TEMP_UP, ButtonName.TEMP_DOWN, ButtonName.FAN_DOWN, ButtonName.DEFROST_MAX, ButtonName.DEFROST_REAR, ButtonName.DEFROST,
+ ButtonName.UPPER_VENT, ButtonName.LOWER_VENT, ButtonName.VOLUME_UP, ButtonName.VOLUME_DOWN, ButtonName.EJECT, ButtonName.SOURCE, ButtonName.SHUFFLE, ButtonName.REPEAT};
+
+ OnButtonListener onButtonListener = new OnButtonListener() {
+ @Override
+ public void onPress(ButtonName buttonName, OnButtonPress buttonPress) {
+ sdlManager.getScreenManager().setTextField1(buttonName + " pressed");
+ Log.i(TAG, "onPress: " + buttonName);
+ }
+
+ @Override
+ public void onEvent(ButtonName buttonName, OnButtonEvent buttonEvent) {
+ sdlManager.getScreenManager().setTextField2(buttonName + " " + buttonEvent.getButtonEventMode());
+ }
+
+ @Override
+ public void onError(String info) {
+ Log.i(TAG, "onError: " + info);
+ }
+ };
+
+ for (ButtonName buttonName : buttonNames) {
+ sdlManager.getScreenManager().addButtonListener(buttonName, onButtonListener);
+ }
+ }
+
+ /**
* Will show a sample test message on screen as well as speak a sample test message
*/
private void showTest(){
diff --git a/javaEE/build.gradle b/javaEE/build.gradle
index 08a6bcf1e..39b6f0956 100644
--- a/javaEE/build.gradle
+++ b/javaEE/build.gradle
@@ -30,6 +30,7 @@ dependencies {
extraLibs 'org.mongodb:bson:3.10.1'
extraLibs 'com.android.support:support-annotations:28.0.0'
extraLibs 'org.java-websocket:Java-WebSocket:1.3.9'
+ extraLibs 'com.livio.taskmaster:taskmaster:0.3.0'
configurations.api.extendsFrom(configurations.extraLibs)
}
diff --git a/javaSE/build.gradle b/javaSE/build.gradle
index c6a393e8a..1f4dcc048 100644
--- a/javaSE/build.gradle
+++ b/javaSE/build.gradle
@@ -31,6 +31,7 @@ dependencies {
extraLibs 'org.mongodb:bson:3.10.1'
extraLibs 'com.android.support:support-annotations:28.0.0'
extraLibs 'org.java-websocket:Java-WebSocket:1.3.9'
+ extraLibs 'com.livio.taskmaster:taskmaster:0.3.0'
configurations.api.extendsFrom(configurations.extraLibs)
}
diff --git a/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java b/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java
index a711438cc..3edb6b4ea 100644
--- a/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java
+++ b/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java
@@ -142,14 +142,6 @@ public class SdlManager extends BaseSdlManager {
}
@Override
- void onProxyClosed(SdlDisconnectedReason reason) {
- Log.i(TAG, "Proxy is closed.");
- if (managerListener != null) {
- managerListener.onDestroy(SdlManager.this);
- }
- }
-
- @Override
public void dispose() {
if (this.permissionManager != null) {
this.permissionManager.dispose();
diff --git a/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java b/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java
index 4a5e3747d..6a8ebe136 100644
--- a/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java
+++ b/javaSE/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java
@@ -35,6 +35,8 @@ package com.smartdevicelink.managers.lifecycle;
import android.support.annotation.RestrictTo;
import com.smartdevicelink.SdlConnection.SdlSession;
+import com.smartdevicelink.exception.SdlException;
+import com.smartdevicelink.proxy.rpc.enums.SdlDisconnectedReason;
import com.smartdevicelink.transport.BaseTransportConfig;
/**
@@ -48,16 +50,28 @@ public class LifecycleManager extends BaseLifecycleManager {
}
@Override
- void initializeProxy() {
- super.initializeProxy();
+ void initialize() {
+ super.initialize();
this.session = new SdlSession(sdlConnectionListener, _transportConfig);
}
@Override
+ void cycle(SdlDisconnectedReason disconnectedReason) {
+ clean();
+ if (session != null) {
+ try {
+ session.startSession();
+ } catch (SdlException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ @Override
void onTransportDisconnected(String info, boolean availablePrimary, BaseTransportConfig transportConfig) {
super.onTransportDisconnected(info, availablePrimary, transportConfig);
if (!availablePrimary) {
- onClose(info, null);
+ onClose(info, null, null);
}
}
}
diff --git a/javaSE/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java b/javaSE/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java
new file mode 100644
index 000000000..c34b0775d
--- /dev/null
+++ b/javaSE/src/main/java/com/smartdevicelink/managers/screen/SubscribeButtonManager.java
@@ -0,0 +1,11 @@
+package com.smartdevicelink.managers.screen;
+
+import android.support.annotation.NonNull;
+import com.smartdevicelink.proxy.interfaces.ISdl;
+
+public class SubscribeButtonManager extends BaseSubscribeButtonManager {
+
+ public SubscribeButtonManager(@NonNull ISdl internalInterface) {
+ super(internalInterface);
+ }
+}
diff --git a/javaSE/src/main/java/com/smartdevicelink/protocol/SdlPacket.java b/javaSE/src/main/java/com/smartdevicelink/protocol/SdlPacket.java
new file mode 100644
index 000000000..d9a14b6f7
--- /dev/null
+++ b/javaSE/src/main/java/com/smartdevicelink/protocol/SdlPacket.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2017 - 2019, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.protocol;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+public class SdlPacket extends BaseSdlPacket {
+
+ public SdlPacket(int version, boolean encryption, int frameType,
+ int serviceType, int frameInfo, int sessionId,
+ int dataSize, int messageId, byte[] payload) {
+ super(version, encryption, frameType, serviceType, frameInfo, sessionId, dataSize, messageId, payload);
+ }
+
+ public SdlPacket(int version, boolean encryption, int frameType,
+ int serviceType, int frameInfo, int sessionId,
+ int dataSize, int messageId, byte[] payload, int offset, int bytesToWrite) {
+ super(version, encryption, frameType, serviceType, frameInfo, sessionId, dataSize, messageId, payload, offset, bytesToWrite);
+ }
+
+ protected SdlPacket() {
+ super();
+ }
+
+ protected SdlPacket(BaseSdlPacket packet) {
+ super(packet);
+ }
+
+ @Deprecated
+ public SdlPacket(Parcel p){}
+
+ @Deprecated
+ public int describeContents() {
+ return 0;
+ }
+
+ @Deprecated
+ public void writeToParcel(Parcel dest, int flags) {}
+
+ @Deprecated
+ public static final Parcelable.Creator<SdlPacket> CREATOR = new Parcelable.Creator<SdlPacket>() {
+ public SdlPacket createFromParcel(Parcel in) {
+ return new SdlPacket(in);
+ }
+
+ @Override
+ public SdlPacket[] newArray(int size) {
+ return new SdlPacket[size];
+ }
+
+ };
+}
diff --git a/javaSE/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java b/javaSE/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java
new file mode 100644
index 000000000..7bc15e2ab
--- /dev/null
+++ b/javaSE/src/main/java/com/smartdevicelink/transport/utl/TransportRecord.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017 - 2019, SmartDeviceLink Consortium, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of the SmartDeviceLink Consortium, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.smartdevicelink.transport.utl;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import com.smartdevicelink.transport.enums.TransportType;
+
+public class TransportRecord extends BaseTransportRecord {
+
+ public TransportRecord(TransportType transportType, String address) {
+ super(transportType, address);
+ }
+
+ @Deprecated
+ public TransportRecord(Parcel p) {}
+
+ @Deprecated
+ public int describeContents() {
+ return 0;
+ };
+
+ @Deprecated
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(type!=null? 1 : 0);
+ if(type != null){
+ dest.writeString(type.name());
+ }
+
+ dest.writeInt(address !=null? 1 : 0);
+ if(address != null){
+ dest.writeString(address);
+ }
+ }
+
+ @Deprecated
+ public static final Parcelable.Creator<TransportRecord> CREATOR = new Parcelable.Creator<TransportRecord>() {
+ public TransportRecord createFromParcel(Parcel in) {
+ return new TransportRecord(in);
+ }
+
+ @Override
+ public TransportRecord[] newArray(int size) {
+ return new TransportRecord[size];
+ }
+
+ };
+}
diff --git a/javaSE/src/main/java/com/smartdevicelink/util/HttpRequestTask.java b/javaSE/src/main/java/com/smartdevicelink/util/HttpRequestTask.java
new file mode 100644
index 000000000..6ecc73e6e
--- /dev/null
+++ b/javaSE/src/main/java/com/smartdevicelink/util/HttpRequestTask.java
@@ -0,0 +1,184 @@
+package com.smartdevicelink.util;
+
+import android.os.AsyncTask;
+import android.util.Log;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+
+@Deprecated
+public class HttpRequestTask extends AsyncTask<String, String, String> {
+ private static final String TAG = "Http Request Task";
+
+ public static final String REQUEST_TYPE_POST = "POST";
+ public static final String REQUEST_TYPE_GET = "GET";
+ public static final String REQUEST_TYPE_DELETE = "DELETE";
+
+ HttpRequestTaskCallback cb;
+
+ /**
+ * @param hcb callback for when this task finishes
+ * <br><br><b> - When calling execute, params as followed: </b><br>
+ * 1. Url String<br>
+ * 2. Request type (Defined in this class) REQUEST_TYPE_POST, REQUEST_TYPE_GET, REQUEST_TYPE_DELETE<br>
+ * 3. (Optional) Data to be sent. <br>
+ * 4. (Optional) Content Type Default will be application/json<br>
+ * 5. (Optional) Accept Type default will be application/json
+ *
+ */
+ @Deprecated
+ public HttpRequestTask( HttpRequestTaskCallback hcb){
+ this.cb = hcb;
+ }
+
+ @Deprecated
+ protected String doInBackground(String... params) {
+ int length = params.length;
+ String urlString = params[0];
+ String request_type = params[1];
+
+ //Grab and set data to be written if included
+ String data;
+ if(length>2){
+ data = params[2];
+ }else{
+ data = null;
+ }
+
+ //Grab and set content type for the header if included
+ String contentType;
+ if(length>3){
+ contentType = params[3];
+ }else{
+ contentType = "application/json";
+ }
+ //Grab and set accept type for the header if included
+ String acceptType;
+ if(length>4){
+ acceptType = params[4];
+ }else{
+ acceptType = "application/json";
+ }
+
+ if(urlString == null || request_type == null){
+ Log.e(TAG, "Can't process request, param error");
+ if(cb!=null){
+ cb.httpFailure(-1);
+ cb = null;
+ }
+ return "Error";
+ }
+
+ HttpURLConnection urlConnection = null;
+ BufferedReader reader = null;
+ try {
+ URL url = new URL(urlString);
+ urlConnection = (HttpURLConnection) url.openConnection();
+ urlConnection.setDoOutput(true);
+ urlConnection.setRequestMethod(request_type);
+ urlConnection.setRequestProperty("Content-Type", contentType);
+ urlConnection.setRequestProperty("Accept", acceptType);
+ //If we have data, we should write it out
+ if(data !=null){
+ Writer writer = new BufferedWriter(new OutputStreamWriter(urlConnection.getOutputStream(), "UTF-8"));
+ writer.write(data);
+ writer.close();
+ }
+ InputStream inputStream = urlConnection.getInputStream();
+
+ int responseCode = urlConnection.getResponseCode();
+ if (responseCode == 200) { //Success
+ //input stream
+ StringBuffer buffer = new StringBuffer();
+ if (inputStream == null) {
+ // Nothing to do.
+ if(cb!=null){
+ cb.httpCallComplete(null);
+ cb = null;
+ }
+ return null;
+ }
+ reader = new BufferedReader(new InputStreamReader(inputStream));
+
+ String inputLine;
+ while ((inputLine = reader.readLine()) != null)
+ buffer.append(inputLine).append("\n");
+ if (buffer.length() == 0) {
+ // Stream was empty. No point in parsing.
+ if(cb!=null){
+ cb.httpCallComplete(null);
+ cb = null;
+ }
+ return null;
+ }
+ String response = null;
+
+ response = buffer.toString();
+ //send to post execute
+ if(cb!=null){
+ cb.httpCallComplete(response);
+ cb = null;
+ }
+ return response;
+ }else{
+ if(cb!=null){
+ cb.httpFailure(responseCode);
+ cb = null;
+ }
+ Log.e(TAG, "Failed to download file - " + responseCode);
+ return null;
+ }
+
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ } catch (NullPointerException e){ // Only to catch error in urlConnection.getOutputStream() - when servers are down
+ e.printStackTrace();
+ urlConnection = null;
+ }
+ finally {
+ if (urlConnection != null) {
+ urlConnection.disconnect();
+ }
+ if (reader != null) {
+ try {
+ reader.close();
+ } catch (final IOException e) {
+ Log.e(TAG, "Error closing stream", e);
+ }
+ }
+ if(cb!=null){
+ cb.httpFailure(-1);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Callback interface for HTTP requests.
+ * @author Joey Grover
+ *
+ */
+ @Deprecated
+ public interface HttpRequestTaskCallback{
+ /**
+ * Called when HTTP request is successfully completed.
+ * @param response The response to the HTTP request.
+ */
+ public abstract void httpCallComplete(String response);
+ /**
+ * Called when HTTP request failed.
+ * @param statusCode The HTTP failure code.
+ */
+ public abstract void httpFailure(int statusCode);
+ }
+
+}