summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulian Kast <Julian.kast@livio.io>2021-03-05 11:47:00 -0500
committerGitHub <noreply@github.com>2021-03-05 11:47:00 -0500
commitaf9d766dcee36cca42a3563a149b4623f04ae688 (patch)
treec289ef30cef3f410c5ecf67860953db72e264099
parenta70d82d4219a80412fb092cbb4989a722083338b (diff)
downloadsdl_android-af9d766dcee36cca42a3563a149b4623f04ae688.tar.gz
Screen manager alert manager (#1555)
* Added AlertAudioData and AudioData classes * Added AlertManager and BaseAlertManager Classes, Some logic to get proof of concept working * Added PresentAlertOperation Class and some bare bones logic for proof of concept * Added AlertView class and AlertCanceledListener interface * Added AlertManager to BaseScreenManager, more refining is needed here * Added FileManager to AlertManager * Cleaned up AudioData and AlertAudioData, implemented creating TTSChunk in AudioData * Modified BaseAlertManager to have queues and to create an operation for Alerts. * Added logic to create the Alert RPC, Upload Images and audio files. Added capability checking and aligned with IOS. * Fixed formatting and Fixed Timeout logic * Move All AlertManager related classes to Screen Manager package * Add logic for Cancel ID to BaseAlertManager and PresentAlertOperation, as well as fix adding audio to an alert in PresentAlertOperation * Fix package naming and null checking * Added check to check and assign SoftButtons Id's in BaseScreenManager, BaseAlertManager and BaseSoftButtonManager * Alert Text field concatenation logic * Added method to managerUtility to check for alert textField capability * Added PermissionManager logic to ScreenManager and AlertManager * Added a setter to AlertView * Rearrange logic in constructor to prevent errors when creating listeners * Fix softbutton id logic and add permission manager to alertManager in BaseScreenManager * Add Permission Manager to Alert Manager * Add Permission Manager import to AlertManager * Add AlertCompletionListener * Add AlertCompletionListener * Renamed SoftButtonLocation to ManagerLocation to be more generic, incase it needs to be used in the future * Modify AudioData Classes, Proposal needs revised to reflect changes. * Add ranges for cancelId's for chioiceSetManager and alertManager * Fix setting audio data for alert RPC in AlertManager and fix names in AudioData class * Added SpeechCapability check for audio files * Added check for supporting alert icon * Added CancelInteraction to presentAlertOperation * Add permission manager to ISDL, revert code adding it to the screenManager * Proposed Adding PermissionManager to ISDL * Add null audio check in PresentAlertOperation as well as crated clone methods for AlertAudioData and AlertView, fixed issue in AudioData where speech capability was not being added to prompts with spoken strings * revert changes adding permissionManager to baseLifecycleManagrer * Add ISdl implementation to SdlManager in android and javaSE * Formatting fix * Adding unit test classes * Fix npe in PresentAlertOperation and make ISdl in SdlManager package private for unit test * Added testPresentAlert to PresentAlertOperation, fix SystemCapabilityManagerTest * Add audio file to testing for alerts * Add null check for softbuttons in PresentAlertOperation * Fixe unit test involving sdlManager ISdl, AlertManagers Permission Manager and setting SoftButtons * Add AlertManager class to javaSE * Added AlertAudioDataTest and fixed UnsupportedOperationException * Remove checkAndAssignButtonIds from BaseSoftButtonManager as we use the screenManager now * Add Unit Test * Add SoftButtonCheck and unit test to AlertManager * Add unit test to AlertManager * Add setters in AlertView as well as add unit test * Add comments and Align with IOS * Align AudioData and AlertAudioData classes with IOS and add comments * Align and cleanup AlertView with iOS * Deprecated setting SoftButtonID's and added comments * Remove unnecessary check * Blocking a unit test to check code cov * Reformat AlertAudioData, Add SoftButtonObject clone method, Add OnRPCNotificationListener in AlertManager of softButtons * Add Unit Test to PresentAlertOperation * Rearrange cancel logic and add unit test. * Add OnFinished to operation * Added custom errors * Change logInfo to logError in PresentAlertOperation for checking if it supports audio files, fix unit test and formatting * Formatting fix * Add DispatchGroup.java and implement it in PresentAlertOperation * Use new FileManager method filesNeedsUpload in PresentAlertOperation * Rename methods for alignment and fix unit test * iOS alignment * iOS Alignment * Align with IOS for setting SoftButtons in AlertView * Update JavaDocs * Update WindowCapability for pending task in AlertManager if Capability changes. * Add max value to cancelID and align naming with iOS * Update hello_sdl_android and hello_sdl_java to use alertManager * Fix unit test * fixed bug in PresentAlertOperation * Set range for SoftButtonId's Currently we don’t have managers for Subtle Alerts, Alert Maneuver, Scrollable Message, Show Constant TBT and Perform Audio Pass Thrus. These RPCs can all have soft buttons (that the developer must assign a custom id to). Limiting the range for managers will allow us to guarantee to developers that what they set in thoes RPC's won’t clash with the screen manager generated soft button ids. * Add getMTU to new ISDL Implementation * Fixed log errors * Add Class description to AlertManager * Update documentation on AlertManager * Update cancel ID Range * Add Class description * Add a throw exception if trying to set a softButton with more then one state for an Alert * Change creatAlert() to alertRPC() in PresentAlertOperation, add log message when Alert is finished presenting and remove cancel check as op is over after Alert has presented * Remove cancel op in PresentAlert OnRPCResponseListener as it is not needed at this point * Removed prompts list and created an audioData HashMap * Fix unit test * Fix CancelAlert logic, remove unnecessary check for getting timeout in AlertView * Fix unit test * fixed comment * Remove default constructors and unnecessary tag * Set alert manager cancel id range is 1-100 and the choice set manager cancel id range is 101-200. * Added a check to see if a file has been uploaded before adding it to the Alert RPC * Align with iOS for textFields truncate * Add JavaDocs * Align uploading Audio for Alert in PresentAlertOperation with iOS * Add SoftButton limit and align Image uploads with IOS * Limit number of softButtons set in alertManager * Align Alert cancel logic with IOS * remove unnecessary setter for WindowCapability * Change nextCancelID to package private for unit test. Fix unit test * Add Comments to AlertView and AudioData Class * Fix audio check in PresentAlertOperation * Add Comments and rearrange method order to match IOS * Remove space in test class * Clean up testing imports and formatting * Remove cancel op test as logic was changed to match iOS * add setter for showWaitIndicator * Update queue number to 6 * Fix speechCapabilities conversion * Fix audioFile check * Add comment to deprecation of setting ID to SoftButtonObject * Fix cancelID for choiceSetManager * Add interface for clearing out references to SoftButtonObjects in BaseAlertManager * Fix unit test for PresentAlertOperation * Remove unnecessary if statement Co-authored-by: Julian Kast <julian@livio.com>
-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);
+ }
+}