summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xandroid/hello_sdl_android/src/main/java/com/sdl/hellosdlandroid/SdlService.java17
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/AlertViewTest.java78
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/SdlManagerTests.java5
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java6
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertAudioDataTest.java56
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertManagerTest.java166
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java315
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/ScreenManagerTests.java93
-rw-r--r--android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/SoftButtonManagerTests.java53
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/SdlManager.java143
-rw-r--r--android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java53
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/AlertCompletionListener.java11
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java6
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/ISdl.java3
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/ManagerUtility.java35
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java6
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/AlertAudioData.java82
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/AlertCanceledListener.java40
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/AlertView.java291
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/AudioData.java168
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java342
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java95
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java45
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/DispatchGroup.java60
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/PresentAlertOperation.java524
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java22
-rw-r--r--base/src/main/java/com/smartdevicelink/managers/screen/choiceset/BaseChoiceSetManager.java23
-rw-r--r--javaSE/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java16
-rw-r--r--javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java144
-rw-r--r--javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java18
30 files changed, 2802 insertions, 114 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 8767b70fa..e4119d16c 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
@@ -11,11 +11,13 @@ import android.os.Build;
import android.os.IBinder;
import android.util.Log;
+import com.smartdevicelink.managers.AlertCompletionListener;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.SdlManager;
import com.smartdevicelink.managers.SdlManagerListener;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.managers.lifecycle.LifecycleConfigurationUpdate;
+import com.smartdevicelink.managers.screen.AlertView;
import com.smartdevicelink.managers.screen.OnButtonListener;
import com.smartdevicelink.managers.screen.choiceset.ChoiceCell;
import com.smartdevicelink.managers.screen.choiceset.ChoiceSet;
@@ -26,7 +28,6 @@ import com.smartdevicelink.managers.screen.menu.VoiceCommand;
import com.smartdevicelink.managers.screen.menu.VoiceCommandSelectionListener;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCNotification;
-import com.smartdevicelink.proxy.rpc.Alert;
import com.smartdevicelink.proxy.rpc.OnButtonEvent;
import com.smartdevicelink.proxy.rpc.OnButtonPress;
import com.smartdevicelink.proxy.rpc.OnHMIStatus;
@@ -424,10 +425,16 @@ public class SdlService extends Service {
}
private void showAlert(String text) {
- Alert alert = new Alert();
- alert.setAlertText1(text);
- alert.setDuration(5000);
- sdlManager.sendRPC(alert);
+ AlertView.Builder builder = new AlertView.Builder();
+ builder.setText(text);
+ builder.setTimeout(5);
+ AlertView alertView = builder.build();
+ sdlManager.getScreenManager().presentAlert(alertView, new AlertCompletionListener() {
+ @Override
+ public void onComplete(boolean success, Integer tryAgainTime) {
+ Log.i(TAG, "Alert presented: "+ success);
+ }
+ });
}
// Choice Set
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/AlertViewTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/AlertViewTest.java
new file mode 100644
index 000000000..041d0bbed
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/AlertViewTest.java
@@ -0,0 +1,78 @@
+package com.smartdevicelink;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.managers.screen.AlertAudioData;
+import com.smartdevicelink.managers.screen.AlertView;
+import com.smartdevicelink.managers.screen.SoftButtonObject;
+import com.smartdevicelink.managers.screen.SoftButtonState;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+@RunWith(AndroidJUnit4.class)
+public class AlertViewTest {
+
+ @Test
+ public void testAlertView() {
+ SdlArtwork artwork1 = new SdlArtwork("test1", FileType.GRAPHIC_PNG, 1, true);
+ SdlArtwork artwork2 = new SdlArtwork("test2", FileType.GRAPHIC_PNG, 2, true);
+
+ SoftButtonState softButtonState1 = new SoftButtonState("object1-state1", "o1s1", new SdlArtwork("image1", FileType.GRAPHIC_PNG, 3, true));
+ SoftButtonObject softButtonObject1 = new SoftButtonObject("object1", Arrays.asList(softButtonState1), softButtonState1.getName(), null);
+ SoftButtonObject softButtonObject2 = new SoftButtonObject("object2", Arrays.asList(softButtonState1), softButtonState1.getName(), null);
+
+ AlertAudioData alertAudioData = new AlertAudioData("hi");
+
+ AlertView.Builder builder = new AlertView.Builder();
+ builder.setText("Test");
+ builder.setTertiaryText("Test");
+ builder.setSecondaryText("Test");
+ builder.setTimeout(1);
+ builder.setIcon(artwork1);
+ builder.setSoftButtons(Collections.singletonList(softButtonObject1));
+ builder.setDefaultTimeOut(3);
+ builder.setAudio(alertAudioData);
+ builder.setShowWaitIndicator(true);
+ AlertView alertView = builder.build();
+
+ assertEquals(alertView.getText(), "Test");
+ assertEquals(alertView.getSecondaryText(), "Test");
+ assertEquals(alertView.getTertiaryText(), "Test");
+ assertTrue(alertView.getAudio().getAudioData().size() > 0);
+ assertEquals(alertView.getIcon().getName(), "test1");
+ assertEquals(alertView.getSoftButtons().get(0).getName(), "object1");
+ assertEquals(alertView.getDefaultTimeout(), 3);
+ assertEquals(alertView.getTimeout().intValue(), 3);
+ assertEquals(alertView.isShowWaitIndicator(), true);
+
+ alertView.setText("Test2");
+ alertView.setTertiaryText("Test2");
+ alertView.setSecondaryText("Test2");
+ alertView.setDefaultTimeout(6);
+ alertView.setTimeout(6);
+ alertView.setAudio(alertAudioData);
+ alertView.setIcon(artwork2);
+ alertView.setSoftButtons(Collections.singletonList(softButtonObject2));
+ alertView.setShowWaitIndicator(false);
+
+ assertEquals(alertView.getText(), "Test2");
+ assertEquals(alertView.getSecondaryText(), "Test2");
+ assertEquals(alertView.getTertiaryText(), "Test2");
+ assertTrue(alertView.getAudio().getAudioData().size() > 0);
+ assertEquals(alertView.getIcon().getName(), "test2");
+ assertEquals(alertView.getSoftButtons().get(0).getName(), "object2");
+ assertEquals(alertView.getDefaultTimeout(), 6);
+ assertEquals(alertView.getTimeout().intValue(), 6);
+ assertEquals(alertView.isShowWaitIndicator(), false);
+
+ }
+}
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 ca941fab6..db9ce11ac 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
@@ -7,6 +7,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.lifecycle.LifecycleConfigurationUpdate;
import com.smartdevicelink.managers.lockscreen.LockScreenConfig;
+import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.proxy.RPCMessage;
import com.smartdevicelink.proxy.RPCRequest;
@@ -140,6 +141,10 @@ public class SdlManagerTests {
// mock internalInterface and set it manually
internalInterface = mock(ISdl.class);
+ manager.set_internalInterface(internalInterface);
+ PermissionManager permissionManager = mock(PermissionManager.class);
+
+ when(internalInterface.getPermissionManager()).thenReturn(permissionManager);
when(internalInterface.getTaskmaster()).thenReturn(new Taskmaster.Builder().build());
manager._internalInterface = internalInterface;
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java
index 95977e857..64e4c1474 100644
--- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/lifecycle/SystemCapabilityManagerTests.java
@@ -7,6 +7,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.ManagerUtility;
+import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.protocol.ISdlServiceListener;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.protocol.enums.SessionType;
@@ -1066,5 +1067,10 @@ public class SystemCapabilityManagerTests {
public SystemCapabilityManager getSystemCapabilityManager() {
return null;
}
+
+ @Override
+ public PermissionManager getPermissionManager() {
+ return null;
+ }
}
}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertAudioDataTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertAudioDataTest.java
new file mode 100644
index 000000000..3459c6e34
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertAudioDataTest.java
@@ -0,0 +1,56 @@
+package com.smartdevicelink.managers.screen;
+
+import android.content.Context;
+import android.net.Uri;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import java.util.Collections;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+public class AlertAudioDataTest {
+ SdlFile testAudio;
+
+ @Before
+ public void setUp() throws Exception {
+ Context mTestContext = getInstrumentation().getContext();
+ Uri uri1 = Uri.parse("android.resource://" + mTestContext.getPackageName() + "/raw/test_audio_square_250hz_80amp_1s.mp3");
+ testAudio = new SdlFile("TestAudioFile", FileType.AUDIO_MP3, uri1, false);
+ }
+
+ @Test
+ public void testConstructors() {
+ AlertAudioData alertAudioData1 = new AlertAudioData("phoneticString", SpeechCapabilities.TEXT);
+ alertAudioData1.setPlayTone(true);
+ assertEquals("phoneticString", alertAudioData1.getAudioData().get(0).getText());
+ assertTrue(alertAudioData1.isPlayTone());
+
+ AlertAudioData alertAudioData2 = new AlertAudioData("spokenString");
+ assertEquals("spokenString", alertAudioData2.getAudioData().get(0).getText());
+
+ AlertAudioData alertAudioData3 = new AlertAudioData(testAudio);
+ assertEquals(alertAudioData3.getAudioData().get(0).getText(), testAudio.getName());
+ }
+
+ @Test
+ public void testAdd() {
+ AlertAudioData alertAudioData1 = new AlertAudioData("phoneticString", SpeechCapabilities.TEXT);
+ alertAudioData1.addAudioFiles(Collections.singletonList(testAudio));
+ alertAudioData1.addPhoneticSpeechSynthesizerStrings(Collections.singletonList("addition"), SpeechCapabilities.TEXT);
+ alertAudioData1.addSpeechSynthesizerStrings(Collections.singletonList("addition2"));
+ alertAudioData1.addAudioFiles(Collections.singletonList(testAudio));
+ assertEquals("phoneticString", alertAudioData1.getAudioData().get(0).getText());
+ assertEquals(testAudio.getName(), alertAudioData1.getAudioData().get(1).getText());
+ assertEquals("addition", alertAudioData1.getAudioData().get(2).getText());
+ assertEquals("addition2", alertAudioData1.getAudioData().get(3).getText());
+ }
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertManagerTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertManagerTest.java
new file mode 100644
index 000000000..1655b3d08
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/AlertManagerTest.java
@@ -0,0 +1,166 @@
+package com.smartdevicelink.managers.screen;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.livio.taskmaster.Taskmaster;
+import com.smartdevicelink.managers.AlertCompletionListener;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.lifecycle.OnSystemCapabilityListener;
+import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
+import com.smartdevicelink.managers.permission.OnPermissionChangeListener;
+import com.smartdevicelink.managers.permission.PermissionManager;
+import com.smartdevicelink.managers.permission.PermissionStatus;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.rpc.DisplayCapability;
+import com.smartdevicelink.proxy.rpc.ImageField;
+import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities;
+import com.smartdevicelink.proxy.rpc.TextField;
+import com.smartdevicelink.proxy.rpc.WindowCapability;
+import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
+import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
+import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
+import com.smartdevicelink.test.TestValues;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class AlertManagerTest {
+ AlertManager alertManager;
+
+ @Before
+ public void setUp() throws Exception {
+ // mock things
+ ISdl internalInterface = mock(ISdl.class);
+ FileManager fileManager = mock(FileManager.class);
+ PermissionManager permissionManager = mock(PermissionManager.class);
+
+ when(internalInterface.getPermissionManager()).thenReturn(permissionManager);
+
+ Taskmaster taskmaster = new Taskmaster.Builder().build();
+ taskmaster.start();
+ when(internalInterface.getTaskmaster()).thenReturn(taskmaster);
+
+ Answer<Void> permissionAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ OnPermissionChangeListener onPermissionChangeListener = (OnPermissionChangeListener) args[2];
+ Map<FunctionID, PermissionStatus > allowedPermissions = new HashMap<>();
+ int permissionGroupStatus = PermissionManager.PERMISSION_GROUP_STATUS_DISALLOWED;
+ onPermissionChangeListener.onPermissionsChange(allowedPermissions,permissionGroupStatus);
+ return null;
+ }
+ };
+ doAnswer(permissionAnswer).when(permissionManager).addListener(any(List.class), anyInt(), any(OnPermissionChangeListener.class));
+
+ Answer<Void> onSystemCapabilityAnswer = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ OnSystemCapabilityListener onSystemCapabilityListener = (OnSystemCapabilityListener) args[1];
+ WindowCapability windowCapability = getWindowCapability(3);
+ DisplayCapability displayCapability = new DisplayCapability();
+ displayCapability.setWindowCapabilities(Collections.singletonList(windowCapability));
+ List<DisplayCapability> capabilities = Collections.singletonList(displayCapability);
+ onSystemCapabilityListener.onCapabilityRetrieved(capabilities);
+ return null;
+ }
+ };
+
+ SystemCapabilityManager systemCapabilityManager = mock(SystemCapabilityManager.class);
+ doAnswer(onSystemCapabilityAnswer).when(systemCapabilityManager).addOnSystemCapabilityListener(eq(SystemCapabilityType.DISPLAYS), any(OnSystemCapabilityListener.class));
+ doReturn(systemCapabilityManager).when(internalInterface).getSystemCapabilityManager();
+
+ alertManager = new AlertManager(internalInterface, fileManager);
+ }
+
+ @Test
+ public void testInstantiation() {
+ assertNotNull(alertManager.currentWindowCapability);
+ assertNotNull(alertManager.nextCancelId);
+ assertFalse(alertManager.isAlertRPCAllowed);
+ }
+
+ @Test
+ public void testPresentAlert() {
+ AlertView.Builder builder = new AlertView.Builder();
+ AlertView alertView = builder.build();
+ alertManager.presentAlert(alertView, new AlertCompletionListener() {
+ @Override
+ public void onComplete(boolean success, Integer tryAgainTime) {
+
+ }
+ });
+ assertTrue(alertManager.transactionQueue.getTasksAsList().size() == 1);
+ }
+
+ private WindowCapability getWindowCapability(int numberOfAlertFields) {
+ TextField alertText1 = new TextField();
+ alertText1.setName(TextFieldName.alertText1);
+ TextField alertText2 = new TextField();
+ alertText2.setName(TextFieldName.alertText2);
+ TextField alertText3 = new TextField();
+ alertText3.setName(TextFieldName.alertText3);
+ TextField mainField4 = new TextField();
+ mainField4.setName(TextFieldName.mainField4);
+
+ List<TextField> textFieldList = new ArrayList<>();
+
+ textFieldList.add(alertText1);
+ textFieldList.add(alertText2);
+ textFieldList.add(alertText3);
+
+ List<TextField> returnList = new ArrayList<>();
+
+ if (numberOfAlertFields > 0) {
+ for (int i = 0; i < numberOfAlertFields; i++) {
+ returnList.add(textFieldList.get(i));
+ }
+ }
+
+ WindowCapability windowCapability = new WindowCapability();
+ windowCapability.setTextFields(returnList);
+
+ ImageField imageField = new ImageField();
+ imageField.setName(ImageFieldName.alertIcon);
+ List<ImageField> imageFieldList = new ArrayList<>();
+ imageFieldList.add(imageField);
+ windowCapability.setImageFields(imageFieldList);
+
+ windowCapability.setImageFields(imageFieldList);
+
+ SoftButtonCapabilities softButtonCapabilities = new SoftButtonCapabilities();
+ softButtonCapabilities.setImageSupported(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setShortPressAvailable(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setLongPressAvailable(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setUpDownAvailable(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setTextSupported(TestValues.GENERAL_BOOLEAN);
+
+ windowCapability.setSoftButtonCapabilities(Collections.singletonList(softButtonCapabilities));
+ return windowCapability;
+ }
+
+}
diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java
new file mode 100644
index 000000000..1283992a2
--- /dev/null
+++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/PresentAlertOperationTest.java
@@ -0,0 +1,315 @@
+package com.smartdevicelink.managers.screen;
+
+import android.content.Context;
+import android.net.Uri;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.managers.AlertCompletionListener;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.file.MultipleFileCompletionListener;
+import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.proxy.RPCRequest;
+import com.smartdevicelink.proxy.rpc.Alert;
+import com.smartdevicelink.proxy.rpc.AlertResponse;
+import com.smartdevicelink.proxy.rpc.CancelInteraction;
+import com.smartdevicelink.proxy.rpc.CancelInteractionResponse;
+import com.smartdevicelink.proxy.rpc.ImageField;
+import com.smartdevicelink.proxy.rpc.OnButtonEvent;
+import com.smartdevicelink.proxy.rpc.OnButtonPress;
+import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
+import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities;
+import com.smartdevicelink.proxy.rpc.TextField;
+import com.smartdevicelink.proxy.rpc.WindowCapability;
+import com.smartdevicelink.proxy.rpc.enums.FileType;
+import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+import com.smartdevicelink.proxy.rpc.enums.TextFieldName;
+import com.smartdevicelink.test.TestValues;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static junit.framework.TestCase.assertEquals;
+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.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(AndroidJUnit4.class)
+public class PresentAlertOperationTest {
+
+ private PresentAlertOperation presentAlertOperation;
+ private WindowCapability defaultMainWindowCapability;
+ private AlertView alertView;
+ private AlertAudioData alertAudioData;
+ SdlArtwork testAlertArtwork, testSoftButtonArtwork;
+ ISdl internalInterface;
+ FileManager fileManager;
+ SoftButtonState alertSoftButtonState;
+ SoftButtonObject alertSoftButtonObject;
+ private List<SpeechCapabilities> speechCapabilities;
+ SdlFile testAudio;
+ AlertCompletionListener alertCompletionListener;
+ BaseAlertManager.AlertSoftButtonClearListener alertSoftButtonClearListener;
+
+ private Answer<Void> onArtworkUploadSuccess = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ MultipleFileCompletionListener listener = (MultipleFileCompletionListener) args[1];
+ listener.onComplete(null);
+ return null;
+ }
+ };
+
+ private Answer<Void> onAlertSuccess = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ RPCRequest message = (RPCRequest) args[0];
+ if (message instanceof Alert) {
+ int correlationId = message.getCorrelationID();
+ AlertResponse alertResponse = new AlertResponse();
+ alertResponse.setSuccess(true);
+ message.getOnRPCResponseListener().onResponse(correlationId, alertResponse);
+ }
+ return null;
+ }
+ };
+
+ private Answer<Void> onCancelAlert = new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ RPCRequest message = (RPCRequest) args[0];
+ if (message instanceof CancelInteraction) {
+ int correlationId = message.getCorrelationID();
+ CancelInteractionResponse cancelInteraction = new CancelInteractionResponse();
+ cancelInteraction.setSuccess(true);
+ message.getOnRPCResponseListener().onResponse(correlationId, cancelInteraction);
+ }
+ return null;
+ }
+ };
+
+ Task task;
+ @Before
+ public void setUp() throws Exception {
+ Context mTestContext = getInstrumentation().getContext();
+ // mock things
+ internalInterface = mock(ISdl.class);
+ fileManager = mock(FileManager.class);
+ task = mock(Task.class);
+
+ alertSoftButtonClearListener = new BaseAlertManager.AlertSoftButtonClearListener() {
+ @Override
+ public void onButtonClear(List<SoftButtonObject> softButtonObjects) {
+
+ }
+ };
+ testAlertArtwork = new SdlArtwork();
+ testAlertArtwork.setName("testArtwork1");
+ Uri uri1 = Uri.parse("android.resource://" + mTestContext.getPackageName() + "/drawable/ic_sdl");
+ testAlertArtwork.setUri(uri1);
+ testAlertArtwork.setType(FileType.GRAPHIC_PNG);
+
+ testSoftButtonArtwork = new SdlArtwork();
+ Uri uri2 = Uri.parse("android.resource://" + mTestContext.getPackageName() + "drawable-hdpi/sdl_lockscreen_icon.png");
+ testSoftButtonArtwork.setName("testArtwork2");
+ testSoftButtonArtwork.setUri(uri2);
+ testSoftButtonArtwork.setType(FileType.GRAPHIC_PNG);
+
+ Uri uri3 = Uri.parse("android.resource://" + mTestContext.getPackageName() + "/raw/test_audio_square_250hz_80amp_1s.mp3");
+ testAudio = new SdlFile("TestAudioFile", FileType.AUDIO_MP3, uri3, false);
+
+ alertAudioData = new AlertAudioData("Spoken Sting");
+ alertAudioData.setPlayTone(true);
+ alertAudioData.addAudioFiles(Collections.singletonList(testAudio));
+
+ alertSoftButtonState = new SoftButtonState("state1", "State 1", testSoftButtonArtwork);
+ SoftButtonObject.OnEventListener onEventListener = new SoftButtonObject.OnEventListener() {
+ @Override
+ public void onPress(SoftButtonObject softButtonObject, OnButtonPress onButtonPress) {
+
+ }
+
+ @Override
+ public void onEvent(SoftButtonObject softButtonObject, OnButtonEvent onButtonEvent) {
+
+ }
+ };
+ alertSoftButtonObject = new SoftButtonObject("Soft button 1", alertSoftButtonState, onEventListener);
+
+ AlertView.Builder builder = new AlertView.Builder();
+ builder.setText("test");
+ builder.setSecondaryText("secondaryText");
+ builder.setTertiaryText("tertiaryText");
+ builder.setAudio(alertAudioData);
+ builder.setIcon(testAlertArtwork);
+ builder.setDefaultTimeOut(10);
+ builder.setTimeout(5);
+ builder.setSoftButtons(Collections.singletonList(alertSoftButtonObject));
+ builder.setShowWaitIndicator(true);
+ alertView = builder.build();
+
+ defaultMainWindowCapability = getWindowCapability(3);
+ speechCapabilities = new ArrayList<SpeechCapabilities>();
+ speechCapabilities.add(SpeechCapabilities.FILE);
+ alertCompletionListener = new AlertCompletionListener() {
+ @Override
+ public void onComplete(boolean success, Integer tryAgainTime) {
+
+ }
+ };
+ presentAlertOperation = new PresentAlertOperation(internalInterface, alertView, defaultMainWindowCapability, speechCapabilities, fileManager, 1, alertCompletionListener, alertSoftButtonClearListener);
+ when(fileManager.fileNeedsUpload(any(SdlFile.class))).thenReturn(true);
+ }
+
+ @Test
+ public void testPresentAlertTruncatedText() {
+ doAnswer(onAlertSuccess).when(internalInterface).sendRPC(any(Alert.class));
+ // Same response works for uploading artworks as it does for files
+
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+ WindowCapability windowCapability = getWindowCapability(1);
+ PresentAlertOperation presentAlertOperation = new PresentAlertOperation(internalInterface, alertView, windowCapability, speechCapabilities, fileManager, 1, alertCompletionListener, alertSoftButtonClearListener);
+ Alert alert = presentAlertOperation.alertRpc();
+
+ assertEquals(alert.getAlertText1(), alertView.getText() + " - " + alertView.getSecondaryText() + " - " + alertView.getTertiaryText());
+
+ windowCapability = getWindowCapability(2);
+
+ presentAlertOperation = new PresentAlertOperation(internalInterface, alertView, windowCapability, speechCapabilities, fileManager, 1, alertCompletionListener, alertSoftButtonClearListener);
+ alert = presentAlertOperation.alertRpc();
+ assertEquals(alert.getAlertText1(), alertView.getText());
+ assertEquals(alert.getAlertText2(),alertView.getSecondaryText() + " - " + alertView.getTertiaryText());
+ }
+
+ @Test
+ public void testPresentAlertHappyPath() {
+ doAnswer(onAlertSuccess).when(internalInterface).sendRPC(any(Alert.class));
+ // Same response works for uploading artworks as it does for files
+ doAnswer(onArtworkUploadSuccess).when(fileManager).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+ doAnswer(onArtworkUploadSuccess).when(fileManager).uploadFiles(any(List.class), any(MultipleFileCompletionListener.class));
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+
+ // Test Images need to be uploaded, sending text and uploading images
+ presentAlertOperation.onExecute();
+
+ // Verifies that uploadArtworks gets called only with the fist presentAlertOperation.onExecute call
+ verify(fileManager, times(1)).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+
+ verify(fileManager, times(1)).uploadFiles(any(List.class), any(MultipleFileCompletionListener.class));
+
+ verify(internalInterface, times(1)).sendRPC(any(Alert.class));
+ }
+
+ @Test
+ public void testPresentAlertNoAudioAndArtwork() {
+ doAnswer(onAlertSuccess).when(internalInterface).sendRPC(any(Alert.class));
+
+ AlertView.Builder builder = new AlertView.Builder();
+ builder.setText("Hi");
+ builder.build();
+ AlertView alertView1 = builder.build();
+
+ presentAlertOperation = new PresentAlertOperation(internalInterface, alertView1, defaultMainWindowCapability, speechCapabilities, fileManager, 2, alertCompletionListener, alertSoftButtonClearListener);
+
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+
+ // Test Images need to be uploaded, sending text and uploading images
+ presentAlertOperation.onExecute();
+
+ // Verifies that uploadArtworks gets called only with the fist presentAlertOperation.onExecute call
+ verify(fileManager, times(0)).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+ verify(fileManager, times(0)).uploadFiles(any(List.class), any(MultipleFileCompletionListener.class));
+
+ verify(internalInterface, times(1)).sendRPC(any(Alert.class));
+ }
+
+ @Test
+ public void testPresentAlertNoImages() {
+ doAnswer(onAlertSuccess).when(internalInterface).sendRPC(any(Alert.class));
+ // Same response works for uploading artworks as it does for files
+ doAnswer(onArtworkUploadSuccess).when(fileManager).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+ doAnswer(onArtworkUploadSuccess).when(fileManager).uploadFiles(any(List.class), any(MultipleFileCompletionListener.class));
+
+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0));
+
+ // Test Images need to be uploaded, sending text and uploading images
+ presentAlertOperation.onExecute();
+
+ // Verifies that uploadArtworks gets called only with the fist presentAlertOperation.onExecute call
+ verify(fileManager, times(1)).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class));
+ verify(internalInterface, times(1)).sendRPC(any(Alert.class));
+ }
+
+ @Test
+ public void testCancelOperation() {
+ //Cancel right away
+ presentAlertOperation.cancelTask();
+ presentAlertOperation.onExecute();
+ verify(internalInterface, times(0)).sendRPC(any(Alert.class));
+ }
+
+ private WindowCapability getWindowCapability(int numberOfAlertFields) {
+ TextField alertText1 = new TextField();
+ alertText1.setName(TextFieldName.alertText1);
+ TextField alertText2 = new TextField();
+ alertText2.setName(TextFieldName.alertText2);
+ TextField alertText3 = new TextField();
+ alertText3.setName(TextFieldName.alertText3);
+ TextField mainField4 = new TextField();
+ mainField4.setName(TextFieldName.mainField4);
+
+ List<TextField> textFieldList = new ArrayList<>();
+
+ textFieldList.add(alertText1);
+ textFieldList.add(alertText2);
+ textFieldList.add(alertText3);
+
+ List<TextField> returnList = new ArrayList<>();
+
+ if (numberOfAlertFields > 0) {
+ for (int i = 0; i < numberOfAlertFields; i++) {
+ returnList.add(textFieldList.get(i));
+ }
+ }
+
+ WindowCapability windowCapability = new WindowCapability();
+ windowCapability.setTextFields(returnList);
+
+ ImageField imageField = new ImageField();
+ imageField.setName(ImageFieldName.alertIcon);
+ List<ImageField> imageFieldList = new ArrayList<>();
+ imageFieldList.add(imageField);
+ windowCapability.setImageFields(imageFieldList);
+
+ windowCapability.setImageFields(imageFieldList);
+
+ SoftButtonCapabilities softButtonCapabilities = new SoftButtonCapabilities();
+ softButtonCapabilities.setImageSupported(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setShortPressAvailable(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setLongPressAvailable(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setUpDownAvailable(TestValues.GENERAL_BOOLEAN);
+ softButtonCapabilities.setTextSupported(TestValues.GENERAL_BOOLEAN);
+
+ windowCapability.setSoftButtonCapabilities(Collections.singletonList(softButtonCapabilities));
+ return windowCapability;
+ }
+}
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 627a900f6..6ccf93aeb 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
@@ -7,6 +7,7 @@ import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.file.FileManager;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdatesMode;
import com.smartdevicelink.proxy.rpc.enums.FileType;
import com.smartdevicelink.proxy.rpc.enums.MetadataType;
@@ -18,9 +19,11 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertNull;
import static junit.framework.TestCase.assertTrue;
import static org.mockito.Mockito.mock;
@@ -41,6 +44,9 @@ public class ScreenManagerTests {
ISdl internalInterface = mock(ISdl.class);
when(internalInterface.getTaskmaster()).thenReturn(new Taskmaster.Builder().build());
+ PermissionManager permissionManager = mock(PermissionManager.class);
+
+ when(internalInterface.getPermissionManager()).thenReturn(permissionManager);
FileManager fileManager = mock(FileManager.class);
screenManager = new ScreenManager(internalInterface, fileManager);
screenManager.start(null);
@@ -174,4 +180,91 @@ public class ScreenManagerTests {
assertEquals(screenManager.getSoftButtonObjectById(200), softButtonObject2);
}
+ @Test
+ public void testSettingSoftButtonId() {
+ // Create softButtonObject1
+ SoftButtonState softButtonState1 = new SoftButtonState("object1-state1", "it is", testArtwork);
+ SoftButtonState softButtonState2 = new SoftButtonState("object1-state2", "Wed", testArtwork);
+ SoftButtonObject softButtonObject1 = new SoftButtonObject("object1", Arrays.asList(softButtonState1, softButtonState2), softButtonState1.getName(), null);
+ softButtonObject1.setButtonId(100);
+
+ // Create softButtonObject2
+ SoftButtonState softButtonState3 = new SoftButtonState("object2-state1", "my", testArtwork);
+ SoftButtonState softButtonState4 = new SoftButtonState("object2-state2", "dudes!", null);
+ SoftButtonObject softButtonObject2 = new SoftButtonObject("object2", Arrays.asList(softButtonState3, softButtonState4), softButtonState3.getName(), null);
+ softButtonObject2.setButtonId(200);
+
+ List<SoftButtonObject> softButtonObjects = Arrays.asList(softButtonObject1, softButtonObject2);
+ assertTrue(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER));
+ screenManager.softButtonIDBySoftButtonManager.add(200);
+ assertFalse(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.ALERT_MANAGER));
+ screenManager.softButtonIDByAlertManager.add(100);
+ assertFalse(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER));
+ screenManager.softButtonIDByAlertManager.clear();
+ screenManager.softButtonIDBySoftButtonManager.clear();
+ assertTrue(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.ALERT_MANAGER));
+ softButtonObject1.setButtonId(400);
+ softButtonObject2.setButtonId(500);
+ assertTrue(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER));
+ SoftButtonObject softButtonObject3 = new SoftButtonObject("object1", Arrays.asList(softButtonState1, softButtonState2), softButtonState1.getName(), null);
+ SoftButtonObject softButtonObject4 = new SoftButtonObject("object2", Arrays.asList(softButtonState3, softButtonState4), softButtonState3.getName(), null);
+ assertTrue(screenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER));
+
+
+
+
+
+ }
+ @Test
+ public void testAssigningIdsToSoftButtonObjects() {
+ SoftButtonObject sbo1, sbo2, sbo3, sbo4, sbo5;
+
+ // Case 1 - don't set id for any button (Manager should set ids automatically starting from 1 and up)
+ sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ screenManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5), BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER);
+ assertEquals("SoftButtonObject id doesn't match the expected value", 1, sbo1.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 2, sbo2.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 3, sbo3.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 4, sbo4.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 5, sbo5.getButtonId());
+
+
+ // Case 2 - Set ids for all buttons (Manager shouldn't alter the ids set by developer)
+ sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo1.setButtonId(100);
+ sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo2.setButtonId(200);
+ sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo3.setButtonId(300);
+ sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo4.setButtonId(400);
+ sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo5.setButtonId(500);
+ screenManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5), BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER);
+ assertEquals("SoftButtonObject id doesn't match the expected value", 100, sbo1.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 200, sbo2.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 300, sbo3.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 400, sbo4.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 500, sbo5.getButtonId());
+
+
+ // Case 3 - Set ids for some buttons (Manager shouldn't alter the ids set by developer. And it should assign ids for the ones that don't have id)
+ sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo1.setButtonId(50);
+ sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ sbo4.setButtonId(100);
+ sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
+ screenManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5), BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER);
+ assertEquals("SoftButtonObject id doesn't match the expected value", 50, sbo1.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 101, sbo2.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 102, sbo3.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 100, sbo4.getButtonId());
+ assertEquals("SoftButtonObject id doesn't match the expected value", 103, sbo5.getButtonId());
+ }
}
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 7719e2996..becc01a74 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
@@ -304,59 +304,6 @@ public class SoftButtonManagerTests {
assertEquals(softButtonState1, softButtonObject1.getCurrentState());
}
- @Test
- public void testAssigningIdsToSoftButtonObjects() {
- SoftButtonObject sbo1, sbo2, sbo3, sbo4, sbo5;
-
- // Case 1 - don't set id for any button (Manager should set ids automatically starting from 1 and up)
- sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- softButtonManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5));
- assertEquals("SoftButtonObject id doesn't match the expected value", 1, sbo1.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 2, sbo2.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 3, sbo3.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 4, sbo4.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 5, sbo5.getButtonId());
-
-
- // Case 2 - Set ids for all buttons (Manager shouldn't alter the ids set by developer)
- sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo1.setButtonId(100);
- sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo2.setButtonId(200);
- sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo3.setButtonId(300);
- sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo4.setButtonId(400);
- sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo5.setButtonId(500);
- softButtonManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5));
- assertEquals("SoftButtonObject id doesn't match the expected value", 100, sbo1.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 200, sbo2.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 300, sbo3.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 400, sbo4.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 500, sbo5.getButtonId());
-
-
- // Case 3 - Set ids for some buttons (Manager shouldn't alter the ids set by developer. And it should assign ids for the ones that don't have id)
- sbo1 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo1.setButtonId(50);
- sbo2 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo3 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo4 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- sbo4.setButtonId(100);
- sbo5 = new SoftButtonObject(null, Collections.EMPTY_LIST, null, null);
- softButtonManager.checkAndAssignButtonIds(Arrays.asList(sbo1, sbo2, sbo3, sbo4, sbo5));
- assertEquals("SoftButtonObject id doesn't match the expected value", 50, sbo1.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 101, sbo2.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 102, sbo3.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 100, sbo4.getButtonId());
- assertEquals("SoftButtonObject id doesn't match the expected value", 103, sbo5.getButtonId());
- }
-
/**
* Test custom overridden softButtonObject equals method
*/
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 09adbbaa6..0c5c47cc9 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
@@ -40,19 +40,33 @@ import android.os.Looper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.audio.AudioStreamManager;
import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
import com.smartdevicelink.managers.lockscreen.LockScreenConfig;
import com.smartdevicelink.managers.lockscreen.LockScreenManager;
import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.managers.screen.ScreenManager;
import com.smartdevicelink.managers.video.VideoStreamManager;
+import com.smartdevicelink.protocol.ISdlServiceListener;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.protocol.enums.SessionType;
+import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.rpc.RegisterAppInterfaceResponse;
+import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
import com.smartdevicelink.proxy.rpc.enums.AppHMIType;
+import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCRequestListener;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
import com.smartdevicelink.transport.BaseTransportConfig;
import com.smartdevicelink.transport.MultiplexTransportConfig;
import com.smartdevicelink.transport.enums.TransportType;
import com.smartdevicelink.transport.utl.TransportRecord;
import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.Version;
import java.util.List;
@@ -374,4 +388,133 @@ public class SdlManager extends BaseSdlManager {
return sdlManager;
}
}
+
+ ISdl _internalInterface = new ISdl() {
+ @Override
+ public void start() {
+ lifecycleManager.getInternalInterface(SdlManager.this).start();
+ }
+
+ @Override
+ public void stop() {
+ lifecycleManager.getInternalInterface(SdlManager.this).start();
+ }
+
+ @Override
+ public boolean isConnected() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).isConnected();
+ }
+
+ @Override
+ public void addServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addServiceListener(serviceType, sdlServiceListener);
+ }
+
+ @Override
+ public void removeServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).removeServiceListener(serviceType, sdlServiceListener);
+ }
+
+ @Override
+ public void startVideoService(VideoStreamingParameters parameters, boolean encrypted) {
+ lifecycleManager.getInternalInterface(SdlManager.this).startVideoService(parameters,encrypted);
+ }
+
+ @Override
+ public void startAudioService(boolean encrypted) {
+ lifecycleManager.getInternalInterface(SdlManager.this).startAudioService(encrypted);
+ }
+
+ @Override
+ public void sendRPC(RPCMessage message) {
+ lifecycleManager.getInternalInterface(SdlManager.this).sendRPC(message);
+ }
+
+ @Override
+ public void sendRPCs(List<? extends RPCMessage> rpcs, OnMultipleRequestListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).sendRPCs(rpcs, listener);
+ }
+
+ @Override
+ public void sendSequentialRPCs(List<? extends RPCMessage> rpcs, OnMultipleRequestListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).sendSequentialRPCs(rpcs, listener);
+ }
+
+ @Override
+ public void addOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addOnRPCNotificationListener(notificationId, listener);
+ }
+
+ @Override
+ public boolean removeOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCNotificationListener(notificationId, listener);
+ }
+
+ @Override
+ public void addOnRPCRequestListener(FunctionID functionID, OnRPCRequestListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addOnRPCRequestListener(functionID, listener);
+ }
+
+ @Override
+ public boolean removeOnRPCRequestListener(FunctionID functionID, OnRPCRequestListener listener) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCRequestListener(functionID, listener);
+ }
+
+ @Override
+ public void addOnRPCListener(FunctionID responseId, OnRPCListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addOnRPCListener(responseId, listener);
+ }
+
+ @Override
+ public boolean removeOnRPCListener(FunctionID responseId, OnRPCListener listener) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCListener(responseId, listener);
+ }
+
+ @Override
+ public RegisterAppInterfaceResponse getRegisterAppInterfaceResponse() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getRegisterAppInterfaceResponse();
+ }
+
+ @Override
+ public boolean isTransportForServiceAvailable(SessionType serviceType) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).isTransportForServiceAvailable(serviceType);
+ }
+
+ @NonNull
+ @Override
+ public SdlMsgVersion getSdlMsgVersion() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getSdlMsgVersion();
+ }
+
+ @NonNull
+ @Override
+ public Version getProtocolVersion() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getProtocolVersion();
+ }
+
+ @Override
+ public long getMtu(SessionType serviceType) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getMtu(serviceType);
+ }
+
+ @Override
+ public void startRPCEncryption() {
+ lifecycleManager.getInternalInterface(SdlManager.this).startRPCEncryption();
+ }
+
+ @Override
+ public Taskmaster getTaskmaster() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getTaskmaster();
+ }
+
+ @Override
+ public SystemCapabilityManager getSystemCapabilityManager() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getSystemCapabilityManager();
+ }
+
+ @Override
+ public PermissionManager getPermissionManager() {
+ return permissionManager;
+ }
+ };
}
diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java
new file mode 100644
index 000000000..c0a5d92e2
--- /dev/null
+++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java
@@ -0,0 +1,53 @@
+/*
+ * 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.managers.screen;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.file.FileManager;
+
+/**
+ * <strong>AlertManager</strong> <br>
+ * <p>
+ * Alert manager handles uploading images and audio needed by an alert, sending an alert and cancelling an alert.<br>
+ * Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br>
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+class AlertManager extends BaseAlertManager {
+
+ public AlertManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
+ super(internalInterface, fileManager);
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/AlertCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/AlertCompletionListener.java
new file mode 100644
index 000000000..7801632ba
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/AlertCompletionListener.java
@@ -0,0 +1,11 @@
+package com.smartdevicelink.managers;
+
+public interface AlertCompletionListener {
+
+ /**
+ * Returns whether an Alert operation was successful or not along with tryAgainTime
+ * @param success - Boolean that is True if Operation was a success, False otherwise.
+ * @param tryAgainTime - Amount of time (in seconds) that an app must wait before resending an alert.
+ */
+ void onComplete(boolean success, Integer tryAgainTime);
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
index 6730ee3eb..24b0d8455 100644
--- a/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/BaseSdlManager.java
@@ -32,6 +32,7 @@
package com.smartdevicelink.managers;
import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
import com.smartdevicelink.managers.file.FileManager;
import com.smartdevicelink.managers.file.FileManagerConfig;
@@ -871,4 +872,9 @@ abstract class BaseSdlManager {
lifecycleManager.startRPCEncryption();
}
}
+
+ @RestrictTo(RestrictTo.Scope.TESTS)
+ void set_internalInterface(ISdl _internalInterface) {
+ this._internalInterface = _internalInterface;
+ }
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/ISdl.java b/base/src/main/java/com/smartdevicelink/managers/ISdl.java
index e12880561..785b5649e 100644
--- a/base/src/main/java/com/smartdevicelink/managers/ISdl.java
+++ b/base/src/main/java/com/smartdevicelink/managers/ISdl.java
@@ -4,6 +4,7 @@ import androidx.annotation.NonNull;
import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
+import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.protocol.ISdlServiceListener;
import com.smartdevicelink.protocol.enums.FunctionID;
import com.smartdevicelink.protocol.enums.SessionType;
@@ -227,4 +228,6 @@ public interface ISdl {
Taskmaster getTaskmaster();
SystemCapabilityManager getSystemCapabilityManager();
+
+ PermissionManager getPermissionManager();
}
diff --git a/base/src/main/java/com/smartdevicelink/managers/ManagerUtility.java b/base/src/main/java/com/smartdevicelink/managers/ManagerUtility.java
index b5ad249bb..6b5297e21 100644
--- a/base/src/main/java/com/smartdevicelink/managers/ManagerUtility.java
+++ b/base/src/main/java/com/smartdevicelink/managers/ManagerUtility.java
@@ -108,6 +108,41 @@ public class ManagerUtility {
}
/**
+ * Method to get number of alert textFields allowed to be set according to WindowCapability
+ *
+ * @param windowCapability WindowCapability representing the capabilities of the desired window
+ * @return linesFound Number of alert textFields found in WindowCapability
+ */
+ public static int getMaxNumberOfAlertFieldLines(final WindowCapability windowCapability) {
+ int highestFound = 0;
+ if (windowCapability != null && windowCapability.getTextFields() != null) {
+ for (TextField field : windowCapability.getTextFields()) {
+ int fieldNumber = 0;
+ if (field != null && field.getName() != null) {
+ switch (field.getName()) {
+ case alertText1:
+ fieldNumber = 1;
+ break;
+ case alertText2:
+ fieldNumber = 2;
+ break;
+ case alertText3:
+ fieldNumber = 3;
+ break;
+ }
+ }
+ if (fieldNumber > 0) {
+ highestFound = Math.max(highestFound, fieldNumber);
+ if (highestFound == 3) {
+ break;
+ }
+ }
+ }
+ }
+ return highestFound;
+ }
+
+ /**
* Method to get a list of all available text fields
*
* @return list of all available text fields with CID1SET Character Set
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 280043a8a..b3c59afb1 100644
--- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java
@@ -40,6 +40,7 @@ import com.smartdevicelink.exception.SdlException;
import com.smartdevicelink.managers.ISdl;
import com.smartdevicelink.managers.SdlManager;
import com.smartdevicelink.managers.ServiceEncryptionListener;
+import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.marshal.JsonRPCMarshaller;
import com.smartdevicelink.protocol.ISdlServiceListener;
import com.smartdevicelink.protocol.ProtocolMessage;
@@ -1099,6 +1100,11 @@ abstract class BaseLifecycleManager {
public SystemCapabilityManager getSystemCapabilityManager() {
return BaseLifecycleManager.this.systemCapabilityManager;
}
+
+ @Override
+ public PermissionManager getPermissionManager() {
+ return null;
+ }
};
/* *******************************************************************************************************
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/AlertAudioData.java b/base/src/main/java/com/smartdevicelink/managers/screen/AlertAudioData.java
new file mode 100644
index 000000000..c8c250ca5
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/AlertAudioData.java
@@ -0,0 +1,82 @@
+/*
+ * 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.managers.screen;
+
+import androidx.annotation.NonNull;
+
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+import com.smartdevicelink.util.DebugTool;
+
+public class AlertAudioData extends AudioData implements Cloneable {
+ // Whether the alert tone should be played before the prompt (if any) is spoken. Defaults to false
+ private boolean playTone;
+
+ public AlertAudioData(@NonNull SdlFile audioFile) {
+ super(audioFile);
+ }
+
+ public AlertAudioData(@NonNull String spokenString) {
+ super(spokenString);
+ }
+
+ public AlertAudioData(@NonNull String phoneticString, @NonNull SpeechCapabilities phoneticType) {
+ super(phoneticString, phoneticType);
+ }
+
+ public boolean isPlayTone() {
+ return playTone;
+ }
+
+ public void setPlayTone(boolean playTone) {
+ this.playTone = playTone;
+ }
+
+ /**
+ * Creates a deep copy of the object
+ *
+ * @return deep copy of the object, null if an exception occurred
+ */
+ @Override
+ public AlertAudioData clone() {
+ try {
+ AlertAudioData alertAudioData = (AlertAudioData) super.clone();
+ return alertAudioData;
+ } catch (CloneNotSupportedException e) {
+ if (DebugTool.isDebugEnabled()) {
+ throw new RuntimeException("Clone not supported by super class");
+ }
+ }
+ return null;
+ }
+} \ No newline at end of file
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/AlertCanceledListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/AlertCanceledListener.java
new file mode 100644
index 000000000..ac65c2035
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/AlertCanceledListener.java
@@ -0,0 +1,40 @@
+/*
+ * 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.managers.screen;
+
+public interface AlertCanceledListener {
+ /**
+ * Notifies the subscriber that the Alert should be cancelled.
+ */
+ void onAlertCanceled();
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/AlertView.java b/base/src/main/java/com/smartdevicelink/managers/screen/AlertView.java
new file mode 100644
index 000000000..4e009147b
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/AlertView.java
@@ -0,0 +1,291 @@
+/*
+ * 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.managers.screen;
+
+import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.util.DebugTool;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AlertView implements Cloneable {
+
+ private static Integer defaultTimeout = 5;
+ private static final int TIMEOUT_MIN = 3;
+ private static final int TIMEOUT_MAX = 10;
+ private String text, secondaryText, tertiaryText;
+ private Integer timeout;
+ private AlertAudioData audio;
+ private boolean showWaitIndicator;
+ private List<SoftButtonObject> softButtons;
+ private SdlArtwork icon;
+ AlertCanceledListener canceledListener;
+
+
+ private AlertView() {
+ this.timeout = defaultTimeout;
+ }
+
+ public static class Builder {
+
+ AlertView alertView;
+
+ public Builder() {
+ alertView = new AlertView();
+ }
+
+ /**
+ * The primary line of text for display on the alert. If fewer than three alert lines are available
+ * on the head unit, the screen manager will automatically concatenate some of the lines together.
+ */
+ public Builder setText(String text) {
+ this.alertView.text = text;
+ return this;
+ }
+
+ /**
+ * The secondary line of text for display on the alert. If fewer than three alert lines are available
+ * on the head unit, the screen manager will automatically concatenate some of the lines together.
+ */
+ public Builder setSecondaryText(String secondaryText) {
+ alertView.secondaryText = secondaryText;
+ return this;
+ }
+
+ /**
+ * The tertiary line of text for display on the alert. If fewer than three alert lines are available
+ * on the head unit, the screen manager will automatically concatenate some of the lines together.
+ */
+ public Builder setTertiaryText(String tertiaryText) {
+ alertView.tertiaryText = tertiaryText;
+ return this;
+ }
+
+ /**
+ * Timeout in seconds. Defaults to 0, which will use `defaultTimeout`. If this is set below the
+ * minimum, it will be capped at 3 seconds. Minimum 3 seconds, maximum 10 seconds. If this is
+ * set above the maximum, it will be capped at 10 seconds. Defaults to 0.
+ *
+ * Please note that if a button is added to the alert, the defaultTimeout and timeout values will be ignored.
+ */
+ public Builder setTimeout(Integer timeout) {
+ alertView.timeout = timeout;
+ return this;
+ }
+
+ /**
+ * If supported, the alert GUI will display some sort of indefinite waiting / refresh / loading
+ * indicator animation. Defaults to NO.
+ */
+ public Builder setShowWaitIndicator(boolean showWaitIndicator) {
+ alertView.showWaitIndicator = showWaitIndicator;
+ return this;
+ }
+
+ /**
+ * Soft buttons the user may select to perform actions. Only one `SoftButtonState` per object
+ * is supported; if any soft button object contains multiple states, an exception will be thrown.
+ */
+ public Builder setSoftButtons(List<SoftButtonObject> softButtons) {
+ alertView.setSoftButtons(softButtons);
+ return this;
+ }
+
+ /**
+ * Text spoken, file(s) played, and/or tone played when the alert appears
+ */
+ public Builder setAudio(AlertAudioData audio) {
+ alertView.audio = audio;
+ return this;
+ }
+
+ /**
+ * An artwork that will be displayed when the icon appears. This will be uploaded prior to the
+ * appearance of the alert if necessary. This will not be uploaded if the head unit does not
+ * declare support for alertIcon.
+ */
+ public Builder setIcon(SdlArtwork icon) {
+ alertView.icon = icon;
+ return this;
+ }
+
+ /**
+ * Set this to change the default timeout for all alerts. If a timeout is not set on an individual
+ * alert object (or if it is set to 0.0), then it will use this timeout instead. See `timeout`
+ * for more details. If this is not set by you, it will default to 5 seconds. The minimum is
+ * 3 seconds, the maximum is 10 seconds. If this is set below the minimum, it will be capped
+ * at 3 seconds. If this is set above the maximum, it will be capped at 10 seconds.
+ */
+ public Builder setDefaultTimeOut(int defaultTimeOut) {
+ alertView.setDefaultTimeout(defaultTimeOut);
+ return this;
+ }
+
+ public AlertView build() {
+ return alertView;
+ }
+ }
+
+ // Notifies the subscriber that the alert should be cancelled.
+ public void cancel() {
+ if (canceledListener == null) {
+ return;
+ }
+ canceledListener.onAlertCanceled();
+ }
+
+ public Integer getTimeout() {
+ if (timeout == null) {
+ timeout = defaultTimeout;
+ } else if (timeout < TIMEOUT_MIN) {
+ return TIMEOUT_MIN;
+ } else if (timeout > TIMEOUT_MAX) {
+ return TIMEOUT_MAX;
+ }
+ return timeout;
+ }
+
+ public AlertAudioData getAudio() {
+ return audio;
+ }
+
+ public void setAudio(AlertAudioData audio) {
+ this.audio = audio;
+ }
+
+ public String getText() {
+ return text;
+ }
+
+ public void setText(String text) {
+ this.text = text;
+ }
+
+ public boolean isShowWaitIndicator() {
+ return showWaitIndicator;
+ }
+
+ public void setShowWaitIndicator(boolean showWaitIndicator) {
+ this.showWaitIndicator = showWaitIndicator;
+ }
+
+ public String getSecondaryText() {
+ return secondaryText;
+ }
+
+ public void setSecondaryText(String secondaryText) {
+ this.secondaryText = secondaryText;
+ }
+
+ public String getTertiaryText() {
+ return tertiaryText;
+ }
+
+ public void setTertiaryText(String tertiaryText) {
+ this.tertiaryText = tertiaryText;
+ }
+
+ public List<SoftButtonObject> getSoftButtons() {
+ return softButtons;
+ }
+
+ public void setSoftButtons(List<SoftButtonObject> softButtons) {
+ for (SoftButtonObject softButtonObject : softButtons) {
+ if (softButtonObject.getStates().size() != 1) {
+ throw new IllegalArgumentException("Attempting create a soft button for an Alert with more than one state. Alerts only support soft buttons with one state");
+ }
+ }
+ this.softButtons = softButtons;
+ }
+
+ public SdlArtwork getIcon() {
+ return icon;
+ }
+
+ public void setIcon(SdlArtwork icon) {
+ this.icon = icon;
+ }
+
+ public int getDefaultTimeout() {
+ return defaultTimeout;
+ }
+
+ public void setDefaultTimeout(int defaultTimeout) {
+ if (defaultTimeout <= TIMEOUT_MIN) {
+ AlertView.defaultTimeout = TIMEOUT_MIN;
+ return;
+ } else if (defaultTimeout >= TIMEOUT_MAX) {
+ AlertView.defaultTimeout = TIMEOUT_MAX;
+ return;
+ }
+ AlertView.defaultTimeout = defaultTimeout;
+ }
+
+ public void setTimeout(int timeout) {
+ this.timeout = timeout;
+ }
+
+ /**
+ * Creates a deep copy of the object
+ *
+ * @return deep copy of the object, null if an exception occurred
+ */
+ @Override
+ public AlertView clone() {
+ try {
+ AlertView alertView = (AlertView) super.clone();
+ if (alertView != null) {
+ if (alertView.getAudio() != null) {
+ alertView.audio = audio.clone();
+ }
+ if (alertView.getSoftButtons() != null) {
+ List<SoftButtonObject> softButtonObjectList = new ArrayList<>();
+ for (int i = 0; i < alertView.softButtons.size(); i++) {
+ SoftButtonObject cloneSoftButton = alertView.softButtons.get(i).clone();
+ softButtonObjectList.add(cloneSoftButton);
+ }
+ alertView.softButtons = softButtonObjectList;
+ }
+ if (alertView.icon != null) {
+ alertView.icon = icon.clone();
+ }
+ }
+ return alertView;
+ } catch (CloneNotSupportedException e) {
+ if (DebugTool.isDebugEnabled()) {
+ throw new RuntimeException("Clone not supported by super class");
+ }
+ }
+ return null;
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/AudioData.java b/base/src/main/java/com/smartdevicelink/managers/screen/AudioData.java
new file mode 100644
index 000000000..18aa110e3
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/AudioData.java
@@ -0,0 +1,168 @@
+/*
+ * 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.managers.screen;
+
+import androidx.annotation.NonNull;
+
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.proxy.rpc.TTSChunk;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+
+public class AudioData {
+
+ // All audio data
+ private List<TTSChunk> audioData;
+
+ // The audio files that will be uploaded.
+ private HashMap<String, SdlFile> audioFiles;
+
+ public AudioData(@NonNull SdlFile audioFile) {
+ this.audioFiles = new HashMap<>();
+ this.audioData = new ArrayList<>();
+ audioFiles.put(audioFile.getName(), audioFile);
+ audioData.add(new TTSChunk(audioFile.getName(), SpeechCapabilities.FILE));
+ }
+
+ public AudioData(@NonNull String spokenString) {
+ this.audioData = new ArrayList<>();
+ audioData.add(new TTSChunk(spokenString, SpeechCapabilities.TEXT));
+ }
+
+ public AudioData(@NonNull String phoneticString, @NonNull SpeechCapabilities phoneticType) {
+ if (!isValidPhoneticType(phoneticType)) {
+ return;
+ }
+ this.audioData = new ArrayList<>();
+ audioData.add(new TTSChunk(phoneticString, phoneticType));
+ }
+
+ /**
+ * Checks if the phonetic type can be used to create a text-to-speech string.
+ *
+ * @param phoneticType The phonetic type of the text-to-speech string
+ * @return True if the phoneticType is of type `SAPI_PHONEMES`, `LHPLUS_PHONEMES`, `TEXT`, or `PRE_RECORDED`; false if not.
+ */
+ boolean isValidPhoneticType(SpeechCapabilities phoneticType) {
+ if (!(phoneticType.equals(SpeechCapabilities.SAPI_PHONEMES) || phoneticType.equals(SpeechCapabilities.LHPLUS_PHONEMES)
+ || phoneticType.equals(SpeechCapabilities.TEXT) || phoneticType.equals(SpeechCapabilities.PRE_RECORDED))) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Add additional SDLFiles holding data or pointing to a file on the file system. When this object
+ * is passed to an `Alert` or `Speak`, the file will be uploaded if it is not already, then played
+ * if the system supports that feature.
+ *
+ * @param audioFiles A list of audio file to be played by the system
+ */
+ public void addAudioFiles(@NonNull List<SdlFile> audioFiles) {
+ if (this.audioFiles == null) {
+ this.audioFiles = new HashMap<>();
+ }
+ if (this.audioData == null) {
+ this.audioData = new ArrayList<>();
+ }
+ for (SdlFile file : audioFiles) {
+ audioData.add(new TTSChunk(file.getName(), SpeechCapabilities.FILE));
+ this.audioFiles.put(file.getName(), file);
+ }
+ }
+
+ /**
+ * Create additional strings to be spoken by the system speech synthesizer.
+ *
+ * @param spokenString The strings to be spoken by the system speech synthesizer
+ */
+ public void addSpeechSynthesizerStrings(@NonNull List<String> spokenString) {
+ if (spokenString.size() == 0) {
+ return;
+ }
+ List<TTSChunk> newPrompts = new ArrayList<>();
+ for (String spoken : spokenString) {
+ if (spoken.length() == 0) {
+ continue;
+ }
+ newPrompts.add(new TTSChunk().setText(spoken).setType(SpeechCapabilities.TEXT));
+ }
+ if (newPrompts.size() == 0) {
+ return;
+ }
+ if (audioData == null) {
+ this.audioData = newPrompts;
+ return;
+ }
+ audioData.addAll(newPrompts);
+ }
+
+ /**
+ * Create additional strings to be spoken by the system speech synthesizer using a phonetic string.
+ *
+ * @param phoneticString The strings to be spoken by the system speech synthesizer
+ * @param phoneticType Must be one of `SAPI_PHONEMES`, `LHPLUS_PHONEMES`, `TEXT`, or `PRE_RECORDED` or no object will be created
+ */
+ public void addPhoneticSpeechSynthesizerStrings(@NonNull List<String> phoneticString, @NonNull SpeechCapabilities phoneticType) {
+ if (!(isValidPhoneticType(phoneticType)) || phoneticString.size() == 0) {
+ return;
+ }
+ List<TTSChunk> newPrompts = new ArrayList<>();
+ for (String phonetic : phoneticString) {
+ if (phonetic.length() == 0) {
+ continue;
+ }
+ newPrompts.add(new TTSChunk(phonetic, phoneticType));
+ }
+ if (newPrompts.size() == 0) {
+ return;
+ }
+ if (audioData == null) {
+ this.audioData = newPrompts;
+ return;
+ }
+ audioData.addAll(newPrompts);
+ }
+
+ HashMap<String, SdlFile> getAudioFiles() {
+ return audioFiles;
+ }
+
+ public List<TTSChunk> getAudioData() {
+ return audioData;
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java
new file mode 100644
index 000000000..56aec5c19
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java
@@ -0,0 +1,342 @@
+/*
+ * 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.managers.screen;
+
+import androidx.annotation.NonNull;
+
+import com.livio.taskmaster.Queue;
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.managers.AlertCompletionListener;
+import com.smartdevicelink.managers.BaseSubManager;
+import com.smartdevicelink.managers.CompletionListener;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.lifecycle.OnSystemCapabilityListener;
+import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
+import com.smartdevicelink.managers.permission.OnPermissionChangeListener;
+import com.smartdevicelink.managers.permission.PermissionElement;
+import com.smartdevicelink.managers.permission.PermissionStatus;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.RPCNotification;
+import com.smartdevicelink.proxy.rpc.DisplayCapability;
+import com.smartdevicelink.proxy.rpc.OnButtonEvent;
+import com.smartdevicelink.proxy.rpc.OnButtonPress;
+import com.smartdevicelink.proxy.rpc.WindowCapability;
+import com.smartdevicelink.proxy.rpc.enums.ButtonName;
+import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
+import com.smartdevicelink.util.DebugTool;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+
+abstract class BaseAlertManager extends BaseSubManager {
+
+ private static final String TAG = "BaseAlertManager";
+ Queue transactionQueue;
+ WindowCapability currentWindowCapability;
+ private OnSystemCapabilityListener onSpeechCapabilityListener, onDisplaysCapabilityListener;
+ List<SpeechCapabilities> speechCapabilities;
+ private UUID permissionListener;
+ boolean isAlertRPCAllowed = false;
+ private final WeakReference<FileManager> fileManager;
+ int nextCancelId;
+ private final int alertCancelIdMin = 1;
+ private final int alertCancelIdMax = 100;
+ private List<SoftButtonObject> softButtonObjects;
+ OnRPCNotificationListener onButtonPressListener, onButtonEventListener;
+
+
+ public BaseAlertManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
+ super(internalInterface);
+ this.transactionQueue = newTransactionQueue();
+ this.fileManager = new WeakReference<>(fileManager);
+ nextCancelId = 0;
+ this.softButtonObjects = new ArrayList<>();
+ addListeners();
+ }
+
+ /**
+ * Starts the manager
+ * @param listener CompletionListener that is called once the BaseSubManager's state is READY, LIMITED, or ERROR
+ */
+ @Override
+ public void start(CompletionListener listener) {
+ transitionToState(READY);
+ super.start(listener);
+ }
+
+ /**
+ * Clean up everything after the manager is no longer needed
+ */
+ @Override
+ public void dispose() {
+ currentWindowCapability = null;
+ speechCapabilities = null;
+ isAlertRPCAllowed = false;
+ softButtonObjects = null;
+
+ if (transactionQueue != null) {
+ transactionQueue.close();
+ transactionQueue = null;
+ }
+
+ // remove listeners
+ if (internalInterface.getSystemCapabilityManager() != null) {
+ internalInterface.getSystemCapabilityManager().removeOnSystemCapabilityListener(SystemCapabilityType.DISPLAYS, onDisplaysCapabilityListener);
+ internalInterface.getSystemCapabilityManager().removeOnSystemCapabilityListener(SystemCapabilityType.SPEECH, onSpeechCapabilityListener);
+ }
+ if (internalInterface.getPermissionManager() != null) {
+ internalInterface.getPermissionManager().removeListener(permissionListener);
+ }
+ internalInterface.removeOnRPCNotificationListener(FunctionID.ON_BUTTON_PRESS, onButtonPressListener);
+ internalInterface.removeOnRPCNotificationListener(FunctionID.ON_BUTTON_EVENT, onButtonEventListener);
+ super.dispose();
+ }
+
+ /**
+ * Creates a PresentAlertOperation and adds it to the transactionQueue
+ *
+ * @param alert - AlertView object that contains alert information
+ * @param listener - AlertCompletionListener that will notify the sender when Alert has completed
+ */
+ public void presentAlert(AlertView alert, AlertCompletionListener listener) {
+ if (getState() == ERROR) {
+ DebugTool.logWarning(TAG, "Alert Manager In Error State");
+ return;
+ }
+
+ // Check for softButtons and assign them ID's, Behavior mimic SoftButtonManager,
+ // as in if invalid ID's are set, Alert will not show up.
+ // It's best if ID's are not set custom and allow the screenManager to set them.
+ if (alert.getSoftButtons() != null) {
+ if (!BaseScreenManager.checkAndAssignButtonIds(alert.getSoftButtons(), BaseScreenManager.ManagerLocation.ALERT_MANAGER)) {
+ DebugTool.logError(TAG, "Attempted to set soft button objects for Alert, but multiple buttons had the same id.");
+ return;
+ }
+ softButtonObjects.addAll(alert.getSoftButtons());
+ }
+
+ if (nextCancelId >= alertCancelIdMax) {
+ nextCancelId = alertCancelIdMin;
+ } else {
+ nextCancelId++;
+ }
+
+ PresentAlertOperation operation = new PresentAlertOperation(internalInterface, alert, currentWindowCapability, speechCapabilities, fileManager.get(), nextCancelId, listener, new AlertSoftButtonClearListener() {
+ @Override
+ public void onButtonClear(List<SoftButtonObject> softButtonObjectList) {
+ // Stop keeping track of SoftButtonObject listeners as operation has finished
+ for (SoftButtonObject object : softButtonObjectList) {
+ softButtonObjects.remove(object);
+ }
+ }
+ });
+ transactionQueue.add(operation, false);
+ }
+
+ /**
+ * Interface that sends a list of SoftButtonObjects back from PresentAlertOperation, to allow BaseAlertManager
+ * to stop keeping track of them for their onButtonEventListener
+ */
+ interface AlertSoftButtonClearListener {
+ void onButtonClear(List<SoftButtonObject> softButtonObjects);
+ }
+
+
+ private Queue newTransactionQueue() {
+ Queue queue = internalInterface.getTaskmaster().createQueue("AlertManager", 6, false);
+ queue.pause();
+ return queue;
+ }
+
+ /**
+ * Get the soft button objects list
+ *
+ * @return a List<SoftButtonObject>
+ */
+ protected List<SoftButtonObject> getSoftButtonObjects() {
+ return softButtonObjects;
+ }
+
+ /**
+ * 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;
+ }
+
+ // Suspend the queue if the WindowCapabilities are null
+ // OR if isAlertRPCAllowed is false
+ private void updateTransactionQueueSuspended() {
+ if (!isAlertRPCAllowed || currentWindowCapability == null) {
+ DebugTool.logInfo(TAG, String.format("Suspending the transaction queue. Current permission status is false: %b, window capabilities are null: %b", isAlertRPCAllowed, currentWindowCapability == null));
+ transactionQueue.pause();
+ } else {
+ DebugTool.logInfo(TAG, "Starting the transaction queue");
+ transactionQueue.resume();
+ }
+ }
+
+
+ private void addListeners() {
+ // Retrieves SpeechCapabilities of the system.
+ onSpeechCapabilityListener = new OnSystemCapabilityListener() {
+ @Override
+ public void onCapabilityRetrieved(Object capability) {
+ speechCapabilities = SystemCapabilityManager.convertToList(capability, SpeechCapabilities.class);
+ }
+
+ @Override
+ public void onError(String info) {
+
+ }
+ };
+ if (internalInterface.getSystemCapabilityManager() != null) {
+ this.internalInterface.getSystemCapabilityManager().getCapability(SystemCapabilityType.SPEECH, onSpeechCapabilityListener, false);
+ }
+ // Retrieves WindowCapability of the system, if WindowCapability are null, queue pauses
+ onDisplaysCapabilityListener = new OnSystemCapabilityListener() {
+ @Override
+ public void onCapabilityRetrieved(Object capability) {
+ // 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) {
+ currentWindowCapability = null;
+ } else {
+ DisplayCapability display = capabilities.get(0);
+ for (WindowCapability windowCapability : display.getWindowCapabilities()) {
+ int currentWindowID = windowCapability.getWindowID() != null ? windowCapability.getWindowID() : PredefinedWindows.DEFAULT_WINDOW.getValue();
+ if (currentWindowID != PredefinedWindows.DEFAULT_WINDOW.getValue()) {
+ continue;
+ }
+ currentWindowCapability = windowCapability;
+ updatePendingOperationsWithNewWindowCapability();
+ break;
+ }
+ }
+ // Update the queue's suspend state
+ updateTransactionQueueSuspended();
+ }
+
+ @Override
+ public void onError(String info) {
+ DebugTool.logError(TAG, "Display Capability cannot be retrieved");
+ currentWindowCapability = null;
+ updateTransactionQueueSuspended();
+ }
+ };
+ if (internalInterface.getSystemCapabilityManager() != null) {
+ this.internalInterface.getSystemCapabilityManager().addOnSystemCapabilityListener(SystemCapabilityType.DISPLAYS, onDisplaysCapabilityListener);
+ }
+
+ // Subscribes to permission updates for the `Alert` RPC. If the alert is not allowed at the current HMI level, the queue is suspended.
+ // Any `Alert` RPCs added while the queue is suspended will be sent when the `Alert` RPC is allowed at the current HMI level and the queue is unsuspended.
+ PermissionElement alertPermissionElement = new PermissionElement(FunctionID.ALERT, null);
+ permissionListener = internalInterface.getPermissionManager().addListener(Collections.singletonList(alertPermissionElement), internalInterface.getPermissionManager().PERMISSION_GROUP_TYPE_ANY, new OnPermissionChangeListener() {
+ @Override
+ public void onPermissionsChange(@NonNull Map<FunctionID, PermissionStatus> allowedPermissions, int permissionGroupStatus) {
+ if (allowedPermissions.get(FunctionID.ALERT) != null) {
+ isAlertRPCAllowed = allowedPermissions.get(FunctionID.ALERT).getIsRPCAllowed();
+ } else {
+ isAlertRPCAllowed = false;
+ }
+ updateTransactionQueueSuspended();
+ }
+ });
+
+ this.onButtonPressListener = new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+ OnButtonPress onButtonPress = (OnButtonPress) notification;
+ if (onButtonPress != null && onButtonPress.getButtonName() == ButtonName.CUSTOM_BUTTON) {
+ Integer buttonId = onButtonPress.getCustomButtonID();
+ if (getSoftButtonObjects() != null) {
+ for (SoftButtonObject softButtonObject : getSoftButtonObjects()) {
+ if (softButtonObject.getButtonId() == buttonId && softButtonObject.getOnEventListener() != null) {
+ softButtonObject.getOnEventListener().onPress(getSoftButtonObjectById(buttonId), onButtonPress);
+ break;
+ }
+ }
+ }
+ }
+ }
+ };
+ this.internalInterface.addOnRPCNotificationListener(FunctionID.ON_BUTTON_PRESS, onButtonPressListener);
+
+ // Add OnButtonEventListener to notify SoftButtonObjects when there is a button event
+ this.onButtonEventListener = new OnRPCNotificationListener() {
+ @Override
+ public void onNotified(RPCNotification notification) {
+ OnButtonEvent onButtonEvent = (OnButtonEvent) notification;
+ if (onButtonEvent != null && onButtonEvent.getButtonName() == ButtonName.CUSTOM_BUTTON) {
+ Integer buttonId = onButtonEvent.getCustomButtonID();
+ if (getSoftButtonObjects() != null) {
+ for (SoftButtonObject softButtonObject : getSoftButtonObjects()) {
+ if (softButtonObject.getButtonId() == buttonId && softButtonObject.getOnEventListener() != null) {
+ softButtonObject.getOnEventListener().onEvent(getSoftButtonObjectById(buttonId), onButtonEvent);
+ break;
+ }
+ }
+ }
+ }
+ }
+ };
+ this.internalInterface.addOnRPCNotificationListener(FunctionID.ON_BUTTON_EVENT, onButtonEventListener);
+ }
+
+ // Updates pending task with new DisplayCapabilities
+ void updatePendingOperationsWithNewWindowCapability() {
+ for (Task task : transactionQueue.getTasksAsList()) {
+ if (!(task instanceof PresentAlertOperation)) {
+ continue;
+ }
+ ((PresentAlertOperation) task).currentWindowCapability = currentWindowCapability;
+ }
+ }
+}
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 8b976b2af..d24ca4d98 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseScreenManager.java
@@ -31,10 +31,12 @@
*/
package com.smartdevicelink.managers.screen;
+import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
+import com.smartdevicelink.managers.AlertCompletionListener;
import com.smartdevicelink.managers.BaseSubManager;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.ISdl;
@@ -58,6 +60,8 @@ import com.smartdevicelink.proxy.rpc.enums.MetadataType;
import com.smartdevicelink.proxy.rpc.enums.TextAlignment;
import com.smartdevicelink.util.DebugTool;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.List;
@@ -77,6 +81,13 @@ abstract class BaseScreenManager extends BaseSubManager {
private MenuManager menuManager;
private ChoiceSetManager choiceSetManager;
private SubscribeButtonManager subscribeButtonManager;
+ private AlertManager alertManager;
+
+ static final int SOFT_BUTTON_ID_NOT_SET_VALUE = -1;
+ static final int SOFT_BUTTON_ID_MIN_VALUE = 0;
+ static final int SOFT_BUTTON_ID_MAX_VALUE = 10000;
+ static HashSet<Integer> softButtonIDBySoftButtonManager;
+ static HashSet<Integer> softButtonIDByAlertManager;
// Sub manager listener
private final CompletionListener subManagerListener = new CompletionListener() {
@@ -85,15 +96,15 @@ abstract class BaseScreenManager extends BaseSubManager {
public synchronized void onComplete(boolean success) {
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) {
+ && subscribeButtonManager.getState() == BaseSubManager.READY && alertManager.getState() == BaseSubManager.READY) {
DebugTool.logInfo(TAG, "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 && subscribeButtonManager.getState() == BaseSubManager.ERROR) {
+ && choiceSetManager.getState() == BaseSubManager.ERROR && subscribeButtonManager.getState() == BaseSubManager.ERROR && alertManager.getState() == BaseSubManager.ERROR) {
DebugTool.logError(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 || subscribeButtonManager.getState() == BaseSubManager.SETTING_UP) {
+ || choiceSetManager.getState() == BaseSubManager.SETTING_UP || subscribeButtonManager.getState() == BaseSubManager.SETTING_UP || alertManager.getState() == BaseSubManager.SETTING_UP) {
DebugTool.logInfo(TAG, "SETTING UP screen manager, at least one sub manager is still setting up");
transitionToState(SETTING_UP);
} else {
@@ -111,6 +122,8 @@ abstract class BaseScreenManager extends BaseSubManager {
BaseScreenManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
super(internalInterface);
this.fileManager = new WeakReference<>(fileManager);
+ softButtonIDBySoftButtonManager = new HashSet<>();
+ softButtonIDByAlertManager = new HashSet<>();
initialize();
}
@@ -124,6 +137,7 @@ abstract class BaseScreenManager extends BaseSubManager {
this.menuManager.start(subManagerListener);
this.choiceSetManager.start(subManagerListener);
this.subscribeButtonManager.start(subManagerListener);
+ this.alertManager.start(subManagerListener);
}
private void initialize() {
@@ -132,6 +146,7 @@ abstract class BaseScreenManager extends BaseSubManager {
this.textAndGraphicManager = new TextAndGraphicManager(internalInterface, fileManager.get(), softButtonManager);
this.menuManager = new MenuManager(internalInterface, fileManager.get());
this.choiceSetManager = new ChoiceSetManager(internalInterface, fileManager.get());
+ this.alertManager = new AlertManager(internalInterface, fileManager.get());
}
this.subscribeButtonManager = new SubscribeButtonManager(internalInterface);
this.voiceCommandManager = new VoiceCommandManager(internalInterface);
@@ -149,6 +164,10 @@ abstract class BaseScreenManager extends BaseSubManager {
menuManager.dispose();
choiceSetManager.dispose();
subscribeButtonManager.dispose();
+ alertManager.dispose();
+ softButtonIDByAlertManager = null;
+ softButtonIDBySoftButtonManager = null;
+
super.dispose();
}
@@ -703,4 +722,74 @@ abstract class BaseScreenManager extends BaseSubManager {
public void removeButtonListener(@NonNull ButtonName buttonName, @NonNull OnButtonListener listener) {
subscribeButtonManager.removeButtonListener(buttonName, listener);
}
+
+ public void presentAlert(AlertView alert, AlertCompletionListener listener) {
+ alertManager.presentAlert(alert, listener);
+ }
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ManagerLocation.SOFTBUTTON_MANAGER, ManagerLocation.ALERT_MANAGER})
+ @interface ManagerLocation {
+ int SOFTBUTTON_MANAGER = 0;
+ int ALERT_MANAGER = 1;
+ }
+
+ /**
+ * Used across managers to check and assign SoftButton ID's to prevent conflicts.
+ *
+ * @param softButtonObjects - list of SoftButtonObjects
+ * @param location - location from which the button came from, so if new buttons get set on screen, we can clear the old ID's out
+ * @return True if ButtonID's are good, False if not.
+ */
+ static boolean checkAndAssignButtonIds(List<SoftButtonObject> softButtonObjects, @ManagerLocation int location) {
+ // Depending on location form which the softButtons came from, we will clear out the id list so they can be reset
+ if (location == ManagerLocation.ALERT_MANAGER) {
+ softButtonIDByAlertManager.clear();
+ } else if (location == ManagerLocation.SOFTBUTTON_MANAGER) {
+ softButtonIDBySoftButtonManager.clear();
+ }
+ // Check if multiple soft button objects have the same id
+ HashSet<Integer> buttonIdsSetHashSet = new HashSet<>();
+ int currentSoftButtonId, numberOfButtonIdsSet = 0, maxButtonIdsSetByDev = SOFT_BUTTON_ID_MIN_VALUE;
+
+ for (SoftButtonObject softButtonObject : softButtonObjects) {
+ currentSoftButtonId = softButtonObject.getButtonId();
+ if (currentSoftButtonId != SOFT_BUTTON_ID_NOT_SET_VALUE) {
+ if (softButtonIDByAlertManager.contains(currentSoftButtonId) || softButtonIDBySoftButtonManager.contains(currentSoftButtonId)) {
+ return false;
+ }
+ numberOfButtonIdsSet++;
+ if (currentSoftButtonId > maxButtonIdsSetByDev) {
+ maxButtonIdsSetByDev = currentSoftButtonId;
+ }
+ buttonIdsSetHashSet.add(softButtonObject.getButtonId());
+ }
+ }
+ if (numberOfButtonIdsSet != buttonIdsSetHashSet.size()) {
+ return false;
+ }
+
+ // Set ids for soft button objects
+ int generatedSoftButtonId = maxButtonIdsSetByDev;
+ 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 == SOFT_BUTTON_ID_NOT_SET_VALUE) {
+ do {
+ if (generatedSoftButtonId >= SOFT_BUTTON_ID_MAX_VALUE) {
+ generatedSoftButtonId = SOFT_BUTTON_ID_MIN_VALUE;
+ }
+ generatedSoftButtonId++;
+ } while (buttonIdsSetHashSet.contains(generatedSoftButtonId));
+ softButtonObject.setButtonId(generatedSoftButtonId);
+ buttonIdsSetHashSet.add(generatedSoftButtonId);
+ if (location == ManagerLocation.ALERT_MANAGER) {
+ softButtonIDByAlertManager.add(generatedSoftButtonId);
+ } else if (location == ManagerLocation.SOFTBUTTON_MANAGER) {
+ softButtonIDBySoftButtonManager.add(generatedSoftButtonId);
+ }
+ }
+ }
+ return true;
+ }
}
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 c2c9cc52e..4d93be501 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseSoftButtonManager.java
@@ -298,7 +298,7 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
return;
}
- if (!checkAndAssignButtonIds(softButtonObjects)) {
+ if (!BaseScreenManager.checkAndAssignButtonIds(softButtonObjects, BaseScreenManager.ManagerLocation.SOFTBUTTON_MANAGER)) {
DebugTool.logError(TAG, "Attempted to set soft button objects, but multiple buttons had the same id");
return;
}
@@ -340,49 +340,6 @@ abstract class BaseSoftButtonManager extends BaseSubManager {
return false;
}
- /**
- * Check if there is a collision in the ids provided by the developer and assign ids to the SoftButtonObjects that do not have ids
- *
- * @param softButtonObjects the list of the SoftButtonObject values that should be displayed on the head unit
- * @return boolean representing whether the ids are unique or not
- */
- boolean checkAndAssignButtonIds(List<SoftButtonObject> softButtonObjects) {
- // Check if multiple soft button objects have the same id
- HashSet<Integer> buttonIdsSetByDevHashSet = new HashSet<>();
- int currentSoftButtonId, numberOfButtonIdsSetByDev = 0, maxButtonIdsSetByDev = SoftButtonObject.SOFT_BUTTON_ID_MIN_VALUE;
- for (SoftButtonObject softButtonObject : softButtonObjects) {
- currentSoftButtonId = softButtonObject.getButtonId();
- if (currentSoftButtonId != SoftButtonObject.SOFT_BUTTON_ID_NOT_SET_VALUE) {
- numberOfButtonIdsSetByDev++;
- if (currentSoftButtonId > maxButtonIdsSetByDev) {
- maxButtonIdsSetByDev = currentSoftButtonId;
- }
- buttonIdsSetByDevHashSet.add(softButtonObject.getButtonId());
- }
- }
- if (numberOfButtonIdsSetByDev != buttonIdsSetByDevHashSet.size()) {
- return false;
- }
-
-
- // Set ids for soft button objects
- int generatedSoftButtonId = maxButtonIdsSetByDev;
- 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) {
- do {
- if (generatedSoftButtonId >= SoftButtonObject.SOFT_BUTTON_ID_MAX_VALUE) {
- generatedSoftButtonId = SoftButtonObject.SOFT_BUTTON_ID_MIN_VALUE;
- }
- generatedSoftButtonId++;
- } while (buttonIdsSetByDevHashSet.contains(generatedSoftButtonId));
- softButtonObject.setButtonId(generatedSoftButtonId);
- }
- }
- return true;
- }
-
private void transitionSoftButton() {
SoftButtonTransitionOperation operation = new SoftButtonTransitionOperation(internalInterface, softButtonObjects, getCurrentMainField1());
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/DispatchGroup.java b/base/src/main/java/com/smartdevicelink/managers/screen/DispatchGroup.java
new file mode 100644
index 000000000..f6dccb715
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/DispatchGroup.java
@@ -0,0 +1,60 @@
+/*
+ * 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.managers.screen;
+
+/**
+ * Created by Julian Kast on 12/10/20.
+ */
+class DispatchGroup {
+ private int count;
+ private Runnable runnable;
+ DispatchGroup() {
+ count = 0;
+ }
+ synchronized void enter() {
+ count++;
+ }
+ synchronized void leave() {
+ count--;
+ run();
+ }
+ void notify(Runnable runnable) {
+ this.runnable = runnable;
+ run();
+ }
+ private void run() {
+ if (count <= 0 && runnable != null) {
+ runnable.run();
+ }
+ }
+}
diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/PresentAlertOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/PresentAlertOperation.java
new file mode 100644
index 000000000..9957ad309
--- /dev/null
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/PresentAlertOperation.java
@@ -0,0 +1,524 @@
+/*
+ * 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.managers.screen;
+
+import com.livio.taskmaster.Task;
+import com.smartdevicelink.managers.AlertCompletionListener;
+import com.smartdevicelink.managers.CompletionListener;
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.ManagerUtility;
+import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.file.MultipleFileCompletionListener;
+import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
+import com.smartdevicelink.managers.file.filetypes.SdlFile;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.proxy.RPCResponse;
+import com.smartdevicelink.proxy.rpc.Alert;
+import com.smartdevicelink.proxy.rpc.AlertResponse;
+import com.smartdevicelink.proxy.rpc.CancelInteraction;
+import com.smartdevicelink.proxy.rpc.SoftButton;
+import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities;
+import com.smartdevicelink.proxy.rpc.TTSChunk;
+import com.smartdevicelink.proxy.rpc.WindowCapability;
+import com.smartdevicelink.proxy.rpc.enums.ImageFieldName;
+import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities;
+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;
+
+/**
+ * Operation that handles uploading images and audio data needed by the alert and, once the data uploads are complete, sending the alert.
+ *
+ * Created by Julian Kast on 12/10/20.
+ */
+public class PresentAlertOperation extends Task {
+ private static final String TAG = "PresentAlertOperation";
+ private AlertView alertView;
+ private AlertCompletionListener listener;
+ private final WeakReference<ISdl> internalInterface;
+ private final WeakReference<FileManager> fileManager;
+ WindowCapability currentWindowCapability;
+ private int cancelId;
+ private List<SpeechCapabilities> speechCapabilities;
+ boolean isAlertPresented;
+ static int SOFTBUTTON_COUNT = 4;
+ private BaseAlertManager.AlertSoftButtonClearListener alertSoftButtonClearListener;
+
+ public PresentAlertOperation(ISdl internalInterface, AlertView alertView, WindowCapability currentCapabilities, List<SpeechCapabilities> speechCapabilities, FileManager fileManager, Integer cancelId, AlertCompletionListener listener, BaseAlertManager.AlertSoftButtonClearListener alertSoftButtonClearListener) {
+ super("PresentAlertOperation");
+ this.internalInterface = new WeakReference<>(internalInterface);
+ this.fileManager = new WeakReference<>(fileManager);
+ this.currentWindowCapability = currentCapabilities;
+ this.speechCapabilities = speechCapabilities;
+ this.alertView = alertView.clone();
+ this.listener = listener;
+ this.cancelId = cancelId;
+ this.isAlertPresented = false;
+ this.alertSoftButtonClearListener = alertSoftButtonClearListener;
+
+ this.alertView.canceledListener = new AlertCanceledListener() {
+ @Override
+ public void onAlertCanceled() {
+ cancelAlert();
+ }
+ };
+ alertView.canceledListener = this.alertView.canceledListener;
+ }
+
+ @Override
+ public void onExecute() {
+ DebugTool.logInfo(TAG, "Alert Operation: Executing present Alert operation");
+ start();
+ }
+
+ private void start() {
+ if (getState() == Task.CANCELED) {
+ finishOperation(false, null);
+ return;
+ }
+ if (!isValidAlertViewData(alertView)) {
+ if (alertView.getAudio() != null && alertView.getAudio().getAudioData().size() > 0) {
+ DebugTool.logError(TAG, "The module does not support the use of only audio file data in an alert. " +
+ "The alert has no data and can not be sent to the module. " +
+ "The use of audio file data in an alert is only supported on modules supporting RPC Spec v5.0 or newer");
+ } else {
+ DebugTool.logError(TAG, "The alert data is invalid." +
+ " At least either text, secondaryText or audio needs to be provided. " +
+ "Make sure to set at least the text, secondaryText or audio properties on the AlertView");
+ }
+ finishOperation(false, null);
+ return;
+ }
+ final DispatchGroup uploadFilesTask = new DispatchGroup();
+
+ // Enter DispatchGroup twice for two tasks needing to be completed, One for uploading images and one for uploading audio files
+ uploadFilesTask.enter();
+ uploadFilesTask.enter();
+
+ // DispatchGroup notify when all tasks are done
+ uploadFilesTask.notify(new Runnable() {
+ @Override
+ public void run() {
+ presentAlert();
+ }
+ });
+
+ // DispatchGroup Task 1: uploading images
+ uploadImages(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ uploadFilesTask.leave();
+ }
+ });
+
+ // DispatchGroup Task 2: uploading audio files
+ uploadAudioFiles(new CompletionListener() {
+ @Override
+ public void onComplete(boolean success) {
+ uploadFilesTask.leave();
+ }
+ });
+ }
+
+ /**
+ * Checks the `AlertView` data to make sure it conforms to the RPC Spec, which says that at least either `alertText1`, `alertText2` or `TTSChunks` need to be provided.
+ * @param alertView - Alert data that needs to be presented
+ * @return true if AlertView data conforms to RPC Spec
+ */
+ private boolean isValidAlertViewData(AlertView alertView) {
+ if (alertView.getText() != null && alertView.getText().length() > 0) {
+ return true;
+ }
+ if (alertView.getSecondaryText() != null && alertView.getSecondaryText().length() > 0) {
+ return true;
+ }
+ if (alertView.getAudio() != null && getTTSChunksForAlert(alertView) != null) {
+ return true;
+ }
+ return false;
+ }
+
+ // Upload methods
+
+ /**
+ * Upload the alert audio files.
+ *
+ * @param listener - CompletionListener called when all audio files have been uploaded
+ */
+ private void uploadAudioFiles(final CompletionListener listener) {
+ if (!supportsAlertAudioFile()) {
+ DebugTool.logInfo(TAG, "Module does not support audio files for alerts, skipping upload of audio files");
+ listener.onComplete(true);
+ return;
+ }
+
+ if (alertView.getAudio() == null || alertView.getAudio().getAudioData() == null || alertView.getAudio().getAudioData().size() == 0) {
+ DebugTool.logInfo(TAG, "No audio files need to be uploaded for alert");
+ listener.onComplete(true);
+ return;
+ }
+
+ List<SdlFile> filesToBeUploaded = new ArrayList<>();
+ for (TTSChunk ttsChunk : alertView.getAudio().getAudioData()) {
+ if(ttsChunk.getType() != SpeechCapabilities.FILE){
+ continue;
+ }
+ SdlFile audioFile = alertView.getAudio().getAudioFiles().get(ttsChunk.getText());
+ if (fileManager.get() == null || !fileManager.get().fileNeedsUpload(audioFile)) {
+ continue;
+ }
+ filesToBeUploaded.add(audioFile);
+ }
+
+ if (filesToBeUploaded.size() == 0) {
+ DebugTool.logInfo(TAG, "No audio files need to be uploaded for alert");
+ listener.onComplete(true);
+ return;
+ }
+
+ DebugTool.logInfo(TAG, "Uploading audio files for alert");
+
+ if (fileManager.get() != null) {
+ fileManager.get().uploadFiles(filesToBeUploaded, new MultipleFileCompletionListener() {
+ @Override
+ public void onComplete(Map<String, String> errors) {
+ if (getState() == Task.CANCELED) {
+ finishOperation(false, null);
+ return;
+ }
+ if (errors != null) {
+ DebugTool.logError(TAG, "Error uploading alert audio files:" + errors.toString());
+ } else {
+ DebugTool.logInfo(TAG, "All alert audio files uploaded");
+ }
+ listener.onComplete(true);
+ }
+ });
+ }
+ }
+
+ /**
+ * Upload the alert icon and soft button images.
+ *
+ * @param listener - CompletionListener called when all images have been uploaded.
+ */
+ private void uploadImages(final CompletionListener listener) {
+ List<SdlArtwork> artworksToBeUploaded = new ArrayList<>();
+
+ if (supportsAlertIcon() && fileManager.get() != null && fileManager.get().fileNeedsUpload(alertView.getIcon())) {
+ artworksToBeUploaded.add(alertView.getIcon());
+ }
+
+ if (alertView.getSoftButtons() != null) {
+ for (int i = 0; i < getSoftButtonCount(); i++) {
+ SoftButtonObject object = alertView.getSoftButtons().get(i);
+ if (supportsSoftButtonImages() && object.getCurrentState() != null && fileManager.get() != null && fileManager.get().fileNeedsUpload(object.getCurrentState().getArtwork())) {
+ artworksToBeUploaded.add(object.getCurrentState().getArtwork());
+ }
+ }
+ }
+
+ if (artworksToBeUploaded.size() == 0) {
+ DebugTool.logInfo(TAG, "No Images to upload for alert");
+ listener.onComplete(true);
+ return;
+ }
+
+ DebugTool.logInfo(TAG, "Uploading images for alert");
+
+ if (fileManager.get() != null) {
+ fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() {
+ @Override
+ public void onComplete(Map<String, String> errors) {
+ if (getState() == Task.CANCELED) {
+ DebugTool.logInfo(TAG, "Operation canceled");
+ finishOperation(false, null);
+ return;
+ }
+
+ if (errors != null) {
+ DebugTool.logError(TAG, "AlertManager artwork failed to upload with error: " + errors.toString());
+ } else {
+ DebugTool.logInfo(TAG, "All alert images uploaded");
+ }
+ listener.onComplete(true);
+ }
+ });
+ }
+ }
+
+ /**
+ * Sends the alert RPC to the module. The operation is finished once a response has been received from the module.
+ */
+ private void presentAlert() {
+ if (getState() == Task.CANCELED) {
+ DebugTool.logInfo(TAG, "Operation canceled");
+ finishOperation(false, null);
+ return;
+ }
+
+ isAlertPresented = true;
+
+ Alert alert = alertRpc();
+
+ alert.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (!response.getSuccess()) {
+ DebugTool.logError(TAG, "There was an error presenting the alert: " + response.getInfo());
+ } else {
+ DebugTool.logInfo(TAG, "Alert finished presenting");
+ }
+ finishOperation(response.getSuccess(), ((AlertResponse) response).getTryAgainTime());
+ }
+ });
+ internalInterface.get().sendRPC(alert);
+ }
+
+ /**
+ * Cancels the alert. If the alert has not yet been sent to the module, it will not be sent.
+ * If the alert is already presented on the module, the alert will be immediately dismissed.
+ * Canceling an already presented alert will only work if connected to modules supporting RPC spec versions 6.0+.
+ * On older versions alert will not be dismissed.
+ */
+ private void cancelAlert() {
+ if (getState() == Task.FINISHED) {
+ DebugTool.logInfo(TAG, "This operation has already finished so it can not be canceled");
+ return;
+ } else if (getState() == Task.CANCELED) {
+ DebugTool.logInfo(TAG, "This operation has already been canceled. It will be finished at some point during the operation.");
+ return;
+ } else if (getState() == Task.IN_PROGRESS) {
+ if (internalInterface.get() != null && internalInterface.get().getSdlMsgVersion() != null && internalInterface.get().getSdlMsgVersion().getMajorVersion() < 6) {
+ DebugTool.logError(TAG, "Attempting to cancel this operation in-progress; if the alert is already presented on the module, it cannot be dismissed.");
+ this.cancelTask();
+ return;
+ } else if (!isAlertPresented) {
+ DebugTool.logError(TAG, "Alert has not yet been sent to the module. Alert will not be shown..");
+ this.cancelTask();
+ return;
+ }
+
+ DebugTool.logInfo(TAG, "Canceling the presented alert");
+
+ CancelInteraction cancelInteraction = new CancelInteraction(FunctionID.ALERT.getId(), cancelId);
+ cancelInteraction.setOnRPCResponseListener(new OnRPCResponseListener() {
+ @Override
+ public void onResponse(int correlationId, RPCResponse response) {
+ if (!response.getSuccess()) {
+ DebugTool.logInfo(TAG, "Error canceling the presented alert: " + response.getInfo());
+ onFinished();
+ return;
+ }
+ DebugTool.logInfo(TAG, "The presented alert was canceled successfully");
+ onFinished();
+ }
+ });
+ internalInterface.get().sendRPC(cancelInteraction);
+ } else {
+ DebugTool.logInfo(TAG, "Cancelling an alert that has not yet started");
+ this.cancelTask();
+ }
+ }
+
+ // Getters / Setters
+
+ Alert alertRpc() {
+ Alert alert = new Alert();
+ alert = assembleAlertText(alert);
+ alert.setDuration(alertView.getTimeout() * 1000);
+
+ if (alertView.getIcon() != null && supportsAlertIcon() && !(fileManager.get().hasUploadedFile(alertView.getIcon()))) {
+ alert.setAlertIcon(alertView.getIcon().getImageRPC());
+ }
+
+ alert.setProgressIndicator(alertView.isShowWaitIndicator());
+ alert.setCancelID(this.cancelId);
+
+ if (alertView.getSoftButtons() != null) {
+ List<SoftButton> softButtons = new ArrayList<>();
+ for (int i = 0; i < getSoftButtonCount(); i++) {
+ softButtons.add(alertView.getSoftButtons().get(i).getCurrentStateSoftButton());
+ }
+ alert.setSoftButtons(softButtons);
+ }
+
+ if (alertView.getAudio() != null) {
+ alert.setPlayTone(alertView.getAudio().isPlayTone());
+ alert.setTtsChunks(getTTSChunksForAlert(alertView));
+ }
+ return alert;
+ }
+
+ /**
+ * Limits the number of SoftButtons that can be set in the AlertRPC to 4
+ *
+ * @return The maximum number of soft buttons that can be sent to the module
+ */
+ private int getSoftButtonCount() {
+ return alertView.getSoftButtons().size() <= 4 ? alertView.getSoftButtons().size() : SOFTBUTTON_COUNT;
+ }
+
+ /**
+ * Checks if AudioFiles are supported by module and removes them form audioData list if they are not
+ * @param alertView
+ * @return List of ttsChunks
+ */
+ private List<TTSChunk> getTTSChunksForAlert(AlertView alertView) {
+ AlertAudioData alertAudioData = alertView.getAudio();
+ List<TTSChunk> ttsChunks = new ArrayList<>();
+ for (TTSChunk chunk : alertAudioData.getAudioData()) {
+ if (chunk.getType() == SpeechCapabilities.FILE && !supportsAlertAudioFile()) {
+ continue;
+ }
+ ttsChunks.add(chunk);
+ }
+ return ttsChunks.size() > 0 ? ttsChunks : null;
+ }
+
+ /**
+ * Checks if the connected module or current template supports soft button images.
+ *
+ * @return True if soft button images are currently supported; false if not.
+ */
+ private boolean supportsSoftButtonImages() {
+ SoftButtonCapabilities softButtonCapabilities = currentWindowCapability.getSoftButtonCapabilities().get(0);
+ return softButtonCapabilities.getImageSupported().booleanValue();
+ }
+
+ /**
+ * Checks if the connected module supports audio files. Using an audio file in an alert will only work if connected to modules supporting RPC spec versions 5.0+.
+ * If the module does not return a speechCapabilities, assume that the module supports playing an audio file.
+ *
+ * @return True if the module supports playing audio files in an alert; false if not.
+ */
+ private boolean supportsAlertAudioFile() {
+ return (internalInterface.get() != null && internalInterface.get().getSdlMsgVersion() != null && internalInterface.get().getSdlMsgVersion().getMajorVersion() >= 5 && speechCapabilities != null && speechCapabilities.contains(SpeechCapabilities.FILE));
+ }
+
+ /**
+ * Checks if the connected module or current template supports alert icons.
+ *
+ * @return True if alert icons are currently supported; false if not.
+ */
+ private boolean supportsAlertIcon() {
+ return ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(currentWindowCapability, ImageFieldName.alertIcon);
+ }
+
+ // Text Helpers
+
+ private Alert assembleAlertText(Alert alert) {
+ List<String> nonNullFields = findNonNullTextFields();
+ if (nonNullFields.isEmpty()) {
+ return alert;
+ }
+ int numberOfLines = currentWindowCapability!= null ? ManagerUtility.WindowCapabilityUtility.getMaxNumberOfAlertFieldLines(currentWindowCapability) : 3;
+ switch (numberOfLines) {
+ case 1:
+ alert = assembleOneLineAlertText(alert, nonNullFields);
+ break;
+ case 2:
+ alert = assembleTwoLineAlertText(alert, nonNullFields);
+ break;
+ case 3:
+ alert = assembleThreeLineAlertText(alert, nonNullFields);
+ break;
+ }
+ return alert;
+ }
+
+ private List<String> findNonNullTextFields() {
+ List<String> list = new ArrayList<>();
+
+ if (alertView.getText() != null && alertView.getText().length() > 0) {
+ list.add(alertView.getText());
+ }
+
+ if (alertView.getSecondaryText() != null && alertView.getSecondaryText().length() > 0) {
+ list.add(alertView.getSecondaryText());
+ }
+
+ if (alertView.getTertiaryText() != null && alertView.getTertiaryText().length() > 0) {
+ list.add(alertView.getTertiaryText());
+ }
+
+ return list;
+ }
+
+ private Alert assembleOneLineAlertText(Alert alert, List<String> alertFields) {
+ StringBuilder alertString1 = new StringBuilder();
+ for (int i = 0; i < alertFields.size(); i++) {
+ if (i > 0) {
+ alertString1.append(" - ").append(alertFields.get(i));
+ } else {
+ alertString1.append(alertFields.get(i));
+ }
+ }
+ alert.setAlertText1(alertString1.toString());
+ return alert;
+ }
+
+ private Alert assembleTwoLineAlertText(Alert alert, List<String> alertFields) {
+ if (alertFields.size() <= 2) {
+ alert.setAlertText1(alertFields.size() > 0 ? alertFields.get(0) : null);
+ alert.setAlertText2(alertFields.size() > 1 ? alertFields.get(1) : null);
+ } else {
+ alert.setAlertText1(alertFields.size() > 0 ? alertFields.get(0) : null);
+ alert.setAlertText2(alertFields.get(1) + " - " + alertFields.get(2));
+ }
+ return alert;
+ }
+
+ private Alert assembleThreeLineAlertText(Alert alert, List<String> alertFields) {
+ alert.setAlertText1(alertFields.size() > 0 ? alertFields.get(0) : null);
+ alert.setAlertText2(alertFields.size() > 1 ? alertFields.get(1) : null);
+ alert.setAlertText3(alertFields.size() > 2 ? alertFields.get(2) : null);
+ return alert;
+ }
+
+ private void finishOperation(boolean success, Integer tryAgainTime) {
+ DebugTool.logInfo(TAG, "Finishing present alert operation");
+ if (listener != null) {
+ listener.onComplete(success, tryAgainTime);
+ }
+ // If alertView has SoftButtons, we need to clear out their references in BaseAlertManager
+ if (alertView.getSoftButtons() != null) {
+ alertSoftButtonClearListener.onButtonClear(alertView.getSoftButtons());
+ }
+ onFinished();
+ }
+}
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 e401f8824..76301bc07 100644
--- a/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java
+++ b/base/src/main/java/com/smartdevicelink/managers/screen/SoftButtonObject.java
@@ -50,7 +50,7 @@ import java.util.List;
*
* @see SoftButtonState
*/
-public class SoftButtonObject {
+public class SoftButtonObject implements Cloneable{
private static final String TAG = "SoftButtonObject";
static final int SOFT_BUTTON_ID_NOT_SET_VALUE = -1;
static final int SOFT_BUTTON_ID_MIN_VALUE = 0;
@@ -295,12 +295,14 @@ public class SoftButtonObject {
}
/**
+ * DO NOT USE! let the managers assign ID's. In next major version change this will be restricted to the library
* Sets the id of the SoftButtonObject <br>
* <strong>Note: If the developer did not set buttonId, the manager will automatically assign an id before the SoftButtons are sent to the head unit.
* Please note that the manager may reuse ids from previous batch of SoftButtons that were already sent to the head unit</strong>
*
* @param buttonId an int value that represents the id of the SoftButtonObject
*/
+ @Deprecated
public void setButtonId(int buttonId) {
if (buttonId < SOFT_BUTTON_ID_MIN_VALUE) {
DebugTool.logError(TAG, "buttonId has to be equal or more than " + SOFT_BUTTON_ID_MIN_VALUE);
@@ -375,4 +377,22 @@ public class SoftButtonObject {
// return comparison
return hashCode() == o.hashCode();
}
+
+ /**
+ * Creates a deep copy of the object
+ *
+ * @return deep copy of the object, null if an exception occurred
+ */
+ @Override
+ public SoftButtonObject clone() {
+ try {
+ SoftButtonObject softButtonObject = (SoftButtonObject) super.clone();
+ return softButtonObject;
+ } catch (CloneNotSupportedException e) {
+ if (DebugTool.isDebugEnabled()) {
+ throw new RuntimeException("Clone not supported by super class");
+ }
+ }
+ return null;
+ }
}
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 f36ad2190..44303c4bd 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
@@ -102,7 +102,8 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
int nextChoiceId;
int nextCancelId;
final int choiceCellIdMin = 1;
- final int choiceCellCancelIdMin = 1;
+ private final int choiceCellCancelIdMin = 101;
+ private final int choiceCellCancelIdMax = 200;
boolean isVROptional;
BaseChoiceSetManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
@@ -365,11 +366,11 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
if (keyboardListener == null) {
// Non-searchable choice set
DebugTool.logInfo(TAG, "Creating non-searchable choice set");
- presentOp = new PresentChoiceSetOperation(internalInterface, pendingPresentationSet, mode, null, null, privateChoiceListener, nextCancelId++);
+ presentOp = new PresentChoiceSetOperation(internalInterface, pendingPresentationSet, mode, null, null, privateChoiceListener, getNextCancelId());
} else {
// Searchable choice set
DebugTool.logInfo(TAG, "Creating searchable choice set");
- presentOp = new PresentChoiceSetOperation(internalInterface, pendingPresentationSet, mode, keyboardConfiguration, keyboardListener, privateChoiceListener, nextCancelId++);
+ presentOp = new PresentChoiceSetOperation(internalInterface, pendingPresentationSet, mode, keyboardConfiguration, keyboardListener, privateChoiceListener, getNextCancelId());
}
transactionQueue.add(presentOp, false);
@@ -412,7 +413,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
// Present a keyboard with the choice set that we used to test VR's optional state
DebugTool.logInfo(TAG, "Presenting Keyboard - Choice Set Manager");
- Integer keyboardCancelID = nextCancelId++;
+ Integer keyboardCancelID = getNextCancelId();
PresentKeyboardOperation keyboardOp = new PresentKeyboardOperation(internalInterface, keyboardConfiguration, initialText, customKeyboardConfig, listener, keyboardCancelID);
currentlyPresentedKeyboardOperation = keyboardOp;
transactionQueue.add(keyboardOp, false);
@@ -730,4 +731,18 @@ abstract class BaseChoiceSetManager extends BaseSubManager {
defaultProperties.setKeypressMode(KeypressMode.RESEND_CURRENT_ENTRY);
return defaultProperties;
}
+
+ /**
+ * Checks and increments the cancelID to keep in in a defined range
+ *
+ * @return an integer for cancelId to be sent to operations.
+ */
+ private int getNextCancelId() {
+ if (nextCancelId >= choiceCellCancelIdMax) {
+ nextCancelId = choiceCellCancelIdMin;
+ } else {
+ nextCancelId++;
+ }
+ return nextCancelId;
+ }
}
diff --git a/javaSE/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java b/javaSE/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java
index d4eef253b..8b15abaf4 100644
--- a/javaSE/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java
+++ b/javaSE/hello_sdl_java/src/main/java/com/smartdevicelink/java/SdlService.java
@@ -32,11 +32,13 @@
package com.smartdevicelink.java;
+import com.smartdevicelink.managers.AlertCompletionListener;
import com.smartdevicelink.managers.CompletionListener;
import com.smartdevicelink.managers.SdlManager;
import com.smartdevicelink.managers.SdlManagerListener;
import com.smartdevicelink.managers.file.filetypes.SdlArtwork;
import com.smartdevicelink.managers.lifecycle.LifecycleConfigurationUpdate;
+import com.smartdevicelink.managers.screen.AlertView;
import com.smartdevicelink.managers.screen.OnButtonListener;
import com.smartdevicelink.managers.screen.choiceset.ChoiceCell;
import com.smartdevicelink.managers.screen.choiceset.ChoiceSet;
@@ -373,10 +375,16 @@ public class SdlService {
}
private void showAlert(String text) {
- Alert alert = new Alert();
- alert.setAlertText1(text);
- alert.setDuration(5000);
- sdlManager.sendRPC(alert);
+ AlertView.Builder builder = new AlertView.Builder();
+ builder.setText(text);
+ builder.setTimeout(5);
+ AlertView alertView = builder.build();
+ sdlManager.getScreenManager().presentAlert(alertView, new AlertCompletionListener() {
+ @Override
+ public void onComplete(boolean success, Integer tryAgainTime) {
+ DebugTool.logInfo(TAG, "Alert presented: "+ success);
+ }
+ });
}
public interface SdlServiceCallback {
diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java
index 09a495355..879ff0f27 100644
--- a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java
+++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/SdlManager.java
@@ -34,11 +34,27 @@ package com.smartdevicelink.managers;
import androidx.annotation.NonNull;
+import com.livio.taskmaster.Taskmaster;
import com.smartdevicelink.managers.file.FileManager;
+import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager;
import com.smartdevicelink.managers.permission.PermissionManager;
import com.smartdevicelink.managers.screen.ScreenManager;
+import com.smartdevicelink.protocol.ISdlServiceListener;
+import com.smartdevicelink.protocol.enums.FunctionID;
+import com.smartdevicelink.protocol.enums.SessionType;
+import com.smartdevicelink.proxy.RPCMessage;
+import com.smartdevicelink.proxy.rpc.RegisterAppInterfaceResponse;
+import com.smartdevicelink.proxy.rpc.SdlMsgVersion;
+import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener;
+import com.smartdevicelink.proxy.rpc.listeners.OnRPCRequestListener;
+import com.smartdevicelink.streaming.video.VideoStreamingParameters;
import com.smartdevicelink.transport.enums.TransportType;
import com.smartdevicelink.util.DebugTool;
+import com.smartdevicelink.util.Version;
+
+import java.util.List;
/**
* <strong>SDLManager</strong> <br>
@@ -178,4 +194,132 @@ public class SdlManager extends BaseSdlManager {
super(appId, appName, listener);
}
}
+ private ISdl _internalInterface = new ISdl() {
+ @Override
+ public void start() {
+ lifecycleManager.getInternalInterface(SdlManager.this).start();
+ }
+
+ @Override
+ public void stop() {
+ lifecycleManager.getInternalInterface(SdlManager.this).start();
+ }
+
+ @Override
+ public boolean isConnected() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).isConnected();
+ }
+
+ @Override
+ public void addServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addServiceListener(serviceType, sdlServiceListener);
+ }
+
+ @Override
+ public void removeServiceListener(SessionType serviceType, ISdlServiceListener sdlServiceListener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).removeServiceListener(serviceType, sdlServiceListener);
+ }
+
+ @Override
+ public void startVideoService(VideoStreamingParameters parameters, boolean encrypted) {
+ lifecycleManager.getInternalInterface(SdlManager.this).startVideoService(parameters,encrypted);
+ }
+
+ @Override
+ public void startAudioService(boolean encrypted) {
+ lifecycleManager.getInternalInterface(SdlManager.this).startAudioService(encrypted);
+ }
+
+ @Override
+ public void sendRPC(RPCMessage message) {
+ lifecycleManager.getInternalInterface(SdlManager.this).sendRPC(message);
+ }
+
+ @Override
+ public void sendRPCs(List<? extends RPCMessage> rpcs, OnMultipleRequestListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).sendRPCs(rpcs, listener);
+ }
+
+ @Override
+ public void sendSequentialRPCs(List<? extends RPCMessage> rpcs, OnMultipleRequestListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).sendSequentialRPCs(rpcs, listener);
+ }
+
+ @Override
+ public void addOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addOnRPCNotificationListener(notificationId, listener);
+ }
+
+ @Override
+ public boolean removeOnRPCNotificationListener(FunctionID notificationId, OnRPCNotificationListener listener) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCNotificationListener(notificationId, listener);
+ }
+
+ @Override
+ public void addOnRPCRequestListener(FunctionID functionID, OnRPCRequestListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addOnRPCRequestListener(functionID, listener);
+ }
+
+ @Override
+ public boolean removeOnRPCRequestListener(FunctionID functionID, OnRPCRequestListener listener) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCRequestListener(functionID, listener);
+ }
+
+ @Override
+ public void addOnRPCListener(FunctionID responseId, OnRPCListener listener) {
+ lifecycleManager.getInternalInterface(SdlManager.this).addOnRPCListener(responseId, listener);
+ }
+
+ @Override
+ public boolean removeOnRPCListener(FunctionID responseId, OnRPCListener listener) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).removeOnRPCListener(responseId, listener);
+ }
+
+ @Override
+ public RegisterAppInterfaceResponse getRegisterAppInterfaceResponse() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getRegisterAppInterfaceResponse();
+ }
+
+ @Override
+ public boolean isTransportForServiceAvailable(SessionType serviceType) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).isTransportForServiceAvailable(serviceType);
+ }
+
+ @NonNull
+ @Override
+ public SdlMsgVersion getSdlMsgVersion() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getSdlMsgVersion();
+ }
+
+ @NonNull
+ @Override
+ public Version getProtocolVersion() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getProtocolVersion();
+ }
+
+ @Override
+ public long getMtu(SessionType serviceType) {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getMtu(serviceType);
+ }
+
+ @Override
+ public void startRPCEncryption() {
+ lifecycleManager.getInternalInterface(SdlManager.this).startRPCEncryption();
+ }
+
+ @Override
+ public Taskmaster getTaskmaster() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getTaskmaster();
+ }
+
+ @Override
+ public SystemCapabilityManager getSystemCapabilityManager() {
+ return lifecycleManager.getInternalInterface(SdlManager.this).getSystemCapabilityManager();
+ }
+
+ @Override
+ public PermissionManager getPermissionManager() {
+ return permissionManager;
+ }
+ };
}
diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java
new file mode 100644
index 000000000..addcee13d
--- /dev/null
+++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/screen/AlertManager.java
@@ -0,0 +1,18 @@
+package com.smartdevicelink.managers.screen;
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+
+import com.smartdevicelink.managers.ISdl;
+import com.smartdevicelink.managers.file.FileManager;
+/**
+ * <strong>AlertManager</strong> <br>
+ * <p>
+ * Note: This class must be accessed through the SdlManager. Do not instantiate it by itself. <br>
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY)
+public class AlertManager extends BaseAlertManager {
+
+ public AlertManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) {
+ super(internalInterface, fileManager);
+ }
+}