From 637672b527ccacc8af9167d8341d7f49fb7be985 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 21 Jan 2021 17:00:44 -0500 Subject: MenuManager refactor initial commit --- .../managers/screen/menu/BaseMenuManager.java | 1167 ++------------------ .../menu/MenuConfigurationUpdateOperation.java | 93 ++ .../screen/menu/MenuManagerCompletionListener.java | 42 + .../managers/screen/menu/MenuReplaceOperation.java | 882 +++++++++++++++ .../managers/screen/menu/MenuShowOperation.java | 90 ++ 5 files changed, 1224 insertions(+), 1050 deletions(-) create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuManagerCompletionListener.java create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index f44e0c2e3..6abb4d3d2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -34,91 +34,64 @@ package com.smartdevicelink.managers.screen.menu; import androidx.annotation.NonNull; +import com.livio.taskmaster.Queue; +import com.livio.taskmaster.Task; import com.smartdevicelink.managers.BaseSubManager; 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.lifecycle.OnSystemCapabilityListener; import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager; import com.smartdevicelink.protocol.enums.FunctionID; import com.smartdevicelink.proxy.RPCNotification; -import com.smartdevicelink.proxy.RPCRequest; -import com.smartdevicelink.proxy.RPCResponse; -import com.smartdevicelink.proxy.rpc.AddCommand; -import com.smartdevicelink.proxy.rpc.AddSubMenu; -import com.smartdevicelink.proxy.rpc.DeleteCommand; -import com.smartdevicelink.proxy.rpc.DeleteSubMenu; import com.smartdevicelink.proxy.rpc.DisplayCapability; -import com.smartdevicelink.proxy.rpc.MenuParams; import com.smartdevicelink.proxy.rpc.OnCommand; import com.smartdevicelink.proxy.rpc.OnHMIStatus; import com.smartdevicelink.proxy.rpc.SdlMsgVersion; -import com.smartdevicelink.proxy.rpc.SetGlobalProperties; -import com.smartdevicelink.proxy.rpc.ShowAppMenu; import com.smartdevicelink.proxy.rpc.WindowCapability; -import com.smartdevicelink.proxy.rpc.enums.DisplayType; import com.smartdevicelink.proxy.rpc.enums.HMILevel; -import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows; import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType; import com.smartdevicelink.proxy.rpc.enums.SystemContext; -import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener; -import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener; import com.smartdevicelink.util.DebugTool; -import org.json.JSONException; - import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Map; abstract class BaseMenuManager extends BaseSubManager { private static final String TAG = "BaseMenuManager"; - private static final int KEEP = 0; - private static final int MARKED_FOR_ADDITION = 1; - private static final int MARKED_FOR_DELETION = 2; private final WeakReference fileManager; - - List menuCells, waitingUpdateMenuCells, oldMenuCells, keepsNew, keepsOld; - List inProgressUpdate; - DynamicMenuUpdatesMode dynamicMenuUpdatesMode; - MenuConfiguration menuConfiguration; - SdlMsgVersion sdlMsgVersion; + private List menuCells, oldMenuCells; + private DynamicMenuUpdatesMode dynamicMenuUpdatesMode; + private MenuConfiguration menuConfiguration; + private SdlMsgVersion sdlMsgVersion; private String displayType; - - boolean waitingOnHMIUpdate; - private boolean hasQueuedUpdate; - HMILevel currentHMILevel; - - OnRPCNotificationListener hmiListener, commandListener; - OnSystemCapabilityListener onDisplaysCapabilityListener; - WindowCapability defaultMainWindowCapability; - - private static final int MAX_ID = 2000000000; - private static final int parentIdNotFound = MAX_ID; - private static final int menuCellIdMin = 1; - int lastMenuId; - - SystemContext currentSystemContext; + private HMILevel currentHMILevel, oldHMILevel; + private SystemContext currentSystemContext, oldSystemContext; + private OnRPCNotificationListener hmiListener, commandListener; + private OnSystemCapabilityListener onDisplaysCapabilityListener; + private WindowCapability defaultMainWindowCapability; + static final int MAX_ID = 2000000000; + static final int menuCellIdMin = 1; + static final int parentIdNotFound = MAX_ID; + private int lastMenuId; + private Queue transactionQueue; BaseMenuManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) { - super(internalInterface); - // Set up some Vars this.fileManager = new WeakReference<>(fileManager); - currentSystemContext = SystemContext.SYSCTXT_MAIN; - currentHMILevel = HMILevel.HMI_NONE; - lastMenuId = menuCellIdMin; - dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; - sdlMsgVersion = internalInterface.getSdlMsgVersion(); + this.currentSystemContext = SystemContext.SYSCTXT_MAIN; + this.currentHMILevel = HMILevel.HMI_NONE; + this.lastMenuId = menuCellIdMin; + this.dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; + this.sdlMsgVersion = internalInterface.getSdlMsgVersion(); + this.transactionQueue = newTransactionQueue(); addListeners(); } @@ -131,24 +104,23 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void dispose() { - lastMenuId = menuCellIdMin; menuCells = null; oldMenuCells = null; - currentHMILevel = null; + currentHMILevel = HMILevel.HMI_NONE; currentSystemContext = SystemContext.SYSCTXT_MAIN; dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; defaultMainWindowCapability = null; - inProgressUpdate = null; - hasQueuedUpdate = false; - waitingOnHMIUpdate = false; - waitingUpdateMenuCells = null; - keepsNew = null; - keepsOld = null; menuConfiguration = null; sdlMsgVersion = null; - // remove listeners + // Cancel the operations + if (transactionQueue != null) { + transactionQueue.close(); + transactionQueue = null; + } + + // Remove listeners internalInterface.removeOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, hmiListener); internalInterface.removeOnRPCNotificationListener(FunctionID.ON_COMMAND, commandListener); if (internalInterface.getSystemCapabilityManager() != null) { @@ -158,45 +130,37 @@ abstract class BaseMenuManager extends BaseSubManager { super.dispose(); } - // SETTERS - public void setDynamicUpdatesMode(@NonNull DynamicMenuUpdatesMode value) { this.dynamicMenuUpdatesMode = value; } + /** + * @return The currently set DynamicMenuUpdatesMode. It defaults to ON_WITH_COMPAT_MODE + */ + public DynamicMenuUpdatesMode getDynamicMenuUpdatesMode() { + return this.dynamicMenuUpdatesMode; + } + /** * Creates and sends all associated Menu RPCs * * @param cells - the menu cells that are to be sent to the head unit, including their sub-cells. */ public void setMenuCells(@NonNull List cells) { - // Create a deep copy of the list so future changes by developers don't affect the algorithm logic - List clonedCells = cloneMenuCellsList(cells); - - if (currentHMILevel == null || currentHMILevel.equals(HMILevel.HMI_NONE) || currentSystemContext.equals(SystemContext.SYSCTXT_MENU)) { - // We are in NONE or the menu is in use, bail out of here - waitingOnHMIUpdate = true; - waitingUpdateMenuCells = new ArrayList<>(); - if (clonedCells != null && !clonedCells.isEmpty()) { - waitingUpdateMenuCells.addAll(clonedCells); - } - return; - } - waitingOnHMIUpdate = false; + final List clonedCells = cloneMenuCellsList(cells); - // Update our Lists - // set old list + // Set old list if (menuCells != null) { oldMenuCells = new ArrayList<>(menuCells); } - // copy new list + // Copy new list menuCells = new ArrayList<>(); if (clonedCells != null && !clonedCells.isEmpty()) { menuCells.addAll(clonedCells); } - // HashSet order doesnt matter / does not allow duplicates + // HashSet order doesn't matter / does not allow duplicates HashSet titleCheckSet = new HashSet<>(); HashSet allMenuVoiceCommands = new HashSet<>(); int voiceCommandCount = 0; @@ -208,6 +172,7 @@ abstract class BaseMenuManager extends BaseSubManager { voiceCommandCount += cell.getVoiceCommands().size(); } } + // Check for duplicate titles if (titleCheckSet.size() != menuCells.size()) { DebugTool.logError(TAG, "Not all cell titles are unique. The menu will not be set"); @@ -220,26 +185,16 @@ abstract class BaseMenuManager extends BaseSubManager { return; } - // Upload the Artworks - List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(menuCells); - if (artworksToBeUploaded.size() > 0 && fileManager.get() != null) { - fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { - @Override - public void onComplete(Map errors) { + cancelPendingMenuReplaceOperations(); - if (errors != null && errors.size() > 0) { - DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); - } else { - DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); - } - // proceed - updateMenuAndDetermineBestUpdateMethod(); - } - }); - } else { - // No Artworks to be uploaded, send off - updateMenuAndDetermineBestUpdateMethod(); - } + MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, oldMenuCells, menuCells, lastMenuId, new MenuManagerCompletionListener() { + @Override + public void onComplete(boolean success, int lastMenuId, List oldMenuCells) { + BaseMenuManager.this.lastMenuId = lastMenuId; + BaseMenuManager.this.oldMenuCells = oldMenuCells; + } + }); + transactionQueue.add(operation, false); } /** @@ -248,48 +203,21 @@ abstract class BaseMenuManager extends BaseSubManager { * @return a List of Currently set menu cells */ public List getMenuCells() { - - if (menuCells != null) { - return menuCells; - } else if (waitingOnHMIUpdate && waitingUpdateMenuCells != null) { - // this will keep from returning null if the menu list is set and we are pending HMI FULL - return waitingUpdateMenuCells; - } else { - return null; - } + return menuCells; } - /** - * @return The currently set DynamicMenuUpdatesMode. It defaults to ON_WITH_COMPAT_MODE - */ - public DynamicMenuUpdatesMode getDynamicMenuUpdatesMode() { - return this.dynamicMenuUpdatesMode; - } - - // OPEN MENU RPCs - /** * Opens the Main Menu */ public boolean openMenu() { - if (sdlMsgVersion.getMajorVersion() < 6) { DebugTool.logWarning(TAG, "Menu opening is only supported on head units with RPC spec version 6.0.0 or later. Currently connected head unit RPC spec version is: " + sdlMsgVersion.getMajorVersion() + "." + sdlMsgVersion.getMinorVersion() + "." + sdlMsgVersion.getPatchVersion()); return false; } - ShowAppMenu showAppMenu = new ShowAppMenu(); - showAppMenu.setOnRPCResponseListener(new OnRPCResponseListener() { - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - DebugTool.logInfo(TAG, "Open Main Menu Request Successful"); - } else { - DebugTool.logError(TAG, "Open Main Menu Request Failed"); - } - } - }); - internalInterface.sendRPC(showAppMenu); + MenuShowOperation operation = new MenuShowOperation(internalInterface, null); + transactionQueue.add(operation, false); + return true; } @@ -299,55 +227,37 @@ abstract class BaseMenuManager extends BaseSubManager { * @param cell - A SubMenu cell whose sub menu you wish to open */ public boolean openSubMenu(@NonNull MenuCell cell) { - if (sdlMsgVersion.getMajorVersion() < 6) { DebugTool.logWarning(TAG, "Sub menu opening is only supported on head units with RPC spec version 6.0.0 or later. Currently connected head unit RPC spec version is: " + sdlMsgVersion.getMajorVersion() + "." + sdlMsgVersion.getMinorVersion() + "." + sdlMsgVersion.getPatchVersion()); return false; } - if (oldMenuCells == null) { + if (menuCells == null) { DebugTool.logError(TAG, "open sub menu called, but no Menu cells have been set"); return false; } + // We must see if we have a copy of this cell, since we clone the objects - for (MenuCell clonedCell : oldMenuCells) { + for (MenuCell clonedCell : menuCells) { if (clonedCell.equals(cell) && clonedCell.getCellId() != MAX_ID) { // We've found the correct sub menu cell - sendOpenSubMenu(clonedCell.getCellId()); + MenuShowOperation operation = new MenuShowOperation(internalInterface, clonedCell.getCellId()); + transactionQueue.add(operation, false); return true; } } - return false; - } - - private void sendOpenSubMenu(Integer id) { - - ShowAppMenu showAppMenu = new ShowAppMenu(); - showAppMenu.setMenuID(id); - showAppMenu.setOnRPCResponseListener(new OnRPCResponseListener() { - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - DebugTool.logInfo(TAG, "Open Sub Menu Request Successful"); - } else { - DebugTool.logError(TAG, "Open Sub Menu Request Failed"); - } - } - }); - internalInterface.sendRPC(showAppMenu); + return false; } - // MENU CONFIG /** * This method is called via the screen manager to set the menuConfiguration. - * This will be used when a menu item with sub-cells has a null value for menuConfiguration + * This will be used when a menuCell with sub-cells has a null value for SubMenuLayout * * @param menuConfiguration - The default menuConfiguration */ public void setMenuConfiguration(@NonNull final MenuConfiguration menuConfiguration) { - if (sdlMsgVersion == null) { DebugTool.logError(TAG, "SDL Message Version is null. Cannot set Menu Configuration"); return; @@ -358,661 +268,34 @@ abstract class BaseMenuManager extends BaseSubManager { return; } - if (currentHMILevel == null || currentHMILevel.equals(HMILevel.HMI_NONE) || currentSystemContext.equals(SystemContext.SYSCTXT_MENU)) { - // We are in NONE or the menu is in use, bail out of here - DebugTool.logError(TAG, "Could not set main menu configuration, HMI level: " + currentHMILevel + ", required: 'Not-NONE', system context: " + currentSystemContext + ", required: 'Not MENU'"); - return; - } - - // In the future, when the manager is switched to use queues, the menuConfiguration should be set when SetGlobalProperties response is received - this.menuConfiguration = menuConfiguration; - - if (menuConfiguration.getMenuLayout() != null) { - - SetGlobalProperties setGlobalProperties = new SetGlobalProperties(); - setGlobalProperties.setMenuLayout(menuConfiguration.getMenuLayout()); - setGlobalProperties.setOnRPCResponseListener(new OnRPCResponseListener() { - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - DebugTool.logInfo(TAG, "Menu Configuration successfully set: " + menuConfiguration.toString()); - } else { - DebugTool.logError(TAG, "onError: " + response.getResultCode() + " | Info: " + response.getInfo()); - } - } - }); - internalInterface.sendRPC(setGlobalProperties); - } else { + if (menuConfiguration.getMenuLayout() == null) { DebugTool.logInfo(TAG, "Menu Layout is null, not sending setGlobalProperties"); - } - } - - public MenuConfiguration getMenuConfiguration() { - return this.menuConfiguration; - } - // UPDATING SYSTEM - - // ROOT MENU - - private void updateMenuAndDetermineBestUpdateMethod() { - - if (currentHMILevel == null || currentHMILevel.equals(HMILevel.HMI_NONE) || currentSystemContext.equals(SystemContext.SYSCTXT_MENU)) { - // We are in NONE or the menu is in use, bail out of here - DebugTool.logInfo(TAG, "HMI in None or System Context Menu, returning"); - waitingOnHMIUpdate = true; - waitingUpdateMenuCells = menuCells; - return; - } - - if (inProgressUpdate != null && inProgressUpdate.size() > 0) { - // there's an in-progress update so this needs to wait - DebugTool.logInfo(TAG, "There is an in progress Menu Update, returning"); - hasQueuedUpdate = true; return; } - // Checks against what the developer set for update mode and against the display type - // to determine how the menu will be updated. This has the ability to be changed during - // a session. - if (checkUpdateMode(dynamicMenuUpdatesMode, displayType)) { - // run the lists through the new algorithm - RunScore rootScore = runMenuCompareAlgorithm(oldMenuCells, menuCells); - if (rootScore == null) { - // send initial menu (score will return null) - // make a copy of our current cells - DebugTool.logInfo(TAG, "Creating initial Menu"); - // Set the IDs if needed - lastMenuId = menuCellIdMin; - updateIdsOnMenuCells(menuCells, parentIdNotFound); - this.oldMenuCells = new ArrayList<>(menuCells); - createAndSendEntireMenu(); - } else { - DebugTool.logInfo(TAG, "Dynamically Updating Menu"); - if (menuCells.size() == 0 && (oldMenuCells != null && oldMenuCells.size() > 0)) { - // the dev wants to clear the menu. We have old cells and an empty array of new ones. - deleteMenuWhenNewCellsEmpty(); - } else { - // lets dynamically update the root menu - dynamicallyUpdateRootMenu(rootScore); - } - } - } else { - // we are in compatibility mode - DebugTool.logInfo(TAG, "Updating menus in compatibility mode"); - lastMenuId = menuCellIdMin; - updateIdsOnMenuCells(menuCells, parentIdNotFound); - // if the old cell array is not null, we want to delete the entire thing, else copy the new array - if (oldMenuCells == null) { - this.oldMenuCells = new ArrayList<>(menuCells); - } - createAndSendEntireMenu(); - } - } - - private boolean checkUpdateMode(DynamicMenuUpdatesMode updateMode, String displayType) { - - if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { - if (displayType == null) { - return true; - } - return (!displayType.equals(DisplayType.GEN3_8_INCH.toString())); - - } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_OFF)) { - return false; - } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_ON)) { - return true; - } - - return true; - } - - private void deleteMenuWhenNewCellsEmpty() { - sendDeleteRPCs(createDeleteRPCsForCells(oldMenuCells), new CompletionListener() { + MenuConfigurationUpdateOperation operation = new MenuConfigurationUpdateOperation(internalInterface, menuConfiguration, new CompletionListener() { @Override public void onComplete(boolean success) { - inProgressUpdate = null; - - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } else { - DebugTool.logInfo(TAG, "Successfully Cleared Menu"); - } - oldMenuCells = null; - if (hasQueuedUpdate) { - hasQueuedUpdate = false; - } + BaseMenuManager.this.menuConfiguration = menuConfiguration; + updatePendingOperationsWithNewMenuConfiguration(); } }); - } - - private void dynamicallyUpdateRootMenu(RunScore bestRootScore) { - - // we need to run through the keeps and see if they have subCells, as they also need to be run - // through the compare function. - List newIntArray = bestRootScore.getCurrentMenu(); - List oldIntArray = bestRootScore.getOldMenu(); - List deleteCommands; - - // Set up deletes - List deletes = new ArrayList<>(); - keepsOld = new ArrayList<>(); - for (int x = 0; x < oldIntArray.size(); x++) { - Integer old = oldIntArray.get(x); - if (old.equals(MARKED_FOR_DELETION)) { - // grab cell to send to function to create delete commands - deletes.add(oldMenuCells.get(x)); - } else if (old.equals(KEEP)) { - keepsOld.add(oldMenuCells.get(x)); - } - } - // create the delete commands - deleteCommands = createDeleteRPCsForCells(deletes); - - // Set up the adds - List adds = new ArrayList<>(); - keepsNew = new ArrayList<>(); - for (int x = 0; x < newIntArray.size(); x++) { - Integer newInt = newIntArray.get(x); - if (newInt.equals(MARKED_FOR_ADDITION)) { - // grab cell to send to function to create add commands - adds.add(menuCells.get(x)); - } else if (newInt.equals(KEEP)) { - keepsNew.add(menuCells.get(x)); - } - } - updateIdsOnDynamicCells(adds); - // this is needed for the onCommands to still work - transferIdsToKeptCells(keepsNew); - - if (adds.size() > 0) { - DebugTool.logInfo(TAG, "Sending root menu updates"); - sendDynamicRootMenuRPCs(deleteCommands, adds); - } else { - DebugTool.logInfo(TAG, "All root menu items are kept. Check the sub menus"); - runSubMenuCompareAlgorithm(); - } - } - - // OTHER - - private void sendDynamicRootMenuRPCs(List deleteCommands, final List updatedCells) { - sendDeleteRPCs(deleteCommands, new CompletionListener() { - @Override - public void onComplete(boolean success) { - createAndSendMenuCellRPCs(updatedCells, new CompletionListener() { - @Override - public void onComplete(boolean success) { - inProgressUpdate = null; - - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } - - if (hasQueuedUpdate) { - //setMenuCells(waitingUpdateMenuCells); - hasQueuedUpdate = false; - } - } - }); - } - }); - } - - // SUB MENUS - - // This is called in the listener in the sendMenu and sendSubMenuCommands Methods - private void runSubMenuCompareAlgorithm() { - // any cells that were re-added have their sub-cells added with them - // at this point all we care about are the cells that were deemed equal and kept. - if (keepsNew == null || keepsNew.size() == 0) { - return; - } - - List commandLists = new ArrayList<>(); - - for (int i = 0; i < keepsNew.size(); i++) { - - MenuCell keptCell = keepsNew.get(i); - MenuCell oldKeptCell = keepsOld.get(i); - - if (oldKeptCell.getSubCells() != null && oldKeptCell.getSubCells().size() > 0 && keptCell.getSubCells() != null && keptCell.getSubCells().size() > 0) { - // ACTUAL LOGIC - RunScore subScore = compareOldAndNewLists(oldKeptCell.getSubCells(), keptCell.getSubCells()); - - if (subScore != null) { - DebugTool.logInfo(TAG, "Sub menu Run Score: " + oldKeptCell.getTitle() + " Score: " + subScore.getScore()); - SubCellCommandList commandList = new SubCellCommandList(oldKeptCell.getTitle(), oldKeptCell.getCellId(), subScore, oldKeptCell.getSubCells(), keptCell.getSubCells()); - commandLists.add(commandList); - } - } - } - createSubMenuDynamicCommands(commandLists); - } - - private void createSubMenuDynamicCommands(final List commandLists) { - - // break out - if (commandLists.size() == 0) { - if (inProgressUpdate != null) { - inProgressUpdate = null; - } - - if (hasQueuedUpdate) { - DebugTool.logInfo(TAG, "Menu Manager has waiting updates, sending now"); - setMenuCells(waitingUpdateMenuCells); - hasQueuedUpdate = false; - } - DebugTool.logInfo(TAG, "All menu updates, including sub menus - done."); - return; - } - - final SubCellCommandList commandList = commandLists.remove(0); - - DebugTool.logInfo(TAG, "Creating and Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - - // grab the scores - RunScore score = commandList.getListsScore(); - List newIntArray = score.getCurrentMenu(); - List oldIntArray = score.getOldMenu(); - - // Grab the sub-menus from the parent cell - final List oldCells = commandList.getOldList(); - final List newCells = commandList.getNewList(); - - // Create the list for the adds - List subCellKeepsNew = new ArrayList<>(); - - List deleteCommands; - - // Set up deletes - List deletes = new ArrayList<>(); - for (int x = 0; x < oldIntArray.size(); x++) { - Integer old = oldIntArray.get(x); - if (old.equals(MARKED_FOR_DELETION)) { - // grab cell to send to function to create delete commands - deletes.add(oldCells.get(x)); - } - } - // create the delete commands - deleteCommands = createDeleteRPCsForCells(deletes); - - // Set up the adds - List adds = new ArrayList<>(); - for (int x = 0; x < newIntArray.size(); x++) { - Integer newInt = newIntArray.get(x); - if (newInt.equals(MARKED_FOR_ADDITION)) { - // grab cell to send to function to create add commands - adds.add(newCells.get(x)); - } else if (newInt.equals(KEEP)) { - subCellKeepsNew.add(newCells.get(x)); - } - } - final List addsWithNewIds = updateIdsOnDynamicSubCells(oldCells, adds, commandList.getParentId()); - // this is needed for the onCommands to still work - transferIdsToKeptSubCells(oldCells, subCellKeepsNew); - - sendDeleteRPCs(deleteCommands, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (addsWithNewIds != null && addsWithNewIds.size() > 0) { - createAndSendDynamicSubMenuRPCs(newCells, addsWithNewIds, new CompletionListener() { - @Override - public void onComplete(boolean success) { - // recurse through next sub list - DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - createSubMenuDynamicCommands(commandLists); - } - }); - } else { - // no add commands to send, recurse through next sub list - DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - createSubMenuDynamicCommands(commandLists); - } - } - }); - } - - // OTHER HELPER METHODS: - - // COMPARISONS - - RunScore runMenuCompareAlgorithm(List oldCells, List newCells) { - - if (oldCells == null || oldCells.size() == 0) { - return null; - } - - RunScore bestScore = compareOldAndNewLists(oldCells, newCells); - DebugTool.logInfo(TAG, "Best menu run score: " + bestScore.getScore()); - - return bestScore; - } - - private RunScore compareOldAndNewLists(List oldCells, List newCells) { - - RunScore bestRunScore = null; - - // This first loop is for each 'run' - for (int run = 0; run < oldCells.size(); run++) { - - List oldArray = new ArrayList<>(oldCells.size()); - List newArray = new ArrayList<>(newCells.size()); - - // Set the statuses - setDeleteStatus(oldCells.size(), oldArray); - setAddStatus(newCells.size(), newArray); - - int startIndex = 0; - - // Keep items that appear in both lists - for (int oldItems = run; oldItems < oldCells.size(); oldItems++) { - - for (int newItems = startIndex; newItems < newCells.size(); newItems++) { - - if (oldCells.get(oldItems).equals(newCells.get(newItems))) { - oldArray.set(oldItems, KEEP); - newArray.set(newItems, KEEP); - // set the new start index - startIndex = newItems + 1; - break; - } - } - } - - // Calculate number of adds, or the 'score' for this run - int numberOfAdds = 0; - - for (int x = 0; x < newArray.size(); x++) { - if (newArray.get(x).equals(MARKED_FOR_ADDITION)) { - numberOfAdds++; - } - } - - // see if we have a new best score and set it if we do - if (bestRunScore == null || numberOfAdds < bestRunScore.getScore()) { - bestRunScore = new RunScore(numberOfAdds, oldArray, newArray); - } - - } - return bestRunScore; - } - - private void setDeleteStatus(Integer size, List oldArray) { - for (int i = 0; i < size; i++) { - oldArray.add(MARKED_FOR_DELETION); - } - } - - private void setAddStatus(Integer size, List newArray) { - for (int i = 0; i < size; i++) { - newArray.add(MARKED_FOR_ADDITION); - } - } - - // ARTWORKS - - private List findAllArtworksToBeUploadedFromCells(List cells) { - // Make sure we can use images in the menus - if (!supportsImages()) { - return new ArrayList<>(); - } - - List artworks = new ArrayList<>(); - for (MenuCell cell : cells) { - if (fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getIcon())) { - artworks.add(cell.getIcon()); - } - if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { - artworks.addAll(findAllArtworksToBeUploadedFromCells(cell.getSubCells())); - } - } - - return artworks; - } - - private boolean shouldRPCsIncludeImages(List cells) { - for (MenuCell cell : cells) { - SdlArtwork artwork = cell.getIcon(); - if (artwork != null && !artwork.isStaticIcon() && fileManager.get() != null && !fileManager.get().hasUploadedFile(artwork)) { - return false; - } else if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { - return shouldRPCsIncludeImages(cell.getSubCells()); - } - } - return true; - } - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - private boolean supportsImages() { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.cmdIcon); - } - - // IDs - - private void updateIdsOnDynamicCells(List dynamicCells) { - if (menuCells != null && menuCells.size() > 0 && dynamicCells != null && dynamicCells.size() > 0) { - for (int z = 0; z < menuCells.size(); z++) { - MenuCell mainCell = menuCells.get(z); - for (int i = 0; i < dynamicCells.size(); i++) { - MenuCell dynamicCell = dynamicCells.get(i); - if (mainCell.equals(dynamicCell)) { - int newId = ++lastMenuId; - menuCells.get(z).setCellId(newId); - dynamicCells.get(i).setCellId(newId); - - if (mainCell.getSubCells() != null && mainCell.getSubCells().size() > 0) { - updateIdsOnMenuCells(mainCell.getSubCells(), mainCell.getCellId()); - } - break; - } - } - } - } - } - - private List updateIdsOnDynamicSubCells(List oldList, List dynamicCells, Integer parentId) { - if (oldList != null && oldList.size() > 0 && dynamicCells != null && dynamicCells.size() > 0) { - for (int z = 0; z < oldList.size(); z++) { - MenuCell mainCell = oldList.get(z); - for (int i = 0; i < dynamicCells.size(); i++) { - MenuCell dynamicCell = dynamicCells.get(i); - if (mainCell.equals(dynamicCell)) { - int newId = ++lastMenuId; - oldList.get(z).setCellId(newId); - dynamicCells.get(i).setParentCellId(parentId); - dynamicCells.get(i).setCellId(newId); - } else { - int newId = ++lastMenuId; - dynamicCells.get(i).setParentCellId(parentId); - dynamicCells.get(i).setCellId(newId); - } - } - } - return dynamicCells; - } - return null; - } - private void updateIdsOnMenuCells(List cells, int parentId) { - for (MenuCell cell : cells) { - int newId = ++lastMenuId; - cell.setCellId(newId); - cell.setParentCellId(parentId); - if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { - updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); - } - } + transactionQueue.add(operation, false); } - private void transferIdsToKeptCells(List keeps) { - for (int z = 0; z < oldMenuCells.size(); z++) { - MenuCell oldCell = oldMenuCells.get(z); - for (int i = 0; i < keeps.size(); i++) { - MenuCell keptCell = keeps.get(i); - if (oldCell.equals(keptCell)) { - keptCell.setCellId(oldCell.getCellId()); - break; - } - } - } - } - - private void transferIdsToKeptSubCells(List old, List keeps) { - for (int z = 0; z < old.size(); z++) { - MenuCell oldCell = old.get(z); - for (int i = 0; i < keeps.size(); i++) { - MenuCell keptCell = keeps.get(i); - if (oldCell.equals(keptCell)) { - keptCell.setCellId(oldCell.getCellId()); - break; - } - } - } - } - - // DELETES - - private List createDeleteRPCsForCells(List cells) { - List deletes = new ArrayList<>(); - for (MenuCell cell : cells) { - if (cell.getSubCells() == null) { - DeleteCommand delete = new DeleteCommand(cell.getCellId()); - deletes.add(delete); - } else { - DeleteSubMenu delete = new DeleteSubMenu(cell.getCellId()); - deletes.add(delete); - } - } - return deletes; - } - - // COMMANDS / SUBMENU RPCs - - private List mainMenuCommandsForCells(List cellsToAdd, boolean shouldHaveArtwork) { - List builtCommands = new ArrayList<>(); - - // We need the index so we will use this type of loop - for (int z = 0; z < menuCells.size(); z++) { - MenuCell mainCell = menuCells.get(z); - for (int i = 0; i < cellsToAdd.size(); i++) { - MenuCell addCell = cellsToAdd.get(i); - if (mainCell.equals(addCell)) { - if (addCell.getSubCells() != null && addCell.getSubCells().size() > 0) { - builtCommands.add(subMenuCommandForMenuCell(addCell, shouldHaveArtwork, z)); - } else { - builtCommands.add(commandForMenuCell(addCell, shouldHaveArtwork, z)); - } - break; - } - } - } - return builtCommands; - } - - private List subMenuCommandsForCells(List cells, boolean shouldHaveArtwork) { - List builtCommands = new ArrayList<>(); - for (MenuCell cell : cells) { - if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { - builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); - } - } - return builtCommands; - } - - List allCommandsForCells(List cells, boolean shouldHaveArtwork) { - List builtCommands = new ArrayList<>(); - - // We need the index so we will use this type of loop - for (int i = 0; i < cells.size(); i++) { - MenuCell cell = cells.get(i); - if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { - builtCommands.add(subMenuCommandForMenuCell(cell, shouldHaveArtwork, i)); - // recursively grab the commands for all the sub cells - builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); - } else { - builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, i)); - } - } - return builtCommands; - } - - private List createCommandsForDynamicSubCells(List oldMenuCells, List cells, boolean shouldHaveArtwork) { - List builtCommands = new ArrayList<>(); - for (int z = 0; z < oldMenuCells.size(); z++) { - MenuCell oldCell = oldMenuCells.get(z); - for (int i = 0; i < cells.size(); i++) { - MenuCell cell = cells.get(i); - if (cell.equals(oldCell)) { - builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, z)); - break; - } - } - } - return builtCommands; - } - - private AddCommand commandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { - - MenuParams params = new MenuParams(cell.getTitle()); - params.setParentID(cell.getParentCellId() != MAX_ID ? cell.getParentCellId() : null); - params.setPosition(position); - - AddCommand command = new AddCommand(cell.getCellId()); - command.setMenuParams(params); - if (cell.getVoiceCommands() != null && !cell.getVoiceCommands().isEmpty()) { - command.setVrCommands(cell.getVoiceCommands()); - } else { - command.setVrCommands(null); - } - command.setCmdIcon((cell.getIcon() != null && shouldHaveArtwork) ? cell.getIcon().getImageRPC() : null); - - return command; - } - - private AddSubMenu subMenuCommandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { - AddSubMenu subMenu = new AddSubMenu(cell.getCellId(), cell.getTitle()); - subMenu.setPosition(position); - if (cell.getSubMenuLayout() != null) { - subMenu.setMenuLayout(cell.getSubMenuLayout()); - } else if (menuConfiguration != null && menuConfiguration.getSubMenuLayout() != null) { - subMenu.setMenuLayout(menuConfiguration.getSubMenuLayout()); - } - subMenu.setMenuIcon((shouldHaveArtwork && (cell.getIcon() != null && cell.getIcon().getImageRPC() != null)) ? cell.getIcon().getImageRPC() : null); - return subMenu; - } - - // CELL COMMAND HANDLING - - private boolean callListenerForCells(List cells, OnCommand command) { - if (cells != null && cells.size() > 0 && command != null) { - for (MenuCell cell : cells) { - - if (cell.getCellId() == command.getCmdID() && cell.getMenuSelectionListener() != null) { - cell.getMenuSelectionListener().onTriggered(command.getTriggerSource()); - return true; - } - if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { - // for each cell, if it has sub cells, recursively loop through those as well - if (callListenerForCells(cell.getSubCells(), command)) { - return true; - } - } - } - } - return false; + public MenuConfiguration getMenuConfiguration() { + return this.menuConfiguration; } - // LISTENERS - private void addListeners() { - // DISPLAY CAPABILITIES - via SCM onDisplaysCapabilityListener = new OnSystemCapabilityListener() { @Override public void onCapabilityRetrieved(Object capability) { // instead of using the parameter it's more safe to use the convenience method List capabilities = SystemCapabilityManager.convertToList(capability, DisplayCapability.class); if (capabilities == null || capabilities.size() == 0) { - DebugTool.logError(TAG, "SoftButton Manager - Capabilities sent here are null or empty"); + DebugTool.logError(TAG, "Capabilities sent here are null or empty"); } else { DisplayCapability display = capabilities.get(0); displayType = display.getDisplayName(); @@ -1035,7 +318,6 @@ abstract class BaseMenuManager extends BaseSubManager { this.internalInterface.getSystemCapabilityManager().addOnSystemCapabilityListener(SystemCapabilityType.DISPLAYS, onDisplaysCapabilityListener); } - // HMI UPDATES hmiListener = new OnRPCNotificationListener() { @Override public void onNotified(RPCNotification notification) { @@ -1043,36 +325,15 @@ abstract class BaseMenuManager extends BaseSubManager { if (onHMIStatus.getWindowID() != null && onHMIStatus.getWindowID() != PredefinedWindows.DEFAULT_WINDOW.getValue()) { return; } - HMILevel oldHMILevel = currentHMILevel; + oldHMILevel = currentHMILevel; currentHMILevel = onHMIStatus.getHmiLevel(); - - // Auto-send an updated menu if we were in NONE and now we are not, and we need an update - if (oldHMILevel == HMILevel.HMI_NONE && currentHMILevel != HMILevel.HMI_NONE && currentSystemContext != SystemContext.SYSCTXT_MENU) { - if (waitingOnHMIUpdate) { - DebugTool.logInfo(TAG, "We now have proper HMI, sending waiting update"); - setMenuCells(waitingUpdateMenuCells); - waitingUpdateMenuCells.clear(); - return; - } - } - - // If we don't check for this and only update when not in the menu, there can be IN_USE errors, especially with submenus. - // We also don't want to encourage changing out the menu while the user is using it for usability reasons. - SystemContext oldContext = currentSystemContext; + oldSystemContext = currentSystemContext; currentSystemContext = onHMIStatus.getSystemContext(); - - if (oldContext == SystemContext.SYSCTXT_MENU && currentSystemContext != SystemContext.SYSCTXT_MENU && currentHMILevel != HMILevel.HMI_NONE) { - if (waitingOnHMIUpdate) { - DebugTool.logInfo(TAG, "We now have a proper system context, sending waiting update"); - setMenuCells(waitingUpdateMenuCells); - waitingUpdateMenuCells.clear(); - } - } + updateTransactionQueueSuspended(); } }; internalInterface.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, hmiListener); - // COMMANDS commandListener = new OnRPCNotificationListener() { @Override public void onNotified(RPCNotification notification) { @@ -1083,265 +344,71 @@ abstract class BaseMenuManager extends BaseSubManager { internalInterface.addOnRPCNotificationListener(FunctionID.ON_COMMAND, commandListener); } - // SEND NEW MENU ITEMS - - private void createAndSendEntireMenu() { - - if (currentHMILevel == null || currentHMILevel.equals(HMILevel.HMI_NONE) || currentSystemContext.equals(SystemContext.SYSCTXT_MENU)) { - // We are in NONE or the menu is in use, bail out of here - DebugTool.logInfo(TAG, "HMI in None or System Context Menu, returning"); - waitingOnHMIUpdate = true; - waitingUpdateMenuCells = menuCells; - return; - } + private Queue newTransactionQueue() { + Queue queue = internalInterface.getTaskmaster().createQueue("MenuManager", 7, false); + queue.pause(); + return queue; + } - if (inProgressUpdate != null && inProgressUpdate.size() > 0) { - // there's an in-progress update so this needs to wait - DebugTool.logInfo(TAG, "There is an in progress Menu Update, returning"); - hasQueuedUpdate = true; - return; + private void updateTransactionQueueSuspended() { + if (oldHMILevel == HMILevel.HMI_NONE && currentHMILevel != HMILevel.HMI_NONE && currentSystemContext != SystemContext.SYSCTXT_MENU) { + // Resume queue if we were in NONE and now we are not + DebugTool.logInfo(TAG, "We now have proper HMI, sending waiting update"); + transactionQueue.resume(); + } else if (oldSystemContext == SystemContext.SYSCTXT_MENU && currentSystemContext != SystemContext.SYSCTXT_MENU && currentHMILevel != HMILevel.HMI_NONE) { + // If we don't check for this and only update when not in the menu, there can be IN_USE errors, especially with submenus. + // We also don't want to encourage changing out the menu while the user is using it for usability reasons. + DebugTool.logInfo(TAG, "We now have a proper system context, sending waiting update"); + transactionQueue.resume(); } - - deleteRootMenu(new CompletionListener() { - @Override - public void onComplete(boolean success) { - createAndSendMenuCellRPCs(menuCells, new CompletionListener() { - @Override - public void onComplete(boolean success) { - inProgressUpdate = null; - - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } - - if (hasQueuedUpdate) { - setMenuCells(waitingUpdateMenuCells); - hasQueuedUpdate = false; - } - } - }); - } - }); } - private void createAndSendMenuCellRPCs(final List menu, final CompletionListener listener) { - - if (menu.size() == 0) { - if (listener != null) { - // This can be considered a success if the user was clearing out their menu - listener.onComplete(true); - } - return; + private List cloneMenuCellsList(List originalList) { + if (originalList == null) { + return null; } - List mainMenuCommands; - final List subMenuCommands; - - if (!shouldRPCsIncludeImages(menu) || !supportsImages()) { - // Send artwork-less menu - mainMenuCommands = mainMenuCommandsForCells(menu, false); - subMenuCommands = subMenuCommandsForCells(menu, false); - } else { - mainMenuCommands = mainMenuCommandsForCells(menu, true); - subMenuCommands = subMenuCommandsForCells(menu, true); + List clone = new ArrayList<>(); + for (MenuCell menuCell : originalList) { + clone.add(menuCell.clone()); } - - // add all built commands to inProgressUpdate - inProgressUpdate = new ArrayList<>(mainMenuCommands); - inProgressUpdate.addAll(subMenuCommands); - - internalInterface.sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - // nothing here - } - - @Override - public void onFinished() { - - if (subMenuCommands.size() > 0) { - sendSubMenuCommandRPCs(subMenuCommands, listener); - DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); - } else { - - if (keepsNew != null && keepsNew.size() > 0) { - runSubMenuCompareAlgorithm(); - } else { - inProgressUpdate = null; - DebugTool.logInfo(TAG, "Finished sending main menu commands."); - } - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, "Main Menu response: " + response.serializeJSON().toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); - } - } - }); - } - - private void sendSubMenuCommandRPCs(List commands, final CompletionListener listener) { - - internalInterface.sendSequentialRPCs(commands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - - } - - @Override - public void onFinished() { - - if (keepsNew != null && keepsNew.size() > 0) { - runSubMenuCompareAlgorithm(); - } else { - DebugTool.logInfo(TAG, "Finished Updating Menu"); - inProgressUpdate = null; - - if (listener != null) { - listener.onComplete(true); - } - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, "Sub Menu response: " + response.serializeJSON().toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - DebugTool.logError(TAG, "Failed to send sub menu commands: " + response.getInfo()); - if (listener != null) { - listener.onComplete(false); - } - } - } - }); + return clone; } - private void createAndSendDynamicSubMenuRPCs(List newMenu, final List adds, final CompletionListener listener) { - - if (adds.size() == 0) { - if (listener != null) { - // This can be considered a success if the user was clearing out their menu - DebugTool.logError(TAG, "Called createAndSendDynamicSubMenuRPCs with empty menu"); - listener.onComplete(true); - } - return; - } - - List mainMenuCommands; - - if (!shouldRPCsIncludeImages(adds) || !supportsImages()) { - // Send artwork-less menu - mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, false); - } else { - mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, true); + private boolean callListenerForCells(List cells, OnCommand command) { + if (cells == null || cells.isEmpty() || command == null) { + return false; } - internalInterface.sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - // nothing here + for (MenuCell cell : cells) { + if (cell.getCellId() == command.getCmdID() && cell.getMenuSelectionListener() != null) { + cell.getMenuSelectionListener().onTriggered(command.getTriggerSource()); + return true; } - - @Override - public void onFinished() { - - if (listener != null) { - listener.onComplete(true); - } + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + // for each cell, if it has sub cells, recursively loop through those as well + return callListenerForCells(cell.getSubCells(), command); } + } - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, "Dynamic Sub Menu response: " + response.serializeJSON().toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); - } - } - }); + return false; } - // DELETE OLD MENU ITEMS - - private void deleteRootMenu(final CompletionListener listener) { - - if (oldMenuCells == null || oldMenuCells.size() == 0) { - if (listener != null) { - // technically this method is successful if there's nothing to delete - DebugTool.logInfo(TAG, "No old cells to delete, returning"); - listener.onComplete(true); + private void updatePendingOperationsWithNewMenuConfiguration() { + for (Task task : transactionQueue.getTasksAsList()) { + if (!(task instanceof MenuReplaceOperation)) { + continue; } - } else { - sendDeleteRPCs(createDeleteRPCsForCells(oldMenuCells), listener); + ((MenuReplaceOperation) task).setMenuConfiguration(menuConfiguration); } } - private void sendDeleteRPCs(List deleteCommands, final CompletionListener listener) { - if (oldMenuCells != null && oldMenuCells.size() == 0) { - if (listener != null) { - // technically this method is successful if there's nothing to delete - DebugTool.logInfo(TAG, "No old cells to delete, returning"); - listener.onComplete(true); - } - return; - } - - if (deleteCommands == null || deleteCommands.size() == 0) { - // no dynamic deletes required. return - if (listener != null) { - // technically this method is successful if there's nothing to delete - listener.onComplete(true); + private void cancelPendingMenuReplaceOperations(){ + for (Task operation : transactionQueue.getTasksAsList()) { + if (!(operation instanceof MenuReplaceOperation)) { + continue; } - return; + operation.cancelTask(); } - - internalInterface.sendRPCs(deleteCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - - } - - @Override - public void onFinished() { - DebugTool.logInfo(TAG, "Successfully deleted cells"); - if (listener != null) { - listener.onComplete(true); - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - - } - }); - } - - private List cloneMenuCellsList(List originalList) { - if (originalList == null) { - return null; - } - - List clone = new ArrayList<>(); - for (MenuCell menuCell : originalList) { - clone.add(menuCell.clone()); - } - return clone; } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java new file mode 100644 index 000000000..b74014617 --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2021 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.menu; + +import com.livio.taskmaster.Task; +import com.smartdevicelink.managers.CompletionListener; +import com.smartdevicelink.managers.ISdl; +import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.rpc.SetGlobalProperties; +import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener; +import com.smartdevicelink.util.DebugTool; + +import java.lang.ref.WeakReference; + +/** + * Created by Bilal Alsharifi on 1/21/21. + */ +class MenuConfigurationUpdateOperation extends Task { + private static final String TAG = "MenuConfigurationUpdateOperation"; + private final WeakReference internalInterface; + private MenuConfiguration menuConfiguration; + private CompletionListener completionListener; + + MenuConfigurationUpdateOperation(ISdl internalInterface, MenuConfiguration menuConfiguration, CompletionListener completionListener) { + super(TAG); + this.internalInterface = new WeakReference<>(internalInterface); + this.menuConfiguration = menuConfiguration; + this.completionListener = completionListener; + } + + @Override + public void onExecute() { + start(); + } + + private void start() { + if (getState() == Task.CANCELED) { + return; + } + + sendSetGlobalProperties(); + } + + private void sendSetGlobalProperties() { + SetGlobalProperties setGlobalProperties = new SetGlobalProperties(); + setGlobalProperties.setMenuLayout(menuConfiguration.getMenuLayout()); + setGlobalProperties.setOnRPCResponseListener(new OnRPCResponseListener() { + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + DebugTool.logInfo(TAG, "Menu Configuration successfully set: " + menuConfiguration.toString()); + } else { + DebugTool.logError(TAG, "onError: " + response.getResultCode() + " | Info: " + response.getInfo()); + } + completionListener.onComplete(response.getSuccess()); + onFinished(); + } + }); + if (internalInterface.get() != null) { + internalInterface.get().sendRPC(setGlobalProperties); + } + } +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuManagerCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuManagerCompletionListener.java new file mode 100644 index 000000000..c856491af --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuManagerCompletionListener.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 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.menu; + +import java.util.List; + +/** + * Created by Bilal Alsharifi on 1/21/21. + */ +interface MenuManagerCompletionListener { + void onComplete(boolean success, int lastMenuId, List oldMenuCells); +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java new file mode 100644 index 000000000..75543d58b --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -0,0 +1,882 @@ +package com.smartdevicelink.managers.screen.menu; + +import com.livio.taskmaster.Task; +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.proxy.RPCRequest; +import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.rpc.AddCommand; +import com.smartdevicelink.proxy.rpc.AddSubMenu; +import com.smartdevicelink.proxy.rpc.DeleteCommand; +import com.smartdevicelink.proxy.rpc.DeleteSubMenu; +import com.smartdevicelink.proxy.rpc.MenuParams; +import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.DisplayType; +import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; +import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; +import com.smartdevicelink.util.DebugTool; + +import org.json.JSONException; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.MAX_ID; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; + +/** + * Created by Bilal Alsharifi on 1/20/21. + */ +class MenuReplaceOperation extends Task { + private static final String TAG = "MenuReplaceOperation"; + private final WeakReference internalInterface; + private final WeakReference fileManager; + private final WindowCapability defaultMainWindowCapability; + private List menuCells, oldMenuCells, keepsNew, keepsOld; + private final MenuManagerCompletionListener completionListener; + private final String displayType; + private final DynamicMenuUpdatesMode dynamicMenuUpdatesMode; + private int lastMenuId; + private MenuConfiguration menuConfiguration; + + private static final int KEEP = 0; + private static final int MARKED_FOR_ADDITION = 1; + private static final int MARKED_FOR_DELETION = 2; + + // todo split to static and dynamic operations + // todo call onFinish & listener when done + // todo add Task.CANCELED checks + + MenuReplaceOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode,MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, int lastMenuId, MenuManagerCompletionListener completionListener) { + super(TAG); + this.internalInterface = new WeakReference<>(internalInterface); + this.fileManager = new WeakReference<>(fileManager); + this.displayType = displayType; + this.dynamicMenuUpdatesMode = dynamicMenuUpdatesMode; + this.menuConfiguration = menuConfiguration; + this.defaultMainWindowCapability = defaultMainWindowCapability; + this.oldMenuCells = oldMenuCells; + this.menuCells = menuCells; + this.lastMenuId = lastMenuId; + this.completionListener = completionListener; + } + + @Override + public void onExecute() { + start(); + } + + private void start() { + if (getState() == Task.CANCELED) { + return; + } + + // Upload the Artworks + List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(menuCells); + if (artworksToBeUploaded.size() > 0 && fileManager.get() != null) { + fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { + @Override + public void onComplete(Map errors) { + + if (errors != null && errors.size() > 0) { + DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); + } else { + DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); + } + // proceed + updateMenuAndDetermineBestUpdateMethod(); + } + }); + } else { + // No Artworks to be uploaded, send off + updateMenuAndDetermineBestUpdateMethod(); + } + } + + private void updateMenuAndDetermineBestUpdateMethod() { + // Checks against what the developer set for update mode and against the display type + // to determine how the menu will be updated. This has the ability to be changed during + // a session. + if (checkUpdateMode(dynamicMenuUpdatesMode, displayType)) { + // run the lists through the new algorithm + RunScore rootScore = runMenuCompareAlgorithm(oldMenuCells, menuCells); + if (rootScore == null) { + // send initial menu (score will return null) + // make a copy of our current cells + DebugTool.logInfo(TAG, "Creating initial Menu"); + // Set the IDs if needed + lastMenuId = menuCellIdMin; + updateIdsOnMenuCells(menuCells, parentIdNotFound); + this.oldMenuCells = new ArrayList<>(menuCells); + createAndSendEntireMenu(); + } else { + DebugTool.logInfo(TAG, "Dynamically Updating Menu"); + if (menuCells.size() == 0 && (oldMenuCells != null && oldMenuCells.size() > 0)) { + // the dev wants to clear the menu. We have old cells and an empty array of new ones. + deleteMenuWhenNewCellsEmpty(); + } else { + // lets dynamically update the root menu + dynamicallyUpdateRootMenu(rootScore); + } + } + } else { + // we are in compatibility mode + DebugTool.logInfo(TAG, "Updating menus in compatibility mode"); + lastMenuId = menuCellIdMin; + updateIdsOnMenuCells(menuCells, parentIdNotFound); + // if the old cell array is not null, we want to delete the entire thing, else copy the new array + if (oldMenuCells == null) { + this.oldMenuCells = new ArrayList<>(menuCells); + } + createAndSendEntireMenu(); + } + } + + + private boolean checkUpdateMode(DynamicMenuUpdatesMode updateMode, String displayType) { + + if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { + if (displayType == null) { + return true; + } + return (!displayType.equals(DisplayType.GEN3_8_INCH.toString())); + + } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_OFF)) { + return false; + } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_ON)) { + return true; + } + + return true; + } + + private void deleteMenuWhenNewCellsEmpty() { + sendDeleteRPCs(createDeleteRPCsForCells(oldMenuCells), new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (!success) { + DebugTool.logError(TAG, "Error Sending Current Menu"); + } else { + DebugTool.logInfo(TAG, "Successfully Cleared Menu"); + } + oldMenuCells = null; + } + }); + } + + private void dynamicallyUpdateRootMenu(RunScore bestRootScore) { + // we need to run through the keeps and see if they have subCells, as they also need to be run + // through the compare function. + List newIntArray = bestRootScore.getCurrentMenu(); + List oldIntArray = bestRootScore.getOldMenu(); + List deleteCommands; + + // Set up deletes + List deletes = new ArrayList<>(); + keepsOld = new ArrayList<>(); + for (int x = 0; x < oldIntArray.size(); x++) { + Integer old = oldIntArray.get(x); + if (old.equals(MARKED_FOR_DELETION)) { + // grab cell to send to function to create delete commands + deletes.add(oldMenuCells.get(x)); + } else if (old.equals(KEEP)) { + keepsOld.add(oldMenuCells.get(x)); + } + } + // create the delete commands + deleteCommands = createDeleteRPCsForCells(deletes); + + // Set up the adds + List adds = new ArrayList<>(); + keepsNew = new ArrayList<>(); + for (int x = 0; x < newIntArray.size(); x++) { + Integer newInt = newIntArray.get(x); + if (newInt.equals(MARKED_FOR_ADDITION)) { + // grab cell to send to function to create add commands + adds.add(menuCells.get(x)); + } else if (newInt.equals(KEEP)) { + keepsNew.add(menuCells.get(x)); + } + } + updateIdsOnDynamicCells(adds); + // this is needed for the onCommands to still work + transferIdsToKeptCells(keepsNew); + + if (adds.size() > 0) { + DebugTool.logInfo(TAG, "Sending root menu updates"); + sendDynamicRootMenuRPCs(deleteCommands, adds); + } else { + DebugTool.logInfo(TAG, "All root menu items are kept. Check the sub menus"); + runSubMenuCompareAlgorithm(); + } + } + + private void createAndSendEntireMenu() { + deleteRootMenu(new CompletionListener() { + @Override + public void onComplete(boolean success) { + createAndSendMenuCellRPCs(menuCells, new CompletionListener() { + @Override + public void onComplete(boolean success) { + + if (!success) { + DebugTool.logError(TAG, "Error Sending Current Menu"); + } + + MenuReplaceOperation.this.completionListener.onComplete(success, lastMenuId, oldMenuCells); + onFinished(); + } + }); + } + }); + } + + private void createAndSendMenuCellRPCs(final List menu, final CompletionListener listener) { + if (menu.size() == 0) { + if (listener != null) { + // This can be considered a success if the user was clearing out their menu + listener.onComplete(true); + } + return; + } + + List mainMenuCommands; + final List subMenuCommands; + + if (!shouldRPCsIncludeImages(menu) || !supportsImages()) { + // Send artwork-less menu + mainMenuCommands = mainMenuCommandsForCells(menu, false); + subMenuCommands = subMenuCommandsForCells(menu, false); + } else { + mainMenuCommands = mainMenuCommandsForCells(menu, true); + subMenuCommands = subMenuCommandsForCells(menu, true); + } + + + internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + // nothing here + } + + @Override + public void onFinished() { + + if (subMenuCommands.size() > 0) { + sendSubMenuCommandRPCs(subMenuCommands, listener); + DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); + } else { + + if (keepsNew != null && keepsNew.size() > 0) { + runSubMenuCompareAlgorithm(); + } else { + DebugTool.logInfo(TAG, "Finished sending main menu commands."); + } + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + try { + DebugTool.logInfo(TAG, "Main Menu response: " + response.serializeJSON().toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); + } + } + }); + } + + private void sendSubMenuCommandRPCs(List commands, final CompletionListener listener) { + + internalInterface.get().sendSequentialRPCs(commands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + + } + + @Override + public void onFinished() { + + if (keepsNew != null && keepsNew.size() > 0) { + runSubMenuCompareAlgorithm(); + } else { + DebugTool.logInfo(TAG, "Finished Updating Menu"); + + if (listener != null) { + listener.onComplete(true); + } + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + try { + DebugTool.logInfo(TAG, "Sub Menu response: " + response.serializeJSON().toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + DebugTool.logError(TAG, "Failed to send sub menu commands: " + response.getInfo()); + if (listener != null) { + listener.onComplete(false); + } + } + } + }); + } + + private void createAndSendDynamicSubMenuRPCs(List newMenu, final List adds, final CompletionListener listener) { + + if (adds.size() == 0) { + if (listener != null) { + // This can be considered a success if the user was clearing out their menu + DebugTool.logError(TAG, "Called createAndSendDynamicSubMenuRPCs with empty menu"); + listener.onComplete(true); + } + return; + } + + List mainMenuCommands; + + if (!shouldRPCsIncludeImages(adds) || !supportsImages()) { + // Send artwork-less menu + mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, false); + } else { + mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, true); + } + + internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + // nothing here + } + + @Override + public void onFinished() { + + if (listener != null) { + listener.onComplete(true); + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + try { + DebugTool.logInfo(TAG, "Dynamic Sub Menu response: " + response.serializeJSON().toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); + } + } + }); + } + + // DELETE OLD MENU ITEMS + + private void deleteRootMenu(final CompletionListener listener) { + + if (oldMenuCells == null || oldMenuCells.size() == 0) { + if (listener != null) { + // technically this method is successful if there's nothing to delete + DebugTool.logInfo(TAG, "No old cells to delete, returning"); + listener.onComplete(true); + } + } else { + sendDeleteRPCs(createDeleteRPCsForCells(oldMenuCells), listener); + } + } + + private void sendDeleteRPCs(List deleteCommands, final CompletionListener listener) { + if (oldMenuCells != null && oldMenuCells.size() == 0) { + if (listener != null) { + // technically this method is successful if there's nothing to delete + DebugTool.logInfo(TAG, "No old cells to delete, returning"); + listener.onComplete(true); + } + return; + } + + if (deleteCommands == null || deleteCommands.size() == 0) { + // no dynamic deletes required. return + if (listener != null) { + // technically this method is successful if there's nothing to delete + listener.onComplete(true); + } + return; + } + + internalInterface.get().sendRPCs(deleteCommands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + + } + + @Override + public void onFinished() { + DebugTool.logInfo(TAG, "Successfully deleted cells"); + if (listener != null) { + listener.onComplete(true); + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + + } + }); + } + + private List subMenuCommandsForCells(List cells, boolean shouldHaveArtwork) { + List builtCommands = new ArrayList<>(); + for (MenuCell cell : cells) { + if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { + builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); + } + } + return builtCommands; + } + + List allCommandsForCells(List cells, boolean shouldHaveArtwork) { + List builtCommands = new ArrayList<>(); + + // We need the index so we will use this type of loop + for (int i = 0; i < cells.size(); i++) { + MenuCell cell = cells.get(i); + if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { + builtCommands.add(subMenuCommandForMenuCell(cell, shouldHaveArtwork, i)); + // recursively grab the commands for all the sub cells + builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); + } else { + builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, i)); + } + } + return builtCommands; + } + + private List createCommandsForDynamicSubCells(List oldMenuCells, List cells, boolean shouldHaveArtwork) { + List builtCommands = new ArrayList<>(); + for (int z = 0; z < oldMenuCells.size(); z++) { + MenuCell oldCell = oldMenuCells.get(z); + for (int i = 0; i < cells.size(); i++) { + MenuCell cell = cells.get(i); + if (cell.equals(oldCell)) { + builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, z)); + break; + } + } + } + return builtCommands; + } + + private AddCommand commandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { + + MenuParams params = new MenuParams(cell.getTitle()); + params.setParentID(cell.getParentCellId() != MAX_ID ? cell.getParentCellId() : null); + params.setPosition(position); + + AddCommand command = new AddCommand(cell.getCellId()); + command.setMenuParams(params); + if (cell.getVoiceCommands() != null && !cell.getVoiceCommands().isEmpty()) { + command.setVrCommands(cell.getVoiceCommands()); + } else { + command.setVrCommands(null); + } + command.setCmdIcon((cell.getIcon() != null && shouldHaveArtwork) ? cell.getIcon().getImageRPC() : null); + + return command; + } + + private AddSubMenu subMenuCommandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { + AddSubMenu subMenu = new AddSubMenu(cell.getCellId(), cell.getTitle()); + subMenu.setPosition(position); + if (cell.getSubMenuLayout() != null) { + subMenu.setMenuLayout(cell.getSubMenuLayout()); + } else if (menuConfiguration != null && menuConfiguration.getSubMenuLayout() != null) { + subMenu.setMenuLayout(menuConfiguration.getSubMenuLayout()); + } + subMenu.setMenuIcon((shouldHaveArtwork && (cell.getIcon() != null && cell.getIcon().getImageRPC() != null)) ? cell.getIcon().getImageRPC() : null); + return subMenu; + } + + private void sendDynamicRootMenuRPCs(List deleteCommands, final List updatedCells) { + sendDeleteRPCs(deleteCommands, new CompletionListener() { + @Override + public void onComplete(boolean success) { + createAndSendMenuCellRPCs(updatedCells, new CompletionListener() { + @Override + public void onComplete(boolean success) { + + if (!success) { + DebugTool.logError(TAG, "Error Sending Current Menu"); + } + } + }); + } + }); + } + + // SUB MENUS + + // This is called in the listener in the sendMenu and sendSubMenuCommands Methods + private void runSubMenuCompareAlgorithm() { + // any cells that were re-added have their sub-cells added with them + // at this point all we care about are the cells that were deemed equal and kept. + if (keepsNew == null || keepsNew.size() == 0) { + return; + } + + List commandLists = new ArrayList<>(); + + for (int i = 0; i < keepsNew.size(); i++) { + + MenuCell keptCell = keepsNew.get(i); + MenuCell oldKeptCell = keepsOld.get(i); + + if (oldKeptCell.getSubCells() != null && oldKeptCell.getSubCells().size() > 0 && keptCell.getSubCells() != null && keptCell.getSubCells().size() > 0) { + // ACTUAL LOGIC + RunScore subScore = compareOldAndNewLists(oldKeptCell.getSubCells(), keptCell.getSubCells()); + + if (subScore != null) { + DebugTool.logInfo(TAG, "Sub menu Run Score: " + oldKeptCell.getTitle() + " Score: " + subScore.getScore()); + SubCellCommandList commandList = new SubCellCommandList(oldKeptCell.getTitle(), oldKeptCell.getCellId(), subScore, oldKeptCell.getSubCells(), keptCell.getSubCells()); + commandLists.add(commandList); + } + } + } + createSubMenuDynamicCommands(commandLists); + } + + private void createSubMenuDynamicCommands(final List commandLists) { + + // break out + if (commandLists.size() == 0) { +// if (hasQueuedUpdate) { +// DebugTool.logInfo(TAG, "Menu Manager has waiting updates, sending now"); +// setMenuCells(waitingUpdateMenuCells); +// } + DebugTool.logInfo(TAG, "All menu updates, including sub menus - done."); + return; + } + + final SubCellCommandList commandList = commandLists.remove(0); + + DebugTool.logInfo(TAG, "Creating and Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); + + // grab the scores + RunScore score = commandList.getListsScore(); + List newIntArray = score.getCurrentMenu(); + List oldIntArray = score.getOldMenu(); + + // Grab the sub-menus from the parent cell + final List oldCells = commandList.getOldList(); + final List newCells = commandList.getNewList(); + + // Create the list for the adds + List subCellKeepsNew = new ArrayList<>(); + + List deleteCommands; + + // Set up deletes + List deletes = new ArrayList<>(); + for (int x = 0; x < oldIntArray.size(); x++) { + Integer old = oldIntArray.get(x); + if (old.equals(MARKED_FOR_DELETION)) { + // grab cell to send to function to create delete commands + deletes.add(oldCells.get(x)); + } + } + // create the delete commands + deleteCommands = createDeleteRPCsForCells(deletes); + + // Set up the adds + List adds = new ArrayList<>(); + for (int x = 0; x < newIntArray.size(); x++) { + Integer newInt = newIntArray.get(x); + if (newInt.equals(MARKED_FOR_ADDITION)) { + // grab cell to send to function to create add commands + adds.add(newCells.get(x)); + } else if (newInt.equals(KEEP)) { + subCellKeepsNew.add(newCells.get(x)); + } + } + final List addsWithNewIds = updateIdsOnDynamicSubCells(oldCells, adds, commandList.getParentId()); + // this is needed for the onCommands to still work + transferIdsToKeptSubCells(oldCells, subCellKeepsNew); + + sendDeleteRPCs(deleteCommands, new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (addsWithNewIds != null && addsWithNewIds.size() > 0) { + createAndSendDynamicSubMenuRPCs(newCells, addsWithNewIds, new CompletionListener() { + @Override + public void onComplete(boolean success) { + // recurse through next sub list + DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); + createSubMenuDynamicCommands(commandLists); + } + }); + } else { + // no add commands to send, recurse through next sub list + DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); + createSubMenuDynamicCommands(commandLists); + } + } + }); + } + + // OTHER HELPER METHODS: + + // COMPARISONS + + RunScore runMenuCompareAlgorithm(List oldCells, List newCells) { + + if (oldCells == null || oldCells.size() == 0) { + return null; + } + + RunScore bestScore = compareOldAndNewLists(oldCells, newCells); + DebugTool.logInfo(TAG, "Best menu run score: " + bestScore.getScore()); + + return bestScore; + } + + private RunScore compareOldAndNewLists(List oldCells, List newCells) { + + RunScore bestRunScore = null; + + // This first loop is for each 'run' + for (int run = 0; run < oldCells.size(); run++) { + + List oldArray = new ArrayList<>(oldCells.size()); + List newArray = new ArrayList<>(newCells.size()); + + // Set the statuses + setDeleteStatus(oldCells.size(), oldArray); + setAddStatus(newCells.size(), newArray); + + int startIndex = 0; + + // Keep items that appear in both lists + for (int oldItems = run; oldItems < oldCells.size(); oldItems++) { + + for (int newItems = startIndex; newItems < newCells.size(); newItems++) { + + if (oldCells.get(oldItems).equals(newCells.get(newItems))) { + oldArray.set(oldItems, KEEP); + newArray.set(newItems, KEEP); + // set the new start index + startIndex = newItems + 1; + break; + } + } + } + + // Calculate number of adds, or the 'score' for this run + int numberOfAdds = 0; + + for (int x = 0; x < newArray.size(); x++) { + if (newArray.get(x).equals(MARKED_FOR_ADDITION)) { + numberOfAdds++; + } + } + + // see if we have a new best score and set it if we do + if (bestRunScore == null || numberOfAdds < bestRunScore.getScore()) { + bestRunScore = new RunScore(numberOfAdds, oldArray, newArray); + } + + } + return bestRunScore; + } + + private void setDeleteStatus(Integer size, List oldArray) { + for (int i = 0; i < size; i++) { + oldArray.add(MARKED_FOR_DELETION); + } + } + + private void setAddStatus(Integer size, List newArray) { + for (int i = 0; i < size; i++) { + newArray.add(MARKED_FOR_ADDITION); + } + } + + private boolean shouldRPCsIncludeImages(List cells) { + for (MenuCell cell : cells) { + SdlArtwork artwork = cell.getIcon(); + if (artwork != null && !artwork.isStaticIcon() && fileManager.get() != null && !fileManager.get().hasUploadedFile(artwork)) { + return false; + } else if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { + return shouldRPCsIncludeImages(cell.getSubCells()); + } + } + return true; + } + + // IDs + + private void updateIdsOnDynamicCells(List dynamicCells) { + if (menuCells != null && menuCells.size() > 0 && dynamicCells != null && dynamicCells.size() > 0) { + for (int z = 0; z < menuCells.size(); z++) { + MenuCell mainCell = menuCells.get(z); + for (int i = 0; i < dynamicCells.size(); i++) { + MenuCell dynamicCell = dynamicCells.get(i); + if (mainCell.equals(dynamicCell)) { + int newId = ++lastMenuId; + menuCells.get(z).setCellId(newId); + dynamicCells.get(i).setCellId(newId); + + if (mainCell.getSubCells() != null && mainCell.getSubCells().size() > 0) { + updateIdsOnMenuCells(mainCell.getSubCells(), mainCell.getCellId()); + } + break; + } + } + } + } + } + + private List updateIdsOnDynamicSubCells(List oldList, List dynamicCells, Integer parentId) { + if (oldList != null && oldList.size() > 0 && dynamicCells != null && dynamicCells.size() > 0) { + for (int z = 0; z < oldList.size(); z++) { + MenuCell mainCell = oldList.get(z); + for (int i = 0; i < dynamicCells.size(); i++) { + MenuCell dynamicCell = dynamicCells.get(i); + if (mainCell.equals(dynamicCell)) { + int newId = ++lastMenuId; + oldList.get(z).setCellId(newId); + dynamicCells.get(i).setParentCellId(parentId); + dynamicCells.get(i).setCellId(newId); + } else { + int newId = ++lastMenuId; + dynamicCells.get(i).setParentCellId(parentId); + dynamicCells.get(i).setCellId(newId); + } + } + } + return dynamicCells; + } + return null; + } + + private void updateIdsOnMenuCells(List cells, int parentId) { + for (MenuCell cell : cells) { + int newId = ++lastMenuId; + cell.setCellId(newId); + cell.setParentCellId(parentId); + if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { + updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); + } + } + } + + private void transferIdsToKeptCells(List keeps) { + for (int z = 0; z < oldMenuCells.size(); z++) { + MenuCell oldCell = oldMenuCells.get(z); + for (int i = 0; i < keeps.size(); i++) { + MenuCell keptCell = keeps.get(i); + if (oldCell.equals(keptCell)) { + keptCell.setCellId(oldCell.getCellId()); + break; + } + } + } + } + + private void transferIdsToKeptSubCells(List old, List keeps) { + for (int z = 0; z < old.size(); z++) { + MenuCell oldCell = old.get(z); + for (int i = 0; i < keeps.size(); i++) { + MenuCell keptCell = keeps.get(i); + if (oldCell.equals(keptCell)) { + keptCell.setCellId(oldCell.getCellId()); + break; + } + } + } + } + + // DELETES + + private List createDeleteRPCsForCells(List cells) { + List deletes = new ArrayList<>(); + for (MenuCell cell : cells) { + if (cell.getSubCells() == null) { + DeleteCommand delete = new DeleteCommand(cell.getCellId()); + deletes.add(delete); + } else { + DeleteSubMenu delete = new DeleteSubMenu(cell.getCellId()); + deletes.add(delete); + } + } + return deletes; + } + + // COMMANDS / SUBMENU RPCs + + private List mainMenuCommandsForCells(List cellsToAdd, boolean shouldHaveArtwork) { + List builtCommands = new ArrayList<>(); + + // We need the index so we will use this type of loop + for (int z = 0; z < menuCells.size(); z++) { + MenuCell mainCell = menuCells.get(z); + for (int i = 0; i < cellsToAdd.size(); i++) { + MenuCell addCell = cellsToAdd.get(i); + if (mainCell.equals(addCell)) { + if (addCell.getSubCells() != null && addCell.getSubCells().size() > 0) { + builtCommands.add(subMenuCommandForMenuCell(addCell, shouldHaveArtwork, z)); + } else { + builtCommands.add(commandForMenuCell(addCell, shouldHaveArtwork, z)); + } + break; + } + } + } + return builtCommands; + } + + + + private List findAllArtworksToBeUploadedFromCells(List cells) { + // Make sure we can use images in the menus + if (!supportsImages()) { + return new ArrayList<>(); + } + + List artworks = new ArrayList<>(); + for (MenuCell cell : cells) { + if (fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getIcon())) { + artworks.add(cell.getIcon()); + } + if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { + artworks.addAll(findAllArtworksToBeUploadedFromCells(cell.getSubCells())); + } + } + + return artworks; + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + private boolean supportsImages() { + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.cmdIcon); + } + + void setMenuConfiguration (MenuConfiguration menuConfiguration) { + this.menuConfiguration = menuConfiguration; + } +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java new file mode 100644 index 000000000..1413ddcee --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2021 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.menu; + +import com.livio.taskmaster.Task; +import com.smartdevicelink.managers.ISdl; +import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.rpc.ShowAppMenu; +import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener; +import com.smartdevicelink.util.DebugTool; + +import java.lang.ref.WeakReference; + +/** + * Created by Bilal Alsharifi on 1/21/21. + */ +class MenuShowOperation extends Task { + private static final String TAG = "MenuShowOperation"; + private final WeakReference internalInterface; + private Integer menuId; + + MenuShowOperation(ISdl internalInterface, Integer menuId) { + super(TAG); + this.internalInterface = new WeakReference<>(internalInterface); + this.menuId = menuId; + } + + @Override + public void onExecute() { + start(); + } + + private void start() { + if (getState() == Task.CANCELED) { + return; + } + + sendShowAppMenu(this.menuId); + } + + private void sendShowAppMenu(Integer id) { + ShowAppMenu showAppMenu = new ShowAppMenu(); + showAppMenu.setMenuID(id); + showAppMenu.setOnRPCResponseListener(new OnRPCResponseListener() { + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + DebugTool.logInfo(TAG, "Open Menu Request Successful"); + } else { + DebugTool.logError(TAG, "Open Menu Request Failed"); + } + onFinished(); + } + }); + + if (internalInterface.get() != null) { + internalInterface.get().sendRPC(showAppMenu); + } + } +} -- cgit v1.2.1 From 8d3a750203e260d94872d6603845d66268a83104 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 22 Jan 2021 10:36:24 -0500 Subject: Update modifiers --- .../managers/screen/menu/BaseMenuManager.java | 20 ++++++++++++-------- .../menu/MenuConfigurationUpdateOperation.java | 4 ++-- .../managers/screen/menu/MenuReplaceOperation.java | 5 ++++- .../managers/screen/menu/MenuShowOperation.java | 2 +- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 6abb4d3d2..c031ec667 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -64,21 +64,25 @@ import java.util.List; abstract class BaseMenuManager extends BaseSubManager { private static final String TAG = "BaseMenuManager"; + static final int MAX_ID = 2000000000; + static final int menuCellIdMin = 1; + static final int parentIdNotFound = MAX_ID; private final WeakReference fileManager; - private List menuCells, oldMenuCells; + private List menuCells; + private List oldMenuCells; private DynamicMenuUpdatesMode dynamicMenuUpdatesMode; private MenuConfiguration menuConfiguration; private SdlMsgVersion sdlMsgVersion; private String displayType; - private HMILevel currentHMILevel, oldHMILevel; - private SystemContext currentSystemContext, oldSystemContext; - private OnRPCNotificationListener hmiListener, commandListener; + private HMILevel oldHMILevel; + private HMILevel currentHMILevel; + private SystemContext oldSystemContext; + private SystemContext currentSystemContext; + private OnRPCNotificationListener hmiListener; + private OnRPCNotificationListener commandListener; private OnSystemCapabilityListener onDisplaysCapabilityListener; private WindowCapability defaultMainWindowCapability; - static final int MAX_ID = 2000000000; - static final int menuCellIdMin = 1; - static final int parentIdNotFound = MAX_ID; private int lastMenuId; private Queue transactionQueue; @@ -160,7 +164,7 @@ abstract class BaseMenuManager extends BaseSubManager { menuCells.addAll(clonedCells); } - // HashSet order doesn't matter / does not allow duplicates + // HashSet order doesn't matter / doesn't allow duplicates HashSet titleCheckSet = new HashSet<>(); HashSet allMenuVoiceCommands = new HashSet<>(); int voiceCommandCount = 0; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java index b74014617..f2bada292 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java @@ -48,8 +48,8 @@ import java.lang.ref.WeakReference; class MenuConfigurationUpdateOperation extends Task { private static final String TAG = "MenuConfigurationUpdateOperation"; private final WeakReference internalInterface; - private MenuConfiguration menuConfiguration; - private CompletionListener completionListener; + private final MenuConfiguration menuConfiguration; + private final CompletionListener completionListener; MenuConfigurationUpdateOperation(ISdl internalInterface, MenuConfiguration menuConfiguration, CompletionListener completionListener) { super(TAG); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 75543d58b..070c3be3c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -39,7 +39,10 @@ class MenuReplaceOperation extends Task { private final WeakReference internalInterface; private final WeakReference fileManager; private final WindowCapability defaultMainWindowCapability; - private List menuCells, oldMenuCells, keepsNew, keepsOld; + private final List menuCells; + private List oldMenuCells; + private List keepsNew; + private List keepsOld; private final MenuManagerCompletionListener completionListener; private final String displayType; private final DynamicMenuUpdatesMode dynamicMenuUpdatesMode; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java index 1413ddcee..86a42c79f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java @@ -47,7 +47,7 @@ import java.lang.ref.WeakReference; class MenuShowOperation extends Task { private static final String TAG = "MenuShowOperation"; private final WeakReference internalInterface; - private Integer menuId; + private final Integer menuId; MenuShowOperation(ISdl internalInterface, Integer menuId) { super(TAG); -- cgit v1.2.1 From f70656bab9cae7106ddb1a4f77cf57a26adf440c Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 22 Jan 2021 10:52:41 -0500 Subject: Add finishOperation --- .../managers/screen/menu/MenuReplaceOperation.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 070c3be3c..da869d486 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -233,8 +233,7 @@ class MenuReplaceOperation extends Task { DebugTool.logError(TAG, "Error Sending Current Menu"); } - MenuReplaceOperation.this.completionListener.onComplete(success, lastMenuId, oldMenuCells); - onFinished(); + finishOperation(success); } }); } @@ -882,4 +881,11 @@ class MenuReplaceOperation extends Task { void setMenuConfiguration (MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } + + private void finishOperation(boolean success) { + if (completionListener != null) { + completionListener.onComplete(success, lastMenuId, oldMenuCells); + } + onFinished(); + } } -- cgit v1.2.1 From 90af64929055c284fb5cc61265b919502db01797 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 22 Jan 2021 11:38:07 -0500 Subject: Reformat code --- .../managers/screen/menu/MenuReplaceOperation.java | 103 ++++++++++++++------- 1 file changed, 67 insertions(+), 36 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index da869d486..882dbe126 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -36,28 +36,27 @@ import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdN */ class MenuReplaceOperation extends Task { private static final String TAG = "MenuReplaceOperation"; + private static final int KEEP = 0; + private static final int MARKED_FOR_ADDITION = 1; + private static final int MARKED_FOR_DELETION = 2; + private final WeakReference internalInterface; private final WeakReference fileManager; private final WindowCapability defaultMainWindowCapability; - private final List menuCells; private List oldMenuCells; - private List keepsNew; + private final List menuCells; private List keepsOld; + private List keepsNew; private final MenuManagerCompletionListener completionListener; private final String displayType; private final DynamicMenuUpdatesMode dynamicMenuUpdatesMode; private int lastMenuId; private MenuConfiguration menuConfiguration; - private static final int KEEP = 0; - private static final int MARKED_FOR_ADDITION = 1; - private static final int MARKED_FOR_DELETION = 2; - // todo split to static and dynamic operations // todo call onFinish & listener when done - // todo add Task.CANCELED checks - MenuReplaceOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode,MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, int lastMenuId, MenuManagerCompletionListener completionListener) { + MenuReplaceOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, int lastMenuId, MenuManagerCompletionListener completionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); @@ -83,12 +82,12 @@ class MenuReplaceOperation extends Task { // Upload the Artworks List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(menuCells); - if (artworksToBeUploaded.size() > 0 && fileManager.get() != null) { + if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { @Override public void onComplete(Map errors) { - if (errors != null && errors.size() > 0) { + if (errors != null && !errors.isEmpty()) { DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); } else { DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); @@ -104,6 +103,10 @@ class MenuReplaceOperation extends Task { } private void updateMenuAndDetermineBestUpdateMethod() { + if (getState() == Task.CANCELED) { + return; + } + // Checks against what the developer set for update mode and against the display type // to determine how the menu will be updated. This has the ability to be changed during // a session. @@ -121,7 +124,7 @@ class MenuReplaceOperation extends Task { createAndSendEntireMenu(); } else { DebugTool.logInfo(TAG, "Dynamically Updating Menu"); - if (menuCells.size() == 0 && (oldMenuCells != null && oldMenuCells.size() > 0)) { + if (menuCells.isEmpty() && (oldMenuCells != null && !oldMenuCells.isEmpty())) { // the dev wants to clear the menu. We have old cells and an empty array of new ones. deleteMenuWhenNewCellsEmpty(); } else { @@ -161,6 +164,10 @@ class MenuReplaceOperation extends Task { } private void deleteMenuWhenNewCellsEmpty() { + if (getState() == Task.CANCELED) { + return; + } + sendDeleteRPCs(createDeleteRPCsForCells(oldMenuCells), new CompletionListener() { @Override public void onComplete(boolean success) { @@ -212,7 +219,7 @@ class MenuReplaceOperation extends Task { // this is needed for the onCommands to still work transferIdsToKeptCells(keepsNew); - if (adds.size() > 0) { + if (!adds.isEmpty()) { DebugTool.logInfo(TAG, "Sending root menu updates"); sendDynamicRootMenuRPCs(deleteCommands, adds); } else { @@ -222,6 +229,10 @@ class MenuReplaceOperation extends Task { } private void createAndSendEntireMenu() { + if (getState() == Task.CANCELED) { + return; + } + deleteRootMenu(new CompletionListener() { @Override public void onComplete(boolean success) { @@ -241,7 +252,11 @@ class MenuReplaceOperation extends Task { } private void createAndSendMenuCellRPCs(final List menu, final CompletionListener listener) { - if (menu.size() == 0) { + if (getState() == Task.CANCELED) { + return; + } + + if (menu.isEmpty()) { if (listener != null) { // This can be considered a success if the user was clearing out their menu listener.onComplete(true); @@ -271,12 +286,12 @@ class MenuReplaceOperation extends Task { @Override public void onFinished() { - if (subMenuCommands.size() > 0) { + if (!subMenuCommands.isEmpty()) { sendSubMenuCommandRPCs(subMenuCommands, listener); DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); } else { - if (keepsNew != null && keepsNew.size() > 0) { + if (keepsNew != null && !keepsNew.isEmpty()) { runSubMenuCompareAlgorithm(); } else { DebugTool.logInfo(TAG, "Finished sending main menu commands."); @@ -300,6 +315,9 @@ class MenuReplaceOperation extends Task { } private void sendSubMenuCommandRPCs(List commands, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } internalInterface.get().sendSequentialRPCs(commands, new OnMultipleRequestListener() { @Override @@ -310,7 +328,7 @@ class MenuReplaceOperation extends Task { @Override public void onFinished() { - if (keepsNew != null && keepsNew.size() > 0) { + if (keepsNew != null && !keepsNew.isEmpty()) { runSubMenuCompareAlgorithm(); } else { DebugTool.logInfo(TAG, "Finished Updating Menu"); @@ -340,8 +358,11 @@ class MenuReplaceOperation extends Task { } private void createAndSendDynamicSubMenuRPCs(List newMenu, final List adds, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } - if (adds.size() == 0) { + if (adds.isEmpty()) { if (listener != null) { // This can be considered a success if the user was clearing out their menu DebugTool.logError(TAG, "Called createAndSendDynamicSubMenuRPCs with empty menu"); @@ -392,7 +413,7 @@ class MenuReplaceOperation extends Task { private void deleteRootMenu(final CompletionListener listener) { - if (oldMenuCells == null || oldMenuCells.size() == 0) { + if (oldMenuCells == null || oldMenuCells.isEmpty()) { if (listener != null) { // technically this method is successful if there's nothing to delete DebugTool.logInfo(TAG, "No old cells to delete, returning"); @@ -404,7 +425,11 @@ class MenuReplaceOperation extends Task { } private void sendDeleteRPCs(List deleteCommands, final CompletionListener listener) { - if (oldMenuCells != null && oldMenuCells.size() == 0) { + if (getState() == Task.CANCELED) { + return; + } + + if (oldMenuCells != null && oldMenuCells.isEmpty()) { if (listener != null) { // technically this method is successful if there's nothing to delete DebugTool.logInfo(TAG, "No old cells to delete, returning"); @@ -413,7 +438,7 @@ class MenuReplaceOperation extends Task { return; } - if (deleteCommands == null || deleteCommands.size() == 0) { + if (deleteCommands == null || deleteCommands.isEmpty()) { // no dynamic deletes required. return if (listener != null) { // technically this method is successful if there's nothing to delete @@ -446,7 +471,7 @@ class MenuReplaceOperation extends Task { private List subMenuCommandsForCells(List cells, boolean shouldHaveArtwork) { List builtCommands = new ArrayList<>(); for (MenuCell cell : cells) { - if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); } } @@ -459,7 +484,7 @@ class MenuReplaceOperation extends Task { // We need the index so we will use this type of loop for (int i = 0; i < cells.size(); i++) { MenuCell cell = cells.get(i); - if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { builtCommands.add(subMenuCommandForMenuCell(cell, shouldHaveArtwork, i)); // recursively grab the commands for all the sub cells builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); @@ -516,6 +541,10 @@ class MenuReplaceOperation extends Task { } private void sendDynamicRootMenuRPCs(List deleteCommands, final List updatedCells) { + if (getState() == Task.CANCELED) { + return; + } + sendDeleteRPCs(deleteCommands, new CompletionListener() { @Override public void onComplete(boolean success) { @@ -538,7 +567,7 @@ class MenuReplaceOperation extends Task { private void runSubMenuCompareAlgorithm() { // any cells that were re-added have their sub-cells added with them // at this point all we care about are the cells that were deemed equal and kept. - if (keepsNew == null || keepsNew.size() == 0) { + if (keepsNew == null || keepsNew.isEmpty()) { return; } @@ -549,7 +578,7 @@ class MenuReplaceOperation extends Task { MenuCell keptCell = keepsNew.get(i); MenuCell oldKeptCell = keepsOld.get(i); - if (oldKeptCell.getSubCells() != null && oldKeptCell.getSubCells().size() > 0 && keptCell.getSubCells() != null && keptCell.getSubCells().size() > 0) { + if (oldKeptCell.getSubCells() != null && !oldKeptCell.getSubCells().isEmpty() && keptCell.getSubCells() != null && !keptCell.getSubCells().isEmpty()) { // ACTUAL LOGIC RunScore subScore = compareOldAndNewLists(oldKeptCell.getSubCells(), keptCell.getSubCells()); @@ -564,9 +593,12 @@ class MenuReplaceOperation extends Task { } private void createSubMenuDynamicCommands(final List commandLists) { + if (getState() == Task.CANCELED) { + return; + } // break out - if (commandLists.size() == 0) { + if (commandLists.isEmpty()) { // if (hasQueuedUpdate) { // DebugTool.logInfo(TAG, "Menu Manager has waiting updates, sending now"); // setMenuCells(waitingUpdateMenuCells); @@ -623,7 +655,7 @@ class MenuReplaceOperation extends Task { sendDeleteRPCs(deleteCommands, new CompletionListener() { @Override public void onComplete(boolean success) { - if (addsWithNewIds != null && addsWithNewIds.size() > 0) { + if (addsWithNewIds != null && !addsWithNewIds.isEmpty()) { createAndSendDynamicSubMenuRPCs(newCells, addsWithNewIds, new CompletionListener() { @Override public void onComplete(boolean success) { @@ -647,7 +679,7 @@ class MenuReplaceOperation extends Task { RunScore runMenuCompareAlgorithm(List oldCells, List newCells) { - if (oldCells == null || oldCells.size() == 0) { + if (oldCells == null || oldCells.isEmpty()) { return null; } @@ -723,7 +755,7 @@ class MenuReplaceOperation extends Task { SdlArtwork artwork = cell.getIcon(); if (artwork != null && !artwork.isStaticIcon() && fileManager.get() != null && !fileManager.get().hasUploadedFile(artwork)) { return false; - } else if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { + } else if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { return shouldRPCsIncludeImages(cell.getSubCells()); } } @@ -733,7 +765,7 @@ class MenuReplaceOperation extends Task { // IDs private void updateIdsOnDynamicCells(List dynamicCells) { - if (menuCells != null && menuCells.size() > 0 && dynamicCells != null && dynamicCells.size() > 0) { + if (menuCells != null && !menuCells.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { for (int z = 0; z < menuCells.size(); z++) { MenuCell mainCell = menuCells.get(z); for (int i = 0; i < dynamicCells.size(); i++) { @@ -743,7 +775,7 @@ class MenuReplaceOperation extends Task { menuCells.get(z).setCellId(newId); dynamicCells.get(i).setCellId(newId); - if (mainCell.getSubCells() != null && mainCell.getSubCells().size() > 0) { + if (mainCell.getSubCells() != null && !mainCell.getSubCells().isEmpty()) { updateIdsOnMenuCells(mainCell.getSubCells(), mainCell.getCellId()); } break; @@ -754,7 +786,7 @@ class MenuReplaceOperation extends Task { } private List updateIdsOnDynamicSubCells(List oldList, List dynamicCells, Integer parentId) { - if (oldList != null && oldList.size() > 0 && dynamicCells != null && dynamicCells.size() > 0) { + if (oldList != null && !oldList.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { for (int z = 0; z < oldList.size(); z++) { MenuCell mainCell = oldList.get(z); for (int i = 0; i < dynamicCells.size(); i++) { @@ -781,7 +813,7 @@ class MenuReplaceOperation extends Task { int newId = ++lastMenuId; cell.setCellId(newId); cell.setParentCellId(parentId); - if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); } } @@ -840,7 +872,7 @@ class MenuReplaceOperation extends Task { for (int i = 0; i < cellsToAdd.size(); i++) { MenuCell addCell = cellsToAdd.get(i); if (mainCell.equals(addCell)) { - if (addCell.getSubCells() != null && addCell.getSubCells().size() > 0) { + if (addCell.getSubCells() != null && !addCell.getSubCells().isEmpty()) { builtCommands.add(subMenuCommandForMenuCell(addCell, shouldHaveArtwork, z)); } else { builtCommands.add(commandForMenuCell(addCell, shouldHaveArtwork, z)); @@ -853,7 +885,6 @@ class MenuReplaceOperation extends Task { } - private List findAllArtworksToBeUploadedFromCells(List cells) { // Make sure we can use images in the menus if (!supportsImages()) { @@ -865,7 +896,7 @@ class MenuReplaceOperation extends Task { if (fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getIcon())) { artworks.add(cell.getIcon()); } - if (cell.getSubCells() != null && cell.getSubCells().size() > 0) { + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { artworks.addAll(findAllArtworksToBeUploadedFromCells(cell.getSubCells())); } } @@ -878,7 +909,7 @@ class MenuReplaceOperation extends Task { return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.cmdIcon); } - void setMenuConfiguration (MenuConfiguration menuConfiguration) { + void setMenuConfiguration(MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } -- cgit v1.2.1 From 1dc2b77e0f08e29d49913f0a70de9701f82c06b5 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 22 Jan 2021 11:46:55 -0500 Subject: Remove lastMenuId from menu manager --- .../managers/screen/menu/BaseMenuManager.java | 8 ++------ .../managers/screen/menu/MenuManagerCompletionListener.java | 2 +- .../managers/screen/menu/MenuReplaceOperation.java | 13 +++++-------- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index c031ec667..3f7399f2a 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -83,7 +83,6 @@ abstract class BaseMenuManager extends BaseSubManager { private OnRPCNotificationListener commandListener; private OnSystemCapabilityListener onDisplaysCapabilityListener; private WindowCapability defaultMainWindowCapability; - private int lastMenuId; private Queue transactionQueue; BaseMenuManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) { @@ -92,7 +91,6 @@ abstract class BaseMenuManager extends BaseSubManager { this.fileManager = new WeakReference<>(fileManager); this.currentSystemContext = SystemContext.SYSCTXT_MAIN; this.currentHMILevel = HMILevel.HMI_NONE; - this.lastMenuId = menuCellIdMin; this.dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; this.sdlMsgVersion = internalInterface.getSdlMsgVersion(); this.transactionQueue = newTransactionQueue(); @@ -108,7 +106,6 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void dispose() { - lastMenuId = menuCellIdMin; menuCells = null; oldMenuCells = null; currentHMILevel = HMILevel.HMI_NONE; @@ -191,10 +188,9 @@ abstract class BaseMenuManager extends BaseSubManager { cancelPendingMenuReplaceOperations(); - MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, oldMenuCells, menuCells, lastMenuId, new MenuManagerCompletionListener() { + MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, oldMenuCells, menuCells, new MenuManagerCompletionListener() { @Override - public void onComplete(boolean success, int lastMenuId, List oldMenuCells) { - BaseMenuManager.this.lastMenuId = lastMenuId; + public void onComplete(boolean success, List oldMenuCells) { BaseMenuManager.this.oldMenuCells = oldMenuCells; } }); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuManagerCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuManagerCompletionListener.java index c856491af..d49d0b5db 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuManagerCompletionListener.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuManagerCompletionListener.java @@ -38,5 +38,5 @@ import java.util.List; * Created by Bilal Alsharifi on 1/21/21. */ interface MenuManagerCompletionListener { - void onComplete(boolean success, int lastMenuId, List oldMenuCells); + void onComplete(boolean success, List oldMenuCells); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 882dbe126..c52327c77 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -56,7 +56,7 @@ class MenuReplaceOperation extends Task { // todo split to static and dynamic operations // todo call onFinish & listener when done - MenuReplaceOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, int lastMenuId, MenuManagerCompletionListener completionListener) { + MenuReplaceOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, MenuManagerCompletionListener completionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); @@ -66,7 +66,6 @@ class MenuReplaceOperation extends Task { this.defaultMainWindowCapability = defaultMainWindowCapability; this.oldMenuCells = oldMenuCells; this.menuCells = menuCells; - this.lastMenuId = lastMenuId; this.completionListener = completionListener; } @@ -86,7 +85,6 @@ class MenuReplaceOperation extends Task { fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { @Override public void onComplete(Map errors) { - if (errors != null && !errors.isEmpty()) { DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); } else { @@ -107,9 +105,8 @@ class MenuReplaceOperation extends Task { return; } - // Checks against what the developer set for update mode and against the display type - // to determine how the menu will be updated. This has the ability to be changed during - // a session. + // Checks against what the developer set for update mode and against the display type to + // determine how the menu will be updated. This has the ability to be changed during a session. if (checkUpdateMode(dynamicMenuUpdatesMode, displayType)) { // run the lists through the new algorithm RunScore rootScore = runMenuCompareAlgorithm(oldMenuCells, menuCells); @@ -133,7 +130,7 @@ class MenuReplaceOperation extends Task { } } } else { - // we are in compatibility mode + // We are in compatibility mode. No need to run the algorithm DebugTool.logInfo(TAG, "Updating menus in compatibility mode"); lastMenuId = menuCellIdMin; updateIdsOnMenuCells(menuCells, parentIdNotFound); @@ -915,7 +912,7 @@ class MenuReplaceOperation extends Task { private void finishOperation(boolean success) { if (completionListener != null) { - completionListener.onComplete(success, lastMenuId, oldMenuCells); + completionListener.onComplete(success, oldMenuCells); } onFinished(); } -- cgit v1.2.1 From 0ca5d2d32c075e3084a975026408ce449bb8101b Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 22 Jan 2021 14:12:07 -0500 Subject: Add listners to know when to finish operation --- .../managers/screen/menu/MenuReplaceOperation.java | 62 +++++++++------------- 1 file changed, 25 insertions(+), 37 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index c52327c77..617c2de43 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -47,7 +47,7 @@ class MenuReplaceOperation extends Task { private final List menuCells; private List keepsOld; private List keepsNew; - private final MenuManagerCompletionListener completionListener; + private final MenuManagerCompletionListener operationCompletionListener; private final String displayType; private final DynamicMenuUpdatesMode dynamicMenuUpdatesMode; private int lastMenuId; @@ -56,7 +56,7 @@ class MenuReplaceOperation extends Task { // todo split to static and dynamic operations // todo call onFinish & listener when done - MenuReplaceOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, MenuManagerCompletionListener completionListener) { + MenuReplaceOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, MenuManagerCompletionListener operationCompletionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); @@ -66,7 +66,7 @@ class MenuReplaceOperation extends Task { this.defaultMainWindowCapability = defaultMainWindowCapability; this.oldMenuCells = oldMenuCells; this.menuCells = menuCells; - this.completionListener = completionListener; + this.operationCompletionListener = operationCompletionListener; } @Override @@ -79,6 +79,15 @@ class MenuReplaceOperation extends Task { return; } + updateMenuCells(new CompletionListener() { + @Override + public void onComplete(boolean success) { + finishOperation(success); + } + }); + } + + private void updateMenuCells(final CompletionListener listener) { // Upload the Artworks List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(menuCells); if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { @@ -91,16 +100,16 @@ class MenuReplaceOperation extends Task { DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); } // proceed - updateMenuAndDetermineBestUpdateMethod(); + updateMenuAndDetermineBestUpdateMethod(listener); } }); } else { // No Artworks to be uploaded, send off - updateMenuAndDetermineBestUpdateMethod(); + updateMenuAndDetermineBestUpdateMethod(listener); } } - private void updateMenuAndDetermineBestUpdateMethod() { + private void updateMenuAndDetermineBestUpdateMethod(CompletionListener listener) { if (getState() == Task.CANCELED) { return; } @@ -108,7 +117,7 @@ class MenuReplaceOperation extends Task { // Checks against what the developer set for update mode and against the display type to // determine how the menu will be updated. This has the ability to be changed during a session. if (checkUpdateMode(dynamicMenuUpdatesMode, displayType)) { - // run the lists through the new algorithm + // Run the lists through the new algorithm RunScore rootScore = runMenuCompareAlgorithm(oldMenuCells, menuCells); if (rootScore == null) { // send initial menu (score will return null) @@ -117,8 +126,8 @@ class MenuReplaceOperation extends Task { // Set the IDs if needed lastMenuId = menuCellIdMin; updateIdsOnMenuCells(menuCells, parentIdNotFound); - this.oldMenuCells = new ArrayList<>(menuCells); - createAndSendEntireMenu(); + this.oldMenuCells = new ArrayList<>(menuCells); // todo why? + createAndSendEntireMenu(listener); } else { DebugTool.logInfo(TAG, "Dynamically Updating Menu"); if (menuCells.isEmpty() && (oldMenuCells != null && !oldMenuCells.isEmpty())) { @@ -136,15 +145,13 @@ class MenuReplaceOperation extends Task { updateIdsOnMenuCells(menuCells, parentIdNotFound); // if the old cell array is not null, we want to delete the entire thing, else copy the new array if (oldMenuCells == null) { - this.oldMenuCells = new ArrayList<>(menuCells); + this.oldMenuCells = new ArrayList<>(menuCells); // todo why? } - createAndSendEntireMenu(); + createAndSendEntireMenu(listener); } } - - + private boolean checkUpdateMode(DynamicMenuUpdatesMode updateMode, String displayType) { - if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { if (displayType == null) { return true; @@ -225,7 +232,7 @@ class MenuReplaceOperation extends Task { } } - private void createAndSendEntireMenu() { + private void createAndSendEntireMenu(final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } @@ -236,12 +243,11 @@ class MenuReplaceOperation extends Task { createAndSendMenuCellRPCs(menuCells, new CompletionListener() { @Override public void onComplete(boolean success) { - if (!success) { DebugTool.logError(TAG, "Error Sending Current Menu"); } - finishOperation(success); + listener.onComplete(success); } }); } @@ -273,7 +279,6 @@ class MenuReplaceOperation extends Task { subMenuCommands = subMenuCommandsForCells(menu, true); } - internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { @Override public void onUpdate(int remainingRequests) { @@ -406,10 +411,7 @@ class MenuReplaceOperation extends Task { }); } - // DELETE OLD MENU ITEMS - private void deleteRootMenu(final CompletionListener listener) { - if (oldMenuCells == null || oldMenuCells.isEmpty()) { if (listener != null) { // technically this method is successful if there's nothing to delete @@ -558,8 +560,6 @@ class MenuReplaceOperation extends Task { }); } - // SUB MENUS - // This is called in the listener in the sendMenu and sendSubMenuCommands Methods private void runSubMenuCompareAlgorithm() { // any cells that were re-added have their sub-cells added with them @@ -670,12 +670,7 @@ class MenuReplaceOperation extends Task { }); } - // OTHER HELPER METHODS: - - // COMPARISONS - RunScore runMenuCompareAlgorithm(List oldCells, List newCells) { - if (oldCells == null || oldCells.isEmpty()) { return null; } @@ -759,8 +754,6 @@ class MenuReplaceOperation extends Task { return true; } - // IDs - private void updateIdsOnDynamicCells(List dynamicCells) { if (menuCells != null && !menuCells.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { for (int z = 0; z < menuCells.size(); z++) { @@ -842,8 +835,6 @@ class MenuReplaceOperation extends Task { } } - // DELETES - private List createDeleteRPCsForCells(List cells) { List deletes = new ArrayList<>(); for (MenuCell cell : cells) { @@ -858,8 +849,6 @@ class MenuReplaceOperation extends Task { return deletes; } - // COMMANDS / SUBMENU RPCs - private List mainMenuCommandsForCells(List cellsToAdd, boolean shouldHaveArtwork) { List builtCommands = new ArrayList<>(); @@ -881,7 +870,6 @@ class MenuReplaceOperation extends Task { return builtCommands; } - private List findAllArtworksToBeUploadedFromCells(List cells) { // Make sure we can use images in the menus if (!supportsImages()) { @@ -911,8 +899,8 @@ class MenuReplaceOperation extends Task { } private void finishOperation(boolean success) { - if (completionListener != null) { - completionListener.onComplete(success, oldMenuCells); + if (operationCompletionListener != null) { + operationCompletionListener.onComplete(success, oldMenuCells); } onFinished(); } -- cgit v1.2.1 From 027092fc04f47947091d63ff6661aaf2a73c49eb Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 22 Jan 2021 14:55:40 -0500 Subject: Update MenuReplaceOperation --- .../managers/screen/menu/BaseMenuManager.java | 2 +- .../managers/screen/menu/MenuReplaceOperation.java | 35 +++++++--------------- 2 files changed, 11 insertions(+), 26 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 3f7399f2a..725b7ea45 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -64,7 +64,7 @@ import java.util.List; abstract class BaseMenuManager extends BaseSubManager { private static final String TAG = "BaseMenuManager"; - static final int MAX_ID = 2000000000; + private static final int MAX_ID = 2000000000; static final int menuCellIdMin = 1; static final int parentIdNotFound = MAX_ID; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 617c2de43..bfed2696f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -27,7 +27,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.MAX_ID; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; @@ -150,7 +149,7 @@ class MenuReplaceOperation extends Task { createAndSendEntireMenu(listener); } } - + private boolean checkUpdateMode(DynamicMenuUpdatesMode updateMode, String displayType) { if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { if (displayType == null) { @@ -282,21 +281,22 @@ class MenuReplaceOperation extends Task { internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { @Override public void onUpdate(int remainingRequests) { - // nothing here } @Override public void onFinished() { - if (!subMenuCommands.isEmpty()) { - sendSubMenuCommandRPCs(subMenuCommands, listener); DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); + sendSubMenuCommandRPCs(subMenuCommands, listener); } else { - if (keepsNew != null && !keepsNew.isEmpty()) { - runSubMenuCompareAlgorithm(); + runSubMenuCompareAlgorithm(); // todo should we pass listener here? } else { DebugTool.logInfo(TAG, "Finished sending main menu commands."); + + if (listener != null) { + listener.onComplete(true); + } } } } @@ -324,14 +324,12 @@ class MenuReplaceOperation extends Task { internalInterface.get().sendSequentialRPCs(commands, new OnMultipleRequestListener() { @Override public void onUpdate(int remainingRequests) { - } @Override public void onFinished() { - if (keepsNew != null && !keepsNew.isEmpty()) { - runSubMenuCompareAlgorithm(); + runSubMenuCompareAlgorithm(); // todo should we pass listener here? } else { DebugTool.logInfo(TAG, "Finished Updating Menu"); @@ -351,9 +349,6 @@ class MenuReplaceOperation extends Task { } } else { DebugTool.logError(TAG, "Failed to send sub menu commands: " + response.getInfo()); - if (listener != null) { - listener.onComplete(false); - } } } }); @@ -428,15 +423,6 @@ class MenuReplaceOperation extends Task { return; } - if (oldMenuCells != null && oldMenuCells.isEmpty()) { - if (listener != null) { - // technically this method is successful if there's nothing to delete - DebugTool.logInfo(TAG, "No old cells to delete, returning"); - listener.onComplete(true); - } - return; - } - if (deleteCommands == null || deleteCommands.isEmpty()) { // no dynamic deletes required. return if (listener != null) { @@ -480,11 +466,11 @@ class MenuReplaceOperation extends Task { List allCommandsForCells(List cells, boolean shouldHaveArtwork) { List builtCommands = new ArrayList<>(); - // We need the index so we will use this type of loop for (int i = 0; i < cells.size(); i++) { MenuCell cell = cells.get(i); if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { builtCommands.add(subMenuCommandForMenuCell(cell, shouldHaveArtwork, i)); + // recursively grab the commands for all the sub cells builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); } else { @@ -510,9 +496,8 @@ class MenuReplaceOperation extends Task { } private AddCommand commandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { - MenuParams params = new MenuParams(cell.getTitle()); - params.setParentID(cell.getParentCellId() != MAX_ID ? cell.getParentCellId() : null); + params.setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null); params.setPosition(position); AddCommand command = new AddCommand(cell.getCellId()); -- cgit v1.2.1 From 6b3da3f3e50472130190481f5d03e0fa75e053db Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 22 Jan 2021 16:55:32 -0500 Subject: Add more listeners to know when to finish operation --- .../managers/screen/menu/MenuReplaceOperation.java | 98 ++++++++-------------- 1 file changed, 36 insertions(+), 62 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index bfed2696f..2b8c4cb82 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -52,9 +52,6 @@ class MenuReplaceOperation extends Task { private int lastMenuId; private MenuConfiguration menuConfiguration; - // todo split to static and dynamic operations - // todo call onFinish & listener when done - MenuReplaceOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, MenuManagerCompletionListener operationCompletionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); @@ -119,10 +116,8 @@ class MenuReplaceOperation extends Task { // Run the lists through the new algorithm RunScore rootScore = runMenuCompareAlgorithm(oldMenuCells, menuCells); if (rootScore == null) { - // send initial menu (score will return null) - // make a copy of our current cells + // Send initial menu without dynamic updates because oldMenuCells is null DebugTool.logInfo(TAG, "Creating initial Menu"); - // Set the IDs if needed lastMenuId = menuCellIdMin; updateIdsOnMenuCells(menuCells, parentIdNotFound); this.oldMenuCells = new ArrayList<>(menuCells); // todo why? @@ -131,10 +126,10 @@ class MenuReplaceOperation extends Task { DebugTool.logInfo(TAG, "Dynamically Updating Menu"); if (menuCells.isEmpty() && (oldMenuCells != null && !oldMenuCells.isEmpty())) { // the dev wants to clear the menu. We have old cells and an empty array of new ones. - deleteMenuWhenNewCellsEmpty(); + deleteMenuWhenNewCellsEmpty(listener); } else { // lets dynamically update the root menu - dynamicallyUpdateRootMenu(rootScore); + dynamicallyUpdateRootMenu(rootScore, listener); } } } else { @@ -166,7 +161,7 @@ class MenuReplaceOperation extends Task { return true; } - private void deleteMenuWhenNewCellsEmpty() { + private void deleteMenuWhenNewCellsEmpty(final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } @@ -180,13 +175,13 @@ class MenuReplaceOperation extends Task { DebugTool.logInfo(TAG, "Successfully Cleared Menu"); } oldMenuCells = null; + listener.onComplete(success); } }); } - private void dynamicallyUpdateRootMenu(RunScore bestRootScore) { - // we need to run through the keeps and see if they have subCells, as they also need to be run - // through the compare function. + private void dynamicallyUpdateRootMenu(RunScore bestRootScore, CompletionListener listener) { + // We need to run through the keeps and see if they have subCells, as they also need to be run through the compare function. List newIntArray = bestRootScore.getCurrentMenu(); List oldIntArray = bestRootScore.getOldMenu(); List deleteCommands; @@ -222,12 +217,12 @@ class MenuReplaceOperation extends Task { // this is needed for the onCommands to still work transferIdsToKeptCells(keepsNew); - if (!adds.isEmpty()) { + if (!adds.isEmpty()) { // todo what if adds was empty but deletes was not? DebugTool.logInfo(TAG, "Sending root menu updates"); - sendDynamicRootMenuRPCs(deleteCommands, adds); + sendDynamicRootMenuRPCs(deleteCommands, adds, listener); } else { DebugTool.logInfo(TAG, "All root menu items are kept. Check the sub menus"); - runSubMenuCompareAlgorithm(); + runSubMenuCompareAlgorithm(listener); } } @@ -258,7 +253,7 @@ class MenuReplaceOperation extends Task { return; } - if (menu.isEmpty()) { + if (menu == null || menu.isEmpty()) { if (listener != null) { // This can be considered a success if the user was clearing out their menu listener.onComplete(true); @@ -290,7 +285,7 @@ class MenuReplaceOperation extends Task { sendSubMenuCommandRPCs(subMenuCommands, listener); } else { if (keepsNew != null && !keepsNew.isEmpty()) { - runSubMenuCompareAlgorithm(); // todo should we pass listener here? + runSubMenuCompareAlgorithm(listener); } else { DebugTool.logInfo(TAG, "Finished sending main menu commands."); @@ -329,7 +324,7 @@ class MenuReplaceOperation extends Task { @Override public void onFinished() { if (keepsNew != null && !keepsNew.isEmpty()) { - runSubMenuCompareAlgorithm(); // todo should we pass listener here? + runSubMenuCompareAlgorithm(listener); } else { DebugTool.logInfo(TAG, "Finished Updating Menu"); @@ -385,7 +380,6 @@ class MenuReplaceOperation extends Task { @Override public void onFinished() { - if (listener != null) { listener.onComplete(true); } @@ -524,7 +518,7 @@ class MenuReplaceOperation extends Task { return subMenu; } - private void sendDynamicRootMenuRPCs(List deleteCommands, final List updatedCells) { + private void sendDynamicRootMenuRPCs(List deleteCommands, final List updatedCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } @@ -535,57 +529,52 @@ class MenuReplaceOperation extends Task { createAndSendMenuCellRPCs(updatedCells, new CompletionListener() { @Override public void onComplete(boolean success) { - if (!success) { DebugTool.logError(TAG, "Error Sending Current Menu"); } + + listener.onComplete(success); } }); } }); } - // This is called in the listener in the sendMenu and sendSubMenuCommands Methods - private void runSubMenuCompareAlgorithm() { + private void runSubMenuCompareAlgorithm(CompletionListener listener) { // any cells that were re-added have their sub-cells added with them // at this point all we care about are the cells that were deemed equal and kept. if (keepsNew == null || keepsNew.isEmpty()) { + listener.onComplete(true); return; } List commandLists = new ArrayList<>(); for (int i = 0; i < keepsNew.size(); i++) { - - MenuCell keptCell = keepsNew.get(i); + MenuCell newKeptCell = keepsNew.get(i); MenuCell oldKeptCell = keepsOld.get(i); - if (oldKeptCell.getSubCells() != null && !oldKeptCell.getSubCells().isEmpty() && keptCell.getSubCells() != null && !keptCell.getSubCells().isEmpty()) { - // ACTUAL LOGIC - RunScore subScore = compareOldAndNewLists(oldKeptCell.getSubCells(), keptCell.getSubCells()); + if (oldKeptCell.getSubCells() != null && !oldKeptCell.getSubCells().isEmpty() && newKeptCell.getSubCells() != null && !newKeptCell.getSubCells().isEmpty()) { + RunScore subScore = compareOldAndNewLists(oldKeptCell.getSubCells(), newKeptCell.getSubCells()); if (subScore != null) { DebugTool.logInfo(TAG, "Sub menu Run Score: " + oldKeptCell.getTitle() + " Score: " + subScore.getScore()); - SubCellCommandList commandList = new SubCellCommandList(oldKeptCell.getTitle(), oldKeptCell.getCellId(), subScore, oldKeptCell.getSubCells(), keptCell.getSubCells()); + SubCellCommandList commandList = new SubCellCommandList(oldKeptCell.getTitle(), oldKeptCell.getCellId(), subScore, oldKeptCell.getSubCells(), newKeptCell.getSubCells()); commandLists.add(commandList); } } } - createSubMenuDynamicCommands(commandLists); + createSubMenuDynamicCommands(commandLists, listener); } - private void createSubMenuDynamicCommands(final List commandLists) { + private void createSubMenuDynamicCommands(final List commandLists, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } - // break out if (commandLists.isEmpty()) { -// if (hasQueuedUpdate) { -// DebugTool.logInfo(TAG, "Menu Manager has waiting updates, sending now"); -// setMenuCells(waitingUpdateMenuCells); -// } DebugTool.logInfo(TAG, "All menu updates, including sub menus - done."); + listener.onComplete(true); return; } @@ -602,11 +591,6 @@ class MenuReplaceOperation extends Task { final List oldCells = commandList.getOldList(); final List newCells = commandList.getNewList(); - // Create the list for the adds - List subCellKeepsNew = new ArrayList<>(); - - List deleteCommands; - // Set up deletes List deletes = new ArrayList<>(); for (int x = 0; x < oldIntArray.size(); x++) { @@ -617,10 +601,11 @@ class MenuReplaceOperation extends Task { } } // create the delete commands - deleteCommands = createDeleteRPCsForCells(deletes); + List deleteCommands = createDeleteRPCsForCells(deletes); // Set up the adds List adds = new ArrayList<>(); + List subCellKeepsNew = new ArrayList<>(); for (int x = 0; x < newIntArray.size(); x++) { Integer newInt = newIntArray.get(x); if (newInt.equals(MARKED_FOR_ADDITION)) { @@ -643,13 +628,13 @@ class MenuReplaceOperation extends Task { public void onComplete(boolean success) { // recurse through next sub list DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - createSubMenuDynamicCommands(commandLists); + createSubMenuDynamicCommands(commandLists, listener); } }); } else { // no add commands to send, recurse through next sub list DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - createSubMenuDynamicCommands(commandLists); + createSubMenuDynamicCommands(commandLists, listener); } } }); @@ -667,18 +652,20 @@ class MenuReplaceOperation extends Task { } private RunScore compareOldAndNewLists(List oldCells, List newCells) { - RunScore bestRunScore = null; // This first loop is for each 'run' for (int run = 0; run < oldCells.size(); run++) { - List oldArray = new ArrayList<>(oldCells.size()); List newArray = new ArrayList<>(newCells.size()); // Set the statuses - setDeleteStatus(oldCells.size(), oldArray); - setAddStatus(newCells.size(), newArray); + for (int i = 0; i < oldCells.size(); i++) { + oldArray.add(MARKED_FOR_DELETION); + } + for (int i = 0; i < newCells.size(); i++) { + newArray.add(MARKED_FOR_ADDITION); + } int startIndex = 0; @@ -699,7 +686,6 @@ class MenuReplaceOperation extends Task { // Calculate number of adds, or the 'score' for this run int numberOfAdds = 0; - for (int x = 0; x < newArray.size(); x++) { if (newArray.get(x).equals(MARKED_FOR_ADDITION)) { numberOfAdds++; @@ -715,18 +701,6 @@ class MenuReplaceOperation extends Task { return bestRunScore; } - private void setDeleteStatus(Integer size, List oldArray) { - for (int i = 0; i < size; i++) { - oldArray.add(MARKED_FOR_DELETION); - } - } - - private void setAddStatus(Integer size, List newArray) { - for (int i = 0; i < size; i++) { - newArray.add(MARKED_FOR_ADDITION); - } - } - private boolean shouldRPCsIncludeImages(List cells) { for (MenuCell cell : cells) { SdlArtwork artwork = cell.getIcon(); @@ -746,7 +720,7 @@ class MenuReplaceOperation extends Task { for (int i = 0; i < dynamicCells.size(); i++) { MenuCell dynamicCell = dynamicCells.get(i); if (mainCell.equals(dynamicCell)) { - int newId = ++lastMenuId; + int newId = ++lastMenuId; //todo won't the lastMenuId reset to 1 every time? menuCells.get(z).setCellId(newId); dynamicCells.get(i).setCellId(newId); -- cgit v1.2.1 From cc515b2bcf2dfbefc9965cdfb2c7e7803cec800d Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 10:41:56 -0500 Subject: Pause MenuManager queue when needed --- .../java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 725b7ea45..170ebd782 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -360,6 +360,8 @@ abstract class BaseMenuManager extends BaseSubManager { // We also don't want to encourage changing out the menu while the user is using it for usability reasons. DebugTool.logInfo(TAG, "We now have a proper system context, sending waiting update"); transactionQueue.resume(); + } else if (currentHMILevel == HMILevel.HMI_NONE || currentSystemContext == SystemContext.SYSCTXT_MENU){ + transactionQueue.pause(); } } -- cgit v1.2.1 From 87bb85e4d3a0963d033cff3bab4bb66e73c0ecad Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 10:46:38 -0500 Subject: Remove some unused code in MenuReplaceOperation --- .../smartdevicelink/managers/screen/menu/MenuReplaceOperation.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 2b8c4cb82..2fbb7fe5f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -120,7 +120,6 @@ class MenuReplaceOperation extends Task { DebugTool.logInfo(TAG, "Creating initial Menu"); lastMenuId = menuCellIdMin; updateIdsOnMenuCells(menuCells, parentIdNotFound); - this.oldMenuCells = new ArrayList<>(menuCells); // todo why? createAndSendEntireMenu(listener); } else { DebugTool.logInfo(TAG, "Dynamically Updating Menu"); @@ -137,10 +136,6 @@ class MenuReplaceOperation extends Task { DebugTool.logInfo(TAG, "Updating menus in compatibility mode"); lastMenuId = menuCellIdMin; updateIdsOnMenuCells(menuCells, parentIdNotFound); - // if the old cell array is not null, we want to delete the entire thing, else copy the new array - if (oldMenuCells == null) { - this.oldMenuCells = new ArrayList<>(menuCells); // todo why? - } createAndSendEntireMenu(listener); } } -- cgit v1.2.1 From 2cb9b48338a8f10bbc9ebd4b1776d6fecdfead9e Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 11:49:49 -0500 Subject: Align menu manager with iOS --- .../menu/DynamicMenuUpdateRunScoreTests.java | 59 ++ .../managers/screen/menu/MenuManagerTests.java | 10 +- .../managers/screen/menu/RunScoreTests.java | 59 -- .../screen/menu/SubCellCommandListTests.java | 2 +- .../managers/screen/menu/BaseMenuManager.java | 8 +- .../screen/menu/DynamicMenuUpdateRunScore.java | 72 ++ .../screen/menu/MenuReplaceDynamicOperation.java | 91 +++ .../managers/screen/menu/MenuReplaceOperation.java | 861 --------------------- .../screen/menu/MenuReplaceStaticOperation.java | 861 +++++++++++++++++++++ .../managers/screen/menu/RunScore.java | 72 -- .../managers/screen/menu/SubCellCommandList.java | 8 +- 11 files changed, 1097 insertions(+), 1006 deletions(-) create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScoreTests.java delete mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/RunScoreTests.java create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java delete mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java delete mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/RunScore.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScoreTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScoreTests.java new file mode 100644 index 000000000..7ea079d8f --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScoreTests.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019 Livio, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Livio Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package com.smartdevicelink.managers.screen.menu; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.smartdevicelink.test.TestValues; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static junit.framework.TestCase.assertEquals; + +@RunWith(AndroidJUnit4.class) +public class DynamicMenuUpdateRunScoreTests { + + @Test + public void testSettersAndGetters() { + + // set everything - we only use the constructor to set variables in the Menu Manager + DynamicMenuUpdateRunScore runScore = new DynamicMenuUpdateRunScore(TestValues.GENERAL_INT, TestValues.GENERAL_INTEGER_LIST, TestValues.GENERAL_INTEGER_LIST); + + // use getters and assert equality + assertEquals(runScore.getScore(), TestValues.GENERAL_INT); + assertEquals(runScore.getCurrentMenu(), TestValues.GENERAL_INTEGER_LIST); + assertEquals(runScore.getOldMenu(), TestValues.GENERAL_INTEGER_LIST); + } + +} diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index 43392b626..61f65b7ae 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -280,7 +280,7 @@ public class MenuManagerTests { assertEquals(menuManager.menuCells.size(), 4); // this happens in the menu manager but lets make sure its behaving - RunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); + DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); List oldMenuScore = Arrays.asList(0, 0, 0, 0); List newMenuScore = Arrays.asList(0, 0, 0, 0, 1); @@ -317,7 +317,7 @@ public class MenuManagerTests { assertEquals(menuManager.menuCells.size(), 4); // this happens in the menu manager but lets make sure its behaving - RunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); + DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); List oldMenuScore = Arrays.asList(0, 0, 0, 2); List newMenuScore = Arrays.asList(0, 0, 0); @@ -354,7 +354,7 @@ public class MenuManagerTests { assertEquals(menuManager.menuCells.size(), 3); // this happens in the menu manager but lets make sure its behaving - RunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); + DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); List oldMenuScore = Arrays.asList(2, 2, 2); List newMenuScore = Arrays.asList(1, 1, 1); @@ -391,7 +391,7 @@ public class MenuManagerTests { assertEquals(menuManager.menuCells.size(), 4); // this happens in the menu manager but lets make sure its behaving - RunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); + DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); List oldMenuScore = Arrays.asList(0, 2, 0, 2); List newMenuScore = Arrays.asList(1, 0, 1, 0); @@ -428,7 +428,7 @@ public class MenuManagerTests { assertEquals(menuManager.menuCells.size(), 4); // this happens in the menu manager but lets make sure its behaving - RunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); + DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); List oldMenuScore = Arrays.asList(2, 0, 0, 0); List newMenuScore = Arrays.asList(0, 0, 0, 1); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/RunScoreTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/RunScoreTests.java deleted file mode 100644 index e92656846..000000000 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/RunScoreTests.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2019 Livio, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following - * disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * Neither the name of the Livio Inc. nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package com.smartdevicelink.managers.screen.menu; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.smartdevicelink.test.TestValues; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static junit.framework.TestCase.assertEquals; - -@RunWith(AndroidJUnit4.class) -public class RunScoreTests { - - @Test - public void testSettersAndGetters() { - - // set everything - we only use the constructor to set variables in the Menu Manager - RunScore runScore = new RunScore(TestValues.GENERAL_INT, TestValues.GENERAL_INTEGER_LIST, TestValues.GENERAL_INTEGER_LIST); - - // use getters and assert equality - assertEquals(runScore.getScore(), TestValues.GENERAL_INT); - assertEquals(runScore.getCurrentMenu(), TestValues.GENERAL_INTEGER_LIST); - assertEquals(runScore.getOldMenu(), TestValues.GENERAL_INTEGER_LIST); - } - -} diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/SubCellCommandListTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/SubCellCommandListTests.java index 03afc5348..e0380aa09 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/SubCellCommandListTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/SubCellCommandListTests.java @@ -47,7 +47,7 @@ public class SubCellCommandListTests { @Test public void testSettersAndGetters() { - RunScore runScore = new RunScore(TestValues.GENERAL_INT, TestValues.GENERAL_INTEGER_LIST, TestValues.GENERAL_INTEGER_LIST); + DynamicMenuUpdateRunScore runScore = new DynamicMenuUpdateRunScore(TestValues.GENERAL_INT, TestValues.GENERAL_INTEGER_LIST, TestValues.GENERAL_INTEGER_LIST); // set everything SubCellCommandList subCellCommandList = new SubCellCommandList(TestValues.GENERAL_STRING, TestValues.GENERAL_INTEGER, runScore, TestValues.GENERAL_MENUCELL_LIST, TestValues.GENERAL_MENUCELL_LIST); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 170ebd782..10d84307e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -188,7 +188,7 @@ abstract class BaseMenuManager extends BaseSubManager { cancelPendingMenuReplaceOperations(); - MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, oldMenuCells, menuCells, new MenuManagerCompletionListener() { + MenuReplaceStaticOperation operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, oldMenuCells, menuCells, new MenuManagerCompletionListener() { @Override public void onComplete(boolean success, List oldMenuCells) { BaseMenuManager.this.oldMenuCells = oldMenuCells; @@ -398,16 +398,16 @@ abstract class BaseMenuManager extends BaseSubManager { private void updatePendingOperationsWithNewMenuConfiguration() { for (Task task : transactionQueue.getTasksAsList()) { - if (!(task instanceof MenuReplaceOperation)) { + if (!(task instanceof MenuReplaceStaticOperation)) { continue; } - ((MenuReplaceOperation) task).setMenuConfiguration(menuConfiguration); + ((MenuReplaceStaticOperation) task).setMenuConfiguration(menuConfiguration); } } private void cancelPendingMenuReplaceOperations(){ for (Task operation : transactionQueue.getTasksAsList()) { - if (!(operation instanceof MenuReplaceOperation)) { + if (!(operation instanceof MenuReplaceStaticOperation)) { continue; } operation.cancelTask(); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java new file mode 100644 index 000000000..f821b81cb --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2019 Livio, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Livio Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package com.smartdevicelink.managers.screen.menu; + +import java.util.List; + +class DynamicMenuUpdateRunScore { + + private int score; + private List oldMenu, currentMenu; + + DynamicMenuUpdateRunScore(int score, List oldMenu, List currentMenu) { + setScore(score); + setOldMenu(oldMenu); + setCurrentMenu(currentMenu); + } + + private void setCurrentMenu(List currentMenu) { + this.currentMenu = currentMenu; + } + + List getCurrentMenu() { + return currentMenu; + } + + private void setOldMenu(List oldMenu) { + this.oldMenu = oldMenu; + } + + List getOldMenu() { + return oldMenu; + } + + private void setScore(int score) { + this.score = score; + } + + public int getScore() { + return score; + } + +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java new file mode 100644 index 000000000..78c1144ba --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -0,0 +1,91 @@ +package com.smartdevicelink.managers.screen.menu; + +import com.livio.taskmaster.Task; +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.proxy.RPCRequest; +import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.rpc.AddCommand; +import com.smartdevicelink.proxy.rpc.AddSubMenu; +import com.smartdevicelink.proxy.rpc.DeleteCommand; +import com.smartdevicelink.proxy.rpc.DeleteSubMenu; +import com.smartdevicelink.proxy.rpc.MenuParams; +import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.DisplayType; +import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; +import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; +import com.smartdevicelink.util.DebugTool; + +import org.json.JSONException; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; + +/** + * Created by Bilal Alsharifi on 1/20/21. + */ +class MenuReplaceDynamicOperation extends Task { + private static final String TAG = "MenuReplaceDynamicOperation"; + + private final WeakReference internalInterface; + private final WeakReference fileManager; + private final WindowCapability defaultMainWindowCapability; + private List oldMenuCells; + private final List menuCells; + private final MenuManagerCompletionListener operationCompletionListener; + private final String displayType; + private final DynamicMenuUpdatesMode dynamicMenuUpdatesMode; + private int lastMenuId; + private MenuConfiguration menuConfiguration; + + MenuReplaceDynamicOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, MenuManagerCompletionListener operationCompletionListener) { + super(TAG); + this.internalInterface = new WeakReference<>(internalInterface); + this.fileManager = new WeakReference<>(fileManager); + this.displayType = displayType; + this.dynamicMenuUpdatesMode = dynamicMenuUpdatesMode; + this.menuConfiguration = menuConfiguration; + this.defaultMainWindowCapability = defaultMainWindowCapability; + this.oldMenuCells = oldMenuCells; + this.menuCells = menuCells; + this.operationCompletionListener = operationCompletionListener; + } + + @Override + public void onExecute() { + start(); + } + + private void start() { + if (getState() == Task.CANCELED) { + return; + } + + updateMenuCells(new CompletionListener() { + @Override + public void onComplete(boolean success) { + finishOperation(success); + } + }); + } + + private void updateMenuCells(final CompletionListener listener) { + + } + + private void finishOperation(boolean success) { + if (operationCompletionListener != null) { + operationCompletionListener.onComplete(success, oldMenuCells); + } + onFinished(); + } +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java deleted file mode 100644 index 2fbb7fe5f..000000000 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ /dev/null @@ -1,861 +0,0 @@ -package com.smartdevicelink.managers.screen.menu; - -import com.livio.taskmaster.Task; -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.proxy.RPCRequest; -import com.smartdevicelink.proxy.RPCResponse; -import com.smartdevicelink.proxy.rpc.AddCommand; -import com.smartdevicelink.proxy.rpc.AddSubMenu; -import com.smartdevicelink.proxy.rpc.DeleteCommand; -import com.smartdevicelink.proxy.rpc.DeleteSubMenu; -import com.smartdevicelink.proxy.rpc.MenuParams; -import com.smartdevicelink.proxy.rpc.WindowCapability; -import com.smartdevicelink.proxy.rpc.enums.DisplayType; -import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; -import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; -import com.smartdevicelink.util.DebugTool; - -import org.json.JSONException; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; - -/** - * Created by Bilal Alsharifi on 1/20/21. - */ -class MenuReplaceOperation extends Task { - private static final String TAG = "MenuReplaceOperation"; - private static final int KEEP = 0; - private static final int MARKED_FOR_ADDITION = 1; - private static final int MARKED_FOR_DELETION = 2; - - private final WeakReference internalInterface; - private final WeakReference fileManager; - private final WindowCapability defaultMainWindowCapability; - private List oldMenuCells; - private final List menuCells; - private List keepsOld; - private List keepsNew; - private final MenuManagerCompletionListener operationCompletionListener; - private final String displayType; - private final DynamicMenuUpdatesMode dynamicMenuUpdatesMode; - private int lastMenuId; - private MenuConfiguration menuConfiguration; - - MenuReplaceOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, MenuManagerCompletionListener operationCompletionListener) { - super(TAG); - this.internalInterface = new WeakReference<>(internalInterface); - this.fileManager = new WeakReference<>(fileManager); - this.displayType = displayType; - this.dynamicMenuUpdatesMode = dynamicMenuUpdatesMode; - this.menuConfiguration = menuConfiguration; - this.defaultMainWindowCapability = defaultMainWindowCapability; - this.oldMenuCells = oldMenuCells; - this.menuCells = menuCells; - this.operationCompletionListener = operationCompletionListener; - } - - @Override - public void onExecute() { - start(); - } - - private void start() { - if (getState() == Task.CANCELED) { - return; - } - - updateMenuCells(new CompletionListener() { - @Override - public void onComplete(boolean success) { - finishOperation(success); - } - }); - } - - private void updateMenuCells(final CompletionListener listener) { - // Upload the Artworks - List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(menuCells); - if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { - fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { - @Override - public void onComplete(Map errors) { - if (errors != null && !errors.isEmpty()) { - DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); - } else { - DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); - } - // proceed - updateMenuAndDetermineBestUpdateMethod(listener); - } - }); - } else { - // No Artworks to be uploaded, send off - updateMenuAndDetermineBestUpdateMethod(listener); - } - } - - private void updateMenuAndDetermineBestUpdateMethod(CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - // Checks against what the developer set for update mode and against the display type to - // determine how the menu will be updated. This has the ability to be changed during a session. - if (checkUpdateMode(dynamicMenuUpdatesMode, displayType)) { - // Run the lists through the new algorithm - RunScore rootScore = runMenuCompareAlgorithm(oldMenuCells, menuCells); - if (rootScore == null) { - // Send initial menu without dynamic updates because oldMenuCells is null - DebugTool.logInfo(TAG, "Creating initial Menu"); - lastMenuId = menuCellIdMin; - updateIdsOnMenuCells(menuCells, parentIdNotFound); - createAndSendEntireMenu(listener); - } else { - DebugTool.logInfo(TAG, "Dynamically Updating Menu"); - if (menuCells.isEmpty() && (oldMenuCells != null && !oldMenuCells.isEmpty())) { - // the dev wants to clear the menu. We have old cells and an empty array of new ones. - deleteMenuWhenNewCellsEmpty(listener); - } else { - // lets dynamically update the root menu - dynamicallyUpdateRootMenu(rootScore, listener); - } - } - } else { - // We are in compatibility mode. No need to run the algorithm - DebugTool.logInfo(TAG, "Updating menus in compatibility mode"); - lastMenuId = menuCellIdMin; - updateIdsOnMenuCells(menuCells, parentIdNotFound); - createAndSendEntireMenu(listener); - } - } - - private boolean checkUpdateMode(DynamicMenuUpdatesMode updateMode, String displayType) { - if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { - if (displayType == null) { - return true; - } - return (!displayType.equals(DisplayType.GEN3_8_INCH.toString())); - - } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_OFF)) { - return false; - } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_ON)) { - return true; - } - - return true; - } - - private void deleteMenuWhenNewCellsEmpty(final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - sendDeleteRPCs(createDeleteRPCsForCells(oldMenuCells), new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } else { - DebugTool.logInfo(TAG, "Successfully Cleared Menu"); - } - oldMenuCells = null; - listener.onComplete(success); - } - }); - } - - private void dynamicallyUpdateRootMenu(RunScore bestRootScore, CompletionListener listener) { - // We need to run through the keeps and see if they have subCells, as they also need to be run through the compare function. - List newIntArray = bestRootScore.getCurrentMenu(); - List oldIntArray = bestRootScore.getOldMenu(); - List deleteCommands; - - // Set up deletes - List deletes = new ArrayList<>(); - keepsOld = new ArrayList<>(); - for (int x = 0; x < oldIntArray.size(); x++) { - Integer old = oldIntArray.get(x); - if (old.equals(MARKED_FOR_DELETION)) { - // grab cell to send to function to create delete commands - deletes.add(oldMenuCells.get(x)); - } else if (old.equals(KEEP)) { - keepsOld.add(oldMenuCells.get(x)); - } - } - // create the delete commands - deleteCommands = createDeleteRPCsForCells(deletes); - - // Set up the adds - List adds = new ArrayList<>(); - keepsNew = new ArrayList<>(); - for (int x = 0; x < newIntArray.size(); x++) { - Integer newInt = newIntArray.get(x); - if (newInt.equals(MARKED_FOR_ADDITION)) { - // grab cell to send to function to create add commands - adds.add(menuCells.get(x)); - } else if (newInt.equals(KEEP)) { - keepsNew.add(menuCells.get(x)); - } - } - updateIdsOnDynamicCells(adds); - // this is needed for the onCommands to still work - transferIdsToKeptCells(keepsNew); - - if (!adds.isEmpty()) { // todo what if adds was empty but deletes was not? - DebugTool.logInfo(TAG, "Sending root menu updates"); - sendDynamicRootMenuRPCs(deleteCommands, adds, listener); - } else { - DebugTool.logInfo(TAG, "All root menu items are kept. Check the sub menus"); - runSubMenuCompareAlgorithm(listener); - } - } - - private void createAndSendEntireMenu(final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - deleteRootMenu(new CompletionListener() { - @Override - public void onComplete(boolean success) { - createAndSendMenuCellRPCs(menuCells, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } - - listener.onComplete(success); - } - }); - } - }); - } - - private void createAndSendMenuCellRPCs(final List menu, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (menu == null || menu.isEmpty()) { - if (listener != null) { - // This can be considered a success if the user was clearing out their menu - listener.onComplete(true); - } - return; - } - - List mainMenuCommands; - final List subMenuCommands; - - if (!shouldRPCsIncludeImages(menu) || !supportsImages()) { - // Send artwork-less menu - mainMenuCommands = mainMenuCommandsForCells(menu, false); - subMenuCommands = subMenuCommandsForCells(menu, false); - } else { - mainMenuCommands = mainMenuCommandsForCells(menu, true); - subMenuCommands = subMenuCommandsForCells(menu, true); - } - - internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - } - - @Override - public void onFinished() { - if (!subMenuCommands.isEmpty()) { - DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); - sendSubMenuCommandRPCs(subMenuCommands, listener); - } else { - if (keepsNew != null && !keepsNew.isEmpty()) { - runSubMenuCompareAlgorithm(listener); - } else { - DebugTool.logInfo(TAG, "Finished sending main menu commands."); - - if (listener != null) { - listener.onComplete(true); - } - } - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, "Main Menu response: " + response.serializeJSON().toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); - } - } - }); - } - - private void sendSubMenuCommandRPCs(List commands, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - internalInterface.get().sendSequentialRPCs(commands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - } - - @Override - public void onFinished() { - if (keepsNew != null && !keepsNew.isEmpty()) { - runSubMenuCompareAlgorithm(listener); - } else { - DebugTool.logInfo(TAG, "Finished Updating Menu"); - - if (listener != null) { - listener.onComplete(true); - } - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, "Sub Menu response: " + response.serializeJSON().toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - DebugTool.logError(TAG, "Failed to send sub menu commands: " + response.getInfo()); - } - } - }); - } - - private void createAndSendDynamicSubMenuRPCs(List newMenu, final List adds, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (adds.isEmpty()) { - if (listener != null) { - // This can be considered a success if the user was clearing out their menu - DebugTool.logError(TAG, "Called createAndSendDynamicSubMenuRPCs with empty menu"); - listener.onComplete(true); - } - return; - } - - List mainMenuCommands; - - if (!shouldRPCsIncludeImages(adds) || !supportsImages()) { - // Send artwork-less menu - mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, false); - } else { - mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, true); - } - - internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - // nothing here - } - - @Override - public void onFinished() { - if (listener != null) { - listener.onComplete(true); - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, "Dynamic Sub Menu response: " + response.serializeJSON().toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); - } - } - }); - } - - private void deleteRootMenu(final CompletionListener listener) { - if (oldMenuCells == null || oldMenuCells.isEmpty()) { - if (listener != null) { - // technically this method is successful if there's nothing to delete - DebugTool.logInfo(TAG, "No old cells to delete, returning"); - listener.onComplete(true); - } - } else { - sendDeleteRPCs(createDeleteRPCsForCells(oldMenuCells), listener); - } - } - - private void sendDeleteRPCs(List deleteCommands, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (deleteCommands == null || deleteCommands.isEmpty()) { - // no dynamic deletes required. return - if (listener != null) { - // technically this method is successful if there's nothing to delete - listener.onComplete(true); - } - return; - } - - internalInterface.get().sendRPCs(deleteCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - - } - - @Override - public void onFinished() { - DebugTool.logInfo(TAG, "Successfully deleted cells"); - if (listener != null) { - listener.onComplete(true); - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - - } - }); - } - - private List subMenuCommandsForCells(List cells, boolean shouldHaveArtwork) { - List builtCommands = new ArrayList<>(); - for (MenuCell cell : cells) { - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); - } - } - return builtCommands; - } - - List allCommandsForCells(List cells, boolean shouldHaveArtwork) { - List builtCommands = new ArrayList<>(); - - for (int i = 0; i < cells.size(); i++) { - MenuCell cell = cells.get(i); - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - builtCommands.add(subMenuCommandForMenuCell(cell, shouldHaveArtwork, i)); - - // recursively grab the commands for all the sub cells - builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); - } else { - builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, i)); - } - } - return builtCommands; - } - - private List createCommandsForDynamicSubCells(List oldMenuCells, List cells, boolean shouldHaveArtwork) { - List builtCommands = new ArrayList<>(); - for (int z = 0; z < oldMenuCells.size(); z++) { - MenuCell oldCell = oldMenuCells.get(z); - for (int i = 0; i < cells.size(); i++) { - MenuCell cell = cells.get(i); - if (cell.equals(oldCell)) { - builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, z)); - break; - } - } - } - return builtCommands; - } - - private AddCommand commandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { - MenuParams params = new MenuParams(cell.getTitle()); - params.setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null); - params.setPosition(position); - - AddCommand command = new AddCommand(cell.getCellId()); - command.setMenuParams(params); - if (cell.getVoiceCommands() != null && !cell.getVoiceCommands().isEmpty()) { - command.setVrCommands(cell.getVoiceCommands()); - } else { - command.setVrCommands(null); - } - command.setCmdIcon((cell.getIcon() != null && shouldHaveArtwork) ? cell.getIcon().getImageRPC() : null); - - return command; - } - - private AddSubMenu subMenuCommandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { - AddSubMenu subMenu = new AddSubMenu(cell.getCellId(), cell.getTitle()); - subMenu.setPosition(position); - if (cell.getSubMenuLayout() != null) { - subMenu.setMenuLayout(cell.getSubMenuLayout()); - } else if (menuConfiguration != null && menuConfiguration.getSubMenuLayout() != null) { - subMenu.setMenuLayout(menuConfiguration.getSubMenuLayout()); - } - subMenu.setMenuIcon((shouldHaveArtwork && (cell.getIcon() != null && cell.getIcon().getImageRPC() != null)) ? cell.getIcon().getImageRPC() : null); - return subMenu; - } - - private void sendDynamicRootMenuRPCs(List deleteCommands, final List updatedCells, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - sendDeleteRPCs(deleteCommands, new CompletionListener() { - @Override - public void onComplete(boolean success) { - createAndSendMenuCellRPCs(updatedCells, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } - - listener.onComplete(success); - } - }); - } - }); - } - - private void runSubMenuCompareAlgorithm(CompletionListener listener) { - // any cells that were re-added have their sub-cells added with them - // at this point all we care about are the cells that were deemed equal and kept. - if (keepsNew == null || keepsNew.isEmpty()) { - listener.onComplete(true); - return; - } - - List commandLists = new ArrayList<>(); - - for (int i = 0; i < keepsNew.size(); i++) { - MenuCell newKeptCell = keepsNew.get(i); - MenuCell oldKeptCell = keepsOld.get(i); - - if (oldKeptCell.getSubCells() != null && !oldKeptCell.getSubCells().isEmpty() && newKeptCell.getSubCells() != null && !newKeptCell.getSubCells().isEmpty()) { - RunScore subScore = compareOldAndNewLists(oldKeptCell.getSubCells(), newKeptCell.getSubCells()); - - if (subScore != null) { - DebugTool.logInfo(TAG, "Sub menu Run Score: " + oldKeptCell.getTitle() + " Score: " + subScore.getScore()); - SubCellCommandList commandList = new SubCellCommandList(oldKeptCell.getTitle(), oldKeptCell.getCellId(), subScore, oldKeptCell.getSubCells(), newKeptCell.getSubCells()); - commandLists.add(commandList); - } - } - } - createSubMenuDynamicCommands(commandLists, listener); - } - - private void createSubMenuDynamicCommands(final List commandLists, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (commandLists.isEmpty()) { - DebugTool.logInfo(TAG, "All menu updates, including sub menus - done."); - listener.onComplete(true); - return; - } - - final SubCellCommandList commandList = commandLists.remove(0); - - DebugTool.logInfo(TAG, "Creating and Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - - // grab the scores - RunScore score = commandList.getListsScore(); - List newIntArray = score.getCurrentMenu(); - List oldIntArray = score.getOldMenu(); - - // Grab the sub-menus from the parent cell - final List oldCells = commandList.getOldList(); - final List newCells = commandList.getNewList(); - - // Set up deletes - List deletes = new ArrayList<>(); - for (int x = 0; x < oldIntArray.size(); x++) { - Integer old = oldIntArray.get(x); - if (old.equals(MARKED_FOR_DELETION)) { - // grab cell to send to function to create delete commands - deletes.add(oldCells.get(x)); - } - } - // create the delete commands - List deleteCommands = createDeleteRPCsForCells(deletes); - - // Set up the adds - List adds = new ArrayList<>(); - List subCellKeepsNew = new ArrayList<>(); - for (int x = 0; x < newIntArray.size(); x++) { - Integer newInt = newIntArray.get(x); - if (newInt.equals(MARKED_FOR_ADDITION)) { - // grab cell to send to function to create add commands - adds.add(newCells.get(x)); - } else if (newInt.equals(KEEP)) { - subCellKeepsNew.add(newCells.get(x)); - } - } - final List addsWithNewIds = updateIdsOnDynamicSubCells(oldCells, adds, commandList.getParentId()); - // this is needed for the onCommands to still work - transferIdsToKeptSubCells(oldCells, subCellKeepsNew); - - sendDeleteRPCs(deleteCommands, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (addsWithNewIds != null && !addsWithNewIds.isEmpty()) { - createAndSendDynamicSubMenuRPCs(newCells, addsWithNewIds, new CompletionListener() { - @Override - public void onComplete(boolean success) { - // recurse through next sub list - DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - createSubMenuDynamicCommands(commandLists, listener); - } - }); - } else { - // no add commands to send, recurse through next sub list - DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - createSubMenuDynamicCommands(commandLists, listener); - } - } - }); - } - - RunScore runMenuCompareAlgorithm(List oldCells, List newCells) { - if (oldCells == null || oldCells.isEmpty()) { - return null; - } - - RunScore bestScore = compareOldAndNewLists(oldCells, newCells); - DebugTool.logInfo(TAG, "Best menu run score: " + bestScore.getScore()); - - return bestScore; - } - - private RunScore compareOldAndNewLists(List oldCells, List newCells) { - RunScore bestRunScore = null; - - // This first loop is for each 'run' - for (int run = 0; run < oldCells.size(); run++) { - List oldArray = new ArrayList<>(oldCells.size()); - List newArray = new ArrayList<>(newCells.size()); - - // Set the statuses - for (int i = 0; i < oldCells.size(); i++) { - oldArray.add(MARKED_FOR_DELETION); - } - for (int i = 0; i < newCells.size(); i++) { - newArray.add(MARKED_FOR_ADDITION); - } - - int startIndex = 0; - - // Keep items that appear in both lists - for (int oldItems = run; oldItems < oldCells.size(); oldItems++) { - - for (int newItems = startIndex; newItems < newCells.size(); newItems++) { - - if (oldCells.get(oldItems).equals(newCells.get(newItems))) { - oldArray.set(oldItems, KEEP); - newArray.set(newItems, KEEP); - // set the new start index - startIndex = newItems + 1; - break; - } - } - } - - // Calculate number of adds, or the 'score' for this run - int numberOfAdds = 0; - for (int x = 0; x < newArray.size(); x++) { - if (newArray.get(x).equals(MARKED_FOR_ADDITION)) { - numberOfAdds++; - } - } - - // see if we have a new best score and set it if we do - if (bestRunScore == null || numberOfAdds < bestRunScore.getScore()) { - bestRunScore = new RunScore(numberOfAdds, oldArray, newArray); - } - - } - return bestRunScore; - } - - private boolean shouldRPCsIncludeImages(List cells) { - for (MenuCell cell : cells) { - SdlArtwork artwork = cell.getIcon(); - if (artwork != null && !artwork.isStaticIcon() && fileManager.get() != null && !fileManager.get().hasUploadedFile(artwork)) { - return false; - } else if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - return shouldRPCsIncludeImages(cell.getSubCells()); - } - } - return true; - } - - private void updateIdsOnDynamicCells(List dynamicCells) { - if (menuCells != null && !menuCells.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { - for (int z = 0; z < menuCells.size(); z++) { - MenuCell mainCell = menuCells.get(z); - for (int i = 0; i < dynamicCells.size(); i++) { - MenuCell dynamicCell = dynamicCells.get(i); - if (mainCell.equals(dynamicCell)) { - int newId = ++lastMenuId; //todo won't the lastMenuId reset to 1 every time? - menuCells.get(z).setCellId(newId); - dynamicCells.get(i).setCellId(newId); - - if (mainCell.getSubCells() != null && !mainCell.getSubCells().isEmpty()) { - updateIdsOnMenuCells(mainCell.getSubCells(), mainCell.getCellId()); - } - break; - } - } - } - } - } - - private List updateIdsOnDynamicSubCells(List oldList, List dynamicCells, Integer parentId) { - if (oldList != null && !oldList.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { - for (int z = 0; z < oldList.size(); z++) { - MenuCell mainCell = oldList.get(z); - for (int i = 0; i < dynamicCells.size(); i++) { - MenuCell dynamicCell = dynamicCells.get(i); - if (mainCell.equals(dynamicCell)) { - int newId = ++lastMenuId; - oldList.get(z).setCellId(newId); - dynamicCells.get(i).setParentCellId(parentId); - dynamicCells.get(i).setCellId(newId); - } else { - int newId = ++lastMenuId; - dynamicCells.get(i).setParentCellId(parentId); - dynamicCells.get(i).setCellId(newId); - } - } - } - return dynamicCells; - } - return null; - } - - private void updateIdsOnMenuCells(List cells, int parentId) { - for (MenuCell cell : cells) { - int newId = ++lastMenuId; - cell.setCellId(newId); - cell.setParentCellId(parentId); - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); - } - } - } - - private void transferIdsToKeptCells(List keeps) { - for (int z = 0; z < oldMenuCells.size(); z++) { - MenuCell oldCell = oldMenuCells.get(z); - for (int i = 0; i < keeps.size(); i++) { - MenuCell keptCell = keeps.get(i); - if (oldCell.equals(keptCell)) { - keptCell.setCellId(oldCell.getCellId()); - break; - } - } - } - } - - private void transferIdsToKeptSubCells(List old, List keeps) { - for (int z = 0; z < old.size(); z++) { - MenuCell oldCell = old.get(z); - for (int i = 0; i < keeps.size(); i++) { - MenuCell keptCell = keeps.get(i); - if (oldCell.equals(keptCell)) { - keptCell.setCellId(oldCell.getCellId()); - break; - } - } - } - } - - private List createDeleteRPCsForCells(List cells) { - List deletes = new ArrayList<>(); - for (MenuCell cell : cells) { - if (cell.getSubCells() == null) { - DeleteCommand delete = new DeleteCommand(cell.getCellId()); - deletes.add(delete); - } else { - DeleteSubMenu delete = new DeleteSubMenu(cell.getCellId()); - deletes.add(delete); - } - } - return deletes; - } - - private List mainMenuCommandsForCells(List cellsToAdd, boolean shouldHaveArtwork) { - List builtCommands = new ArrayList<>(); - - // We need the index so we will use this type of loop - for (int z = 0; z < menuCells.size(); z++) { - MenuCell mainCell = menuCells.get(z); - for (int i = 0; i < cellsToAdd.size(); i++) { - MenuCell addCell = cellsToAdd.get(i); - if (mainCell.equals(addCell)) { - if (addCell.getSubCells() != null && !addCell.getSubCells().isEmpty()) { - builtCommands.add(subMenuCommandForMenuCell(addCell, shouldHaveArtwork, z)); - } else { - builtCommands.add(commandForMenuCell(addCell, shouldHaveArtwork, z)); - } - break; - } - } - } - return builtCommands; - } - - private List findAllArtworksToBeUploadedFromCells(List cells) { - // Make sure we can use images in the menus - if (!supportsImages()) { - return new ArrayList<>(); - } - - List artworks = new ArrayList<>(); - for (MenuCell cell : cells) { - if (fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getIcon())) { - artworks.add(cell.getIcon()); - } - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - artworks.addAll(findAllArtworksToBeUploadedFromCells(cell.getSubCells())); - } - } - - return artworks; - } - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - private boolean supportsImages() { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.cmdIcon); - } - - void setMenuConfiguration(MenuConfiguration menuConfiguration) { - this.menuConfiguration = menuConfiguration; - } - - private void finishOperation(boolean success) { - if (operationCompletionListener != null) { - operationCompletionListener.onComplete(success, oldMenuCells); - } - onFinished(); - } -} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java new file mode 100644 index 000000000..9a8c9730e --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -0,0 +1,861 @@ +package com.smartdevicelink.managers.screen.menu; + +import com.livio.taskmaster.Task; +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.proxy.RPCRequest; +import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.rpc.AddCommand; +import com.smartdevicelink.proxy.rpc.AddSubMenu; +import com.smartdevicelink.proxy.rpc.DeleteCommand; +import com.smartdevicelink.proxy.rpc.DeleteSubMenu; +import com.smartdevicelink.proxy.rpc.MenuParams; +import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.DisplayType; +import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; +import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; +import com.smartdevicelink.util.DebugTool; + +import org.json.JSONException; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; + +/** + * Created by Bilal Alsharifi on 1/20/21. + */ +class MenuReplaceStaticOperation extends Task { + private static final String TAG = "MenuReplaceOperation"; + private static final int KEEP = 0; + private static final int MARKED_FOR_ADDITION = 1; + private static final int MARKED_FOR_DELETION = 2; + + private final WeakReference internalInterface; + private final WeakReference fileManager; + private final WindowCapability defaultMainWindowCapability; + private List oldMenuCells; + private final List menuCells; + private List keepsOld; + private List keepsNew; + private final MenuManagerCompletionListener operationCompletionListener; + private final String displayType; + private final DynamicMenuUpdatesMode dynamicMenuUpdatesMode; + private int lastMenuId; + private MenuConfiguration menuConfiguration; + + MenuReplaceStaticOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, MenuManagerCompletionListener operationCompletionListener) { + super(TAG); + this.internalInterface = new WeakReference<>(internalInterface); + this.fileManager = new WeakReference<>(fileManager); + this.displayType = displayType; + this.dynamicMenuUpdatesMode = dynamicMenuUpdatesMode; + this.menuConfiguration = menuConfiguration; + this.defaultMainWindowCapability = defaultMainWindowCapability; + this.oldMenuCells = oldMenuCells; + this.menuCells = menuCells; + this.operationCompletionListener = operationCompletionListener; + } + + @Override + public void onExecute() { + start(); + } + + private void start() { + if (getState() == Task.CANCELED) { + return; + } + + updateMenuCells(new CompletionListener() { + @Override + public void onComplete(boolean success) { + finishOperation(success); + } + }); + } + + private void updateMenuCells(final CompletionListener listener) { + // Upload the Artworks + List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(menuCells); + if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { + fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { + @Override + public void onComplete(Map errors) { + if (errors != null && !errors.isEmpty()) { + DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); + } else { + DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); + } + // proceed + updateMenuAndDetermineBestUpdateMethod(listener); + } + }); + } else { + // No Artworks to be uploaded, send off + updateMenuAndDetermineBestUpdateMethod(listener); + } + } + + private void updateMenuAndDetermineBestUpdateMethod(CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + // Checks against what the developer set for update mode and against the display type to + // determine how the menu will be updated. This has the ability to be changed during a session. + if (checkUpdateMode(dynamicMenuUpdatesMode, displayType)) { + // Run the lists through the new algorithm + DynamicMenuUpdateRunScore rootScore = runMenuCompareAlgorithm(oldMenuCells, menuCells); + if (rootScore == null) { + // Send initial menu without dynamic updates because oldMenuCells is null + DebugTool.logInfo(TAG, "Creating initial Menu"); + lastMenuId = menuCellIdMin; + updateIdsOnMenuCells(menuCells, parentIdNotFound); + createAndSendEntireMenu(listener); + } else { + DebugTool.logInfo(TAG, "Dynamically Updating Menu"); + if (menuCells.isEmpty() && (oldMenuCells != null && !oldMenuCells.isEmpty())) { + // the dev wants to clear the menu. We have old cells and an empty array of new ones. + deleteMenuWhenNewCellsEmpty(listener); + } else { + // lets dynamically update the root menu + dynamicallyUpdateRootMenu(rootScore, listener); + } + } + } else { + // We are in compatibility mode. No need to run the algorithm + DebugTool.logInfo(TAG, "Updating menus in compatibility mode"); + lastMenuId = menuCellIdMin; + updateIdsOnMenuCells(menuCells, parentIdNotFound); + createAndSendEntireMenu(listener); + } + } + + private boolean checkUpdateMode(DynamicMenuUpdatesMode updateMode, String displayType) { + if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { + if (displayType == null) { + return true; + } + return (!displayType.equals(DisplayType.GEN3_8_INCH.toString())); + + } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_OFF)) { + return false; + } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_ON)) { + return true; + } + + return true; + } + + private void deleteMenuWhenNewCellsEmpty(final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + sendDeleteRPCs(createDeleteRPCsForCells(oldMenuCells), new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (!success) { + DebugTool.logError(TAG, "Error Sending Current Menu"); + } else { + DebugTool.logInfo(TAG, "Successfully Cleared Menu"); + } + oldMenuCells = null; + listener.onComplete(success); + } + }); + } + + private void dynamicallyUpdateRootMenu(DynamicMenuUpdateRunScore bestRootScore, CompletionListener listener) { + // We need to run through the keeps and see if they have subCells, as they also need to be run through the compare function. + List newIntArray = bestRootScore.getCurrentMenu(); + List oldIntArray = bestRootScore.getOldMenu(); + List deleteCommands; + + // Set up deletes + List deletes = new ArrayList<>(); + keepsOld = new ArrayList<>(); + for (int x = 0; x < oldIntArray.size(); x++) { + Integer old = oldIntArray.get(x); + if (old.equals(MARKED_FOR_DELETION)) { + // grab cell to send to function to create delete commands + deletes.add(oldMenuCells.get(x)); + } else if (old.equals(KEEP)) { + keepsOld.add(oldMenuCells.get(x)); + } + } + // create the delete commands + deleteCommands = createDeleteRPCsForCells(deletes); + + // Set up the adds + List adds = new ArrayList<>(); + keepsNew = new ArrayList<>(); + for (int x = 0; x < newIntArray.size(); x++) { + Integer newInt = newIntArray.get(x); + if (newInt.equals(MARKED_FOR_ADDITION)) { + // grab cell to send to function to create add commands + adds.add(menuCells.get(x)); + } else if (newInt.equals(KEEP)) { + keepsNew.add(menuCells.get(x)); + } + } + updateIdsOnDynamicCells(adds); + // this is needed for the onCommands to still work + transferIdsToKeptCells(keepsNew); + + if (!adds.isEmpty()) { // todo what if adds was empty but deletes was not? + DebugTool.logInfo(TAG, "Sending root menu updates"); + sendDynamicRootMenuRPCs(deleteCommands, adds, listener); + } else { + DebugTool.logInfo(TAG, "All root menu items are kept. Check the sub menus"); + runSubMenuCompareAlgorithm(listener); + } + } + + private void createAndSendEntireMenu(final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + deleteRootMenu(new CompletionListener() { + @Override + public void onComplete(boolean success) { + createAndSendMenuCellRPCs(menuCells, new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (!success) { + DebugTool.logError(TAG, "Error Sending Current Menu"); + } + + listener.onComplete(success); + } + }); + } + }); + } + + private void createAndSendMenuCellRPCs(final List menu, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + if (menu == null || menu.isEmpty()) { + if (listener != null) { + // This can be considered a success if the user was clearing out their menu + listener.onComplete(true); + } + return; + } + + List mainMenuCommands; + final List subMenuCommands; + + if (!shouldRPCsIncludeImages(menu) || !supportsImages()) { + // Send artwork-less menu + mainMenuCommands = mainMenuCommandsForCells(menu, false); + subMenuCommands = subMenuCommandsForCells(menu, false); + } else { + mainMenuCommands = mainMenuCommandsForCells(menu, true); + subMenuCommands = subMenuCommandsForCells(menu, true); + } + + internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + } + + @Override + public void onFinished() { + if (!subMenuCommands.isEmpty()) { + DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); + sendSubMenuCommandRPCs(subMenuCommands, listener); + } else { + if (keepsNew != null && !keepsNew.isEmpty()) { + runSubMenuCompareAlgorithm(listener); + } else { + DebugTool.logInfo(TAG, "Finished sending main menu commands."); + + if (listener != null) { + listener.onComplete(true); + } + } + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + try { + DebugTool.logInfo(TAG, "Main Menu response: " + response.serializeJSON().toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); + } + } + }); + } + + private void sendSubMenuCommandRPCs(List commands, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + internalInterface.get().sendSequentialRPCs(commands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + } + + @Override + public void onFinished() { + if (keepsNew != null && !keepsNew.isEmpty()) { + runSubMenuCompareAlgorithm(listener); + } else { + DebugTool.logInfo(TAG, "Finished Updating Menu"); + + if (listener != null) { + listener.onComplete(true); + } + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + try { + DebugTool.logInfo(TAG, "Sub Menu response: " + response.serializeJSON().toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + DebugTool.logError(TAG, "Failed to send sub menu commands: " + response.getInfo()); + } + } + }); + } + + private void createAndSendDynamicSubMenuRPCs(List newMenu, final List adds, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + if (adds.isEmpty()) { + if (listener != null) { + // This can be considered a success if the user was clearing out their menu + DebugTool.logError(TAG, "Called createAndSendDynamicSubMenuRPCs with empty menu"); + listener.onComplete(true); + } + return; + } + + List mainMenuCommands; + + if (!shouldRPCsIncludeImages(adds) || !supportsImages()) { + // Send artwork-less menu + mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, false); + } else { + mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, true); + } + + internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + // nothing here + } + + @Override + public void onFinished() { + if (listener != null) { + listener.onComplete(true); + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + try { + DebugTool.logInfo(TAG, "Dynamic Sub Menu response: " + response.serializeJSON().toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); + } + } + }); + } + + private void deleteRootMenu(final CompletionListener listener) { + if (oldMenuCells == null || oldMenuCells.isEmpty()) { + if (listener != null) { + // technically this method is successful if there's nothing to delete + DebugTool.logInfo(TAG, "No old cells to delete, returning"); + listener.onComplete(true); + } + } else { + sendDeleteRPCs(createDeleteRPCsForCells(oldMenuCells), listener); + } + } + + private void sendDeleteRPCs(List deleteCommands, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + if (deleteCommands == null || deleteCommands.isEmpty()) { + // no dynamic deletes required. return + if (listener != null) { + // technically this method is successful if there's nothing to delete + listener.onComplete(true); + } + return; + } + + internalInterface.get().sendRPCs(deleteCommands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + + } + + @Override + public void onFinished() { + DebugTool.logInfo(TAG, "Successfully deleted cells"); + if (listener != null) { + listener.onComplete(true); + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + + } + }); + } + + private List subMenuCommandsForCells(List cells, boolean shouldHaveArtwork) { + List builtCommands = new ArrayList<>(); + for (MenuCell cell : cells) { + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); + } + } + return builtCommands; + } + + List allCommandsForCells(List cells, boolean shouldHaveArtwork) { + List builtCommands = new ArrayList<>(); + + for (int i = 0; i < cells.size(); i++) { + MenuCell cell = cells.get(i); + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + builtCommands.add(subMenuCommandForMenuCell(cell, shouldHaveArtwork, i)); + + // recursively grab the commands for all the sub cells + builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); + } else { + builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, i)); + } + } + return builtCommands; + } + + private List createCommandsForDynamicSubCells(List oldMenuCells, List cells, boolean shouldHaveArtwork) { + List builtCommands = new ArrayList<>(); + for (int z = 0; z < oldMenuCells.size(); z++) { + MenuCell oldCell = oldMenuCells.get(z); + for (int i = 0; i < cells.size(); i++) { + MenuCell cell = cells.get(i); + if (cell.equals(oldCell)) { + builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, z)); + break; + } + } + } + return builtCommands; + } + + private AddCommand commandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { + MenuParams params = new MenuParams(cell.getTitle()); + params.setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null); + params.setPosition(position); + + AddCommand command = new AddCommand(cell.getCellId()); + command.setMenuParams(params); + if (cell.getVoiceCommands() != null && !cell.getVoiceCommands().isEmpty()) { + command.setVrCommands(cell.getVoiceCommands()); + } else { + command.setVrCommands(null); + } + command.setCmdIcon((cell.getIcon() != null && shouldHaveArtwork) ? cell.getIcon().getImageRPC() : null); + + return command; + } + + private AddSubMenu subMenuCommandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { + AddSubMenu subMenu = new AddSubMenu(cell.getCellId(), cell.getTitle()); + subMenu.setPosition(position); + if (cell.getSubMenuLayout() != null) { + subMenu.setMenuLayout(cell.getSubMenuLayout()); + } else if (menuConfiguration != null && menuConfiguration.getSubMenuLayout() != null) { + subMenu.setMenuLayout(menuConfiguration.getSubMenuLayout()); + } + subMenu.setMenuIcon((shouldHaveArtwork && (cell.getIcon() != null && cell.getIcon().getImageRPC() != null)) ? cell.getIcon().getImageRPC() : null); + return subMenu; + } + + private void sendDynamicRootMenuRPCs(List deleteCommands, final List updatedCells, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + sendDeleteRPCs(deleteCommands, new CompletionListener() { + @Override + public void onComplete(boolean success) { + createAndSendMenuCellRPCs(updatedCells, new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (!success) { + DebugTool.logError(TAG, "Error Sending Current Menu"); + } + + listener.onComplete(success); + } + }); + } + }); + } + + private void runSubMenuCompareAlgorithm(CompletionListener listener) { + // any cells that were re-added have their sub-cells added with them + // at this point all we care about are the cells that were deemed equal and kept. + if (keepsNew == null || keepsNew.isEmpty()) { + listener.onComplete(true); + return; + } + + List commandLists = new ArrayList<>(); + + for (int i = 0; i < keepsNew.size(); i++) { + MenuCell newKeptCell = keepsNew.get(i); + MenuCell oldKeptCell = keepsOld.get(i); + + if (oldKeptCell.getSubCells() != null && !oldKeptCell.getSubCells().isEmpty() && newKeptCell.getSubCells() != null && !newKeptCell.getSubCells().isEmpty()) { + DynamicMenuUpdateRunScore subScore = compareOldAndNewLists(oldKeptCell.getSubCells(), newKeptCell.getSubCells()); + + if (subScore != null) { + DebugTool.logInfo(TAG, "Sub menu Run Score: " + oldKeptCell.getTitle() + " Score: " + subScore.getScore()); + SubCellCommandList commandList = new SubCellCommandList(oldKeptCell.getTitle(), oldKeptCell.getCellId(), subScore, oldKeptCell.getSubCells(), newKeptCell.getSubCells()); + commandLists.add(commandList); + } + } + } + createSubMenuDynamicCommands(commandLists, listener); + } + + private void createSubMenuDynamicCommands(final List commandLists, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + if (commandLists.isEmpty()) { + DebugTool.logInfo(TAG, "All menu updates, including sub menus - done."); + listener.onComplete(true); + return; + } + + final SubCellCommandList commandList = commandLists.remove(0); + + DebugTool.logInfo(TAG, "Creating and Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); + + // grab the scores + DynamicMenuUpdateRunScore score = commandList.getListsScore(); + List newIntArray = score.getCurrentMenu(); + List oldIntArray = score.getOldMenu(); + + // Grab the sub-menus from the parent cell + final List oldCells = commandList.getOldList(); + final List newCells = commandList.getNewList(); + + // Set up deletes + List deletes = new ArrayList<>(); + for (int x = 0; x < oldIntArray.size(); x++) { + Integer old = oldIntArray.get(x); + if (old.equals(MARKED_FOR_DELETION)) { + // grab cell to send to function to create delete commands + deletes.add(oldCells.get(x)); + } + } + // create the delete commands + List deleteCommands = createDeleteRPCsForCells(deletes); + + // Set up the adds + List adds = new ArrayList<>(); + List subCellKeepsNew = new ArrayList<>(); + for (int x = 0; x < newIntArray.size(); x++) { + Integer newInt = newIntArray.get(x); + if (newInt.equals(MARKED_FOR_ADDITION)) { + // grab cell to send to function to create add commands + adds.add(newCells.get(x)); + } else if (newInt.equals(KEEP)) { + subCellKeepsNew.add(newCells.get(x)); + } + } + final List addsWithNewIds = updateIdsOnDynamicSubCells(oldCells, adds, commandList.getParentId()); + // this is needed for the onCommands to still work + transferIdsToKeptSubCells(oldCells, subCellKeepsNew); + + sendDeleteRPCs(deleteCommands, new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (addsWithNewIds != null && !addsWithNewIds.isEmpty()) { + createAndSendDynamicSubMenuRPCs(newCells, addsWithNewIds, new CompletionListener() { + @Override + public void onComplete(boolean success) { + // recurse through next sub list + DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); + createSubMenuDynamicCommands(commandLists, listener); + } + }); + } else { + // no add commands to send, recurse through next sub list + DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); + createSubMenuDynamicCommands(commandLists, listener); + } + } + }); + } + + DynamicMenuUpdateRunScore runMenuCompareAlgorithm(List oldCells, List newCells) { + if (oldCells == null || oldCells.isEmpty()) { + return null; + } + + DynamicMenuUpdateRunScore bestScore = compareOldAndNewLists(oldCells, newCells); + DebugTool.logInfo(TAG, "Best menu run score: " + bestScore.getScore()); + + return bestScore; + } + + private DynamicMenuUpdateRunScore compareOldAndNewLists(List oldCells, List newCells) { + DynamicMenuUpdateRunScore bestRunScore = null; + + // This first loop is for each 'run' + for (int run = 0; run < oldCells.size(); run++) { + List oldArray = new ArrayList<>(oldCells.size()); + List newArray = new ArrayList<>(newCells.size()); + + // Set the statuses + for (int i = 0; i < oldCells.size(); i++) { + oldArray.add(MARKED_FOR_DELETION); + } + for (int i = 0; i < newCells.size(); i++) { + newArray.add(MARKED_FOR_ADDITION); + } + + int startIndex = 0; + + // Keep items that appear in both lists + for (int oldItems = run; oldItems < oldCells.size(); oldItems++) { + + for (int newItems = startIndex; newItems < newCells.size(); newItems++) { + + if (oldCells.get(oldItems).equals(newCells.get(newItems))) { + oldArray.set(oldItems, KEEP); + newArray.set(newItems, KEEP); + // set the new start index + startIndex = newItems + 1; + break; + } + } + } + + // Calculate number of adds, or the 'score' for this run + int numberOfAdds = 0; + for (int x = 0; x < newArray.size(); x++) { + if (newArray.get(x).equals(MARKED_FOR_ADDITION)) { + numberOfAdds++; + } + } + + // see if we have a new best score and set it if we do + if (bestRunScore == null || numberOfAdds < bestRunScore.getScore()) { + bestRunScore = new DynamicMenuUpdateRunScore(numberOfAdds, oldArray, newArray); + } + + } + return bestRunScore; + } + + private boolean shouldRPCsIncludeImages(List cells) { + for (MenuCell cell : cells) { + SdlArtwork artwork = cell.getIcon(); + if (artwork != null && !artwork.isStaticIcon() && fileManager.get() != null && !fileManager.get().hasUploadedFile(artwork)) { + return false; + } else if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + return shouldRPCsIncludeImages(cell.getSubCells()); + } + } + return true; + } + + private void updateIdsOnDynamicCells(List dynamicCells) { + if (menuCells != null && !menuCells.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { + for (int z = 0; z < menuCells.size(); z++) { + MenuCell mainCell = menuCells.get(z); + for (int i = 0; i < dynamicCells.size(); i++) { + MenuCell dynamicCell = dynamicCells.get(i); + if (mainCell.equals(dynamicCell)) { + int newId = ++lastMenuId; //todo won't the lastMenuId reset to 1 every time? + menuCells.get(z).setCellId(newId); + dynamicCells.get(i).setCellId(newId); + + if (mainCell.getSubCells() != null && !mainCell.getSubCells().isEmpty()) { + updateIdsOnMenuCells(mainCell.getSubCells(), mainCell.getCellId()); + } + break; + } + } + } + } + } + + private List updateIdsOnDynamicSubCells(List oldList, List dynamicCells, Integer parentId) { + if (oldList != null && !oldList.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { + for (int z = 0; z < oldList.size(); z++) { + MenuCell mainCell = oldList.get(z); + for (int i = 0; i < dynamicCells.size(); i++) { + MenuCell dynamicCell = dynamicCells.get(i); + if (mainCell.equals(dynamicCell)) { + int newId = ++lastMenuId; + oldList.get(z).setCellId(newId); + dynamicCells.get(i).setParentCellId(parentId); + dynamicCells.get(i).setCellId(newId); + } else { + int newId = ++lastMenuId; + dynamicCells.get(i).setParentCellId(parentId); + dynamicCells.get(i).setCellId(newId); + } + } + } + return dynamicCells; + } + return null; + } + + private void updateIdsOnMenuCells(List cells, int parentId) { + for (MenuCell cell : cells) { + int newId = ++lastMenuId; + cell.setCellId(newId); + cell.setParentCellId(parentId); + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); + } + } + } + + private void transferIdsToKeptCells(List keeps) { + for (int z = 0; z < oldMenuCells.size(); z++) { + MenuCell oldCell = oldMenuCells.get(z); + for (int i = 0; i < keeps.size(); i++) { + MenuCell keptCell = keeps.get(i); + if (oldCell.equals(keptCell)) { + keptCell.setCellId(oldCell.getCellId()); + break; + } + } + } + } + + private void transferIdsToKeptSubCells(List old, List keeps) { + for (int z = 0; z < old.size(); z++) { + MenuCell oldCell = old.get(z); + for (int i = 0; i < keeps.size(); i++) { + MenuCell keptCell = keeps.get(i); + if (oldCell.equals(keptCell)) { + keptCell.setCellId(oldCell.getCellId()); + break; + } + } + } + } + + private List createDeleteRPCsForCells(List cells) { + List deletes = new ArrayList<>(); + for (MenuCell cell : cells) { + if (cell.getSubCells() == null) { + DeleteCommand delete = new DeleteCommand(cell.getCellId()); + deletes.add(delete); + } else { + DeleteSubMenu delete = new DeleteSubMenu(cell.getCellId()); + deletes.add(delete); + } + } + return deletes; + } + + private List mainMenuCommandsForCells(List cellsToAdd, boolean shouldHaveArtwork) { + List builtCommands = new ArrayList<>(); + + // We need the index so we will use this type of loop + for (int z = 0; z < menuCells.size(); z++) { + MenuCell mainCell = menuCells.get(z); + for (int i = 0; i < cellsToAdd.size(); i++) { + MenuCell addCell = cellsToAdd.get(i); + if (mainCell.equals(addCell)) { + if (addCell.getSubCells() != null && !addCell.getSubCells().isEmpty()) { + builtCommands.add(subMenuCommandForMenuCell(addCell, shouldHaveArtwork, z)); + } else { + builtCommands.add(commandForMenuCell(addCell, shouldHaveArtwork, z)); + } + break; + } + } + } + return builtCommands; + } + + private List findAllArtworksToBeUploadedFromCells(List cells) { + // Make sure we can use images in the menus + if (!supportsImages()) { + return new ArrayList<>(); + } + + List artworks = new ArrayList<>(); + for (MenuCell cell : cells) { + if (fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getIcon())) { + artworks.add(cell.getIcon()); + } + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + artworks.addAll(findAllArtworksToBeUploadedFromCells(cell.getSubCells())); + } + } + + return artworks; + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + private boolean supportsImages() { + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.cmdIcon); + } + + void setMenuConfiguration(MenuConfiguration menuConfiguration) { + this.menuConfiguration = menuConfiguration; + } + + private void finishOperation(boolean success) { + if (operationCompletionListener != null) { + operationCompletionListener.onComplete(success, oldMenuCells); + } + onFinished(); + } +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/RunScore.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/RunScore.java deleted file mode 100644 index 6d031bb0e..000000000 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/RunScore.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2019 Livio, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following - * disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * Neither the name of the Livio Inc. nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package com.smartdevicelink.managers.screen.menu; - -import java.util.List; - -class RunScore { - - private int score; - private List oldMenu, currentMenu; - - RunScore(int score, List oldMenu, List currentMenu) { - setScore(score); - setOldMenu(oldMenu); - setCurrentMenu(currentMenu); - } - - private void setCurrentMenu(List currentMenu) { - this.currentMenu = currentMenu; - } - - List getCurrentMenu() { - return currentMenu; - } - - private void setOldMenu(List oldMenu) { - this.oldMenu = oldMenu; - } - - List getOldMenu() { - return oldMenu; - } - - private void setScore(int score) { - this.score = score; - } - - public int getScore() { - return score; - } - -} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/SubCellCommandList.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/SubCellCommandList.java index 604d52b09..c8567d5a8 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/SubCellCommandList.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/SubCellCommandList.java @@ -36,12 +36,12 @@ import java.util.List; class SubCellCommandList { - private RunScore listsScore; + private DynamicMenuUpdateRunScore listsScore; private String menuTitle; private Integer parentId; private List oldList, newList; - SubCellCommandList(String menuTitle, Integer parentId, RunScore listsScore, List oldList, List newList) { + SubCellCommandList(String menuTitle, Integer parentId, DynamicMenuUpdateRunScore listsScore, List oldList, List newList) { setMenuTitle(menuTitle); setParentId(parentId); setListsScore(listsScore); @@ -65,11 +65,11 @@ class SubCellCommandList { return menuTitle; } - private void setListsScore(RunScore listsScore) { + private void setListsScore(DynamicMenuUpdateRunScore listsScore) { this.listsScore = listsScore; } - RunScore getListsScore() { + DynamicMenuUpdateRunScore getListsScore() { return listsScore; } -- cgit v1.2.1 From 7057e356e70587f4708422633b85f8f56136b1f7 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 13:19:16 -0500 Subject: Refactor MenuConfigurationUpdateOperation --- .../screen/menu/DynamicMenuUpdateAlgorithm.java | 7 ++++++ .../menu/MenuConfigurationUpdateOperation.java | 25 +++++++++++++++++++--- .../screen/menu/MenuReplaceStaticOperation.java | 2 +- 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java new file mode 100644 index 000000000..c34b5e16a --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -0,0 +1,7 @@ +package com.smartdevicelink.managers.screen.menu; + +/** + * Created by Bilal Alsharifi on 1/25/21. + */ +class DynamicMenuUpdateAlgorithm { +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java index f2bada292..abd66d453 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java @@ -37,10 +37,13 @@ import com.smartdevicelink.managers.CompletionListener; import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.SetGlobalProperties; +import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener; import com.smartdevicelink.util.DebugTool; import java.lang.ref.WeakReference; +import java.util.List; /** * Created by Bilal Alsharifi on 1/21/21. @@ -48,12 +51,14 @@ import java.lang.ref.WeakReference; class MenuConfigurationUpdateOperation extends Task { private static final String TAG = "MenuConfigurationUpdateOperation"; private final WeakReference internalInterface; + private final List availableMenuLayouts; private final MenuConfiguration menuConfiguration; private final CompletionListener completionListener; - MenuConfigurationUpdateOperation(ISdl internalInterface, MenuConfiguration menuConfiguration, CompletionListener completionListener) { + MenuConfigurationUpdateOperation(ISdl internalInterface, WindowCapability defaultMainWindowCapability, MenuConfiguration menuConfiguration, CompletionListener completionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); + this.availableMenuLayouts = defaultMainWindowCapability != null ? defaultMainWindowCapability.getMenuLayoutsAvailable() : null; this.menuConfiguration = menuConfiguration; this.completionListener = completionListener; } @@ -68,6 +73,14 @@ class MenuConfigurationUpdateOperation extends Task { return; } + if (availableMenuLayouts == null) { + DebugTool.logWarning(TAG, "Could not set the main menu configuration. Which menu layouts can be used is not available"); + finishOperation(false); + } else if (!availableMenuLayouts.contains(menuConfiguration.getMenuLayout()) || !availableMenuLayouts.contains(menuConfiguration.getSubMenuLayout())) { + DebugTool.logError(TAG, String.format("One or more of the set menu layouts are not available on this system. The menu configuration will not be set. Available menu layouts: %s, set menu layouts: %s", availableMenuLayouts, menuConfiguration)); + finishOperation(false); + } + sendSetGlobalProperties(); } @@ -82,12 +95,18 @@ class MenuConfigurationUpdateOperation extends Task { } else { DebugTool.logError(TAG, "onError: " + response.getResultCode() + " | Info: " + response.getInfo()); } - completionListener.onComplete(response.getSuccess()); - onFinished(); + finishOperation(response.getSuccess()); } }); if (internalInterface.get() != null) { internalInterface.get().sendRPC(setGlobalProperties); } } + + private void finishOperation(boolean success) { + if (completionListener != null) { + completionListener.onComplete(success); + } + onFinished(); + } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 9a8c9730e..bcb7c2369 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -34,7 +34,7 @@ import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdN * Created by Bilal Alsharifi on 1/20/21. */ class MenuReplaceStaticOperation extends Task { - private static final String TAG = "MenuReplaceOperation"; + private static final String TAG = "MenuReplaceStaticOperation"; private static final int KEEP = 0; private static final int MARKED_FOR_ADDITION = 1; private static final int MARKED_FOR_DELETION = 2; -- cgit v1.2.1 From 18094f8fba627d09c8e30e89d0b03ba5bf80b65b Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 13:21:06 -0500 Subject: Rename menuCells to currnetMenuCells --- .../managers/screen/menu/BaseMenuManager.java | 71 +++++++++++----------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 10d84307e..1d7913709 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -69,7 +69,7 @@ abstract class BaseMenuManager extends BaseSubManager { static final int parentIdNotFound = MAX_ID; private final WeakReference fileManager; - private List menuCells; + private List currentMenuCells; private List oldMenuCells; private DynamicMenuUpdatesMode dynamicMenuUpdatesMode; private MenuConfiguration menuConfiguration; @@ -106,7 +106,7 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void dispose() { - menuCells = null; + currentMenuCells = null; oldMenuCells = null; currentHMILevel = HMILevel.HMI_NONE; currentSystemContext = SystemContext.SYSCTXT_MAIN; @@ -152,13 +152,13 @@ abstract class BaseMenuManager extends BaseSubManager { final List clonedCells = cloneMenuCellsList(cells); // Set old list - if (menuCells != null) { - oldMenuCells = new ArrayList<>(menuCells); + if (currentMenuCells != null) { + oldMenuCells = new ArrayList<>(currentMenuCells); } // Copy new list - menuCells = new ArrayList<>(); + currentMenuCells = new ArrayList<>(); if (clonedCells != null && !clonedCells.isEmpty()) { - menuCells.addAll(clonedCells); + currentMenuCells.addAll(clonedCells); } // HashSet order doesn't matter / doesn't allow duplicates @@ -166,7 +166,7 @@ abstract class BaseMenuManager extends BaseSubManager { HashSet allMenuVoiceCommands = new HashSet<>(); int voiceCommandCount = 0; - for (MenuCell cell : menuCells) { + for (MenuCell cell : currentMenuCells) { titleCheckSet.add(cell.getTitle()); if (cell.getVoiceCommands() != null) { allMenuVoiceCommands.addAll(cell.getVoiceCommands()); @@ -175,7 +175,7 @@ abstract class BaseMenuManager extends BaseSubManager { } // Check for duplicate titles - if (titleCheckSet.size() != menuCells.size()) { + if (titleCheckSet.size() != currentMenuCells.size()) { DebugTool.logError(TAG, "Not all cell titles are unique. The menu will not be set"); return; } @@ -186,9 +186,15 @@ abstract class BaseMenuManager extends BaseSubManager { return; } - cancelPendingMenuReplaceOperations(); + // Cancel pending MenuReplaceOperations + for (Task operation : transactionQueue.getTasksAsList()) { + if (!(operation instanceof MenuReplaceStaticOperation)) { + continue; + } + operation.cancelTask(); + } - MenuReplaceStaticOperation operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, oldMenuCells, menuCells, new MenuManagerCompletionListener() { + MenuReplaceStaticOperation operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, oldMenuCells, currentMenuCells, new MenuManagerCompletionListener() { @Override public void onComplete(boolean success, List oldMenuCells) { BaseMenuManager.this.oldMenuCells = oldMenuCells; @@ -203,7 +209,7 @@ abstract class BaseMenuManager extends BaseSubManager { * @return a List of Currently set menu cells */ public List getMenuCells() { - return menuCells; + return currentMenuCells; } /** @@ -232,13 +238,13 @@ abstract class BaseMenuManager extends BaseSubManager { return false; } - if (menuCells == null) { + if (currentMenuCells == null) { DebugTool.logError(TAG, "open sub menu called, but no Menu cells have been set"); return false; } // We must see if we have a copy of this cell, since we clone the objects - for (MenuCell clonedCell : menuCells) { + for (MenuCell clonedCell : currentMenuCells) { if (clonedCell.equals(cell) && clonedCell.getCellId() != MAX_ID) { // We've found the correct sub menu cell MenuShowOperation operation = new MenuShowOperation(internalInterface, clonedCell.getCellId()); @@ -273,14 +279,29 @@ abstract class BaseMenuManager extends BaseSubManager { return; } - MenuConfigurationUpdateOperation operation = new MenuConfigurationUpdateOperation(internalInterface, menuConfiguration, new CompletionListener() { + MenuConfigurationUpdateOperation operation = new MenuConfigurationUpdateOperation(internalInterface, defaultMainWindowCapability, menuConfiguration, new CompletionListener() { @Override public void onComplete(boolean success) { BaseMenuManager.this.menuConfiguration = menuConfiguration; - updatePendingOperationsWithNewMenuConfiguration(); + + // Update pending operations with new menuConfiguration + for (Task task : transactionQueue.getTasksAsList()) { + if (!(task instanceof MenuReplaceStaticOperation)) { + continue; + } + ((MenuReplaceStaticOperation) task).setMenuConfiguration(menuConfiguration); + } } }); + // Cancel previous menu configuration operations + for (Task task : transactionQueue.getTasksAsList()) { + if (!(task instanceof MenuConfigurationUpdateOperation)) { + continue; + } + task.cancelTask(); + } + transactionQueue.add(operation, false); } @@ -338,7 +359,7 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void onNotified(RPCNotification notification) { OnCommand onCommand = (OnCommand) notification; - callListenerForCells(menuCells, onCommand); + callListenerForCells(currentMenuCells, onCommand); } }; internalInterface.addOnRPCNotificationListener(FunctionID.ON_COMMAND, commandListener); @@ -395,22 +416,4 @@ abstract class BaseMenuManager extends BaseSubManager { return false; } - - private void updatePendingOperationsWithNewMenuConfiguration() { - for (Task task : transactionQueue.getTasksAsList()) { - if (!(task instanceof MenuReplaceStaticOperation)) { - continue; - } - ((MenuReplaceStaticOperation) task).setMenuConfiguration(menuConfiguration); - } - } - - private void cancelPendingMenuReplaceOperations(){ - for (Task operation : transactionQueue.getTasksAsList()) { - if (!(operation instanceof MenuReplaceStaticOperation)) { - continue; - } - operation.cancelTask(); - } - } } -- cgit v1.2.1 From 915ed7c64241f7ffb144bfcb285eb76a9d338ec3 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 13:24:51 -0500 Subject: Update var names --- .../managers/screen/menu/BaseMenuManager.java | 10 ++-- .../screen/menu/MenuReplaceStaticOperation.java | 56 +++++++++++----------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 1d7913709..a782bd89c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -70,7 +70,7 @@ abstract class BaseMenuManager extends BaseSubManager { private final WeakReference fileManager; private List currentMenuCells; - private List oldMenuCells; + private List menuCells; private DynamicMenuUpdatesMode dynamicMenuUpdatesMode; private MenuConfiguration menuConfiguration; private SdlMsgVersion sdlMsgVersion; @@ -107,7 +107,7 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void dispose() { currentMenuCells = null; - oldMenuCells = null; + menuCells = null; currentHMILevel = HMILevel.HMI_NONE; currentSystemContext = SystemContext.SYSCTXT_MAIN; dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; @@ -153,7 +153,7 @@ abstract class BaseMenuManager extends BaseSubManager { // Set old list if (currentMenuCells != null) { - oldMenuCells = new ArrayList<>(currentMenuCells); + menuCells = new ArrayList<>(currentMenuCells); } // Copy new list currentMenuCells = new ArrayList<>(); @@ -194,10 +194,10 @@ abstract class BaseMenuManager extends BaseSubManager { operation.cancelTask(); } - MenuReplaceStaticOperation operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, oldMenuCells, currentMenuCells, new MenuManagerCompletionListener() { + MenuReplaceStaticOperation operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, menuCells, currentMenuCells, new MenuManagerCompletionListener() { @Override public void onComplete(boolean success, List oldMenuCells) { - BaseMenuManager.this.oldMenuCells = oldMenuCells; + BaseMenuManager.this.menuCells = oldMenuCells; } }); transactionQueue.add(operation, false); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index bcb7c2369..13e8afae8 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -42,8 +42,8 @@ class MenuReplaceStaticOperation extends Task { private final WeakReference internalInterface; private final WeakReference fileManager; private final WindowCapability defaultMainWindowCapability; - private List oldMenuCells; - private final List menuCells; + private List currentMenu; + private final List updatedMenu; private List keepsOld; private List keepsNew; private final MenuManagerCompletionListener operationCompletionListener; @@ -52,7 +52,7 @@ class MenuReplaceStaticOperation extends Task { private int lastMenuId; private MenuConfiguration menuConfiguration; - MenuReplaceStaticOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, MenuManagerCompletionListener operationCompletionListener) { + MenuReplaceStaticOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); @@ -60,8 +60,8 @@ class MenuReplaceStaticOperation extends Task { this.dynamicMenuUpdatesMode = dynamicMenuUpdatesMode; this.menuConfiguration = menuConfiguration; this.defaultMainWindowCapability = defaultMainWindowCapability; - this.oldMenuCells = oldMenuCells; - this.menuCells = menuCells; + this.currentMenu = currentMenu; + this.updatedMenu = updatedMenu; this.operationCompletionListener = operationCompletionListener; } @@ -85,7 +85,7 @@ class MenuReplaceStaticOperation extends Task { private void updateMenuCells(final CompletionListener listener) { // Upload the Artworks - List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(menuCells); + List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu); if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { @Override @@ -114,16 +114,16 @@ class MenuReplaceStaticOperation extends Task { // determine how the menu will be updated. This has the ability to be changed during a session. if (checkUpdateMode(dynamicMenuUpdatesMode, displayType)) { // Run the lists through the new algorithm - DynamicMenuUpdateRunScore rootScore = runMenuCompareAlgorithm(oldMenuCells, menuCells); + DynamicMenuUpdateRunScore rootScore = runMenuCompareAlgorithm(currentMenu, updatedMenu); if (rootScore == null) { // Send initial menu without dynamic updates because oldMenuCells is null DebugTool.logInfo(TAG, "Creating initial Menu"); lastMenuId = menuCellIdMin; - updateIdsOnMenuCells(menuCells, parentIdNotFound); + updateIdsOnMenuCells(updatedMenu, parentIdNotFound); createAndSendEntireMenu(listener); } else { DebugTool.logInfo(TAG, "Dynamically Updating Menu"); - if (menuCells.isEmpty() && (oldMenuCells != null && !oldMenuCells.isEmpty())) { + if (updatedMenu.isEmpty() && (currentMenu != null && !currentMenu.isEmpty())) { // the dev wants to clear the menu. We have old cells and an empty array of new ones. deleteMenuWhenNewCellsEmpty(listener); } else { @@ -135,7 +135,7 @@ class MenuReplaceStaticOperation extends Task { // We are in compatibility mode. No need to run the algorithm DebugTool.logInfo(TAG, "Updating menus in compatibility mode"); lastMenuId = menuCellIdMin; - updateIdsOnMenuCells(menuCells, parentIdNotFound); + updateIdsOnMenuCells(updatedMenu, parentIdNotFound); createAndSendEntireMenu(listener); } } @@ -161,7 +161,7 @@ class MenuReplaceStaticOperation extends Task { return; } - sendDeleteRPCs(createDeleteRPCsForCells(oldMenuCells), new CompletionListener() { + sendDeleteRPCs(createDeleteRPCsForCells(currentMenu), new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { @@ -169,7 +169,7 @@ class MenuReplaceStaticOperation extends Task { } else { DebugTool.logInfo(TAG, "Successfully Cleared Menu"); } - oldMenuCells = null; + currentMenu = null; listener.onComplete(success); } }); @@ -188,9 +188,9 @@ class MenuReplaceStaticOperation extends Task { Integer old = oldIntArray.get(x); if (old.equals(MARKED_FOR_DELETION)) { // grab cell to send to function to create delete commands - deletes.add(oldMenuCells.get(x)); + deletes.add(currentMenu.get(x)); } else if (old.equals(KEEP)) { - keepsOld.add(oldMenuCells.get(x)); + keepsOld.add(currentMenu.get(x)); } } // create the delete commands @@ -203,9 +203,9 @@ class MenuReplaceStaticOperation extends Task { Integer newInt = newIntArray.get(x); if (newInt.equals(MARKED_FOR_ADDITION)) { // grab cell to send to function to create add commands - adds.add(menuCells.get(x)); + adds.add(updatedMenu.get(x)); } else if (newInt.equals(KEEP)) { - keepsNew.add(menuCells.get(x)); + keepsNew.add(updatedMenu.get(x)); } } updateIdsOnDynamicCells(adds); @@ -229,7 +229,7 @@ class MenuReplaceStaticOperation extends Task { deleteRootMenu(new CompletionListener() { @Override public void onComplete(boolean success) { - createAndSendMenuCellRPCs(menuCells, new CompletionListener() { + createAndSendMenuCellRPCs(updatedMenu, new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { @@ -396,14 +396,14 @@ class MenuReplaceStaticOperation extends Task { } private void deleteRootMenu(final CompletionListener listener) { - if (oldMenuCells == null || oldMenuCells.isEmpty()) { + if (currentMenu == null || currentMenu.isEmpty()) { if (listener != null) { // technically this method is successful if there's nothing to delete DebugTool.logInfo(TAG, "No old cells to delete, returning"); listener.onComplete(true); } } else { - sendDeleteRPCs(createDeleteRPCsForCells(oldMenuCells), listener); + sendDeleteRPCs(createDeleteRPCsForCells(currentMenu), listener); } } @@ -709,14 +709,14 @@ class MenuReplaceStaticOperation extends Task { } private void updateIdsOnDynamicCells(List dynamicCells) { - if (menuCells != null && !menuCells.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { - for (int z = 0; z < menuCells.size(); z++) { - MenuCell mainCell = menuCells.get(z); + if (updatedMenu != null && !updatedMenu.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { + for (int z = 0; z < updatedMenu.size(); z++) { + MenuCell mainCell = updatedMenu.get(z); for (int i = 0; i < dynamicCells.size(); i++) { MenuCell dynamicCell = dynamicCells.get(i); if (mainCell.equals(dynamicCell)) { int newId = ++lastMenuId; //todo won't the lastMenuId reset to 1 every time? - menuCells.get(z).setCellId(newId); + updatedMenu.get(z).setCellId(newId); dynamicCells.get(i).setCellId(newId); if (mainCell.getSubCells() != null && !mainCell.getSubCells().isEmpty()) { @@ -764,8 +764,8 @@ class MenuReplaceStaticOperation extends Task { } private void transferIdsToKeptCells(List keeps) { - for (int z = 0; z < oldMenuCells.size(); z++) { - MenuCell oldCell = oldMenuCells.get(z); + for (int z = 0; z < currentMenu.size(); z++) { + MenuCell oldCell = currentMenu.get(z); for (int i = 0; i < keeps.size(); i++) { MenuCell keptCell = keeps.get(i); if (oldCell.equals(keptCell)) { @@ -807,8 +807,8 @@ class MenuReplaceStaticOperation extends Task { List builtCommands = new ArrayList<>(); // We need the index so we will use this type of loop - for (int z = 0; z < menuCells.size(); z++) { - MenuCell mainCell = menuCells.get(z); + for (int z = 0; z < updatedMenu.size(); z++) { + MenuCell mainCell = updatedMenu.get(z); for (int i = 0; i < cellsToAdd.size(); i++) { MenuCell addCell = cellsToAdd.get(i); if (mainCell.equals(addCell)) { @@ -854,7 +854,7 @@ class MenuReplaceStaticOperation extends Task { private void finishOperation(boolean success) { if (operationCompletionListener != null) { - operationCompletionListener.onComplete(success, oldMenuCells); + operationCompletionListener.onComplete(success, currentMenu); } onFinished(); } -- cgit v1.2.1 From d774b215478d89f9e39e23bc8c9485023fc5bf62 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 13:43:39 -0500 Subject: Remove unnecessary checks --- .../managers/screen/menu/BaseMenuManager.java | 38 ++++++++++------------ .../screen/menu/MenuManagerCompletionListener.java | 2 +- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index a782bd89c..ede97e73f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -106,8 +106,8 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void dispose() { - currentMenuCells = null; menuCells = null; + currentMenuCells = null; currentHMILevel = HMILevel.HMI_NONE; currentSystemContext = SystemContext.SYSCTXT_MAIN; dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; @@ -148,25 +148,20 @@ abstract class BaseMenuManager extends BaseSubManager { * @param cells - the menu cells that are to be sent to the head unit, including their sub-cells. */ public void setMenuCells(@NonNull List cells) { + if (cells == null) { + DebugTool.logError(TAG, "cells list cannot be null!"); + return; + } + // Create a deep copy of the list so future changes by developers don't affect the algorithm logic final List clonedCells = cloneMenuCellsList(cells); - // Set old list - if (currentMenuCells != null) { - menuCells = new ArrayList<>(currentMenuCells); - } - // Copy new list - currentMenuCells = new ArrayList<>(); - if (clonedCells != null && !clonedCells.isEmpty()) { - currentMenuCells.addAll(clonedCells); - } - // HashSet order doesn't matter / doesn't allow duplicates HashSet titleCheckSet = new HashSet<>(); HashSet allMenuVoiceCommands = new HashSet<>(); int voiceCommandCount = 0; - for (MenuCell cell : currentMenuCells) { + for (MenuCell cell : clonedCells) { titleCheckSet.add(cell.getTitle()); if (cell.getVoiceCommands() != null) { allMenuVoiceCommands.addAll(cell.getVoiceCommands()); @@ -175,7 +170,7 @@ abstract class BaseMenuManager extends BaseSubManager { } // Check for duplicate titles - if (titleCheckSet.size() != currentMenuCells.size()) { + if (titleCheckSet.size() != clonedCells.size()) { DebugTool.logError(TAG, "Not all cell titles are unique. The menu will not be set"); return; } @@ -186,6 +181,9 @@ abstract class BaseMenuManager extends BaseSubManager { return; } + currentMenuCells = new ArrayList<>(menuCells); + menuCells = new ArrayList<>(clonedCells); + // Cancel pending MenuReplaceOperations for (Task operation : transactionQueue.getTasksAsList()) { if (!(operation instanceof MenuReplaceStaticOperation)) { @@ -194,10 +192,10 @@ abstract class BaseMenuManager extends BaseSubManager { operation.cancelTask(); } - MenuReplaceStaticOperation operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, menuCells, currentMenuCells, new MenuManagerCompletionListener() { + MenuReplaceStaticOperation operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, currentMenuCells, menuCells, new MenuManagerCompletionListener() { @Override - public void onComplete(boolean success, List oldMenuCells) { - BaseMenuManager.this.menuCells = oldMenuCells; + public void onComplete(boolean success, List currentMenuCells) { + BaseMenuManager.this.currentMenuCells = currentMenuCells; } }); transactionQueue.add(operation, false); @@ -209,7 +207,7 @@ abstract class BaseMenuManager extends BaseSubManager { * @return a List of Currently set menu cells */ public List getMenuCells() { - return currentMenuCells; + return menuCells; } /** @@ -238,13 +236,13 @@ abstract class BaseMenuManager extends BaseSubManager { return false; } - if (currentMenuCells == null) { + if (menuCells == null) { DebugTool.logError(TAG, "open sub menu called, but no Menu cells have been set"); return false; } // We must see if we have a copy of this cell, since we clone the objects - for (MenuCell clonedCell : currentMenuCells) { + for (MenuCell clonedCell : menuCells) { if (clonedCell.equals(cell) && clonedCell.getCellId() != MAX_ID) { // We've found the correct sub menu cell MenuShowOperation operation = new MenuShowOperation(internalInterface, clonedCell.getCellId()); @@ -359,7 +357,7 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void onNotified(RPCNotification notification) { OnCommand onCommand = (OnCommand) notification; - callListenerForCells(currentMenuCells, onCommand); + callListenerForCells(menuCells, onCommand); } }; internalInterface.addOnRPCNotificationListener(FunctionID.ON_COMMAND, commandListener); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuManagerCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuManagerCompletionListener.java index d49d0b5db..584fa90ca 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuManagerCompletionListener.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuManagerCompletionListener.java @@ -38,5 +38,5 @@ import java.util.List; * Created by Bilal Alsharifi on 1/21/21. */ interface MenuManagerCompletionListener { - void onComplete(boolean success, List oldMenuCells); + void onComplete(boolean success, List currentMenuCells); } -- cgit v1.2.1 From f3e3acd73ecaf2e5241aea1239c5d6363652aabc Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 14:20:32 -0500 Subject: Refactor MenuShowOperation --- .../managers/screen/menu/BaseMenuManager.java | 92 ++++++++++++---------- .../screen/menu/MenuReplaceStaticOperation.java | 4 +- .../managers/screen/menu/MenuReplaceUtilities.java | 7 ++ .../managers/screen/menu/MenuShowOperation.java | 9 ++- 4 files changed, 65 insertions(+), 47 deletions(-) create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index ede97e73f..a3e88787f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -59,6 +59,7 @@ import com.smartdevicelink.util.DebugTool; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -94,7 +95,8 @@ abstract class BaseMenuManager extends BaseSubManager { this.dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; this.sdlMsgVersion = internalInterface.getSdlMsgVersion(); this.transactionQueue = newTransactionQueue(); - + this.menuCells = new ArrayList<>(); + this.currentMenuCells = new ArrayList<>(); addListeners(); } @@ -106,8 +108,8 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void dispose() { - menuCells = null; - currentMenuCells = null; + menuCells = new ArrayList<>(); + currentMenuCells = new ArrayList<>(); currentHMILevel = HMILevel.HMI_NONE; currentSystemContext = SystemContext.SYSCTXT_MAIN; dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; @@ -152,7 +154,7 @@ abstract class BaseMenuManager extends BaseSubManager { DebugTool.logError(TAG, "cells list cannot be null!"); return; } - + // Create a deep copy of the list so future changes by developers don't affect the algorithm logic final List clonedCells = cloneMenuCellsList(cells); @@ -186,10 +188,9 @@ abstract class BaseMenuManager extends BaseSubManager { // Cancel pending MenuReplaceOperations for (Task operation : transactionQueue.getTasksAsList()) { - if (!(operation instanceof MenuReplaceStaticOperation)) { - continue; + if (operation instanceof MenuReplaceStaticOperation) { + operation.cancelTask(); } - operation.cancelTask(); } MenuReplaceStaticOperation operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, currentMenuCells, menuCells, new MenuManagerCompletionListener() { @@ -210,20 +211,51 @@ abstract class BaseMenuManager extends BaseSubManager { return menuCells; } - /** - * Opens the Main Menu - */ - public boolean openMenu() { - if (sdlMsgVersion.getMajorVersion() < 6) { - DebugTool.logWarning(TAG, "Menu opening is only supported on head units with RPC spec version 6.0.0 or later. Currently connected head unit RPC spec version is: " + sdlMsgVersion.getMajorVersion() + "." + sdlMsgVersion.getMinorVersion() + "." + sdlMsgVersion.getPatchVersion()); + private boolean openMenuPrivate(MenuCell cell){ + MenuCell foundClonedCell = null; + + if (cell != null) { + // We must see if we have a copy of this cell, since we clone the objects + for (MenuCell clonedCell : menuCells) { + if (clonedCell.equals(cell) && clonedCell.getCellId() != MAX_ID) { + // We've found the correct sub menu cell + foundClonedCell = clonedCell; + break; + } + } + } + + if (cell != null && (cell.getSubCells() == null || cell.getSubCells().isEmpty())) { + DebugTool.logError(DebugTool.TAG, String.format("The cell %s does not contain any sub cells, so no submenu can be opened", foundClonedCell.getTitle())); + return false; + } else if (cell != null && foundClonedCell == null) { + DebugTool.logError(DebugTool.TAG, "This cell has not been sent to the head unit, so no submenu can be opened. Make sure that the cell exists in the SDLManager.menu array"); + return false; + } else if (sdlMsgVersion.getMajorVersion() < 6) { + DebugTool.logWarning(TAG, "The openSubmenu method is not supported on this head unit."); return false; } - MenuShowOperation operation = new MenuShowOperation(internalInterface, null); + // Create the operation + MenuShowOperation operation = new MenuShowOperation(internalInterface, foundClonedCell); + + // Cancel previous open menu operations + for (Task task : transactionQueue.getTasksAsList()) { + if (task instanceof MenuShowOperation) { + task.cancelTask(); + } + } + transactionQueue.add(operation, false); return true; } + /** + * Opens the Main Menu + */ + public boolean openMenu() { + return openMenuPrivate(null); + } /** * Opens a subMenu. The cell you pass in must be constructed with {@link MenuCell(String,SdlArtwork,List)} @@ -231,27 +263,7 @@ abstract class BaseMenuManager extends BaseSubManager { * @param cell - A SubMenu cell whose sub menu you wish to open */ public boolean openSubMenu(@NonNull MenuCell cell) { - if (sdlMsgVersion.getMajorVersion() < 6) { - DebugTool.logWarning(TAG, "Sub menu opening is only supported on head units with RPC spec version 6.0.0 or later. Currently connected head unit RPC spec version is: " + sdlMsgVersion.getMajorVersion() + "." + sdlMsgVersion.getMinorVersion() + "." + sdlMsgVersion.getPatchVersion()); - return false; - } - - if (menuCells == null) { - DebugTool.logError(TAG, "open sub menu called, but no Menu cells have been set"); - return false; - } - - // We must see if we have a copy of this cell, since we clone the objects - for (MenuCell clonedCell : menuCells) { - if (clonedCell.equals(cell) && clonedCell.getCellId() != MAX_ID) { - // We've found the correct sub menu cell - MenuShowOperation operation = new MenuShowOperation(internalInterface, clonedCell.getCellId()); - transactionQueue.add(operation, false); - return true; - } - } - - return false; + return openMenuPrivate(cell); } @@ -284,20 +296,18 @@ abstract class BaseMenuManager extends BaseSubManager { // Update pending operations with new menuConfiguration for (Task task : transactionQueue.getTasksAsList()) { - if (!(task instanceof MenuReplaceStaticOperation)) { - continue; + if (task instanceof MenuReplaceStaticOperation) { + ((MenuReplaceStaticOperation) task).setMenuConfiguration(menuConfiguration); } - ((MenuReplaceStaticOperation) task).setMenuConfiguration(menuConfiguration); } } }); // Cancel previous menu configuration operations for (Task task : transactionQueue.getTasksAsList()) { - if (!(task instanceof MenuConfigurationUpdateOperation)) { - continue; + if (task instanceof MenuConfigurationUpdateOperation) { + task.cancelTask(); } - task.cancelTask(); } transactionQueue.add(operation, false); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 13e8afae8..6be5bcd3e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -112,7 +112,7 @@ class MenuReplaceStaticOperation extends Task { // Checks against what the developer set for update mode and against the display type to // determine how the menu will be updated. This has the ability to be changed during a session. - if (checkUpdateMode(dynamicMenuUpdatesMode, displayType)) { + if (isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType)) { // Run the lists through the new algorithm DynamicMenuUpdateRunScore rootScore = runMenuCompareAlgorithm(currentMenu, updatedMenu); if (rootScore == null) { @@ -140,7 +140,7 @@ class MenuReplaceStaticOperation extends Task { } } - private boolean checkUpdateMode(DynamicMenuUpdatesMode updateMode, String displayType) { + private boolean isDynamicMenuUpdateActive(DynamicMenuUpdatesMode updateMode, String displayType) { if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { if (displayType == null) { return true; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java new file mode 100644 index 000000000..d44ee0bde --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -0,0 +1,7 @@ +package com.smartdevicelink.managers.screen.menu; + +/** + * Created by Bilal Alsharifi on 1/25/21. + */ +class MenuReplaceUtilities { +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java index 86a42c79f..6c0c8519c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java @@ -47,12 +47,12 @@ import java.lang.ref.WeakReference; class MenuShowOperation extends Task { private static final String TAG = "MenuShowOperation"; private final WeakReference internalInterface; - private final Integer menuId; + private final MenuCell submenuCell; - MenuShowOperation(ISdl internalInterface, Integer menuId) { + MenuShowOperation(ISdl internalInterface, MenuCell menuCell) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); - this.menuId = menuId; + this.submenuCell = menuCell; } @Override @@ -65,7 +65,8 @@ class MenuShowOperation extends Task { return; } - sendShowAppMenu(this.menuId); + Integer menuId = submenuCell != null ? submenuCell.getCellId() : null; + sendShowAppMenu(menuId); } private void sendShowAppMenu(Integer id) { -- cgit v1.2.1 From 5a6a8710509549a300a3e46e89ba017699aaa6e7 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 14:28:44 -0500 Subject: Create DynamicMenuUpdateAlgorithm class --- .../screen/menu/DynamicMenuUpdateAlgorithm.java | 70 +++++++++++++++++++ .../screen/menu/MenuReplaceStaticOperation.java | 81 +++------------------- 2 files changed, 79 insertions(+), 72 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index c34b5e16a..5be323858 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -1,7 +1,77 @@ package com.smartdevicelink.managers.screen.menu; +import com.smartdevicelink.util.DebugTool; + +import java.util.ArrayList; +import java.util.List; + /** * Created by Bilal Alsharifi on 1/25/21. */ class DynamicMenuUpdateAlgorithm { + private static final String TAG = "DynamicMenuUpdateAlgorithm"; + static final int KEEP = 0; + static final int MARKED_FOR_ADDITION = 1; + static final int MARKED_FOR_DELETION = 2; + + static DynamicMenuUpdateRunScore runMenuCompareAlgorithm(List oldCells, List newCells) { + if (oldCells == null || oldCells.isEmpty()) { + return null; + } + + DynamicMenuUpdateRunScore bestScore = compareOldAndNewLists(oldCells, newCells); + DebugTool.logInfo(TAG, "Best menu run score: " + bestScore.getScore()); + + return bestScore; + } + + static DynamicMenuUpdateRunScore compareOldAndNewLists(List oldCells, List newCells) { + DynamicMenuUpdateRunScore bestRunScore = null; + + // This first loop is for each 'run' + for (int run = 0; run < oldCells.size(); run++) { + List oldArray = new ArrayList<>(oldCells.size()); + List newArray = new ArrayList<>(newCells.size()); + + // Set the statuses + for (int i = 0; i < oldCells.size(); i++) { + oldArray.add(MARKED_FOR_DELETION); + } + for (int i = 0; i < newCells.size(); i++) { + newArray.add(MARKED_FOR_ADDITION); + } + + int startIndex = 0; + + // Keep items that appear in both lists + for (int oldItems = run; oldItems < oldCells.size(); oldItems++) { + + for (int newItems = startIndex; newItems < newCells.size(); newItems++) { + + if (oldCells.get(oldItems).equals(newCells.get(newItems))) { + oldArray.set(oldItems, KEEP); + newArray.set(newItems, KEEP); + // set the new start index + startIndex = newItems + 1; + break; + } + } + } + + // Calculate number of adds, or the 'score' for this run + int numberOfAdds = 0; + for (int x = 0; x < newArray.size(); x++) { + if (newArray.get(x).equals(MARKED_FOR_ADDITION)) { + numberOfAdds++; + } + } + + // see if we have a new best score and set it if we do + if (bestRunScore == null || numberOfAdds < bestRunScore.getScore()) { + bestRunScore = new DynamicMenuUpdateRunScore(numberOfAdds, oldArray, newArray); + } + + } + return bestRunScore; + } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 6be5bcd3e..a2389369a 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -35,9 +35,6 @@ import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdN */ class MenuReplaceStaticOperation extends Task { private static final String TAG = "MenuReplaceStaticOperation"; - private static final int KEEP = 0; - private static final int MARKED_FOR_ADDITION = 1; - private static final int MARKED_FOR_DELETION = 2; private final WeakReference internalInterface; private final WeakReference fileManager; @@ -114,7 +111,7 @@ class MenuReplaceStaticOperation extends Task { // determine how the menu will be updated. This has the ability to be changed during a session. if (isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType)) { // Run the lists through the new algorithm - DynamicMenuUpdateRunScore rootScore = runMenuCompareAlgorithm(currentMenu, updatedMenu); + DynamicMenuUpdateRunScore rootScore = DynamicMenuUpdateAlgorithm.runMenuCompareAlgorithm(currentMenu, updatedMenu); if (rootScore == null) { // Send initial menu without dynamic updates because oldMenuCells is null DebugTool.logInfo(TAG, "Creating initial Menu"); @@ -186,10 +183,10 @@ class MenuReplaceStaticOperation extends Task { keepsOld = new ArrayList<>(); for (int x = 0; x < oldIntArray.size(); x++) { Integer old = oldIntArray.get(x); - if (old.equals(MARKED_FOR_DELETION)) { + if (old.equals(DynamicMenuUpdateAlgorithm.MARKED_FOR_DELETION)) { // grab cell to send to function to create delete commands deletes.add(currentMenu.get(x)); - } else if (old.equals(KEEP)) { + } else if (old.equals(DynamicMenuUpdateAlgorithm.KEEP)) { keepsOld.add(currentMenu.get(x)); } } @@ -201,10 +198,10 @@ class MenuReplaceStaticOperation extends Task { keepsNew = new ArrayList<>(); for (int x = 0; x < newIntArray.size(); x++) { Integer newInt = newIntArray.get(x); - if (newInt.equals(MARKED_FOR_ADDITION)) { + if (newInt.equals(DynamicMenuUpdateAlgorithm.MARKED_FOR_ADDITION)) { // grab cell to send to function to create add commands adds.add(updatedMenu.get(x)); - } else if (newInt.equals(KEEP)) { + } else if (newInt.equals(DynamicMenuUpdateAlgorithm.KEEP)) { keepsNew.add(updatedMenu.get(x)); } } @@ -550,7 +547,7 @@ class MenuReplaceStaticOperation extends Task { MenuCell oldKeptCell = keepsOld.get(i); if (oldKeptCell.getSubCells() != null && !oldKeptCell.getSubCells().isEmpty() && newKeptCell.getSubCells() != null && !newKeptCell.getSubCells().isEmpty()) { - DynamicMenuUpdateRunScore subScore = compareOldAndNewLists(oldKeptCell.getSubCells(), newKeptCell.getSubCells()); + DynamicMenuUpdateRunScore subScore = DynamicMenuUpdateAlgorithm.compareOldAndNewLists(oldKeptCell.getSubCells(), newKeptCell.getSubCells()); if (subScore != null) { DebugTool.logInfo(TAG, "Sub menu Run Score: " + oldKeptCell.getTitle() + " Score: " + subScore.getScore()); @@ -590,7 +587,7 @@ class MenuReplaceStaticOperation extends Task { List deletes = new ArrayList<>(); for (int x = 0; x < oldIntArray.size(); x++) { Integer old = oldIntArray.get(x); - if (old.equals(MARKED_FOR_DELETION)) { + if (old.equals(DynamicMenuUpdateAlgorithm.MARKED_FOR_DELETION)) { // grab cell to send to function to create delete commands deletes.add(oldCells.get(x)); } @@ -603,10 +600,10 @@ class MenuReplaceStaticOperation extends Task { List subCellKeepsNew = new ArrayList<>(); for (int x = 0; x < newIntArray.size(); x++) { Integer newInt = newIntArray.get(x); - if (newInt.equals(MARKED_FOR_ADDITION)) { + if (newInt.equals(DynamicMenuUpdateAlgorithm.MARKED_FOR_ADDITION)) { // grab cell to send to function to create add commands adds.add(newCells.get(x)); - } else if (newInt.equals(KEEP)) { + } else if (newInt.equals(DynamicMenuUpdateAlgorithm.KEEP)) { subCellKeepsNew.add(newCells.get(x)); } } @@ -635,66 +632,6 @@ class MenuReplaceStaticOperation extends Task { }); } - DynamicMenuUpdateRunScore runMenuCompareAlgorithm(List oldCells, List newCells) { - if (oldCells == null || oldCells.isEmpty()) { - return null; - } - - DynamicMenuUpdateRunScore bestScore = compareOldAndNewLists(oldCells, newCells); - DebugTool.logInfo(TAG, "Best menu run score: " + bestScore.getScore()); - - return bestScore; - } - - private DynamicMenuUpdateRunScore compareOldAndNewLists(List oldCells, List newCells) { - DynamicMenuUpdateRunScore bestRunScore = null; - - // This first loop is for each 'run' - for (int run = 0; run < oldCells.size(); run++) { - List oldArray = new ArrayList<>(oldCells.size()); - List newArray = new ArrayList<>(newCells.size()); - - // Set the statuses - for (int i = 0; i < oldCells.size(); i++) { - oldArray.add(MARKED_FOR_DELETION); - } - for (int i = 0; i < newCells.size(); i++) { - newArray.add(MARKED_FOR_ADDITION); - } - - int startIndex = 0; - - // Keep items that appear in both lists - for (int oldItems = run; oldItems < oldCells.size(); oldItems++) { - - for (int newItems = startIndex; newItems < newCells.size(); newItems++) { - - if (oldCells.get(oldItems).equals(newCells.get(newItems))) { - oldArray.set(oldItems, KEEP); - newArray.set(newItems, KEEP); - // set the new start index - startIndex = newItems + 1; - break; - } - } - } - - // Calculate number of adds, or the 'score' for this run - int numberOfAdds = 0; - for (int x = 0; x < newArray.size(); x++) { - if (newArray.get(x).equals(MARKED_FOR_ADDITION)) { - numberOfAdds++; - } - } - - // see if we have a new best score and set it if we do - if (bestRunScore == null || numberOfAdds < bestRunScore.getScore()) { - bestRunScore = new DynamicMenuUpdateRunScore(numberOfAdds, oldArray, newArray); - } - - } - return bestRunScore; - } private boolean shouldRPCsIncludeImages(List cells) { for (MenuCell cell : cells) { -- cgit v1.2.1 From 1a6c325dfe0e2cf839faef75912b5c9dfd1de55a Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 15:24:19 -0500 Subject: Move lastMenuId to BaseMenuManager --- .../com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 3 +++ .../managers/screen/menu/MenuReplaceStaticOperation.java | 7 ++----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index a3e88787f..1af3f41cf 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -85,10 +85,12 @@ abstract class BaseMenuManager extends BaseSubManager { private OnSystemCapabilityListener onDisplaysCapabilityListener; private WindowCapability defaultMainWindowCapability; private Queue transactionQueue; + static int lastMenuId; // todo this shouldn't be static and should be fully managed in the manager BaseMenuManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) { super(internalInterface); + this.lastMenuId = menuCellIdMin; this.fileManager = new WeakReference<>(fileManager); this.currentSystemContext = SystemContext.SYSCTXT_MAIN; this.currentHMILevel = HMILevel.HMI_NONE; @@ -108,6 +110,7 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void dispose() { + lastMenuId = menuCellIdMin; menuCells = new ArrayList<>(); currentMenuCells = new ArrayList<>(); currentHMILevel = HMILevel.HMI_NONE; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index a2389369a..3186c74e1 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -27,7 +27,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.lastMenuId; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; /** @@ -46,7 +46,6 @@ class MenuReplaceStaticOperation extends Task { private final MenuManagerCompletionListener operationCompletionListener; private final String displayType; private final DynamicMenuUpdatesMode dynamicMenuUpdatesMode; - private int lastMenuId; private MenuConfiguration menuConfiguration; MenuReplaceStaticOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { @@ -115,7 +114,6 @@ class MenuReplaceStaticOperation extends Task { if (rootScore == null) { // Send initial menu without dynamic updates because oldMenuCells is null DebugTool.logInfo(TAG, "Creating initial Menu"); - lastMenuId = menuCellIdMin; updateIdsOnMenuCells(updatedMenu, parentIdNotFound); createAndSendEntireMenu(listener); } else { @@ -131,7 +129,6 @@ class MenuReplaceStaticOperation extends Task { } else { // We are in compatibility mode. No need to run the algorithm DebugTool.logInfo(TAG, "Updating menus in compatibility mode"); - lastMenuId = menuCellIdMin; updateIdsOnMenuCells(updatedMenu, parentIdNotFound); createAndSendEntireMenu(listener); } @@ -652,7 +649,7 @@ class MenuReplaceStaticOperation extends Task { for (int i = 0; i < dynamicCells.size(); i++) { MenuCell dynamicCell = dynamicCells.get(i); if (mainCell.equals(dynamicCell)) { - int newId = ++lastMenuId; //todo won't the lastMenuId reset to 1 every time? + int newId = ++lastMenuId; updatedMenu.get(z).setCellId(newId); dynamicCells.get(i).setCellId(newId); -- cgit v1.2.1 From 4306f0d80fe14569d6b47322e148d693839c14df Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 16:41:06 -0500 Subject: Move some methods to MenuReplaceUtilities --- .../screen/menu/MenuReplaceStaticOperation.java | 161 ++------------------- .../managers/screen/menu/MenuReplaceUtilities.java | 149 +++++++++++++++++++ 2 files changed, 164 insertions(+), 146 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 3186c74e1..d5ea4cc2f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -3,20 +3,14 @@ package com.smartdevicelink.managers.screen.menu; import com.livio.taskmaster.Task; 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.proxy.RPCRequest; import com.smartdevicelink.proxy.RPCResponse; -import com.smartdevicelink.proxy.rpc.AddCommand; -import com.smartdevicelink.proxy.rpc.AddSubMenu; -import com.smartdevicelink.proxy.rpc.DeleteCommand; -import com.smartdevicelink.proxy.rpc.DeleteSubMenu; -import com.smartdevicelink.proxy.rpc.MenuParams; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.DisplayType; -import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; +import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.util.DebugTool; @@ -29,6 +23,7 @@ import java.util.Map; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.lastMenuId; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.*; /** * Created by Bilal Alsharifi on 1/20/21. @@ -81,7 +76,7 @@ class MenuReplaceStaticOperation extends Task { private void updateMenuCells(final CompletionListener listener) { // Upload the Artworks - List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu); + List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), defaultMainWindowCapability); if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { @Override @@ -155,7 +150,7 @@ class MenuReplaceStaticOperation extends Task { return; } - sendDeleteRPCs(createDeleteRPCsForCells(currentMenu), new CompletionListener() { + sendDeleteRPCs(deleteCommandsForCells(currentMenu), new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { @@ -188,7 +183,7 @@ class MenuReplaceStaticOperation extends Task { } } // create the delete commands - deleteCommands = createDeleteRPCsForCells(deletes); + deleteCommands = deleteCommandsForCells(deletes); // Set up the adds List adds = new ArrayList<>(); @@ -252,14 +247,16 @@ class MenuReplaceStaticOperation extends Task { List mainMenuCommands; final List subMenuCommands; + List availableMenuLayouts = defaultMainWindowCapability != null ? defaultMainWindowCapability.getMenuLayoutsAvailable() : null; + MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; - if (!shouldRPCsIncludeImages(menu) || !supportsImages()) { + if (!shouldRPCsIncludeImages(menu, fileManager.get()) || !supportsImages(defaultMainWindowCapability)) { // Send artwork-less menu - mainMenuCommands = mainMenuCommandsForCells(menu, false); - subMenuCommands = subMenuCommandsForCells(menu, false); + mainMenuCommands = mainMenuCommandsForCells(menu, false, updatedMenu, availableMenuLayouts, defaultSubmenuLayout); + subMenuCommands = subMenuCommandsForCells(menu, false, availableMenuLayouts, defaultSubmenuLayout); } else { - mainMenuCommands = mainMenuCommandsForCells(menu, true); - subMenuCommands = subMenuCommandsForCells(menu, true); + mainMenuCommands = mainMenuCommandsForCells(menu, true, updatedMenu, availableMenuLayouts, defaultSubmenuLayout); + subMenuCommands = subMenuCommandsForCells(menu, true, availableMenuLayouts, defaultSubmenuLayout); } internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { @@ -354,7 +351,7 @@ class MenuReplaceStaticOperation extends Task { List mainMenuCommands; - if (!shouldRPCsIncludeImages(adds) || !supportsImages()) { + if (!shouldRPCsIncludeImages(adds, fileManager.get()) || !supportsImages(defaultMainWindowCapability)) { // Send artwork-less menu mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, false); } else { @@ -397,7 +394,7 @@ class MenuReplaceStaticOperation extends Task { listener.onComplete(true); } } else { - sendDeleteRPCs(createDeleteRPCsForCells(currentMenu), listener); + sendDeleteRPCs(deleteCommandsForCells(currentMenu), listener); } } @@ -436,33 +433,6 @@ class MenuReplaceStaticOperation extends Task { }); } - private List subMenuCommandsForCells(List cells, boolean shouldHaveArtwork) { - List builtCommands = new ArrayList<>(); - for (MenuCell cell : cells) { - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); - } - } - return builtCommands; - } - - List allCommandsForCells(List cells, boolean shouldHaveArtwork) { - List builtCommands = new ArrayList<>(); - - for (int i = 0; i < cells.size(); i++) { - MenuCell cell = cells.get(i); - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - builtCommands.add(subMenuCommandForMenuCell(cell, shouldHaveArtwork, i)); - - // recursively grab the commands for all the sub cells - builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork)); - } else { - builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, i)); - } - } - return builtCommands; - } - private List createCommandsForDynamicSubCells(List oldMenuCells, List cells, boolean shouldHaveArtwork) { List builtCommands = new ArrayList<>(); for (int z = 0; z < oldMenuCells.size(); z++) { @@ -478,35 +448,6 @@ class MenuReplaceStaticOperation extends Task { return builtCommands; } - private AddCommand commandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { - MenuParams params = new MenuParams(cell.getTitle()); - params.setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null); - params.setPosition(position); - - AddCommand command = new AddCommand(cell.getCellId()); - command.setMenuParams(params); - if (cell.getVoiceCommands() != null && !cell.getVoiceCommands().isEmpty()) { - command.setVrCommands(cell.getVoiceCommands()); - } else { - command.setVrCommands(null); - } - command.setCmdIcon((cell.getIcon() != null && shouldHaveArtwork) ? cell.getIcon().getImageRPC() : null); - - return command; - } - - private AddSubMenu subMenuCommandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { - AddSubMenu subMenu = new AddSubMenu(cell.getCellId(), cell.getTitle()); - subMenu.setPosition(position); - if (cell.getSubMenuLayout() != null) { - subMenu.setMenuLayout(cell.getSubMenuLayout()); - } else if (menuConfiguration != null && menuConfiguration.getSubMenuLayout() != null) { - subMenu.setMenuLayout(menuConfiguration.getSubMenuLayout()); - } - subMenu.setMenuIcon((shouldHaveArtwork && (cell.getIcon() != null && cell.getIcon().getImageRPC() != null)) ? cell.getIcon().getImageRPC() : null); - return subMenu; - } - private void sendDynamicRootMenuRPCs(List deleteCommands, final List updatedCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; @@ -590,7 +531,7 @@ class MenuReplaceStaticOperation extends Task { } } // create the delete commands - List deleteCommands = createDeleteRPCsForCells(deletes); + List deleteCommands = deleteCommandsForCells(deletes); // Set up the adds List adds = new ArrayList<>(); @@ -629,19 +570,6 @@ class MenuReplaceStaticOperation extends Task { }); } - - private boolean shouldRPCsIncludeImages(List cells) { - for (MenuCell cell : cells) { - SdlArtwork artwork = cell.getIcon(); - if (artwork != null && !artwork.isStaticIcon() && fileManager.get() != null && !fileManager.get().hasUploadedFile(artwork)) { - return false; - } else if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - return shouldRPCsIncludeImages(cell.getSubCells()); - } - } - return true; - } - private void updateIdsOnDynamicCells(List dynamicCells) { if (updatedMenu != null && !updatedMenu.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { for (int z = 0; z < updatedMenu.size(); z++) { @@ -723,65 +651,6 @@ class MenuReplaceStaticOperation extends Task { } } - private List createDeleteRPCsForCells(List cells) { - List deletes = new ArrayList<>(); - for (MenuCell cell : cells) { - if (cell.getSubCells() == null) { - DeleteCommand delete = new DeleteCommand(cell.getCellId()); - deletes.add(delete); - } else { - DeleteSubMenu delete = new DeleteSubMenu(cell.getCellId()); - deletes.add(delete); - } - } - return deletes; - } - - private List mainMenuCommandsForCells(List cellsToAdd, boolean shouldHaveArtwork) { - List builtCommands = new ArrayList<>(); - - // We need the index so we will use this type of loop - for (int z = 0; z < updatedMenu.size(); z++) { - MenuCell mainCell = updatedMenu.get(z); - for (int i = 0; i < cellsToAdd.size(); i++) { - MenuCell addCell = cellsToAdd.get(i); - if (mainCell.equals(addCell)) { - if (addCell.getSubCells() != null && !addCell.getSubCells().isEmpty()) { - builtCommands.add(subMenuCommandForMenuCell(addCell, shouldHaveArtwork, z)); - } else { - builtCommands.add(commandForMenuCell(addCell, shouldHaveArtwork, z)); - } - break; - } - } - } - return builtCommands; - } - - private List findAllArtworksToBeUploadedFromCells(List cells) { - // Make sure we can use images in the menus - if (!supportsImages()) { - return new ArrayList<>(); - } - - List artworks = new ArrayList<>(); - for (MenuCell cell : cells) { - if (fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getIcon())) { - artworks.add(cell.getIcon()); - } - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - artworks.addAll(findAllArtworksToBeUploadedFromCells(cell.getSubCells())); - } - } - - return artworks; - } - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - private boolean supportsImages() { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.cmdIcon); - } - void setMenuConfiguration(MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index d44ee0bde..379ac546f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -1,7 +1,156 @@ package com.smartdevicelink.managers.screen.menu; +import com.smartdevicelink.managers.ManagerUtility; +import com.smartdevicelink.managers.file.FileManager; +import com.smartdevicelink.managers.file.filetypes.SdlArtwork; +import com.smartdevicelink.proxy.RPCRequest; +import com.smartdevicelink.proxy.rpc.AddCommand; +import com.smartdevicelink.proxy.rpc.AddSubMenu; +import com.smartdevicelink.proxy.rpc.DeleteCommand; +import com.smartdevicelink.proxy.rpc.DeleteSubMenu; +import com.smartdevicelink.proxy.rpc.Image; +import com.smartdevicelink.proxy.rpc.MenuParams; +import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; +import com.smartdevicelink.proxy.rpc.enums.MenuLayout; + +import java.util.ArrayList; +import java.util.List; + +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; + /** * Created by Bilal Alsharifi on 1/25/21. */ class MenuReplaceUtilities { + static List deleteCommandsForCells(List cells) { + List deletes = new ArrayList<>(); + for (MenuCell cell : cells) { + if (cell.getSubCells() == null) { + DeleteCommand delete = new DeleteCommand(cell.getCellId()); + deletes.add(delete); + } else { + DeleteSubMenu delete = new DeleteSubMenu(cell.getCellId()); + deletes.add(delete); + } + } + return deletes; + } + + static List findAllArtworksToBeUploadedFromCells(List cells, FileManager fileManager, WindowCapability windowCapability) { + // Make sure we can use images in the menus + if (!supportsImages(windowCapability)) { + return new ArrayList<>(); + } + + List artworks = new ArrayList<>(); + for (MenuCell cell : cells) { + if (fileManager != null && fileManager.fileNeedsUpload(cell.getIcon())) { + artworks.add(cell.getIcon()); + } + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + artworks.addAll(findAllArtworksToBeUploadedFromCells(cell.getSubCells(), fileManager, windowCapability)); + } + } + + return artworks; + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + static boolean supportsImages(WindowCapability windowCapability) { + return windowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); + } + + static boolean shouldRPCsIncludeImages(List cells, FileManager fileManager) { + for (MenuCell cell : cells) { + SdlArtwork artwork = cell.getIcon(); + if (artwork != null && !artwork.isStaticIcon() && fileManager != null && !fileManager.hasUploadedFile(artwork)) { + return false; + } else if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + return shouldRPCsIncludeImages(cell.getSubCells(), fileManager); + } + } + return true; + } + + static List mainMenuCommandsForCells(List cellsToAdd, boolean shouldHaveArtwork, List updatedMenu, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { + List builtCommands = new ArrayList<>(); + + // We need the index so we will use this type of loop + for (int z = 0; z < updatedMenu.size(); z++) { + MenuCell mainCell = updatedMenu.get(z); + for (int i = 0; i < cellsToAdd.size(); i++) { + MenuCell addCell = cellsToAdd.get(i); + if (mainCell.equals(addCell)) { + if (addCell.getSubCells() != null && !addCell.getSubCells().isEmpty()) { + builtCommands.add(subMenuCommandForMenuCell(addCell, shouldHaveArtwork, z, availableMenuLayouts, defaultSubmenuLayout)); + } else { + builtCommands.add(commandForMenuCell(addCell, shouldHaveArtwork, z)); + } + break; + } + } + } + return builtCommands; + } + + static List subMenuCommandsForCells(List cells, boolean shouldHaveArtwork, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { + List builtCommands = new ArrayList<>(); + for (MenuCell cell : cells) { + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork, availableMenuLayouts, defaultSubmenuLayout)); + } + } + return builtCommands; + } + + static List allCommandsForCells(List cells, boolean shouldHaveArtwork, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { + List builtCommands = new ArrayList<>(); + + for (int i = 0; i < cells.size(); i++) { + MenuCell cell = cells.get(i); + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + builtCommands.add(subMenuCommandForMenuCell(cell, shouldHaveArtwork, i, availableMenuLayouts, defaultSubmenuLayout)); + + // recursively grab the commands for all the sub cells + builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork, availableMenuLayouts, defaultSubmenuLayout)); + } else { + builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, i)); + } + } + return builtCommands; + } + + static AddCommand commandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { + MenuParams params = new MenuParams(cell.getTitle()); + params.setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null); + params.setPosition(position); + + AddCommand command = new AddCommand(cell.getCellId()); + command.setMenuParams(params); + if (cell.getVoiceCommands() != null && !cell.getVoiceCommands().isEmpty()) { + command.setVrCommands(cell.getVoiceCommands()); + } else { + command.setVrCommands(null); + } + command.setCmdIcon((cell.getIcon() != null && shouldHaveArtwork) ? cell.getIcon().getImageRPC() : null); + + return command; + } + + static AddSubMenu subMenuCommandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { + Image icon = (shouldHaveArtwork && (cell.getIcon() != null && cell.getIcon().getImageRPC() != null)) ? cell.getIcon().getImageRPC() : null; + + MenuLayout submenuLayout; + if (cell.getSubMenuLayout() != null && availableMenuLayouts != null && availableMenuLayouts.contains(cell.getSubMenuLayout())) { + submenuLayout = cell.getSubMenuLayout(); + } else { + submenuLayout = defaultSubmenuLayout; + } + + return new AddSubMenu(cell.getCellId(), cell.getTitle()) + .setPosition(position) + .setMenuLayout(submenuLayout) + .setMenuIcon(icon); + } } -- cgit v1.2.1 From 6a161408fde35d6206d045c32b4d3366ee31152b Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 17:15:43 -0500 Subject: Add MenuCellState enum --- .../screen/menu/DynamicMenuUpdateAlgorithm.java | 49 +++++++++++++--------- .../screen/menu/DynamicMenuUpdateRunScore.java | 16 +++---- .../screen/menu/MenuReplaceStaticOperation.java | 43 +++++++++---------- 3 files changed, 59 insertions(+), 49 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index 5be323858..298f56e52 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -10,47 +10,56 @@ import java.util.List; */ class DynamicMenuUpdateAlgorithm { private static final String TAG = "DynamicMenuUpdateAlgorithm"; - static final int KEEP = 0; - static final int MARKED_FOR_ADDITION = 1; - static final int MARKED_FOR_DELETION = 2; - static DynamicMenuUpdateRunScore runMenuCompareAlgorithm(List oldCells, List newCells) { - if (oldCells == null || oldCells.isEmpty()) { + // Cell state that tells the menu manager what it should do with a given SDLMenuCell + enum MenuCellState { + // Marks the cell to be deleted + DELETE, + + // Marks the cell to be added + ADD, + + // Marks the cell to be kept + KEEP + } + + static DynamicMenuUpdateRunScore compareOldMenuCells(List oldMenuCells, List updatedMenuCells) { + if (oldMenuCells == null || oldMenuCells.isEmpty()) { return null; } - DynamicMenuUpdateRunScore bestScore = compareOldAndNewLists(oldCells, newCells); + DynamicMenuUpdateRunScore bestScore = startCompareAtRun(oldMenuCells, updatedMenuCells); DebugTool.logInfo(TAG, "Best menu run score: " + bestScore.getScore()); return bestScore; } - static DynamicMenuUpdateRunScore compareOldAndNewLists(List oldCells, List newCells) { + static DynamicMenuUpdateRunScore startCompareAtRun(List oldMenuCells, List updatedMenuCells) { DynamicMenuUpdateRunScore bestRunScore = null; // This first loop is for each 'run' - for (int run = 0; run < oldCells.size(); run++) { - List oldArray = new ArrayList<>(oldCells.size()); - List newArray = new ArrayList<>(newCells.size()); + for (int run = 0; run < oldMenuCells.size(); run++) { + List oldArray = new ArrayList<>(oldMenuCells.size()); + List newArray = new ArrayList<>(updatedMenuCells.size()); // Set the statuses - for (int i = 0; i < oldCells.size(); i++) { - oldArray.add(MARKED_FOR_DELETION); + for (int i = 0; i < oldMenuCells.size(); i++) { + oldArray.add(MenuCellState.DELETE); } - for (int i = 0; i < newCells.size(); i++) { - newArray.add(MARKED_FOR_ADDITION); + for (int i = 0; i < updatedMenuCells.size(); i++) { + newArray.add(MenuCellState.KEEP); } int startIndex = 0; // Keep items that appear in both lists - for (int oldItems = run; oldItems < oldCells.size(); oldItems++) { + for (int oldItems = run; oldItems < oldMenuCells.size(); oldItems++) { - for (int newItems = startIndex; newItems < newCells.size(); newItems++) { + for (int newItems = startIndex; newItems < updatedMenuCells.size(); newItems++) { - if (oldCells.get(oldItems).equals(newCells.get(newItems))) { - oldArray.set(oldItems, KEEP); - newArray.set(newItems, KEEP); + if (oldMenuCells.get(oldItems).equals(updatedMenuCells.get(newItems))) { + oldArray.set(oldItems, MenuCellState.KEEP); + newArray.set(newItems, MenuCellState.KEEP); // set the new start index startIndex = newItems + 1; break; @@ -61,7 +70,7 @@ class DynamicMenuUpdateAlgorithm { // Calculate number of adds, or the 'score' for this run int numberOfAdds = 0; for (int x = 0; x < newArray.size(); x++) { - if (newArray.get(x).equals(MARKED_FOR_ADDITION)) { + if (newArray.get(x).equals(MenuCellState.ADD)) { numberOfAdds++; } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java index f821b81cb..20533b52c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java @@ -33,31 +33,32 @@ package com.smartdevicelink.managers.screen.menu; import java.util.List; +import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; class DynamicMenuUpdateRunScore { - private int score; - private List oldMenu, currentMenu; + private List oldMenu, currentMenu; + - DynamicMenuUpdateRunScore(int score, List oldMenu, List currentMenu) { + DynamicMenuUpdateRunScore(int score, List oldMenu, List currentMenu) { setScore(score); setOldMenu(oldMenu); setCurrentMenu(currentMenu); } - private void setCurrentMenu(List currentMenu) { + private void setCurrentMenu(List currentMenu) { this.currentMenu = currentMenu; } - List getCurrentMenu() { + List getCurrentMenu() { return currentMenu; } - private void setOldMenu(List oldMenu) { + private void setOldMenu(List oldMenu) { this.oldMenu = oldMenu; } - List getOldMenu() { + List getOldMenu() { return oldMenu; } @@ -68,5 +69,4 @@ class DynamicMenuUpdateRunScore { public int getScore() { return score; } - } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index d5ea4cc2f..41f60f70c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -1,6 +1,7 @@ package com.smartdevicelink.managers.screen.menu; import com.livio.taskmaster.Task; +import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; import com.smartdevicelink.managers.CompletionListener; import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.managers.file.FileManager; @@ -105,7 +106,7 @@ class MenuReplaceStaticOperation extends Task { // determine how the menu will be updated. This has the ability to be changed during a session. if (isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType)) { // Run the lists through the new algorithm - DynamicMenuUpdateRunScore rootScore = DynamicMenuUpdateAlgorithm.runMenuCompareAlgorithm(currentMenu, updatedMenu); + DynamicMenuUpdateRunScore rootScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); if (rootScore == null) { // Send initial menu without dynamic updates because oldMenuCells is null DebugTool.logInfo(TAG, "Creating initial Menu"); @@ -166,19 +167,19 @@ class MenuReplaceStaticOperation extends Task { private void dynamicallyUpdateRootMenu(DynamicMenuUpdateRunScore bestRootScore, CompletionListener listener) { // We need to run through the keeps and see if they have subCells, as they also need to be run through the compare function. - List newIntArray = bestRootScore.getCurrentMenu(); - List oldIntArray = bestRootScore.getOldMenu(); + List newStatesArray = bestRootScore.getCurrentMenu(); + List oldStatesArray = bestRootScore.getOldMenu(); List deleteCommands; // Set up deletes List deletes = new ArrayList<>(); keepsOld = new ArrayList<>(); - for (int x = 0; x < oldIntArray.size(); x++) { - Integer old = oldIntArray.get(x); - if (old.equals(DynamicMenuUpdateAlgorithm.MARKED_FOR_DELETION)) { + for (int x = 0; x < oldStatesArray.size(); x++) { + MenuCellState old = oldStatesArray.get(x); + if (old.equals(MenuCellState.DELETE)) { // grab cell to send to function to create delete commands deletes.add(currentMenu.get(x)); - } else if (old.equals(DynamicMenuUpdateAlgorithm.KEEP)) { + } else if (old.equals(MenuCellState.KEEP)) { keepsOld.add(currentMenu.get(x)); } } @@ -188,12 +189,12 @@ class MenuReplaceStaticOperation extends Task { // Set up the adds List adds = new ArrayList<>(); keepsNew = new ArrayList<>(); - for (int x = 0; x < newIntArray.size(); x++) { - Integer newInt = newIntArray.get(x); - if (newInt.equals(DynamicMenuUpdateAlgorithm.MARKED_FOR_ADDITION)) { + for (int x = 0; x < newStatesArray.size(); x++) { + MenuCellState newState = newStatesArray.get(x); + if (newState.equals(MenuCellState.ADD)) { // grab cell to send to function to create add commands adds.add(updatedMenu.get(x)); - } else if (newInt.equals(DynamicMenuUpdateAlgorithm.KEEP)) { + } else if (newState.equals(MenuCellState.KEEP)) { keepsNew.add(updatedMenu.get(x)); } } @@ -485,7 +486,7 @@ class MenuReplaceStaticOperation extends Task { MenuCell oldKeptCell = keepsOld.get(i); if (oldKeptCell.getSubCells() != null && !oldKeptCell.getSubCells().isEmpty() && newKeptCell.getSubCells() != null && !newKeptCell.getSubCells().isEmpty()) { - DynamicMenuUpdateRunScore subScore = DynamicMenuUpdateAlgorithm.compareOldAndNewLists(oldKeptCell.getSubCells(), newKeptCell.getSubCells()); + DynamicMenuUpdateRunScore subScore = DynamicMenuUpdateAlgorithm.startCompareAtRun(oldKeptCell.getSubCells(), newKeptCell.getSubCells()); if (subScore != null) { DebugTool.logInfo(TAG, "Sub menu Run Score: " + oldKeptCell.getTitle() + " Score: " + subScore.getScore()); @@ -514,8 +515,8 @@ class MenuReplaceStaticOperation extends Task { // grab the scores DynamicMenuUpdateRunScore score = commandList.getListsScore(); - List newIntArray = score.getCurrentMenu(); - List oldIntArray = score.getOldMenu(); + List newStates = score.getCurrentMenu(); + List oldStates = score.getOldMenu(); // Grab the sub-menus from the parent cell final List oldCells = commandList.getOldList(); @@ -523,9 +524,9 @@ class MenuReplaceStaticOperation extends Task { // Set up deletes List deletes = new ArrayList<>(); - for (int x = 0; x < oldIntArray.size(); x++) { - Integer old = oldIntArray.get(x); - if (old.equals(DynamicMenuUpdateAlgorithm.MARKED_FOR_DELETION)) { + for (int x = 0; x < oldStates.size(); x++) { + MenuCellState old = oldStates.get(x); + if (old.equals(MenuCellState.DELETE)) { // grab cell to send to function to create delete commands deletes.add(oldCells.get(x)); } @@ -536,12 +537,12 @@ class MenuReplaceStaticOperation extends Task { // Set up the adds List adds = new ArrayList<>(); List subCellKeepsNew = new ArrayList<>(); - for (int x = 0; x < newIntArray.size(); x++) { - Integer newInt = newIntArray.get(x); - if (newInt.equals(DynamicMenuUpdateAlgorithm.MARKED_FOR_ADDITION)) { + for (int x = 0; x < newStates.size(); x++) { + MenuCellState newState = newStates.get(x); + if (newState.equals(MenuCellState.ADD)) { // grab cell to send to function to create add commands adds.add(newCells.get(x)); - } else if (newInt.equals(DynamicMenuUpdateAlgorithm.KEEP)) { + } else if (newState.equals(MenuCellState.KEEP)) { subCellKeepsNew.add(newCells.get(x)); } } -- cgit v1.2.1 From 6748d9c4a606e3c7857480aeee0630a1c9b76783 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 25 Jan 2021 18:00:02 -0500 Subject: Fix issue in DynamicMenuUpdateAlgorithm --- .../managers/screen/menu/DynamicMenuUpdateAlgorithm.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index 298f56e52..1c76e3cc0 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -47,7 +47,7 @@ class DynamicMenuUpdateAlgorithm { oldArray.add(MenuCellState.DELETE); } for (int i = 0; i < updatedMenuCells.size(); i++) { - newArray.add(MenuCellState.KEEP); + newArray.add(MenuCellState.ADD); } int startIndex = 0; -- cgit v1.2.1 From f4def8de02ed71616b63831f66c0845560311464 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 26 Jan 2021 11:58:29 -0500 Subject: Update canceling pending operations --- .../managers/screen/menu/BaseMenuManager.java | 36 ++++++++++++++++++++-- .../screen/menu/MenuReplaceDynamicOperation.java | 3 ++ .../screen/menu/MenuReplaceStaticOperation.java | 17 +--------- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 1af3f41cf..12182eb86 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -50,6 +50,7 @@ import com.smartdevicelink.proxy.rpc.OnCommand; import com.smartdevicelink.proxy.rpc.OnHMIStatus; import com.smartdevicelink.proxy.rpc.SdlMsgVersion; import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.DisplayType; import com.smartdevicelink.proxy.rpc.enums.HMILevel; import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows; import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType; @@ -191,17 +192,27 @@ abstract class BaseMenuManager extends BaseSubManager { // Cancel pending MenuReplaceOperations for (Task operation : transactionQueue.getTasksAsList()) { - if (operation instanceof MenuReplaceStaticOperation) { + if (operation instanceof MenuReplaceStaticOperation || operation instanceof MenuReplaceDynamicOperation) { operation.cancelTask(); } } - MenuReplaceStaticOperation operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, currentMenuCells, menuCells, new MenuManagerCompletionListener() { + // Checks against what the developer set for update mode and against the display type to + // determine how the menu will be updated. This has the ability to be changed during a session. + Task operation = null; + MenuManagerCompletionListener menuManagerCompletionListener = new MenuManagerCompletionListener() { @Override public void onComplete(boolean success, List currentMenuCells) { BaseMenuManager.this.currentMenuCells = currentMenuCells; } - }); + }; + + if (isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType)) { + operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, currentMenuCells, menuCells, menuManagerCompletionListener); + } else { + operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, currentMenuCells, menuCells, menuManagerCompletionListener); + } + transactionQueue.add(operation, false); } @@ -301,6 +312,8 @@ abstract class BaseMenuManager extends BaseSubManager { for (Task task : transactionQueue.getTasksAsList()) { if (task instanceof MenuReplaceStaticOperation) { ((MenuReplaceStaticOperation) task).setMenuConfiguration(menuConfiguration); + } else if (task instanceof MenuReplaceDynamicOperation) { + ((MenuReplaceDynamicOperation) task).setMenuConfiguration(menuConfiguration); } } } @@ -427,4 +440,21 @@ abstract class BaseMenuManager extends BaseSubManager { return false; } + + // todo change to private + static boolean isDynamicMenuUpdateActive(DynamicMenuUpdatesMode updateMode, String displayType) { + if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { + if (displayType == null) { + return true; + } + return (!displayType.equals(DisplayType.GEN3_8_INCH.toString())); + + } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_OFF)) { + return false; + } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_ON)) { + return true; + } + + return true; + } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 78c1144ba..7c8b93497 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -88,4 +88,7 @@ class MenuReplaceDynamicOperation extends Task { } onFinished(); } + + public void setMenuConfiguration(MenuConfiguration menuConfiguration) { + } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 41f60f70c..5c12672dc 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.isDynamicMenuUpdateActive; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.lastMenuId; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.*; @@ -130,22 +131,6 @@ class MenuReplaceStaticOperation extends Task { } } - private boolean isDynamicMenuUpdateActive(DynamicMenuUpdatesMode updateMode, String displayType) { - if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { - if (displayType == null) { - return true; - } - return (!displayType.equals(DisplayType.GEN3_8_INCH.toString())); - - } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_OFF)) { - return false; - } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_ON)) { - return true; - } - - return true; - } - private void deleteMenuWhenNewCellsEmpty(final CompletionListener listener) { if (getState() == Task.CANCELED) { return; -- cgit v1.2.1 From de7c2a4189838998a9408f26adf7dee67b78a8cc Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 26 Jan 2021 13:14:21 -0500 Subject: Add initial code to MenuReplaceDynamicOperation --- .../managers/screen/menu/BaseMenuManager.java | 4 +- .../screen/menu/MenuReplaceDynamicOperation.java | 593 ++++++++++++++++++++- .../screen/menu/MenuReplaceStaticOperation.java | 390 +------------- 3 files changed, 587 insertions(+), 400 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 12182eb86..27a2af5cb 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -208,9 +208,9 @@ abstract class BaseMenuManager extends BaseSubManager { }; if (isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType)) { - operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, currentMenuCells, menuCells, menuManagerCompletionListener); + operation = new MenuReplaceDynamicOperation(internalInterface, fileManager.get(), menuConfiguration, defaultMainWindowCapability, currentMenuCells, menuCells, menuManagerCompletionListener); } else { - operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), displayType, dynamicMenuUpdatesMode, menuConfiguration, defaultMainWindowCapability, currentMenuCells, menuCells, menuManagerCompletionListener); + operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), menuConfiguration, defaultMainWindowCapability, currentMenuCells, menuCells, menuManagerCompletionListener); } transactionQueue.add(operation, false); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 7c8b93497..56d8deaf9 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -3,20 +3,14 @@ package com.smartdevicelink.managers.screen.menu; import com.livio.taskmaster.Task; 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.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; import com.smartdevicelink.proxy.RPCRequest; import com.smartdevicelink.proxy.RPCResponse; -import com.smartdevicelink.proxy.rpc.AddCommand; -import com.smartdevicelink.proxy.rpc.AddSubMenu; -import com.smartdevicelink.proxy.rpc.DeleteCommand; -import com.smartdevicelink.proxy.rpc.DeleteSubMenu; -import com.smartdevicelink.proxy.rpc.MenuParams; import com.smartdevicelink.proxy.rpc.WindowCapability; -import com.smartdevicelink.proxy.rpc.enums.DisplayType; -import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; +import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.util.DebugTool; @@ -27,8 +21,15 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.lastMenuId; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandForMenuCell; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.shouldRPCsIncludeImages; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.supportsImages; /** * Created by Bilal Alsharifi on 1/20/21. @@ -39,24 +40,21 @@ class MenuReplaceDynamicOperation extends Task { private final WeakReference internalInterface; private final WeakReference fileManager; private final WindowCapability defaultMainWindowCapability; - private List oldMenuCells; - private final List menuCells; + private List currentMenu; + private final List updatedMenu; + private List keepsOld; + private List keepsNew; private final MenuManagerCompletionListener operationCompletionListener; - private final String displayType; - private final DynamicMenuUpdatesMode dynamicMenuUpdatesMode; - private int lastMenuId; private MenuConfiguration menuConfiguration; - MenuReplaceDynamicOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List oldMenuCells, List menuCells, MenuManagerCompletionListener operationCompletionListener) { + MenuReplaceDynamicOperation(ISdl internalInterface, FileManager fileManager, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); - this.displayType = displayType; - this.dynamicMenuUpdatesMode = dynamicMenuUpdatesMode; this.menuConfiguration = menuConfiguration; this.defaultMainWindowCapability = defaultMainWindowCapability; - this.oldMenuCells = oldMenuCells; - this.menuCells = menuCells; + this.currentMenu = currentMenu; + this.updatedMenu = updatedMenu; this.operationCompletionListener = operationCompletionListener; } @@ -79,16 +77,565 @@ class MenuReplaceDynamicOperation extends Task { } private void updateMenuCells(final CompletionListener listener) { + // Upload the Artworks + List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), defaultMainWindowCapability); + if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { + fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { + @Override + public void onComplete(Map errors) { + if (errors != null && !errors.isEmpty()) { + DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); + } else { + DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); + } + // proceed + updateMenuAndDetermineBestUpdateMethod(listener); + } + }); + } else { + // No Artworks to be uploaded, send off + updateMenuAndDetermineBestUpdateMethod(listener); + } + } + + private void updateMenuAndDetermineBestUpdateMethod(CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + // Run the lists through the new algorithm + DynamicMenuUpdateRunScore rootScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); + if (rootScore == null) { + // Send initial menu without dynamic updates because oldMenuCells is null + DebugTool.logInfo(TAG, "Creating initial Menu"); + updateIdsOnMenuCells(updatedMenu, parentIdNotFound); + createAndSendEntireMenu(listener); + } else { + DebugTool.logInfo(TAG, "Dynamically Updating Menu"); + if (updatedMenu.isEmpty() && (currentMenu != null && !currentMenu.isEmpty())) { + // the dev wants to clear the menu. We have old cells and an empty array of new ones. + deleteMenuWhenNewCellsEmpty(listener); + } else { + // lets dynamically update the root menu + dynamicallyUpdateRootMenu(rootScore, listener); + } + } + } + + private void deleteMenuWhenNewCellsEmpty(final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + sendDeleteRPCs(deleteCommandsForCells(currentMenu), new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (!success) { + DebugTool.logError(TAG, "Error Sending Current Menu"); + } else { + DebugTool.logInfo(TAG, "Successfully Cleared Menu"); + } + currentMenu = null; + listener.onComplete(success); + } + }); + } + + private void dynamicallyUpdateRootMenu(DynamicMenuUpdateRunScore bestRootScore, CompletionListener listener) { + // We need to run through the keeps and see if they have subCells, as they also need to be run through the compare function. + List newStatesArray = bestRootScore.getCurrentMenu(); + List oldStatesArray = bestRootScore.getOldMenu(); + List deleteCommands; + + // Set up deletes + List deletes = new ArrayList<>(); + keepsOld = new ArrayList<>(); + for (int x = 0; x < oldStatesArray.size(); x++) { + MenuCellState old = oldStatesArray.get(x); + if (old.equals(MenuCellState.DELETE)) { + // grab cell to send to function to create delete commands + deletes.add(currentMenu.get(x)); + } else if (old.equals(MenuCellState.KEEP)) { + keepsOld.add(currentMenu.get(x)); + } + } + // create the delete commands + deleteCommands = deleteCommandsForCells(deletes); + + // Set up the adds + List adds = new ArrayList<>(); + keepsNew = new ArrayList<>(); + for (int x = 0; x < newStatesArray.size(); x++) { + MenuCellState newState = newStatesArray.get(x); + if (newState.equals(MenuCellState.ADD)) { + // grab cell to send to function to create add commands + adds.add(updatedMenu.get(x)); + } else if (newState.equals(MenuCellState.KEEP)) { + keepsNew.add(updatedMenu.get(x)); + } + } + updateIdsOnDynamicCells(adds); + // this is needed for the onCommands to still work + transferIdsToKeptCells(keepsNew); + + if (!adds.isEmpty()) { // todo what if adds was empty but deletes was not? + DebugTool.logInfo(TAG, "Sending root menu updates"); + sendDynamicRootMenuRPCs(deleteCommands, adds, listener); + } else { + DebugTool.logInfo(TAG, "All root menu items are kept. Check the sub menus"); + runSubMenuCompareAlgorithm(listener); + } + } + + private void createAndSendEntireMenu(final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + deleteRootMenu(new CompletionListener() { + @Override + public void onComplete(boolean success) { + createAndSendMenuCellRPCs(updatedMenu, new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (!success) { + DebugTool.logError(TAG, "Error Sending Current Menu"); + } + + listener.onComplete(success); + } + }); + } + }); + } + + private void createAndSendMenuCellRPCs(final List menu, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + if (menu == null || menu.isEmpty()) { + if (listener != null) { + // This can be considered a success if the user was clearing out their menu + listener.onComplete(true); + } + return; + } + + List mainMenuCommands; + final List subMenuCommands; + List availableMenuLayouts = defaultMainWindowCapability != null ? defaultMainWindowCapability.getMenuLayoutsAvailable() : null; + MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; + if (!shouldRPCsIncludeImages(menu, fileManager.get()) || !supportsImages(defaultMainWindowCapability)) { + // Send artwork-less menu + mainMenuCommands = mainMenuCommandsForCells(menu, false, updatedMenu, availableMenuLayouts, defaultSubmenuLayout); + subMenuCommands = subMenuCommandsForCells(menu, false, availableMenuLayouts, defaultSubmenuLayout); + } else { + mainMenuCommands = mainMenuCommandsForCells(menu, true, updatedMenu, availableMenuLayouts, defaultSubmenuLayout); + subMenuCommands = subMenuCommandsForCells(menu, true, availableMenuLayouts, defaultSubmenuLayout); + } + + internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + } + + @Override + public void onFinished() { + if (!subMenuCommands.isEmpty()) { + DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); + sendSubMenuCommandRPCs(subMenuCommands, listener); + } else { + if (keepsNew != null && !keepsNew.isEmpty()) { + runSubMenuCompareAlgorithm(listener); + } else { + DebugTool.logInfo(TAG, "Finished sending main menu commands."); + + if (listener != null) { + listener.onComplete(true); + } + } + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + try { + DebugTool.logInfo(TAG, "Main Menu response: " + response.serializeJSON().toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); + } + } + }); + } + + private void sendSubMenuCommandRPCs(List commands, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + internalInterface.get().sendSequentialRPCs(commands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + } + + @Override + public void onFinished() { + if (keepsNew != null && !keepsNew.isEmpty()) { + runSubMenuCompareAlgorithm(listener); + } else { + DebugTool.logInfo(TAG, "Finished Updating Menu"); + + if (listener != null) { + listener.onComplete(true); + } + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + try { + DebugTool.logInfo(TAG, "Sub Menu response: " + response.serializeJSON().toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + DebugTool.logError(TAG, "Failed to send sub menu commands: " + response.getInfo()); + } + } + }); + } + + private void createAndSendDynamicSubMenuRPCs(List newMenu, final List adds, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + if (adds.isEmpty()) { + if (listener != null) { + // This can be considered a success if the user was clearing out their menu + DebugTool.logError(TAG, "Called createAndSendDynamicSubMenuRPCs with empty menu"); + listener.onComplete(true); + } + return; + } + + List mainMenuCommands; + + if (!shouldRPCsIncludeImages(adds, fileManager.get()) || !supportsImages(defaultMainWindowCapability)) { + // Send artwork-less menu + mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, false); + } else { + mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, true); + } + + internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + // nothing here + } + + @Override + public void onFinished() { + if (listener != null) { + listener.onComplete(true); + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + try { + DebugTool.logInfo(TAG, "Dynamic Sub Menu response: " + response.serializeJSON().toString()); + } catch (JSONException e) { + e.printStackTrace(); + } + } else { + DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); + } + } + }); + } + + private void deleteRootMenu(final CompletionListener listener) { + if (currentMenu == null || currentMenu.isEmpty()) { + if (listener != null) { + // technically this method is successful if there's nothing to delete + DebugTool.logInfo(TAG, "No old cells to delete, returning"); + listener.onComplete(true); + } + } else { + sendDeleteRPCs(deleteCommandsForCells(currentMenu), listener); + } + } + + private void sendDeleteRPCs(List deleteCommands, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + if (deleteCommands == null || deleteCommands.isEmpty()) { + // no dynamic deletes required. return + if (listener != null) { + // technically this method is successful if there's nothing to delete + listener.onComplete(true); + } + return; + } + + internalInterface.get().sendRPCs(deleteCommands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + + } + + @Override + public void onFinished() { + DebugTool.logInfo(TAG, "Successfully deleted cells"); + if (listener != null) { + listener.onComplete(true); + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + + } + }); + } + + private List createCommandsForDynamicSubCells(List oldMenuCells, List cells, boolean shouldHaveArtwork) { + List builtCommands = new ArrayList<>(); + for (int z = 0; z < oldMenuCells.size(); z++) { + MenuCell oldCell = oldMenuCells.get(z); + for (int i = 0; i < cells.size(); i++) { + MenuCell cell = cells.get(i); + if (cell.equals(oldCell)) { + builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, z)); + break; + } + } + } + return builtCommands; + } + + private void sendDynamicRootMenuRPCs(List deleteCommands, final List updatedCells, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + sendDeleteRPCs(deleteCommands, new CompletionListener() { + @Override + public void onComplete(boolean success) { + createAndSendMenuCellRPCs(updatedCells, new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (!success) { + DebugTool.logError(TAG, "Error Sending Current Menu"); + } + + listener.onComplete(success); + } + }); + } + }); + } + + private void runSubMenuCompareAlgorithm(CompletionListener listener) { + // any cells that were re-added have their sub-cells added with them + // at this point all we care about are the cells that were deemed equal and kept. + if (keepsNew == null || keepsNew.isEmpty()) { + listener.onComplete(true); + return; + } + + List commandLists = new ArrayList<>(); + + for (int i = 0; i < keepsNew.size(); i++) { + MenuCell newKeptCell = keepsNew.get(i); + MenuCell oldKeptCell = keepsOld.get(i); + + if (oldKeptCell.getSubCells() != null && !oldKeptCell.getSubCells().isEmpty() && newKeptCell.getSubCells() != null && !newKeptCell.getSubCells().isEmpty()) { + DynamicMenuUpdateRunScore subScore = DynamicMenuUpdateAlgorithm.startCompareAtRun(oldKeptCell.getSubCells(), newKeptCell.getSubCells()); + + if (subScore != null) { + DebugTool.logInfo(TAG, "Sub menu Run Score: " + oldKeptCell.getTitle() + " Score: " + subScore.getScore()); + SubCellCommandList commandList = new SubCellCommandList(oldKeptCell.getTitle(), oldKeptCell.getCellId(), subScore, oldKeptCell.getSubCells(), newKeptCell.getSubCells()); + commandLists.add(commandList); + } + } + } + createSubMenuDynamicCommands(commandLists, listener); + } + + private void createSubMenuDynamicCommands(final List commandLists, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + if (commandLists.isEmpty()) { + DebugTool.logInfo(TAG, "All menu updates, including sub menus - done."); + listener.onComplete(true); + return; + } + + final SubCellCommandList commandList = commandLists.remove(0); + + DebugTool.logInfo(TAG, "Creating and Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); + + // grab the scores + DynamicMenuUpdateRunScore score = commandList.getListsScore(); + List newStates = score.getCurrentMenu(); + List oldStates = score.getOldMenu(); + + // Grab the sub-menus from the parent cell + final List oldCells = commandList.getOldList(); + final List newCells = commandList.getNewList(); + + // Set up deletes + List deletes = new ArrayList<>(); + for (int x = 0; x < oldStates.size(); x++) { + MenuCellState old = oldStates.get(x); + if (old.equals(MenuCellState.DELETE)) { + // grab cell to send to function to create delete commands + deletes.add(oldCells.get(x)); + } + } + // create the delete commands + List deleteCommands = deleteCommandsForCells(deletes); + + // Set up the adds + List adds = new ArrayList<>(); + List subCellKeepsNew = new ArrayList<>(); + for (int x = 0; x < newStates.size(); x++) { + MenuCellState newState = newStates.get(x); + if (newState.equals(MenuCellState.ADD)) { + // grab cell to send to function to create add commands + adds.add(newCells.get(x)); + } else if (newState.equals(MenuCellState.KEEP)) { + subCellKeepsNew.add(newCells.get(x)); + } + } + final List addsWithNewIds = updateIdsOnDynamicSubCells(oldCells, adds, commandList.getParentId()); + // this is needed for the onCommands to still work + transferIdsToKeptSubCells(oldCells, subCellKeepsNew); + + sendDeleteRPCs(deleteCommands, new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (addsWithNewIds != null && !addsWithNewIds.isEmpty()) { + createAndSendDynamicSubMenuRPCs(newCells, addsWithNewIds, new CompletionListener() { + @Override + public void onComplete(boolean success) { + // recurse through next sub list + DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); + createSubMenuDynamicCommands(commandLists, listener); + } + }); + } else { + // no add commands to send, recurse through next sub list + DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); + createSubMenuDynamicCommands(commandLists, listener); + } + } + }); + } + + private void updateIdsOnDynamicCells(List dynamicCells) { + if (updatedMenu != null && !updatedMenu.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { + for (int z = 0; z < updatedMenu.size(); z++) { + MenuCell mainCell = updatedMenu.get(z); + for (int i = 0; i < dynamicCells.size(); i++) { + MenuCell dynamicCell = dynamicCells.get(i); + if (mainCell.equals(dynamicCell)) { + int newId = ++lastMenuId; + updatedMenu.get(z).setCellId(newId); + dynamicCells.get(i).setCellId(newId); + + if (mainCell.getSubCells() != null && !mainCell.getSubCells().isEmpty()) { + updateIdsOnMenuCells(mainCell.getSubCells(), mainCell.getCellId()); + } + break; + } + } + } + } + } + + private List updateIdsOnDynamicSubCells(List oldList, List dynamicCells, Integer parentId) { + if (oldList != null && !oldList.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { + for (int z = 0; z < oldList.size(); z++) { + MenuCell mainCell = oldList.get(z); + for (int i = 0; i < dynamicCells.size(); i++) { + MenuCell dynamicCell = dynamicCells.get(i); + if (mainCell.equals(dynamicCell)) { + int newId = ++lastMenuId; + oldList.get(z).setCellId(newId); + dynamicCells.get(i).setParentCellId(parentId); + dynamicCells.get(i).setCellId(newId); + } else { + int newId = ++lastMenuId; + dynamicCells.get(i).setParentCellId(parentId); + dynamicCells.get(i).setCellId(newId); + } + } + } + return dynamicCells; + } + return null; + } + + private void updateIdsOnMenuCells(List cells, int parentId) { + for (MenuCell cell : cells) { + int newId = ++lastMenuId; + cell.setCellId(newId); + cell.setParentCellId(parentId); + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); + } + } + } + + private void transferIdsToKeptCells(List keeps) { + for (int z = 0; z < currentMenu.size(); z++) { + MenuCell oldCell = currentMenu.get(z); + for (int i = 0; i < keeps.size(); i++) { + MenuCell keptCell = keeps.get(i); + if (oldCell.equals(keptCell)) { + keptCell.setCellId(oldCell.getCellId()); + break; + } + } + } + } + + private void transferIdsToKeptSubCells(List old, List keeps) { + for (int z = 0; z < old.size(); z++) { + MenuCell oldCell = old.get(z); + for (int i = 0; i < keeps.size(); i++) { + MenuCell keptCell = keeps.get(i); + if (oldCell.equals(keptCell)) { + keptCell.setCellId(oldCell.getCellId()); + break; + } + } + } + } + + void setMenuConfiguration(MenuConfiguration menuConfiguration) { + this.menuConfiguration = menuConfiguration; } private void finishOperation(boolean success) { if (operationCompletionListener != null) { - operationCompletionListener.onComplete(success, oldMenuCells); + operationCompletionListener.onComplete(success, currentMenu); } onFinished(); } - - public void setMenuConfiguration(MenuConfiguration menuConfiguration) { - } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 5c12672dc..f01274c61 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -1,7 +1,6 @@ package com.smartdevicelink.managers.screen.menu; import com.livio.taskmaster.Task; -import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; import com.smartdevicelink.managers.CompletionListener; import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.managers.file.FileManager; @@ -10,7 +9,6 @@ import com.smartdevicelink.managers.file.filetypes.SdlArtwork; import com.smartdevicelink.proxy.RPCRequest; import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.WindowCapability; -import com.smartdevicelink.proxy.rpc.enums.DisplayType; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.util.DebugTool; @@ -18,14 +16,17 @@ import com.smartdevicelink.util.DebugTool; import org.json.JSONException; import java.lang.ref.WeakReference; -import java.util.ArrayList; import java.util.List; import java.util.Map; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.isDynamicMenuUpdateActive; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.lastMenuId; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.*; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.shouldRPCsIncludeImages; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.supportsImages; /** * Created by Bilal Alsharifi on 1/20/21. @@ -38,19 +39,13 @@ class MenuReplaceStaticOperation extends Task { private final WindowCapability defaultMainWindowCapability; private List currentMenu; private final List updatedMenu; - private List keepsOld; - private List keepsNew; private final MenuManagerCompletionListener operationCompletionListener; - private final String displayType; - private final DynamicMenuUpdatesMode dynamicMenuUpdatesMode; private MenuConfiguration menuConfiguration; - MenuReplaceStaticOperation(ISdl internalInterface, FileManager fileManager, String displayType, DynamicMenuUpdatesMode dynamicMenuUpdatesMode, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { + MenuReplaceStaticOperation(ISdl internalInterface, FileManager fileManager, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); - this.displayType = displayType; - this.dynamicMenuUpdatesMode = dynamicMenuUpdatesMode; this.menuConfiguration = menuConfiguration; this.defaultMainWindowCapability = defaultMainWindowCapability; this.currentMenu = currentMenu; @@ -103,97 +98,8 @@ class MenuReplaceStaticOperation extends Task { return; } - // Checks against what the developer set for update mode and against the display type to - // determine how the menu will be updated. This has the ability to be changed during a session. - if (isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType)) { - // Run the lists through the new algorithm - DynamicMenuUpdateRunScore rootScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); - if (rootScore == null) { - // Send initial menu without dynamic updates because oldMenuCells is null - DebugTool.logInfo(TAG, "Creating initial Menu"); - updateIdsOnMenuCells(updatedMenu, parentIdNotFound); - createAndSendEntireMenu(listener); - } else { - DebugTool.logInfo(TAG, "Dynamically Updating Menu"); - if (updatedMenu.isEmpty() && (currentMenu != null && !currentMenu.isEmpty())) { - // the dev wants to clear the menu. We have old cells and an empty array of new ones. - deleteMenuWhenNewCellsEmpty(listener); - } else { - // lets dynamically update the root menu - dynamicallyUpdateRootMenu(rootScore, listener); - } - } - } else { - // We are in compatibility mode. No need to run the algorithm - DebugTool.logInfo(TAG, "Updating menus in compatibility mode"); - updateIdsOnMenuCells(updatedMenu, parentIdNotFound); - createAndSendEntireMenu(listener); - } - } - - private void deleteMenuWhenNewCellsEmpty(final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - sendDeleteRPCs(deleteCommandsForCells(currentMenu), new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } else { - DebugTool.logInfo(TAG, "Successfully Cleared Menu"); - } - currentMenu = null; - listener.onComplete(success); - } - }); - } - - private void dynamicallyUpdateRootMenu(DynamicMenuUpdateRunScore bestRootScore, CompletionListener listener) { - // We need to run through the keeps and see if they have subCells, as they also need to be run through the compare function. - List newStatesArray = bestRootScore.getCurrentMenu(); - List oldStatesArray = bestRootScore.getOldMenu(); - List deleteCommands; - - // Set up deletes - List deletes = new ArrayList<>(); - keepsOld = new ArrayList<>(); - for (int x = 0; x < oldStatesArray.size(); x++) { - MenuCellState old = oldStatesArray.get(x); - if (old.equals(MenuCellState.DELETE)) { - // grab cell to send to function to create delete commands - deletes.add(currentMenu.get(x)); - } else if (old.equals(MenuCellState.KEEP)) { - keepsOld.add(currentMenu.get(x)); - } - } - // create the delete commands - deleteCommands = deleteCommandsForCells(deletes); - - // Set up the adds - List adds = new ArrayList<>(); - keepsNew = new ArrayList<>(); - for (int x = 0; x < newStatesArray.size(); x++) { - MenuCellState newState = newStatesArray.get(x); - if (newState.equals(MenuCellState.ADD)) { - // grab cell to send to function to create add commands - adds.add(updatedMenu.get(x)); - } else if (newState.equals(MenuCellState.KEEP)) { - keepsNew.add(updatedMenu.get(x)); - } - } - updateIdsOnDynamicCells(adds); - // this is needed for the onCommands to still work - transferIdsToKeptCells(keepsNew); - - if (!adds.isEmpty()) { // todo what if adds was empty but deletes was not? - DebugTool.logInfo(TAG, "Sending root menu updates"); - sendDynamicRootMenuRPCs(deleteCommands, adds, listener); - } else { - DebugTool.logInfo(TAG, "All root menu items are kept. Check the sub menus"); - runSubMenuCompareAlgorithm(listener); - } + updateIdsOnMenuCells(updatedMenu, parentIdNotFound); + createAndSendEntireMenu(listener); } private void createAndSendEntireMenu(final CompletionListener listener) { @@ -256,14 +162,10 @@ class MenuReplaceStaticOperation extends Task { DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); sendSubMenuCommandRPCs(subMenuCommands, listener); } else { - if (keepsNew != null && !keepsNew.isEmpty()) { - runSubMenuCompareAlgorithm(listener); - } else { - DebugTool.logInfo(TAG, "Finished sending main menu commands."); + DebugTool.logInfo(TAG, "Finished sending main menu commands."); - if (listener != null) { - listener.onComplete(true); - } + if (listener != null) { + listener.onComplete(true); } } } @@ -295,63 +197,8 @@ class MenuReplaceStaticOperation extends Task { @Override public void onFinished() { - if (keepsNew != null && !keepsNew.isEmpty()) { - runSubMenuCompareAlgorithm(listener); - } else { - DebugTool.logInfo(TAG, "Finished Updating Menu"); - - if (listener != null) { - listener.onComplete(true); - } - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, "Sub Menu response: " + response.serializeJSON().toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - DebugTool.logError(TAG, "Failed to send sub menu commands: " + response.getInfo()); - } - } - }); - } + DebugTool.logInfo(TAG, "Finished Updating Menu"); - private void createAndSendDynamicSubMenuRPCs(List newMenu, final List adds, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (adds.isEmpty()) { - if (listener != null) { - // This can be considered a success if the user was clearing out their menu - DebugTool.logError(TAG, "Called createAndSendDynamicSubMenuRPCs with empty menu"); - listener.onComplete(true); - } - return; - } - - List mainMenuCommands; - - if (!shouldRPCsIncludeImages(adds, fileManager.get()) || !supportsImages(defaultMainWindowCapability)) { - // Send artwork-less menu - mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, false); - } else { - mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, true); - } - - internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - // nothing here - } - - @Override - public void onFinished() { if (listener != null) { listener.onComplete(true); } @@ -361,12 +208,12 @@ class MenuReplaceStaticOperation extends Task { public void onResponse(int correlationId, RPCResponse response) { if (response.getSuccess()) { try { - DebugTool.logInfo(TAG, "Dynamic Sub Menu response: " + response.serializeJSON().toString()); + DebugTool.logInfo(TAG, "Sub Menu response: " + response.serializeJSON().toString()); } catch (JSONException e) { e.printStackTrace(); } } else { - DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); + DebugTool.logError(TAG, "Failed to send sub menu commands: " + response.getInfo()); } } }); @@ -419,187 +266,6 @@ class MenuReplaceStaticOperation extends Task { }); } - private List createCommandsForDynamicSubCells(List oldMenuCells, List cells, boolean shouldHaveArtwork) { - List builtCommands = new ArrayList<>(); - for (int z = 0; z < oldMenuCells.size(); z++) { - MenuCell oldCell = oldMenuCells.get(z); - for (int i = 0; i < cells.size(); i++) { - MenuCell cell = cells.get(i); - if (cell.equals(oldCell)) { - builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, z)); - break; - } - } - } - return builtCommands; - } - - private void sendDynamicRootMenuRPCs(List deleteCommands, final List updatedCells, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - sendDeleteRPCs(deleteCommands, new CompletionListener() { - @Override - public void onComplete(boolean success) { - createAndSendMenuCellRPCs(updatedCells, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } - - listener.onComplete(success); - } - }); - } - }); - } - - private void runSubMenuCompareAlgorithm(CompletionListener listener) { - // any cells that were re-added have their sub-cells added with them - // at this point all we care about are the cells that were deemed equal and kept. - if (keepsNew == null || keepsNew.isEmpty()) { - listener.onComplete(true); - return; - } - - List commandLists = new ArrayList<>(); - - for (int i = 0; i < keepsNew.size(); i++) { - MenuCell newKeptCell = keepsNew.get(i); - MenuCell oldKeptCell = keepsOld.get(i); - - if (oldKeptCell.getSubCells() != null && !oldKeptCell.getSubCells().isEmpty() && newKeptCell.getSubCells() != null && !newKeptCell.getSubCells().isEmpty()) { - DynamicMenuUpdateRunScore subScore = DynamicMenuUpdateAlgorithm.startCompareAtRun(oldKeptCell.getSubCells(), newKeptCell.getSubCells()); - - if (subScore != null) { - DebugTool.logInfo(TAG, "Sub menu Run Score: " + oldKeptCell.getTitle() + " Score: " + subScore.getScore()); - SubCellCommandList commandList = new SubCellCommandList(oldKeptCell.getTitle(), oldKeptCell.getCellId(), subScore, oldKeptCell.getSubCells(), newKeptCell.getSubCells()); - commandLists.add(commandList); - } - } - } - createSubMenuDynamicCommands(commandLists, listener); - } - - private void createSubMenuDynamicCommands(final List commandLists, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (commandLists.isEmpty()) { - DebugTool.logInfo(TAG, "All menu updates, including sub menus - done."); - listener.onComplete(true); - return; - } - - final SubCellCommandList commandList = commandLists.remove(0); - - DebugTool.logInfo(TAG, "Creating and Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - - // grab the scores - DynamicMenuUpdateRunScore score = commandList.getListsScore(); - List newStates = score.getCurrentMenu(); - List oldStates = score.getOldMenu(); - - // Grab the sub-menus from the parent cell - final List oldCells = commandList.getOldList(); - final List newCells = commandList.getNewList(); - - // Set up deletes - List deletes = new ArrayList<>(); - for (int x = 0; x < oldStates.size(); x++) { - MenuCellState old = oldStates.get(x); - if (old.equals(MenuCellState.DELETE)) { - // grab cell to send to function to create delete commands - deletes.add(oldCells.get(x)); - } - } - // create the delete commands - List deleteCommands = deleteCommandsForCells(deletes); - - // Set up the adds - List adds = new ArrayList<>(); - List subCellKeepsNew = new ArrayList<>(); - for (int x = 0; x < newStates.size(); x++) { - MenuCellState newState = newStates.get(x); - if (newState.equals(MenuCellState.ADD)) { - // grab cell to send to function to create add commands - adds.add(newCells.get(x)); - } else if (newState.equals(MenuCellState.KEEP)) { - subCellKeepsNew.add(newCells.get(x)); - } - } - final List addsWithNewIds = updateIdsOnDynamicSubCells(oldCells, adds, commandList.getParentId()); - // this is needed for the onCommands to still work - transferIdsToKeptSubCells(oldCells, subCellKeepsNew); - - sendDeleteRPCs(deleteCommands, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (addsWithNewIds != null && !addsWithNewIds.isEmpty()) { - createAndSendDynamicSubMenuRPCs(newCells, addsWithNewIds, new CompletionListener() { - @Override - public void onComplete(boolean success) { - // recurse through next sub list - DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - createSubMenuDynamicCommands(commandLists, listener); - } - }); - } else { - // no add commands to send, recurse through next sub list - DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - createSubMenuDynamicCommands(commandLists, listener); - } - } - }); - } - - private void updateIdsOnDynamicCells(List dynamicCells) { - if (updatedMenu != null && !updatedMenu.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { - for (int z = 0; z < updatedMenu.size(); z++) { - MenuCell mainCell = updatedMenu.get(z); - for (int i = 0; i < dynamicCells.size(); i++) { - MenuCell dynamicCell = dynamicCells.get(i); - if (mainCell.equals(dynamicCell)) { - int newId = ++lastMenuId; - updatedMenu.get(z).setCellId(newId); - dynamicCells.get(i).setCellId(newId); - - if (mainCell.getSubCells() != null && !mainCell.getSubCells().isEmpty()) { - updateIdsOnMenuCells(mainCell.getSubCells(), mainCell.getCellId()); - } - break; - } - } - } - } - } - - private List updateIdsOnDynamicSubCells(List oldList, List dynamicCells, Integer parentId) { - if (oldList != null && !oldList.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { - for (int z = 0; z < oldList.size(); z++) { - MenuCell mainCell = oldList.get(z); - for (int i = 0; i < dynamicCells.size(); i++) { - MenuCell dynamicCell = dynamicCells.get(i); - if (mainCell.equals(dynamicCell)) { - int newId = ++lastMenuId; - oldList.get(z).setCellId(newId); - dynamicCells.get(i).setParentCellId(parentId); - dynamicCells.get(i).setCellId(newId); - } else { - int newId = ++lastMenuId; - dynamicCells.get(i).setParentCellId(parentId); - dynamicCells.get(i).setCellId(newId); - } - } - } - return dynamicCells; - } - return null; - } - private void updateIdsOnMenuCells(List cells, int parentId) { for (MenuCell cell : cells) { int newId = ++lastMenuId; @@ -611,32 +277,6 @@ class MenuReplaceStaticOperation extends Task { } } - private void transferIdsToKeptCells(List keeps) { - for (int z = 0; z < currentMenu.size(); z++) { - MenuCell oldCell = currentMenu.get(z); - for (int i = 0; i < keeps.size(); i++) { - MenuCell keptCell = keeps.get(i); - if (oldCell.equals(keptCell)) { - keptCell.setCellId(oldCell.getCellId()); - break; - } - } - } - } - - private void transferIdsToKeptSubCells(List old, List keeps) { - for (int z = 0; z < old.size(); z++) { - MenuCell oldCell = old.get(z); - for (int i = 0; i < keeps.size(); i++) { - MenuCell keptCell = keeps.get(i); - if (oldCell.equals(keptCell)) { - keptCell.setCellId(oldCell.getCellId()); - break; - } - } - } - } - void setMenuConfiguration(MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } -- cgit v1.2.1 From 8be2de8b3ef974669f83814163a2c0447287fe53 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 26 Jan 2021 13:14:55 -0500 Subject: make isDynamicMenuUpdateActive private --- .../java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 27a2af5cb..9b0ae2ca2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -441,8 +441,7 @@ abstract class BaseMenuManager extends BaseSubManager { return false; } - // todo change to private - static boolean isDynamicMenuUpdateActive(DynamicMenuUpdatesMode updateMode, String displayType) { + private boolean isDynamicMenuUpdateActive(DynamicMenuUpdatesMode updateMode, String displayType) { if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { if (displayType == null) { return true; -- cgit v1.2.1 From b103a629d67422ab8e1bc1cfdd1890ff2ba931d2 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 26 Jan 2021 13:30:01 -0500 Subject: Reformat code --- .../smartdevicelink/managers/screen/menu/BaseMenuManager.java | 4 ++-- .../managers/screen/menu/MenuReplaceDynamicOperation.java | 4 ++-- .../managers/screen/menu/MenuReplaceStaticOperation.java | 10 +++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 9b0ae2ca2..c963a32e0 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -208,9 +208,9 @@ abstract class BaseMenuManager extends BaseSubManager { }; if (isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType)) { - operation = new MenuReplaceDynamicOperation(internalInterface, fileManager.get(), menuConfiguration, defaultMainWindowCapability, currentMenuCells, menuCells, menuManagerCompletionListener); + operation = new MenuReplaceDynamicOperation(internalInterface, fileManager.get(), defaultMainWindowCapability, menuConfiguration, currentMenuCells, menuCells, menuManagerCompletionListener); } else { - operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), menuConfiguration, defaultMainWindowCapability, currentMenuCells, menuCells, menuManagerCompletionListener); + operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), defaultMainWindowCapability, menuConfiguration, currentMenuCells, menuCells, menuManagerCompletionListener); } transactionQueue.add(operation, false); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 56d8deaf9..eff0fa0a4 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -47,12 +47,12 @@ class MenuReplaceDynamicOperation extends Task { private final MenuManagerCompletionListener operationCompletionListener; private MenuConfiguration menuConfiguration; - MenuReplaceDynamicOperation(ISdl internalInterface, FileManager fileManager, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { + MenuReplaceDynamicOperation(ISdl internalInterface, FileManager fileManager, WindowCapability defaultMainWindowCapability, MenuConfiguration menuConfiguration, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); - this.menuConfiguration = menuConfiguration; this.defaultMainWindowCapability = defaultMainWindowCapability; + this.menuConfiguration = menuConfiguration; this.currentMenu = currentMenu; this.updatedMenu = updatedMenu; this.operationCompletionListener = operationCompletionListener; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index f01274c61..2a0628ed4 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -37,17 +37,17 @@ class MenuReplaceStaticOperation extends Task { private final WeakReference internalInterface; private final WeakReference fileManager; private final WindowCapability defaultMainWindowCapability; - private List currentMenu; + private final List currentMenu; private final List updatedMenu; private final MenuManagerCompletionListener operationCompletionListener; private MenuConfiguration menuConfiguration; - MenuReplaceStaticOperation(ISdl internalInterface, FileManager fileManager, MenuConfiguration menuConfiguration, WindowCapability defaultMainWindowCapability, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { + MenuReplaceStaticOperation(ISdl internalInterface, FileManager fileManager, WindowCapability defaultMainWindowCapability, MenuConfiguration menuConfiguration, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); - this.menuConfiguration = menuConfiguration; this.defaultMainWindowCapability = defaultMainWindowCapability; + this.menuConfiguration = menuConfiguration; this.currentMenu = currentMenu; this.updatedMenu = updatedMenu; this.operationCompletionListener = operationCompletionListener; @@ -78,6 +78,10 @@ class MenuReplaceStaticOperation extends Task { fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { @Override public void onComplete(Map errors) { + if (getState() == Task.CANCELED) { + return; + } + if (errors != null && !errors.isEmpty()) { DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); } else { -- cgit v1.2.1 From 322761c221d260d5ededf94530caf34942b28154 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 26 Jan 2021 13:38:09 -0500 Subject: Remove updateMenuAndDetermineBestUpdateMethod() --- .../screen/menu/MenuReplaceStaticOperation.java | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 2a0628ed4..44fd643d4 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -78,38 +78,27 @@ class MenuReplaceStaticOperation extends Task { fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { @Override public void onComplete(Map errors) { - if (getState() == Task.CANCELED) { - return; - } - if (errors != null && !errors.isEmpty()) { DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); } else { DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); } - // proceed - updateMenuAndDetermineBestUpdateMethod(listener); + + createAndSendEntireMenu(listener); } }); } else { // No Artworks to be uploaded, send off - updateMenuAndDetermineBestUpdateMethod(listener); + createAndSendEntireMenu(listener); } } - private void updateMenuAndDetermineBestUpdateMethod(CompletionListener listener) { + private void createAndSendEntireMenu(final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } updateIdsOnMenuCells(updatedMenu, parentIdNotFound); - createAndSendEntireMenu(listener); - } - - private void createAndSendEntireMenu(final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } deleteRootMenu(new CompletionListener() { @Override -- cgit v1.2.1 From 7e26a5e9484d1c1f2cb238d98b479c65968d9877 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 26 Jan 2021 13:59:38 -0500 Subject: Algin MenuReplaceStaticOperation with iOS --- .../screen/menu/MenuReplaceStaticOperation.java | 69 +++++++++------------- 1 file changed, 28 insertions(+), 41 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 44fd643d4..032352045 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -72,7 +72,6 @@ class MenuReplaceStaticOperation extends Task { } private void updateMenuCells(final CompletionListener listener) { - // Upload the Artworks List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), defaultMainWindowCapability); if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { @@ -84,26 +83,26 @@ class MenuReplaceStaticOperation extends Task { DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); } - createAndSendEntireMenu(listener); + updateMenuWithCellsToDelete(currentMenu, updatedMenu, listener); } }); } else { // No Artworks to be uploaded, send off - createAndSendEntireMenu(listener); + updateMenuWithCellsToDelete(currentMenu, updatedMenu, listener); } } - private void createAndSendEntireMenu(final CompletionListener listener) { + private void updateMenuWithCellsToDelete(final List deleteCells, final List addCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } - updateIdsOnMenuCells(updatedMenu, parentIdNotFound); + updateIdsOnMenuCells(addCells, parentIdNotFound); - deleteRootMenu(new CompletionListener() { + sendDeleteCurrentMenu(deleteCells, new CompletionListener() { @Override public void onComplete(boolean success) { - createAndSendMenuCellRPCs(updatedMenu, new CompletionListener() { + sendNewMenuCells(addCells, new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { @@ -117,31 +116,30 @@ class MenuReplaceStaticOperation extends Task { }); } - private void createAndSendMenuCellRPCs(final List menu, final CompletionListener listener) { + private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } - if (menu == null || menu.isEmpty()) { - if (listener != null) { - // This can be considered a success if the user was clearing out their menu - listener.onComplete(true); - } + if (newMenuCells == null || newMenuCells.isEmpty()) { + // This can be considered a success if the user was clearing out their menu + DebugTool.logInfo(TAG, "There are no cells to update."); + listener.onComplete(true); return; } - - List mainMenuCommands; - final List subMenuCommands; + List availableMenuLayouts = defaultMainWindowCapability != null ? defaultMainWindowCapability.getMenuLayoutsAvailable() : null; MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; + List mainMenuCommands; + final List subMenuCommands; - if (!shouldRPCsIncludeImages(menu, fileManager.get()) || !supportsImages(defaultMainWindowCapability)) { + if (!shouldRPCsIncludeImages(newMenuCells, fileManager.get()) || !supportsImages(defaultMainWindowCapability)) { // Send artwork-less menu - mainMenuCommands = mainMenuCommandsForCells(menu, false, updatedMenu, availableMenuLayouts, defaultSubmenuLayout); - subMenuCommands = subMenuCommandsForCells(menu, false, availableMenuLayouts, defaultSubmenuLayout); + mainMenuCommands = mainMenuCommandsForCells(newMenuCells, false, newMenuCells, availableMenuLayouts, defaultSubmenuLayout); + subMenuCommands = subMenuCommandsForCells(newMenuCells, false, availableMenuLayouts, defaultSubmenuLayout); } else { - mainMenuCommands = mainMenuCommandsForCells(menu, true, updatedMenu, availableMenuLayouts, defaultSubmenuLayout); - subMenuCommands = subMenuCommandsForCells(menu, true, availableMenuLayouts, defaultSubmenuLayout); + mainMenuCommands = mainMenuCommandsForCells(newMenuCells, true, newMenuCells, availableMenuLayouts, defaultSubmenuLayout); + subMenuCommands = subMenuCommandsForCells(newMenuCells, true, availableMenuLayouts, defaultSubmenuLayout); } internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { @@ -212,29 +210,18 @@ class MenuReplaceStaticOperation extends Task { }); } - private void deleteRootMenu(final CompletionListener listener) { - if (currentMenu == null || currentMenu.isEmpty()) { - if (listener != null) { - // technically this method is successful if there's nothing to delete - DebugTool.logInfo(TAG, "No old cells to delete, returning"); - listener.onComplete(true); - } - } else { - sendDeleteRPCs(deleteCommandsForCells(currentMenu), listener); - } - } - - private void sendDeleteRPCs(List deleteCommands, final CompletionListener listener) { - if (getState() == Task.CANCELED) { + private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { + if (deleteMenuCells == null || deleteMenuCells.isEmpty()) { + // Technically this method is successful if there's nothing to delete + DebugTool.logInfo(TAG, "No old cells to delete, returning"); + listener.onComplete(true); return; } + List deleteCommands = deleteCommandsForCells(deleteMenuCells); - if (deleteCommands == null || deleteCommands.isEmpty()) { - // no dynamic deletes required. return - if (listener != null) { - // technically this method is successful if there's nothing to delete - listener.onComplete(true); - } + if (deleteCommands.isEmpty()) { + // Technically this method is successful if there's nothing to delete + listener.onComplete(true); return; } -- cgit v1.2.1 From 9936bc80a8ee65d888a09d1c910df4b9ad18fe7c Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 26 Jan 2021 14:44:24 -0500 Subject: Add shouldCellIncludeImage() --- .../screen/menu/MenuReplaceDynamicOperation.java | 28 +++++++------ .../screen/menu/MenuReplaceStaticOperation.java | 14 +------ .../managers/screen/menu/MenuReplaceUtilities.java | 47 +++++++++++----------- 3 files changed, 42 insertions(+), 47 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index eff0fa0a4..858689355 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -27,7 +27,6 @@ import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.comm import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.shouldRPCsIncludeImages; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.supportsImages; @@ -222,19 +221,12 @@ class MenuReplaceDynamicOperation extends Task { return; } - List mainMenuCommands; - final List subMenuCommands; List availableMenuLayouts = defaultMainWindowCapability != null ? defaultMainWindowCapability.getMenuLayoutsAvailable() : null; MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; - if (!shouldRPCsIncludeImages(menu, fileManager.get()) || !supportsImages(defaultMainWindowCapability)) { - // Send artwork-less menu - mainMenuCommands = mainMenuCommandsForCells(menu, false, updatedMenu, availableMenuLayouts, defaultSubmenuLayout); - subMenuCommands = subMenuCommandsForCells(menu, false, availableMenuLayouts, defaultSubmenuLayout); - } else { - mainMenuCommands = mainMenuCommandsForCells(menu, true, updatedMenu, availableMenuLayouts, defaultSubmenuLayout); - subMenuCommands = subMenuCommandsForCells(menu, true, availableMenuLayouts, defaultSubmenuLayout); - } + List mainMenuCommands = mainMenuCommandsForCells(menu, fileManager.get(), updatedMenu, availableMenuLayouts, defaultSubmenuLayout); + final List subMenuCommands = subMenuCommandsForCells(menu, fileManager.get(), availableMenuLayouts, defaultSubmenuLayout); + internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { @Override @@ -417,7 +409,7 @@ class MenuReplaceDynamicOperation extends Task { for (int i = 0; i < cells.size(); i++) { MenuCell cell = cells.get(i); if (cell.equals(oldCell)) { - builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, z)); + builtCommands.add(commandForMenuCell(cell, fileManager.get(), z)); break; } } @@ -628,6 +620,18 @@ class MenuReplaceDynamicOperation extends Task { } } + private boolean shouldRPCsIncludeImages(List cells, FileManager fileManager) { + for (MenuCell cell : cells) { + SdlArtwork artwork = cell.getIcon(); + if (artwork != null && !artwork.isStaticIcon() && fileManager != null && !fileManager.hasUploadedFile(artwork)) { + return false; + } else if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + return shouldRPCsIncludeImages(cell.getSubCells(), fileManager); + } + } + return true; + } + void setMenuConfiguration(MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 032352045..f5b2a4d74 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -24,9 +24,7 @@ import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdN import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.shouldRPCsIncludeImages; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.supportsImages; /** * Created by Bilal Alsharifi on 1/20/21. @@ -130,17 +128,9 @@ class MenuReplaceStaticOperation extends Task { List availableMenuLayouts = defaultMainWindowCapability != null ? defaultMainWindowCapability.getMenuLayoutsAvailable() : null; MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; - List mainMenuCommands; - final List subMenuCommands; - if (!shouldRPCsIncludeImages(newMenuCells, fileManager.get()) || !supportsImages(defaultMainWindowCapability)) { - // Send artwork-less menu - mainMenuCommands = mainMenuCommandsForCells(newMenuCells, false, newMenuCells, availableMenuLayouts, defaultSubmenuLayout); - subMenuCommands = subMenuCommandsForCells(newMenuCells, false, availableMenuLayouts, defaultSubmenuLayout); - } else { - mainMenuCommands = mainMenuCommandsForCells(newMenuCells, true, newMenuCells, availableMenuLayouts, defaultSubmenuLayout); - subMenuCommands = subMenuCommandsForCells(newMenuCells, true, availableMenuLayouts, defaultSubmenuLayout); - } + List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), updatedMenu, availableMenuLayouts, defaultSubmenuLayout); + final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), availableMenuLayouts, defaultSubmenuLayout); internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { @Override diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 379ac546f..67e02bb83 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -61,19 +61,17 @@ class MenuReplaceUtilities { return windowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); } - static boolean shouldRPCsIncludeImages(List cells, FileManager fileManager) { - for (MenuCell cell : cells) { - SdlArtwork artwork = cell.getIcon(); - if (artwork != null && !artwork.isStaticIcon() && fileManager != null && !fileManager.hasUploadedFile(artwork)) { - return false; - } else if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - return shouldRPCsIncludeImages(cell.getSubCells(), fileManager); - } + static boolean shouldCellIncludeImage(MenuCell cell, FileManager fileManager) { + // todo should check if images are supported? + SdlArtwork artwork = cell.getIcon(); + if (artwork == null) { + return false; } - return true; + // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image + return (fileManager.hasUploadedFile(artwork) || artwork.isStaticIcon()); } - static List mainMenuCommandsForCells(List cellsToAdd, boolean shouldHaveArtwork, List updatedMenu, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { + static List mainMenuCommandsForCells(List cellsToAdd, FileManager fileManager, List updatedMenu, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { List builtCommands = new ArrayList<>(); // We need the index so we will use this type of loop @@ -83,9 +81,9 @@ class MenuReplaceUtilities { MenuCell addCell = cellsToAdd.get(i); if (mainCell.equals(addCell)) { if (addCell.getSubCells() != null && !addCell.getSubCells().isEmpty()) { - builtCommands.add(subMenuCommandForMenuCell(addCell, shouldHaveArtwork, z, availableMenuLayouts, defaultSubmenuLayout)); + builtCommands.add(subMenuCommandForMenuCell(addCell, fileManager, z, availableMenuLayouts, defaultSubmenuLayout)); } else { - builtCommands.add(commandForMenuCell(addCell, shouldHaveArtwork, z)); + builtCommands.add(commandForMenuCell(addCell, fileManager, z)); } break; } @@ -94,52 +92,55 @@ class MenuReplaceUtilities { return builtCommands; } - static List subMenuCommandsForCells(List cells, boolean shouldHaveArtwork, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { + static List subMenuCommandsForCells(List cells, FileManager fileManager, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { List builtCommands = new ArrayList<>(); for (MenuCell cell : cells) { if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork, availableMenuLayouts, defaultSubmenuLayout)); + builtCommands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, availableMenuLayouts, defaultSubmenuLayout)); } } return builtCommands; } - static List allCommandsForCells(List cells, boolean shouldHaveArtwork, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { + static List allCommandsForCells(List cells, FileManager fileManager, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { List builtCommands = new ArrayList<>(); for (int i = 0; i < cells.size(); i++) { MenuCell cell = cells.get(i); if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - builtCommands.add(subMenuCommandForMenuCell(cell, shouldHaveArtwork, i, availableMenuLayouts, defaultSubmenuLayout)); + builtCommands.add(subMenuCommandForMenuCell(cell, fileManager, i, availableMenuLayouts, defaultSubmenuLayout)); // recursively grab the commands for all the sub cells - builtCommands.addAll(allCommandsForCells(cell.getSubCells(), shouldHaveArtwork, availableMenuLayouts, defaultSubmenuLayout)); + builtCommands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, availableMenuLayouts, defaultSubmenuLayout)); } else { - builtCommands.add(commandForMenuCell(cell, shouldHaveArtwork, i)); + builtCommands.add(commandForMenuCell(cell, fileManager, i)); } } return builtCommands; } - static AddCommand commandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position) { + static AddCommand commandForMenuCell(MenuCell cell, FileManager fileManager, int position) { + AddCommand command = new AddCommand(cell.getCellId()); + MenuParams params = new MenuParams(cell.getTitle()); params.setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null); params.setPosition(position); - AddCommand command = new AddCommand(cell.getCellId()); command.setMenuParams(params); if (cell.getVoiceCommands() != null && !cell.getVoiceCommands().isEmpty()) { command.setVrCommands(cell.getVoiceCommands()); } else { command.setVrCommands(null); } - command.setCmdIcon((cell.getIcon() != null && shouldHaveArtwork) ? cell.getIcon().getImageRPC() : null); + boolean shouldCellIncludeImage = cell.getIcon() != null && shouldCellIncludeImage(cell, fileManager); + command.setCmdIcon(shouldCellIncludeImage ? cell.getIcon().getImageRPC() : null); return command; } - static AddSubMenu subMenuCommandForMenuCell(MenuCell cell, boolean shouldHaveArtwork, int position, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { - Image icon = (shouldHaveArtwork && (cell.getIcon() != null && cell.getIcon().getImageRPC() != null)) ? cell.getIcon().getImageRPC() : null; + static AddSubMenu subMenuCommandForMenuCell(MenuCell cell, FileManager fileManager, int position, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { + boolean shouldCellIncludeImage = cell.getIcon() != null && cell.getIcon().getImageRPC() != null && shouldCellIncludeImage(cell, fileManager); + Image icon = (shouldCellIncludeImage ? cell.getIcon().getImageRPC() : null); MenuLayout submenuLayout; if (cell.getSubMenuLayout() != null && availableMenuLayouts != null && availableMenuLayouts.contains(cell.getSubMenuLayout())) { -- cgit v1.2.1 From fbb01b2b3df8daf259bb6d8a7fc5075aa473ddc6 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 26 Jan 2021 15:09:14 -0500 Subject: Use sendRPCs instead of sendSequentialRPCs --- .../screen/menu/MenuReplaceDynamicOperation.java | 6 ++-- .../screen/menu/MenuReplaceStaticOperation.java | 36 +++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 858689355..20c0604ea 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -228,7 +228,7 @@ class MenuReplaceDynamicOperation extends Task { final List subMenuCommands = subMenuCommandsForCells(menu, fileManager.get(), availableMenuLayouts, defaultSubmenuLayout); - internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { + internalInterface.get().sendRPCs(mainMenuCommands, new OnMultipleRequestListener() { @Override public void onUpdate(int remainingRequests) { } @@ -271,7 +271,7 @@ class MenuReplaceDynamicOperation extends Task { return; } - internalInterface.get().sendSequentialRPCs(commands, new OnMultipleRequestListener() { + internalInterface.get().sendRPCs(commands, new OnMultipleRequestListener() { @Override public void onUpdate(int remainingRequests) { } @@ -327,7 +327,7 @@ class MenuReplaceDynamicOperation extends Task { mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, true); } - internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { + internalInterface.get().sendRPCs(mainMenuCommands, new OnMultipleRequestListener() { @Override public void onUpdate(int remainingRequests) { // nothing here diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index f5b2a4d74..46f670f66 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -75,31 +75,33 @@ class MenuReplaceStaticOperation extends Task { fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { @Override public void onComplete(Map errors) { + if (getState() == Task.CANCELED) { + return; + } + if (errors != null && !errors.isEmpty()) { - DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); + DebugTool.logError(TAG, "Error uploading menu artworks: " + errors.toString()); } else { - DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); + DebugTool.logInfo(TAG, "All menu artworks uploaded"); } updateMenuWithCellsToDelete(currentMenu, updatedMenu, listener); } }); } else { - // No Artworks to be uploaded, send off + // Cells have no artwork to load updateMenuWithCellsToDelete(currentMenu, updatedMenu, listener); } } private void updateMenuWithCellsToDelete(final List deleteCells, final List addCells, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - updateIdsOnMenuCells(addCells, parentIdNotFound); - sendDeleteCurrentMenu(deleteCells, new CompletionListener() { @Override public void onComplete(boolean success) { + if (getState() == Task.CANCELED) { + return; + } + sendNewMenuCells(addCells, new CompletionListener() { @Override public void onComplete(boolean success) { @@ -115,24 +117,22 @@ class MenuReplaceStaticOperation extends Task { } private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - if (newMenuCells == null || newMenuCells.isEmpty()) { // This can be considered a success if the user was clearing out their menu DebugTool.logInfo(TAG, "There are no cells to update."); listener.onComplete(true); return; } - + + updateIdsOnMenuCells(newMenuCells, parentIdNotFound); + List availableMenuLayouts = defaultMainWindowCapability != null ? defaultMainWindowCapability.getMenuLayoutsAvailable() : null; MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), updatedMenu, availableMenuLayouts, defaultSubmenuLayout); final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), availableMenuLayouts, defaultSubmenuLayout); - internalInterface.get().sendSequentialRPCs(mainMenuCommands, new OnMultipleRequestListener() { + internalInterface.get().sendRPCs(mainMenuCommands, new OnMultipleRequestListener() { @Override public void onUpdate(int remainingRequests) { } @@ -141,7 +141,7 @@ class MenuReplaceStaticOperation extends Task { public void onFinished() { if (!subMenuCommands.isEmpty()) { DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); - sendSubMenuCommandRPCs(subMenuCommands, listener); + sendNewSubMenuCells(subMenuCommands, listener); } else { DebugTool.logInfo(TAG, "Finished sending main menu commands."); @@ -166,12 +166,12 @@ class MenuReplaceStaticOperation extends Task { }); } - private void sendSubMenuCommandRPCs(List commands, final CompletionListener listener) { + private void sendNewSubMenuCells(List commands, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } - internalInterface.get().sendSequentialRPCs(commands, new OnMultipleRequestListener() { + internalInterface.get().sendRPCs(commands, new OnMultipleRequestListener() { @Override public void onUpdate(int remainingRequests) { } -- cgit v1.2.1 From 8c3d8ff41ef0f9194973adf83f2f023f3b3048d3 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 11:14:19 -0500 Subject: Dont include images if they are not supported --- .../screen/menu/MenuReplaceDynamicOperation.java | 20 +++++----- .../screen/menu/MenuReplaceStaticOperation.java | 10 ++--- .../managers/screen/menu/MenuReplaceUtilities.java | 45 ++++++++++------------ 3 files changed, 33 insertions(+), 42 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 20c0604ea..bc7cf985a 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -3,6 +3,7 @@ package com.smartdevicelink.managers.screen.menu; import com.livio.taskmaster.Task; 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; @@ -10,6 +11,7 @@ import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuC import com.smartdevicelink.proxy.RPCRequest; import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.util.DebugTool; @@ -23,12 +25,7 @@ import java.util.Map; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.lastMenuId; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandForMenuCell; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.supportsImages; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.*; /** * Created by Bilal Alsharifi on 1/20/21. @@ -221,11 +218,10 @@ class MenuReplaceDynamicOperation extends Task { return; } - List availableMenuLayouts = defaultMainWindowCapability != null ? defaultMainWindowCapability.getMenuLayoutsAvailable() : null; MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; - List mainMenuCommands = mainMenuCommandsForCells(menu, fileManager.get(), updatedMenu, availableMenuLayouts, defaultSubmenuLayout); - final List subMenuCommands = subMenuCommandsForCells(menu, fileManager.get(), availableMenuLayouts, defaultSubmenuLayout); + List mainMenuCommands = mainMenuCommandsForCells(menu, fileManager.get(), defaultMainWindowCapability, updatedMenu, defaultSubmenuLayout); + final List subMenuCommands = subMenuCommandsForCells(menu, fileManager.get(), defaultMainWindowCapability, defaultSubmenuLayout); internalInterface.get().sendRPCs(mainMenuCommands, new OnMultipleRequestListener() { @@ -409,7 +405,7 @@ class MenuReplaceDynamicOperation extends Task { for (int i = 0; i < cells.size(); i++) { MenuCell cell = cells.get(i); if (cell.equals(oldCell)) { - builtCommands.add(commandForMenuCell(cell, fileManager.get(), z)); + builtCommands.add(commandForMenuCell(cell, fileManager.get(), defaultMainWindowCapability, z)); break; } } @@ -632,6 +628,10 @@ class MenuReplaceDynamicOperation extends Task { return true; } + private static boolean supportsImages(WindowCapability windowCapability) { + return windowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); + } + void setMenuConfiguration(MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 46f670f66..4fe96e763 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -21,10 +21,7 @@ import java.util.Map; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.lastMenuId; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.*; /** * Created by Bilal Alsharifi on 1/20/21. @@ -126,11 +123,10 @@ class MenuReplaceStaticOperation extends Task { updateIdsOnMenuCells(newMenuCells, parentIdNotFound); - List availableMenuLayouts = defaultMainWindowCapability != null ? defaultMainWindowCapability.getMenuLayoutsAvailable() : null; MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; - List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), updatedMenu, availableMenuLayouts, defaultSubmenuLayout); - final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), availableMenuLayouts, defaultSubmenuLayout); + List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), defaultMainWindowCapability, updatedMenu, defaultSubmenuLayout); + final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), defaultMainWindowCapability, defaultSubmenuLayout); internalInterface.get().sendRPCs(mainMenuCommands, new OnMultipleRequestListener() { @Override diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 67e02bb83..40b76fe0e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -39,7 +39,7 @@ class MenuReplaceUtilities { static List findAllArtworksToBeUploadedFromCells(List cells, FileManager fileManager, WindowCapability windowCapability) { // Make sure we can use images in the menus - if (!supportsImages(windowCapability)) { + if (!ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { return new ArrayList<>(); } @@ -56,22 +56,16 @@ class MenuReplaceUtilities { return artworks; } - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - static boolean supportsImages(WindowCapability windowCapability) { - return windowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); - } - - static boolean shouldCellIncludeImage(MenuCell cell, FileManager fileManager) { - // todo should check if images are supported? - SdlArtwork artwork = cell.getIcon(); - if (artwork == null) { + static boolean shouldCellIncludeImage(MenuCell cell, FileManager fileManager, WindowCapability windowCapability) { + // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image + boolean supportsImage = cell.getSubCells() != null && !cell.getSubCells().isEmpty() ? ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.subMenuIcon) : ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); + if (cell.getIcon() == null || !supportsImage) { return false; } - // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image - return (fileManager.hasUploadedFile(artwork) || artwork.isStaticIcon()); + return (fileManager.hasUploadedFile(cell.getIcon()) || cell.getIcon().isStaticIcon()); } - static List mainMenuCommandsForCells(List cellsToAdd, FileManager fileManager, List updatedMenu, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { + static List mainMenuCommandsForCells(List cellsToAdd, FileManager fileManager, WindowCapability windowCapability, List updatedMenu, MenuLayout defaultSubmenuLayout) { List builtCommands = new ArrayList<>(); // We need the index so we will use this type of loop @@ -81,9 +75,9 @@ class MenuReplaceUtilities { MenuCell addCell = cellsToAdd.get(i); if (mainCell.equals(addCell)) { if (addCell.getSubCells() != null && !addCell.getSubCells().isEmpty()) { - builtCommands.add(subMenuCommandForMenuCell(addCell, fileManager, z, availableMenuLayouts, defaultSubmenuLayout)); + builtCommands.add(subMenuCommandForMenuCell(addCell, fileManager, windowCapability, z, defaultSubmenuLayout)); } else { - builtCommands.add(commandForMenuCell(addCell, fileManager, z)); + builtCommands.add(commandForMenuCell(addCell, fileManager, windowCapability, z)); } break; } @@ -92,34 +86,34 @@ class MenuReplaceUtilities { return builtCommands; } - static List subMenuCommandsForCells(List cells, FileManager fileManager, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { + static List subMenuCommandsForCells(List cells, FileManager fileManager, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { List builtCommands = new ArrayList<>(); for (MenuCell cell : cells) { if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - builtCommands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, availableMenuLayouts, defaultSubmenuLayout)); + builtCommands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); } } return builtCommands; } - static List allCommandsForCells(List cells, FileManager fileManager, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { + static List allCommandsForCells(List cells, FileManager fileManager, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { List builtCommands = new ArrayList<>(); for (int i = 0; i < cells.size(); i++) { MenuCell cell = cells.get(i); if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - builtCommands.add(subMenuCommandForMenuCell(cell, fileManager, i, availableMenuLayouts, defaultSubmenuLayout)); + builtCommands.add(subMenuCommandForMenuCell(cell, fileManager, windowCapability, i, defaultSubmenuLayout)); // recursively grab the commands for all the sub cells - builtCommands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, availableMenuLayouts, defaultSubmenuLayout)); + builtCommands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); } else { - builtCommands.add(commandForMenuCell(cell, fileManager, i)); + builtCommands.add(commandForMenuCell(cell, fileManager, windowCapability, i)); } } return builtCommands; } - static AddCommand commandForMenuCell(MenuCell cell, FileManager fileManager, int position) { + static AddCommand commandForMenuCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position) { AddCommand command = new AddCommand(cell.getCellId()); MenuParams params = new MenuParams(cell.getTitle()); @@ -132,17 +126,18 @@ class MenuReplaceUtilities { } else { command.setVrCommands(null); } - boolean shouldCellIncludeImage = cell.getIcon() != null && shouldCellIncludeImage(cell, fileManager); + boolean shouldCellIncludeImage = cell.getIcon() != null && shouldCellIncludeImage(cell, fileManager, windowCapability); command.setCmdIcon(shouldCellIncludeImage ? cell.getIcon().getImageRPC() : null); return command; } - static AddSubMenu subMenuCommandForMenuCell(MenuCell cell, FileManager fileManager, int position, List availableMenuLayouts, MenuLayout defaultSubmenuLayout) { - boolean shouldCellIncludeImage = cell.getIcon() != null && cell.getIcon().getImageRPC() != null && shouldCellIncludeImage(cell, fileManager); + static AddSubMenu subMenuCommandForMenuCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position, MenuLayout defaultSubmenuLayout) { + boolean shouldCellIncludeImage = cell.getIcon() != null && cell.getIcon().getImageRPC() != null && shouldCellIncludeImage(cell, fileManager, windowCapability); Image icon = (shouldCellIncludeImage ? cell.getIcon().getImageRPC() : null); MenuLayout submenuLayout; + List availableMenuLayouts = windowCapability != null ? windowCapability.getMenuLayoutsAvailable() : null; if (cell.getSubMenuLayout() != null && availableMenuLayouts != null && availableMenuLayouts.contains(cell.getSubMenuLayout())) { submenuLayout = cell.getSubMenuLayout(); } else { -- cgit v1.2.1 From 1b33b720b32e9dc2a887bbdaacf13bfd7d6475d7 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 11:40:29 -0500 Subject: Update menu replace operations with new currentMenu --- .../managers/screen/menu/BaseMenuManager.java | 13 ++++++++++++- .../managers/screen/menu/MenuReplaceDynamicOperation.java | 4 ++++ .../managers/screen/menu/MenuReplaceStaticOperation.java | 6 +++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index c963a32e0..94a173a6b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -199,11 +199,12 @@ abstract class BaseMenuManager extends BaseSubManager { // Checks against what the developer set for update mode and against the display type to // determine how the menu will be updated. This has the ability to be changed during a session. - Task operation = null; + Task operation; MenuManagerCompletionListener menuManagerCompletionListener = new MenuManagerCompletionListener() { @Override public void onComplete(boolean success, List currentMenuCells) { BaseMenuManager.this.currentMenuCells = currentMenuCells; + updateMenuReplaceOperationsWithNewCurrentMenu(); } }; @@ -441,6 +442,16 @@ abstract class BaseMenuManager extends BaseSubManager { return false; } + private void updateMenuReplaceOperationsWithNewCurrentMenu () { + for (Task task : transactionQueue.getTasksAsList()) { + if (task instanceof MenuReplaceStaticOperation) { + ((MenuReplaceStaticOperation) task).setCurrentMenu(this.currentMenuCells); + } else if (task instanceof MenuReplaceDynamicOperation) { + ((MenuReplaceDynamicOperation) task).setCurrentMenu(this.currentMenuCells); + } + } + } + private boolean isDynamicMenuUpdateActive(DynamicMenuUpdatesMode updateMode, String displayType) { if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { if (displayType == null) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index bc7cf985a..11cf22039 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -636,6 +636,10 @@ class MenuReplaceDynamicOperation extends Task { this.menuConfiguration = menuConfiguration; } + public void setCurrentMenu(List currentMenuCells) { + this.currentMenu = currentMenuCells; + } + private void finishOperation(boolean success) { if (operationCompletionListener != null) { operationCompletionListener.onComplete(success, currentMenu); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 4fe96e763..2128c0acb 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -32,7 +32,7 @@ class MenuReplaceStaticOperation extends Task { private final WeakReference internalInterface; private final WeakReference fileManager; private final WindowCapability defaultMainWindowCapability; - private final List currentMenu; + private List currentMenu; private final List updatedMenu; private final MenuManagerCompletionListener operationCompletionListener; private MenuConfiguration menuConfiguration; @@ -247,6 +247,10 @@ class MenuReplaceStaticOperation extends Task { this.menuConfiguration = menuConfiguration; } + public void setCurrentMenu(List currentMenuCells) { + this.currentMenu = currentMenuCells; + } + private void finishOperation(boolean success) { if (operationCompletionListener != null) { operationCompletionListener.onComplete(success, currentMenu); -- cgit v1.2.1 From f8713042c7520b1cc1cf506e69d54f9ceb9346ce Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 13:42:44 -0500 Subject: Rename windowCapability vars --- .../managers/screen/menu/BaseMenuManager.java | 15 +++++++-------- .../screen/menu/MenuConfigurationUpdateOperation.java | 4 ++-- .../screen/menu/MenuReplaceDynamicOperation.java | 18 +++++++++--------- .../screen/menu/MenuReplaceStaticOperation.java | 12 ++++++------ 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 94a173a6b..a5a2535b2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -60,7 +60,6 @@ import com.smartdevicelink.util.DebugTool; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -84,7 +83,7 @@ abstract class BaseMenuManager extends BaseSubManager { private OnRPCNotificationListener hmiListener; private OnRPCNotificationListener commandListener; private OnSystemCapabilityListener onDisplaysCapabilityListener; - private WindowCapability defaultMainWindowCapability; + private WindowCapability windowCapability; private Queue transactionQueue; static int lastMenuId; // todo this shouldn't be static and should be fully managed in the manager @@ -117,7 +116,7 @@ abstract class BaseMenuManager extends BaseSubManager { currentHMILevel = HMILevel.HMI_NONE; currentSystemContext = SystemContext.SYSCTXT_MAIN; dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; - defaultMainWindowCapability = null; + windowCapability = null; menuConfiguration = null; sdlMsgVersion = null; @@ -209,9 +208,9 @@ abstract class BaseMenuManager extends BaseSubManager { }; if (isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType)) { - operation = new MenuReplaceDynamicOperation(internalInterface, fileManager.get(), defaultMainWindowCapability, menuConfiguration, currentMenuCells, menuCells, menuManagerCompletionListener); + operation = new MenuReplaceDynamicOperation(internalInterface, fileManager.get(), windowCapability, menuConfiguration, currentMenuCells, menuCells, menuManagerCompletionListener); } else { - operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), defaultMainWindowCapability, menuConfiguration, currentMenuCells, menuCells, menuManagerCompletionListener); + operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), windowCapability, menuConfiguration, currentMenuCells, menuCells, menuManagerCompletionListener); } transactionQueue.add(operation, false); @@ -304,7 +303,7 @@ abstract class BaseMenuManager extends BaseSubManager { return; } - MenuConfigurationUpdateOperation operation = new MenuConfigurationUpdateOperation(internalInterface, defaultMainWindowCapability, menuConfiguration, new CompletionListener() { + MenuConfigurationUpdateOperation operation = new MenuConfigurationUpdateOperation(internalInterface, windowCapability, menuConfiguration, new CompletionListener() { @Override public void onComplete(boolean success) { BaseMenuManager.this.menuConfiguration = menuConfiguration; @@ -348,7 +347,7 @@ abstract class BaseMenuManager extends BaseSubManager { for (WindowCapability windowCapability : display.getWindowCapabilities()) { int currentWindowID = windowCapability.getWindowID() != null ? windowCapability.getWindowID() : PredefinedWindows.DEFAULT_WINDOW.getValue(); if (currentWindowID == PredefinedWindows.DEFAULT_WINDOW.getValue()) { - defaultMainWindowCapability = windowCapability; + BaseMenuManager.this.windowCapability = windowCapability; } } } @@ -357,7 +356,7 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void onError(String info) { DebugTool.logError(TAG, "Display Capability cannot be retrieved"); - defaultMainWindowCapability = null; + windowCapability = null; } }; if (internalInterface.getSystemCapabilityManager() != null) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java index abd66d453..95ed76217 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java @@ -55,10 +55,10 @@ class MenuConfigurationUpdateOperation extends Task { private final MenuConfiguration menuConfiguration; private final CompletionListener completionListener; - MenuConfigurationUpdateOperation(ISdl internalInterface, WindowCapability defaultMainWindowCapability, MenuConfiguration menuConfiguration, CompletionListener completionListener) { + MenuConfigurationUpdateOperation(ISdl internalInterface, WindowCapability windowCapability, MenuConfiguration menuConfiguration, CompletionListener completionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); - this.availableMenuLayouts = defaultMainWindowCapability != null ? defaultMainWindowCapability.getMenuLayoutsAvailable() : null; + this.availableMenuLayouts = windowCapability != null ? windowCapability.getMenuLayoutsAvailable() : null; this.menuConfiguration = menuConfiguration; this.completionListener = completionListener; } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 11cf22039..70e8c6915 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -35,7 +35,7 @@ class MenuReplaceDynamicOperation extends Task { private final WeakReference internalInterface; private final WeakReference fileManager; - private final WindowCapability defaultMainWindowCapability; + private final WindowCapability windowCapability; private List currentMenu; private final List updatedMenu; private List keepsOld; @@ -43,11 +43,11 @@ class MenuReplaceDynamicOperation extends Task { private final MenuManagerCompletionListener operationCompletionListener; private MenuConfiguration menuConfiguration; - MenuReplaceDynamicOperation(ISdl internalInterface, FileManager fileManager, WindowCapability defaultMainWindowCapability, MenuConfiguration menuConfiguration, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { + MenuReplaceDynamicOperation(ISdl internalInterface, FileManager fileManager, WindowCapability windowCapability, MenuConfiguration menuConfiguration, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); - this.defaultMainWindowCapability = defaultMainWindowCapability; + this.windowCapability = windowCapability; this.menuConfiguration = menuConfiguration; this.currentMenu = currentMenu; this.updatedMenu = updatedMenu; @@ -74,7 +74,7 @@ class MenuReplaceDynamicOperation extends Task { private void updateMenuCells(final CompletionListener listener) { // Upload the Artworks - List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), defaultMainWindowCapability); + List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability); if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { @Override @@ -220,8 +220,8 @@ class MenuReplaceDynamicOperation extends Task { MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; - List mainMenuCommands = mainMenuCommandsForCells(menu, fileManager.get(), defaultMainWindowCapability, updatedMenu, defaultSubmenuLayout); - final List subMenuCommands = subMenuCommandsForCells(menu, fileManager.get(), defaultMainWindowCapability, defaultSubmenuLayout); + List mainMenuCommands = mainMenuCommandsForCells(menu, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); + final List subMenuCommands = subMenuCommandsForCells(menu, fileManager.get(), windowCapability, defaultSubmenuLayout); internalInterface.get().sendRPCs(mainMenuCommands, new OnMultipleRequestListener() { @@ -316,7 +316,7 @@ class MenuReplaceDynamicOperation extends Task { List mainMenuCommands; - if (!shouldRPCsIncludeImages(adds, fileManager.get()) || !supportsImages(defaultMainWindowCapability)) { + if (!shouldRPCsIncludeImages(adds, fileManager.get()) || !supportsImages(windowCapability)) { // Send artwork-less menu mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, false); } else { @@ -405,7 +405,7 @@ class MenuReplaceDynamicOperation extends Task { for (int i = 0; i < cells.size(); i++) { MenuCell cell = cells.get(i); if (cell.equals(oldCell)) { - builtCommands.add(commandForMenuCell(cell, fileManager.get(), defaultMainWindowCapability, z)); + builtCommands.add(commandForMenuCell(cell, fileManager.get(), windowCapability, z)); break; } } @@ -628,7 +628,7 @@ class MenuReplaceDynamicOperation extends Task { return true; } - private static boolean supportsImages(WindowCapability windowCapability) { + private boolean supportsImages(WindowCapability windowCapability) { return windowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 2128c0acb..f1db1e7cb 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -31,17 +31,17 @@ class MenuReplaceStaticOperation extends Task { private final WeakReference internalInterface; private final WeakReference fileManager; - private final WindowCapability defaultMainWindowCapability; + private final WindowCapability windowCapability; private List currentMenu; private final List updatedMenu; private final MenuManagerCompletionListener operationCompletionListener; private MenuConfiguration menuConfiguration; - MenuReplaceStaticOperation(ISdl internalInterface, FileManager fileManager, WindowCapability defaultMainWindowCapability, MenuConfiguration menuConfiguration, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { + MenuReplaceStaticOperation(ISdl internalInterface, FileManager fileManager, WindowCapability windowCapability, MenuConfiguration menuConfiguration, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); - this.defaultMainWindowCapability = defaultMainWindowCapability; + this.windowCapability = windowCapability; this.menuConfiguration = menuConfiguration; this.currentMenu = currentMenu; this.updatedMenu = updatedMenu; @@ -67,7 +67,7 @@ class MenuReplaceStaticOperation extends Task { } private void updateMenuCells(final CompletionListener listener) { - List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), defaultMainWindowCapability); + List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability); if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { @Override @@ -125,8 +125,8 @@ class MenuReplaceStaticOperation extends Task { MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; - List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), defaultMainWindowCapability, updatedMenu, defaultSubmenuLayout); - final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), defaultMainWindowCapability, defaultSubmenuLayout); + List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); + final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); internalInterface.get().sendRPCs(mainMenuCommands, new OnMultipleRequestListener() { @Override -- cgit v1.2.1 From 056a744b3a4091afc8bfbe80043310f8551dda54 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 13:56:55 -0500 Subject: Align DynamicMenuUpdateRunScore with iOS --- .../screen/menu/DynamicMenuUpdateAlgorithm.java | 2 +- .../screen/menu/DynamicMenuUpdateRunScore.java | 29 +++++++++++----------- .../screen/menu/MenuReplaceDynamicOperation.java | 8 +++--- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index 1c76e3cc0..7db183e8b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -77,7 +77,7 @@ class DynamicMenuUpdateAlgorithm { // see if we have a new best score and set it if we do if (bestRunScore == null || numberOfAdds < bestRunScore.getScore()) { - bestRunScore = new DynamicMenuUpdateRunScore(numberOfAdds, oldArray, newArray); + bestRunScore = new DynamicMenuUpdateRunScore(oldArray, newArray, numberOfAdds); } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java index 20533b52c..10741ddc8 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java @@ -36,30 +36,31 @@ import java.util.List; import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; class DynamicMenuUpdateRunScore { - private int score; - private List oldMenu, currentMenu; + private List oldStatus; // Will contain all the Deletes and Keeps + private List updatedStatus; // Will contain all the Adds and Keeps + private int score; // Will contain the score, number of total Adds that will need to be created - - DynamicMenuUpdateRunScore(int score, List oldMenu, List currentMenu) { + DynamicMenuUpdateRunScore(List oldStatus, List updatedStatus, int score) { + setOldStatus(oldStatus); + setUpdatedStatus(updatedStatus); setScore(score); - setOldMenu(oldMenu); - setCurrentMenu(currentMenu); + } - private void setCurrentMenu(List currentMenu) { - this.currentMenu = currentMenu; + private void setUpdatedStatus(List updatedStatus) { + this.updatedStatus = updatedStatus; } - List getCurrentMenu() { - return currentMenu; + List getUpdatedStatus() { + return updatedStatus; } - private void setOldMenu(List oldMenu) { - this.oldMenu = oldMenu; + private void setOldStatus(List oldStatus) { + this.oldStatus = oldStatus; } - List getOldMenu() { - return oldMenu; + List getOldStatus() { + return oldStatus; } private void setScore(int score) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 70e8c6915..053cfae52 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -139,8 +139,8 @@ class MenuReplaceDynamicOperation extends Task { private void dynamicallyUpdateRootMenu(DynamicMenuUpdateRunScore bestRootScore, CompletionListener listener) { // We need to run through the keeps and see if they have subCells, as they also need to be run through the compare function. - List newStatesArray = bestRootScore.getCurrentMenu(); - List oldStatesArray = bestRootScore.getOldMenu(); + List newStatesArray = bestRootScore.getUpdatedStatus(); + List oldStatesArray = bestRootScore.getOldStatus(); List deleteCommands; // Set up deletes @@ -479,8 +479,8 @@ class MenuReplaceDynamicOperation extends Task { // grab the scores DynamicMenuUpdateRunScore score = commandList.getListsScore(); - List newStates = score.getCurrentMenu(); - List oldStates = score.getOldMenu(); + List newStates = score.getUpdatedStatus(); + List oldStates = score.getOldStatus(); // Grab the sub-menus from the parent cell final List oldCells = commandList.getOldList(); -- cgit v1.2.1 From 2cc08c3ae6803cc47778c0658c96803d7520a489 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 14:35:19 -0500 Subject: Extract duplicate code into helper methods --- .../screen/menu/MenuReplaceDynamicOperation.java | 133 +++++++++++---------- 1 file changed, 73 insertions(+), 60 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 053cfae52..3cb2b89dd 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -38,8 +38,8 @@ class MenuReplaceDynamicOperation extends Task { private final WindowCapability windowCapability; private List currentMenu; private final List updatedMenu; - private List keepsOld; - private List keepsNew; + private List oldKeeps; + private List newKeeps; private final MenuManagerCompletionListener operationCompletionListener; private MenuConfiguration menuConfiguration; @@ -71,7 +71,7 @@ class MenuReplaceDynamicOperation extends Task { } }); } - + private void updateMenuCells(final CompletionListener listener) { // Upload the Artworks List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability); @@ -137,46 +137,74 @@ class MenuReplaceDynamicOperation extends Task { }); } + private List filterDeleteMenuItemsWithOldMenuItems(List oldMenuCells, List oldStatusList){ + List deleteCells = new ArrayList<>(); + // The index of the status should correlate 1-1 with the number of items in the menu + // [KEEP,DELETE,KEEP,DELETE] => [A,B,C,D] = [B,D] + for (int index = 0; index < oldStatusList.size(); index++) { + if (oldStatusList.get(index).equals(MenuCellState.DELETE)) { + deleteCells.add(oldMenuCells.get(index)); + } + } + return deleteCells; + } + + private List filterAddMenuItemsWithNewMenuItems(List newMenuCells, List newStatusList){ + List addCells = new ArrayList<>(); + // The index of the status should correlate 1-1 with the number of items in the menu + // [KEEP,ADD,KEEP,ADD] => [A,B,C,D] = [B,D] + for (int index = 0; index < newStatusList.size(); index++) { + if (newStatusList.get(index).equals(MenuCellState.ADD)) { + addCells.add(newMenuCells.get(index)); + } + } + return addCells; + } + + private List filterKeepMenuItemsWithOldMenuItems(List oldMenuCells, List keepStatusList){ + List keepMenuCells = new ArrayList<>(); + for (int index = 0; index < keepStatusList.size(); index++) { + if (keepStatusList.get(index).equals(MenuCellState.KEEP)) { + keepMenuCells.add(oldMenuCells.get(index)); + } + } + return keepMenuCells; + } + + private List filterKeepMenuItemsWithNewMenuItems(List newMenuCells, List keepStatusList){ + List keepMenuCells = new ArrayList<>(); + for (int index = 0; index < keepStatusList.size(); index++) { + if (keepStatusList.get(index).equals(MenuCellState.KEEP)) { + keepMenuCells.add(newMenuCells.get(index)); + } + } + return keepMenuCells; + } + private void dynamicallyUpdateRootMenu(DynamicMenuUpdateRunScore bestRootScore, CompletionListener listener) { // We need to run through the keeps and see if they have subCells, as they also need to be run through the compare function. - List newStatesArray = bestRootScore.getUpdatedStatus(); - List oldStatesArray = bestRootScore.getOldStatus(); + List deleteMenuStatus = bestRootScore.getOldStatus(); + List addMenuStatus = bestRootScore.getUpdatedStatus(); List deleteCommands; // Set up deletes - List deletes = new ArrayList<>(); - keepsOld = new ArrayList<>(); - for (int x = 0; x < oldStatesArray.size(); x++) { - MenuCellState old = oldStatesArray.get(x); - if (old.equals(MenuCellState.DELETE)) { - // grab cell to send to function to create delete commands - deletes.add(currentMenu.get(x)); - } else if (old.equals(MenuCellState.KEEP)) { - keepsOld.add(currentMenu.get(x)); - } - } + List cellsToDelete = filterDeleteMenuItemsWithOldMenuItems(currentMenu, deleteMenuStatus); + oldKeeps = filterKeepMenuItemsWithOldMenuItems(currentMenu, deleteMenuStatus); + // create the delete commands - deleteCommands = deleteCommandsForCells(deletes); + deleteCommands = deleteCommandsForCells(cellsToDelete); // Set up the adds - List adds = new ArrayList<>(); - keepsNew = new ArrayList<>(); - for (int x = 0; x < newStatesArray.size(); x++) { - MenuCellState newState = newStatesArray.get(x); - if (newState.equals(MenuCellState.ADD)) { - // grab cell to send to function to create add commands - adds.add(updatedMenu.get(x)); - } else if (newState.equals(MenuCellState.KEEP)) { - keepsNew.add(updatedMenu.get(x)); - } - } - updateIdsOnDynamicCells(adds); + List cellsToAdd = filterAddMenuItemsWithNewMenuItems(updatedMenu, addMenuStatus); + newKeeps = filterKeepMenuItemsWithNewMenuItems(updatedMenu, addMenuStatus); + + updateIdsOnDynamicCells(cellsToAdd); // this is needed for the onCommands to still work - transferIdsToKeptCells(keepsNew); + transferIdsToKeptCells(newKeeps); - if (!adds.isEmpty()) { // todo what if adds was empty but deletes was not? + if (!cellsToAdd.isEmpty()) { // todo what if adds was empty but deletes was not? DebugTool.logInfo(TAG, "Sending root menu updates"); - sendDynamicRootMenuRPCs(deleteCommands, adds, listener); + sendDynamicRootMenuRPCs(deleteCommands, cellsToAdd, listener); } else { DebugTool.logInfo(TAG, "All root menu items are kept. Check the sub menus"); runSubMenuCompareAlgorithm(listener); @@ -235,7 +263,7 @@ class MenuReplaceDynamicOperation extends Task { DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); sendSubMenuCommandRPCs(subMenuCommands, listener); } else { - if (keepsNew != null && !keepsNew.isEmpty()) { + if (newKeeps != null && !newKeeps.isEmpty()) { runSubMenuCompareAlgorithm(listener); } else { DebugTool.logInfo(TAG, "Finished sending main menu commands."); @@ -274,7 +302,7 @@ class MenuReplaceDynamicOperation extends Task { @Override public void onFinished() { - if (keepsNew != null && !keepsNew.isEmpty()) { + if (newKeeps != null && !newKeeps.isEmpty()) { runSubMenuCompareAlgorithm(listener); } else { DebugTool.logInfo(TAG, "Finished Updating Menu"); @@ -438,16 +466,16 @@ class MenuReplaceDynamicOperation extends Task { private void runSubMenuCompareAlgorithm(CompletionListener listener) { // any cells that were re-added have their sub-cells added with them // at this point all we care about are the cells that were deemed equal and kept. - if (keepsNew == null || keepsNew.isEmpty()) { + if (newKeeps == null || newKeeps.isEmpty()) { listener.onComplete(true); return; } List commandLists = new ArrayList<>(); - for (int i = 0; i < keepsNew.size(); i++) { - MenuCell newKeptCell = keepsNew.get(i); - MenuCell oldKeptCell = keepsOld.get(i); + for (int i = 0; i < newKeeps.size(); i++) { + MenuCell newKeptCell = newKeeps.get(i); + MenuCell oldKeptCell = oldKeeps.get(i); if (oldKeptCell.getSubCells() != null && !oldKeptCell.getSubCells().isEmpty() && newKeptCell.getSubCells() != null && !newKeptCell.getSubCells().isEmpty()) { DynamicMenuUpdateRunScore subScore = DynamicMenuUpdateAlgorithm.startCompareAtRun(oldKeptCell.getSubCells(), newKeptCell.getSubCells()); @@ -486,31 +514,16 @@ class MenuReplaceDynamicOperation extends Task { final List oldCells = commandList.getOldList(); final List newCells = commandList.getNewList(); - // Set up deletes - List deletes = new ArrayList<>(); - for (int x = 0; x < oldStates.size(); x++) { - MenuCellState old = oldStates.get(x); - if (old.equals(MenuCellState.DELETE)) { - // grab cell to send to function to create delete commands - deletes.add(oldCells.get(x)); - } - } + List cellsToDelete = filterDeleteMenuItemsWithOldMenuItems(currentMenu, oldStates); + // create the delete commands - List deleteCommands = deleteCommandsForCells(deletes); + List deleteCommands = deleteCommandsForCells(cellsToDelete); // Set up the adds - List adds = new ArrayList<>(); - List subCellKeepsNew = new ArrayList<>(); - for (int x = 0; x < newStates.size(); x++) { - MenuCellState newState = newStates.get(x); - if (newState.equals(MenuCellState.ADD)) { - // grab cell to send to function to create add commands - adds.add(newCells.get(x)); - } else if (newState.equals(MenuCellState.KEEP)) { - subCellKeepsNew.add(newCells.get(x)); - } - } - final List addsWithNewIds = updateIdsOnDynamicSubCells(oldCells, adds, commandList.getParentId()); + List cellsToAdd = filterAddMenuItemsWithNewMenuItems(updatedMenu, newStates); + List subCellKeepsNew = filterKeepMenuItemsWithNewMenuItems(updatedMenu, newStates); + + final List addsWithNewIds = updateIdsOnDynamicSubCells(oldCells, cellsToAdd, commandList.getParentId()); // this is needed for the onCommands to still work transferIdsToKeptSubCells(oldCells, subCellKeepsNew); -- cgit v1.2.1 From 41aaf544647968d68644540155c2ee127df1f835 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 14:47:37 -0500 Subject: Refactor MenuReplaceDynamicOperation --- .../screen/menu/MenuReplaceDynamicOperation.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 3cb2b89dd..9afaa01ad 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -185,24 +185,23 @@ class MenuReplaceDynamicOperation extends Task { // We need to run through the keeps and see if they have subCells, as they also need to be run through the compare function. List deleteMenuStatus = bestRootScore.getOldStatus(); List addMenuStatus = bestRootScore.getUpdatedStatus(); - List deleteCommands; - // Set up deletes List cellsToDelete = filterDeleteMenuItemsWithOldMenuItems(currentMenu, deleteMenuStatus); - oldKeeps = filterKeepMenuItemsWithOldMenuItems(currentMenu, deleteMenuStatus); - - // create the delete commands - deleteCommands = deleteCommandsForCells(cellsToDelete); - - // Set up the adds List cellsToAdd = filterAddMenuItemsWithNewMenuItems(updatedMenu, addMenuStatus); + + // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares + oldKeeps = filterKeepMenuItemsWithOldMenuItems(currentMenu, deleteMenuStatus); newKeeps = filterKeepMenuItemsWithNewMenuItems(updatedMenu, addMenuStatus); + List deleteCommands = deleteCommandsForCells(cellsToDelete); + updateIdsOnDynamicCells(cellsToAdd); + + // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. // this is needed for the onCommands to still work transferIdsToKeptCells(newKeeps); - if (!cellsToAdd.isEmpty()) { // todo what if adds was empty but deletes was not? + if (!cellsToAdd.isEmpty()) { DebugTool.logInfo(TAG, "Sending root menu updates"); sendDynamicRootMenuRPCs(deleteCommands, cellsToAdd, listener); } else { -- cgit v1.2.1 From 397235f0a2c55089862a1e8ec819c6a7487e1047 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 14:53:38 -0500 Subject: Refactor MenuReplaceDynamicOperation --- .../screen/menu/MenuReplaceDynamicOperation.java | 37 ++++++++++------------ 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 9afaa01ad..418396378 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -25,7 +25,11 @@ import java.util.Map; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.lastMenuId; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.*; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandForMenuCell; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; /** * Created by Bilal Alsharifi on 1/20/21. @@ -123,7 +127,7 @@ class MenuReplaceDynamicOperation extends Task { return; } - sendDeleteRPCs(deleteCommandsForCells(currentMenu), new CompletionListener() { + sendDeleteCurrentMenu(currentMenu, new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { @@ -193,8 +197,6 @@ class MenuReplaceDynamicOperation extends Task { oldKeeps = filterKeepMenuItemsWithOldMenuItems(currentMenu, deleteMenuStatus); newKeeps = filterKeepMenuItemsWithNewMenuItems(updatedMenu, addMenuStatus); - List deleteCommands = deleteCommandsForCells(cellsToDelete); - updateIdsOnDynamicCells(cellsToAdd); // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. @@ -203,7 +205,7 @@ class MenuReplaceDynamicOperation extends Task { if (!cellsToAdd.isEmpty()) { DebugTool.logInfo(TAG, "Sending root menu updates"); - sendDynamicRootMenuRPCs(deleteCommands, cellsToAdd, listener); + sendDynamicRootMenuRPCs(cellsToDelete, cellsToAdd, listener); } else { DebugTool.logInfo(TAG, "All root menu items are kept. Check the sub menus"); runSubMenuCompareAlgorithm(listener); @@ -386,25 +388,23 @@ class MenuReplaceDynamicOperation extends Task { listener.onComplete(true); } } else { - sendDeleteRPCs(deleteCommandsForCells(currentMenu), listener); + sendDeleteCurrentMenu(currentMenu, listener); } } - private void sendDeleteRPCs(List deleteCommands, final CompletionListener listener) { + private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } - if (deleteCommands == null || deleteCommands.isEmpty()) { - // no dynamic deletes required. return - if (listener != null) { - // technically this method is successful if there's nothing to delete - listener.onComplete(true); - } + if (deleteMenuCells.isEmpty()) { + listener.onComplete(true); return; } - internalInterface.get().sendRPCs(deleteCommands, new OnMultipleRequestListener() { + List deleteMenuCommands = deleteCommandsForCells(deleteMenuCells); + + internalInterface.get().sendRPCs(deleteMenuCommands, new OnMultipleRequestListener() { @Override public void onUpdate(int remainingRequests) { @@ -440,12 +440,12 @@ class MenuReplaceDynamicOperation extends Task { return builtCommands; } - private void sendDynamicRootMenuRPCs(List deleteCommands, final List updatedCells, final CompletionListener listener) { + private void sendDynamicRootMenuRPCs(List deleteMenuCells, final List updatedCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } - sendDeleteRPCs(deleteCommands, new CompletionListener() { + sendDeleteCurrentMenu(deleteMenuCells, new CompletionListener() { @Override public void onComplete(boolean success) { createAndSendMenuCellRPCs(updatedCells, new CompletionListener() { @@ -515,9 +515,6 @@ class MenuReplaceDynamicOperation extends Task { List cellsToDelete = filterDeleteMenuItemsWithOldMenuItems(currentMenu, oldStates); - // create the delete commands - List deleteCommands = deleteCommandsForCells(cellsToDelete); - // Set up the adds List cellsToAdd = filterAddMenuItemsWithNewMenuItems(updatedMenu, newStates); List subCellKeepsNew = filterKeepMenuItemsWithNewMenuItems(updatedMenu, newStates); @@ -526,7 +523,7 @@ class MenuReplaceDynamicOperation extends Task { // this is needed for the onCommands to still work transferIdsToKeptSubCells(oldCells, subCellKeepsNew); - sendDeleteRPCs(deleteCommands, new CompletionListener() { + sendDeleteCurrentMenu(cellsToDelete, new CompletionListener() { @Override public void onComplete(boolean success) { if (addsWithNewIds != null && !addsWithNewIds.isEmpty()) { -- cgit v1.2.1 From 41b962df4a30d9b4b89ad72435b3308085a880bc Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 15:00:41 -0500 Subject: Refactor MenuReplaceDynamicOperation --- .../screen/menu/MenuReplaceDynamicOperation.java | 23 ++++++++++------------ 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 418396378..06fc74111 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -220,7 +220,7 @@ class MenuReplaceDynamicOperation extends Task { deleteRootMenu(new CompletionListener() { @Override public void onComplete(boolean success) { - createAndSendMenuCellRPCs(updatedMenu, new CompletionListener() { + sendNewMenuCells(updatedMenu, new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { @@ -234,24 +234,21 @@ class MenuReplaceDynamicOperation extends Task { }); } - private void createAndSendMenuCellRPCs(final List menu, final CompletionListener listener) { + private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } - if (menu == null || menu.isEmpty()) { - if (listener != null) { - // This can be considered a success if the user was clearing out their menu - listener.onComplete(true); - } + if (newMenuCells == null || newMenuCells.isEmpty()) { + // This can be considered a success if the user was clearing out their menu + listener.onComplete(true); return; } MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; - List mainMenuCommands = mainMenuCommandsForCells(menu, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); - final List subMenuCommands = subMenuCommandsForCells(menu, fileManager.get(), windowCapability, defaultSubmenuLayout); - + List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); + final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); internalInterface.get().sendRPCs(mainMenuCommands, new OnMultipleRequestListener() { @Override @@ -262,7 +259,7 @@ class MenuReplaceDynamicOperation extends Task { public void onFinished() { if (!subMenuCommands.isEmpty()) { DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); - sendSubMenuCommandRPCs(subMenuCommands, listener); + sendNewSubMenuCells(subMenuCommands, listener); } else { if (newKeeps != null && !newKeeps.isEmpty()) { runSubMenuCompareAlgorithm(listener); @@ -291,7 +288,7 @@ class MenuReplaceDynamicOperation extends Task { }); } - private void sendSubMenuCommandRPCs(List commands, final CompletionListener listener) { + private void sendNewSubMenuCells(List commands, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } @@ -448,7 +445,7 @@ class MenuReplaceDynamicOperation extends Task { sendDeleteCurrentMenu(deleteMenuCells, new CompletionListener() { @Override public void onComplete(boolean success) { - createAndSendMenuCellRPCs(updatedCells, new CompletionListener() { + sendNewMenuCells(updatedCells, new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { -- cgit v1.2.1 From c9d32db74cac522e2f249243fd470daa33332628 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 15:14:22 -0500 Subject: Cleanup image handling code in MenuReplaceDynamicOperation --- .../screen/menu/MenuReplaceDynamicOperation.java | 27 ++-------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 06fc74111..ce6fff8c9 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -340,14 +340,7 @@ class MenuReplaceDynamicOperation extends Task { return; } - List mainMenuCommands; - - if (!shouldRPCsIncludeImages(adds, fileManager.get()) || !supportsImages(windowCapability)) { - // Send artwork-less menu - mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, false); - } else { - mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds, true); - } + List mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds); internalInterface.get().sendRPCs(mainMenuCommands, new OnMultipleRequestListener() { @Override @@ -422,7 +415,7 @@ class MenuReplaceDynamicOperation extends Task { }); } - private List createCommandsForDynamicSubCells(List oldMenuCells, List cells, boolean shouldHaveArtwork) { + private List createCommandsForDynamicSubCells(List oldMenuCells, List cells) { List builtCommands = new ArrayList<>(); for (int z = 0; z < oldMenuCells.size(); z++) { MenuCell oldCell = oldMenuCells.get(z); @@ -622,22 +615,6 @@ class MenuReplaceDynamicOperation extends Task { } } - private boolean shouldRPCsIncludeImages(List cells, FileManager fileManager) { - for (MenuCell cell : cells) { - SdlArtwork artwork = cell.getIcon(); - if (artwork != null && !artwork.isStaticIcon() && fileManager != null && !fileManager.hasUploadedFile(artwork)) { - return false; - } else if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - return shouldRPCsIncludeImages(cell.getSubCells(), fileManager); - } - } - return true; - } - - private boolean supportsImages(WindowCapability windowCapability) { - return windowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); - } - void setMenuConfiguration(MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } -- cgit v1.2.1 From c33e8ad58853c5fa8d0928b1758a222fd5cbc413 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 15:38:01 -0500 Subject: Refactor DynamicMenuUpdateAlgorithm --- .../screen/menu/DynamicMenuUpdateAlgorithm.java | 98 +++++++++++++--------- .../screen/menu/MenuReplaceDynamicOperation.java | 2 +- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index 7db183e8b..3141c2ae8 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -13,14 +13,9 @@ class DynamicMenuUpdateAlgorithm { // Cell state that tells the menu manager what it should do with a given SDLMenuCell enum MenuCellState { - // Marks the cell to be deleted - DELETE, - - // Marks the cell to be added - ADD, - - // Marks the cell to be kept - KEEP + DELETE, // Marks the cell to be deleted + ADD, // Marks the cell to be added + KEEP // Marks the cell to be kept } static DynamicMenuUpdateRunScore compareOldMenuCells(List oldMenuCells, List updatedMenuCells) { @@ -28,59 +23,80 @@ class DynamicMenuUpdateAlgorithm { return null; } - DynamicMenuUpdateRunScore bestScore = startCompareAtRun(oldMenuCells, updatedMenuCells); + DynamicMenuUpdateRunScore bestScore = startCompareAtRun(0, oldMenuCells, updatedMenuCells); DebugTool.logInfo(TAG, "Best menu run score: " + bestScore.getScore()); return bestScore; } - static DynamicMenuUpdateRunScore startCompareAtRun(List oldMenuCells, List updatedMenuCells) { - DynamicMenuUpdateRunScore bestRunScore = null; - - // This first loop is for each 'run' - for (int run = 0; run < oldMenuCells.size(); run++) { - List oldArray = new ArrayList<>(oldMenuCells.size()); - List newArray = new ArrayList<>(updatedMenuCells.size()); + static DynamicMenuUpdateRunScore startCompareAtRun(int startRun, List oldMenuCells, List updatedMenuCells) { + DynamicMenuUpdateRunScore bestScore = null; - // Set the statuses - for (int i = 0; i < oldMenuCells.size(); i++) { - oldArray.add(MenuCellState.DELETE); - } - for (int i = 0; i < updatedMenuCells.size(); i++) { - newArray.add(MenuCellState.ADD); - } + for (int run = startRun; run < oldMenuCells.size(); run++) { + // Set the menu status as a 1-1 array, start off will oldMenus = all Deletes, newMenu = all Adds + List oldMenuStatus = buildAllDeleteStatusesForMenu(oldMenuCells); + List newMenuStatus = buildAllAddStatusesForMenu(updatedMenuCells); int startIndex = 0; - - // Keep items that appear in both lists - for (int oldItems = run; oldItems < oldMenuCells.size(); oldItems++) { - - for (int newItems = startIndex; newItems < updatedMenuCells.size(); newItems++) { - - if (oldMenuCells.get(oldItems).equals(updatedMenuCells.get(newItems))) { - oldArray.set(oldItems, MenuCellState.KEEP); - newArray.set(newItems, MenuCellState.KEEP); - // set the new start index - startIndex = newItems + 1; + for (int oldCellIndex = run; oldCellIndex < oldMenuCells.size(); oldCellIndex++) { + // For each old item, create inner loop to compare old cells to new cells to find a match + // if a match if found we mark the index at match for both the old and the new status to + // keep since we do not want to send RPCs for those cases + for (int newCellIndex = startIndex; newCellIndex < updatedMenuCells.size(); newCellIndex++) { + if (oldMenuCells.get(oldCellIndex).equals(updatedMenuCells.get(newCellIndex))) { + oldMenuStatus.set(oldCellIndex, MenuCellState.KEEP); + newMenuStatus.set(newCellIndex, MenuCellState.KEEP); + startIndex = newCellIndex + 1; break; } } } - // Calculate number of adds, or the 'score' for this run + // // Add RPC are the biggest operation so we need to find the run with the least amount of Adds. + // We will reset the run we use each time a runScore is less than the current score. int numberOfAdds = 0; - for (int x = 0; x < newArray.size(); x++) { - if (newArray.get(x).equals(MenuCellState.ADD)) { + for (int status = 0; status < newMenuStatus.size(); status++) { + if (newMenuStatus.get(status).equals(MenuCellState.ADD)) { numberOfAdds++; } } - // see if we have a new best score and set it if we do - if (bestRunScore == null || numberOfAdds < bestRunScore.getScore()) { - bestRunScore = new DynamicMenuUpdateRunScore(oldArray, newArray, numberOfAdds); + // As soon as we a run that requires 0 Adds we will use it since we cant do better then 0 + if (numberOfAdds == 0) { + bestScore = new DynamicMenuUpdateRunScore(oldMenuStatus, newMenuStatus, numberOfAdds); + return bestScore; } + // if we haven't create the bestScore object or if the current score beats the old score then we will create a new bestScore + if (bestScore == null || numberOfAdds < bestScore.getScore()) { + bestScore = new DynamicMenuUpdateRunScore(oldMenuStatus, newMenuStatus, numberOfAdds); + } + + } + return bestScore; + } + + /** + * Builds a 1-1 array of Deletes for every element in the array + * @param oldMenu The old menu array + */ + private static List buildAllDeleteStatusesForMenu (List oldMenu){ + List oldMenuStatus = new ArrayList<>(oldMenu.size()); + for (int index = 0; index < oldMenu.size(); index++) { + oldMenuStatus.add(MenuCellState.DELETE); + } + return oldMenuStatus; + } + + /** + * Builds a 1-1 array of Adds for every element in the array + * @param newMenu The new menu array + */ + private static List buildAllAddStatusesForMenu (List newMenu){ + List newMenuStatus = new ArrayList<>(newMenu.size()); + for (int index = 0; index < newMenu.size(); index++) { + newMenuStatus.add(MenuCellState.ADD); } - return bestRunScore; + return newMenuStatus; } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index ce6fff8c9..555056439 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -467,7 +467,7 @@ class MenuReplaceDynamicOperation extends Task { MenuCell oldKeptCell = oldKeeps.get(i); if (oldKeptCell.getSubCells() != null && !oldKeptCell.getSubCells().isEmpty() && newKeptCell.getSubCells() != null && !newKeptCell.getSubCells().isEmpty()) { - DynamicMenuUpdateRunScore subScore = DynamicMenuUpdateAlgorithm.startCompareAtRun(oldKeptCell.getSubCells(), newKeptCell.getSubCells()); + DynamicMenuUpdateRunScore subScore = DynamicMenuUpdateAlgorithm.startCompareAtRun(0, oldKeptCell.getSubCells(), newKeptCell.getSubCells()); if (subScore != null) { DebugTool.logInfo(TAG, "Sub menu Run Score: " + oldKeptCell.getTitle() + " Score: " + subScore.getScore()); -- cgit v1.2.1 From 08914d30d62263e8adb09f0680a54eae67460fa1 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 15:51:58 -0500 Subject: Update compareOldMenuCells() --- .../managers/screen/menu/DynamicMenuUpdateAlgorithm.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index 3141c2ae8..9ad5f3a34 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -19,14 +19,14 @@ class DynamicMenuUpdateAlgorithm { } static DynamicMenuUpdateRunScore compareOldMenuCells(List oldMenuCells, List updatedMenuCells) { - if (oldMenuCells == null || oldMenuCells.isEmpty()) { + if (!oldMenuCells.isEmpty() && updatedMenuCells.isEmpty()) { + return new DynamicMenuUpdateRunScore(buildAllDeleteStatusesForMenu(oldMenuCells), new ArrayList(), 0); + } else if (oldMenuCells.isEmpty() && !updatedMenuCells.isEmpty()){ + return new DynamicMenuUpdateRunScore(new ArrayList(), buildAllAddStatusesForMenu(updatedMenuCells), updatedMenuCells.size()); + } else if (oldMenuCells.isEmpty()){ return null; } - - DynamicMenuUpdateRunScore bestScore = startCompareAtRun(0, oldMenuCells, updatedMenuCells); - DebugTool.logInfo(TAG, "Best menu run score: " + bestScore.getScore()); - - return bestScore; + return startCompareAtRun(0, oldMenuCells, updatedMenuCells); } static DynamicMenuUpdateRunScore startCompareAtRun(int startRun, List oldMenuCells, List updatedMenuCells) { -- cgit v1.2.1 From d23612683bfeb232fc86f892c666c585db253058 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 16:45:15 -0500 Subject: Remove unsed imports --- .../managers/screen/menu/DynamicMenuUpdateAlgorithm.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index 9ad5f3a34..d94ed5d3d 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -1,7 +1,5 @@ package com.smartdevicelink.managers.screen.menu; -import com.smartdevicelink.util.DebugTool; - import java.util.ArrayList; import java.util.List; @@ -9,8 +7,6 @@ import java.util.List; * Created by Bilal Alsharifi on 1/25/21. */ class DynamicMenuUpdateAlgorithm { - private static final String TAG = "DynamicMenuUpdateAlgorithm"; - // Cell state that tells the menu manager what it should do with a given SDLMenuCell enum MenuCellState { DELETE, // Marks the cell to be deleted -- cgit v1.2.1 From 96f7480a271bbe510739efa44964ad1b67a31aa6 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 16:59:52 -0500 Subject: Replace all filter methods with one generic method --- .../screen/menu/MenuReplaceDynamicOperation.java | 62 +++++----------------- 1 file changed, 13 insertions(+), 49 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 555056439..0bd3d4758 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -3,7 +3,6 @@ package com.smartdevicelink.managers.screen.menu; import com.livio.taskmaster.Task; 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; @@ -11,7 +10,6 @@ import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuC import com.smartdevicelink.proxy.RPCRequest; import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.WindowCapability; -import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.util.DebugTool; @@ -141,48 +139,14 @@ class MenuReplaceDynamicOperation extends Task { }); } - private List filterDeleteMenuItemsWithOldMenuItems(List oldMenuCells, List oldStatusList){ - List deleteCells = new ArrayList<>(); - // The index of the status should correlate 1-1 with the number of items in the menu - // [KEEP,DELETE,KEEP,DELETE] => [A,B,C,D] = [B,D] - for (int index = 0; index < oldStatusList.size(); index++) { - if (oldStatusList.get(index).equals(MenuCellState.DELETE)) { - deleteCells.add(oldMenuCells.get(index)); + private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState){ + List filteredCells = new ArrayList<>(); + for (int index = 0; index < statusList.size(); index++) { + if (statusList.get(index).equals(menuCellState)) { + filteredCells.add(menuCells.get(index)); } } - return deleteCells; - } - - private List filterAddMenuItemsWithNewMenuItems(List newMenuCells, List newStatusList){ - List addCells = new ArrayList<>(); - // The index of the status should correlate 1-1 with the number of items in the menu - // [KEEP,ADD,KEEP,ADD] => [A,B,C,D] = [B,D] - for (int index = 0; index < newStatusList.size(); index++) { - if (newStatusList.get(index).equals(MenuCellState.ADD)) { - addCells.add(newMenuCells.get(index)); - } - } - return addCells; - } - - private List filterKeepMenuItemsWithOldMenuItems(List oldMenuCells, List keepStatusList){ - List keepMenuCells = new ArrayList<>(); - for (int index = 0; index < keepStatusList.size(); index++) { - if (keepStatusList.get(index).equals(MenuCellState.KEEP)) { - keepMenuCells.add(oldMenuCells.get(index)); - } - } - return keepMenuCells; - } - - private List filterKeepMenuItemsWithNewMenuItems(List newMenuCells, List keepStatusList){ - List keepMenuCells = new ArrayList<>(); - for (int index = 0; index < keepStatusList.size(); index++) { - if (keepStatusList.get(index).equals(MenuCellState.KEEP)) { - keepMenuCells.add(newMenuCells.get(index)); - } - } - return keepMenuCells; + return filteredCells; } private void dynamicallyUpdateRootMenu(DynamicMenuUpdateRunScore bestRootScore, CompletionListener listener) { @@ -190,12 +154,12 @@ class MenuReplaceDynamicOperation extends Task { List deleteMenuStatus = bestRootScore.getOldStatus(); List addMenuStatus = bestRootScore.getUpdatedStatus(); - List cellsToDelete = filterDeleteMenuItemsWithOldMenuItems(currentMenu, deleteMenuStatus); - List cellsToAdd = filterAddMenuItemsWithNewMenuItems(updatedMenu, addMenuStatus); + List cellsToDelete = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.DELETE); + List cellsToAdd = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.ADD); // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares - oldKeeps = filterKeepMenuItemsWithOldMenuItems(currentMenu, deleteMenuStatus); - newKeeps = filterKeepMenuItemsWithNewMenuItems(updatedMenu, addMenuStatus); + oldKeeps = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.KEEP); + newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); updateIdsOnDynamicCells(cellsToAdd); @@ -503,11 +467,11 @@ class MenuReplaceDynamicOperation extends Task { final List oldCells = commandList.getOldList(); final List newCells = commandList.getNewList(); - List cellsToDelete = filterDeleteMenuItemsWithOldMenuItems(currentMenu, oldStates); + List cellsToDelete = filterMenuCellsWithStatusList(currentMenu, oldStates, MenuCellState.DELETE); // Set up the adds - List cellsToAdd = filterAddMenuItemsWithNewMenuItems(updatedMenu, newStates); - List subCellKeepsNew = filterKeepMenuItemsWithNewMenuItems(updatedMenu, newStates); + List cellsToAdd = filterMenuCellsWithStatusList(updatedMenu, newStates, MenuCellState.ADD); + List subCellKeepsNew = filterMenuCellsWithStatusList(updatedMenu, newStates, MenuCellState.KEEP); final List addsWithNewIds = updateIdsOnDynamicSubCells(oldCells, cellsToAdd, commandList.getParentId()); // this is needed for the onCommands to still work -- cgit v1.2.1 From d606ebbe160cea3e319723c5c134cdc59c385010 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Jan 2021 17:04:45 -0500 Subject: Remove duplicate code --- .../managers/screen/menu/MenuReplaceDynamicOperation.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 0bd3d4758..8c3edcc72 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -525,16 +525,12 @@ class MenuReplaceDynamicOperation extends Task { MenuCell mainCell = oldList.get(z); for (int i = 0; i < dynamicCells.size(); i++) { MenuCell dynamicCell = dynamicCells.get(i); + int newId = ++lastMenuId; if (mainCell.equals(dynamicCell)) { - int newId = ++lastMenuId; oldList.get(z).setCellId(newId); - dynamicCells.get(i).setParentCellId(parentId); - dynamicCells.get(i).setCellId(newId); - } else { - int newId = ++lastMenuId; - dynamicCells.get(i).setParentCellId(parentId); - dynamicCells.get(i).setCellId(newId); } + dynamicCells.get(i).setParentCellId(parentId); + dynamicCells.get(i).setCellId(newId); } } return dynamicCells; -- cgit v1.2.1 From 467c29b7d119e9dd2dac2f32f42df0ae4a66fb55 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 28 Jan 2021 10:45:09 -0500 Subject: Add some helper methods to MenuReplaceUtilities --- .../managers/screen/menu/BaseMenuManager.java | 2 +- .../screen/menu/MenuReplaceStaticOperation.java | 72 +++++++++++----------- .../managers/screen/menu/MenuReplaceUtilities.java | 29 +++++++++ 3 files changed, 66 insertions(+), 37 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index a5a2535b2..1fe34e36a 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -65,7 +65,7 @@ import java.util.List; abstract class BaseMenuManager extends BaseSubManager { private static final String TAG = "BaseMenuManager"; - private static final int MAX_ID = 2000000000; + private static final int MAX_ID = Integer.MAX_VALUE; static final int menuCellIdMin = 1; static final int parentIdNotFound = MAX_ID; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index f1db1e7cb..d5fc2a232 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -113,6 +113,42 @@ class MenuReplaceStaticOperation extends Task { }); } + private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { + if (deleteMenuCells == null || deleteMenuCells.isEmpty()) { + // Technically this method is successful if there's nothing to delete + DebugTool.logInfo(TAG, "No old cells to delete, returning"); + listener.onComplete(true); + return; + } + List deleteMenuCommands = deleteCommandsForCells(deleteMenuCells); + + if (deleteMenuCommands.isEmpty()) { + // Technically this method is successful if there's nothing to delete + listener.onComplete(true); + return; + } + + internalInterface.get().sendRPCs(deleteMenuCommands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + + } + + @Override + public void onFinished() { + DebugTool.logInfo(TAG, "Successfully deleted cells"); + if (listener != null) { + listener.onComplete(true); + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + + } + }); + } + private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { if (newMenuCells == null || newMenuCells.isEmpty()) { // This can be considered a success if the user was clearing out their menu @@ -196,42 +232,6 @@ class MenuReplaceStaticOperation extends Task { }); } - private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { - if (deleteMenuCells == null || deleteMenuCells.isEmpty()) { - // Technically this method is successful if there's nothing to delete - DebugTool.logInfo(TAG, "No old cells to delete, returning"); - listener.onComplete(true); - return; - } - List deleteCommands = deleteCommandsForCells(deleteMenuCells); - - if (deleteCommands.isEmpty()) { - // Technically this method is successful if there's nothing to delete - listener.onComplete(true); - return; - } - - internalInterface.get().sendRPCs(deleteCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - - } - - @Override - public void onFinished() { - DebugTool.logInfo(TAG, "Successfully deleted cells"); - if (listener != null) { - listener.onComplete(true); - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - - } - }); - } - private void updateIdsOnMenuCells(List cells, int parentId) { for (MenuCell cell : cells) { int newId = ++lastMenuId; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 40b76fe0e..db0669666 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -23,6 +23,20 @@ import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdN * Created by Bilal Alsharifi on 1/25/21. */ class MenuReplaceUtilities { + static int commandIdForRPCRequest(RPCRequest request) { + int commandId = 0; + if (request instanceof AddCommand) { + commandId = ((AddCommand) request).getCmdID(); + } else if (request instanceof AddSubMenu) { + commandId = ((AddSubMenu) request).getMenuID(); + } else if (request instanceof DeleteCommand) { + commandId = ((DeleteCommand) request).getCmdID(); + } else if (request instanceof DeleteSubMenu) { + commandId = ((DeleteSubMenu) request).getMenuID(); + } + return commandId; + } + static List deleteCommandsForCells(List cells) { List deletes = new ArrayList<>(); for (MenuCell cell : cells) { @@ -149,4 +163,19 @@ class MenuReplaceUtilities { .setMenuLayout(submenuLayout) .setMenuIcon(icon); } + + static List removeMenuCellFromCurrentMainMenuList(List menuCellList, int commandId) { + for (MenuCell menuCell : menuCellList) { + if (menuCell.getCellId() == commandId) { + menuCellList.remove(menuCell); + return menuCellList; + } else if (menuCell.getSubCells() != null && !menuCell.getSubCells().isEmpty()) { + List newList = removeMenuCellFromCurrentMainMenuList(menuCell.getSubCells(), commandId); + if (newList != null) { + menuCell.setSubCells(newList); + } + } + } + return null; + } } -- cgit v1.2.1 From 2158810dab1406991f37f93bee57094b862b1f50 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 28 Jan 2021 14:24:30 -0500 Subject: Fix issue with parentIdNotFound --- .../com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 1fe34e36a..7dbe24e11 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -65,9 +65,8 @@ import java.util.List; abstract class BaseMenuManager extends BaseSubManager { private static final String TAG = "BaseMenuManager"; - private static final int MAX_ID = Integer.MAX_VALUE; static final int menuCellIdMin = 1; - static final int parentIdNotFound = MAX_ID; + static final int parentIdNotFound = 2000000000; private final WeakReference fileManager; private List currentMenuCells; @@ -231,7 +230,7 @@ abstract class BaseMenuManager extends BaseSubManager { if (cell != null) { // We must see if we have a copy of this cell, since we clone the objects for (MenuCell clonedCell : menuCells) { - if (clonedCell.equals(cell) && clonedCell.getCellId() != MAX_ID) { + if (clonedCell.equals(cell) && clonedCell.getCellId() != parentIdNotFound) { // We've found the correct sub menu cell foundClonedCell = clonedCell; break; -- cgit v1.2.1 From f06c57caa281d09149ff1a0f619b1c2aac6402b3 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 28 Jan 2021 14:26:35 -0500 Subject: Make oldKeeps & newKeeps local vars --- .../screen/menu/MenuReplaceDynamicOperation.java | 28 ++++++++++------------ 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 8c3edcc72..8b66852b4 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -40,8 +40,6 @@ class MenuReplaceDynamicOperation extends Task { private final WindowCapability windowCapability; private List currentMenu; private final List updatedMenu; - private List oldKeeps; - private List newKeeps; private final MenuManagerCompletionListener operationCompletionListener; private MenuConfiguration menuConfiguration; @@ -158,8 +156,8 @@ class MenuReplaceDynamicOperation extends Task { List cellsToAdd = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.ADD); // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares - oldKeeps = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.KEEP); - newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); + List oldKeeps = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.KEEP); + List newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); updateIdsOnDynamicCells(cellsToAdd); @@ -169,10 +167,10 @@ class MenuReplaceDynamicOperation extends Task { if (!cellsToAdd.isEmpty()) { DebugTool.logInfo(TAG, "Sending root menu updates"); - sendDynamicRootMenuRPCs(cellsToDelete, cellsToAdd, listener); + sendDynamicRootMenuRPCs(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, listener); } else { DebugTool.logInfo(TAG, "All root menu items are kept. Check the sub menus"); - runSubMenuCompareAlgorithm(listener); + runSubMenuCompareAlgorithm(oldKeeps, newKeeps, listener); } } @@ -184,7 +182,7 @@ class MenuReplaceDynamicOperation extends Task { deleteRootMenu(new CompletionListener() { @Override public void onComplete(boolean success) { - sendNewMenuCells(updatedMenu, new CompletionListener() { + sendNewMenuCells(updatedMenu, null, null, new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { @@ -198,7 +196,7 @@ class MenuReplaceDynamicOperation extends Task { }); } - private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { + private void sendNewMenuCells(final List newMenuCells, final List oldKeeps, final List newKeeps, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } @@ -223,10 +221,10 @@ class MenuReplaceDynamicOperation extends Task { public void onFinished() { if (!subMenuCommands.isEmpty()) { DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); - sendNewSubMenuCells(subMenuCommands, listener); + sendNewSubMenuCells(subMenuCommands, oldKeeps, newKeeps, listener); } else { if (newKeeps != null && !newKeeps.isEmpty()) { - runSubMenuCompareAlgorithm(listener); + runSubMenuCompareAlgorithm(oldKeeps, newKeeps, listener); } else { DebugTool.logInfo(TAG, "Finished sending main menu commands."); @@ -252,7 +250,7 @@ class MenuReplaceDynamicOperation extends Task { }); } - private void sendNewSubMenuCells(List commands, final CompletionListener listener) { + private void sendNewSubMenuCells(List commands, final List oldKeeps, final List newKeeps, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } @@ -265,7 +263,7 @@ class MenuReplaceDynamicOperation extends Task { @Override public void onFinished() { if (newKeeps != null && !newKeeps.isEmpty()) { - runSubMenuCompareAlgorithm(listener); + runSubMenuCompareAlgorithm(oldKeeps, newKeeps, listener); } else { DebugTool.logInfo(TAG, "Finished Updating Menu"); @@ -394,7 +392,7 @@ class MenuReplaceDynamicOperation extends Task { return builtCommands; } - private void sendDynamicRootMenuRPCs(List deleteMenuCells, final List updatedCells, final CompletionListener listener) { + private void sendDynamicRootMenuRPCs(List deleteMenuCells, final List updatedCells, final List oldKeeps, final List newKeeps, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } @@ -402,7 +400,7 @@ class MenuReplaceDynamicOperation extends Task { sendDeleteCurrentMenu(deleteMenuCells, new CompletionListener() { @Override public void onComplete(boolean success) { - sendNewMenuCells(updatedCells, new CompletionListener() { + sendNewMenuCells(updatedCells, oldKeeps, newKeeps, new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { @@ -416,7 +414,7 @@ class MenuReplaceDynamicOperation extends Task { }); } - private void runSubMenuCompareAlgorithm(CompletionListener listener) { + private void runSubMenuCompareAlgorithm(List oldKeeps, List newKeeps, CompletionListener listener) { // any cells that were re-added have their sub-cells added with them // at this point all we care about are the cells that were deemed equal and kept. if (newKeeps == null || newKeeps.isEmpty()) { -- cgit v1.2.1 From e57394363b27d9736b708c9e18d7429592ae871f Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 28 Jan 2021 14:30:47 -0500 Subject: Simplify logic when both old and new menus are empty --- .../screen/menu/MenuReplaceDynamicOperation.java | 58 ++++------------------ 1 file changed, 11 insertions(+), 47 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 8b66852b4..5d5f24fcf 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -22,7 +22,6 @@ import java.util.List; import java.util.Map; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.lastMenuId; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandForMenuCell; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; @@ -102,19 +101,18 @@ class MenuReplaceDynamicOperation extends Task { // Run the lists through the new algorithm DynamicMenuUpdateRunScore rootScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); if (rootScore == null) { - // Send initial menu without dynamic updates because oldMenuCells is null - DebugTool.logInfo(TAG, "Creating initial Menu"); - updateIdsOnMenuCells(updatedMenu, parentIdNotFound); - createAndSendEntireMenu(listener); + // both new and menu cells are empty. Nothing needs to be done + finishOperation(true); + return; + } + + DebugTool.logInfo(TAG, "Dynamically Updating Menu"); + if (updatedMenu.isEmpty() && (currentMenu != null && !currentMenu.isEmpty())) { + // the dev wants to clear the menu. We have old cells and an empty array of new ones. + deleteMenuWhenNewCellsEmpty(listener); } else { - DebugTool.logInfo(TAG, "Dynamically Updating Menu"); - if (updatedMenu.isEmpty() && (currentMenu != null && !currentMenu.isEmpty())) { - // the dev wants to clear the menu. We have old cells and an empty array of new ones. - deleteMenuWhenNewCellsEmpty(listener); - } else { - // lets dynamically update the root menu - dynamicallyUpdateRootMenu(rootScore, listener); - } + // lets dynamically update the root menu + dynamicallyUpdateRootMenu(rootScore, listener); } } @@ -174,28 +172,6 @@ class MenuReplaceDynamicOperation extends Task { } } - private void createAndSendEntireMenu(final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - deleteRootMenu(new CompletionListener() { - @Override - public void onComplete(boolean success) { - sendNewMenuCells(updatedMenu, null, null, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } - - listener.onComplete(success); - } - }); - } - }); - } - private void sendNewMenuCells(final List newMenuCells, final List oldKeeps, final List newKeeps, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; @@ -332,18 +308,6 @@ class MenuReplaceDynamicOperation extends Task { }); } - private void deleteRootMenu(final CompletionListener listener) { - if (currentMenu == null || currentMenu.isEmpty()) { - if (listener != null) { - // technically this method is successful if there's nothing to delete - DebugTool.logInfo(TAG, "No old cells to delete, returning"); - listener.onComplete(true); - } - } else { - sendDeleteCurrentMenu(currentMenu, listener); - } - } - private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; -- cgit v1.2.1 From 4f5e415cc666355ed148942de5cffa37e4b51a05 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 28 Jan 2021 15:11:32 -0500 Subject: Simplify logic for deleting old menu --- .../screen/menu/MenuReplaceDynamicOperation.java | 38 ++-------------------- 1 file changed, 3 insertions(+), 35 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 5d5f24fcf..cb3837f34 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -101,38 +101,12 @@ class MenuReplaceDynamicOperation extends Task { // Run the lists through the new algorithm DynamicMenuUpdateRunScore rootScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); if (rootScore == null) { - // both new and menu cells are empty. Nothing needs to be done + // Both old and new menu cells are empty. Nothing needs to be done. finishOperation(true); return; } - DebugTool.logInfo(TAG, "Dynamically Updating Menu"); - if (updatedMenu.isEmpty() && (currentMenu != null && !currentMenu.isEmpty())) { - // the dev wants to clear the menu. We have old cells and an empty array of new ones. - deleteMenuWhenNewCellsEmpty(listener); - } else { - // lets dynamically update the root menu - dynamicallyUpdateRootMenu(rootScore, listener); - } - } - - private void deleteMenuWhenNewCellsEmpty(final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - sendDeleteCurrentMenu(currentMenu, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } else { - DebugTool.logInfo(TAG, "Successfully Cleared Menu"); - } - currentMenu = null; - listener.onComplete(success); - } - }); + dynamicallyUpdateRootMenu(rootScore, listener); } private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState){ @@ -163,13 +137,7 @@ class MenuReplaceDynamicOperation extends Task { // this is needed for the onCommands to still work transferIdsToKeptCells(newKeeps); - if (!cellsToAdd.isEmpty()) { - DebugTool.logInfo(TAG, "Sending root menu updates"); - sendDynamicRootMenuRPCs(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, listener); - } else { - DebugTool.logInfo(TAG, "All root menu items are kept. Check the sub menus"); - runSubMenuCompareAlgorithm(oldKeeps, newKeeps, listener); - } + sendDynamicRootMenuRPCs(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, listener); } private void sendNewMenuCells(final List newMenuCells, final List oldKeeps, final List newKeeps, final CompletionListener listener) { -- cgit v1.2.1 From 6b36c8c92c8c0f0873dc28b4942eea2e1d0746ec Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 28 Jan 2021 15:18:39 -0500 Subject: Cleanup code in MenuReplaceDynamicOperation --- .../screen/menu/MenuReplaceDynamicOperation.java | 35 ++++++++++------------ 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index cb3837f34..995bd2a2a 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -83,17 +83,26 @@ class MenuReplaceDynamicOperation extends Task { } else { DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); } - // proceed - updateMenuAndDetermineBestUpdateMethod(listener); + dynamicallyUpdateRootMenu(listener); } }); } else { // No Artworks to be uploaded, send off - updateMenuAndDetermineBestUpdateMethod(listener); + dynamicallyUpdateRootMenu(listener); } } - private void updateMenuAndDetermineBestUpdateMethod(CompletionListener listener) { + private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState){ + List filteredCells = new ArrayList<>(); + for (int index = 0; index < statusList.size(); index++) { + if (statusList.get(index).equals(menuCellState)) { + filteredCells.add(menuCells.get(index)); + } + } + return filteredCells; + } + + private void dynamicallyUpdateRootMenu(CompletionListener listener) { if (getState() == Task.CANCELED) { return; } @@ -106,23 +115,9 @@ class MenuReplaceDynamicOperation extends Task { return; } - dynamicallyUpdateRootMenu(rootScore, listener); - } - - private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState){ - List filteredCells = new ArrayList<>(); - for (int index = 0; index < statusList.size(); index++) { - if (statusList.get(index).equals(menuCellState)) { - filteredCells.add(menuCells.get(index)); - } - } - return filteredCells; - } - - private void dynamicallyUpdateRootMenu(DynamicMenuUpdateRunScore bestRootScore, CompletionListener listener) { // We need to run through the keeps and see if they have subCells, as they also need to be run through the compare function. - List deleteMenuStatus = bestRootScore.getOldStatus(); - List addMenuStatus = bestRootScore.getUpdatedStatus(); + List deleteMenuStatus = rootScore.getOldStatus(); + List addMenuStatus = rootScore.getUpdatedStatus(); List cellsToDelete = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.DELETE); List cellsToAdd = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.ADD); -- cgit v1.2.1 From 11fa9c97d0d1320cc86906ca4616a439dd22ba9a Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 28 Jan 2021 15:48:29 -0500 Subject: Clean up code in MenuReplaceDynamicOperation --- .../screen/menu/MenuReplaceDynamicOperation.java | 78 ++++++++++------------ 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 995bd2a2a..38a916a05 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -70,61 +70,30 @@ class MenuReplaceDynamicOperation extends Task { } }); } - - private void updateMenuCells(final CompletionListener listener) { - // Upload the Artworks - List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability); - if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { - fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { - @Override - public void onComplete(Map errors) { - if (errors != null && !errors.isEmpty()) { - DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); - } else { - DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); - } - dynamicallyUpdateRootMenu(listener); - } - }); - } else { - // No Artworks to be uploaded, send off - dynamicallyUpdateRootMenu(listener); - } - } - - private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState){ - List filteredCells = new ArrayList<>(); - for (int index = 0; index < statusList.size(); index++) { - if (statusList.get(index).equals(menuCellState)) { - filteredCells.add(menuCells.get(index)); - } - } - return filteredCells; - } - private void dynamicallyUpdateRootMenu(CompletionListener listener) { + private void updateMenuCells(final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } // Run the lists through the new algorithm - DynamicMenuUpdateRunScore rootScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); - if (rootScore == null) { + DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); + if (runScore == null) { // Both old and new menu cells are empty. Nothing needs to be done. finishOperation(true); return; } // We need to run through the keeps and see if they have subCells, as they also need to be run through the compare function. - List deleteMenuStatus = rootScore.getOldStatus(); - List addMenuStatus = rootScore.getUpdatedStatus(); + List deleteMenuStatus = runScore.getOldStatus(); + List addMenuStatus = runScore.getUpdatedStatus(); - List cellsToDelete = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.DELETE); - List cellsToAdd = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.ADD); + final List cellsToDelete = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.DELETE); + final List cellsToAdd = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.ADD); // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares - List oldKeeps = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.KEEP); - List newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); + final List oldKeeps = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.KEEP); + final List newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); updateIdsOnDynamicCells(cellsToAdd); @@ -132,7 +101,24 @@ class MenuReplaceDynamicOperation extends Task { // this is needed for the onCommands to still work transferIdsToKeptCells(newKeeps); - sendDynamicRootMenuRPCs(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, listener); + // Upload the Artworks + List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability); + if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { + fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { + @Override + public void onComplete(Map errors) { + if (errors != null && !errors.isEmpty()) { + DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); + } else { + DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); + } + sendDynamicRootMenuRPCs(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, listener); + } + }); + } else { + // No Artworks to be uploaded, send off + sendDynamicRootMenuRPCs(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, listener); + } } private void sendNewMenuCells(final List newMenuCells, final List oldKeeps, final List newKeeps, final CompletionListener listener) { @@ -500,6 +486,16 @@ class MenuReplaceDynamicOperation extends Task { } } + private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState){ + List filteredCells = new ArrayList<>(); + for (int index = 0; index < statusList.size(); index++) { + if (statusList.get(index).equals(menuCellState)) { + filteredCells.add(menuCells.get(index)); + } + } + return filteredCells; + } + void setMenuConfiguration(MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } -- cgit v1.2.1 From 43f58be86f1333942f221442ff40a9274cca3de6 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 28 Jan 2021 15:57:25 -0500 Subject: Cleanup code in MenuReplaceDynamicOperation --- .../screen/menu/MenuReplaceDynamicOperation.java | 121 ++++++++++----------- 1 file changed, 60 insertions(+), 61 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 38a916a05..aeda7be71 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -76,15 +76,14 @@ class MenuReplaceDynamicOperation extends Task { return; } - // Run the lists through the new algorithm DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); + + // If both old and new menu cells are empty. Then nothing needs to be done. if (runScore == null) { - // Both old and new menu cells are empty. Nothing needs to be done. finishOperation(true); return; } - // We need to run through the keeps and see if they have subCells, as they also need to be run through the compare function. List deleteMenuStatus = runScore.getOldStatus(); List addMenuStatus = runScore.getUpdatedStatus(); @@ -112,13 +111,68 @@ class MenuReplaceDynamicOperation extends Task { } else { DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); } - sendDynamicRootMenuRPCs(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, listener); + updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, listener); } }); } else { - // No Artworks to be uploaded, send off - sendDynamicRootMenuRPCs(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, listener); + // Cells have no artwork to load + updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, listener); + } + } + + private void updateMenuWithCellsToDelete(List deleteMenuCells, final List updatedCells, final List oldKeeps, final List newKeeps, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + sendDeleteCurrentMenu(deleteMenuCells, new CompletionListener() { + @Override + public void onComplete(boolean success) { + sendNewMenuCells(updatedCells, oldKeeps, newKeeps, new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (!success) { + DebugTool.logError(TAG, "Error Sending Current Menu"); + } + + listener.onComplete(success); + } + }); + } + }); + } + + private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; } + + if (deleteMenuCells.isEmpty()) { + listener.onComplete(true); + return; + } + + List deleteMenuCommands = deleteCommandsForCells(deleteMenuCells); + + internalInterface.get().sendRPCs(deleteMenuCommands, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + + } + + @Override + public void onFinished() { + DebugTool.logInfo(TAG, "Successfully deleted cells"); + if (listener != null) { + listener.onComplete(true); + } + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + + } + }); } private void sendNewMenuCells(final List newMenuCells, final List oldKeeps, final List newKeeps, final CompletionListener listener) { @@ -257,39 +311,6 @@ class MenuReplaceDynamicOperation extends Task { }); } - private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (deleteMenuCells.isEmpty()) { - listener.onComplete(true); - return; - } - - List deleteMenuCommands = deleteCommandsForCells(deleteMenuCells); - - internalInterface.get().sendRPCs(deleteMenuCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - - } - - @Override - public void onFinished() { - DebugTool.logInfo(TAG, "Successfully deleted cells"); - if (listener != null) { - listener.onComplete(true); - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - - } - }); - } - private List createCommandsForDynamicSubCells(List oldMenuCells, List cells) { List builtCommands = new ArrayList<>(); for (int z = 0; z < oldMenuCells.size(); z++) { @@ -305,28 +326,6 @@ class MenuReplaceDynamicOperation extends Task { return builtCommands; } - private void sendDynamicRootMenuRPCs(List deleteMenuCells, final List updatedCells, final List oldKeeps, final List newKeeps, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - sendDeleteCurrentMenu(deleteMenuCells, new CompletionListener() { - @Override - public void onComplete(boolean success) { - sendNewMenuCells(updatedCells, oldKeeps, newKeeps, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } - - listener.onComplete(success); - } - }); - } - }); - } - private void runSubMenuCompareAlgorithm(List oldKeeps, List newKeeps, CompletionListener listener) { // any cells that were re-added have their sub-cells added with them // at this point all we care about are the cells that were deemed equal and kept. -- cgit v1.2.1 From 3d1af0dff5ef2f9db054d3212c8085a8364f78e6 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 28 Jan 2021 16:17:35 -0500 Subject: Cleanup code in MenuReplaceDynamicOperation --- .../screen/menu/MenuReplaceDynamicOperation.java | 41 ++++++++++++++-------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index aeda7be71..d5cbd5a4c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -111,12 +111,22 @@ class MenuReplaceDynamicOperation extends Task { } else { DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); } - updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, listener); + updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, new CompletionListener() { + @Override + public void onComplete(boolean success) { + startSubMenuUpdatesWithOldKeptCells(oldKeeps, newKeeps, 0, listener); + } + }); } }); } else { // Cells have no artwork to load - updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, listener); + updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, new CompletionListener() { + @Override + public void onComplete(boolean success) { + startSubMenuUpdatesWithOldKeptCells(oldKeeps, newKeeps, 0, listener); + } + }); } } @@ -198,19 +208,10 @@ class MenuReplaceDynamicOperation extends Task { @Override public void onFinished() { - if (!subMenuCommands.isEmpty()) { - DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); - sendNewSubMenuCells(subMenuCommands, oldKeeps, newKeeps, listener); + if (subMenuCommands.isEmpty()) { + listener.onComplete(true); } else { - if (newKeeps != null && !newKeeps.isEmpty()) { - runSubMenuCompareAlgorithm(oldKeeps, newKeeps, listener); - } else { - DebugTool.logInfo(TAG, "Finished sending main menu commands."); - - if (listener != null) { - listener.onComplete(true); - } - } + sendNewSubMenuCells(subMenuCommands, oldKeeps, newKeeps, listener); } } @@ -229,6 +230,18 @@ class MenuReplaceDynamicOperation extends Task { }); } + private void startSubMenuUpdatesWithOldKeptCells (List oldKeeps, List newKeeps, int startIndex, CompletionListener listener){ + if (newKeeps != null && !newKeeps.isEmpty()) { + runSubMenuCompareAlgorithm(oldKeeps, newKeeps, listener); + } else { + DebugTool.logInfo(TAG, "Finished sending main menu commands."); + + if (listener != null) { + listener.onComplete(true); + } + } + } + private void sendNewSubMenuCells(List commands, final List oldKeeps, final List newKeeps, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; -- cgit v1.2.1 From 433508eabb2aee7f944e92f530b744c45730c4de Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 28 Jan 2021 16:26:27 -0500 Subject: Remove unnecessary code from MenuReplaceDynamicOperation --- .../managers/screen/menu/MenuReplaceDynamicOperation.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index d5cbd5a4c..f3f1881fb 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -254,15 +254,8 @@ class MenuReplaceDynamicOperation extends Task { @Override public void onFinished() { - if (newKeeps != null && !newKeeps.isEmpty()) { - runSubMenuCompareAlgorithm(oldKeeps, newKeeps, listener); - } else { - DebugTool.logInfo(TAG, "Finished Updating Menu"); - - if (listener != null) { - listener.onComplete(true); - } - } + DebugTool.logInfo(TAG, "Finished Updating Menu"); + listener.onComplete(true); } @Override -- cgit v1.2.1 From f58f8753e6c1bd6a0e47aae6d7d7a26de76fe7e7 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 28 Jan 2021 16:59:19 -0500 Subject: Add method to MenuReplaceUtilities to handle sending RPCs --- .../screen/menu/MenuReplaceDynamicOperation.java | 111 +++------------------ .../screen/menu/MenuReplaceStaticOperation.java | 98 ++++-------------- .../managers/screen/menu/MenuReplaceUtilities.java | 32 ++++++ 3 files changed, 64 insertions(+), 177 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index f3f1881fb..cc9f82f4e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -8,14 +8,10 @@ import com.smartdevicelink.managers.file.MultipleFileCompletionListener; import com.smartdevicelink.managers.file.filetypes.SdlArtwork; import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; import com.smartdevicelink.proxy.RPCRequest; -import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; -import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.util.DebugTool; -import org.json.JSONException; - import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -26,6 +22,7 @@ import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.comm import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.sendRPCs; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; /** @@ -164,23 +161,11 @@ class MenuReplaceDynamicOperation extends Task { List deleteMenuCommands = deleteCommandsForCells(deleteMenuCells); - internalInterface.get().sendRPCs(deleteMenuCommands, new OnMultipleRequestListener() { + sendRPCs(deleteMenuCommands, internalInterface.get(), new CompletionListener() { @Override - public void onUpdate(int remainingRequests) { - - } - - @Override - public void onFinished() { + public void onComplete(boolean success) { DebugTool.logInfo(TAG, "Successfully deleted cells"); - if (listener != null) { - listener.onComplete(true); - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - + listener.onComplete(success); } }); } @@ -201,31 +186,16 @@ class MenuReplaceDynamicOperation extends Task { List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); - internalInterface.get().sendRPCs(mainMenuCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - } - + sendRPCs(mainMenuCommands, internalInterface.get(), new CompletionListener() { @Override - public void onFinished() { - if (subMenuCommands.isEmpty()) { - listener.onComplete(true); - } else { - sendNewSubMenuCells(subMenuCommands, oldKeeps, newKeeps, listener); - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, "Main Menu response: " + response.serializeJSON().toString()); - } catch (JSONException e) { - e.printStackTrace(); + public void onComplete(boolean success) { + sendRPCs(subMenuCommands, internalInterface.get(), new CompletionListener() { + @Override + public void onComplete(boolean success) { + DebugTool.logInfo(TAG, "Finished Updating Menu"); + listener.onComplete(success); } - } else { - DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); - } + }); } }); } @@ -242,37 +212,6 @@ class MenuReplaceDynamicOperation extends Task { } } - private void sendNewSubMenuCells(List commands, final List oldKeeps, final List newKeeps, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - internalInterface.get().sendRPCs(commands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - } - - @Override - public void onFinished() { - DebugTool.logInfo(TAG, "Finished Updating Menu"); - listener.onComplete(true); - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, "Sub Menu response: " + response.serializeJSON().toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - DebugTool.logError(TAG, "Failed to send sub menu commands: " + response.getInfo()); - } - } - }); - } - private void createAndSendDynamicSubMenuRPCs(List newMenu, final List adds, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; @@ -289,30 +228,10 @@ class MenuReplaceDynamicOperation extends Task { List mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds); - internalInterface.get().sendRPCs(mainMenuCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - // nothing here - } - - @Override - public void onFinished() { - if (listener != null) { - listener.onComplete(true); - } - } - + sendRPCs(mainMenuCommands, internalInterface.get(), new CompletionListener() { @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, "Dynamic Sub Menu response: " + response.serializeJSON().toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); - } + public void onComplete(boolean success) { + listener.onComplete(success); } }); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index d5fc2a232..fafedc6df 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -7,21 +7,21 @@ import com.smartdevicelink.managers.file.FileManager; import com.smartdevicelink.managers.file.MultipleFileCompletionListener; import com.smartdevicelink.managers.file.filetypes.SdlArtwork; import com.smartdevicelink.proxy.RPCRequest; -import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; -import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.util.DebugTool; -import org.json.JSONException; - import java.lang.ref.WeakReference; import java.util.List; import java.util.Map; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.lastMenuId; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.*; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.sendRPCs; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; /** * Created by Bilal Alsharifi on 1/20/21. @@ -128,23 +128,11 @@ class MenuReplaceStaticOperation extends Task { return; } - internalInterface.get().sendRPCs(deleteMenuCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - - } - + sendRPCs(deleteMenuCommands, internalInterface.get(), new CompletionListener() { @Override - public void onFinished() { + public void onComplete(boolean success) { DebugTool.logInfo(TAG, "Successfully deleted cells"); - if (listener != null) { - listener.onComplete(true); - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - + listener.onComplete(success); } }); } @@ -164,70 +152,18 @@ class MenuReplaceStaticOperation extends Task { List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); - internalInterface.get().sendRPCs(mainMenuCommands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - } - - @Override - public void onFinished() { - if (!subMenuCommands.isEmpty()) { - DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); - sendNewSubMenuCells(subMenuCommands, listener); - } else { - DebugTool.logInfo(TAG, "Finished sending main menu commands."); - - if (listener != null) { - listener.onComplete(true); - } - } - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, "Main Menu response: " + response.serializeJSON().toString()); - } catch (JSONException e) { - e.printStackTrace(); - } - } else { - DebugTool.logError(TAG, "Result: " + response.getResultCode() + " Info: " + response.getInfo()); - } - } - }); - } - - private void sendNewSubMenuCells(List commands, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - internalInterface.get().sendRPCs(commands, new OnMultipleRequestListener() { - @Override - public void onUpdate(int remainingRequests) { - } - + sendRPCs(mainMenuCommands, internalInterface.get(), new CompletionListener() { @Override - public void onFinished() { - DebugTool.logInfo(TAG, "Finished Updating Menu"); - - if (listener != null) { - listener.onComplete(true); - } - } + public void onComplete(boolean success) { + DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - try { - DebugTool.logInfo(TAG, "Sub Menu response: " + response.serializeJSON().toString()); - } catch (JSONException e) { - e.printStackTrace(); + sendRPCs(subMenuCommands, internalInterface.get(), new CompletionListener() { + @Override + public void onComplete(boolean success) { + DebugTool.logInfo(TAG, "Finished Updating Menu"); + listener.onComplete(success); } - } else { - DebugTool.logError(TAG, "Failed to send sub menu commands: " + response.getInfo()); - } + }); } }); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index db0669666..f4a7ac72b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -1,9 +1,12 @@ package com.smartdevicelink.managers.screen.menu; +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.filetypes.SdlArtwork; import com.smartdevicelink.proxy.RPCRequest; +import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.AddCommand; import com.smartdevicelink.proxy.rpc.AddSubMenu; import com.smartdevicelink.proxy.rpc.DeleteCommand; @@ -13,6 +16,8 @@ import com.smartdevicelink.proxy.rpc.MenuParams; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; +import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; +import com.smartdevicelink.util.DebugTool; import java.util.ArrayList; import java.util.List; @@ -23,6 +28,8 @@ import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdN * Created by Bilal Alsharifi on 1/25/21. */ class MenuReplaceUtilities { + private static final String TAG = "MenuReplaceUtilities"; + static int commandIdForRPCRequest(RPCRequest request) { int commandId = 0; if (request instanceof AddCommand) { @@ -178,4 +185,29 @@ class MenuReplaceUtilities { } return null; } + + static void sendRPCs(List requests, ISdl internalInterface, final CompletionListener listener) { + if (requests == null || requests.isEmpty()) { + listener.onComplete(true); + } + + internalInterface.sendRPCs(requests, new OnMultipleRequestListener() { + @Override + public void onUpdate(int remainingRequests) { + } + + @Override + public void onFinished() { + // todo should we pass false if one failed? + listener.onComplete(true); + } + + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (!response.getSuccess()) { + DebugTool.logError(TAG, "Failed to send RPC. Result: " + response.getResultCode() + " Info: " + response.getInfo()); + } + } + }); + } } -- cgit v1.2.1 From 80b493327a290fdad73d9787a35216f65b9a33bf Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 29 Jan 2021 10:14:00 -0500 Subject: Cleanup MenuReplaceDynamicOperation --- .../screen/menu/MenuReplaceDynamicOperation.java | 33 +++++++++++++++------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index cc9f82f4e..d8fc6e228 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -108,7 +108,7 @@ class MenuReplaceDynamicOperation extends Task { } else { DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); } - updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, new CompletionListener() { + updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, new CompletionListener() { @Override public void onComplete(boolean success) { startSubMenuUpdatesWithOldKeptCells(oldKeeps, newKeeps, 0, listener); @@ -118,7 +118,7 @@ class MenuReplaceDynamicOperation extends Task { }); } else { // Cells have no artwork to load - updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, oldKeeps, newKeeps, new CompletionListener() { + updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, new CompletionListener() { @Override public void onComplete(boolean success) { startSubMenuUpdatesWithOldKeptCells(oldKeeps, newKeeps, 0, listener); @@ -127,15 +127,15 @@ class MenuReplaceDynamicOperation extends Task { } } - private void updateMenuWithCellsToDelete(List deleteMenuCells, final List updatedCells, final List oldKeeps, final List newKeeps, final CompletionListener listener) { + private void updateMenuWithCellsToDelete(List deleteCells, final List addCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } - sendDeleteCurrentMenu(deleteMenuCells, new CompletionListener() { + sendDeleteCurrentMenu(deleteCells, new CompletionListener() { @Override public void onComplete(boolean success) { - sendNewMenuCells(updatedCells, oldKeeps, newKeeps, new CompletionListener() { + sendNewMenuCells(addCells, new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { @@ -160,39 +160,52 @@ class MenuReplaceDynamicOperation extends Task { } List deleteMenuCommands = deleteCommandsForCells(deleteMenuCells); - sendRPCs(deleteMenuCommands, internalInterface.get(), new CompletionListener() { @Override public void onComplete(boolean success) { - DebugTool.logInfo(TAG, "Successfully deleted cells"); + if (!success) { + DebugTool.logWarning(TAG, "Unable to delete all old menu commands"); + } else { + DebugTool.logInfo(TAG, "Finished deleting old menu"); + } listener.onComplete(success); } }); } - private void sendNewMenuCells(final List newMenuCells, final List oldKeeps, final List newKeeps, final CompletionListener listener) { + private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } if (newMenuCells == null || newMenuCells.isEmpty()) { // This can be considered a success if the user was clearing out their menu + DebugTool.logInfo(TAG, "There are no cells to update."); listener.onComplete(true); return; } MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; - List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); + final List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); sendRPCs(mainMenuCommands, internalInterface.get(), new CompletionListener() { @Override public void onComplete(boolean success) { + if (!success) { + DebugTool.logError(TAG, "Failed to send main menu commands"); + listener.onComplete(false); + return; + } sendRPCs(subMenuCommands, internalInterface.get(), new CompletionListener() { @Override public void onComplete(boolean success) { - DebugTool.logInfo(TAG, "Finished Updating Menu"); + if (!success) { + DebugTool.logError(TAG, "Failed to send sub menu commands"); + } else { + DebugTool.logInfo(TAG, "Finished updating menu"); + } listener.onComplete(success); } }); -- cgit v1.2.1 From 3d6d0768822c5aa57662bc33852b4ed33544cdf4 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 29 Jan 2021 10:21:29 -0500 Subject: Cleanup MenuReplaceDynamicOperation --- .../screen/menu/MenuReplaceDynamicOperation.java | 84 +++++++++++----------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index d8fc6e228..41983c405 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -213,9 +213,9 @@ class MenuReplaceDynamicOperation extends Task { }); } - private void startSubMenuUpdatesWithOldKeptCells (List oldKeeps, List newKeeps, int startIndex, CompletionListener listener){ - if (newKeeps != null && !newKeeps.isEmpty()) { - runSubMenuCompareAlgorithm(oldKeeps, newKeeps, listener); + private void startSubMenuUpdatesWithOldKeptCells(List oldKeptCells, List newKeptCells, int startIndex, CompletionListener listener) { + if (newKeptCells != null && !newKeptCells.isEmpty()) { + runSubMenuCompareAlgorithm(oldKeptCells, newKeptCells, listener); } else { DebugTool.logInfo(TAG, "Finished sending main menu commands."); @@ -225,45 +225,6 @@ class MenuReplaceDynamicOperation extends Task { } } - private void createAndSendDynamicSubMenuRPCs(List newMenu, final List adds, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (adds.isEmpty()) { - if (listener != null) { - // This can be considered a success if the user was clearing out their menu - DebugTool.logError(TAG, "Called createAndSendDynamicSubMenuRPCs with empty menu"); - listener.onComplete(true); - } - return; - } - - List mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds); - - sendRPCs(mainMenuCommands, internalInterface.get(), new CompletionListener() { - @Override - public void onComplete(boolean success) { - listener.onComplete(success); - } - }); - } - - private List createCommandsForDynamicSubCells(List oldMenuCells, List cells) { - List builtCommands = new ArrayList<>(); - for (int z = 0; z < oldMenuCells.size(); z++) { - MenuCell oldCell = oldMenuCells.get(z); - for (int i = 0; i < cells.size(); i++) { - MenuCell cell = cells.get(i); - if (cell.equals(oldCell)) { - builtCommands.add(commandForMenuCell(cell, fileManager.get(), windowCapability, z)); - break; - } - } - } - return builtCommands; - } - private void runSubMenuCompareAlgorithm(List oldKeeps, List newKeeps, CompletionListener listener) { // any cells that were re-added have their sub-cells added with them // at this point all we care about are the cells that were deemed equal and kept. @@ -346,6 +307,45 @@ class MenuReplaceDynamicOperation extends Task { }); } + private void createAndSendDynamicSubMenuRPCs(List newMenu, final List adds, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + if (adds.isEmpty()) { + if (listener != null) { + // This can be considered a success if the user was clearing out their menu + DebugTool.logError(TAG, "Called createAndSendDynamicSubMenuRPCs with empty menu"); + listener.onComplete(true); + } + return; + } + + List mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds); + + sendRPCs(mainMenuCommands, internalInterface.get(), new CompletionListener() { + @Override + public void onComplete(boolean success) { + listener.onComplete(success); + } + }); + } + + private List createCommandsForDynamicSubCells(List oldMenuCells, List cells) { + List builtCommands = new ArrayList<>(); + for (int z = 0; z < oldMenuCells.size(); z++) { + MenuCell oldCell = oldMenuCells.get(z); + for (int i = 0; i < cells.size(); i++) { + MenuCell cell = cells.get(i); + if (cell.equals(oldCell)) { + builtCommands.add(commandForMenuCell(cell, fileManager.get(), windowCapability, z)); + break; + } + } + } + return builtCommands; + } + private void updateIdsOnDynamicCells(List dynamicCells) { if (updatedMenu != null && !updatedMenu.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { for (int z = 0; z < updatedMenu.size(); z++) { -- cgit v1.2.1 From 9bec60a4929332de83e63328f539e714da97a350 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 29 Jan 2021 10:47:25 -0500 Subject: Add missing param --- .../managers/screen/menu/MenuReplaceDynamicOperation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 41983c405..7fcc00ac7 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -135,7 +135,7 @@ class MenuReplaceDynamicOperation extends Task { sendDeleteCurrentMenu(deleteCells, new CompletionListener() { @Override public void onComplete(boolean success) { - sendNewMenuCells(addCells, new CompletionListener() { + sendNewMenuCells(addCells, currentMenu, new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { @@ -173,7 +173,7 @@ class MenuReplaceDynamicOperation extends Task { }); } - private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { + private void sendNewMenuCells(final List newMenuCells, final List oldMenu, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } -- cgit v1.2.1 From 476a091ffe76a2f202c0c74438d4e81f8523f18b Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 29 Jan 2021 13:35:09 -0500 Subject: Add startSubMenuUpdatesWithOldKeptCells() method --- .../screen/menu/MenuReplaceDynamicOperation.java | 55 +++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 7fcc00ac7..abd6c623e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -213,7 +213,60 @@ class MenuReplaceDynamicOperation extends Task { }); } - private void startSubMenuUpdatesWithOldKeptCells(List oldKeptCells, List newKeptCells, int startIndex, CompletionListener listener) { + private void startSubMenuUpdatesWithOldKeptCells(final List oldKeptCells, final List newKeptCells, final int startIndex, final CompletionListener listener) { + if (oldKeptCells.isEmpty() || startIndex >= oldKeptCells.size()) { + listener.onComplete(true); + return; + } + + if (oldKeptCells.get(startIndex) != null && oldKeptCells.get(startIndex).getSubCells() != null && !oldKeptCells.get(startIndex).getSubCells().isEmpty()){ + DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); + + // If both old and new menu cells are empty. Then nothing needs to be done. + if (tempScore == null) { + finishOperation(true); + return; + } + + List deleteMenuStatus = tempScore.getOldStatus(); + List addMenuStatus = tempScore.getUpdatedStatus(); + + final List cellsToDelete = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.DELETE); + final List cellsToAdd = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.ADD); + + final List oldKeeps = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.KEEP); + final List newKeeps = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.KEEP); + + transferCellIDFromOldCells(oldKeeps, newKeeps); + + sendDeleteCurrentMenu(cellsToDelete, new CompletionListener() { + @Override + public void onComplete(boolean success) { + sendNewMenuCells(cellsToAdd, currentMenu.get(startIndex).getSubCells(), new CompletionListener() { + @Override + public void onComplete(boolean success) { + // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements + startSubMenuUpdatesWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + } + }); + } + }); + } else { + // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements + startSubMenuUpdatesWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + } + } + + private void transferCellIDFromOldCells(List oldCells, List newCells) { + if (oldCells.isEmpty()) { + return; + } + for (int i = 0; i < newCells.size(); i++) { + newCells.get(i).setCellId(oldCells.get(i).getCellId()); + } + } + + private void startSubMenuUpdatesWithOldKeptCells2(List oldKeptCells, List newKeptCells, int startIndex, CompletionListener listener) { if (newKeptCells != null && !newKeptCells.isEmpty()) { runSubMenuCompareAlgorithm(oldKeptCells, newKeptCells, listener); } else { -- cgit v1.2.1 From 60f277c137b16ecbc7608157e4505b637fb69b00 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 29 Jan 2021 13:36:26 -0500 Subject: Delete some unsued code --- .../screen/menu/MenuReplaceDynamicOperation.java | 165 --------------------- 1 file changed, 165 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index abd6c623e..3869a3478 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -266,139 +266,6 @@ class MenuReplaceDynamicOperation extends Task { } } - private void startSubMenuUpdatesWithOldKeptCells2(List oldKeptCells, List newKeptCells, int startIndex, CompletionListener listener) { - if (newKeptCells != null && !newKeptCells.isEmpty()) { - runSubMenuCompareAlgorithm(oldKeptCells, newKeptCells, listener); - } else { - DebugTool.logInfo(TAG, "Finished sending main menu commands."); - - if (listener != null) { - listener.onComplete(true); - } - } - } - - private void runSubMenuCompareAlgorithm(List oldKeeps, List newKeeps, CompletionListener listener) { - // any cells that were re-added have their sub-cells added with them - // at this point all we care about are the cells that were deemed equal and kept. - if (newKeeps == null || newKeeps.isEmpty()) { - listener.onComplete(true); - return; - } - - List commandLists = new ArrayList<>(); - - for (int i = 0; i < newKeeps.size(); i++) { - MenuCell newKeptCell = newKeeps.get(i); - MenuCell oldKeptCell = oldKeeps.get(i); - - if (oldKeptCell.getSubCells() != null && !oldKeptCell.getSubCells().isEmpty() && newKeptCell.getSubCells() != null && !newKeptCell.getSubCells().isEmpty()) { - DynamicMenuUpdateRunScore subScore = DynamicMenuUpdateAlgorithm.startCompareAtRun(0, oldKeptCell.getSubCells(), newKeptCell.getSubCells()); - - if (subScore != null) { - DebugTool.logInfo(TAG, "Sub menu Run Score: " + oldKeptCell.getTitle() + " Score: " + subScore.getScore()); - SubCellCommandList commandList = new SubCellCommandList(oldKeptCell.getTitle(), oldKeptCell.getCellId(), subScore, oldKeptCell.getSubCells(), newKeptCell.getSubCells()); - commandLists.add(commandList); - } - } - } - createSubMenuDynamicCommands(commandLists, listener); - } - - private void createSubMenuDynamicCommands(final List commandLists, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (commandLists.isEmpty()) { - DebugTool.logInfo(TAG, "All menu updates, including sub menus - done."); - listener.onComplete(true); - return; - } - - final SubCellCommandList commandList = commandLists.remove(0); - - DebugTool.logInfo(TAG, "Creating and Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - - // grab the scores - DynamicMenuUpdateRunScore score = commandList.getListsScore(); - List newStates = score.getUpdatedStatus(); - List oldStates = score.getOldStatus(); - - // Grab the sub-menus from the parent cell - final List oldCells = commandList.getOldList(); - final List newCells = commandList.getNewList(); - - List cellsToDelete = filterMenuCellsWithStatusList(currentMenu, oldStates, MenuCellState.DELETE); - - // Set up the adds - List cellsToAdd = filterMenuCellsWithStatusList(updatedMenu, newStates, MenuCellState.ADD); - List subCellKeepsNew = filterMenuCellsWithStatusList(updatedMenu, newStates, MenuCellState.KEEP); - - final List addsWithNewIds = updateIdsOnDynamicSubCells(oldCells, cellsToAdd, commandList.getParentId()); - // this is needed for the onCommands to still work - transferIdsToKeptSubCells(oldCells, subCellKeepsNew); - - sendDeleteCurrentMenu(cellsToDelete, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (addsWithNewIds != null && !addsWithNewIds.isEmpty()) { - createAndSendDynamicSubMenuRPCs(newCells, addsWithNewIds, new CompletionListener() { - @Override - public void onComplete(boolean success) { - // recurse through next sub list - DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - createSubMenuDynamicCommands(commandLists, listener); - } - }); - } else { - // no add commands to send, recurse through next sub list - DebugTool.logInfo(TAG, "Finished Sending Dynamic Sub Commands For Root Menu Cell: " + commandList.getMenuTitle()); - createSubMenuDynamicCommands(commandLists, listener); - } - } - }); - } - - private void createAndSendDynamicSubMenuRPCs(List newMenu, final List adds, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (adds.isEmpty()) { - if (listener != null) { - // This can be considered a success if the user was clearing out their menu - DebugTool.logError(TAG, "Called createAndSendDynamicSubMenuRPCs with empty menu"); - listener.onComplete(true); - } - return; - } - - List mainMenuCommands = createCommandsForDynamicSubCells(newMenu, adds); - - sendRPCs(mainMenuCommands, internalInterface.get(), new CompletionListener() { - @Override - public void onComplete(boolean success) { - listener.onComplete(success); - } - }); - } - - private List createCommandsForDynamicSubCells(List oldMenuCells, List cells) { - List builtCommands = new ArrayList<>(); - for (int z = 0; z < oldMenuCells.size(); z++) { - MenuCell oldCell = oldMenuCells.get(z); - for (int i = 0; i < cells.size(); i++) { - MenuCell cell = cells.get(i); - if (cell.equals(oldCell)) { - builtCommands.add(commandForMenuCell(cell, fileManager.get(), windowCapability, z)); - break; - } - } - } - return builtCommands; - } - private void updateIdsOnDynamicCells(List dynamicCells) { if (updatedMenu != null && !updatedMenu.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { for (int z = 0; z < updatedMenu.size(); z++) { @@ -420,25 +287,6 @@ class MenuReplaceDynamicOperation extends Task { } } - private List updateIdsOnDynamicSubCells(List oldList, List dynamicCells, Integer parentId) { - if (oldList != null && !oldList.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { - for (int z = 0; z < oldList.size(); z++) { - MenuCell mainCell = oldList.get(z); - for (int i = 0; i < dynamicCells.size(); i++) { - MenuCell dynamicCell = dynamicCells.get(i); - int newId = ++lastMenuId; - if (mainCell.equals(dynamicCell)) { - oldList.get(z).setCellId(newId); - } - dynamicCells.get(i).setParentCellId(parentId); - dynamicCells.get(i).setCellId(newId); - } - } - return dynamicCells; - } - return null; - } - private void updateIdsOnMenuCells(List cells, int parentId) { for (MenuCell cell : cells) { int newId = ++lastMenuId; @@ -463,19 +311,6 @@ class MenuReplaceDynamicOperation extends Task { } } - private void transferIdsToKeptSubCells(List old, List keeps) { - for (int z = 0; z < old.size(); z++) { - MenuCell oldCell = old.get(z); - for (int i = 0; i < keeps.size(); i++) { - MenuCell keptCell = keeps.get(i); - if (oldCell.equals(keptCell)) { - keptCell.setCellId(oldCell.getCellId()); - break; - } - } - } - } - private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState){ List filteredCells = new ArrayList<>(); for (int index = 0; index < statusList.size(); index++) { -- cgit v1.2.1 From 032460b5a172be33c7e6298c792107278980cc75 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 29 Jan 2021 13:53:51 -0500 Subject: Move setting ids logic from operations to manager --- .../managers/screen/menu/BaseMenuManager.java | 16 ++++++- .../screen/menu/MenuReplaceDynamicOperation.java | 51 +--------------------- .../screen/menu/MenuReplaceStaticOperation.java | 15 ------- 3 files changed, 16 insertions(+), 66 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 7dbe24e11..0b8f0345e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -84,7 +84,7 @@ abstract class BaseMenuManager extends BaseSubManager { private OnSystemCapabilityListener onDisplaysCapabilityListener; private WindowCapability windowCapability; private Queue transactionQueue; - static int lastMenuId; // todo this shouldn't be static and should be fully managed in the manager + private int lastMenuId; BaseMenuManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) { super(internalInterface); @@ -185,6 +185,8 @@ abstract class BaseMenuManager extends BaseSubManager { return; } + updateIdsOnMenuCells(clonedCells, parentIdNotFound); + currentMenuCells = new ArrayList<>(menuCells); menuCells = new ArrayList<>(clonedCells); @@ -465,4 +467,16 @@ abstract class BaseMenuManager extends BaseSubManager { return true; } + + private void updateIdsOnMenuCells(List menuCells, int parentId) { + for (MenuCell cell : menuCells) { + cell.setCellId(lastMenuId++); + if (parentId != parentIdNotFound) { + cell.setParentCellId(parentId); + } + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); + } + } + } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 3869a3478..505341b3d 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -17,8 +17,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.lastMenuId; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandForMenuCell; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; @@ -91,11 +89,9 @@ class MenuReplaceDynamicOperation extends Task { final List oldKeeps = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.KEEP); final List newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); - updateIdsOnDynamicCells(cellsToAdd); - // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. // this is needed for the onCommands to still work - transferIdsToKeptCells(newKeeps); + transferCellIDFromOldCells(oldKeeps, newKeeps); // Upload the Artworks List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability); @@ -266,51 +262,6 @@ class MenuReplaceDynamicOperation extends Task { } } - private void updateIdsOnDynamicCells(List dynamicCells) { - if (updatedMenu != null && !updatedMenu.isEmpty() && dynamicCells != null && !dynamicCells.isEmpty()) { - for (int z = 0; z < updatedMenu.size(); z++) { - MenuCell mainCell = updatedMenu.get(z); - for (int i = 0; i < dynamicCells.size(); i++) { - MenuCell dynamicCell = dynamicCells.get(i); - if (mainCell.equals(dynamicCell)) { - int newId = ++lastMenuId; - updatedMenu.get(z).setCellId(newId); - dynamicCells.get(i).setCellId(newId); - - if (mainCell.getSubCells() != null && !mainCell.getSubCells().isEmpty()) { - updateIdsOnMenuCells(mainCell.getSubCells(), mainCell.getCellId()); - } - break; - } - } - } - } - } - - private void updateIdsOnMenuCells(List cells, int parentId) { - for (MenuCell cell : cells) { - int newId = ++lastMenuId; - cell.setCellId(newId); - cell.setParentCellId(parentId); - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); - } - } - } - - private void transferIdsToKeptCells(List keeps) { - for (int z = 0; z < currentMenu.size(); z++) { - MenuCell oldCell = currentMenu.get(z); - for (int i = 0; i < keeps.size(); i++) { - MenuCell keptCell = keeps.get(i); - if (oldCell.equals(keptCell)) { - keptCell.setCellId(oldCell.getCellId()); - break; - } - } - } - } - private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState){ List filteredCells = new ArrayList<>(); for (int index = 0; index < statusList.size(); index++) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index fafedc6df..66a993c42 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -15,8 +15,6 @@ import java.lang.ref.WeakReference; import java.util.List; import java.util.Map; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.lastMenuId; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; @@ -145,8 +143,6 @@ class MenuReplaceStaticOperation extends Task { return; } - updateIdsOnMenuCells(newMenuCells, parentIdNotFound); - MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); @@ -168,17 +164,6 @@ class MenuReplaceStaticOperation extends Task { }); } - private void updateIdsOnMenuCells(List cells, int parentId) { - for (MenuCell cell : cells) { - int newId = ++lastMenuId; - cell.setCellId(newId); - cell.setParentCellId(parentId); - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); - } - } - } - void setMenuConfiguration(MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } -- cgit v1.2.1 From fc50a523ee4e4dd7e71faecb2bee8a0671eb6cbf Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 29 Jan 2021 13:57:41 -0500 Subject: Remove SubCellCommandList --- .../screen/menu/SubCellCommandListTests.java | 63 --------------- .../managers/screen/menu/SubCellCommandList.java | 91 ---------------------- 2 files changed, 154 deletions(-) delete mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/SubCellCommandListTests.java delete mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/SubCellCommandList.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/SubCellCommandListTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/SubCellCommandListTests.java deleted file mode 100644 index e0380aa09..000000000 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/SubCellCommandListTests.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2019 Livio, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following - * disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * Neither the name of the Livio Inc. nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package com.smartdevicelink.managers.screen.menu; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.smartdevicelink.test.TestValues; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static junit.framework.TestCase.assertEquals; - -@RunWith(AndroidJUnit4.class) -public class SubCellCommandListTests { - - @Test - public void testSettersAndGetters() { - - DynamicMenuUpdateRunScore runScore = new DynamicMenuUpdateRunScore(TestValues.GENERAL_INT, TestValues.GENERAL_INTEGER_LIST, TestValues.GENERAL_INTEGER_LIST); - - // set everything - SubCellCommandList subCellCommandList = new SubCellCommandList(TestValues.GENERAL_STRING, TestValues.GENERAL_INTEGER, runScore, TestValues.GENERAL_MENUCELL_LIST, TestValues.GENERAL_MENUCELL_LIST); - - // use getters and assert equality - assertEquals(subCellCommandList.getMenuTitle(), TestValues.GENERAL_STRING); - assertEquals(subCellCommandList.getParentId(), TestValues.GENERAL_INTEGER); - assertEquals(runScore, runScore); - assertEquals(subCellCommandList.getNewList(), TestValues.GENERAL_MENUCELL_LIST); - assertEquals(subCellCommandList.getOldList(), TestValues.GENERAL_MENUCELL_LIST); - - } -} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/SubCellCommandList.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/SubCellCommandList.java deleted file mode 100644 index c8567d5a8..000000000 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/SubCellCommandList.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (c) 2019 Livio, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following - * disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * Neither the name of the Livio Inc. nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -package com.smartdevicelink.managers.screen.menu; - -import java.util.List; - -class SubCellCommandList { - - private DynamicMenuUpdateRunScore listsScore; - private String menuTitle; - private Integer parentId; - private List oldList, newList; - - SubCellCommandList(String menuTitle, Integer parentId, DynamicMenuUpdateRunScore listsScore, List oldList, List newList) { - setMenuTitle(menuTitle); - setParentId(parentId); - setListsScore(listsScore); - setOldList(oldList); - setNewList(newList); - } - - private void setParentId(Integer parentId) { - this.parentId = parentId; - } - - Integer getParentId() { - return parentId; - } - - private void setMenuTitle(String menuTitle) { - this.menuTitle = menuTitle; - } - - String getMenuTitle() { - return menuTitle; - } - - private void setListsScore(DynamicMenuUpdateRunScore listsScore) { - this.listsScore = listsScore; - } - - DynamicMenuUpdateRunScore getListsScore() { - return listsScore; - } - - private void setOldList(List oldList) { - this.oldList = oldList; - } - - List getOldList() { - return oldList; - } - - private void setNewList(List newList) { - this.newList = newList; - } - - List getNewList() { - return newList; - } -} -- cgit v1.2.1 From 9fed89f15eeb34ddeddc4aa3258bfae5b17c5c55 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 29 Jan 2021 14:03:07 -0500 Subject: Remove unsed code --- .../managers/screen/menu/MenuReplaceUtilities.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index f4a7ac72b..191be041b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -171,21 +171,6 @@ class MenuReplaceUtilities { .setMenuIcon(icon); } - static List removeMenuCellFromCurrentMainMenuList(List menuCellList, int commandId) { - for (MenuCell menuCell : menuCellList) { - if (menuCell.getCellId() == commandId) { - menuCellList.remove(menuCell); - return menuCellList; - } else if (menuCell.getSubCells() != null && !menuCell.getSubCells().isEmpty()) { - List newList = removeMenuCellFromCurrentMainMenuList(menuCell.getSubCells(), commandId); - if (newList != null) { - menuCell.setSubCells(newList); - } - } - } - return null; - } - static void sendRPCs(List requests, ISdl internalInterface, final CompletionListener listener) { if (requests == null || requests.isEmpty()) { listener.onComplete(true); -- cgit v1.2.1 From 9505f6f345e0f623816be9592ac1472477eafdeb Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 29 Jan 2021 16:17:41 -0500 Subject: Refactor sending RPCs code to return commandId --- .../screen/menu/MenuReplaceDynamicOperation.java | 34 +++++++++++----- .../screen/menu/MenuReplaceStaticOperation.java | 28 ++++++++++--- .../managers/screen/menu/MenuReplaceUtilities.java | 24 ++++++----- .../screen/menu/SendingRPCsCompletionListener.java | 46 ++++++++++++++++++++++ 4 files changed, 108 insertions(+), 24 deletions(-) create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/SendingRPCsCompletionListener.java diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 505341b3d..9739410cd 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -8,6 +8,7 @@ import com.smartdevicelink.managers.file.MultipleFileCompletionListener; import com.smartdevicelink.managers.file.filetypes.SdlArtwork; import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; import com.smartdevicelink.proxy.RPCRequest; +import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.util.DebugTool; @@ -156,16 +157,21 @@ class MenuReplaceDynamicOperation extends Task { } List deleteMenuCommands = deleteCommandsForCells(deleteMenuCells); - sendRPCs(deleteMenuCommands, internalInterface.get(), new CompletionListener() { + sendRPCs(deleteMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { @Override - public void onComplete(boolean success) { + public void onComplete(boolean success, Map errors) { if (!success) { - DebugTool.logWarning(TAG, "Unable to delete all old menu commands"); + DebugTool.logWarning(TAG, "Unable to delete all old menu commands" + errors); } else { DebugTool.logInfo(TAG, "Finished deleting old menu"); } listener.onComplete(success); } + + @Override + public void onResponse(RPCRequest request, RPCResponse response, int commandId) { + + } }); } @@ -186,26 +192,36 @@ class MenuReplaceDynamicOperation extends Task { final List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); - sendRPCs(mainMenuCommands, internalInterface.get(), new CompletionListener() { + sendRPCs(mainMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { @Override - public void onComplete(boolean success) { + public void onComplete(boolean success, Map errors) { if (!success) { - DebugTool.logError(TAG, "Failed to send main menu commands"); + DebugTool.logError(TAG, "Failed to send main menu commands" + errors); listener.onComplete(false); return; } - sendRPCs(subMenuCommands, internalInterface.get(), new CompletionListener() { + sendRPCs(subMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { @Override - public void onComplete(boolean success) { + public void onComplete(boolean success, Map errors) { if (!success) { - DebugTool.logError(TAG, "Failed to send sub menu commands"); + DebugTool.logError(TAG, "Failed to send sub menu commands" + errors); } else { DebugTool.logInfo(TAG, "Finished updating menu"); } listener.onComplete(success); } + + @Override + public void onResponse(RPCRequest request, RPCResponse response, int commandId) { + + } }); } + + @Override + public void onResponse(RPCRequest request, RPCResponse response, int commandId) { + + } }); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java index 66a993c42..415c20fec 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java @@ -7,6 +7,7 @@ import com.smartdevicelink.managers.file.FileManager; import com.smartdevicelink.managers.file.MultipleFileCompletionListener; import com.smartdevicelink.managers.file.filetypes.SdlArtwork; import com.smartdevicelink.proxy.RPCRequest; +import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.util.DebugTool; @@ -126,12 +127,17 @@ class MenuReplaceStaticOperation extends Task { return; } - sendRPCs(deleteMenuCommands, internalInterface.get(), new CompletionListener() { + sendRPCs(deleteMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { @Override - public void onComplete(boolean success) { + public void onComplete(boolean success, Map errors) { DebugTool.logInfo(TAG, "Successfully deleted cells"); listener.onComplete(success); } + + @Override + public void onResponse(RPCRequest request, RPCResponse response, int commandId) { + + } }); } @@ -148,19 +154,29 @@ class MenuReplaceStaticOperation extends Task { List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); - sendRPCs(mainMenuCommands, internalInterface.get(), new CompletionListener() { + sendRPCs(mainMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { @Override - public void onComplete(boolean success) { + public void onComplete(boolean success, Map errors) { DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); - sendRPCs(subMenuCommands, internalInterface.get(), new CompletionListener() { + sendRPCs(subMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { @Override - public void onComplete(boolean success) { + public void onComplete(boolean success, Map errors) { DebugTool.logInfo(TAG, "Finished Updating Menu"); listener.onComplete(success); } + + @Override + public void onResponse(RPCRequest request, RPCResponse response, int commandId) { + + } }); } + + @Override + public void onResponse(RPCRequest request, RPCResponse response, int commandId) { + + } }); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 191be041b..6dad7da68 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -1,6 +1,5 @@ package com.smartdevicelink.managers.screen.menu; -import com.smartdevicelink.managers.CompletionListener; import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.managers.ManagerUtility; import com.smartdevicelink.managers.file.FileManager; @@ -17,10 +16,11 @@ import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; -import com.smartdevicelink.util.DebugTool; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; @@ -28,8 +28,6 @@ import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdN * Created by Bilal Alsharifi on 1/25/21. */ class MenuReplaceUtilities { - private static final String TAG = "MenuReplaceUtilities"; - static int commandIdForRPCRequest(RPCRequest request) { int commandId = 0; if (request instanceof AddCommand) { @@ -171,9 +169,10 @@ class MenuReplaceUtilities { .setMenuIcon(icon); } - static void sendRPCs(List requests, ISdl internalInterface, final CompletionListener listener) { + static void sendRPCs(final List requests, ISdl internalInterface, final SendingRPCsCompletionListener listener) { + final Map errors = new HashMap<>(); if (requests == null || requests.isEmpty()) { - listener.onComplete(true); + listener.onComplete(true, errors); } internalInterface.sendRPCs(requests, new OnMultipleRequestListener() { @@ -183,15 +182,22 @@ class MenuReplaceUtilities { @Override public void onFinished() { - // todo should we pass false if one failed? - listener.onComplete(true); + listener.onComplete(errors.isEmpty(), errors); } @Override public void onResponse(int correlationId, RPCResponse response) { + RPCRequest request = null; + for (RPCRequest r : requests) { + if (response.getCorrelationID().equals(r.getCorrelationID())) { + request = r; + break; + } + } if (!response.getSuccess()) { - DebugTool.logError(TAG, "Failed to send RPC. Result: " + response.getResultCode() + " Info: " + response.getInfo()); + errors.put(request, "Failed to send RPC. Result: " + response.getResultCode() + " Info: " + response.getInfo()); } + listener.onResponse(request, response, commandIdForRPCRequest(request)); } }); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/SendingRPCsCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/SendingRPCsCompletionListener.java new file mode 100644 index 000000000..72a15cce1 --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/SendingRPCsCompletionListener.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 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.menu; + +import com.smartdevicelink.proxy.RPCRequest; +import com.smartdevicelink.proxy.RPCResponse; + +import java.util.Map; + +/** + * Created by Bilal Alsharifi on 1/29/21. + */ +interface SendingRPCsCompletionListener { + void onComplete(boolean success, Map errors); + void onResponse(RPCRequest request, RPCResponse response, int commandId); +} -- cgit v1.2.1 From 690ab07aa3e94cc55b6d0312557a8197a8499a2d Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 2 Feb 2021 10:46:15 -0500 Subject: Use MenuReplaceDynamicOperation for both static and dynamic updates --- .../managers/screen/menu/BaseMenuManager.java | 7 ++----- .../screen/menu/DynamicMenuUpdateAlgorithm.java | 4 ++-- .../screen/menu/MenuReplaceDynamicOperation.java | 18 +++++++++++++++--- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 0b8f0345e..f665f13cd 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -208,11 +208,8 @@ abstract class BaseMenuManager extends BaseSubManager { } }; - if (isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType)) { - operation = new MenuReplaceDynamicOperation(internalInterface, fileManager.get(), windowCapability, menuConfiguration, currentMenuCells, menuCells, menuManagerCompletionListener); - } else { - operation = new MenuReplaceStaticOperation(internalInterface, fileManager.get(), windowCapability, menuConfiguration, currentMenuCells, menuCells, menuManagerCompletionListener); - } + boolean isDynamicMenuUpdateActive = isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType); + operation = new MenuReplaceDynamicOperation(internalInterface, fileManager.get(), windowCapability, menuConfiguration, currentMenuCells, menuCells, isDynamicMenuUpdateActive, menuManagerCompletionListener); transactionQueue.add(operation, false); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index d94ed5d3d..ea7c7fe89 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -76,7 +76,7 @@ class DynamicMenuUpdateAlgorithm { * Builds a 1-1 array of Deletes for every element in the array * @param oldMenu The old menu array */ - private static List buildAllDeleteStatusesForMenu (List oldMenu){ + static List buildAllDeleteStatusesForMenu (List oldMenu){ List oldMenuStatus = new ArrayList<>(oldMenu.size()); for (int index = 0; index < oldMenu.size(); index++) { oldMenuStatus.add(MenuCellState.DELETE); @@ -88,7 +88,7 @@ class DynamicMenuUpdateAlgorithm { * Builds a 1-1 array of Adds for every element in the array * @param newMenu The new menu array */ - private static List buildAllAddStatusesForMenu (List newMenu){ + static List buildAllAddStatusesForMenu (List newMenu){ List newMenuStatus = new ArrayList<>(newMenu.size()); for (int index = 0; index < newMenu.size(); index++) { newMenuStatus.add(MenuCellState.ADD); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java index 9739410cd..1b1c4c197 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java @@ -18,6 +18,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.buildAllAddStatusesForMenu; +import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.buildAllDeleteStatusesForMenu; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; @@ -35,10 +37,11 @@ class MenuReplaceDynamicOperation extends Task { private final WindowCapability windowCapability; private List currentMenu; private final List updatedMenu; + private final boolean isDynamicMenuUpdateActive; private final MenuManagerCompletionListener operationCompletionListener; private MenuConfiguration menuConfiguration; - MenuReplaceDynamicOperation(ISdl internalInterface, FileManager fileManager, WindowCapability windowCapability, MenuConfiguration menuConfiguration, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { + MenuReplaceDynamicOperation(ISdl internalInterface, FileManager fileManager, WindowCapability windowCapability, MenuConfiguration menuConfiguration, List currentMenu, List updatedMenu, boolean isDynamicMenuUpdateActive, MenuManagerCompletionListener operationCompletionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); @@ -46,6 +49,7 @@ class MenuReplaceDynamicOperation extends Task { this.menuConfiguration = menuConfiguration; this.currentMenu = currentMenu; this.updatedMenu = updatedMenu; + this.isDynamicMenuUpdateActive = isDynamicMenuUpdateActive; this.operationCompletionListener = operationCompletionListener; } @@ -72,11 +76,19 @@ class MenuReplaceDynamicOperation extends Task { return; } - DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); + DynamicMenuUpdateRunScore runScore = null; + + if (isDynamicMenuUpdateActive) { + DebugTool.logInfo(TAG, "Dynamic menu update is active. Running the algorithm to find the best run score"); + runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); + } else { + DebugTool.logInfo(TAG, "Dynamic menu update is not active. Forcing the deletion of all old cells and adding new ones"); + runScore = new DynamicMenuUpdateRunScore(buildAllDeleteStatusesForMenu(currentMenu), buildAllAddStatusesForMenu(updatedMenu), updatedMenu.size()); + } // If both old and new menu cells are empty. Then nothing needs to be done. if (runScore == null) { - finishOperation(true); + listener.onComplete(true); return; } -- cgit v1.2.1 From d1a53328eef1880c52de3c7aa958346d176896c4 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 2 Feb 2021 10:49:48 -0500 Subject: Remove MenuReplaceStaticOperation --- .../managers/screen/menu/BaseMenuManager.java | 16 +- .../screen/menu/MenuReplaceDynamicOperation.java | 317 --------------------- .../managers/screen/menu/MenuReplaceOperation.java | 317 +++++++++++++++++++++ .../screen/menu/MenuReplaceStaticOperation.java | 197 ------------- 4 files changed, 323 insertions(+), 524 deletions(-) delete mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java delete mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index f665f13cd..c428d2e7b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -192,7 +192,7 @@ abstract class BaseMenuManager extends BaseSubManager { // Cancel pending MenuReplaceOperations for (Task operation : transactionQueue.getTasksAsList()) { - if (operation instanceof MenuReplaceStaticOperation || operation instanceof MenuReplaceDynamicOperation) { + if (operation instanceof MenuReplaceOperation) { operation.cancelTask(); } } @@ -209,7 +209,7 @@ abstract class BaseMenuManager extends BaseSubManager { }; boolean isDynamicMenuUpdateActive = isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType); - operation = new MenuReplaceDynamicOperation(internalInterface, fileManager.get(), windowCapability, menuConfiguration, currentMenuCells, menuCells, isDynamicMenuUpdateActive, menuManagerCompletionListener); + operation = new MenuReplaceOperation(internalInterface, fileManager.get(), windowCapability, menuConfiguration, currentMenuCells, menuCells, isDynamicMenuUpdateActive, menuManagerCompletionListener); transactionQueue.add(operation, false); } @@ -308,10 +308,8 @@ abstract class BaseMenuManager extends BaseSubManager { // Update pending operations with new menuConfiguration for (Task task : transactionQueue.getTasksAsList()) { - if (task instanceof MenuReplaceStaticOperation) { - ((MenuReplaceStaticOperation) task).setMenuConfiguration(menuConfiguration); - } else if (task instanceof MenuReplaceDynamicOperation) { - ((MenuReplaceDynamicOperation) task).setMenuConfiguration(menuConfiguration); + if (task instanceof MenuReplaceOperation) { + ((MenuReplaceOperation) task).setMenuConfiguration(menuConfiguration); } } } @@ -441,10 +439,8 @@ abstract class BaseMenuManager extends BaseSubManager { private void updateMenuReplaceOperationsWithNewCurrentMenu () { for (Task task : transactionQueue.getTasksAsList()) { - if (task instanceof MenuReplaceStaticOperation) { - ((MenuReplaceStaticOperation) task).setCurrentMenu(this.currentMenuCells); - } else if (task instanceof MenuReplaceDynamicOperation) { - ((MenuReplaceDynamicOperation) task).setCurrentMenu(this.currentMenuCells); + if (task instanceof MenuReplaceOperation) { + ((MenuReplaceOperation) task).setCurrentMenu(this.currentMenuCells); } } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java deleted file mode 100644 index 1b1c4c197..000000000 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceDynamicOperation.java +++ /dev/null @@ -1,317 +0,0 @@ -package com.smartdevicelink.managers.screen.menu; - -import com.livio.taskmaster.Task; -import com.smartdevicelink.managers.CompletionListener; -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.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; -import com.smartdevicelink.proxy.RPCRequest; -import com.smartdevicelink.proxy.RPCResponse; -import com.smartdevicelink.proxy.rpc.WindowCapability; -import com.smartdevicelink.proxy.rpc.enums.MenuLayout; -import com.smartdevicelink.util.DebugTool; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.buildAllAddStatusesForMenu; -import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.buildAllDeleteStatusesForMenu; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.sendRPCs; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; - -/** - * Created by Bilal Alsharifi on 1/20/21. - */ -class MenuReplaceDynamicOperation extends Task { - private static final String TAG = "MenuReplaceDynamicOperation"; - - private final WeakReference internalInterface; - private final WeakReference fileManager; - private final WindowCapability windowCapability; - private List currentMenu; - private final List updatedMenu; - private final boolean isDynamicMenuUpdateActive; - private final MenuManagerCompletionListener operationCompletionListener; - private MenuConfiguration menuConfiguration; - - MenuReplaceDynamicOperation(ISdl internalInterface, FileManager fileManager, WindowCapability windowCapability, MenuConfiguration menuConfiguration, List currentMenu, List updatedMenu, boolean isDynamicMenuUpdateActive, MenuManagerCompletionListener operationCompletionListener) { - super(TAG); - this.internalInterface = new WeakReference<>(internalInterface); - this.fileManager = new WeakReference<>(fileManager); - this.windowCapability = windowCapability; - this.menuConfiguration = menuConfiguration; - this.currentMenu = currentMenu; - this.updatedMenu = updatedMenu; - this.isDynamicMenuUpdateActive = isDynamicMenuUpdateActive; - this.operationCompletionListener = operationCompletionListener; - } - - @Override - public void onExecute() { - start(); - } - - private void start() { - if (getState() == Task.CANCELED) { - return; - } - - updateMenuCells(new CompletionListener() { - @Override - public void onComplete(boolean success) { - finishOperation(success); - } - }); - } - - private void updateMenuCells(final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - DynamicMenuUpdateRunScore runScore = null; - - if (isDynamicMenuUpdateActive) { - DebugTool.logInfo(TAG, "Dynamic menu update is active. Running the algorithm to find the best run score"); - runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); - } else { - DebugTool.logInfo(TAG, "Dynamic menu update is not active. Forcing the deletion of all old cells and adding new ones"); - runScore = new DynamicMenuUpdateRunScore(buildAllDeleteStatusesForMenu(currentMenu), buildAllAddStatusesForMenu(updatedMenu), updatedMenu.size()); - } - - // If both old and new menu cells are empty. Then nothing needs to be done. - if (runScore == null) { - listener.onComplete(true); - return; - } - - List deleteMenuStatus = runScore.getOldStatus(); - List addMenuStatus = runScore.getUpdatedStatus(); - - final List cellsToDelete = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.DELETE); - final List cellsToAdd = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.ADD); - - // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares - final List oldKeeps = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.KEEP); - final List newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); - - // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. - // this is needed for the onCommands to still work - transferCellIDFromOldCells(oldKeeps, newKeeps); - - // Upload the Artworks - List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability); - if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { - fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { - @Override - public void onComplete(Map errors) { - if (errors != null && !errors.isEmpty()) { - DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); - } else { - DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); - } - updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, new CompletionListener() { - @Override - public void onComplete(boolean success) { - startSubMenuUpdatesWithOldKeptCells(oldKeeps, newKeeps, 0, listener); - } - }); - } - }); - } else { - // Cells have no artwork to load - updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, new CompletionListener() { - @Override - public void onComplete(boolean success) { - startSubMenuUpdatesWithOldKeptCells(oldKeeps, newKeeps, 0, listener); - } - }); - } - } - - private void updateMenuWithCellsToDelete(List deleteCells, final List addCells, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - sendDeleteCurrentMenu(deleteCells, new CompletionListener() { - @Override - public void onComplete(boolean success) { - sendNewMenuCells(addCells, currentMenu, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } - - listener.onComplete(success); - } - }); - } - }); - } - - private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (deleteMenuCells.isEmpty()) { - listener.onComplete(true); - return; - } - - List deleteMenuCommands = deleteCommandsForCells(deleteMenuCells); - sendRPCs(deleteMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { - @Override - public void onComplete(boolean success, Map errors) { - if (!success) { - DebugTool.logWarning(TAG, "Unable to delete all old menu commands" + errors); - } else { - DebugTool.logInfo(TAG, "Finished deleting old menu"); - } - listener.onComplete(success); - } - - @Override - public void onResponse(RPCRequest request, RPCResponse response, int commandId) { - - } - }); - } - - private void sendNewMenuCells(final List newMenuCells, final List oldMenu, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (newMenuCells == null || newMenuCells.isEmpty()) { - // This can be considered a success if the user was clearing out their menu - DebugTool.logInfo(TAG, "There are no cells to update."); - listener.onComplete(true); - return; - } - - MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; - - final List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); - final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); - - sendRPCs(mainMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { - @Override - public void onComplete(boolean success, Map errors) { - if (!success) { - DebugTool.logError(TAG, "Failed to send main menu commands" + errors); - listener.onComplete(false); - return; - } - sendRPCs(subMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { - @Override - public void onComplete(boolean success, Map errors) { - if (!success) { - DebugTool.logError(TAG, "Failed to send sub menu commands" + errors); - } else { - DebugTool.logInfo(TAG, "Finished updating menu"); - } - listener.onComplete(success); - } - - @Override - public void onResponse(RPCRequest request, RPCResponse response, int commandId) { - - } - }); - } - - @Override - public void onResponse(RPCRequest request, RPCResponse response, int commandId) { - - } - }); - } - - private void startSubMenuUpdatesWithOldKeptCells(final List oldKeptCells, final List newKeptCells, final int startIndex, final CompletionListener listener) { - if (oldKeptCells.isEmpty() || startIndex >= oldKeptCells.size()) { - listener.onComplete(true); - return; - } - - if (oldKeptCells.get(startIndex) != null && oldKeptCells.get(startIndex).getSubCells() != null && !oldKeptCells.get(startIndex).getSubCells().isEmpty()){ - DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); - - // If both old and new menu cells are empty. Then nothing needs to be done. - if (tempScore == null) { - finishOperation(true); - return; - } - - List deleteMenuStatus = tempScore.getOldStatus(); - List addMenuStatus = tempScore.getUpdatedStatus(); - - final List cellsToDelete = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.DELETE); - final List cellsToAdd = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.ADD); - - final List oldKeeps = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.KEEP); - final List newKeeps = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.KEEP); - - transferCellIDFromOldCells(oldKeeps, newKeeps); - - sendDeleteCurrentMenu(cellsToDelete, new CompletionListener() { - @Override - public void onComplete(boolean success) { - sendNewMenuCells(cellsToAdd, currentMenu.get(startIndex).getSubCells(), new CompletionListener() { - @Override - public void onComplete(boolean success) { - // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements - startSubMenuUpdatesWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); - } - }); - } - }); - } else { - // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements - startSubMenuUpdatesWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); - } - } - - private void transferCellIDFromOldCells(List oldCells, List newCells) { - if (oldCells.isEmpty()) { - return; - } - for (int i = 0; i < newCells.size(); i++) { - newCells.get(i).setCellId(oldCells.get(i).getCellId()); - } - } - - private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState){ - List filteredCells = new ArrayList<>(); - for (int index = 0; index < statusList.size(); index++) { - if (statusList.get(index).equals(menuCellState)) { - filteredCells.add(menuCells.get(index)); - } - } - return filteredCells; - } - - void setMenuConfiguration(MenuConfiguration menuConfiguration) { - this.menuConfiguration = menuConfiguration; - } - - public void setCurrentMenu(List currentMenuCells) { - this.currentMenu = currentMenuCells; - } - - private void finishOperation(boolean success) { - if (operationCompletionListener != null) { - operationCompletionListener.onComplete(success, currentMenu); - } - onFinished(); - } -} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java new file mode 100644 index 000000000..a45c2de9c --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -0,0 +1,317 @@ +package com.smartdevicelink.managers.screen.menu; + +import com.livio.taskmaster.Task; +import com.smartdevicelink.managers.CompletionListener; +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.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; +import com.smartdevicelink.proxy.RPCRequest; +import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.MenuLayout; +import com.smartdevicelink.util.DebugTool; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.buildAllAddStatusesForMenu; +import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.buildAllDeleteStatusesForMenu; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.sendRPCs; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; + +/** + * Created by Bilal Alsharifi on 1/20/21. + */ +class MenuReplaceOperation extends Task { + private static final String TAG = "MenuReplaceOperation"; + + private final WeakReference internalInterface; + private final WeakReference fileManager; + private final WindowCapability windowCapability; + private List currentMenu; + private final List updatedMenu; + private final boolean isDynamicMenuUpdateActive; + private final MenuManagerCompletionListener operationCompletionListener; + private MenuConfiguration menuConfiguration; + + MenuReplaceOperation(ISdl internalInterface, FileManager fileManager, WindowCapability windowCapability, MenuConfiguration menuConfiguration, List currentMenu, List updatedMenu, boolean isDynamicMenuUpdateActive, MenuManagerCompletionListener operationCompletionListener) { + super(TAG); + this.internalInterface = new WeakReference<>(internalInterface); + this.fileManager = new WeakReference<>(fileManager); + this.windowCapability = windowCapability; + this.menuConfiguration = menuConfiguration; + this.currentMenu = currentMenu; + this.updatedMenu = updatedMenu; + this.isDynamicMenuUpdateActive = isDynamicMenuUpdateActive; + this.operationCompletionListener = operationCompletionListener; + } + + @Override + public void onExecute() { + start(); + } + + private void start() { + if (getState() == Task.CANCELED) { + return; + } + + updateMenuCells(new CompletionListener() { + @Override + public void onComplete(boolean success) { + finishOperation(success); + } + }); + } + + private void updateMenuCells(final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + DynamicMenuUpdateRunScore runScore = null; + + if (isDynamicMenuUpdateActive) { + DebugTool.logInfo(TAG, "Dynamic menu update is active. Running the algorithm to find the best run score"); + runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); + } else { + DebugTool.logInfo(TAG, "Dynamic menu update is not active. Forcing the deletion of all old cells and adding new ones"); + runScore = new DynamicMenuUpdateRunScore(buildAllDeleteStatusesForMenu(currentMenu), buildAllAddStatusesForMenu(updatedMenu), updatedMenu.size()); + } + + // If both old and new menu cells are empty. Then nothing needs to be done. + if (runScore == null) { + listener.onComplete(true); + return; + } + + List deleteMenuStatus = runScore.getOldStatus(); + List addMenuStatus = runScore.getUpdatedStatus(); + + final List cellsToDelete = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.DELETE); + final List cellsToAdd = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.ADD); + + // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares + final List oldKeeps = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.KEEP); + final List newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); + + // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. + // this is needed for the onCommands to still work + transferCellIDFromOldCells(oldKeeps, newKeeps); + + // Upload the Artworks + List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability); + if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { + fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { + @Override + public void onComplete(Map errors) { + if (errors != null && !errors.isEmpty()) { + DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); + } else { + DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); + } + updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, new CompletionListener() { + @Override + public void onComplete(boolean success) { + startSubMenuUpdatesWithOldKeptCells(oldKeeps, newKeeps, 0, listener); + } + }); + } + }); + } else { + // Cells have no artwork to load + updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, new CompletionListener() { + @Override + public void onComplete(boolean success) { + startSubMenuUpdatesWithOldKeptCells(oldKeeps, newKeeps, 0, listener); + } + }); + } + } + + private void updateMenuWithCellsToDelete(List deleteCells, final List addCells, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + sendDeleteCurrentMenu(deleteCells, new CompletionListener() { + @Override + public void onComplete(boolean success) { + sendNewMenuCells(addCells, currentMenu, new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (!success) { + DebugTool.logError(TAG, "Error Sending Current Menu"); + } + + listener.onComplete(success); + } + }); + } + }); + } + + private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + if (deleteMenuCells.isEmpty()) { + listener.onComplete(true); + return; + } + + List deleteMenuCommands = deleteCommandsForCells(deleteMenuCells); + sendRPCs(deleteMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { + @Override + public void onComplete(boolean success, Map errors) { + if (!success) { + DebugTool.logWarning(TAG, "Unable to delete all old menu commands" + errors); + } else { + DebugTool.logInfo(TAG, "Finished deleting old menu"); + } + listener.onComplete(success); + } + + @Override + public void onResponse(RPCRequest request, RPCResponse response, int commandId) { + + } + }); + } + + private void sendNewMenuCells(final List newMenuCells, final List oldMenu, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + + if (newMenuCells == null || newMenuCells.isEmpty()) { + // This can be considered a success if the user was clearing out their menu + DebugTool.logInfo(TAG, "There are no cells to update."); + listener.onComplete(true); + return; + } + + MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; + + final List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); + final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); + + sendRPCs(mainMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { + @Override + public void onComplete(boolean success, Map errors) { + if (!success) { + DebugTool.logError(TAG, "Failed to send main menu commands" + errors); + listener.onComplete(false); + return; + } + sendRPCs(subMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { + @Override + public void onComplete(boolean success, Map errors) { + if (!success) { + DebugTool.logError(TAG, "Failed to send sub menu commands" + errors); + } else { + DebugTool.logInfo(TAG, "Finished updating menu"); + } + listener.onComplete(success); + } + + @Override + public void onResponse(RPCRequest request, RPCResponse response, int commandId) { + + } + }); + } + + @Override + public void onResponse(RPCRequest request, RPCResponse response, int commandId) { + + } + }); + } + + private void startSubMenuUpdatesWithOldKeptCells(final List oldKeptCells, final List newKeptCells, final int startIndex, final CompletionListener listener) { + if (oldKeptCells.isEmpty() || startIndex >= oldKeptCells.size()) { + listener.onComplete(true); + return; + } + + if (oldKeptCells.get(startIndex) != null && oldKeptCells.get(startIndex).getSubCells() != null && !oldKeptCells.get(startIndex).getSubCells().isEmpty()){ + DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); + + // If both old and new menu cells are empty. Then nothing needs to be done. + if (tempScore == null) { + finishOperation(true); + return; + } + + List deleteMenuStatus = tempScore.getOldStatus(); + List addMenuStatus = tempScore.getUpdatedStatus(); + + final List cellsToDelete = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.DELETE); + final List cellsToAdd = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.ADD); + + final List oldKeeps = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.KEEP); + final List newKeeps = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.KEEP); + + transferCellIDFromOldCells(oldKeeps, newKeeps); + + sendDeleteCurrentMenu(cellsToDelete, new CompletionListener() { + @Override + public void onComplete(boolean success) { + sendNewMenuCells(cellsToAdd, currentMenu.get(startIndex).getSubCells(), new CompletionListener() { + @Override + public void onComplete(boolean success) { + // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements + startSubMenuUpdatesWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + } + }); + } + }); + } else { + // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements + startSubMenuUpdatesWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + } + } + + private void transferCellIDFromOldCells(List oldCells, List newCells) { + if (oldCells.isEmpty()) { + return; + } + for (int i = 0; i < newCells.size(); i++) { + newCells.get(i).setCellId(oldCells.get(i).getCellId()); + } + } + + private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState){ + List filteredCells = new ArrayList<>(); + for (int index = 0; index < statusList.size(); index++) { + if (statusList.get(index).equals(menuCellState)) { + filteredCells.add(menuCells.get(index)); + } + } + return filteredCells; + } + + void setMenuConfiguration(MenuConfiguration menuConfiguration) { + this.menuConfiguration = menuConfiguration; + } + + public void setCurrentMenu(List currentMenuCells) { + this.currentMenu = currentMenuCells; + } + + private void finishOperation(boolean success) { + if (operationCompletionListener != null) { + operationCompletionListener.onComplete(success, currentMenu); + } + onFinished(); + } +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java deleted file mode 100644 index 415c20fec..000000000 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceStaticOperation.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.smartdevicelink.managers.screen.menu; - -import com.livio.taskmaster.Task; -import com.smartdevicelink.managers.CompletionListener; -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.proxy.RPCRequest; -import com.smartdevicelink.proxy.RPCResponse; -import com.smartdevicelink.proxy.rpc.WindowCapability; -import com.smartdevicelink.proxy.rpc.enums.MenuLayout; -import com.smartdevicelink.util.DebugTool; - -import java.lang.ref.WeakReference; -import java.util.List; -import java.util.Map; - -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.sendRPCs; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; - -/** - * Created by Bilal Alsharifi on 1/20/21. - */ -class MenuReplaceStaticOperation extends Task { - private static final String TAG = "MenuReplaceStaticOperation"; - - private final WeakReference internalInterface; - private final WeakReference fileManager; - private final WindowCapability windowCapability; - private List currentMenu; - private final List updatedMenu; - private final MenuManagerCompletionListener operationCompletionListener; - private MenuConfiguration menuConfiguration; - - MenuReplaceStaticOperation(ISdl internalInterface, FileManager fileManager, WindowCapability windowCapability, MenuConfiguration menuConfiguration, List currentMenu, List updatedMenu, MenuManagerCompletionListener operationCompletionListener) { - super(TAG); - this.internalInterface = new WeakReference<>(internalInterface); - this.fileManager = new WeakReference<>(fileManager); - this.windowCapability = windowCapability; - this.menuConfiguration = menuConfiguration; - this.currentMenu = currentMenu; - this.updatedMenu = updatedMenu; - this.operationCompletionListener = operationCompletionListener; - } - - @Override - public void onExecute() { - start(); - } - - private void start() { - if (getState() == Task.CANCELED) { - return; - } - - updateMenuCells(new CompletionListener() { - @Override - public void onComplete(boolean success) { - finishOperation(success); - } - }); - } - - private void updateMenuCells(final CompletionListener listener) { - List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability); - if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { - fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { - @Override - public void onComplete(Map errors) { - if (getState() == Task.CANCELED) { - return; - } - - if (errors != null && !errors.isEmpty()) { - DebugTool.logError(TAG, "Error uploading menu artworks: " + errors.toString()); - } else { - DebugTool.logInfo(TAG, "All menu artworks uploaded"); - } - - updateMenuWithCellsToDelete(currentMenu, updatedMenu, listener); - } - }); - } else { - // Cells have no artwork to load - updateMenuWithCellsToDelete(currentMenu, updatedMenu, listener); - } - } - - private void updateMenuWithCellsToDelete(final List deleteCells, final List addCells, final CompletionListener listener) { - sendDeleteCurrentMenu(deleteCells, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (getState() == Task.CANCELED) { - return; - } - - sendNewMenuCells(addCells, new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (!success) { - DebugTool.logError(TAG, "Error Sending Current Menu"); - } - - listener.onComplete(success); - } - }); - } - }); - } - - private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { - if (deleteMenuCells == null || deleteMenuCells.isEmpty()) { - // Technically this method is successful if there's nothing to delete - DebugTool.logInfo(TAG, "No old cells to delete, returning"); - listener.onComplete(true); - return; - } - List deleteMenuCommands = deleteCommandsForCells(deleteMenuCells); - - if (deleteMenuCommands.isEmpty()) { - // Technically this method is successful if there's nothing to delete - listener.onComplete(true); - return; - } - - sendRPCs(deleteMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { - @Override - public void onComplete(boolean success, Map errors) { - DebugTool.logInfo(TAG, "Successfully deleted cells"); - listener.onComplete(success); - } - - @Override - public void onResponse(RPCRequest request, RPCResponse response, int commandId) { - - } - }); - } - - private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { - if (newMenuCells == null || newMenuCells.isEmpty()) { - // This can be considered a success if the user was clearing out their menu - DebugTool.logInfo(TAG, "There are no cells to update."); - listener.onComplete(true); - return; - } - - MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; - - List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); - final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); - - sendRPCs(mainMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { - @Override - public void onComplete(boolean success, Map errors) { - DebugTool.logInfo(TAG, "Finished sending main menu commands. Sending sub menu commands."); - - sendRPCs(subMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { - @Override - public void onComplete(boolean success, Map errors) { - DebugTool.logInfo(TAG, "Finished Updating Menu"); - listener.onComplete(success); - } - - @Override - public void onResponse(RPCRequest request, RPCResponse response, int commandId) { - - } - }); - } - - @Override - public void onResponse(RPCRequest request, RPCResponse response, int commandId) { - - } - }); - } - - void setMenuConfiguration(MenuConfiguration menuConfiguration) { - this.menuConfiguration = menuConfiguration; - } - - public void setCurrentMenu(List currentMenuCells) { - this.currentMenu = currentMenuCells; - } - - private void finishOperation(boolean success) { - if (operationCompletionListener != null) { - operationCompletionListener.onComplete(success, currentMenu); - } - onFinished(); - } -} -- cgit v1.2.1 From 298fa97edcbbe54fed99ca3a664a08c8f0ffa8d7 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 2 Feb 2021 12:02:20 -0500 Subject: Cleanup MenuReplaceOperation --- .../managers/screen/menu/MenuReplaceOperation.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index a45c2de9c..7d55bdcc7 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -76,7 +76,7 @@ class MenuReplaceOperation extends Task { return; } - DynamicMenuUpdateRunScore runScore = null; + DynamicMenuUpdateRunScore runScore; if (isDynamicMenuUpdateActive) { DebugTool.logInfo(TAG, "Dynamic menu update is active. Running the algorithm to find the best run score"); @@ -103,7 +103,8 @@ class MenuReplaceOperation extends Task { final List newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. - // this is needed for the onCommands to still work + // This is needed for the onCommands to still work + // We will transfer the ids for subCells later transferCellIDFromOldCells(oldKeeps, newKeeps); // Upload the Artworks @@ -144,7 +145,7 @@ class MenuReplaceOperation extends Task { sendDeleteCurrentMenu(deleteCells, new CompletionListener() { @Override public void onComplete(boolean success) { - sendNewMenuCells(addCells, currentMenu, new CompletionListener() { + sendNewMenuCells(addCells, new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { @@ -187,13 +188,12 @@ class MenuReplaceOperation extends Task { }); } - private void sendNewMenuCells(final List newMenuCells, final List oldMenu, final CompletionListener listener) { + private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } if (newMenuCells == null || newMenuCells.isEmpty()) { - // This can be considered a success if the user was clearing out their menu DebugTool.logInfo(TAG, "There are no cells to update."); listener.onComplete(true); return; @@ -248,7 +248,8 @@ class MenuReplaceOperation extends Task { // If both old and new menu cells are empty. Then nothing needs to be done. if (tempScore == null) { - finishOperation(true); + // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements + startSubMenuUpdatesWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); return; } @@ -266,7 +267,7 @@ class MenuReplaceOperation extends Task { sendDeleteCurrentMenu(cellsToDelete, new CompletionListener() { @Override public void onComplete(boolean success) { - sendNewMenuCells(cellsToAdd, currentMenu.get(startIndex).getSubCells(), new CompletionListener() { + sendNewMenuCells(cellsToAdd, new CompletionListener() { @Override public void onComplete(boolean success) { // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements @@ -282,7 +283,7 @@ class MenuReplaceOperation extends Task { } private void transferCellIDFromOldCells(List oldCells, List newCells) { - if (oldCells.isEmpty()) { + if (oldCells == null || oldCells.isEmpty()) { return; } for (int i = 0; i < newCells.size(); i++) { -- cgit v1.2.1 From 2ca3dd9558179f074c0b403dddc68a2cebada150 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 2 Feb 2021 12:03:00 -0500 Subject: Reformat code in MenuReplaceOperation --- .../smartdevicelink/managers/screen/menu/MenuReplaceOperation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 7d55bdcc7..dd1853998 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -243,7 +243,7 @@ class MenuReplaceOperation extends Task { return; } - if (oldKeptCells.get(startIndex) != null && oldKeptCells.get(startIndex).getSubCells() != null && !oldKeptCells.get(startIndex).getSubCells().isEmpty()){ + if (oldKeptCells.get(startIndex) != null && oldKeptCells.get(startIndex).getSubCells() != null && !oldKeptCells.get(startIndex).getSubCells().isEmpty()) { DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); // If both old and new menu cells are empty. Then nothing needs to be done. @@ -291,7 +291,7 @@ class MenuReplaceOperation extends Task { } } - private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState){ + private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState) { List filteredCells = new ArrayList<>(); for (int index = 0; index < statusList.size(); index++) { if (statusList.get(index).equals(menuCellState)) { -- cgit v1.2.1 From b9acc323a6a0cb5b595f1c93d27fc7a06420b00e Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 2 Feb 2021 13:33:25 -0500 Subject: Reformat MenuReplaceUtilities.java --- .../managers/screen/menu/MenuReplaceUtilities.java | 56 +++++++++++----------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 6dad7da68..3ea90df15 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -28,34 +28,6 @@ import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdN * Created by Bilal Alsharifi on 1/25/21. */ class MenuReplaceUtilities { - static int commandIdForRPCRequest(RPCRequest request) { - int commandId = 0; - if (request instanceof AddCommand) { - commandId = ((AddCommand) request).getCmdID(); - } else if (request instanceof AddSubMenu) { - commandId = ((AddSubMenu) request).getMenuID(); - } else if (request instanceof DeleteCommand) { - commandId = ((DeleteCommand) request).getCmdID(); - } else if (request instanceof DeleteSubMenu) { - commandId = ((DeleteSubMenu) request).getMenuID(); - } - return commandId; - } - - static List deleteCommandsForCells(List cells) { - List deletes = new ArrayList<>(); - for (MenuCell cell : cells) { - if (cell.getSubCells() == null) { - DeleteCommand delete = new DeleteCommand(cell.getCellId()); - deletes.add(delete); - } else { - DeleteSubMenu delete = new DeleteSubMenu(cell.getCellId()); - deletes.add(delete); - } - } - return deletes; - } - static List findAllArtworksToBeUploadedFromCells(List cells, FileManager fileManager, WindowCapability windowCapability) { // Make sure we can use images in the menus if (!ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { @@ -84,6 +56,34 @@ class MenuReplaceUtilities { return (fileManager.hasUploadedFile(cell.getIcon()) || cell.getIcon().isStaticIcon()); } + static int commandIdForRPCRequest(RPCRequest request) { + int commandId = 0; + if (request instanceof AddCommand) { + commandId = ((AddCommand) request).getCmdID(); + } else if (request instanceof AddSubMenu) { + commandId = ((AddSubMenu) request).getMenuID(); + } else if (request instanceof DeleteCommand) { + commandId = ((DeleteCommand) request).getCmdID(); + } else if (request instanceof DeleteSubMenu) { + commandId = ((DeleteSubMenu) request).getMenuID(); + } + return commandId; + } + + static List deleteCommandsForCells(List cells) { + List deletes = new ArrayList<>(); + for (MenuCell cell : cells) { + if (cell.getSubCells() == null) { + DeleteCommand delete = new DeleteCommand(cell.getCellId()); + deletes.add(delete); + } else { + DeleteSubMenu delete = new DeleteSubMenu(cell.getCellId()); + deletes.add(delete); + } + } + return deletes; + } + static List mainMenuCommandsForCells(List cellsToAdd, FileManager fileManager, WindowCapability windowCapability, List updatedMenu, MenuLayout defaultSubmenuLayout) { List builtCommands = new ArrayList<>(); -- cgit v1.2.1 From 3fd33688f06d63806bb555ae2a2fb20560c47551 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 2 Feb 2021 14:27:49 -0500 Subject: Update currentMenu after receiving response for each request --- .../managers/screen/menu/MenuReplaceOperation.java | 24 +++- .../managers/screen/menu/MenuReplaceUtilities.java | 125 +++++++++++++++++---- .../screen/menu/SendingRPCsCompletionListener.java | 2 +- 3 files changed, 122 insertions(+), 29 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index dd1853998..b146fedad 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -20,9 +20,13 @@ import java.util.Map; import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.buildAllAddStatusesForMenu; import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.buildAllDeleteStatusesForMenu; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addMenuRequestWithCommandId; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandIdForRPCRequest; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.positionForRPCRequest; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.removeMenuCellFromList; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.sendRPCs; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; @@ -182,8 +186,10 @@ class MenuReplaceOperation extends Task { } @Override - public void onResponse(RPCRequest request, RPCResponse response, int commandId) { - + public void onResponse(RPCRequest request, RPCResponse response) { + // Find the id of the successful request and remove it from the current menu list wherever it may have been + int commandId = commandIdForRPCRequest(request); + removeMenuCellFromList(currentMenu, commandId); } }); } @@ -224,15 +230,21 @@ class MenuReplaceOperation extends Task { } @Override - public void onResponse(RPCRequest request, RPCResponse response, int commandId) { - + public void onResponse(RPCRequest request, RPCResponse response) { + // Find the id of the successful request and add it from the current menu list wherever it needs to be + int commandId = commandIdForRPCRequest(request); + int position = positionForRPCRequest(request); + addMenuRequestWithCommandId(commandId, position, newMenuCells, currentMenu); } }); } @Override - public void onResponse(RPCRequest request, RPCResponse response, int commandId) { - + public void onResponse(RPCRequest request, RPCResponse response) { + // Find the id of the successful request and add it from the current menu list wherever it needs to be + int commandId = commandIdForRPCRequest(request); + int position = positionForRPCRequest(request); + addMenuRequestWithCommandId(commandId, position, newMenuCells, currentMenu); } }); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 3ea90df15..e55dc75de 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -70,10 +70,20 @@ class MenuReplaceUtilities { return commandId; } + static int positionForRPCRequest(RPCRequest request) { + int position = 0; + if (request instanceof AddCommand) { + position = ((AddCommand) request).getMenuParams().getPosition(); + } else if (request instanceof AddSubMenu) { + position = ((AddSubMenu) request).getPosition(); + } + return position; + } + static List deleteCommandsForCells(List cells) { List deletes = new ArrayList<>(); for (MenuCell cell : cells) { - if (cell.getSubCells() == null) { + if (cell.getSubCells() == null || cell.getSubCells().isEmpty()) { DeleteCommand delete = new DeleteCommand(cell.getCellId()); deletes.add(delete); } else { @@ -84,52 +94,52 @@ class MenuReplaceUtilities { return deletes; } - static List mainMenuCommandsForCells(List cellsToAdd, FileManager fileManager, WindowCapability windowCapability, List updatedMenu, MenuLayout defaultSubmenuLayout) { - List builtCommands = new ArrayList<>(); + static List mainMenuCommandsForCells(List cells, FileManager fileManager, WindowCapability windowCapability, List menu, MenuLayout defaultSubmenuLayout) { + List commands = new ArrayList<>(); // We need the index so we will use this type of loop - for (int z = 0; z < updatedMenu.size(); z++) { - MenuCell mainCell = updatedMenu.get(z); - for (int i = 0; i < cellsToAdd.size(); i++) { - MenuCell addCell = cellsToAdd.get(i); + for (int menuInteger = 0; menuInteger < menu.size(); menuInteger++) { + MenuCell mainCell = menu.get(menuInteger); + for (int updateCellsIndex = 0; updateCellsIndex < cells.size(); updateCellsIndex++) { + MenuCell addCell = cells.get(updateCellsIndex); if (mainCell.equals(addCell)) { if (addCell.getSubCells() != null && !addCell.getSubCells().isEmpty()) { - builtCommands.add(subMenuCommandForMenuCell(addCell, fileManager, windowCapability, z, defaultSubmenuLayout)); + commands.add(subMenuCommandForMenuCell(addCell, fileManager, windowCapability, menuInteger, defaultSubmenuLayout)); } else { - builtCommands.add(commandForMenuCell(addCell, fileManager, windowCapability, z)); + commands.add(commandForMenuCell(addCell, fileManager, windowCapability, menuInteger)); } break; } } } - return builtCommands; + return commands; } static List subMenuCommandsForCells(List cells, FileManager fileManager, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { - List builtCommands = new ArrayList<>(); + List commands = new ArrayList<>(); for (MenuCell cell : cells) { if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - builtCommands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); + commands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); } } - return builtCommands; + return commands; } static List allCommandsForCells(List cells, FileManager fileManager, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { - List builtCommands = new ArrayList<>(); + List commands = new ArrayList<>(); - for (int i = 0; i < cells.size(); i++) { - MenuCell cell = cells.get(i); + for (int cellIndex = 0; cellIndex < cells.size(); cellIndex++) { + MenuCell cell = cells.get(cellIndex); if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - builtCommands.add(subMenuCommandForMenuCell(cell, fileManager, windowCapability, i, defaultSubmenuLayout)); + commands.add(subMenuCommandForMenuCell(cell, fileManager, windowCapability, cellIndex, defaultSubmenuLayout)); // recursively grab the commands for all the sub cells - builtCommands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); + commands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); } else { - builtCommands.add(commandForMenuCell(cell, fileManager, windowCapability, i)); + commands.add(commandForMenuCell(cell, fileManager, windowCapability, cellIndex)); } } - return builtCommands; + return commands; } static AddCommand commandForMenuCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position) { @@ -169,6 +179,76 @@ class MenuReplaceUtilities { .setMenuIcon(icon); } + static boolean removeMenuCellFromList(List menuCellList, int commandId) { + for (MenuCell menuCell : menuCellList) { + if (menuCell.getCellId() == commandId) { + // If the cell id matches the command id, remove it from the list and return + menuCellList.remove(menuCell); + return true; + } else if (menuCell.getSubCells() != null && !menuCell.getSubCells().isEmpty()) { + // If the menu cell has sub cells, we need to recurse and check the sub cells + List newList = menuCell.getSubCells(); + boolean foundAndRemovedItem = removeMenuCellFromList(newList, commandId); + if (foundAndRemovedItem) { + menuCell.setSubCells(newList); + return true; + } + } + } + return false; + } + + static boolean addMenuRequestWithCommandId(int commandId, int position, List newMenuList, List mainMenuList) { + MenuCell addedCell = null; + for (MenuCell cell : newMenuList) { + if (cell.getCellId() == commandId) { + addedCell = cell; + break; + } else if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + return addMenuRequestWithCommandId(commandId, position, cell.getSubCells(), mainMenuList); + } + } + if (addedCell != null) { + addMenuCell(addedCell, mainMenuList, position); + return true; + } + return false; + } + + private static boolean addMenuCell(MenuCell cell, List menuCellList, int position) { + if (cell.getParentCellId() != parentIdNotFound) { + // If the cell has a parent id, we need to find the cell with a matching cell id and insert it into its submenu + for (MenuCell menuCell : menuCellList) { + if (menuCell.getCellId() == cell.getParentCellId()) { + // If we found the correct submenu, insert it into that submenu + insertMenuCell(cell, menuCell.getSubCells(), position); + return true; + } else if (menuCell.getSubCells() != null && !menuCell.getSubCells().isEmpty()) { + // Check the sub cells of this cell to see if any of those have cell ids that match the parent cell id + List newList = menuCell.getSubCells(); + boolean foundAndAddedItem = addMenuCell(cell, newList, position); + if (foundAndAddedItem) { + menuCell.setSubCells(newList); + return true; + } + } + } + } else { + // The cell does not have a parent id, just insert it into the main menu + insertMenuCell(cell, menuCellList, position); + return true; + } + return false; + } + + private static void insertMenuCell(MenuCell cell, List cellList, int position) { + if (position > cellList.size()) { + cellList.add(cell); + } else { + cellList.add(position, cell); + } + } + static void sendRPCs(final List requests, ISdl internalInterface, final SendingRPCsCompletionListener listener) { final Map errors = new HashMap<>(); if (requests == null || requests.isEmpty()) { @@ -197,8 +277,9 @@ class MenuReplaceUtilities { if (!response.getSuccess()) { errors.put(request, "Failed to send RPC. Result: " + response.getResultCode() + " Info: " + response.getInfo()); } - listener.onResponse(request, response, commandIdForRPCRequest(request)); + listener.onResponse(request, response); } }); } -} + +} \ No newline at end of file diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/SendingRPCsCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/SendingRPCsCompletionListener.java index 72a15cce1..1339df1eb 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/SendingRPCsCompletionListener.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/SendingRPCsCompletionListener.java @@ -42,5 +42,5 @@ import java.util.Map; */ interface SendingRPCsCompletionListener { void onComplete(boolean success, Map errors); - void onResponse(RPCRequest request, RPCResponse response, int commandId); + void onResponse(RPCRequest request, RPCResponse response); } -- cgit v1.2.1 From 7c1cb77466f3a375ec06d4b9aa95b4ec4eb60890 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 2 Feb 2021 14:45:16 -0500 Subject: Simplify updateTransactionQueueSuspended() --- .../managers/screen/menu/BaseMenuManager.java | 53 ++++++++++------------ 1 file changed, 24 insertions(+), 29 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index c428d2e7b..20758a38b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -73,7 +73,6 @@ abstract class BaseMenuManager extends BaseSubManager { private List menuCells; private DynamicMenuUpdatesMode dynamicMenuUpdatesMode; private MenuConfiguration menuConfiguration; - private SdlMsgVersion sdlMsgVersion; private String displayType; private HMILevel oldHMILevel; private HMILevel currentHMILevel; @@ -90,14 +89,14 @@ abstract class BaseMenuManager extends BaseSubManager { super(internalInterface); this.lastMenuId = menuCellIdMin; + this.menuConfiguration = new MenuConfiguration(null, null); + this.menuCells = new ArrayList<>(); + this.currentMenuCells = new ArrayList<>(); + this.dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; + this.transactionQueue = newTransactionQueue(); this.fileManager = new WeakReference<>(fileManager); this.currentSystemContext = SystemContext.SYSCTXT_MAIN; this.currentHMILevel = HMILevel.HMI_NONE; - this.dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; - this.sdlMsgVersion = internalInterface.getSdlMsgVersion(); - this.transactionQueue = newTransactionQueue(); - this.menuCells = new ArrayList<>(); - this.currentMenuCells = new ArrayList<>(); addListeners(); } @@ -117,7 +116,6 @@ abstract class BaseMenuManager extends BaseSubManager { dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; windowCapability = null; menuConfiguration = null; - sdlMsgVersion = null; // Cancel the operations if (transactionQueue != null) { @@ -135,6 +133,23 @@ abstract class BaseMenuManager extends BaseSubManager { super.dispose(); } + private Queue newTransactionQueue() { + Queue queue = internalInterface.getTaskmaster().createQueue("MenuManager", 7, false); + queue.pause(); + return queue; + } + + // Suspend the queue if the HMI level is NONE since we want to delay sending RPCs until we're in non-NONE + private void updateTransactionQueueSuspended() { + if (currentHMILevel == HMILevel.HMI_NONE || currentSystemContext == SystemContext.SYSCTXT_MENU) { + DebugTool.logInfo(TAG, String.format("Suspending the transaction queue. Current HMI level is: %s, current system context is: %s", currentHMILevel, currentSystemContext)); + transactionQueue.pause(); + } else { + DebugTool.logInfo(TAG, "Starting the transaction queue"); + transactionQueue.resume(); + } + } + public void setDynamicUpdatesMode(@NonNull DynamicMenuUpdatesMode value) { this.dynamicMenuUpdatesMode = value; } @@ -243,7 +258,7 @@ abstract class BaseMenuManager extends BaseSubManager { } else if (cell != null && foundClonedCell == null) { DebugTool.logError(DebugTool.TAG, "This cell has not been sent to the head unit, so no submenu can be opened. Make sure that the cell exists in the SDLManager.menu array"); return false; - } else if (sdlMsgVersion.getMajorVersion() < 6) { + } else if (internalInterface.getSdlMsgVersion().getMajorVersion() < 6) { DebugTool.logWarning(TAG, "The openSubmenu method is not supported on this head unit."); return false; } @@ -286,6 +301,7 @@ abstract class BaseMenuManager extends BaseSubManager { * @param menuConfiguration - The default menuConfiguration */ public void setMenuConfiguration(@NonNull final MenuConfiguration menuConfiguration) { + SdlMsgVersion sdlMsgVersion = internalInterface.getSdlMsgVersion(); if (sdlMsgVersion == null) { DebugTool.logError(TAG, "SDL Message Version is null. Cannot set Menu Configuration"); return; @@ -385,27 +401,6 @@ abstract class BaseMenuManager extends BaseSubManager { internalInterface.addOnRPCNotificationListener(FunctionID.ON_COMMAND, commandListener); } - private Queue newTransactionQueue() { - Queue queue = internalInterface.getTaskmaster().createQueue("MenuManager", 7, false); - queue.pause(); - return queue; - } - - private void updateTransactionQueueSuspended() { - if (oldHMILevel == HMILevel.HMI_NONE && currentHMILevel != HMILevel.HMI_NONE && currentSystemContext != SystemContext.SYSCTXT_MENU) { - // Resume queue if we were in NONE and now we are not - DebugTool.logInfo(TAG, "We now have proper HMI, sending waiting update"); - transactionQueue.resume(); - } else if (oldSystemContext == SystemContext.SYSCTXT_MENU && currentSystemContext != SystemContext.SYSCTXT_MENU && currentHMILevel != HMILevel.HMI_NONE) { - // If we don't check for this and only update when not in the menu, there can be IN_USE errors, especially with submenus. - // We also don't want to encourage changing out the menu while the user is using it for usability reasons. - DebugTool.logInfo(TAG, "We now have a proper system context, sending waiting update"); - transactionQueue.resume(); - } else if (currentHMILevel == HMILevel.HMI_NONE || currentSystemContext == SystemContext.SYSCTXT_MENU){ - transactionQueue.pause(); - } - } - private List cloneMenuCellsList(List originalList) { if (originalList == null) { return null; -- cgit v1.2.1 From 210be579fd2a07e6fbc36e7075f43924c4d896ed Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 2 Feb 2021 15:12:23 -0500 Subject: Add MenuConfiguration equals() --- .../managers/screen/menu/MenuConfiguration.java | 32 ++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfiguration.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfiguration.java index f478c80fc..9ae1ae28d 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfiguration.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfiguration.java @@ -101,4 +101,36 @@ public class MenuConfiguration { return "MenuConfiguration: MenuLayout = " + this.mainMenuLayout + " | SubMenuLayout = " + this.submenuLayout; } + /** + * Note: You should compare using the {@link #equals(Object)} method.
+ * Hash the parameters of the object and return the result for comparison + * @return the hash code as an int + */ + @Override + public int hashCode() { + int result = 1; + result += ((getMenuLayout() == null) ? 0 : Integer.rotateLeft(getMenuLayout().hashCode(), 1)); + result += ((getSubMenuLayout() == null) ? 0 : Integer.rotateLeft(getSubMenuLayout().hashCode(), 2)); + return result; + } + + /** + * Uses our custom hashCode for MenuConfiguration objects + * + * @param o - The object to compare + * @return boolean of whether the objects are the same or not + */ + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + // if this is the same memory address, its the same + if (this == o) return true; + // if this is not an instance of this class, not the same + if (!(o instanceof MenuConfiguration)) return false; + // if we get to this point, create the hashes and compare them + return hashCode() == o.hashCode(); + } + } -- cgit v1.2.1 From d90ea3ec5e02629074d17493b38371b66cb20612 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 2 Feb 2021 15:12:37 -0500 Subject: Cleanup BaseMenuManager --- .../managers/screen/menu/BaseMenuManager.java | 48 +++++++++++----------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 20758a38b..24f7d442f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -74,9 +74,7 @@ abstract class BaseMenuManager extends BaseSubManager { private DynamicMenuUpdatesMode dynamicMenuUpdatesMode; private MenuConfiguration menuConfiguration; private String displayType; - private HMILevel oldHMILevel; private HMILevel currentHMILevel; - private SystemContext oldSystemContext; private SystemContext currentSystemContext; private OnRPCNotificationListener hmiListener; private OnRPCNotificationListener commandListener; @@ -179,7 +177,6 @@ abstract class BaseMenuManager extends BaseSubManager { HashSet titleCheckSet = new HashSet<>(); HashSet allMenuVoiceCommands = new HashSet<>(); int voiceCommandCount = 0; - for (MenuCell cell : clonedCells) { titleCheckSet.add(cell.getTitle()); if (cell.getVoiceCommands() != null) { @@ -202,29 +199,23 @@ abstract class BaseMenuManager extends BaseSubManager { updateIdsOnMenuCells(clonedCells, parentIdNotFound); - currentMenuCells = new ArrayList<>(menuCells); menuCells = new ArrayList<>(clonedCells); - // Cancel pending MenuReplaceOperations - for (Task operation : transactionQueue.getTasksAsList()) { - if (operation instanceof MenuReplaceOperation) { - operation.cancelTask(); - } - } - - // Checks against what the developer set for update mode and against the display type to - // determine how the menu will be updated. This has the ability to be changed during a session. - Task operation; - MenuManagerCompletionListener menuManagerCompletionListener = new MenuManagerCompletionListener() { + boolean isDynamicMenuUpdateActive = isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType); + Task operation = new MenuReplaceOperation(internalInterface, fileManager.get(), windowCapability, menuConfiguration, currentMenuCells, menuCells, isDynamicMenuUpdateActive, new MenuManagerCompletionListener() { @Override public void onComplete(boolean success, List currentMenuCells) { BaseMenuManager.this.currentMenuCells = currentMenuCells; updateMenuReplaceOperationsWithNewCurrentMenu(); } - }; + }); - boolean isDynamicMenuUpdateActive = isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType); - operation = new MenuReplaceOperation(internalInterface, fileManager.get(), windowCapability, menuConfiguration, currentMenuCells, menuCells, isDynamicMenuUpdateActive, menuManagerCompletionListener); + // Cancel previous MenuReplaceOperations + for (Task task : transactionQueue.getTasksAsList()) { + if (task instanceof MenuReplaceOperation) { + task.cancelTask(); + } + } transactionQueue.add(operation, false); } @@ -317,15 +308,24 @@ abstract class BaseMenuManager extends BaseSubManager { return; } + if (menuConfiguration.equals(this.menuConfiguration)) { + DebugTool.logInfo(TAG, "New menu configuration is equal to existing one, will not set new configuration"); + return; + } + MenuConfigurationUpdateOperation operation = new MenuConfigurationUpdateOperation(internalInterface, windowCapability, menuConfiguration, new CompletionListener() { @Override public void onComplete(boolean success) { - BaseMenuManager.this.menuConfiguration = menuConfiguration; + if (!success) { + DebugTool.logError(TAG, "Error setting new menu configuration. Will revert to old menu configuration."); + } else { + BaseMenuManager.this.menuConfiguration = menuConfiguration; - // Update pending operations with new menuConfiguration - for (Task task : transactionQueue.getTasksAsList()) { - if (task instanceof MenuReplaceOperation) { - ((MenuReplaceOperation) task).setMenuConfiguration(menuConfiguration); + // Update pending operations with new menuConfiguration + for (Task task : transactionQueue.getTasksAsList()) { + if (task instanceof MenuReplaceOperation) { + ((MenuReplaceOperation) task).setMenuConfiguration(menuConfiguration); + } } } } @@ -382,9 +382,7 @@ abstract class BaseMenuManager extends BaseSubManager { if (onHMIStatus.getWindowID() != null && onHMIStatus.getWindowID() != PredefinedWindows.DEFAULT_WINDOW.getValue()) { return; } - oldHMILevel = currentHMILevel; currentHMILevel = onHMIStatus.getHmiLevel(); - oldSystemContext = currentSystemContext; currentSystemContext = onHMIStatus.getSystemContext(); updateTransactionQueueSuspended(); } -- cgit v1.2.1 From 2d726ca3c2113234a724d991bd607c1c9e15dd7a Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 2 Feb 2021 15:40:27 -0500 Subject: Cleanup MenuConfigurationUpdateOperation.java --- .../managers/screen/menu/BaseMenuManager.java | 16 ------- .../menu/MenuConfigurationUpdateOperation.java | 56 +++++++++++++++++----- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 24f7d442f..c8069e02a 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -292,22 +292,6 @@ abstract class BaseMenuManager extends BaseSubManager { * @param menuConfiguration - The default menuConfiguration */ public void setMenuConfiguration(@NonNull final MenuConfiguration menuConfiguration) { - SdlMsgVersion sdlMsgVersion = internalInterface.getSdlMsgVersion(); - if (sdlMsgVersion == null) { - DebugTool.logError(TAG, "SDL Message Version is null. Cannot set Menu Configuration"); - return; - } - - if (sdlMsgVersion.getMajorVersion() < 6) { - DebugTool.logWarning(TAG, "Menu configurations is only supported on head units with RPC spec version 6.0.0 or later. Currently connected head unit RPC spec version is: " + sdlMsgVersion.getMajorVersion() + "." + sdlMsgVersion.getMinorVersion() + "." + sdlMsgVersion.getPatchVersion()); - return; - } - - if (menuConfiguration.getMenuLayout() == null) { - DebugTool.logInfo(TAG, "Menu Layout is null, not sending setGlobalProperties"); - return; - } - if (menuConfiguration.equals(this.menuConfiguration)) { DebugTool.logInfo(TAG, "New menu configuration is equal to existing one, will not set new configuration"); return; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java index 95ed76217..2a4cb43d2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java @@ -36,6 +36,7 @@ import com.livio.taskmaster.Task; import com.smartdevicelink.managers.CompletionListener; import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.rpc.SdlMsgVersion; import com.smartdevicelink.proxy.rpc.SetGlobalProperties; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; @@ -52,14 +53,14 @@ class MenuConfigurationUpdateOperation extends Task { private static final String TAG = "MenuConfigurationUpdateOperation"; private final WeakReference internalInterface; private final List availableMenuLayouts; - private final MenuConfiguration menuConfiguration; + private final MenuConfiguration updatedMenuConfiguration; private final CompletionListener completionListener; MenuConfigurationUpdateOperation(ISdl internalInterface, WindowCapability windowCapability, MenuConfiguration menuConfiguration, CompletionListener completionListener) { super(TAG); this.internalInterface = new WeakReference<>(internalInterface); this.availableMenuLayouts = windowCapability != null ? windowCapability.getMenuLayoutsAvailable() : null; - this.menuConfiguration = menuConfiguration; + this.updatedMenuConfiguration = menuConfiguration; this.completionListener = completionListener; } @@ -73,29 +74,58 @@ class MenuConfigurationUpdateOperation extends Task { return; } + sendSetGlobalProperties(new CompletionListener() { + @Override + public void onComplete(boolean success) { + finishOperation(success); + } + }); + } + + private void sendSetGlobalProperties(final CompletionListener listener) { + if (internalInterface.get() == null) { + listener.onComplete(false); + return; + } + + SdlMsgVersion sdlMsgVersion = internalInterface.get().getSdlMsgVersion(); + if (sdlMsgVersion == null) { + DebugTool.logError(TAG, "SDL Message Version is null. Cannot set Menu Configuration"); + listener.onComplete(false); + return; + } + + if (sdlMsgVersion.getMajorVersion() < 6) { + DebugTool.logWarning(TAG, "Menu configurations is only supported on head units with RPC spec version 6.0.0 or later. Currently connected head unit RPC spec version is: " + sdlMsgVersion.getMajorVersion() + "." + sdlMsgVersion.getMinorVersion() + "." + sdlMsgVersion.getPatchVersion()); + listener.onComplete(false); + return; + } + + if (updatedMenuConfiguration.getMenuLayout() == null) { + DebugTool.logInfo(TAG, "Menu Layout is null, not sending setGlobalProperties"); + listener.onComplete(false); + return; + } + if (availableMenuLayouts == null) { DebugTool.logWarning(TAG, "Could not set the main menu configuration. Which menu layouts can be used is not available"); - finishOperation(false); - } else if (!availableMenuLayouts.contains(menuConfiguration.getMenuLayout()) || !availableMenuLayouts.contains(menuConfiguration.getSubMenuLayout())) { - DebugTool.logError(TAG, String.format("One or more of the set menu layouts are not available on this system. The menu configuration will not be set. Available menu layouts: %s, set menu layouts: %s", availableMenuLayouts, menuConfiguration)); - finishOperation(false); + listener.onComplete(false); + } else if (!availableMenuLayouts.contains(updatedMenuConfiguration.getMenuLayout()) || !availableMenuLayouts.contains(updatedMenuConfiguration.getSubMenuLayout())) { + DebugTool.logError(TAG, String.format("One or more of the set menu layouts are not available on this system. The menu configuration will not be set. Available menu layouts: %s, set menu layouts: %s", availableMenuLayouts, updatedMenuConfiguration)); + listener.onComplete(false); } - sendSetGlobalProperties(); - } - - private void sendSetGlobalProperties() { SetGlobalProperties setGlobalProperties = new SetGlobalProperties(); - setGlobalProperties.setMenuLayout(menuConfiguration.getMenuLayout()); + setGlobalProperties.setMenuLayout(updatedMenuConfiguration.getMenuLayout()); setGlobalProperties.setOnRPCResponseListener(new OnRPCResponseListener() { @Override public void onResponse(int correlationId, RPCResponse response) { if (response.getSuccess()) { - DebugTool.logInfo(TAG, "Menu Configuration successfully set: " + menuConfiguration.toString()); + DebugTool.logInfo(TAG, "Menu Configuration successfully set: " + updatedMenuConfiguration.toString()); } else { DebugTool.logError(TAG, "onError: " + response.getResultCode() + " | Info: " + response.getInfo()); } - finishOperation(response.getSuccess()); + listener.onComplete(response.getSuccess()); } }); if (internalInterface.get() != null) { -- cgit v1.2.1 From 3b79057ef139fc4a4e7a8ab5418924fcf19ce398 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 2 Feb 2021 15:40:37 -0500 Subject: Cleanup MenuShowOperation --- .../com/smartdevicelink/managers/screen/menu/MenuShowOperation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java index 6c0c8519c..b5172cf99 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java @@ -76,9 +76,9 @@ class MenuShowOperation extends Task { @Override public void onResponse(int correlationId, RPCResponse response) { if (response.getSuccess()) { - DebugTool.logInfo(TAG, "Open Menu Request Successful"); + DebugTool.logInfo(TAG, "Successfully opened application menu"); } else { - DebugTool.logError(TAG, "Open Menu Request Failed"); + DebugTool.logError(TAG, "Open Menu Request Failed. Result code: " + response.getResultCode() + ". Info: "+ response.getInfo()); } onFinished(); } -- cgit v1.2.1 From f4ff734326ae31e61fcbe96e37e108216d9bd691 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 2 Feb 2021 16:17:15 -0500 Subject: Cleanup MenuReplaceOperation --- .../managers/screen/menu/MenuReplaceOperation.java | 37 +++++++++++----------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index b146fedad..f5b6d7470 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -76,10 +76,6 @@ class MenuReplaceOperation extends Task { } private void updateMenuCells(final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - DynamicMenuUpdateRunScore runScore; if (isDynamicMenuUpdateActive) { @@ -142,10 +138,6 @@ class MenuReplaceOperation extends Task { } private void updateMenuWithCellsToDelete(List deleteCells, final List addCells, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - sendDeleteCurrentMenu(deleteCells, new CompletionListener() { @Override public void onComplete(boolean success) { @@ -168,7 +160,7 @@ class MenuReplaceOperation extends Task { return; } - if (deleteMenuCells.isEmpty()) { + if (deleteMenuCells == null || deleteMenuCells.isEmpty()) { listener.onComplete(true); return; } @@ -218,6 +210,11 @@ class MenuReplaceOperation extends Task { listener.onComplete(false); return; } + + if (getState() == Task.CANCELED) { + return; + } + sendRPCs(subMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { @Override public void onComplete(boolean success, Map errors) { @@ -250,6 +247,10 @@ class MenuReplaceOperation extends Task { } private void startSubMenuUpdatesWithOldKeptCells(final List oldKeptCells, final List newKeptCells, final int startIndex, final CompletionListener listener) { + if (getState() == Task.CANCELED) { + return; + } + if (oldKeptCells.isEmpty() || startIndex >= oldKeptCells.size()) { listener.onComplete(true); return; @@ -294,15 +295,6 @@ class MenuReplaceOperation extends Task { } } - private void transferCellIDFromOldCells(List oldCells, List newCells) { - if (oldCells == null || oldCells.isEmpty()) { - return; - } - for (int i = 0; i < newCells.size(); i++) { - newCells.get(i).setCellId(oldCells.get(i).getCellId()); - } - } - private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState) { List filteredCells = new ArrayList<>(); for (int index = 0; index < statusList.size(); index++) { @@ -313,6 +305,15 @@ class MenuReplaceOperation extends Task { return filteredCells; } + private void transferCellIDFromOldCells(List oldCells, List newCells) { + if (oldCells == null || oldCells.isEmpty()) { + return; + } + for (int i = 0; i < newCells.size(); i++) { + newCells.get(i).setCellId(oldCells.get(i).getCellId()); + } + } + void setMenuConfiguration(MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } -- cgit v1.2.1 From 615e49be5ead6dd7083e8cfc4d650506eced57f4 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 3 Feb 2021 11:18:43 -0500 Subject: Fix DynamicMenuUpdateRunScoreTests --- .../managers/screen/ScreenManagerTests.java | 10 ++++++---- .../menu/DynamicMenuUpdateRunScoreTests.java | 14 +++++++++++--- .../managers/screen/menu/BaseMenuManager.java | 22 +++++++++++----------- .../screen/menu/DynamicMenuUpdateRunScore.java | 1 - 4 files changed, 28 insertions(+), 19 deletions(-) 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..dcf531c15 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 @@ -62,14 +62,15 @@ public class ScreenManagerTests { assertNull(screenManager.getTextField2Type()); assertNull(screenManager.getTextField3Type()); assertNull(screenManager.getTextField4Type()); - assertNull(screenManager.getMenu()); + assertTrue(screenManager.getMenu().isEmpty()); assertNull(screenManager.getVoiceCommands()); assertTrue(screenManager.getSoftButtonObjects().isEmpty()); assertNull(screenManager.getSoftButtonObjectByName("test")); assertNull(screenManager.getSoftButtonObjectById(1)); assertEquals(screenManager.getDynamicMenuUpdatesMode(), DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE); assertEquals(screenManager.getState(), BaseSubManager.READY); - assertNull(screenManager.getMenuConfiguration()); + assertNull(screenManager.getMenuConfiguration().getMenuLayout()); + assertNull(screenManager.getMenuConfiguration().getSubMenuLayout()); } @Test @@ -141,10 +142,11 @@ public class ScreenManagerTests { screenManager.setMenu(TestValues.GENERAL_MENUCELL_LIST); screenManager.setMenuConfiguration(TestValues.GENERAL_MENU_CONFIGURATION); - assertEquals(screenManager.getMenu(), TestValues.GENERAL_MENUCELL_LIST); assertEquals(screenManager.getDynamicMenuUpdatesMode(), DynamicMenuUpdatesMode.FORCE_ON); + assertEquals(screenManager.getMenu(), TestValues.GENERAL_MENUCELL_LIST); // Should not set because of improper RAI response and improper HMI states - assertNull(screenManager.getMenuConfiguration()); + assertNull(screenManager.getMenuConfiguration().getMenuLayout()); + assertNull(screenManager.getMenuConfiguration().getSubMenuLayout()); } @Test diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScoreTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScoreTests.java index 7ea079d8f..69350965a 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScoreTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScoreTests.java @@ -39,6 +39,12 @@ import com.smartdevicelink.test.TestValues; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Arrays; +import java.util.List; + +import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState.ADD; +import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState.DELETE; +import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState.KEEP; import static junit.framework.TestCase.assertEquals; @RunWith(AndroidJUnit4.class) @@ -48,12 +54,14 @@ public class DynamicMenuUpdateRunScoreTests { public void testSettersAndGetters() { // set everything - we only use the constructor to set variables in the Menu Manager - DynamicMenuUpdateRunScore runScore = new DynamicMenuUpdateRunScore(TestValues.GENERAL_INT, TestValues.GENERAL_INTEGER_LIST, TestValues.GENERAL_INTEGER_LIST); + List oldStatus = Arrays.asList(KEEP, DELETE); + List updatedStatus = Arrays.asList(KEEP, ADD); + DynamicMenuUpdateRunScore runScore = new DynamicMenuUpdateRunScore(oldStatus, updatedStatus, TestValues.GENERAL_INT); // use getters and assert equality assertEquals(runScore.getScore(), TestValues.GENERAL_INT); - assertEquals(runScore.getCurrentMenu(), TestValues.GENERAL_INTEGER_LIST); - assertEquals(runScore.getOldMenu(), TestValues.GENERAL_INTEGER_LIST); + assertEquals(runScore.getOldStatus(), oldStatus); + assertEquals(runScore.getUpdatedStatus(), updatedStatus); } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index c8069e02a..6de157aaf 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -69,19 +69,19 @@ abstract class BaseMenuManager extends BaseSubManager { static final int parentIdNotFound = 2000000000; private final WeakReference fileManager; - private List currentMenuCells; - private List menuCells; - private DynamicMenuUpdatesMode dynamicMenuUpdatesMode; - private MenuConfiguration menuConfiguration; + List currentMenuCells; + List menuCells; + DynamicMenuUpdatesMode dynamicMenuUpdatesMode; + MenuConfiguration menuConfiguration; private String displayType; - private HMILevel currentHMILevel; - private SystemContext currentSystemContext; - private OnRPCNotificationListener hmiListener; - private OnRPCNotificationListener commandListener; - private OnSystemCapabilityListener onDisplaysCapabilityListener; - private WindowCapability windowCapability; + HMILevel currentHMILevel; + SystemContext currentSystemContext; + OnRPCNotificationListener hmiListener; + OnRPCNotificationListener commandListener; + OnSystemCapabilityListener onDisplaysCapabilityListener; + WindowCapability windowCapability; private Queue transactionQueue; - private int lastMenuId; + int lastMenuId; BaseMenuManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) { super(internalInterface); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java index 10741ddc8..2cc93f2f1 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java @@ -44,7 +44,6 @@ class DynamicMenuUpdateRunScore { setOldStatus(oldStatus); setUpdatedStatus(updatedStatus); setScore(score); - } private void setUpdatedStatus(List updatedStatus) { -- cgit v1.2.1 From 0f586bd073cd24403f62a3501898925464eaab0c Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 3 Feb 2021 16:02:38 -0500 Subject: Fix menu manager unit tests - part 1 --- .../managers/screen/menu/MenuManagerTests.java | 1034 ++++++++++---------- .../managers/screen/menu/BaseMenuManager.java | 2 +- 2 files changed, 520 insertions(+), 516 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index 61f65b7ae..3be2dd3ca 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -34,6 +34,7 @@ package com.smartdevicelink.managers.screen.menu; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.livio.taskmaster.Taskmaster; import com.smartdevicelink.R; import com.smartdevicelink.managers.BaseSubManager; import com.smartdevicelink.managers.CompletionListener; @@ -41,6 +42,7 @@ import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.managers.file.FileManager; import com.smartdevicelink.managers.file.filetypes.SdlArtwork; import com.smartdevicelink.protocol.enums.FunctionID; +import com.smartdevicelink.proxy.RPCMessage; import com.smartdevicelink.proxy.RPCRequest; import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.OnCommand; @@ -53,6 +55,7 @@ import com.smartdevicelink.proxy.rpc.enums.HMILevel; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.proxy.rpc.enums.SystemContext; import com.smartdevicelink.proxy.rpc.enums.TriggerSource; +import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener; import org.junit.After; @@ -67,6 +70,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertNotNull; @@ -78,6 +82,7 @@ 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; /** * the Algorithm specific tests are defined based on: https://github.com/smartdevicelink/sdl_evolution/blob/master/proposals/0210-mobile-dynamic-menu-cell-updating.md @@ -123,7 +128,29 @@ public class MenuManagerTests { }; doAnswer(onCommandAnswer).when(internalInterface).addOnRPCNotificationListener(eq(FunctionID.ON_COMMAND), any(OnRPCNotificationListener.class)); - Answer answer = new Answer() { + // When internalInterface.sendRPCs() is called, call listener.onFinished() to fake the response + final Answer answer = new Answer() { + @Override + public Void answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + List rpcs = (List) args[0]; + OnMultipleRequestListener listener = (OnMultipleRequestListener) args[1]; + + for (RPCMessage rpcMessage : rpcs) { + RPCRequest request = (RPCRequest) rpcMessage; + RPCResponse response = new RPCResponse(request.getFunctionID().toString()); + response.setCorrelationID(request.getCorrelationID()); + response.setSuccess(true); + listener.onResponse(request.getCorrelationID(), response); + } + + listener.onFinished(); + return null; + } + }; + doAnswer(answer).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); + + Answer setGlobalPropertiesAnswer = new Answer() { @Override public Void answer(InvocationOnMock invocation) { Object[] args = invocation.getArguments(); @@ -134,105 +161,74 @@ public class MenuManagerTests { return null; } }; - doAnswer(answer).when(internalInterface).sendRPC(any(SetGlobalProperties.class)); + doAnswer(setGlobalPropertiesAnswer).when(internalInterface).sendRPC(any(SetGlobalProperties.class)); + // Create MenuManager + Taskmaster taskmaster = new Taskmaster.Builder().build(); + taskmaster.start(); + when(internalInterface.getTaskmaster()).thenReturn(taskmaster); menuManager = new MenuManager(internalInterface, fileManager); // Check some stuff during setup - assertEquals(menuManager.currentHMILevel, HMILevel.HMI_NONE); - assertEquals(menuManager.getState(), BaseSubManager.SETTING_UP); - assertEquals(menuManager.currentSystemContext, SystemContext.SYSCTXT_MAIN); - assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE); - assertEquals(menuManager.lastMenuId, 1); - assertNull(menuManager.menuCells); - assertNull(menuManager.waitingUpdateMenuCells); - assertNull(menuManager.oldMenuCells); - assertNull(menuManager.inProgressUpdate); - assertNull(menuManager.keepsNew); - assertNull(menuManager.keepsOld); - assertNull(menuManager.menuConfiguration); + assertEquals(HMILevel.HMI_NONE, menuManager.currentHMILevel); + assertEquals(BaseSubManager.SETTING_UP, menuManager.getState()); + assertEquals(SystemContext.SYSCTXT_MAIN, menuManager.currentSystemContext); + assertEquals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE, menuManager.dynamicMenuUpdatesMode); + assertEquals(menuCellIdMin, menuManager.lastMenuId); + assertTrue(menuManager.menuCells.isEmpty()); + assertTrue(menuManager.currentMenuCells.isEmpty()); + assertNull(menuManager.menuConfiguration.getMenuLayout()); + assertNull(menuManager.menuConfiguration.getSubMenuLayout()); assertNotNull(menuManager.hmiListener); assertNotNull(menuManager.commandListener); assertNotNull(menuManager.onDisplaysCapabilityListener); - - } - - @After - public void tearDown() throws Exception { - - menuManager.dispose(); - - assertEquals(menuManager.currentSystemContext, SystemContext.SYSCTXT_MAIN); - assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE); - assertEquals(menuManager.lastMenuId, 1); - assertNull(menuManager.menuCells); - assertNull(menuManager.oldMenuCells); - assertNull(menuManager.currentHMILevel); - assertNull(menuManager.defaultMainWindowCapability); - assertNull(menuManager.inProgressUpdate); - assertNull(menuManager.waitingUpdateMenuCells); - assertNull(menuManager.keepsNew); - assertNull(menuManager.keepsOld); - assertNull(menuManager.menuConfiguration); - - // after everything, make sure we are in the correct state - assertEquals(menuManager.getState(), BaseSubManager.SHUTDOWN); - } @Test public void testStartMenuManager() { - menuManager.start(new CompletionListener() { @Override public void onComplete(boolean success) { assertTrue(success); // Make sure the state has changed, as the Screen Manager is dependant on it - assertEquals(menuManager.getState(), BaseSubManager.READY); + assertEquals(BaseSubManager.READY, menuManager.getState()); } }); } @Test public void testHMINotReady() { - - menuManager.currentHMILevel = HMILevel.HMI_NONE; menuManager.setMenuCells(cells); + assertEquals(HMILevel.HMI_NONE, menuManager.currentHMILevel); + assertTrue(menuManager.currentMenuCells.isEmpty()); - // updating voice commands before HMI is ready - assertTrue(menuManager.waitingOnHMIUpdate); - // these are the 2 commands we have waiting - assertEquals(menuManager.waitingUpdateMenuCells.size(), 4); - assertEquals(menuManager.currentHMILevel, HMILevel.HMI_NONE); // The Menu Manager should send new menu once HMI full occurs sendFakeCoreOnHMIFullNotifications(); + // Listener should be triggered - which sets new HMI level and should proceed to send our pending update - assertEquals(menuManager.currentHMILevel, HMILevel.HMI_FULL); - // This being false means it received the hmi notification and sent the pending commands - assertFalse(menuManager.waitingOnHMIUpdate); + assertEquals(HMILevel.HMI_FULL, menuManager.currentHMILevel); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(cells, menuManager.currentMenuCells); } @Test public void testUpdatingOldWay() { - // Force Menu Manager to use the old way of deleting / sending all menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_OFF); assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_OFF); // when we only send one command to update, we should only be returned one add command List newArray = Arrays.asList(mainCell1, mainCell4); - assertEquals(menuManager.allCommandsForCells(newArray, false).size(), 4); // 1 root cells, 1 sub menu root cell, 2 sub menu cells + assertEquals(MenuReplaceUtilities.allCommandsForCells(newArray, menuManager.fileManager.get(), menuManager.windowCapability, MenuLayout.LIST).size(), 4); // 1 root cells, 1 sub menu root cell, 2 sub menu cells menuManager.currentHMILevel = HMILevel.HMI_FULL; menuManager.setMenuCells(newArray); - // Algorithm should NOT have run - assertNull(menuManager.keepsNew); - assertNull(menuManager.keepsOld); - // Unlike voice commands, the Menu Manager dynamically assigns Cell ID's. Because of this, we need to get the updated - // cell list after setting it and then test the listeners, as they use the newly assigned cell ID's. - List updatedCells = menuManager.getMenuCells(); - - for (MenuCell cell : updatedCells) { + // Sleep to give time to Taskmaster to run the operations + sleep(); + for (MenuCell cell : menuManager.currentMenuCells) { // grab 2 of our newly updated cells - 1 root and 1 sub cell, and make sure they can get triggered if (cell.getTitle().equalsIgnoreCase("Test Cell 1")) { // Fake onCommand - we want to make sure that we can pass back onCommand events to our root Menu Cell @@ -258,277 +254,277 @@ public class MenuManagerTests { } } - @Test - public void testAlgorithmTest1() { - - // Force Menu Manager to use the new way - menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); - assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); - - // start fresh - menuManager.oldMenuCells = null; - menuManager.menuCells = null; - menuManager.inProgressUpdate = null; - menuManager.waitingUpdateMenuCells = null; - menuManager.waitingOnHMIUpdate = false; - - menuManager.currentHMILevel = HMILevel.HMI_FULL; - // send new cells. They should set the old way - List oldMenu = createDynamicMenu1(); - List newMenu = createDynamicMenu1New(); - menuManager.setMenuCells(oldMenu); - assertEquals(menuManager.menuCells.size(), 4); - - // this happens in the menu manager but lets make sure its behaving - DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); - - List oldMenuScore = Arrays.asList(0, 0, 0, 0); - List newMenuScore = Arrays.asList(0, 0, 0, 0, 1); - - assertEquals(runScore.getScore(), 1); - assertEquals(runScore.getOldMenu(), oldMenuScore); - assertEquals(runScore.getCurrentMenu(), newMenuScore); - - menuManager.setMenuCells(newMenu); - assertEquals(menuManager.menuCells.size(), 5); - assertEquals(menuManager.keepsNew.size(), 4); - assertEquals(menuManager.keepsOld.size(), 4); - } - - @Test - public void testAlgorithmTest2() { - - // Force Menu Manager to use the new way - menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); - assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); - - // start fresh - menuManager.oldMenuCells = null; - menuManager.menuCells = null; - menuManager.inProgressUpdate = null; - menuManager.waitingUpdateMenuCells = null; - menuManager.waitingOnHMIUpdate = false; - - menuManager.currentHMILevel = HMILevel.HMI_FULL; - // send new cells. They should set the old way - List oldMenu = createDynamicMenu2(); - List newMenu = createDynamicMenu2New(); - menuManager.setMenuCells(oldMenu); - assertEquals(menuManager.menuCells.size(), 4); - - // this happens in the menu manager but lets make sure its behaving - DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); - - List oldMenuScore = Arrays.asList(0, 0, 0, 2); - List newMenuScore = Arrays.asList(0, 0, 0); - - assertEquals(runScore.getScore(), 0); - assertEquals(runScore.getOldMenu(), oldMenuScore); - assertEquals(runScore.getCurrentMenu(), newMenuScore); - - menuManager.setMenuCells(newMenu); - assertEquals(menuManager.menuCells.size(), 3); - assertEquals(menuManager.keepsNew.size(), 3); - assertEquals(menuManager.keepsOld.size(), 3); - } - - @Test - public void testAlgorithmTest3() { - - // Force Menu Manager to use the new way - menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); - assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); - - // start fresh - menuManager.oldMenuCells = null; - menuManager.menuCells = null; - menuManager.inProgressUpdate = null; - menuManager.waitingUpdateMenuCells = null; - menuManager.waitingOnHMIUpdate = false; - - menuManager.currentHMILevel = HMILevel.HMI_FULL; - // send new cells. They should set the old way - List oldMenu = createDynamicMenu3(); - List newMenu = createDynamicMenu3New(); - menuManager.setMenuCells(oldMenu); - assertEquals(menuManager.menuCells.size(), 3); - - // this happens in the menu manager but lets make sure its behaving - DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); - - List oldMenuScore = Arrays.asList(2, 2, 2); - List newMenuScore = Arrays.asList(1, 1, 1); - - assertEquals(runScore.getScore(), 3); - assertEquals(runScore.getOldMenu(), oldMenuScore); - assertEquals(runScore.getCurrentMenu(), newMenuScore); - - menuManager.setMenuCells(newMenu); - assertEquals(menuManager.menuCells.size(), 3); - assertEquals(menuManager.keepsNew.size(), 0); - assertEquals(menuManager.keepsOld.size(), 0); - } - - @Test - public void testAlgorithmTest4() { - - // Force Menu Manager to use the new way - menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); - assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); - - // start fresh - menuManager.oldMenuCells = null; - menuManager.menuCells = null; - menuManager.inProgressUpdate = null; - menuManager.waitingUpdateMenuCells = null; - menuManager.waitingOnHMIUpdate = false; - - menuManager.currentHMILevel = HMILevel.HMI_FULL; - // send new cells. They should set the old way - List oldMenu = createDynamicMenu4(); - List newMenu = createDynamicMenu4New(); - menuManager.setMenuCells(oldMenu); - assertEquals(menuManager.menuCells.size(), 4); - - // this happens in the menu manager but lets make sure its behaving - DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); - - List oldMenuScore = Arrays.asList(0, 2, 0, 2); - List newMenuScore = Arrays.asList(1, 0, 1, 0); - - assertEquals(runScore.getScore(), 2); - assertEquals(runScore.getOldMenu(), oldMenuScore); - assertEquals(runScore.getCurrentMenu(), newMenuScore); - - menuManager.setMenuCells(newMenu); - assertEquals(menuManager.menuCells.size(), 4); - assertEquals(menuManager.keepsNew.size(), 2); - assertEquals(menuManager.keepsOld.size(), 2); - } - - @Test - public void testAlgorithmTest5() { - - // Force Menu Manager to use the new way - menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); - assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); - - // start fresh - menuManager.oldMenuCells = null; - menuManager.menuCells = null; - menuManager.inProgressUpdate = null; - menuManager.waitingUpdateMenuCells = null; - menuManager.waitingOnHMIUpdate = false; - - menuManager.currentHMILevel = HMILevel.HMI_FULL; - // send new cells. They should set the old way - List oldMenu = createDynamicMenu5(); - List newMenu = createDynamicMenu5New(); - menuManager.setMenuCells(oldMenu); - assertEquals(menuManager.menuCells.size(), 4); - - // this happens in the menu manager but lets make sure its behaving - DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); - - List oldMenuScore = Arrays.asList(2, 0, 0, 0); - List newMenuScore = Arrays.asList(0, 0, 0, 1); - - assertEquals(runScore.getScore(), 1); - assertEquals(runScore.getOldMenu(), oldMenuScore); - assertEquals(runScore.getCurrentMenu(), newMenuScore); - - menuManager.setMenuCells(newMenu); - assertEquals(menuManager.menuCells.size(), 4); - assertEquals(menuManager.keepsNew.size(), 3); - assertEquals(menuManager.keepsOld.size(), 3); - } - - @Test - public void testSettingNullMenu() { - - // Make sure we can send an empty menu with no issues - // start fresh - menuManager.oldMenuCells = null; - menuManager.menuCells = null; - menuManager.inProgressUpdate = null; - menuManager.waitingUpdateMenuCells = null; - menuManager.waitingOnHMIUpdate = false; - - menuManager.currentHMILevel = HMILevel.HMI_FULL; - // send new cells. They should set the old way - List oldMenu = createDynamicMenu1(); - List newMenu = null; - menuManager.setMenuCells(oldMenu); - assertEquals(menuManager.menuCells.size(), 4); - - menuManager.setMenuCells(newMenu); - assertEquals(menuManager.menuCells.size(), 0); - } - - @Test - public void testClearingMenu() { - - // Make sure we can send an empty menu with no issues - // start fresh - menuManager.oldMenuCells = null; - menuManager.menuCells = null; - menuManager.inProgressUpdate = null; - menuManager.waitingUpdateMenuCells = null; - menuManager.waitingOnHMIUpdate = false; - - menuManager.currentHMILevel = HMILevel.HMI_FULL; - // send new cells. They should set the old way - List oldMenu = createDynamicMenu1(); - List newMenu = Collections.emptyList(); - menuManager.setMenuCells(oldMenu); - assertEquals(menuManager.menuCells.size(), 4); - - menuManager.setMenuCells(newMenu); - assertEquals(menuManager.menuCells.size(), 0); - } - - @Test - public void testOpeningMainMenu() { - // call open Menu - MenuManager mockMenuManager = mock(MenuManager.class); - mockMenuManager.openMenu(); - verify(mockMenuManager, Mockito.times(1)).openMenu(); - } - - @Test - public void testOpeningSubMenuNullCells() { - // call open Menu - MenuManager mockMenuManager = mock(MenuManager.class); - MenuCell cell = mock(MenuCell.class); - mockMenuManager.oldMenuCells = null; - assertFalse(mockMenuManager.openSubMenu(cell)); - } - - @Test - public void testOpeningSubMenu() { - // call open Menu - List testCells = createTestCells(); - menuManager.oldMenuCells = testCells; - menuManager.sdlMsgVersion = new SdlMsgVersion(6, 0); - // has to get success response to be true - assertTrue(menuManager.openSubMenu(testCells.get(3))); - } - - @Test - public void testSetMenuConfiguration() { - menuManager.currentHMILevel = HMILevel.HMI_FULL; - menuManager.currentSystemContext = SystemContext.SYSCTXT_MAIN; - menuManager.sdlMsgVersion = new SdlMsgVersion(6, 0); - menuManager.defaultMainWindowCapability = new WindowCapability(); - - List menuLayouts = Arrays.asList(MenuLayout.LIST, MenuLayout.TILES); - menuManager.defaultMainWindowCapability.setMenuLayoutsAvailable(menuLayouts); - - MenuConfiguration menuConfigurationTest = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); - menuManager.setMenuConfiguration(menuConfigurationTest); - assertEquals(menuManager.menuConfiguration, menuConfigurationTest); - - } +// @Test +// public void testAlgorithmTest1() { +// +// // Force Menu Manager to use the new way +// menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); +// assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); +// +// // start fresh +// menuManager.oldMenuCells = null; +// menuManager.menuCells = null; +// menuManager.inProgressUpdate = null; +// menuManager.waitingUpdateMenuCells = null; +// menuManager.waitingOnHMIUpdate = false; +// +// menuManager.currentHMILevel = HMILevel.HMI_FULL; +// // send new cells. They should set the old way +// List oldMenu = createDynamicMenu1(); +// List newMenu = createDynamicMenu1New(); +// menuManager.setMenuCells(oldMenu); +// assertEquals(menuManager.menuCells.size(), 4); +// +// // this happens in the menu manager but lets make sure its behaving +// DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); +// +// List oldMenuScore = Arrays.asList(0, 0, 0, 0); +// List newMenuScore = Arrays.asList(0, 0, 0, 0, 1); +// +// assertEquals(runScore.getScore(), 1); +// assertEquals(runScore.getOldMenu(), oldMenuScore); +// assertEquals(runScore.getCurrentMenu(), newMenuScore); +// +// menuManager.setMenuCells(newMenu); +// assertEquals(menuManager.menuCells.size(), 5); +// assertEquals(menuManager.keepsNew.size(), 4); +// assertEquals(menuManager.keepsOld.size(), 4); +// } + +// @Test +// public void testAlgorithmTest2() { +// +// // Force Menu Manager to use the new way +// menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); +// assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); +// +// // start fresh +// menuManager.oldMenuCells = null; +// menuManager.menuCells = null; +// menuManager.inProgressUpdate = null; +// menuManager.waitingUpdateMenuCells = null; +// menuManager.waitingOnHMIUpdate = false; +// +// menuManager.currentHMILevel = HMILevel.HMI_FULL; +// // send new cells. They should set the old way +// List oldMenu = createDynamicMenu2(); +// List newMenu = createDynamicMenu2New(); +// menuManager.setMenuCells(oldMenu); +// assertEquals(menuManager.menuCells.size(), 4); +// +// // this happens in the menu manager but lets make sure its behaving +// DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); +// +// List oldMenuScore = Arrays.asList(0, 0, 0, 2); +// List newMenuScore = Arrays.asList(0, 0, 0); +// +// assertEquals(runScore.getScore(), 0); +// assertEquals(runScore.getOldMenu(), oldMenuScore); +// assertEquals(runScore.getCurrentMenu(), newMenuScore); +// +// menuManager.setMenuCells(newMenu); +// assertEquals(menuManager.menuCells.size(), 3); +// assertEquals(menuManager.keepsNew.size(), 3); +// assertEquals(menuManager.keepsOld.size(), 3); +// } +// +// @Test +// public void testAlgorithmTest3() { +// +// // Force Menu Manager to use the new way +// menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); +// assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); +// +// // start fresh +// menuManager.oldMenuCells = null; +// menuManager.menuCells = null; +// menuManager.inProgressUpdate = null; +// menuManager.waitingUpdateMenuCells = null; +// menuManager.waitingOnHMIUpdate = false; +// +// menuManager.currentHMILevel = HMILevel.HMI_FULL; +// // send new cells. They should set the old way +// List oldMenu = createDynamicMenu3(); +// List newMenu = createDynamicMenu3New(); +// menuManager.setMenuCells(oldMenu); +// assertEquals(menuManager.menuCells.size(), 3); +// +// // this happens in the menu manager but lets make sure its behaving +// DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); +// +// List oldMenuScore = Arrays.asList(2, 2, 2); +// List newMenuScore = Arrays.asList(1, 1, 1); +// +// assertEquals(runScore.getScore(), 3); +// assertEquals(runScore.getOldMenu(), oldMenuScore); +// assertEquals(runScore.getCurrentMenu(), newMenuScore); +// +// menuManager.setMenuCells(newMenu); +// assertEquals(menuManager.menuCells.size(), 3); +// assertEquals(menuManager.keepsNew.size(), 0); +// assertEquals(menuManager.keepsOld.size(), 0); +// } +// +// @Test +// public void testAlgorithmTest4() { +// +// // Force Menu Manager to use the new way +// menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); +// assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); +// +// // start fresh +// menuManager.oldMenuCells = null; +// menuManager.menuCells = null; +// menuManager.inProgressUpdate = null; +// menuManager.waitingUpdateMenuCells = null; +// menuManager.waitingOnHMIUpdate = false; +// +// menuManager.currentHMILevel = HMILevel.HMI_FULL; +// // send new cells. They should set the old way +// List oldMenu = createDynamicMenu4(); +// List newMenu = createDynamicMenu4New(); +// menuManager.setMenuCells(oldMenu); +// assertEquals(menuManager.menuCells.size(), 4); +// +// // this happens in the menu manager but lets make sure its behaving +// DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); +// +// List oldMenuScore = Arrays.asList(0, 2, 0, 2); +// List newMenuScore = Arrays.asList(1, 0, 1, 0); +// +// assertEquals(runScore.getScore(), 2); +// assertEquals(runScore.getOldMenu(), oldMenuScore); +// assertEquals(runScore.getCurrentMenu(), newMenuScore); +// +// menuManager.setMenuCells(newMenu); +// assertEquals(menuManager.menuCells.size(), 4); +// assertEquals(menuManager.keepsNew.size(), 2); +// assertEquals(menuManager.keepsOld.size(), 2); +// } +// +// @Test +// public void testAlgorithmTest5() { +// +// // Force Menu Manager to use the new way +// menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); +// assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); +// +// // start fresh +// menuManager.oldMenuCells = null; +// menuManager.menuCells = null; +// menuManager.inProgressUpdate = null; +// menuManager.waitingUpdateMenuCells = null; +// menuManager.waitingOnHMIUpdate = false; +// +// menuManager.currentHMILevel = HMILevel.HMI_FULL; +// // send new cells. They should set the old way +// List oldMenu = createDynamicMenu5(); +// List newMenu = createDynamicMenu5New(); +// menuManager.setMenuCells(oldMenu); +// assertEquals(menuManager.menuCells.size(), 4); +// +// // this happens in the menu manager but lets make sure its behaving +// DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); +// +// List oldMenuScore = Arrays.asList(2, 0, 0, 0); +// List newMenuScore = Arrays.asList(0, 0, 0, 1); +// +// assertEquals(runScore.getScore(), 1); +// assertEquals(runScore.getOldMenu(), oldMenuScore); +// assertEquals(runScore.getCurrentMenu(), newMenuScore); +// +// menuManager.setMenuCells(newMenu); +// assertEquals(menuManager.menuCells.size(), 4); +// assertEquals(menuManager.keepsNew.size(), 3); +// assertEquals(menuManager.keepsOld.size(), 3); +// } +// +// @Test +// public void testSettingNullMenu() { +// +// // Make sure we can send an empty menu with no issues +// // start fresh +// menuManager.oldMenuCells = null; +// menuManager.menuCells = null; +// menuManager.inProgressUpdate = null; +// menuManager.waitingUpdateMenuCells = null; +// menuManager.waitingOnHMIUpdate = false; +// +// menuManager.currentHMILevel = HMILevel.HMI_FULL; +// // send new cells. They should set the old way +// List oldMenu = createDynamicMenu1(); +// List newMenu = null; +// menuManager.setMenuCells(oldMenu); +// assertEquals(menuManager.menuCells.size(), 4); +// +// menuManager.setMenuCells(newMenu); +// assertEquals(menuManager.menuCells.size(), 0); +// } +// +// @Test +// public void testClearingMenu() { +// +// // Make sure we can send an empty menu with no issues +// // start fresh +// menuManager.oldMenuCells = null; +// menuManager.menuCells = null; +// menuManager.inProgressUpdate = null; +// menuManager.waitingUpdateMenuCells = null; +// menuManager.waitingOnHMIUpdate = false; +// +// menuManager.currentHMILevel = HMILevel.HMI_FULL; +// // send new cells. They should set the old way +// List oldMenu = createDynamicMenu1(); +// List newMenu = Collections.emptyList(); +// menuManager.setMenuCells(oldMenu); +// assertEquals(menuManager.menuCells.size(), 4); +// +// menuManager.setMenuCells(newMenu); +// assertEquals(menuManager.menuCells.size(), 0); +// } +// +// @Test +// public void testOpeningMainMenu() { +// // call open Menu +// MenuManager mockMenuManager = mock(MenuManager.class); +// mockMenuManager.openMenu(); +// verify(mockMenuManager, Mockito.times(1)).openMenu(); +// } +// +// @Test +// public void testOpeningSubMenuNullCells() { +// // call open Menu +// MenuManager mockMenuManager = mock(MenuManager.class); +// MenuCell cell = mock(MenuCell.class); +// mockMenuManager.oldMenuCells = null; +// assertFalse(mockMenuManager.openSubMenu(cell)); +// } +// +// @Test +// public void testOpeningSubMenu() { +// // call open Menu +// List testCells = createTestCells(); +// menuManager.oldMenuCells = testCells; +// menuManager.sdlMsgVersion = new SdlMsgVersion(6, 0); +// // has to get success response to be true +// assertTrue(menuManager.openSubMenu(testCells.get(3))); +// } +// +// @Test +// public void testSetMenuConfiguration() { +// menuManager.currentHMILevel = HMILevel.HMI_FULL; +// menuManager.currentSystemContext = SystemContext.SYSCTXT_MAIN; +// menuManager.sdlMsgVersion = new SdlMsgVersion(6, 0); +// menuManager.defaultMainWindowCapability = new WindowCapability(); +// +// List menuLayouts = Arrays.asList(MenuLayout.LIST, MenuLayout.TILES); +// menuManager.defaultMainWindowCapability.setMenuLayoutsAvailable(menuLayouts); +// +// MenuConfiguration menuConfigurationTest = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); +// menuManager.setMenuConfiguration(menuConfigurationTest); +// assertEquals(menuManager.menuConfiguration, menuConfigurationTest); +// +// } // HELPERS @@ -536,6 +532,7 @@ public class MenuManagerTests { private void sendFakeCoreOnHMIFullNotifications() { OnHMIStatus onHMIStatusFakeNotification = new OnHMIStatus(); onHMIStatusFakeNotification.setHmiLevel(HMILevel.HMI_FULL); + onHMIStatusFakeNotification.setSystemContext(SystemContext.SYSCTXT_MAIN); onHMIStatusListener.onNotified(onHMIStatusFakeNotification); } @@ -569,189 +566,196 @@ public class MenuManagerTests { return Arrays.asList(mainCell1, mainCell2, mainCell3, mainCell4); } - - private List createDynamicMenu1() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); - - MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); - - MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); - - MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); - - return Arrays.asList(A, B, C, D); - - } - - private List createDynamicMenu1New() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerE = mock(MenuSelectionListener.class); - - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); - - MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); - - MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); - - MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); - - MenuCell E = new MenuCell("E", null, null, menuSelectionListenerE); - - return Arrays.asList(A, B, C, D, E); - - } - - private List createDynamicMenu2() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); - - MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); - - MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); - - MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); - - return Arrays.asList(A, B, C, D); - - } - - private List createDynamicMenu2New() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); - - MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); - - MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); - - return Arrays.asList(A, B, C); - - } - - private List createDynamicMenu3() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); - - MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); - - MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); - - return Arrays.asList(A, B, C); - - } - - private List createDynamicMenu3New() { - - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerE = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerF = mock(MenuSelectionListener.class); - - MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); - - MenuCell E = new MenuCell("E", null, null, menuSelectionListenerE); - - MenuCell F = new MenuCell("F", null, null, menuSelectionListenerF); - - return Arrays.asList(D, E, F); - - } - - private List createDynamicMenu4() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); - - MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); - - MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); - - MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); - - return Arrays.asList(A, B, C, D); - - } - - private List createDynamicMenu4New() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); - - MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); - - MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); - - MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); - - return Arrays.asList(B, A, D, C); - - } - - private List createDynamicMenu5() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); - - MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); - - MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); - - MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); - - return Arrays.asList(A, B, C, D); - - } - - private List createDynamicMenu5New() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); - - MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); - - MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); - - MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); - - return Arrays.asList(B, C, D, A); - +// +// private List createDynamicMenu1() { +// +// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); +// +// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); +// +// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); +// +// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); +// +// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); +// +// return Arrays.asList(A, B, C, D); +// +// } +// +// private List createDynamicMenu1New() { +// +// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerE = mock(MenuSelectionListener.class); +// +// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); +// +// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); +// +// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); +// +// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); +// +// MenuCell E = new MenuCell("E", null, null, menuSelectionListenerE); +// +// return Arrays.asList(A, B, C, D, E); +// +// } +// +// private List createDynamicMenu2() { +// +// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); +// +// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); +// +// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); +// +// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); +// +// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); +// +// return Arrays.asList(A, B, C, D); +// +// } +// +// private List createDynamicMenu2New() { +// +// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); +// +// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); +// +// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); +// +// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); +// +// return Arrays.asList(A, B, C); +// +// } +// +// private List createDynamicMenu3() { +// +// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); +// +// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); +// +// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); +// +// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); +// +// return Arrays.asList(A, B, C); +// +// } +// +// private List createDynamicMenu3New() { +// +// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerE = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerF = mock(MenuSelectionListener.class); +// +// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); +// +// MenuCell E = new MenuCell("E", null, null, menuSelectionListenerE); +// +// MenuCell F = new MenuCell("F", null, null, menuSelectionListenerF); +// +// return Arrays.asList(D, E, F); +// +// } +// +// private List createDynamicMenu4() { +// +// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); +// +// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); +// +// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); +// +// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); +// +// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); +// +// return Arrays.asList(A, B, C, D); +// +// } +// +// private List createDynamicMenu4New() { +// +// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); +// +// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); +// +// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); +// +// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); +// +// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); +// +// return Arrays.asList(B, A, D, C); +// +// } +// +// private List createDynamicMenu5() { +// +// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); +// +// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); +// +// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); +// +// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); +// +// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); +// +// return Arrays.asList(A, B, C, D); +// +// } +// +// private List createDynamicMenu5New() { +// +// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); +// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); +// +// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); +// +// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); +// +// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); +// +// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); +// +// return Arrays.asList(B, C, D, A); +// +// } + + private void sleep() { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } } - } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 6de157aaf..35990ec81 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -68,7 +68,7 @@ abstract class BaseMenuManager extends BaseSubManager { static final int menuCellIdMin = 1; static final int parentIdNotFound = 2000000000; - private final WeakReference fileManager; + final WeakReference fileManager; List currentMenuCells; List menuCells; DynamicMenuUpdatesMode dynamicMenuUpdatesMode; -- cgit v1.2.1 From 590082d8371dcf3d546faa8057f6f4c5bbae268e Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 3 Feb 2021 16:47:54 -0500 Subject: Fix menu manager unit tests - part 2 --- .../managers/screen/menu/MenuManagerTests.java | 460 +++++++++++---------- 1 file changed, 240 insertions(+), 220 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index 3be2dd3ca..ce77dc719 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -66,11 +66,14 @@ import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; +import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState.ADD; +import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState.KEEP; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertNotNull; @@ -254,42 +257,49 @@ public class MenuManagerTests { } } -// @Test -// public void testAlgorithmTest1() { -// -// // Force Menu Manager to use the new way -// menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); -// assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); -// -// // start fresh -// menuManager.oldMenuCells = null; -// menuManager.menuCells = null; -// menuManager.inProgressUpdate = null; -// menuManager.waitingUpdateMenuCells = null; -// menuManager.waitingOnHMIUpdate = false; -// -// menuManager.currentHMILevel = HMILevel.HMI_FULL; -// // send new cells. They should set the old way -// List oldMenu = createDynamicMenu1(); -// List newMenu = createDynamicMenu1New(); -// menuManager.setMenuCells(oldMenu); -// assertEquals(menuManager.menuCells.size(), 4); -// -// // this happens in the menu manager but lets make sure its behaving -// DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); -// -// List oldMenuScore = Arrays.asList(0, 0, 0, 0); -// List newMenuScore = Arrays.asList(0, 0, 0, 0, 1); -// -// assertEquals(runScore.getScore(), 1); -// assertEquals(runScore.getOldMenu(), oldMenuScore); -// assertEquals(runScore.getCurrentMenu(), newMenuScore); -// -// menuManager.setMenuCells(newMenu); -// assertEquals(menuManager.menuCells.size(), 5); -// assertEquals(menuManager.keepsNew.size(), 4); -// assertEquals(menuManager.keepsOld.size(), 4); -// } + @Test + public void testAlgorithmTest1() { + // Force Menu Manager to use the new way + menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); + assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); + + // start fresh + menuManager.currentMenuCells = new ArrayList<>(); + menuManager.menuCells = new ArrayList<>(); + + sendFakeCoreOnHMIFullNotifications(); + + // send new cells. They should set the old way + List oldMenu = createDynamicMenu1(); + List newMenu = createDynamicMenu1New(); + menuManager.setMenuCells(oldMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(menuManager.currentMenuCells.size(), 4); + + // this happens in the menu manager but lets make sure its behaving + DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); + + List oldMenuStatus = Arrays.asList(KEEP, KEEP, KEEP, KEEP); + List newMenuStatus = Arrays.asList(KEEP, KEEP, KEEP, KEEP, ADD); + + assertEquals(1, runScore.getScore()); + assertEquals(runScore.getOldStatus(), oldMenuStatus); + assertEquals(runScore.getUpdatedStatus(), newMenuStatus); + + menuManager.setMenuCells(newMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(5, menuManager.currentMenuCells.size()); + List oldKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getOldStatus(), KEEP); + List newKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getUpdatedStatus(), KEEP); + assertEquals(oldKeeps.size(), 4); + assertEquals(newKeeps.size(), 4); + } // @Test // public void testAlgorithmTest2() { @@ -566,190 +576,200 @@ public class MenuManagerTests { return Arrays.asList(mainCell1, mainCell2, mainCell3, mainCell4); } -// -// private List createDynamicMenu1() { -// -// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); -// -// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); -// -// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); -// -// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); -// -// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); -// -// return Arrays.asList(A, B, C, D); -// -// } -// -// private List createDynamicMenu1New() { -// -// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerE = mock(MenuSelectionListener.class); -// -// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); -// -// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); -// -// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); -// -// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); -// -// MenuCell E = new MenuCell("E", null, null, menuSelectionListenerE); -// -// return Arrays.asList(A, B, C, D, E); -// -// } -// -// private List createDynamicMenu2() { -// -// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); -// -// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); -// -// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); -// -// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); -// -// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); -// -// return Arrays.asList(A, B, C, D); -// -// } -// -// private List createDynamicMenu2New() { -// -// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); -// -// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); -// -// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); -// -// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); -// -// return Arrays.asList(A, B, C); -// -// } -// -// private List createDynamicMenu3() { -// -// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); -// -// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); -// -// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); -// -// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); -// -// return Arrays.asList(A, B, C); -// -// } -// -// private List createDynamicMenu3New() { -// -// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerE = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerF = mock(MenuSelectionListener.class); -// -// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); -// -// MenuCell E = new MenuCell("E", null, null, menuSelectionListenerE); -// -// MenuCell F = new MenuCell("F", null, null, menuSelectionListenerF); -// -// return Arrays.asList(D, E, F); -// -// } -// -// private List createDynamicMenu4() { -// -// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); -// -// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); -// -// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); -// -// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); -// -// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); -// -// return Arrays.asList(A, B, C, D); -// -// } -// -// private List createDynamicMenu4New() { -// -// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); -// -// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); -// -// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); -// -// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); -// -// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); -// -// return Arrays.asList(B, A, D, C); -// -// } -// -// private List createDynamicMenu5() { -// -// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); -// -// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); -// -// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); -// -// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); -// -// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); -// -// return Arrays.asList(A, B, C, D); -// -// } -// -// private List createDynamicMenu5New() { -// -// MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); -// MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); -// -// MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); -// -// MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); -// -// MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); -// -// MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); -// -// return Arrays.asList(B, C, D, A); -// -// } + + private List createDynamicMenu1() { + + MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); + + MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); + + MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); + + MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); + + MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); + + return Arrays.asList(A, B, C, D); + + } + + private List createDynamicMenu1New() { + + MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerE = mock(MenuSelectionListener.class); + + MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); + + MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); + + MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); + + MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); + + MenuCell E = new MenuCell("E", null, null, menuSelectionListenerE); + + return Arrays.asList(A, B, C, D, E); + + } + + private List createDynamicMenu2() { + + MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); + + MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); + + MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); + + MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); + + MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); + + return Arrays.asList(A, B, C, D); + + } + + private List createDynamicMenu2New() { + + MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); + + MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); + + MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); + + MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); + + return Arrays.asList(A, B, C); + + } + + private List createDynamicMenu3() { + + MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); + + MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); + + MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); + + MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); + + return Arrays.asList(A, B, C); + + } + + private List createDynamicMenu3New() { + + MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerE = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerF = mock(MenuSelectionListener.class); + + MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); + + MenuCell E = new MenuCell("E", null, null, menuSelectionListenerE); + + MenuCell F = new MenuCell("F", null, null, menuSelectionListenerF); + + return Arrays.asList(D, E, F); + + } + + private List createDynamicMenu4() { + + MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); + + MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); + + MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); + + MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); + + MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); + + return Arrays.asList(A, B, C, D); + + } + + private List createDynamicMenu4New() { + + MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); + + MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); + + MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); + + MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); + + MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); + + return Arrays.asList(B, A, D, C); + + } + + private List createDynamicMenu5() { + + MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); + + MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); + + MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); + + MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); + + MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); + + return Arrays.asList(A, B, C, D); + + } + + private List createDynamicMenu5New() { + + MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); + MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); + + MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); + + MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); + + MenuCell C = new MenuCell("C", null, null, menuSelectionListenerC); + + MenuCell D = new MenuCell("D", null, null, menuSelectionListenerD); + + return Arrays.asList(B, C, D, A); + + } + + private List filterMenuCellsWithStatusList(List menuCells, List statusList, DynamicMenuUpdateAlgorithm.MenuCellState menuCellState) { + List filteredCells = new ArrayList<>(); + for (int index = 0; index < statusList.size(); index++) { + if (statusList.get(index).equals(menuCellState)) { + filteredCells.add(menuCells.get(index)); + } + } + return filteredCells; + } private void sleep() { try { -- cgit v1.2.1 From 749060a3b625511f8f3c34445a20abe416741c2d Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 3 Feb 2021 17:02:46 -0500 Subject: Fix menu manager unit tests - part 3 --- .../managers/screen/menu/MenuManagerTests.java | 338 ++++++++++++--------- 1 file changed, 186 insertions(+), 152 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index ce77dc719..3579735b6 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -72,7 +72,9 @@ import java.util.Collections; import java.util.List; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; +import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState.ADD; +import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState.DELETE; import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState.KEEP; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; @@ -282,8 +284,8 @@ public class MenuManagerTests { // this happens in the menu manager but lets make sure its behaving DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); - List oldMenuStatus = Arrays.asList(KEEP, KEEP, KEEP, KEEP); - List newMenuStatus = Arrays.asList(KEEP, KEEP, KEEP, KEEP, ADD); + List oldMenuStatus = Arrays.asList(KEEP, KEEP, KEEP, KEEP); + List newMenuStatus = Arrays.asList(KEEP, KEEP, KEEP, KEEP, ADD); assertEquals(1, runScore.getScore()); assertEquals(runScore.getOldStatus(), oldMenuStatus); @@ -297,158 +299,190 @@ public class MenuManagerTests { assertEquals(5, menuManager.currentMenuCells.size()); List oldKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getOldStatus(), KEEP); List newKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getUpdatedStatus(), KEEP); - assertEquals(oldKeeps.size(), 4); - assertEquals(newKeeps.size(), 4); + assertEquals(4, oldKeeps.size()); + assertEquals(4, newKeeps.size()); + } + + @Test + public void testAlgorithmTest2() { + + // Force Menu Manager to use the new way + menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); + assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); + + // start fresh + menuManager.currentMenuCells = new ArrayList<>(); + menuManager.menuCells = new ArrayList<>(); + + sendFakeCoreOnHMIFullNotifications(); + + // send new cells. They should set the old way + List oldMenu = createDynamicMenu2(); + List newMenu = createDynamicMenu2New(); + menuManager.setMenuCells(oldMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(4, menuManager.currentMenuCells.size()); + + // this happens in the menu manager but lets make sure its behaving + DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); + + List oldMenuScore = Arrays.asList(KEEP, KEEP, KEEP, DELETE); + List newMenuScore = Arrays.asList(KEEP, KEEP, KEEP); + + assertEquals(runScore.getScore(), 0); + assertEquals(runScore.getOldStatus(), oldMenuScore); + assertEquals(runScore.getUpdatedStatus(), newMenuScore); + + menuManager.setMenuCells(newMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(3, menuManager.currentMenuCells.size()); + List oldKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getOldStatus(), KEEP); + List newKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getUpdatedStatus(), KEEP); + assertEquals(3, oldKeeps.size()); + assertEquals(3, newKeeps.size()); + } + + @Test + public void testAlgorithmTest3() { + + // Force Menu Manager to use the new way + menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); + assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); + + // start fresh + menuManager.currentMenuCells = new ArrayList<>(); + menuManager.menuCells = new ArrayList<>(); + + sendFakeCoreOnHMIFullNotifications(); + + // send new cells. They should set the old way + List oldMenu = createDynamicMenu3(); + List newMenu = createDynamicMenu3New(); + menuManager.setMenuCells(oldMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(menuManager.currentMenuCells.size(), 3); + + // this happens in the menu manager but lets make sure its behaving + DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); + + List oldMenuStatus = Arrays.asList(DELETE, DELETE, DELETE); + List newMenuStatus = Arrays.asList(ADD, ADD, ADD); + + assertEquals(runScore.getScore(), 3); + assertEquals(runScore.getOldStatus(), oldMenuStatus); + assertEquals(runScore.getUpdatedStatus(), newMenuStatus); + + menuManager.setMenuCells(newMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(menuManager.currentMenuCells.size(), 3); + List oldKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getOldStatus(), KEEP); + List newKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getUpdatedStatus(), KEEP); + assertEquals(0, newKeeps.size()); + assertEquals(0, oldKeeps.size()); + } + + @Test + public void testAlgorithmTest4() { + + // Force Menu Manager to use the new way + menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); + assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); + + // start fresh + menuManager.currentMenuCells = new ArrayList<>(); + menuManager.menuCells = new ArrayList<>(); + + sendFakeCoreOnHMIFullNotifications(); + + // send new cells. They should set the old way + List oldMenu = createDynamicMenu4(); + List newMenu = createDynamicMenu4New(); + menuManager.setMenuCells(oldMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(menuManager.currentMenuCells.size(), 4); + + // this happens in the menu manager but lets make sure its behaving + DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); + + List oldMenuStatus = Arrays.asList(KEEP, DELETE, KEEP, DELETE); + List newMenuStatus = Arrays.asList(ADD, KEEP, ADD, KEEP); + + assertEquals(runScore.getScore(), 2); + assertEquals(runScore.getOldStatus(), oldMenuStatus); + assertEquals(runScore.getUpdatedStatus(), newMenuStatus); + + menuManager.setMenuCells(newMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(menuManager.currentMenuCells.size(), 4); + List oldKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getOldStatus(), KEEP); + List newKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getUpdatedStatus(), KEEP); + assertEquals(2, newKeeps.size()); + assertEquals(2, oldKeeps.size()); + } + + @Test + public void testAlgorithmTest5() { + + // Force Menu Manager to use the new way + menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); + assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); + + // start fresh + menuManager.currentMenuCells = new ArrayList<>(); + menuManager.menuCells = new ArrayList<>(); + + sendFakeCoreOnHMIFullNotifications(); + + // send new cells. They should set the old way + List oldMenu = createDynamicMenu5(); + List newMenu = createDynamicMenu5New(); + menuManager.setMenuCells(oldMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(menuManager.currentMenuCells.size(), 4); + + // this happens in the menu manager but lets make sure its behaving + DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); + + List oldMenuStatus = Arrays.asList(DELETE, KEEP, KEEP, KEEP); + List newMenuStatus = Arrays.asList(KEEP, KEEP, KEEP, ADD); + + assertEquals(runScore.getScore(), 1); + assertEquals(runScore.getOldStatus(), oldMenuStatus); + assertEquals(runScore.getUpdatedStatus(), newMenuStatus); + + menuManager.setMenuCells(newMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(menuManager.currentMenuCells.size(), 4); + List oldKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getOldStatus(), KEEP); + List newKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getUpdatedStatus(), KEEP); + assertEquals(3, newKeeps.size()); + assertEquals(3, oldKeeps.size()); } -// @Test -// public void testAlgorithmTest2() { -// -// // Force Menu Manager to use the new way -// menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); -// assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); -// -// // start fresh -// menuManager.oldMenuCells = null; -// menuManager.menuCells = null; -// menuManager.inProgressUpdate = null; -// menuManager.waitingUpdateMenuCells = null; -// menuManager.waitingOnHMIUpdate = false; -// -// menuManager.currentHMILevel = HMILevel.HMI_FULL; -// // send new cells. They should set the old way -// List oldMenu = createDynamicMenu2(); -// List newMenu = createDynamicMenu2New(); -// menuManager.setMenuCells(oldMenu); -// assertEquals(menuManager.menuCells.size(), 4); -// -// // this happens in the menu manager but lets make sure its behaving -// DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); -// -// List oldMenuScore = Arrays.asList(0, 0, 0, 2); -// List newMenuScore = Arrays.asList(0, 0, 0); -// -// assertEquals(runScore.getScore(), 0); -// assertEquals(runScore.getOldMenu(), oldMenuScore); -// assertEquals(runScore.getCurrentMenu(), newMenuScore); -// -// menuManager.setMenuCells(newMenu); -// assertEquals(menuManager.menuCells.size(), 3); -// assertEquals(menuManager.keepsNew.size(), 3); -// assertEquals(menuManager.keepsOld.size(), 3); -// } -// -// @Test -// public void testAlgorithmTest3() { -// -// // Force Menu Manager to use the new way -// menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); -// assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); -// -// // start fresh -// menuManager.oldMenuCells = null; -// menuManager.menuCells = null; -// menuManager.inProgressUpdate = null; -// menuManager.waitingUpdateMenuCells = null; -// menuManager.waitingOnHMIUpdate = false; -// -// menuManager.currentHMILevel = HMILevel.HMI_FULL; -// // send new cells. They should set the old way -// List oldMenu = createDynamicMenu3(); -// List newMenu = createDynamicMenu3New(); -// menuManager.setMenuCells(oldMenu); -// assertEquals(menuManager.menuCells.size(), 3); -// -// // this happens in the menu manager but lets make sure its behaving -// DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); -// -// List oldMenuScore = Arrays.asList(2, 2, 2); -// List newMenuScore = Arrays.asList(1, 1, 1); -// -// assertEquals(runScore.getScore(), 3); -// assertEquals(runScore.getOldMenu(), oldMenuScore); -// assertEquals(runScore.getCurrentMenu(), newMenuScore); -// -// menuManager.setMenuCells(newMenu); -// assertEquals(menuManager.menuCells.size(), 3); -// assertEquals(menuManager.keepsNew.size(), 0); -// assertEquals(menuManager.keepsOld.size(), 0); -// } -// -// @Test -// public void testAlgorithmTest4() { -// -// // Force Menu Manager to use the new way -// menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); -// assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); -// -// // start fresh -// menuManager.oldMenuCells = null; -// menuManager.menuCells = null; -// menuManager.inProgressUpdate = null; -// menuManager.waitingUpdateMenuCells = null; -// menuManager.waitingOnHMIUpdate = false; -// -// menuManager.currentHMILevel = HMILevel.HMI_FULL; -// // send new cells. They should set the old way -// List oldMenu = createDynamicMenu4(); -// List newMenu = createDynamicMenu4New(); -// menuManager.setMenuCells(oldMenu); -// assertEquals(menuManager.menuCells.size(), 4); -// -// // this happens in the menu manager but lets make sure its behaving -// DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); -// -// List oldMenuScore = Arrays.asList(0, 2, 0, 2); -// List newMenuScore = Arrays.asList(1, 0, 1, 0); -// -// assertEquals(runScore.getScore(), 2); -// assertEquals(runScore.getOldMenu(), oldMenuScore); -// assertEquals(runScore.getCurrentMenu(), newMenuScore); -// -// menuManager.setMenuCells(newMenu); -// assertEquals(menuManager.menuCells.size(), 4); -// assertEquals(menuManager.keepsNew.size(), 2); -// assertEquals(menuManager.keepsOld.size(), 2); -// } -// -// @Test -// public void testAlgorithmTest5() { -// -// // Force Menu Manager to use the new way -// menuManager.setDynamicUpdatesMode(DynamicMenuUpdatesMode.FORCE_ON); -// assertEquals(menuManager.dynamicMenuUpdatesMode, DynamicMenuUpdatesMode.FORCE_ON); -// -// // start fresh -// menuManager.oldMenuCells = null; -// menuManager.menuCells = null; -// menuManager.inProgressUpdate = null; -// menuManager.waitingUpdateMenuCells = null; -// menuManager.waitingOnHMIUpdate = false; -// -// menuManager.currentHMILevel = HMILevel.HMI_FULL; -// // send new cells. They should set the old way -// List oldMenu = createDynamicMenu5(); -// List newMenu = createDynamicMenu5New(); -// menuManager.setMenuCells(oldMenu); -// assertEquals(menuManager.menuCells.size(), 4); -// -// // this happens in the menu manager but lets make sure its behaving -// DynamicMenuUpdateRunScore runScore = menuManager.runMenuCompareAlgorithm(oldMenu, newMenu); -// -// List oldMenuScore = Arrays.asList(2, 0, 0, 0); -// List newMenuScore = Arrays.asList(0, 0, 0, 1); -// -// assertEquals(runScore.getScore(), 1); -// assertEquals(runScore.getOldMenu(), oldMenuScore); -// assertEquals(runScore.getCurrentMenu(), newMenuScore); -// -// menuManager.setMenuCells(newMenu); -// assertEquals(menuManager.menuCells.size(), 4); -// assertEquals(menuManager.keepsNew.size(), 3); -// assertEquals(menuManager.keepsOld.size(), 3); -// } -// // @Test // public void testSettingNullMenu() { // -- cgit v1.2.1 From e90489d1c1b422d26ffb38541cfa6f3ba5431b47 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 4 Feb 2021 10:38:02 -0500 Subject: Replace menuCells with currentMenuCells --- .../smartdevicelink/managers/screen/menu/BaseMenuManager.java | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 35990ec81..f65426b38 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -165,11 +165,6 @@ abstract class BaseMenuManager extends BaseSubManager { * @param cells - the menu cells that are to be sent to the head unit, including their sub-cells. */ public void setMenuCells(@NonNull List cells) { - if (cells == null) { - DebugTool.logError(TAG, "cells list cannot be null!"); - return; - } - // Create a deep copy of the list so future changes by developers don't affect the algorithm logic final List clonedCells = cloneMenuCellsList(cells); @@ -234,7 +229,7 @@ abstract class BaseMenuManager extends BaseSubManager { if (cell != null) { // We must see if we have a copy of this cell, since we clone the objects - for (MenuCell clonedCell : menuCells) { + for (MenuCell clonedCell : currentMenuCells) { if (clonedCell.equals(cell) && clonedCell.getCellId() != parentIdNotFound) { // We've found the correct sub menu cell foundClonedCell = clonedCell; @@ -377,7 +372,7 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void onNotified(RPCNotification notification) { OnCommand onCommand = (OnCommand) notification; - callListenerForCells(menuCells, onCommand); + callListenerForCells(currentMenuCells, onCommand); } }; internalInterface.addOnRPCNotificationListener(FunctionID.ON_COMMAND, commandListener); @@ -385,7 +380,7 @@ abstract class BaseMenuManager extends BaseSubManager { private List cloneMenuCellsList(List originalList) { if (originalList == null) { - return null; + return new ArrayList<>(); } List clone = new ArrayList<>(); -- cgit v1.2.1 From 5a81704997ade2dd69beb8a0024b45009333df85 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 4 Feb 2021 10:38:19 -0500 Subject: Fix menu manager unit tests - part 4 --- .../managers/screen/menu/MenuManagerTests.java | 191 +++++++++++---------- 1 file changed, 105 insertions(+), 86 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index 3579735b6..42555fde5 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -57,6 +57,7 @@ import com.smartdevicelink.proxy.rpc.enums.SystemContext; import com.smartdevicelink.proxy.rpc.enums.TriggerSource; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener; +import com.smartdevicelink.util.Version; import org.junit.After; import org.junit.Before; @@ -110,6 +111,9 @@ public class MenuManagerTests { final ISdl internalInterface = mock(ISdl.class); FileManager fileManager = mock(FileManager.class); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(6, 0, 0))); + + // When internalInterface.addOnRPCNotificationListener(FunctionID.ON_HMI_STATUS, OnRPCNotificationListener) is called // inside MenuManager's constructor, then keep a reference to the OnRPCNotificationListener so we can trigger it later // to emulate what Core does when it sends OnHMIStatus notification @@ -483,92 +487,107 @@ public class MenuManagerTests { assertEquals(3, oldKeeps.size()); } -// @Test -// public void testSettingNullMenu() { -// -// // Make sure we can send an empty menu with no issues -// // start fresh -// menuManager.oldMenuCells = null; -// menuManager.menuCells = null; -// menuManager.inProgressUpdate = null; -// menuManager.waitingUpdateMenuCells = null; -// menuManager.waitingOnHMIUpdate = false; -// -// menuManager.currentHMILevel = HMILevel.HMI_FULL; -// // send new cells. They should set the old way -// List oldMenu = createDynamicMenu1(); -// List newMenu = null; -// menuManager.setMenuCells(oldMenu); -// assertEquals(menuManager.menuCells.size(), 4); -// -// menuManager.setMenuCells(newMenu); -// assertEquals(menuManager.menuCells.size(), 0); -// } -// -// @Test -// public void testClearingMenu() { -// -// // Make sure we can send an empty menu with no issues -// // start fresh -// menuManager.oldMenuCells = null; -// menuManager.menuCells = null; -// menuManager.inProgressUpdate = null; -// menuManager.waitingUpdateMenuCells = null; -// menuManager.waitingOnHMIUpdate = false; -// -// menuManager.currentHMILevel = HMILevel.HMI_FULL; -// // send new cells. They should set the old way -// List oldMenu = createDynamicMenu1(); -// List newMenu = Collections.emptyList(); -// menuManager.setMenuCells(oldMenu); -// assertEquals(menuManager.menuCells.size(), 4); -// -// menuManager.setMenuCells(newMenu); -// assertEquals(menuManager.menuCells.size(), 0); -// } -// -// @Test -// public void testOpeningMainMenu() { -// // call open Menu -// MenuManager mockMenuManager = mock(MenuManager.class); -// mockMenuManager.openMenu(); -// verify(mockMenuManager, Mockito.times(1)).openMenu(); -// } -// -// @Test -// public void testOpeningSubMenuNullCells() { -// // call open Menu -// MenuManager mockMenuManager = mock(MenuManager.class); -// MenuCell cell = mock(MenuCell.class); -// mockMenuManager.oldMenuCells = null; -// assertFalse(mockMenuManager.openSubMenu(cell)); -// } -// -// @Test -// public void testOpeningSubMenu() { -// // call open Menu -// List testCells = createTestCells(); -// menuManager.oldMenuCells = testCells; -// menuManager.sdlMsgVersion = new SdlMsgVersion(6, 0); -// // has to get success response to be true -// assertTrue(menuManager.openSubMenu(testCells.get(3))); -// } -// -// @Test -// public void testSetMenuConfiguration() { -// menuManager.currentHMILevel = HMILevel.HMI_FULL; -// menuManager.currentSystemContext = SystemContext.SYSCTXT_MAIN; -// menuManager.sdlMsgVersion = new SdlMsgVersion(6, 0); -// menuManager.defaultMainWindowCapability = new WindowCapability(); -// -// List menuLayouts = Arrays.asList(MenuLayout.LIST, MenuLayout.TILES); -// menuManager.defaultMainWindowCapability.setMenuLayoutsAvailable(menuLayouts); -// -// MenuConfiguration menuConfigurationTest = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); -// menuManager.setMenuConfiguration(menuConfigurationTest); -// assertEquals(menuManager.menuConfiguration, menuConfigurationTest); -// -// } + @Test + public void testSettingNullMenu() { + // Make sure we can send an empty menu with no issues + // start fresh + menuManager.currentMenuCells = new ArrayList<>(); + menuManager.menuCells = new ArrayList<>(); + + sendFakeCoreOnHMIFullNotifications(); + + // send new cells. They should set the old way + List oldMenu = createDynamicMenu1(); + List newMenu = null; + menuManager.setMenuCells(oldMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(4, menuManager.currentMenuCells.size()); + + menuManager.setMenuCells(newMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(0, menuManager.currentMenuCells.size()); + } + + @Test + public void testClearingMenu() { + // Make sure we can send an empty menu with no issues + // start fresh + menuManager.currentMenuCells = new ArrayList<>(); + menuManager.menuCells = new ArrayList<>(); + + sendFakeCoreOnHMIFullNotifications(); + + // send new cells. They should set the old way + List oldMenu = createDynamicMenu1(); + List newMenu = Collections.emptyList(); + menuManager.setMenuCells(oldMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(4, menuManager.currentMenuCells.size()); + + menuManager.setMenuCells(newMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(0 , menuManager.currentMenuCells.size()); + } + + @Test + public void testOpeningMainMenu() { + // call open Menu + MenuManager mockMenuManager = mock(MenuManager.class); + mockMenuManager.openMenu(); + verify(mockMenuManager, Mockito.times(1)).openMenu(); + } + + @Test + public void testOpeningSubMenuNullCells() { + // call open Menu + MenuManager mockMenuManager = mock(MenuManager.class); + MenuCell cell = mock(MenuCell.class); + mockMenuManager.currentMenuCells = null; + assertFalse(mockMenuManager.openSubMenu(cell)); + } + + @Test + public void testOpeningSubMenu() { + // call open Menu + List testCells = createTestCells(); + menuManager.setMenuCells(testCells); + + sendFakeCoreOnHMIFullNotifications(); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + // Has to get success response to be true + MenuCell submenu = testCells.get(3); + assertTrue(menuManager.openSubMenu(submenu)); + } + + @Test + public void testSetMenuConfiguration() { + sendFakeCoreOnHMIFullNotifications(); + menuManager.windowCapability = new WindowCapability(); + menuManager.windowCapability.setMenuLayoutsAvailable(Arrays.asList(MenuLayout.LIST, MenuLayout.TILES)); + + MenuConfiguration menuConfigurationTest = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); + menuManager.setMenuConfiguration(menuConfigurationTest); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + assertEquals(menuManager.menuConfiguration, menuConfigurationTest); + } // HELPERS -- cgit v1.2.1 From d2fafab9907d4345eeb49b5f9c45962ca06190db Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 4 Feb 2021 11:14:48 -0500 Subject: MenuReplaceOperation cleanup --- .../managers/screen/menu/MenuReplaceOperation.java | 35 ++++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index f5b6d7470..66d550ebe 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -121,7 +121,7 @@ class MenuReplaceOperation extends Task { updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, new CompletionListener() { @Override public void onComplete(boolean success) { - startSubMenuUpdatesWithOldKeptCells(oldKeeps, newKeeps, 0, listener); + updateSubMenuWithOldKeptCells(oldKeeps, newKeeps, 0, listener); } }); } @@ -131,12 +131,18 @@ class MenuReplaceOperation extends Task { updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, new CompletionListener() { @Override public void onComplete(boolean success) { - startSubMenuUpdatesWithOldKeptCells(oldKeeps, newKeeps, 0, listener); + updateSubMenuWithOldKeptCells(oldKeeps, newKeeps, 0, listener); } }); } } + /** + * Takes the main menu cells to delete and add, and deletes the current menu cells, then adds the new menu cells in the correct locations + * @param deleteCells The cells that need to be deleted + * @param addCells The cells that need to be added + * @param listener A CompletionListener called when complete + */ private void updateMenuWithCellsToDelete(List deleteCells, final List addCells, final CompletionListener listener) { sendDeleteCurrentMenu(deleteCells, new CompletionListener() { @Override @@ -155,6 +161,11 @@ class MenuReplaceOperation extends Task { }); } + /** + * Send Delete RPCs for given menu cells + * @param deleteMenuCells The menu cells to be deleted + * @param listener A CompletionListener called when the RPCs are finished with an error if any failed + */ private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; @@ -186,6 +197,11 @@ class MenuReplaceOperation extends Task { }); } + /** + * Send Add RPCs for given new menu cells compared to old menu cells + * @param newMenuCells The new menu cells we want displayed + * @param listener A CompletionListener called when the RPCs are finished with an error if any failed + */ private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; @@ -246,7 +262,14 @@ class MenuReplaceOperation extends Task { }); } - private void startSubMenuUpdatesWithOldKeptCells(final List oldKeptCells, final List newKeptCells, final int startIndex, final CompletionListener listener) { + /** + * Takes the submenu cells that are old keeps and new keeps and determines which cells need to be deleted or added + * @param oldKeptCells The old kept cells + * @param newKeptCells The new kept cells + * @param startIndex The index of the main menu to use + * @param listener A CompletionListener called when complete + */ + private void updateSubMenuWithOldKeptCells(final List oldKeptCells, final List newKeptCells, final int startIndex, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } @@ -262,7 +285,7 @@ class MenuReplaceOperation extends Task { // If both old and new menu cells are empty. Then nothing needs to be done. if (tempScore == null) { // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements - startSubMenuUpdatesWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); return; } @@ -284,14 +307,14 @@ class MenuReplaceOperation extends Task { @Override public void onComplete(boolean success) { // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements - startSubMenuUpdatesWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); } }); } }); } else { // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements - startSubMenuUpdatesWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); } } -- cgit v1.2.1 From 863639c650289441069aaf5626f6e4284919f26b Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 4 Feb 2021 14:00:36 -0500 Subject: Fix an issue in MenuCell.hashCode() --- .../main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index 83e265f8e..bbf32d09f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -302,7 +302,7 @@ public class MenuCell implements Cloneable { result += ((getTitle() == null) ? 0 : Integer.rotateLeft(getTitle().hashCode(), 1)); result += ((getIcon() == null) ? 0 : Integer.rotateLeft(getIcon().hashCode(), 2)); result += ((getVoiceCommands() == null) ? 0 : Integer.rotateLeft(getVoiceCommands().hashCode(), 3)); - result += ((getSubCells() == null) ? 0 : Integer.rotateLeft(1, 4)); + result += ((getSubCells() == null) ? 0 : Integer.rotateLeft(getSubCells().hashCode(), 4)); return result; } -- cgit v1.2.1 From ad761cce8a3168dc288aa4cf2f812a422f185cfe Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 4 Feb 2021 14:01:15 -0500 Subject: Add unit tests for removeMenuCellFromList() --- .../screen/menu/MenuReplaceUtilitiesTests.java | 196 +++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java new file mode 100644 index 000000000..5a1ede905 --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2021 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.menu; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.smartdevicelink.managers.file.filetypes.SdlArtwork; +import com.smartdevicelink.proxy.rpc.enums.MenuLayout; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + +/** + * Created by Bilal Alsharifi on 2/4/21. + */ +@RunWith(AndroidJUnit4.class) +public class MenuReplaceUtilitiesTests { + static int lastMenuId = menuCellIdMin; + + @Before + public void setUp() throws Exception { + } + + @Test + public void testRemoveMenuCellFromList() { + MenuCell menuCellToDelete; + boolean cellRemoved; + List actualMenuCellList = createMenuCellList(); + List expectedMenuCellList = createMenuCellList(); + + // Delete cell c4 + menuCellToDelete = actualMenuCellList.get(3); + cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + assertTrue(cellRemoved); + expectedMenuCellList.remove(3); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(3, actualMenuCellList.size()); + + // Delete cell c4 again - removal should fail and list should not change + cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + assertFalse(cellRemoved); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(3, actualMenuCellList.size()); + + // Delete cell c3 + menuCellToDelete = actualMenuCellList.get(2); + cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + assertTrue(cellRemoved); + expectedMenuCellList.remove(2); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(2, actualMenuCellList.size()); + + // Delete cell c2-2-2 + menuCellToDelete = actualMenuCellList.get(1).getSubCells().get(1).getSubCells().get(1); + cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + assertTrue(cellRemoved); + expectedMenuCellList.get(1).getSubCells().get(1).getSubCells().remove(1); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(2, actualMenuCellList.size()); + assertEquals(1, actualMenuCellList.get(1).getSubCells().get(1).getSubCells().size()); + + // Delete cell c2-2-1 + menuCellToDelete = actualMenuCellList.get(1).getSubCells().get(1).getSubCells().get(0); + cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + assertTrue(cellRemoved); + expectedMenuCellList.get(1).getSubCells().get(1).getSubCells().remove(0); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(2, actualMenuCellList.size()); + assertEquals(0, actualMenuCellList.get(1).getSubCells().get(1).getSubCells().size()); + + // Delete cell c2-2 + menuCellToDelete = actualMenuCellList.get(1).getSubCells().get(1); + cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + assertTrue(cellRemoved); + expectedMenuCellList.get(1).getSubCells().remove(1); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(2, actualMenuCellList.size()); + assertEquals(1, actualMenuCellList.get(1).getSubCells().size()); + + // Delete cell c2-1 + menuCellToDelete = actualMenuCellList.get(1).getSubCells().get(0); + cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + assertTrue(cellRemoved); + expectedMenuCellList.get(1).getSubCells().remove(0); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(2, actualMenuCellList.size()); + assertEquals(0, actualMenuCellList.get(1).getSubCells().size()); + + // Delete cell c2 + menuCellToDelete = actualMenuCellList.get(1); + cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + assertTrue(cellRemoved); + expectedMenuCellList.remove(1); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(1, actualMenuCellList.size()); + + // Delete cell c1 + menuCellToDelete = actualMenuCellList.get(0); + cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + assertTrue(cellRemoved); + expectedMenuCellList.remove(0); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(0, actualMenuCellList.size()); + } + + private List createMenuCellList() { + /* + + c1 c2 c3 c4 + / \ / \ + / \ / \ + c2-1 c2-2 c4-1 c4-2 + / \ + / \ + c2-2-1 c2-2-2 + + */ + + SdlArtwork sdlArtwork = null; + List voiceCommands = null; + MenuSelectionListener listener = null; + MenuLayout subMenuLayout = null; + + MenuCell menuCell1 = new MenuCell("c1", sdlArtwork, voiceCommands, listener); + + MenuCell menuCell2_1 = new MenuCell("c2_1", sdlArtwork, voiceCommands, listener); + MenuCell menuCell2_2_1 = new MenuCell("c2_2_1", sdlArtwork, voiceCommands, listener); + MenuCell menuCell2_2_2 = new MenuCell("c2_2_2", sdlArtwork, voiceCommands, listener); + MenuCell menuCell2_2 = new MenuCell("c2_2", subMenuLayout, sdlArtwork, new ArrayList<>(Arrays.asList(menuCell2_2_1, menuCell2_2_2))); + MenuCell menuCell2 = new MenuCell("c2", subMenuLayout, sdlArtwork, new ArrayList<>(Arrays.asList(menuCell2_1, menuCell2_2))); + + MenuCell menuCell3 = new MenuCell("c3", sdlArtwork, voiceCommands, listener); + + MenuCell menuCell4_1 = new MenuCell("c4_1", sdlArtwork, voiceCommands, listener); + MenuCell menuCell4_2 = new MenuCell("c4_2", sdlArtwork, voiceCommands, listener); + MenuCell menuCell4 = new MenuCell("c4", subMenuLayout, sdlArtwork, new ArrayList<>(Arrays.asList(menuCell4_1, menuCell4_2))); + + List menuCellList = new ArrayList<>(Arrays.asList(menuCell1, menuCell2, menuCell3, menuCell4)); + updateIdsOnMenuCells(menuCellList, parentIdNotFound); + + return menuCellList ; + } + + private void updateIdsOnMenuCells(List menuCells, int parentId) { + for (MenuCell cell : menuCells) { + cell.setCellId(lastMenuId++); + if (parentId != parentIdNotFound) { + cell.setParentCellId(parentId); + } + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); + } + } + } +} -- cgit v1.2.1 From 7dd6bbda9d1856396ba23f2260f02e53283d8c2e Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 4 Feb 2021 16:10:15 -0500 Subject: Clear subcells before inserting mail cell in insertMenuCell() --- .../managers/screen/menu/MenuReplaceUtilities.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index e55dc75de..2a202cca3 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -242,10 +242,16 @@ class MenuReplaceUtilities { } private static void insertMenuCell(MenuCell cell, List cellList, int position) { + MenuCell cellToInsert = cell; + if (cellToInsert.getSubCells() != null) { + // We should not add the subCells automatically when adding a parent cell + cellToInsert = cell.clone(); + cellToInsert.getSubCells().clear(); + } if (position > cellList.size()) { - cellList.add(cell); + cellList.add(cellToInsert); } else { - cellList.add(position, cell); + cellList.add(position, cellToInsert); } } -- cgit v1.2.1 From 1cc09120da98ed12aa8990aa73189d8f83b7283c Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 4 Feb 2021 16:10:36 -0500 Subject: Add unit tests for addMenuRequestWithCommandId() --- .../screen/menu/MenuReplaceUtilitiesTests.java | 84 ++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java index 5a1ede905..bb222eb3d 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java @@ -144,6 +144,61 @@ public class MenuReplaceUtilitiesTests { assertEquals(0, actualMenuCellList.size()); } + @Test + public void testAddMenuRequestWithCommandId() { + MenuCell menuCellToAdd; + boolean cellAdded; + List actualMenuCellList = createMenuCellList(); + List expectedMenuCellList = createMenuCellList(); + List newMenuList = createNewMenuList(); + + // Add cell c5 + menuCellToAdd = newMenuList.get(0); + cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 4, newMenuList, actualMenuCellList); + assertTrue(cellAdded); + expectedMenuCellList.add(4, cloneMenuCellAndRemoveSubCells(menuCellToAdd)); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(5, actualMenuCellList.size()); + assertEquals(0, actualMenuCellList.get(4).getSubCells().size()); + + // Add cell c5-1 + menuCellToAdd = newMenuList.get(0).getSubCells().get(0); + cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 0, newMenuList, actualMenuCellList); + assertTrue(cellAdded); + expectedMenuCellList.get(4).getSubCells().add(0, cloneMenuCellAndRemoveSubCells(menuCellToAdd)); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(5, actualMenuCellList.size()); + assertEquals(1, actualMenuCellList.get(4).getSubCells().size()); + + // Add cell c5-2 + menuCellToAdd = newMenuList.get(0).getSubCells().get(1); + cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 1, newMenuList, actualMenuCellList); + assertTrue(cellAdded); + expectedMenuCellList.get(4).getSubCells().add(1, cloneMenuCellAndRemoveSubCells(menuCellToAdd)); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(5, actualMenuCellList.size()); + assertEquals(2, actualMenuCellList.get(4).getSubCells().size()); + assertEquals(0, actualMenuCellList.get(4).getSubCells().get(1).getSubCells().size()); + + // Add cell c5-2-2 + menuCellToAdd = newMenuList.get(0).getSubCells().get(1).getSubCells().get(0); + cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 0, newMenuList, actualMenuCellList); + assertTrue(cellAdded); + expectedMenuCellList.get(4).getSubCells().get(1).getSubCells().add(0, cloneMenuCellAndRemoveSubCells(menuCellToAdd)); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(5, actualMenuCellList.size()); + assertEquals(2, actualMenuCellList.get(4).getSubCells().size()); + assertEquals(1, actualMenuCellList.get(4).getSubCells().get(1).getSubCells().size()); + } + + private MenuCell cloneMenuCellAndRemoveSubCells(MenuCell menuCell) { + MenuCell clonedCell = menuCell.clone(); + if (clonedCell.getSubCells() != null) { + clonedCell.getSubCells().clear(); + } + return clonedCell; + } + private List createMenuCellList() { /* @@ -182,6 +237,35 @@ public class MenuReplaceUtilitiesTests { return menuCellList ; } + private List createNewMenuList() { + /* + + c5 + / \ + / \ + c5-1 c5-2 + / + / + c5-2-1 + + */ + + SdlArtwork sdlArtwork = null; + List voiceCommands = null; + MenuSelectionListener listener = null; + MenuLayout subMenuLayout = null; + + MenuCell menuCell5_1 = new MenuCell("c5_1", sdlArtwork, voiceCommands, listener); + MenuCell menuCell5_2_1 = new MenuCell("c5_2_1", sdlArtwork, voiceCommands, listener); + MenuCell menuCell5_2 = new MenuCell("c5_2", subMenuLayout, sdlArtwork, new ArrayList<>(Arrays.asList(menuCell5_2_1))); + MenuCell menuCell5 = new MenuCell("c5", subMenuLayout, sdlArtwork, new ArrayList<>(Arrays.asList(menuCell5_1, menuCell5_2))); + + List newMenuList = new ArrayList<>(Arrays.asList(menuCell5)); + updateIdsOnMenuCells(newMenuList, parentIdNotFound); + + return newMenuList ; + } + private void updateIdsOnMenuCells(List menuCells, int parentId) { for (MenuCell cell : menuCells) { cell.setCellId(lastMenuId++); -- cgit v1.2.1 From 4964871bbf06a6d75d22e42fccf703112e9454ba Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 5 Feb 2021 10:22:09 -0500 Subject: Update currentMenu only if the RPC succeeded --- .../managers/screen/menu/MenuReplaceOperation.java | 28 +++++++++++++--------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 66d550ebe..85e6df8c9 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -190,9 +190,11 @@ class MenuReplaceOperation extends Task { @Override public void onResponse(RPCRequest request, RPCResponse response) { - // Find the id of the successful request and remove it from the current menu list wherever it may have been - int commandId = commandIdForRPCRequest(request); - removeMenuCellFromList(currentMenu, commandId); + if (response.getSuccess()) { + // Find the id of the successful request and remove it from the current menu list wherever it may have been + int commandId = commandIdForRPCRequest(request); + removeMenuCellFromList(currentMenu, commandId); + } } }); } @@ -244,20 +246,24 @@ class MenuReplaceOperation extends Task { @Override public void onResponse(RPCRequest request, RPCResponse response) { - // Find the id of the successful request and add it from the current menu list wherever it needs to be - int commandId = commandIdForRPCRequest(request); - int position = positionForRPCRequest(request); - addMenuRequestWithCommandId(commandId, position, newMenuCells, currentMenu); + if (response.getSuccess()) { + // Find the id of the successful request and add it from the current menu list wherever it needs to be + int commandId = commandIdForRPCRequest(request); + int position = positionForRPCRequest(request); + addMenuRequestWithCommandId(commandId, position, newMenuCells, currentMenu); + } } }); } @Override public void onResponse(RPCRequest request, RPCResponse response) { - // Find the id of the successful request and add it from the current menu list wherever it needs to be - int commandId = commandIdForRPCRequest(request); - int position = positionForRPCRequest(request); - addMenuRequestWithCommandId(commandId, position, newMenuCells, currentMenu); + if (response.getSuccess()) { + // Find the id of the successful request and add it from the current menu list wherever it needs to be + int commandId = commandIdForRPCRequest(request); + int position = positionForRPCRequest(request); + addMenuRequestWithCommandId(commandId, position, newMenuCells, currentMenu); + } } }); } -- cgit v1.2.1 From 2ebf3fcf06178c94125cc829bc50f7820da742f7 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 5 Feb 2021 14:02:01 -0500 Subject: Update comments --- .../com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 2a202cca3..05b156063 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -97,7 +97,7 @@ class MenuReplaceUtilities { static List mainMenuCommandsForCells(List cells, FileManager fileManager, WindowCapability windowCapability, List menu, MenuLayout defaultSubmenuLayout) { List commands = new ArrayList<>(); - // We need the index so we will use this type of loop + // We need the index to use it as position so we will use this type of loop for (int menuInteger = 0; menuInteger < menu.size(); menuInteger++) { MenuCell mainCell = menu.get(menuInteger); for (int updateCellsIndex = 0; updateCellsIndex < cells.size(); updateCellsIndex++) { -- cgit v1.2.1 From de55dcc38a2cbd5b640b586474e5868182f64433 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 5 Feb 2021 14:21:39 -0500 Subject: Set parentId in subMenuCommandForMenuCell() --- .../com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java | 1 + 1 file changed, 1 insertion(+) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 05b156063..b30e19eb8 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -174,6 +174,7 @@ class MenuReplaceUtilities { } return new AddSubMenu(cell.getCellId(), cell.getTitle()) + .setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null) .setPosition(position) .setMenuLayout(submenuLayout) .setMenuIcon(icon); -- cgit v1.2.1 From a0bdb05a9142e2a7a12da96e612923fde7dc2503 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 5 Feb 2021 14:49:42 -0500 Subject: Add more MenuCell tests --- .../managers/screen/menu/MenuCellTests.java | 28 ++++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java index dfd0477e5..67bfad64d 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java @@ -43,13 +43,13 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; +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.assertNotNull; import static junit.framework.TestCase.assertNotSame; -import static junit.framework.TestCase.assertTrue; +import static org.junit.Assert.assertNotEquals; @RunWith(AndroidJUnit4.class) public class MenuCellTests { @@ -63,7 +63,6 @@ public class MenuCellTests { @Test public void testSettersAndGetters() { - // set everything MenuCell menuCell = new MenuCell(TestValues.GENERAL_STRING, null, null, menuSelectionListener); menuCell.setIcon(TestValues.GENERAL_ARTWORK); @@ -83,7 +82,6 @@ public class MenuCellTests { @Test public void testConstructors() { - // first constructor was tested in previous method, use the last two here MenuCell menuCell3 = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_STRING_LIST, menuSelectionListener); @@ -105,22 +103,29 @@ public class MenuCellTests { @Test public void testEquality() { + MenuCell menuCell1 = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_STRING_LIST, menuSelectionListener); + MenuCell menuCell1_1 = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_STRING_LIST, menuSelectionListener); + menuCell1.setSubCells(Collections.singletonList(menuCell1_1)); - //We should use assertTrue (or assertFalse) because we want to use the overridden equals() method - - MenuCell menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_STRING_LIST, menuSelectionListener); MenuCell menuCell2 = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_STRING_LIST, menuSelectionListener); + MenuCell menuCell2_1 = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_STRING_LIST, menuSelectionListener); + menuCell2.setSubCells(Collections.singletonList(menuCell2_1)); // these are the same object, should be equal. - assertTrue(menuCell.equals(menuCell)); + assertEquals(menuCell1, menuCell1); // Make sure these are marked as equals, even though they are different objects - assertTrue(menuCell.equals(menuCell2)); + assertEquals(menuCell1, menuCell2); MenuCell menuCell3 = new MenuCell(TestValues.GENERAL_STRING, null, TestValues.GENERAL_STRING_LIST, menuSelectionListener); // these should be different - assertFalse(menuCell.equals(menuCell3)); + assertNotEquals(menuCell1, menuCell3); + + menuCell1_1.setTitle("new title"); + + // Make sure sub cells are compared + assertNotEquals(menuCell1, menuCell2); } @Test @@ -161,8 +166,5 @@ public class MenuCellTests { assertNotSame(originalSubCells.get(i), cloneSubCells.get(i)); } - - } - } -- cgit v1.2.1 From f49a6190d10c62861251044d663f72ff8a62270e Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 5 Feb 2021 15:18:47 -0500 Subject: Add unit tests for ShouldCellIncludeImage() --- .../screen/menu/MenuReplaceUtilitiesTests.java | 64 +++++++++++++ .../java/com/smartdevicelink/test/TestValues.java | 100 +-------------------- 2 files changed, 66 insertions(+), 98 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java index bb222eb3d..180ad3617 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java @@ -34,8 +34,13 @@ package com.smartdevicelink.managers.screen.menu; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.smartdevicelink.managers.file.FileManager; import com.smartdevicelink.managers.file.filetypes.SdlArtwork; +import com.smartdevicelink.proxy.rpc.ImageField; +import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; +import com.smartdevicelink.test.TestValues; import org.junit.Before; import org.junit.Test; @@ -50,6 +55,9 @@ import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdN import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * Created by Bilal Alsharifi on 2/4/21. @@ -191,6 +199,62 @@ public class MenuReplaceUtilitiesTests { assertEquals(1, actualMenuCellList.get(4).getSubCells().get(1).getSubCells().size()); } + @Test + public void testShouldCellIncludeImage() { + MenuCell menuCell; + WindowCapability windowCapability; + FileManager fileManager; + List voiceCommands = null; + + // Case 1 + menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); + windowCapability = createWindowCapability(true, true); + fileManager = createMockFileManager(true); + assertTrue(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability)); + + // Case 2 - Image are not supported + menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); + windowCapability = createWindowCapability(false, false); + fileManager = createMockFileManager(true); + assertFalse(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability)); + + // Case 3 - Artwork is null + menuCell = new MenuCell(TestValues.GENERAL_STRING, null, voiceCommands, null); + windowCapability = createWindowCapability(true, true); + fileManager = createMockFileManager(true); + assertFalse(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability)); + + // Case 4 - Artwork has not been uploaded + menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); + windowCapability = createWindowCapability(true, true); + fileManager = createMockFileManager(false); + assertFalse(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability)); + + // Case 5 - Artwork is static icon + menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK_STATIC, voiceCommands, null); + windowCapability = createWindowCapability(true, true); + fileManager = createMockFileManager(false); + assertTrue(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability)); + } + + private WindowCapability createWindowCapability (boolean supportsCmdIcon, boolean supportsSubMenuIcon) { + WindowCapability windowCapability = new WindowCapability(); + windowCapability.setImageFields(new ArrayList()); + if (supportsCmdIcon) { + windowCapability.getImageFields().add(new ImageField(ImageFieldName.cmdIcon, null)); + } + if (supportsSubMenuIcon) { + windowCapability.getImageFields().add(new ImageField(ImageFieldName.subMenuIcon, null)); + } + return windowCapability; + } + + private FileManager createMockFileManager (boolean hasUploadedFile) { + FileManager fileManager = mock(FileManager.class); + when(fileManager.hasUploadedFile(any(SdlArtwork.class))).thenReturn(hasUploadedFile); + return fileManager; + } + private MenuCell cloneMenuCellAndRemoveSubCells(MenuCell menuCell) { MenuCell clonedCell = menuCell.clone(); if (clonedCell.getSubCells() != null) { diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java index b3189f264..68b6035e0 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/TestValues.java @@ -17,104 +17,7 @@ import com.smartdevicelink.managers.screen.menu.VoiceCommandSelectionListener; import com.smartdevicelink.protocol.SdlProtocol; import com.smartdevicelink.protocol.enums.FunctionID; import com.smartdevicelink.proxy.rpc.*; -import com.smartdevicelink.proxy.rpc.enums.AmbientLightStatus; -import com.smartdevicelink.proxy.rpc.enums.AppHMIType; -import com.smartdevicelink.proxy.rpc.enums.AppInterfaceUnregisteredReason; -import com.smartdevicelink.proxy.rpc.enums.AppServiceType; -import com.smartdevicelink.proxy.rpc.enums.AudioStreamingIndicator; -import com.smartdevicelink.proxy.rpc.enums.AudioStreamingState; -import com.smartdevicelink.proxy.rpc.enums.AudioType; -import com.smartdevicelink.proxy.rpc.enums.BitsPerSample; -import com.smartdevicelink.proxy.rpc.enums.ButtonEventMode; -import com.smartdevicelink.proxy.rpc.enums.ButtonName; -import com.smartdevicelink.proxy.rpc.enums.ButtonPressMode; -import com.smartdevicelink.proxy.rpc.enums.CapacityUnit; -import com.smartdevicelink.proxy.rpc.enums.CarModeStatus; -import com.smartdevicelink.proxy.rpc.enums.CharacterSet; -import com.smartdevicelink.proxy.rpc.enums.CompassDirection; -import com.smartdevicelink.proxy.rpc.enums.ComponentVolumeStatus; -import com.smartdevicelink.proxy.rpc.enums.DefrostZone; -import com.smartdevicelink.proxy.rpc.enums.DeviceLevelStatus; -import com.smartdevicelink.proxy.rpc.enums.Dimension; -import com.smartdevicelink.proxy.rpc.enums.Direction; -import com.smartdevicelink.proxy.rpc.enums.DisplayMode; -import com.smartdevicelink.proxy.rpc.enums.DisplayType; -import com.smartdevicelink.proxy.rpc.enums.DistanceUnit; -import com.smartdevicelink.proxy.rpc.enums.DoorStatusType; -import com.smartdevicelink.proxy.rpc.enums.DriverDistractionState; -import com.smartdevicelink.proxy.rpc.enums.ECallConfirmationStatus; -import com.smartdevicelink.proxy.rpc.enums.EmergencyEventType; -import com.smartdevicelink.proxy.rpc.enums.FileType; -import com.smartdevicelink.proxy.rpc.enums.FuelCutoffStatus; -import com.smartdevicelink.proxy.rpc.enums.FuelType; -import com.smartdevicelink.proxy.rpc.enums.GlobalProperty; -import com.smartdevicelink.proxy.rpc.enums.HMILevel; -import com.smartdevicelink.proxy.rpc.enums.HmiZoneCapabilities; -import com.smartdevicelink.proxy.rpc.enums.HybridAppPreference; -import com.smartdevicelink.proxy.rpc.enums.IgnitionStableStatus; -import com.smartdevicelink.proxy.rpc.enums.IgnitionStatus; -import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; -import com.smartdevicelink.proxy.rpc.enums.ImageType; -import com.smartdevicelink.proxy.rpc.enums.InteractionMode; -import com.smartdevicelink.proxy.rpc.enums.KeyboardEvent; -import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout; -import com.smartdevicelink.proxy.rpc.enums.KeypressMode; -import com.smartdevicelink.proxy.rpc.enums.Language; -import com.smartdevicelink.proxy.rpc.enums.LayoutMode; -import com.smartdevicelink.proxy.rpc.enums.LightName; -import com.smartdevicelink.proxy.rpc.enums.LightStatus; -import com.smartdevicelink.proxy.rpc.enums.LockScreenStatus; -import com.smartdevicelink.proxy.rpc.enums.MassageCushion; -import com.smartdevicelink.proxy.rpc.enums.MassageMode; -import com.smartdevicelink.proxy.rpc.enums.MassageZone; -import com.smartdevicelink.proxy.rpc.enums.MediaClockFormat; -import com.smartdevicelink.proxy.rpc.enums.MediaType; -import com.smartdevicelink.proxy.rpc.enums.MenuLayout; -import com.smartdevicelink.proxy.rpc.enums.MetadataType; -import com.smartdevicelink.proxy.rpc.enums.ModuleType; -import com.smartdevicelink.proxy.rpc.enums.NavigationAction; -import com.smartdevicelink.proxy.rpc.enums.NavigationJunction; -import com.smartdevicelink.proxy.rpc.enums.PRNDL; -import com.smartdevicelink.proxy.rpc.enums.PowerModeQualificationStatus; -import com.smartdevicelink.proxy.rpc.enums.PowerModeStatus; -import com.smartdevicelink.proxy.rpc.enums.PrerecordedSpeech; -import com.smartdevicelink.proxy.rpc.enums.PrimaryAudioSource; -import com.smartdevicelink.proxy.rpc.enums.RadioBand; -import com.smartdevicelink.proxy.rpc.enums.RadioState; -import com.smartdevicelink.proxy.rpc.enums.RequestType; -import com.smartdevicelink.proxy.rpc.enums.Result; -import com.smartdevicelink.proxy.rpc.enums.SamplingRate; -import com.smartdevicelink.proxy.rpc.enums.SeatMemoryActionType; -import com.smartdevicelink.proxy.rpc.enums.SeekIndicatorType; -import com.smartdevicelink.proxy.rpc.enums.ServiceUpdateReason; -import com.smartdevicelink.proxy.rpc.enums.SoftButtonType; -import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities; -import com.smartdevicelink.proxy.rpc.enums.SupportedSeat; -import com.smartdevicelink.proxy.rpc.enums.SystemAction; -import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType; -import com.smartdevicelink.proxy.rpc.enums.SystemContext; -import com.smartdevicelink.proxy.rpc.enums.TBTState; -import com.smartdevicelink.proxy.rpc.enums.TPMS; -import com.smartdevicelink.proxy.rpc.enums.TemperatureUnit; -import com.smartdevicelink.proxy.rpc.enums.TextAlignment; -import com.smartdevicelink.proxy.rpc.enums.TextFieldName; -import com.smartdevicelink.proxy.rpc.enums.TouchType; -import com.smartdevicelink.proxy.rpc.enums.TransmissionType; -import com.smartdevicelink.proxy.rpc.enums.TriggerSource; -import com.smartdevicelink.proxy.rpc.enums.UpdateMode; -import com.smartdevicelink.proxy.rpc.enums.VehicleDataEventStatus; -import com.smartdevicelink.proxy.rpc.enums.VehicleDataNotificationStatus; -import com.smartdevicelink.proxy.rpc.enums.VehicleDataResultCode; -import com.smartdevicelink.proxy.rpc.enums.VehicleDataStatus; -import com.smartdevicelink.proxy.rpc.enums.VehicleDataType; -import com.smartdevicelink.proxy.rpc.enums.VentilationMode; -import com.smartdevicelink.proxy.rpc.enums.VideoStreamingCodec; -import com.smartdevicelink.proxy.rpc.enums.VideoStreamingProtocol; -import com.smartdevicelink.proxy.rpc.enums.VideoStreamingState; -import com.smartdevicelink.proxy.rpc.enums.VrCapabilities; -import com.smartdevicelink.proxy.rpc.enums.WarningLightStatus; -import com.smartdevicelink.proxy.rpc.enums.WayPointType; -import com.smartdevicelink.proxy.rpc.enums.WindowType; +import com.smartdevicelink.proxy.rpc.enums.*; import com.smartdevicelink.util.Version; import org.json.JSONArray; @@ -350,6 +253,7 @@ public class TestValues { public static final DisplayCapability GENERAL_DISPLAY_CAPABILITY = new DisplayCapability(); public static final SdlArtwork GENERAL_ARTWORK = new SdlArtwork("sdl", FileType.GRAPHIC_PNG, R.drawable.ic_sdl, false); + public static final SdlArtwork GENERAL_ARTWORK_STATIC = new SdlArtwork(StaticIconName.BACK); public static final MenuLayout GENERAL_MENU_LAYOUT = MenuLayout.LIST; public static final MenuConfiguration GENERAL_MENU_CONFIGURATION = new MenuConfiguration(GENERAL_MENU_LAYOUT, GENERAL_MENU_LAYOUT); -- cgit v1.2.1 From a79dd81352acbde981d63321ecac8ea76d2062b8 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 8 Feb 2021 14:33:27 -0500 Subject: Update WindowCapability for pending replace operations --- .../managers/screen/menu/BaseMenuManager.java | 9 ++++++++ .../managers/screen/menu/MenuReplaceOperation.java | 24 ++++++++++++++-------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index f65426b38..f7fa752a2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -339,6 +339,7 @@ abstract class BaseMenuManager extends BaseSubManager { int currentWindowID = windowCapability.getWindowID() != null ? windowCapability.getWindowID() : PredefinedWindows.DEFAULT_WINDOW.getValue(); if (currentWindowID == PredefinedWindows.DEFAULT_WINDOW.getValue()) { BaseMenuManager.this.windowCapability = windowCapability; + updateMenuReplaceOperationsWithNewWindowCapability(); } } } @@ -417,6 +418,14 @@ abstract class BaseMenuManager extends BaseSubManager { } } + private void updateMenuReplaceOperationsWithNewWindowCapability() { + for (Task task : transactionQueue.getTasksAsList()) { + if (task instanceof MenuReplaceOperation) { + ((MenuReplaceOperation) task).setWindowCapability(this.windowCapability); + } + } + } + private boolean isDynamicMenuUpdateActive(DynamicMenuUpdatesMode updateMode, String displayType) { if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { if (displayType == null) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 85e6df8c9..3f53528a2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -38,7 +38,7 @@ class MenuReplaceOperation extends Task { private final WeakReference internalInterface; private final WeakReference fileManager; - private final WindowCapability windowCapability; + private WindowCapability windowCapability; private List currentMenu; private final List updatedMenu; private final boolean isDynamicMenuUpdateActive; @@ -139,9 +139,10 @@ class MenuReplaceOperation extends Task { /** * Takes the main menu cells to delete and add, and deletes the current menu cells, then adds the new menu cells in the correct locations + * * @param deleteCells The cells that need to be deleted - * @param addCells The cells that need to be added - * @param listener A CompletionListener called when complete + * @param addCells The cells that need to be added + * @param listener A CompletionListener called when complete */ private void updateMenuWithCellsToDelete(List deleteCells, final List addCells, final CompletionListener listener) { sendDeleteCurrentMenu(deleteCells, new CompletionListener() { @@ -163,8 +164,9 @@ class MenuReplaceOperation extends Task { /** * Send Delete RPCs for given menu cells + * * @param deleteMenuCells The menu cells to be deleted - * @param listener A CompletionListener called when the RPCs are finished with an error if any failed + * @param listener A CompletionListener called when the RPCs are finished with an error if any failed */ private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { @@ -201,8 +203,9 @@ class MenuReplaceOperation extends Task { /** * Send Add RPCs for given new menu cells compared to old menu cells + * * @param newMenuCells The new menu cells we want displayed - * @param listener A CompletionListener called when the RPCs are finished with an error if any failed + * @param listener A CompletionListener called when the RPCs are finished with an error if any failed */ private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { if (getState() == Task.CANCELED) { @@ -270,10 +273,11 @@ class MenuReplaceOperation extends Task { /** * Takes the submenu cells that are old keeps and new keeps and determines which cells need to be deleted or added + * * @param oldKeptCells The old kept cells * @param newKeptCells The new kept cells - * @param startIndex The index of the main menu to use - * @param listener A CompletionListener called when complete + * @param startIndex The index of the main menu to use + * @param listener A CompletionListener called when complete */ private void updateSubMenuWithOldKeptCells(final List oldKeptCells, final List newKeptCells, final int startIndex, final CompletionListener listener) { if (getState() == Task.CANCELED) { @@ -347,10 +351,14 @@ class MenuReplaceOperation extends Task { this.menuConfiguration = menuConfiguration; } - public void setCurrentMenu(List currentMenuCells) { + void setCurrentMenu(List currentMenuCells) { this.currentMenu = currentMenuCells; } + void setWindowCapability(WindowCapability windowCapability) { + this.windowCapability = windowCapability; + } + private void finishOperation(boolean success) { if (operationCompletionListener != null) { operationCompletionListener.onComplete(success, currentMenu); -- cgit v1.2.1 From 02bf117d3bd28409da23d1241572f328f8b7062c Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 8 Feb 2021 14:54:04 -0500 Subject: Add updateMenuReplaceOperationsWithNewMenuConfiguration() --- .../managers/screen/menu/BaseMenuManager.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index f7fa752a2..b98e0188d 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -299,13 +299,7 @@ abstract class BaseMenuManager extends BaseSubManager { DebugTool.logError(TAG, "Error setting new menu configuration. Will revert to old menu configuration."); } else { BaseMenuManager.this.menuConfiguration = menuConfiguration; - - // Update pending operations with new menuConfiguration - for (Task task : transactionQueue.getTasksAsList()) { - if (task instanceof MenuReplaceOperation) { - ((MenuReplaceOperation) task).setMenuConfiguration(menuConfiguration); - } - } + updateMenuReplaceOperationsWithNewMenuConfiguration(); } } }); @@ -426,6 +420,14 @@ abstract class BaseMenuManager extends BaseSubManager { } } + private void updateMenuReplaceOperationsWithNewMenuConfiguration(){ + for (Task task : transactionQueue.getTasksAsList()) { + if (task instanceof MenuReplaceOperation) { + ((MenuReplaceOperation) task).setMenuConfiguration(menuConfiguration); + } + } + } + private boolean isDynamicMenuUpdateActive(DynamicMenuUpdatesMode updateMode, String displayType) { if (updateMode.equals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE)) { if (displayType == null) { -- cgit v1.2.1 From ebfd8ce6fac2b954f7d961af6c393f130ab5c704 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 8 Feb 2021 16:12:34 -0500 Subject: Add MenuConfigurationUpdateOperationTests --- .../MenuConfigurationUpdateOperationTests.java | 225 +++++++++++++++++++++ .../managers/screen/menu/MenuManagerTests.java | 1 - 2 files changed, 225 insertions(+), 1 deletion(-) create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java new file mode 100644 index 000000000..e68c821b4 --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2019 Livio, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Livio Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package com.smartdevicelink.managers.screen.menu; + +import android.os.Handler; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.livio.taskmaster.Queue; +import com.livio.taskmaster.Taskmaster; +import com.smartdevicelink.managers.CompletionListener; +import com.smartdevicelink.managers.ISdl; +import com.smartdevicelink.proxy.RPCRequest; +import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.rpc.SdlMsgVersion; +import com.smartdevicelink.proxy.rpc.SetGlobalProperties; +import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.MenuLayout; +import com.smartdevicelink.util.Version; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.Random; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static junit.framework.TestCase.assertFalse; +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.verify; +import static org.mockito.Mockito.when; + +@RunWith(AndroidJUnit4.class) +public class MenuConfigurationUpdateOperationTests { + + private Handler mainHandler; + private Taskmaster taskmaster; + private Queue transactionQueue; + + @Before + public void setUp() throws Exception { + mainHandler = new Handler(getInstrumentation().getTargetContext().getMainLooper()); + taskmaster = new Taskmaster.Builder().build(); + taskmaster.start(); + transactionQueue= taskmaster.createQueue("MenuManager", new Random().nextInt(), false); + } + + @Test + public void testSuccess() { + final ISdl internalInterface = mock(ISdl.class); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(7, 0, 0))); + doAnswer(createSetGlobalPropertiesAnswer(true)).when(internalInterface).sendRPC(any(SetGlobalProperties.class)); + WindowCapability windowCapability = createWindowCapability(true, true); + MenuConfiguration menuConfiguration = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); + MenuConfigurationUpdateOperation operation = new MenuConfigurationUpdateOperation(internalInterface, windowCapability, menuConfiguration, new CompletionListener() { + @Override + public void onComplete(final boolean success) { + assertOnMainThread(new Runnable() { + @Override + public void run() { + assertTrue(success); + verify(internalInterface, Mockito.times(1)).sendRPC(any(SetGlobalProperties.class)); + } + }); + } + }); + transactionQueue.add(operation, false); + } + + @Test + public void testFailsRPCVersionOld() { + final ISdl internalInterface = mock(ISdl.class); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(5, 0, 0))); + doAnswer(createSetGlobalPropertiesAnswer(true)).when(internalInterface).sendRPC(any(SetGlobalProperties.class)); + WindowCapability windowCapability = createWindowCapability(true, true); + MenuConfiguration menuConfiguration = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); + MenuConfigurationUpdateOperation operation = new MenuConfigurationUpdateOperation(internalInterface, windowCapability, menuConfiguration, new CompletionListener() { + @Override + public void onComplete(final boolean success) { + assertOnMainThread(new Runnable() { + @Override + public void run() { + assertFalse(success); + verify(internalInterface, Mockito.times(0)).sendRPC(any(SetGlobalProperties.class)); + } + }); + } + }); + transactionQueue.add(operation, false); + } + + @Test + public void testFailsMenuLayoutNotSet() { + final ISdl internalInterface = mock(ISdl.class); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(7, 0, 0))); + doAnswer(createSetGlobalPropertiesAnswer(true)).when(internalInterface).sendRPC(any(SetGlobalProperties.class)); + WindowCapability windowCapability = createWindowCapability(true, true); + MenuConfiguration menuConfiguration = new MenuConfiguration(null, MenuLayout.LIST); + MenuConfigurationUpdateOperation operation = new MenuConfigurationUpdateOperation(internalInterface, windowCapability, menuConfiguration, new CompletionListener() { + @Override + public void onComplete(final boolean success) { + assertOnMainThread(new Runnable() { + @Override + public void run() { + assertFalse(success); + verify(internalInterface, Mockito.times(0)).sendRPC(any(SetGlobalProperties.class)); + } + }); + } + }); + transactionQueue.add(operation, false); + } + + @Test + public void testFailsMenuLayoutsAvailableEmpty() { + final ISdl internalInterface = mock(ISdl.class); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(7, 0, 0))); + doAnswer(createSetGlobalPropertiesAnswer(true)).when(internalInterface).sendRPC(any(SetGlobalProperties.class)); + WindowCapability windowCapability = createWindowCapability(true, true); + MenuConfiguration menuConfiguration = new MenuConfiguration(null, null); + MenuConfigurationUpdateOperation operation = new MenuConfigurationUpdateOperation(internalInterface, windowCapability, menuConfiguration, new CompletionListener() { + @Override + public void onComplete(final boolean success) { + assertOnMainThread(new Runnable() { + @Override + public void run() { + assertFalse(success); + verify(internalInterface, Mockito.times(0)).sendRPC(any(SetGlobalProperties.class)); + } + }); + } + }); + transactionQueue.add(operation, false); + } + + @Test + public void testFailsRPCNotSent() { + final ISdl internalInterface = mock(ISdl.class); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(7, 0, 0))); + doAnswer(createSetGlobalPropertiesAnswer(false)).when(internalInterface).sendRPC(any(SetGlobalProperties.class)); + WindowCapability windowCapability = createWindowCapability(true, true); + MenuConfiguration menuConfiguration = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); + MenuConfigurationUpdateOperation operation = new MenuConfigurationUpdateOperation(internalInterface, windowCapability, menuConfiguration, new CompletionListener() { + @Override + public void onComplete(final boolean success) { + assertOnMainThread(new Runnable() { + @Override + public void run() { + assertFalse(success); + verify(internalInterface, Mockito.times(1)).sendRPC(any(SetGlobalProperties.class)); + } + }); + } + }); + transactionQueue.add(operation, false); + } + + private Answer createSetGlobalPropertiesAnswer(final boolean success){ + return new Answer() { + @Override + public Void answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + RPCRequest request = (RPCRequest) args[0]; + RPCResponse response = new RPCResponse(request.getFunctionID().toString()); + response.setSuccess(success); + request.getOnRPCResponseListener().onResponse(request.getCorrelationID(), response); + return null; + } + }; + } + + private WindowCapability createWindowCapability (boolean supportsList, boolean supportsTile) { + WindowCapability windowCapability = new WindowCapability(); + windowCapability.setMenuLayoutsAvailable(new ArrayList()); + if (supportsList) { + windowCapability.getMenuLayoutsAvailable().add(MenuLayout.LIST); + } + if (supportsTile) { + windowCapability.getMenuLayoutsAvailable().add(MenuLayout.TILES); + } + return windowCapability; + } + + // Asserts on Taskmaster threads will fail silently so we need to do the assertions on main thread if the code is triggered from Taskmaster + private void assertOnMainThread(Runnable runnable) { + mainHandler.post(runnable); + } +} diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index 42555fde5..e2736e8ba 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -59,7 +59,6 @@ import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener; import com.smartdevicelink.util.Version; -import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -- cgit v1.2.1 From 3df1e6016856ced21b06f1bd7560aa0aaf290f3f Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 9 Feb 2021 10:10:05 -0500 Subject: Add MenuShowOperationTests --- .../MenuConfigurationUpdateOperationTests.java | 2 +- .../screen/menu/MenuShowOperationTests.java | 125 +++++++++++++++++++++ 2 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java index e68c821b4..53e880aba 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java @@ -79,7 +79,7 @@ public class MenuConfigurationUpdateOperationTests { mainHandler = new Handler(getInstrumentation().getTargetContext().getMainLooper()); taskmaster = new Taskmaster.Builder().build(); taskmaster.start(); - transactionQueue= taskmaster.createQueue("MenuManager", new Random().nextInt(), false); + transactionQueue = taskmaster.createQueue("MenuManager", new Random().nextInt(), false); } @Test diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java new file mode 100644 index 000000000..ba4591a26 --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019 Livio, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Livio Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +package com.smartdevicelink.managers.screen.menu; + +import android.os.Handler; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.livio.taskmaster.Queue; +import com.livio.taskmaster.Taskmaster; +import com.smartdevicelink.managers.ISdl; +import com.smartdevicelink.proxy.RPCRequest; +import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.rpc.ShowAppMenu; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.Random; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +@RunWith(AndroidJUnit4.class) +public class MenuShowOperationTests { + private Handler mainHandler; + private Taskmaster taskmaster; + private Queue transactionQueue; + + @Before + public void setUp() throws Exception { + mainHandler = new Handler(getInstrumentation().getTargetContext().getMainLooper()); + taskmaster = new Taskmaster.Builder().build(); + taskmaster.start(); + transactionQueue = taskmaster.createQueue("MenuManager", new Random().nextInt(), false); + } + + @Test + public void testSuccess() { + final ISdl internalInterface = mock(ISdl.class); + MenuCell menuCell = null; + Integer menuIdToAssert = menuCell != null ? menuCell.getCellId() : null; + Answer showAppMenuAnswer = createShowAppMenuAnswer(true, menuIdToAssert); + doAnswer(showAppMenuAnswer).when(internalInterface).sendRPC(any(ShowAppMenu.class)); + MenuShowOperation operation = new MenuShowOperation(internalInterface, menuCell); + transactionQueue.add(operation, false); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + verify(internalInterface, Mockito.times(1)).sendRPC(any(ShowAppMenu.class)); + } + + private Answer createShowAppMenuAnswer(final boolean success, final Integer menuIdToAssert){ + return new Answer() { + @Override + public Void answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + final RPCRequest request = (RPCRequest) args[0]; + assertOnMainThread(new Runnable() { + @Override + public void run() { + ShowAppMenu showAppMenu = (ShowAppMenu) request; + assertEquals(showAppMenu.getMenuID(), menuIdToAssert); + } + }); + RPCResponse response = new RPCResponse(request.getFunctionID().toString()); + response.setSuccess(success); + request.getOnRPCResponseListener().onResponse(request.getCorrelationID(), response); + return null; + } + }; + } + + private void sleep() { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + // Asserts on Taskmaster threads will fail silently so we need to do the assertions on main thread if the code is triggered from Taskmaster + private void assertOnMainThread(Runnable runnable) { + mainHandler.post(runnable); + } +} -- cgit v1.2.1 From 1e4f662e37390c9fd32c2a411b49a8cf75893e57 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 10 Feb 2021 16:33:11 -0500 Subject: Alignment fixes --- .../smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index b30e19eb8..903891110 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -210,8 +210,7 @@ class MenuReplaceUtilities { } } if (addedCell != null) { - addMenuCell(addedCell, mainMenuList, position); - return true; + return addMenuCell(addedCell, mainMenuList, position); } return false; } @@ -234,12 +233,13 @@ class MenuReplaceUtilities { } } } + return false; } else { // The cell does not have a parent id, just insert it into the main menu insertMenuCell(cell, menuCellList, position); return true; } - return false; + } private static void insertMenuCell(MenuCell cell, List cellList, int position) { -- cgit v1.2.1 From 8d2c4903ee82d929b5bc2728d40c60d4c01c8a17 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 10 Feb 2021 16:49:02 -0500 Subject: Add more MenuShowOperation tests --- .../MenuConfigurationUpdateOperationTests.java | 2 +- .../screen/menu/MenuShowOperationTests.java | 23 ++++++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java index 53e880aba..c52cf6186 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Livio, Inc. + * Copyright (c) 2021 Livio, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java index ba4591a26..2f9c6e67e 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Livio, Inc. + * Copyright (c) 2021 Livio, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,6 +42,8 @@ import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.proxy.RPCRequest; import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.ShowAppMenu; +import com.smartdevicelink.proxy.rpc.enums.MenuLayout; +import com.smartdevicelink.test.TestValues; import org.junit.Before; import org.junit.Test; @@ -74,7 +76,7 @@ public class MenuShowOperationTests { } @Test - public void testSuccess() { + public void testOpenMainMenu() { final ISdl internalInterface = mock(ISdl.class); MenuCell menuCell = null; Integer menuIdToAssert = menuCell != null ? menuCell.getCellId() : null; @@ -89,6 +91,23 @@ public class MenuShowOperationTests { verify(internalInterface, Mockito.times(1)).sendRPC(any(ShowAppMenu.class)); } + @Test + public void testOpenSubMenu() { + final ISdl internalInterface = mock(ISdl.class); + MenuCell menuCell = new MenuCell(TestValues.GENERAL_STRING, MenuLayout.TILES, null, null); + menuCell.setCellId(TestValues.GENERAL_INT); + Integer menuIdToAssert = menuCell != null ? menuCell.getCellId() : null; + Answer showAppMenuAnswer = createShowAppMenuAnswer(true, menuIdToAssert); + doAnswer(showAppMenuAnswer).when(internalInterface).sendRPC(any(ShowAppMenu.class)); + MenuShowOperation operation = new MenuShowOperation(internalInterface, menuCell); + transactionQueue.add(operation, false); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + + verify(internalInterface, Mockito.times(1)).sendRPC(any(ShowAppMenu.class)); + } + private Answer createShowAppMenuAnswer(final boolean success, final Integer menuIdToAssert){ return new Answer() { @Override -- cgit v1.2.1 From dce2d7fbeda8da2b4dde4cc32d177670e1cb3947 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 12 Feb 2021 13:31:43 -0500 Subject: Add MenuReplaceOperationTests --- .../screen/menu/MenuReplaceOperationTests.java | 194 +++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java new file mode 100644 index 000000000..d7f36a79b --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java @@ -0,0 +1,194 @@ +/* + * Copyright (c) 2021 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.menu; + +import android.os.Handler; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.livio.taskmaster.Queue; +import com.livio.taskmaster.Taskmaster; +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.proxy.RPCMessage; +import com.smartdevicelink.proxy.RPCRequest; +import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.MenuLayout; +import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; +import com.smartdevicelink.test.TestValues; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; +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.verify; +import static org.mockito.Mockito.when; + +@RunWith(AndroidJUnit4.class) +public class MenuReplaceOperationTests { + static int lastMenuId = menuCellIdMin; + + private Handler mainHandler; + private Taskmaster taskmaster; + private Queue transactionQueue; + + @Before + public void setUp() throws Exception { + mainHandler = new Handler(getInstrumentation().getTargetContext().getMainLooper()); + taskmaster = new Taskmaster.Builder().build(); + taskmaster.start(); + transactionQueue = taskmaster.createQueue("MenuManager", new Random().nextInt(), false); + } + + @Test + public void testSuccess() { + final ISdl internalInterface = createISdlMock(); + + FileManager fileManager = createFileManagerMock(); + + WindowCapability windowCapability = createWindowCapability(true, true); + MenuConfiguration menuConfiguration = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); + List currentMenu = new ArrayList<>(); + MenuCell menuCell1 = new MenuCell("cell 1", TestValues.GENERAL_ARTWORK, null, null); + MenuCell menuCell2 = new MenuCell("cell 2", TestValues.GENERAL_ARTWORK, null, null); + + final List updatedMenu = Arrays.asList(menuCell1, menuCell2); + updateIdsOnMenuCells(updatedMenu, parentIdNotFound); + MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, currentMenu, updatedMenu, true, new MenuManagerCompletionListener() { + @Override + public void onComplete(final boolean success, final List currentMenuCells) { + assertOnMainThread(new Runnable() { + @Override + public void run() { + assertTrue(success); + assertEquals(currentMenuCells, updatedMenu); + } + }); + } + }); + transactionQueue.add(operation, false); + } + + private ISdl createISdlMock(){ + final ISdl internalInterface = mock(ISdl.class); + + // When internalInterface.sendRPCs() is called, call listener.onFinished() to fake the response + final Answer answer = new Answer() { + @Override + public Void answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + List rpcs = (List) args[0]; + OnMultipleRequestListener listener = (OnMultipleRequestListener) args[1]; + + for (RPCMessage rpcMessage : rpcs) { + RPCRequest request = (RPCRequest) rpcMessage; + RPCResponse response = new RPCResponse(request.getFunctionID().toString()); + response.setCorrelationID(request.getCorrelationID()); + response.setSuccess(true); + listener.onResponse(request.getCorrelationID(), response); + } + + listener.onFinished(); + return null; + } + }; + doAnswer(answer).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); + + return internalInterface; + } + private FileManager createFileManagerMock() { + FileManager fileManager = mock(FileManager.class); + + when(fileManager.hasUploadedFile(any(SdlArtwork.class))).thenReturn(true); + + Answer onFileManagerUploadAnswer = new Answer() { + @Override + public Void answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + MultipleFileCompletionListener multipleFileCompletionListener = (MultipleFileCompletionListener) args[1]; + multipleFileCompletionListener.onComplete(null); + return null; + } + }; + doAnswer(onFileManagerUploadAnswer).when(fileManager).uploadArtworks(any(List.class), any(MultipleFileCompletionListener.class)); + return fileManager; + } + + private WindowCapability createWindowCapability (boolean supportsList, boolean supportsTile) { + WindowCapability windowCapability = new WindowCapability(); + windowCapability.setMenuLayoutsAvailable(new ArrayList()); + if (supportsList) { + windowCapability.getMenuLayoutsAvailable().add(MenuLayout.LIST); + } + if (supportsTile) { + windowCapability.getMenuLayoutsAvailable().add(MenuLayout.TILES); + } + return windowCapability; + } + + private void updateIdsOnMenuCells(List menuCells, int parentId) { + for (MenuCell cell : menuCells) { + cell.setCellId(lastMenuId++); + if (parentId != parentIdNotFound) { + cell.setParentCellId(parentId); + } + if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); + } + } + } + + // Asserts on Taskmaster threads will fail silently so we need to do the assertions on main thread if the code is triggered from Taskmaster + private void assertOnMainThread(Runnable runnable) { + mainHandler.post(runnable); + } +} -- cgit v1.2.1 From f719dd8beaf7142218880da74b7ca974f9124143 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 12 Feb 2021 13:46:02 -0500 Subject: Fix an issue with MenuReplaceUtilities.sendRPCs() --- .../com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java | 1 + 1 file changed, 1 insertion(+) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 903891110..03d502e76 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -260,6 +260,7 @@ class MenuReplaceUtilities { final Map errors = new HashMap<>(); if (requests == null || requests.isEmpty()) { listener.onComplete(true, errors); + return; } internalInterface.sendRPCs(requests, new OnMultipleRequestListener() { -- cgit v1.2.1 From 60afc80ddfe163b8f852c020048bbd1d98d038ca Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 12 Feb 2021 17:06:52 -0500 Subject: Fix an issue in addMenuRequestWithCommandId() --- .../smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 03d502e76..4039af35d 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -206,7 +206,10 @@ class MenuReplaceUtilities { addedCell = cell; break; } else if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - return addMenuRequestWithCommandId(commandId, position, cell.getSubCells(), mainMenuList); + boolean success = addMenuRequestWithCommandId(commandId, position, cell.getSubCells(), mainMenuList); + if (success) { + return true; + } } } if (addedCell != null) { -- cgit v1.2.1 From b27731ba2c87c420392af04ffb5f9cdf110e541b Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 12 Feb 2021 17:10:06 -0500 Subject: Add more unit tests for addMenuRequestWithCommandId() --- .../screen/menu/MenuReplaceOperationTests.java | 15 +++++++------- .../screen/menu/MenuReplaceUtilitiesTests.java | 24 +++++++++++++++++----- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java index d7f36a79b..f49b455fb 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java @@ -92,13 +92,12 @@ public class MenuReplaceOperationTests { @Test public void testSuccess() { final ISdl internalInterface = createISdlMock(); - FileManager fileManager = createFileManagerMock(); - WindowCapability windowCapability = createWindowCapability(true, true); MenuConfiguration menuConfiguration = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); List currentMenu = new ArrayList<>(); - MenuCell menuCell1 = new MenuCell("cell 1", TestValues.GENERAL_ARTWORK, null, null); + MenuCell menuCell1_1 = new MenuCell("cell 1_1", TestValues.GENERAL_ARTWORK, null, null); + MenuCell menuCell1 = new MenuCell("cell 1", null, TestValues.GENERAL_ARTWORK, Arrays.asList(menuCell1_1)); MenuCell menuCell2 = new MenuCell("cell 2", TestValues.GENERAL_ARTWORK, null, null); final List updatedMenu = Arrays.asList(menuCell1, menuCell2); @@ -111,6 +110,7 @@ public class MenuReplaceOperationTests { public void run() { assertTrue(success); assertEquals(currentMenuCells, updatedMenu); + verify(internalInterface, Mockito.times(2)).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); } }); } @@ -118,7 +118,7 @@ public class MenuReplaceOperationTests { transactionQueue.add(operation, false); } - private ISdl createISdlMock(){ + private ISdl createISdlMock() { final ISdl internalInterface = mock(ISdl.class); // When internalInterface.sendRPCs() is called, call listener.onFinished() to fake the response @@ -126,7 +126,7 @@ public class MenuReplaceOperationTests { @Override public Void answer(InvocationOnMock invocation) { Object[] args = invocation.getArguments(); - List rpcs = (List) args[0]; + List rpcs = (List) args[0]; OnMultipleRequestListener listener = (OnMultipleRequestListener) args[1]; for (RPCMessage rpcMessage : rpcs) { @@ -145,6 +145,7 @@ public class MenuReplaceOperationTests { return internalInterface; } + private FileManager createFileManagerMock() { FileManager fileManager = mock(FileManager.class); @@ -163,7 +164,7 @@ public class MenuReplaceOperationTests { return fileManager; } - private WindowCapability createWindowCapability (boolean supportsList, boolean supportsTile) { + private WindowCapability createWindowCapability(boolean supportsList, boolean supportsTile) { WindowCapability windowCapability = new WindowCapability(); windowCapability.setMenuLayoutsAvailable(new ArrayList()); if (supportsList) { @@ -175,7 +176,7 @@ public class MenuReplaceOperationTests { return windowCapability; } - private void updateIdsOnMenuCells(List menuCells, int parentId) { + private void updateIdsOnMenuCells(List menuCells, int parentId) { for (MenuCell cell : menuCells) { cell.setCellId(lastMenuId++); if (parentId != parentIdNotFound) { diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java index 180ad3617..18a63b489 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java @@ -178,6 +178,17 @@ public class MenuReplaceUtilitiesTests { assertEquals(5, actualMenuCellList.size()); assertEquals(1, actualMenuCellList.get(4).getSubCells().size()); + // Add cell c5-1-1 + menuCellToAdd = newMenuList.get(0).getSubCells().get(0).getSubCells().get(0); + cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 0, newMenuList, actualMenuCellList); + assertTrue(cellAdded); + expectedMenuCellList.get(4).getSubCells().get(0).getSubCells().add(0, cloneMenuCellAndRemoveSubCells(menuCellToAdd)); + assertEquals(expectedMenuCellList, actualMenuCellList); + assertEquals(5, actualMenuCellList.size()); + assertEquals(1, actualMenuCellList.get(4).getSubCells().size()); + assertEquals(1, actualMenuCellList.get(4).getSubCells().get(0).getSubCells().size()); + + // Add cell c5-2 menuCellToAdd = newMenuList.get(0).getSubCells().get(1); cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 1, newMenuList, actualMenuCellList); @@ -186,9 +197,10 @@ public class MenuReplaceUtilitiesTests { assertEquals(expectedMenuCellList, actualMenuCellList); assertEquals(5, actualMenuCellList.size()); assertEquals(2, actualMenuCellList.get(4).getSubCells().size()); + assertEquals(1, actualMenuCellList.get(4).getSubCells().get(0).getSubCells().size()); assertEquals(0, actualMenuCellList.get(4).getSubCells().get(1).getSubCells().size()); - // Add cell c5-2-2 + // Add cell c5-2-1 menuCellToAdd = newMenuList.get(0).getSubCells().get(1).getSubCells().get(0); cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 0, newMenuList, actualMenuCellList); assertTrue(cellAdded); @@ -196,6 +208,7 @@ public class MenuReplaceUtilitiesTests { assertEquals(expectedMenuCellList, actualMenuCellList); assertEquals(5, actualMenuCellList.size()); assertEquals(2, actualMenuCellList.get(4).getSubCells().size()); + assertEquals(1, actualMenuCellList.get(4).getSubCells().get(0).getSubCells().size()); assertEquals(1, actualMenuCellList.get(4).getSubCells().get(1).getSubCells().size()); } @@ -308,9 +321,9 @@ public class MenuReplaceUtilitiesTests { / \ / \ c5-1 c5-2 - / - / - c5-2-1 + / / + / / + c5-1-1 c5-2-1 */ @@ -319,7 +332,8 @@ public class MenuReplaceUtilitiesTests { MenuSelectionListener listener = null; MenuLayout subMenuLayout = null; - MenuCell menuCell5_1 = new MenuCell("c5_1", sdlArtwork, voiceCommands, listener); + MenuCell menuCell5_1_1 = new MenuCell("c5_1_1", sdlArtwork, voiceCommands, listener); + MenuCell menuCell5_1 = new MenuCell("c5_1", subMenuLayout, sdlArtwork, new ArrayList<>(Arrays.asList(menuCell5_1_1))); MenuCell menuCell5_2_1 = new MenuCell("c5_2_1", sdlArtwork, voiceCommands, listener); MenuCell menuCell5_2 = new MenuCell("c5_2", subMenuLayout, sdlArtwork, new ArrayList<>(Arrays.asList(menuCell5_2_1))); MenuCell menuCell5 = new MenuCell("c5", subMenuLayout, sdlArtwork, new ArrayList<>(Arrays.asList(menuCell5_1, menuCell5_2))); -- cgit v1.2.1 From 73934d3a58b553980f2273fef7e2970c0b22b022 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 12 Feb 2021 17:11:07 -0500 Subject: Remove extra line --- .../smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java | 1 - 1 file changed, 1 deletion(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java index 18a63b489..a40a9968b 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java @@ -188,7 +188,6 @@ public class MenuReplaceUtilitiesTests { assertEquals(1, actualMenuCellList.get(4).getSubCells().size()); assertEquals(1, actualMenuCellList.get(4).getSubCells().get(0).getSubCells().size()); - // Add cell c5-2 menuCellToAdd = newMenuList.get(0).getSubCells().get(1); cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 1, newMenuList, actualMenuCellList); -- cgit v1.2.1 From 7cd516844d76d44c2c13ce977a7db7041a6aee23 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 15 Feb 2021 17:21:42 -0500 Subject: Fix potential issue in addMenuCell() --- .../com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 4039af35d..e15f64a36 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -223,6 +223,9 @@ class MenuReplaceUtilities { // If the cell has a parent id, we need to find the cell with a matching cell id and insert it into its submenu for (MenuCell menuCell : menuCellList) { if (menuCell.getCellId() == cell.getParentCellId()) { + if (menuCell.getSubCells() == null) { + menuCell.setSubCells(new ArrayList()); + } // If we found the correct submenu, insert it into that submenu insertMenuCell(cell, menuCell.getSubCells(), position); return true; -- cgit v1.2.1 From 3dd51e0bedae9f17dfa8ad3314e56fde59b0012c Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 23 Feb 2021 14:14:04 -0500 Subject: Remove unused import --- .../java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 1 - 1 file changed, 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index b98e0188d..80bdb053e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -48,7 +48,6 @@ import com.smartdevicelink.proxy.RPCNotification; import com.smartdevicelink.proxy.rpc.DisplayCapability; import com.smartdevicelink.proxy.rpc.OnCommand; import com.smartdevicelink.proxy.rpc.OnHMIStatus; -import com.smartdevicelink.proxy.rpc.SdlMsgVersion; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.DisplayType; import com.smartdevicelink.proxy.rpc.enums.HMILevel; -- cgit v1.2.1 From db382d37bc531dcac5f87d5049f64cb87aca4f75 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 23 Feb 2021 15:13:16 -0500 Subject: Fix issue in getMenuConfiguration() --- .../com/smartdevicelink/managers/screen/ScreenManagerTests.java | 4 +--- .../com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 8 ++++++-- 2 files changed, 7 insertions(+), 5 deletions(-) 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 dcf531c15..35fe0db30 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 @@ -144,9 +144,7 @@ public class ScreenManagerTests { assertEquals(screenManager.getDynamicMenuUpdatesMode(), DynamicMenuUpdatesMode.FORCE_ON); assertEquals(screenManager.getMenu(), TestValues.GENERAL_MENUCELL_LIST); - // Should not set because of improper RAI response and improper HMI states - assertNull(screenManager.getMenuConfiguration().getMenuLayout()); - assertNull(screenManager.getMenuConfiguration().getSubMenuLayout()); + assertEquals(screenManager.getMenuConfiguration(), TestValues.GENERAL_MENU_CONFIGURATION); } @Test diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 80bdb053e..ffda4f5ff 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -71,6 +71,7 @@ abstract class BaseMenuManager extends BaseSubManager { List currentMenuCells; List menuCells; DynamicMenuUpdatesMode dynamicMenuUpdatesMode; + MenuConfiguration currentMenuConfiguration; MenuConfiguration menuConfiguration; private String displayType; HMILevel currentHMILevel; @@ -113,6 +114,7 @@ abstract class BaseMenuManager extends BaseSubManager { dynamicMenuUpdatesMode = DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE; windowCapability = null; menuConfiguration = null; + currentMenuConfiguration = null; // Cancel the operations if (transactionQueue != null) { @@ -291,13 +293,15 @@ abstract class BaseMenuManager extends BaseSubManager { return; } + this.menuConfiguration = menuConfiguration; + MenuConfigurationUpdateOperation operation = new MenuConfigurationUpdateOperation(internalInterface, windowCapability, menuConfiguration, new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { DebugTool.logError(TAG, "Error setting new menu configuration. Will revert to old menu configuration."); } else { - BaseMenuManager.this.menuConfiguration = menuConfiguration; + BaseMenuManager.this.currentMenuConfiguration = menuConfiguration; updateMenuReplaceOperationsWithNewMenuConfiguration(); } } @@ -422,7 +426,7 @@ abstract class BaseMenuManager extends BaseSubManager { private void updateMenuReplaceOperationsWithNewMenuConfiguration(){ for (Task task : transactionQueue.getTasksAsList()) { if (task instanceof MenuReplaceOperation) { - ((MenuReplaceOperation) task).setMenuConfiguration(menuConfiguration); + ((MenuReplaceOperation) task).setMenuConfiguration(currentMenuConfiguration); } } } -- cgit v1.2.1 From 212a3d644add64a45235273f85bdd35bd95b4e2a Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 24 Feb 2021 10:24:30 -0500 Subject: Update menu manager for main menu ui proposal --- .../managers/screen/menu/MenuReplaceUtilities.java | 40 ++++++++++++++++------ 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index e15f64a36..7771fc4d4 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -36,8 +36,13 @@ class MenuReplaceUtilities { List artworks = new ArrayList<>(); for (MenuCell cell : cells) { - if (fileManager != null && fileManager.fileNeedsUpload(cell.getIcon())) { - artworks.add(cell.getIcon()); + if (fileManager != null) { + if (fileManager.fileNeedsUpload(cell.getIcon())) { + artworks.add(cell.getIcon()); + } + if (fileManager.fileNeedsUpload(cell.getSecondaryArtwork())) { + artworks.add(cell.getSecondaryArtwork()); + } } if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { artworks.addAll(findAllArtworksToBeUploadedFromCells(cell.getSubCells(), fileManager, windowCapability)); @@ -47,13 +52,16 @@ class MenuReplaceUtilities { return artworks; } - static boolean shouldCellIncludeImage(MenuCell cell, FileManager fileManager, WindowCapability windowCapability) { + static boolean shouldCellIncludeImage(MenuCell cell, FileManager fileManager, WindowCapability windowCapability, boolean isSecondaryImage) { // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image - boolean supportsImage = cell.getSubCells() != null && !cell.getSubCells().isEmpty() ? ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.subMenuIcon) : ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); - if (cell.getIcon() == null || !supportsImage) { + SdlArtwork artwork = isSecondaryImage ? cell.getSecondaryArtwork() : cell.getIcon(); + ImageFieldName mainMenuImageFieldName = isSecondaryImage ? ImageFieldName.menuCommandSecondaryImage : ImageFieldName.cmdIcon; + ImageFieldName subMenuImageFieldName = isSecondaryImage ? ImageFieldName.menuSubMenuSecondaryImage : ImageFieldName.subMenuIcon; + boolean supportsImage = cell.getSubCells() != null && !cell.getSubCells().isEmpty() ? ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, subMenuImageFieldName) : ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, mainMenuImageFieldName); + if (artwork == null || !supportsImage) { return false; } - return (fileManager.hasUploadedFile(cell.getIcon()) || cell.getIcon().isStaticIcon()); + return (fileManager.hasUploadedFile(artwork) || artwork.isStaticIcon()); } static int commandIdForRPCRequest(RPCRequest request) { @@ -146,6 +154,8 @@ class MenuReplaceUtilities { AddCommand command = new AddCommand(cell.getCellId()); MenuParams params = new MenuParams(cell.getTitle()); + params.setSecondaryText(cell.getSecondaryText()); + params.setTertiaryText(cell.getTertiaryText()); params.setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null); params.setPosition(position); @@ -155,15 +165,20 @@ class MenuReplaceUtilities { } else { command.setVrCommands(null); } - boolean shouldCellIncludeImage = cell.getIcon() != null && shouldCellIncludeImage(cell, fileManager, windowCapability); - command.setCmdIcon(shouldCellIncludeImage ? cell.getIcon().getImageRPC() : null); + boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && shouldCellIncludeImage(cell, fileManager, windowCapability, false); + command.setCmdIcon(shouldCellIncludePrimaryImage ? cell.getIcon().getImageRPC() : null); + + boolean shouldCellIncludeSecondaryImage = cell.getSecondaryArtwork() != null && shouldCellIncludeImage(cell, fileManager, windowCapability, true); + command.setSecondaryImage(shouldCellIncludeSecondaryImage ? cell.getSecondaryArtwork().getImageRPC() : null); return command; } static AddSubMenu subMenuCommandForMenuCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position, MenuLayout defaultSubmenuLayout) { - boolean shouldCellIncludeImage = cell.getIcon() != null && cell.getIcon().getImageRPC() != null && shouldCellIncludeImage(cell, fileManager, windowCapability); - Image icon = (shouldCellIncludeImage ? cell.getIcon().getImageRPC() : null); + boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && cell.getIcon().getImageRPC() != null && shouldCellIncludeImage(cell, fileManager, windowCapability, false); + Image icon = (shouldCellIncludePrimaryImage ? cell.getIcon().getImageRPC() : null); + boolean shouldCellIncludeSecondaryImage = cell.getSecondaryArtwork() != null && cell.getSecondaryArtwork().getImageRPC() != null && shouldCellIncludeImage(cell, fileManager, windowCapability, true); + Image secondaryIcon = (shouldCellIncludeSecondaryImage ? cell.getSecondaryArtwork().getImageRPC() : null); MenuLayout submenuLayout; List availableMenuLayouts = windowCapability != null ? windowCapability.getMenuLayoutsAvailable() : null; @@ -175,9 +190,12 @@ class MenuReplaceUtilities { return new AddSubMenu(cell.getCellId(), cell.getTitle()) .setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null) + .setSecondaryText(cell.getSecondaryText()) + .setTertiaryText(cell.getTertiaryText()) .setPosition(position) .setMenuLayout(submenuLayout) - .setMenuIcon(icon); + .setMenuIcon(icon) + .setSecondaryImage(secondaryIcon); } static boolean removeMenuCellFromList(List menuCellList, int commandId) { -- cgit v1.2.1 From f6e9e369ebd4e9417b6be6977dd85e76245056b4 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 24 Feb 2021 11:18:10 -0500 Subject: Fix menu manager tests --- .../managers/screen/menu/MenuReplaceUtilitiesTests.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java index a40a9968b..0e1dec624 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java @@ -222,31 +222,31 @@ public class MenuReplaceUtilitiesTests { menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(true); - assertTrue(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability)); + assertTrue(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability, false)); // Case 2 - Image are not supported menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); windowCapability = createWindowCapability(false, false); fileManager = createMockFileManager(true); - assertFalse(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability)); + assertFalse(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability, false)); // Case 3 - Artwork is null menuCell = new MenuCell(TestValues.GENERAL_STRING, null, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(true); - assertFalse(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability)); + assertFalse(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability, false)); // Case 4 - Artwork has not been uploaded menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(false); - assertFalse(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability)); + assertFalse(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability, false)); // Case 5 - Artwork is static icon menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK_STATIC, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(false); - assertTrue(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability)); + assertTrue(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability, false)); } private WindowCapability createWindowCapability (boolean supportsCmdIcon, boolean supportsSubMenuIcon) { -- cgit v1.2.1 From 6b5a6e629d1bebc612bc8a699b061b6c914c2200 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 25 Feb 2021 15:37:01 -0500 Subject: Fix potential NPE --- .../java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index ffda4f5ff..40b8378fe 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -240,7 +240,7 @@ abstract class BaseMenuManager extends BaseSubManager { } if (cell != null && (cell.getSubCells() == null || cell.getSubCells().isEmpty())) { - DebugTool.logError(DebugTool.TAG, String.format("The cell %s does not contain any sub cells, so no submenu can be opened", foundClonedCell.getTitle())); + DebugTool.logError(DebugTool.TAG, String.format("The cell %s does not contain any sub cells, so no submenu can be opened", cell.getTitle())); return false; } else if (cell != null && foundClonedCell == null) { DebugTool.logError(DebugTool.TAG, "This cell has not been sent to the head unit, so no submenu can be opened. Make sure that the cell exists in the SDLManager.menu array"); -- cgit v1.2.1 From 4bb1d3bbb253776720cded207e82de1c4d331cea Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 28 Apr 2021 16:17:47 -0400 Subject: Fix unit tests --- .../com/smartdevicelink/managers/file/filetypes/SdlArtworkTests.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/filetypes/SdlArtworkTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/filetypes/SdlArtworkTests.java index f4fb5e3d2..49a10ab3d 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/filetypes/SdlArtworkTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/filetypes/SdlArtworkTests.java @@ -37,6 +37,9 @@ public class SdlArtworkTests { } public static boolean equalTest(SdlArtwork original, SdlArtwork clone) { + if (original == null && clone == null) { + return true; + } assertNotNull(original); assertNotNull(clone); -- cgit v1.2.1 From e958528b1da754aca81099877a3e854ada2ee3f1 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 29 Apr 2021 11:19:12 -0400 Subject: Reformat code --- .../smartdevicelink/managers/screen/menu/BaseMenuManager.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 40b8378fe..4ac606f19 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -225,7 +225,7 @@ abstract class BaseMenuManager extends BaseSubManager { return menuCells; } - private boolean openMenuPrivate(MenuCell cell){ + private boolean openMenuPrivate(MenuCell cell) { MenuCell foundClonedCell = null; if (cell != null) { @@ -240,7 +240,7 @@ abstract class BaseMenuManager extends BaseSubManager { } if (cell != null && (cell.getSubCells() == null || cell.getSubCells().isEmpty())) { - DebugTool.logError(DebugTool.TAG, String.format("The cell %s does not contain any sub cells, so no submenu can be opened", cell.getTitle())); + DebugTool.logError(DebugTool.TAG, String.format("The cell %s does not contain any sub cells, so no submenu can be opened", cell.getTitle())); return false; } else if (cell != null && foundClonedCell == null) { DebugTool.logError(DebugTool.TAG, "This cell has not been sent to the head unit, so no submenu can be opened. Make sure that the cell exists in the SDLManager.menu array"); @@ -264,6 +264,7 @@ abstract class BaseMenuManager extends BaseSubManager { return true; } + /** * Opens the Main Menu */ @@ -407,7 +408,7 @@ abstract class BaseMenuManager extends BaseSubManager { return false; } - private void updateMenuReplaceOperationsWithNewCurrentMenu () { + private void updateMenuReplaceOperationsWithNewCurrentMenu() { for (Task task : transactionQueue.getTasksAsList()) { if (task instanceof MenuReplaceOperation) { ((MenuReplaceOperation) task).setCurrentMenu(this.currentMenuCells); @@ -423,7 +424,7 @@ abstract class BaseMenuManager extends BaseSubManager { } } - private void updateMenuReplaceOperationsWithNewMenuConfiguration(){ + private void updateMenuReplaceOperationsWithNewMenuConfiguration() { for (Task task : transactionQueue.getTasksAsList()) { if (task instanceof MenuReplaceOperation) { ((MenuReplaceOperation) task).setMenuConfiguration(currentMenuConfiguration); -- cgit v1.2.1 From ba75aeb378fee3cca4f186052177fbcf036c223c Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 29 Apr 2021 11:58:46 -0400 Subject: Reformat code --- .../managers/screen/menu/DynamicMenuUpdateAlgorithm.java | 6 +++--- .../managers/screen/menu/DynamicMenuUpdateRunScore.java | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index ea7c7fe89..8bf6b3718 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -17,15 +17,15 @@ class DynamicMenuUpdateAlgorithm { static DynamicMenuUpdateRunScore compareOldMenuCells(List oldMenuCells, List updatedMenuCells) { if (!oldMenuCells.isEmpty() && updatedMenuCells.isEmpty()) { return new DynamicMenuUpdateRunScore(buildAllDeleteStatusesForMenu(oldMenuCells), new ArrayList(), 0); - } else if (oldMenuCells.isEmpty() && !updatedMenuCells.isEmpty()){ + } else if (oldMenuCells.isEmpty() && !updatedMenuCells.isEmpty()) { return new DynamicMenuUpdateRunScore(new ArrayList(), buildAllAddStatusesForMenu(updatedMenuCells), updatedMenuCells.size()); - } else if (oldMenuCells.isEmpty()){ + } else if (oldMenuCells.isEmpty()) { return null; } return startCompareAtRun(0, oldMenuCells, updatedMenuCells); } - static DynamicMenuUpdateRunScore startCompareAtRun(int startRun, List oldMenuCells, List updatedMenuCells) { + private static DynamicMenuUpdateRunScore startCompareAtRun(int startRun, List oldMenuCells, List updatedMenuCells) { DynamicMenuUpdateRunScore bestScore = null; for (int run = startRun; run < oldMenuCells.size(); run++) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java index 2cc93f2f1..2d5e75462 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java @@ -32,12 +32,13 @@ package com.smartdevicelink.managers.screen.menu; -import java.util.List; import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; +import java.util.List; + class DynamicMenuUpdateRunScore { private List oldStatus; // Will contain all the Deletes and Keeps - private List updatedStatus; // Will contain all the Adds and Keeps + private List updatedStatus; // Will contain all the Adds and Keeps private int score; // Will contain the score, number of total Adds that will need to be created DynamicMenuUpdateRunScore(List oldStatus, List updatedStatus, int score) { -- cgit v1.2.1 From 9506c1feb933bcc8e7334138a373e240954fbe6f Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 29 Apr 2021 15:15:15 -0400 Subject: Fix empty string bug with Menu Cell #1623 --- .../managers/screen/menu/MenuReplaceOperation.java | 2 +- .../managers/screen/menu/MenuReplaceUtilities.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 3f53528a2..a93a1b01a 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -102,7 +102,7 @@ class MenuReplaceOperation extends Task { final List oldKeeps = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.KEEP); final List newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); - // Since we are creating a new Menu but keeping old cells we must first transfer the old cellIDs to the new menus kept cells. + // Since we are creating a new menu but keeping old cells, we must first transfer the old cellIDs to the new menu kept cells. // This is needed for the onCommands to still work // We will transfer the ids for subCells later transferCellIDFromOldCells(oldKeeps, newKeeps); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 7771fc4d4..74447ef99 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -141,7 +141,7 @@ class MenuReplaceUtilities { if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { commands.add(subMenuCommandForMenuCell(cell, fileManager, windowCapability, cellIndex, defaultSubmenuLayout)); - // recursively grab the commands for all the sub cells + // Recursively grab the commands for all the sub cells commands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); } else { commands.add(commandForMenuCell(cell, fileManager, windowCapability, cellIndex)); @@ -154,8 +154,8 @@ class MenuReplaceUtilities { AddCommand command = new AddCommand(cell.getCellId()); MenuParams params = new MenuParams(cell.getTitle()); - params.setSecondaryText(cell.getSecondaryText()); - params.setTertiaryText(cell.getTertiaryText()); + params.setSecondaryText(cell.getSecondaryText() != null && cell.getSecondaryText().isEmpty() ? null : cell.getSecondaryText()); + params.setTertiaryText(cell.getTertiaryText() != null && cell.getTertiaryText().isEmpty() ? null : cell.getTertiaryText()); params.setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null); params.setPosition(position); @@ -190,8 +190,8 @@ class MenuReplaceUtilities { return new AddSubMenu(cell.getCellId(), cell.getTitle()) .setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null) - .setSecondaryText(cell.getSecondaryText()) - .setTertiaryText(cell.getTertiaryText()) + .setSecondaryText(cell.getSecondaryText() != null && cell.getSecondaryText().isEmpty() ? null : cell.getSecondaryText()) + .setTertiaryText(cell.getTertiaryText() != null && cell.getTertiaryText().isEmpty() ? null : cell.getTertiaryText()) .setPosition(position) .setMenuLayout(submenuLayout) .setMenuIcon(icon) -- cgit v1.2.1 From 5f781a49987d1255d337cea7b72da182984f2017 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 30 Apr 2021 14:50:17 -0400 Subject: =?UTF-8?q?Don=E2=80=99t=20set=20secondary=20text=20and=20image=20?= =?UTF-8?q?if=20not=20supported=20(Fixes=20#1628)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../managers/screen/menu/MenuReplaceUtilities.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 74447ef99..f9df3787a 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -1,7 +1,6 @@ package com.smartdevicelink.managers.screen.menu; import com.smartdevicelink.managers.ISdl; -import com.smartdevicelink.managers.ManagerUtility; import com.smartdevicelink.managers.file.FileManager; import com.smartdevicelink.managers.file.filetypes.SdlArtwork; import com.smartdevicelink.proxy.RPCRequest; @@ -15,6 +14,7 @@ import com.smartdevicelink.proxy.rpc.MenuParams; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; +import com.smartdevicelink.proxy.rpc.enums.TextFieldName; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import java.util.ArrayList; @@ -22,6 +22,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; +import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; /** @@ -30,7 +32,7 @@ import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdN class MenuReplaceUtilities { static List findAllArtworksToBeUploadedFromCells(List cells, FileManager fileManager, WindowCapability windowCapability) { // Make sure we can use images in the menus - if (!ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { + if (!hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { return new ArrayList<>(); } @@ -40,7 +42,7 @@ class MenuReplaceUtilities { if (fileManager.fileNeedsUpload(cell.getIcon())) { artworks.add(cell.getIcon()); } - if (fileManager.fileNeedsUpload(cell.getSecondaryArtwork())) { + if (hasImageFieldOfName(windowCapability, ImageFieldName.menuCommandSecondaryImage) && fileManager.fileNeedsUpload(cell.getSecondaryArtwork())) { artworks.add(cell.getSecondaryArtwork()); } } @@ -57,7 +59,7 @@ class MenuReplaceUtilities { SdlArtwork artwork = isSecondaryImage ? cell.getSecondaryArtwork() : cell.getIcon(); ImageFieldName mainMenuImageFieldName = isSecondaryImage ? ImageFieldName.menuCommandSecondaryImage : ImageFieldName.cmdIcon; ImageFieldName subMenuImageFieldName = isSecondaryImage ? ImageFieldName.menuSubMenuSecondaryImage : ImageFieldName.subMenuIcon; - boolean supportsImage = cell.getSubCells() != null && !cell.getSubCells().isEmpty() ? ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, subMenuImageFieldName) : ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(windowCapability, mainMenuImageFieldName); + boolean supportsImage = cell.getSubCells() != null && !cell.getSubCells().isEmpty() ? hasImageFieldOfName(windowCapability, subMenuImageFieldName) : hasImageFieldOfName(windowCapability, mainMenuImageFieldName); if (artwork == null || !supportsImage) { return false; } @@ -154,8 +156,8 @@ class MenuReplaceUtilities { AddCommand command = new AddCommand(cell.getCellId()); MenuParams params = new MenuParams(cell.getTitle()); - params.setSecondaryText(cell.getSecondaryText() != null && cell.getSecondaryText().isEmpty() ? null : cell.getSecondaryText()); - params.setTertiaryText(cell.getTertiaryText() != null && cell.getTertiaryText().isEmpty() ? null : cell.getTertiaryText()); + params.setSecondaryText((cell.getSecondaryText() != null && !cell.getSecondaryText().isEmpty() && hasTextFieldOfName(windowCapability, TextFieldName.menuCommandSecondaryText)) ? cell.getSecondaryText() : null); + params.setTertiaryText((cell.getTertiaryText() != null && !cell.getTertiaryText().isEmpty() && hasTextFieldOfName(windowCapability, TextFieldName.menuCommandTertiaryText)) ? cell.getTertiaryText() : null); params.setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null); params.setPosition(position); @@ -190,8 +192,8 @@ class MenuReplaceUtilities { return new AddSubMenu(cell.getCellId(), cell.getTitle()) .setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null) - .setSecondaryText(cell.getSecondaryText() != null && cell.getSecondaryText().isEmpty() ? null : cell.getSecondaryText()) - .setTertiaryText(cell.getTertiaryText() != null && cell.getTertiaryText().isEmpty() ? null : cell.getTertiaryText()) + .setSecondaryText((cell.getSecondaryText() != null && !cell.getSecondaryText().isEmpty() && hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuSecondaryText)) ? cell.getSecondaryText() : null) + .setTertiaryText((cell.getTertiaryText() != null && !cell.getTertiaryText().isEmpty() && hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuTertiaryText)) ? cell.getTertiaryText() : null) .setPosition(position) .setMenuLayout(submenuLayout) .setMenuIcon(icon) -- cgit v1.2.1 From 76938f02467fdc84b217969c1ecfb6583f88fc4b Mon Sep 17 00:00:00 2001 From: RHeniz Date: Fri, 9 Jul 2021 11:29:24 -0400 Subject: fix back-to-back choiceset issue --- .../screen/choiceset/ChoiceSetManagerTests.java | 1 - .../screen/choiceset/BaseChoiceSetManager.java | 52 +++++++++------------- .../choiceset/PresentChoiceSetOperation.java | 4 ++ 3 files changed, 26 insertions(+), 31 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java index 81f912361..d06922d69 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java @@ -123,7 +123,6 @@ public class ChoiceSetManagerTests { assertNull(csm.currentHMILevel); assertNull(csm.currentSystemContext); assertNull(csm.defaultMainWindowCapability); - assertNull(csm.pendingPresentationSet); assertNull(csm.pendingPresentOperation); assertEquals(csm.transactionQueue.getTasksAsList().size(), 0); 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 893909718..7c5b120e7 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 @@ -95,7 +95,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { String displayName; SystemContext currentSystemContext; HashSet preloadedChoices, pendingPreloadChoices; - ChoiceSet pendingPresentationSet; // We will pass operations into this to be completed final Queue transactionQueue; @@ -151,7 +150,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { currentSystemContext = null; defaultMainWindowCapability = null; - pendingPresentationSet = null; pendingPresentOperation = null; isVROptional = false; nextChoiceId = choiceCellIdMin; @@ -263,19 +261,20 @@ abstract class BaseChoiceSetManager extends BaseSubManager { final HashSet cellsToBeDeleted = choicesToBeDeletedWithArray(choices); HashSet cellsToBeRemovedFromPending = choicesToBeRemovedFromPendingWithArray(choices); // If choices are deleted that are already uploaded or pending and are used by a pending presentation, cancel it and send an error - HashSet pendingPresentationChoices = new HashSet<>(); - if (pendingPresentationSet != null && pendingPresentationSet.getChoices() != null) { - pendingPresentationChoices.addAll(pendingPresentationSet.getChoices()); - } + for (Task task: transactionQueue.getTasksAsList()) { + if (task instanceof PresentChoiceSetOperation && task.getState() != Task.CANCELED && task.getState()!= Task.FINISHED) { + PresentChoiceSetOperation presentOperation = (PresentChoiceSetOperation) task; + HashSet presentOpChoicesSet = new HashSet<>(presentOperation.getChoiceSet().getChoices()); + if (presentOpChoicesSet.equals(cellsToBeDeleted) || presentOpChoicesSet.equals(cellsToBeRemovedFromPending)) { + presentOperation.cancelTask(); + if (presentOperation.getChoiceSet().canceledListener != null) { + presentOperation.getChoiceSet().canceledListener.onChoiceSetCanceled(); + } + } + } - if (pendingPresentOperation != null && pendingPresentOperation.getState() != Task.CANCELED && pendingPresentOperation.getState() != Task.FINISHED && (cellsToBeDeleted.retainAll(pendingPresentationChoices) || cellsToBeRemovedFromPending.retainAll(pendingPresentationChoices))) { - pendingPresentOperation.cancelTask(); - DebugTool.logWarning(TAG, "Attempting to delete choice cells while there is a pending presentation operation. Pending presentation cancelled."); - pendingPresentOperation = null; } - // Remove cells from pending and delete choices - pendingPresentationChoices.removeAll(cellsToBeRemovedFromPending); for (Task operation : transactionQueue.getTasksAsList()) { if (!(operation instanceof PreloadChoicesOperation)) { continue; @@ -322,45 +321,39 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return; } - if (this.pendingPresentationSet != null && pendingPresentOperation != null) { - pendingPresentOperation.cancelTask(); - DebugTool.logWarning(TAG, "Presenting a choice set while one is currently presented. Cancelling previous and continuing"); - } - - this.pendingPresentationSet = choiceSet; - preloadChoices(this.pendingPresentationSet.getChoices(), new CompletionListener() { + preloadChoices(choiceSet.getChoices(), new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { choiceSet.getChoiceSetSelectionListener().onError("There was an error pre-loading choice set choices"); } else { - sendPresentOperation(keyboardListener, mode); + sendPresentOperation(choiceSet, keyboardListener, mode); } } }); } - private void sendPresentOperation(KeyboardListener keyboardListener, InteractionMode mode) { + private void sendPresentOperation(final ChoiceSet choiceSet, KeyboardListener keyboardListener, InteractionMode mode) { if (mode == null) { mode = InteractionMode.MANUAL_ONLY; } - findIdsOnChoiceSet(pendingPresentationSet); + findIdsOnChoiceSet(choiceSet); // Pass back the information to the developer ChoiceSetSelectionListener privateChoiceListener = new ChoiceSetSelectionListener() { @Override public void onChoiceSelected(ChoiceCell choiceCell, TriggerSource triggerSource, int rowIndex) { - if (pendingPresentationSet != null && pendingPresentationSet.getChoiceSetSelectionListener() != null) { - pendingPresentationSet.getChoiceSetSelectionListener().onChoiceSelected(choiceCell, triggerSource, rowIndex); + if (choiceSet != null && choiceSet.getChoiceSetSelectionListener() != null) { + choiceSet.getChoiceSetSelectionListener().onChoiceSelected(choiceCell, triggerSource, rowIndex); } } @Override public void onError(String error) { - if (pendingPresentationSet != null && pendingPresentationSet.getChoiceSetSelectionListener() != null) { - pendingPresentationSet.getChoiceSetSelectionListener().onError(error); + if (choiceSet != null && choiceSet.getChoiceSetSelectionListener() != null) { + choiceSet.getChoiceSetSelectionListener().onError(error); } } }; @@ -370,11 +363,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, getNextCancelId()); + presentOp = new PresentChoiceSetOperation(internalInterface, choiceSet, 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, getNextCancelId()); + presentOp = new PresentChoiceSetOperation(internalInterface, choiceSet, mode, keyboardConfiguration, keyboardListener, privateChoiceListener, getNextCancelId()); } transactionQueue.add(presentOp, false); @@ -400,9 +393,8 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return null; } - if (pendingPresentationSet != null && pendingPresentOperation != null) { + if (pendingPresentOperation != null) { pendingPresentOperation.cancelTask(); - pendingPresentationSet = null; DebugTool.logWarning(TAG, "There is a current or pending choice set, cancelling and continuing."); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java index b5d3945d1..a09209f5b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java @@ -300,6 +300,10 @@ class PresentChoiceSetOperation extends Task { return choiceIds; } + public ChoiceSet getChoiceSet() { + return this.choiceSet; + } + // HELPERS void setSelectedCellWithId(Integer cellId) { -- cgit v1.2.1 From 314d3bd52f3dc75a7635d45467b43bc2edbf5681 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 12 Jul 2021 14:59:15 -0400 Subject: Add missing return statements in MenuConfigurationUpdateOperation --- .../managers/screen/menu/MenuConfigurationUpdateOperation.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java index 2a4cb43d2..430509bea 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuConfigurationUpdateOperation.java @@ -110,9 +110,11 @@ class MenuConfigurationUpdateOperation extends Task { if (availableMenuLayouts == null) { DebugTool.logWarning(TAG, "Could not set the main menu configuration. Which menu layouts can be used is not available"); listener.onComplete(false); + return; } else if (!availableMenuLayouts.contains(updatedMenuConfiguration.getMenuLayout()) || !availableMenuLayouts.contains(updatedMenuConfiguration.getSubMenuLayout())) { DebugTool.logError(TAG, String.format("One or more of the set menu layouts are not available on this system. The menu configuration will not be set. Available menu layouts: %s, set menu layouts: %s", availableMenuLayouts, updatedMenuConfiguration)); listener.onComplete(false); + return; } SetGlobalProperties setGlobalProperties = new SetGlobalProperties(); -- cgit v1.2.1 From 85973d84ce9f49e8246f0ff9c28bfaddd4a91a7b Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 12 Jul 2021 15:23:47 -0400 Subject: Update comments --- .../java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 4ac606f19..da6b03ad7 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -273,7 +273,7 @@ abstract class BaseMenuManager extends BaseSubManager { } /** - * Opens a subMenu. The cell you pass in must be constructed with {@link MenuCell(String,SdlArtwork,List)} + * Opens a subMenu. * * @param cell - A SubMenu cell whose sub menu you wish to open */ -- cgit v1.2.1 From 7952a520ec36120dd2fae001ad0531c316449331 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 12 Jul 2021 15:43:52 -0400 Subject: Update comments --- .../java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index da6b03ad7..66a8d754a 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -284,7 +284,7 @@ abstract class BaseMenuManager extends BaseSubManager { /** * This method is called via the screen manager to set the menuConfiguration. - * This will be used when a menuCell with sub-cells has a null value for SubMenuLayout + * The menuConfiguration.SubMenuLayout value will be used when a menuCell with sub-cells has a null value for SubMenuLayout * * @param menuConfiguration - The default menuConfiguration */ @@ -438,7 +438,6 @@ abstract class BaseMenuManager extends BaseSubManager { return true; } return (!displayType.equals(DisplayType.GEN3_8_INCH.toString())); - } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_OFF)) { return false; } else if (updateMode.equals(DynamicMenuUpdatesMode.FORCE_ON)) { -- cgit v1.2.1 From d149fee8e2148a37dd1bfaab9f1f7ad20f2ee9cb Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 13 Jul 2021 13:57:59 -0400 Subject: Update shouldCellIncludeImage() --- .../managers/screen/menu/MenuReplaceUtilities.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index f9df3787a..a3834b577 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -55,14 +55,19 @@ class MenuReplaceUtilities { } static boolean shouldCellIncludeImage(MenuCell cell, FileManager fileManager, WindowCapability windowCapability, boolean isSecondaryImage) { - // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image SdlArtwork artwork = isSecondaryImage ? cell.getSecondaryArtwork() : cell.getIcon(); + if (artwork == null) { + return false; + } + ImageFieldName mainMenuImageFieldName = isSecondaryImage ? ImageFieldName.menuCommandSecondaryImage : ImageFieldName.cmdIcon; ImageFieldName subMenuImageFieldName = isSecondaryImage ? ImageFieldName.menuSubMenuSecondaryImage : ImageFieldName.subMenuIcon; boolean supportsImage = cell.getSubCells() != null && !cell.getSubCells().isEmpty() ? hasImageFieldOfName(windowCapability, subMenuImageFieldName) : hasImageFieldOfName(windowCapability, mainMenuImageFieldName); - if (artwork == null || !supportsImage) { + if (!supportsImage) { return false; } + + // If the icon has been uploaded, or if the icon is a static icon, the cell should include the image return (fileManager.hasUploadedFile(artwork) || artwork.isStaticIcon()); } -- cgit v1.2.1 From 523c70ee47b10d1911d3336f5e2ebf042bbddb82 Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 14 Jul 2021 15:03:08 -0400 Subject: Starting to rework deleteChoiceOperations --- .../choiceset/DeleteChoicesOperationTests.java | 2 +- .../screen/choiceset/BaseChoiceSetManager.java | 38 ++------------- .../choiceset/DeleteChoicesCompletionListener.java | 13 ++++++ .../screen/choiceset/DeleteChoicesOperation.java | 54 +++++++++++++++++++--- 4 files changed, 65 insertions(+), 42 deletions(-) create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesCompletionListener.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperationTests.java index 2b472eda8..fdb141b07 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperationTests.java @@ -66,7 +66,7 @@ public class DeleteChoicesOperationTests { cellsToDelete.add(cell2); ISdl internalInterface = mock(ISdl.class); - deleteChoicesOperation = new DeleteChoicesOperation(internalInterface, cellsToDelete, null); + deleteChoicesOperation = new DeleteChoicesOperation(internalInterface, cellsToDelete, null, 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 7c5b120e7..2a994230a 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 @@ -257,46 +257,14 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return; } - // Find cells to be deleted that are already uploaded or are pending upload - final HashSet cellsToBeDeleted = choicesToBeDeletedWithArray(choices); - HashSet cellsToBeRemovedFromPending = choicesToBeRemovedFromPendingWithArray(choices); - // If choices are deleted that are already uploaded or pending and are used by a pending presentation, cancel it and send an error - for (Task task: transactionQueue.getTasksAsList()) { - if (task instanceof PresentChoiceSetOperation && task.getState() != Task.CANCELED && task.getState()!= Task.FINISHED) { - PresentChoiceSetOperation presentOperation = (PresentChoiceSetOperation) task; - HashSet presentOpChoicesSet = new HashSet<>(presentOperation.getChoiceSet().getChoices()); - if (presentOpChoicesSet.equals(cellsToBeDeleted) || presentOpChoicesSet.equals(cellsToBeRemovedFromPending)) { - presentOperation.cancelTask(); - if (presentOperation.getChoiceSet().canceledListener != null) { - presentOperation.getChoiceSet().canceledListener.onChoiceSetCanceled(); - } - } - } - - } - - for (Task operation : transactionQueue.getTasksAsList()) { - if (!(operation instanceof PreloadChoicesOperation)) { - continue; - } - ((PreloadChoicesOperation) operation).removeChoicesFromUpload(cellsToBeRemovedFromPending); - } - - // Find Choices to delete - if (cellsToBeDeleted.size() == 0) { - DebugTool.logInfo(TAG, "Cells to be deleted size == 0"); - return; - } - findIdsOnChoices(cellsToBeDeleted); - - DeleteChoicesOperation deleteChoicesOperation = new DeleteChoicesOperation(internalInterface, cellsToBeDeleted, new CompletionListener() { + DeleteChoicesOperation deleteChoicesOperation = new DeleteChoicesOperation(internalInterface, new HashSet<>(preloadedChoices), preloadedChoices, new DeleteChoicesCompletionListener() { @Override - public void onComplete(boolean success) { + public void onComplete(boolean success, HashSet updatedLoadedChoiceCells) { if (!success) { DebugTool.logError(TAG, "Failed to delete choices"); return; } - preloadedChoices.removeAll(cellsToBeDeleted); + preloadedChoices = updatedLoadedChoiceCells; } }); transactionQueue.add(deleteChoicesOperation, false); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesCompletionListener.java new file mode 100644 index 000000000..346814fb4 --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesCompletionListener.java @@ -0,0 +1,13 @@ +package com.smartdevicelink.managers.screen.choiceset; + +import java.util.HashSet; + +public interface DeleteChoicesCompletionListener { + + /** + * Returns whether an DeleteChoices operation was successful or not along with which choice cells are loaded + * @param success - Boolean that is True if Operation was a success, False otherwise. + * @param loadedChoiceCells - A set of the ChoiceCells that are loaded + */ + void onComplete(boolean success, HashSet loadedChoiceCells); +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java index 53f209eae..8da2e21f6 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java @@ -51,19 +51,26 @@ import java.util.List; class DeleteChoicesOperation extends Task { private static final String TAG = "DeleteChoicesOperation"; private final WeakReference internalInterface; - private final HashSet cellsToDelete; - private final CompletionListener completionListener; + private HashSet cellsToDelete; + private final DeleteChoicesCompletionListener completionListener; + private HashSet loadedCells; + private boolean completionSuccess = false; - DeleteChoicesOperation(ISdl internalInterface, HashSet cellsToDelete, CompletionListener completionListener) { + DeleteChoicesOperation(ISdl internalInterface, HashSet cellsToDelete, HashSet loadedCells, DeleteChoicesCompletionListener completionListener) { super("DeleteChoicesOperation"); this.internalInterface = new WeakReference<>(internalInterface); this.cellsToDelete = cellsToDelete; this.completionListener = completionListener; + this.loadedCells = loadedCells == null ? new HashSet() : new HashSet<>(loadedCells); } @Override public void onExecute() { DebugTool.logInfo(TAG, "Choice Operation: Executing delete choices operation"); + updateCellsToDelete(); + if (this.cellsToDelete == null || this.cellsToDelete.isEmpty()) { + DeleteChoicesOperation.super.onFinished(); + } sendDeletions(); } @@ -82,7 +89,7 @@ class DeleteChoicesOperation extends Task { @Override public void onFinished() { if (completionListener != null) { - completionListener.onComplete(true); + completionSuccess = true; } DebugTool.logInfo(TAG, "Successfully deleted choices"); @@ -93,23 +100,58 @@ class DeleteChoicesOperation extends Task { public void onResponse(int correlationId, RPCResponse response) { if (!response.getSuccess()) { if (completionListener != null) { - completionListener.onComplete(false); + completionSuccess = false; } DebugTool.logError(TAG, "Failed to delete choice: " + response.getInfo() + " | Corr ID: " + correlationId); DeleteChoicesOperation.super.onFinished(); + } else { + if (loadedCells != null) { + loadedCells.remove(cellFromChoiceId(correlationId)); + } } } }); } } else { if (completionListener != null) { - completionListener.onComplete(true); + completionListener.onComplete(true, this.loadedCells); } DebugTool.logInfo(TAG, "No Choices to delete, continue"); } } + public void setLoadedCells(HashSet loadedCells) { + this.loadedCells = new HashSet<>(loadedCells); + } + + public HashSet getLoadedCells() { + return new HashSet<>(this.loadedCells); + } + + private void updateCellsToDelete() { + HashSet updatedCellsToDelete = new HashSet<>(this.cellsToDelete); + updatedCellsToDelete.retainAll(loadedCells); + + this.cellsToDelete = updatedCellsToDelete; + } + + private ChoiceCell cellFromChoiceId(int choiceId) { + for (ChoiceCell cell : this.loadedCells) { + if (cell.getChoiceId() == choiceId) { + return cell; + } + } + + return null; + } + + @Override + protected void onFinished() { + this.completionListener.onComplete(completionSuccess, this.loadedCells); + super.onFinished(); + } + List createDeleteSets() { List deleteChoices = new ArrayList<>(cellsToDelete.size()); for (ChoiceCell cell : cellsToDelete) { -- cgit v1.2.1 From 6bb1b41b4d54914438999724928fe917b4b5ddce Mon Sep 17 00:00:00 2001 From: RHeniz Date: Thu, 15 Jul 2021 16:13:05 -0400 Subject: Update present and delete operations --- .../choiceset/PresentChoiceSetOperationTests.java | 18 ++++++++-------- .../screen/choiceset/BaseChoiceSetManager.java | 4 ++-- .../screen/choiceset/DeleteChoicesOperation.java | 8 ++++++++ .../choiceset/PresentChoiceSetOperation.java | 24 +++++++++++++++++++++- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java index 3d22f352b..a942c6de1 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java @@ -113,7 +113,7 @@ public class PresentChoiceSetOperationTests { @Test public void testGetLayoutMode() { // First we will check knowing our keyboard listener is NOT NULL - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER); + presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); assertEquals(presentChoiceSetOperation.getLayoutMode(), LayoutMode.LIST_WITH_SEARCH); presentChoiceSetOperation.keyboardListener = null; @@ -122,7 +122,7 @@ public class PresentChoiceSetOperationTests { @Test public void testGetPerformInteraction() { - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER); + presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); PerformInteraction pi = presentChoiceSetOperation.getPerformInteraction(); assertEquals(pi.getInitialText(), "Test"); @@ -136,7 +136,7 @@ public class PresentChoiceSetOperationTests { @Test public void testSetSelectedCellWithId() { - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER); + presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); assertNull(presentChoiceSetOperation.selectedCellRow); presentChoiceSetOperation.setSelectedCellWithId(0); @@ -154,7 +154,7 @@ public class PresentChoiceSetOperationTests { @Test public void testCancelingChoiceSetSuccessfullyIfThreadIsRunning() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER); + presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); queue.add(presentChoiceSetOperation, false); sleep(); @@ -189,7 +189,7 @@ public class PresentChoiceSetOperationTests { @Test public void testCancelingChoiceSetUnsuccessfullyIfThreadIsRunning() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER); + presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); queue.add(presentChoiceSetOperation, false); sleep(); @@ -223,7 +223,7 @@ public class PresentChoiceSetOperationTests { @Test public void testCancelingChoiceSetIfThreadHasFinished() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER); + presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER,null); presentChoiceSetOperation.finishOperation(); assertEquals(Task.FINISHED, presentChoiceSetOperation.getState()); @@ -237,7 +237,7 @@ public class PresentChoiceSetOperationTests { @Test public void testCancelingChoiceSetIfThreadHasNotYetRun() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER); + presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); assertEquals(Task.BLOCKED, presentChoiceSetOperation.getState()); @@ -258,7 +258,7 @@ public class PresentChoiceSetOperationTests { public void testCancelingChoiceSetIfHeadUnitDoesNotSupportFeature() { // Cancel Interaction is only supported on RPC specs v.6.0.0+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3)); - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER); + presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); queue.add(presentChoiceSetOperation, false); sleep(); @@ -274,7 +274,7 @@ public class PresentChoiceSetOperationTests { public void testCancelingChoiceSetIfHeadUnitDoesNotSupportFeatureButThreadIsNotRunning() { // Cancel Interaction is only supported on RPC specs v.6.0.0+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3)); - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER); + presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); assertEquals(Task.BLOCKED, presentChoiceSetOperation.getState()); 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 2a994230a..1f0f425f9 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 @@ -331,11 +331,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, choiceSet, mode, null, null, privateChoiceListener, getNextCancelId()); + presentOp = new PresentChoiceSetOperation(internalInterface, choiceSet, mode, null, null, privateChoiceListener, getNextCancelId(), this.preloadedChoices); } else { // Searchable choice set DebugTool.logInfo(TAG, "Creating searchable choice set"); - presentOp = new PresentChoiceSetOperation(internalInterface, choiceSet, mode, keyboardConfiguration, keyboardListener, privateChoiceListener, getNextCancelId()); + presentOp = new PresentChoiceSetOperation(internalInterface, choiceSet, mode, keyboardConfiguration, keyboardListener, privateChoiceListener, getNextCancelId(), this.preloadedChoices); } transactionQueue.add(presentOp, false); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java index 8da2e21f6..47e6a960b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java @@ -133,6 +133,14 @@ class DeleteChoicesOperation extends Task { HashSet updatedCellsToDelete = new HashSet<>(this.cellsToDelete); updatedCellsToDelete.retainAll(loadedCells); + for (ChoiceCell cell : updatedCellsToDelete) { + for (ChoiceCell loadedCell : this.loadedCells) { + if (loadedCell.equals(cell)) { + cell.setChoiceId(loadedCell.getChoiceId()); + } + } + } + this.cellsToDelete = updatedCellsToDelete; } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java index a09209f5b..2f84734d5 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java @@ -59,6 +59,7 @@ import com.smartdevicelink.util.DebugTool; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; class PresentChoiceSetOperation extends Task { @@ -77,9 +78,10 @@ class PresentChoiceSetOperation extends Task { Integer selectedCellRow; KeyboardListener keyboardListener; final SdlMsgVersion sdlMsgVersion; + private HashSet loadedCells; PresentChoiceSetOperation(ISdl internalInterface, ChoiceSet choiceSet, InteractionMode mode, - KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, ChoiceSetSelectionListener choiceSetSelectionListener, Integer cancelID) { + KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, ChoiceSetSelectionListener choiceSetSelectionListener, Integer cancelID, HashSet loadedCells) { super("PresentChoiceSetOperation"); this.internalInterface = new WeakReference<>(internalInterface); this.keyboardListener = keyboardListener; @@ -97,6 +99,7 @@ class PresentChoiceSetOperation extends Task { this.selectedCellRow = null; this.choiceSetSelectionListener = choiceSetSelectionListener; this.sdlMsgVersion = internalInterface.getSdlMsgVersion(); + this.loadedCells = loadedCells; } @Override @@ -112,6 +115,15 @@ class PresentChoiceSetOperation extends Task { return; } + HashSet choiceSetCells = new HashSet<>(this.choiceSet.getChoices()); + if (!choiceSetCells.containsAll(this.loadedCells)) { + this.choiceSetSelectionListener.onError("Choices not available for presentation"); + finishOperation(); + return; + } + + updateChoiceSetChoicesId(); + // Check if we're using a keyboard (searchable) choice set and setup keyboard properties if we need to if (keyboardListener != null && choiceSet.getCustomKeyboardConfiguration() != null) { keyboardProperties = choiceSet.getCustomKeyboardConfiguration(); @@ -306,6 +318,16 @@ class PresentChoiceSetOperation extends Task { // HELPERS + void updateChoiceSetChoicesId() { + for (ChoiceCell cell : this.choiceSet.getChoices()) { + for (ChoiceCell loadedCell : this.loadedCells) { + if (loadedCell.equals(cell)) { + cell.setChoiceId(loadedCell.getChoiceId()); + } + } + } + } + void setSelectedCellWithId(Integer cellId) { if (choiceSet.getChoices() != null && cellId != null) { List cells = choiceSet.getChoices(); -- cgit v1.2.1 From 147c7e69ee22cb5ef64f54d3335a60ebc193b4e4 Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 16 Jul 2021 08:17:47 -0400 Subject: Refactor --- .../managers/screen/choiceset/DeleteChoicesOperation.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java index 47e6a960b..80058cb69 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java @@ -36,7 +36,6 @@ package com.smartdevicelink.managers.screen.choiceset; import com.livio.taskmaster.Task; -import com.smartdevicelink.managers.CompletionListener; import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.DeleteInteractionChoiceSet; @@ -107,7 +106,7 @@ class DeleteChoicesOperation extends Task { DeleteChoicesOperation.super.onFinished(); } else { if (loadedCells != null) { - loadedCells.remove(cellFromChoiceId(correlationId)); + loadedCells.remove(loadedCellFromChoiceId(correlationId)); } } } @@ -144,7 +143,7 @@ class DeleteChoicesOperation extends Task { this.cellsToDelete = updatedCellsToDelete; } - private ChoiceCell cellFromChoiceId(int choiceId) { + private ChoiceCell loadedCellFromChoiceId(int choiceId) { for (ChoiceCell cell : this.loadedCells) { if (cell.getChoiceId() == choiceId) { return cell; -- cgit v1.2.1 From b4365f08f8118ec62f9fd88dd58f3ce3b7b0af6f Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 16 Jul 2021 15:43:29 -0400 Subject: Updates to ChoiceSet Manager and Operations --- .../screen/choiceset/ChoiceSetManagerTests.java | 129 -------------- .../choiceset/PreloadChoicesOperationTests.java | 6 +- .../screen/choiceset/BaseChoiceSetManager.java | 191 +++++---------------- .../PreloadChoicesCompletionListener.java | 13 ++ .../screen/choiceset/PreloadChoicesOperation.java | 32 +++- .../choiceset/PresentChoiceSetOperation.java | 8 + 6 files changed, 92 insertions(+), 287 deletions(-) create mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesCompletionListener.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java index d06922d69..cf1b71c38 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java @@ -108,11 +108,9 @@ public class ChoiceSetManagerTests { assertFalse(csm.isVROptional); assertNotNull(csm.fileManager); assertNotNull(csm.preloadedChoices); - assertNotNull(csm.pendingPreloadChoices); assertNotNull(csm.transactionQueue); assertNotNull(csm.hmiListener); assertNotNull(csm.onDisplayCapabilityListener); - assertNull(csm.pendingPresentOperation); } @After @@ -123,7 +121,6 @@ public class ChoiceSetManagerTests { assertNull(csm.currentHMILevel); assertNull(csm.currentSystemContext); assertNull(csm.defaultMainWindowCapability); - assertNull(csm.pendingPresentOperation); assertEquals(csm.transactionQueue.getTasksAsList().size(), 0); assertEquals(csm.nextChoiceId, 1); @@ -227,85 +224,6 @@ public class ChoiceSetManagerTests { assertNotSame(cell3.getChoiceId(), 2000000000); } - @Test - public void testAddUniqueNamesToCells() { - ChoiceCell cell1 = new ChoiceCell("McDonalds", "1 mile away", null, null, null, null); - ChoiceCell cell2 = new ChoiceCell("McDonalds", "2 mile away", null, null, null, null); - ChoiceCell cell3 = new ChoiceCell("Starbucks", "3 mile away", null, null, null, null); - ChoiceCell cell4 = new ChoiceCell("McDonalds", "4 mile away", null, null, null, null); - ChoiceCell cell5 = new ChoiceCell("Starbucks", "5 mile away", null, null, null, null); - ChoiceCell cell6 = new ChoiceCell("Meijer", "6 mile away", null, null, null, null); - List cellList = new ArrayList<>(); - - cellList.add(cell1); - cellList.add(cell2); - cellList.add(cell3); - cellList.add(cell4); - cellList.add(cell5); - cellList.add(cell6); - - csm.addUniqueNamesToCells(cellList); - - assertEquals(cell1.getUniqueText(), "McDonalds"); - assertEquals(cell2.getUniqueText(), "McDonalds (2)"); - assertEquals(cell3.getUniqueText(), "Starbucks"); - assertEquals(cell4.getUniqueText(), "McDonalds (3)"); - assertEquals(cell5.getUniqueText(), "Starbucks (2)"); - assertEquals(cell6.getUniqueText(), "Meijer"); - } - - @Test - public void testChoicesToBeRemovedFromPendingWithArray() { - - ChoiceCell cell1 = new ChoiceCell("test"); - ChoiceCell cell2 = new ChoiceCell("test2"); - ChoiceCell cell3 = new ChoiceCell("test3"); - - HashSet pendingPreloadSet = new HashSet<>(); - pendingPreloadSet.add(cell1); - pendingPreloadSet.add(cell2); - pendingPreloadSet.add(cell3); - - csm.pendingPreloadChoices.clear(); - csm.pendingPreloadChoices = pendingPreloadSet; - - List choices = new ArrayList<>(); - choices.add(cell2); - - HashSet returnedChoices = csm.choicesToBeRemovedFromPendingWithArray(choices); - - assertEquals(returnedChoices.size(), 1); - for (ChoiceCell cell : returnedChoices) { - assertEquals(cell.getText(), "test2"); - } - } - - @Test - public void testChoicesToBeUploadedWithArray() { - - ChoiceCell cell1 = new ChoiceCell("test"); - ChoiceCell cell2 = new ChoiceCell("test2"); - ChoiceCell cell3 = new ChoiceCell("test3"); - - HashSet pendingDeleteSet = new HashSet<>(); - pendingDeleteSet.add(cell1); - pendingDeleteSet.add(cell2); - pendingDeleteSet.add(cell3); - - csm.preloadedChoices.clear(); - csm.preloadedChoices = pendingDeleteSet; - - List choices = new ArrayList<>(); - choices.add(cell2); - - HashSet returnedChoices = csm.choicesToBeDeletedWithArray(choices); - - assertEquals(returnedChoices.size(), 1); - for (ChoiceCell cell : returnedChoices) { - assertEquals(cell.getText(), "test2"); - } - } - @Test public void testPresentingKeyboardShouldReturnCancelIDIfKeyboardCanBeSent() { ISdl internalInterface = mock(ISdl.class); @@ -473,51 +391,4 @@ public class ChoiceSetManagerTests { verify(testKeyboardOp, times(0)).dismissKeyboard(); verify(testKeyboardOp2, times(1)).dismissKeyboard(); } - - @Test - public void testUniquenessForAvailableFields() { - WindowCapability windowCapability = new WindowCapability(); - TextField secondaryText = new TextField(); - secondaryText.setName(TextFieldName.secondaryText); - TextField tertiaryText = new TextField(); - tertiaryText.setName(TextFieldName.tertiaryText); - - List textFields = new ArrayList<>(); - textFields.add(secondaryText); - textFields.add(tertiaryText); - windowCapability.setTextFields(textFields); - - ImageField choiceImage = new ImageField(); - choiceImage.setName(ImageFieldName.choiceImage); - ImageField choiceSecondaryImage = new ImageField(); - choiceSecondaryImage.setName(ImageFieldName.choiceSecondaryImage); - List imageFieldList = new ArrayList<>(); - imageFieldList.add(choiceImage); - imageFieldList.add(choiceSecondaryImage); - windowCapability.setImageFields(imageFieldList); - - csm.defaultMainWindowCapability = windowCapability; - - ChoiceCell cell1 = new ChoiceCell("Item 1", "null", "tertiaryText", null, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_ARTWORK); - ChoiceCell cell2 = new ChoiceCell("Item 1", "null2", "tertiaryText2", null, null, null); - List choiceCellList = new ArrayList<>(); - choiceCellList.add(cell1); - choiceCellList.add(cell2); - - List removedProperties = csm.removeUnusedProperties(choiceCellList); - assertNotNull(removedProperties.get(0).getSecondaryText()); - - textFields.remove(secondaryText); - textFields.remove(tertiaryText); - imageFieldList.remove(choiceImage); - imageFieldList.remove(choiceSecondaryImage); - - removedProperties = csm.removeUnusedProperties(choiceCellList); - csm.addUniqueNamesBasedOnStrippedCells(removedProperties, choiceCellList); - assertEquals(choiceCellList.get(1).getUniqueText(), "Item 1 (2)"); - - - } - - } diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java index 9e879a73a..1730d6585 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java @@ -109,7 +109,7 @@ public class PreloadChoicesOperationTests { // We still want the mock fileManager to use the real implementation for fileNeedsUpload() when(fileManager.fileNeedsUpload(any(SdlFile.class))).thenCallRealMethod(); - preloadChoicesOperation = new PreloadChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null); + preloadChoicesOperation = new PreloadChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null, null); } /** @@ -125,7 +125,7 @@ public class PreloadChoicesOperationTests { ISdl internalInterface = mock(ISdl.class); FileManager fileManager = mock(FileManager.class); - preloadChoicesOperationNullCapability = new PreloadChoicesOperation(internalInterface, fileManager, null, null, true, cellsToPreload, null); + preloadChoicesOperationNullCapability = new PreloadChoicesOperation(internalInterface, fileManager, null, null, true, cellsToPreload, null, null); } /** @@ -152,7 +152,7 @@ public class PreloadChoicesOperationTests { ISdl internalInterface = mock(ISdl.class); FileManager fileManager = mock(FileManager.class); - preloadChoicesOperationEmptyCapability = new PreloadChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null); + preloadChoicesOperationEmptyCapability = new PreloadChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null, null); } @Test 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 1f0f425f9..adfdce339 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 @@ -94,11 +94,10 @@ abstract class BaseChoiceSetManager extends BaseSubManager { WindowCapability defaultMainWindowCapability; String displayName; SystemContext currentSystemContext; - HashSet preloadedChoices, pendingPreloadChoices; + HashSet preloadedChoices; // We will pass operations into this to be completed final Queue transactionQueue; - Task pendingPresentOperation; PresentKeyboardOperation currentlyPresentedKeyboardOperation; @@ -125,7 +124,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { // setting/instantiating class vars this.fileManager = new WeakReference<>(fileManager); preloadedChoices = new HashSet<>(); - pendingPreloadChoices = new HashSet<>(); nextChoiceId = choiceCellIdMin; nextCancelId = choiceCellCancelIdMin; isVROptional = false; @@ -150,7 +148,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { currentSystemContext = null; defaultMainWindowCapability = null; - pendingPresentOperation = null; + preloadedChoices = null; isVROptional = false; nextChoiceId = choiceCellIdMin; nextCancelId = choiceCellCancelIdMin; @@ -201,12 +199,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return; } - LinkedHashSet mutableChoicesToUpload = getChoicesToBeUploadedWithArray(choices); - - mutableChoicesToUpload.removeAll(preloadedChoices); - mutableChoicesToUpload.removeAll(pendingPreloadChoices); - - final LinkedHashSet choicesToUpload = (LinkedHashSet) mutableChoicesToUpload.clone(); + final LinkedHashSet choicesToUpload = (LinkedHashSet) choices; if (choicesToUpload.size() == 0) { if (listener != null) { @@ -217,27 +210,20 @@ abstract class BaseChoiceSetManager extends BaseSubManager { updateIdsOnChoices(choicesToUpload); - // Add the preload cells to the pending preload choices - pendingPreloadChoices.addAll(choicesToUpload); - if (fileManager.get() != null) { - PreloadChoicesOperation preloadChoicesOperation = new PreloadChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, new CompletionListener() { + PreloadChoicesOperation preloadChoicesOperation = new PreloadChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, new PreloadChoicesCompletionListener() { @Override - public void onComplete(boolean success) { - if (success) { - preloadedChoices.addAll(choicesToUpload); - pendingPreloadChoices.removeAll(choicesToUpload); - if (listener != null) { - listener.onComplete(true); - } - } else { - DebugTool.logError(TAG, "There was an error pre loading choice cells"); - if (listener != null) { - listener.onComplete(false); + public void onComplete(boolean success, HashSet loadedCells) { + preloadedChoices = loadedCells; + updatePendingTasksWithCurrentPreloads(); + if (listener != null) { + if (!success) { + DebugTool.logError(TAG, "There was an error pre loading choice cells"); } + listener.onComplete(success); } } - }); + }, this.preloadedChoices); transactionQueue.add(preloadChoicesOperation, false); } else { @@ -265,6 +251,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return; } preloadedChoices = updatedLoadedChoiceCells; + updatePendingTasksWithCurrentPreloads(); } }); transactionQueue.add(deleteChoicesOperation, false); @@ -289,6 +276,8 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return; } + preloadChoices(choiceSet.getChoices(), null); + preloadChoices(choiceSet.getChoices(), new CompletionListener() { @Override public void onComplete(boolean success) { @@ -307,8 +296,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { mode = InteractionMode.MANUAL_ONLY; } - findIdsOnChoiceSet(choiceSet); - // Pass back the information to the developer ChoiceSetSelectionListener privateChoiceListener = new ChoiceSetSelectionListener() { @Override @@ -321,6 +308,13 @@ abstract class BaseChoiceSetManager extends BaseSubManager { @Override public void onError(String error) { if (choiceSet != null && choiceSet.getChoiceSetSelectionListener() != null) { + if (error != null) { + choiceSet.getChoiceSetSelectionListener().onError(error); + } else if (getState() == SHUTDOWN) { + choiceSet.getChoiceSetSelectionListener().onError("Incorrect State"); + } else { + DebugTool.logError(TAG, "Present finished but an unhandled state occurred and callback failed"); + } choiceSet.getChoiceSetSelectionListener().onError(error); } } @@ -339,7 +333,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { } transactionQueue.add(presentOp, false); - pendingPresentOperation = presentOp; } /** @@ -361,11 +354,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return null; } - if (pendingPresentOperation != null) { - pendingPresentOperation.cancelTask(); - DebugTool.logWarning(TAG, "There is a current or pending choice set, cancelling and continuing."); - } - customKeyboardConfig = createValidKeyboardConfigurationBasedOnKeyboardCapabilitiesFromConfiguration(customKeyboardConfig); if (customKeyboardConfig == null) { if (this.keyboardConfiguration != null) { @@ -381,7 +369,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { PresentKeyboardOperation keyboardOp = new PresentKeyboardOperation(internalInterface, keyboardConfiguration, initialText, customKeyboardConfig, listener, keyboardCancelID); currentlyPresentedKeyboardOperation = keyboardOp; transactionQueue.add(keyboardOp, false); - pendingPresentOperation = keyboardOp; return keyboardCancelID; } @@ -487,63 +474,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { // CHOICE SET MANAGEMENT HELPERS - HashSet choicesToBeDeletedWithArray(List choices) { - HashSet choicesSet = new HashSet<>(choices); - choicesSet.retainAll(this.preloadedChoices); - return choicesSet; - } - - HashSet choicesToBeRemovedFromPendingWithArray(List choices) { - HashSet choicesSet = new HashSet<>(choices); - choicesSet.retainAll(this.pendingPreloadChoices); - return choicesSet; - } - - /** - * Checks if 2 or more cells have the same text/title. In case this condition is true, this function will handle the presented issue by adding "(count)". - * E.g. Choices param contains 2 cells with text/title "Address" will be handled by updating the uniqueText/uniqueTitle of the second cell to "Address (2)". - * @param choices The list of choiceCells to be uploaded. - */ - void addUniqueNamesToCells(List choices) { - HashMap dictCounter = new HashMap<>(); - - for (ChoiceCell cell : choices) { - String cellName = cell.getText(); - Integer counter = dictCounter.get(cellName); - - if (counter != null) { - dictCounter.put(cellName, ++counter); - cell.setUniqueText(cell.getText() + " (" + counter + ")"); - } else { - dictCounter.put(cellName, 1); - } - } - } - - void addUniqueNamesBasedOnStrippedCells(List strippedCells, List unstrippedCells) { - if (strippedCells == null || unstrippedCells == null || strippedCells.size() != unstrippedCells.size()) { - return; - } - // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary - HashMap dictCounter = new HashMap<>(); - for (int i = 0; i < strippedCells.size(); i++) { - ChoiceCell cell = strippedCells.get(i); - Integer counter = dictCounter.get(cell); - if (counter != null) { - counter++; - dictCounter.put(cell, counter); - } else { - dictCounter.put(cell, 1); - } - - counter = dictCounter.get(cell); - - if (counter > 1) { - unstrippedCells.get(i).setUniqueText(unstrippedCells.get(i).getText() + " (" + counter + ")"); - } - } - } - private List cloneChoiceCellList(List originalList) { if (originalList == null) { return null; @@ -556,46 +486,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return clone; } - private LinkedHashSet getChoicesToBeUploadedWithArray(List choices) { - // Clone choices - List choicesClone = cloneChoiceCellList(choices); - if (choices != null && internalInterface.getSdlMsgVersion() != null - && (internalInterface.getSdlMsgVersion().getMajorVersion() < 7 - || (internalInterface.getSdlMsgVersion().getMajorVersion() == 7 && internalInterface.getSdlMsgVersion().getMinorVersion() == 0))) { - // If we're on < RPC 7.1, all primary texts need to be unique, so we don't need to check removed properties and duplicate cells - addUniqueNamesToCells(choicesClone); - } else { - List strippedCellsClone = removeUnusedProperties(choicesClone); - addUniqueNamesBasedOnStrippedCells(strippedCellsClone, choicesClone); - } - LinkedHashSet choiceCloneLinkedHash = new LinkedHashSet<>(choicesClone); - choiceCloneLinkedHash.removeAll(preloadedChoices); - return choiceCloneLinkedHash; - } - - List removeUnusedProperties(List choiceCells) { - List strippedCellsClone = cloneChoiceCellList(choiceCells); - //Clone Cells - for (ChoiceCell cell : strippedCellsClone) { - // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI - cell.setVoiceCommands(null); - - if (!hasImageFieldOfName(ImageFieldName.choiceImage)) { - cell.setArtwork(null); - } - if (!hasTextFieldOfName(TextFieldName.secondaryText)) { - cell.setSecondaryText(null); - } - if (!hasTextFieldOfName(TextFieldName.tertiaryText)) { - cell.setTertiaryText(null); - } - if (!hasImageFieldOfName(ImageFieldName.choiceSecondaryImage)) { - cell.setSecondaryArtwork(null); - } - } - return strippedCellsClone; - } - void updateIdsOnChoices(LinkedHashSet choices) { for (ChoiceCell cell : choices) { cell.setChoiceId(this.nextChoiceId); @@ -603,24 +493,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { } } - private void findIdsOnChoiceSet(ChoiceSet choiceSet) { - findIdsOnChoices(new HashSet<>(choiceSet.getChoices())); - } - - private void findIdsOnChoices(HashSet choices) { - for (ChoiceCell cell : choices) { - ChoiceCell uploadCell = null; - if (pendingPreloadChoices.contains(cell)) { - uploadCell = findIfPresent(cell, pendingPreloadChoices); - } else if (preloadedChoices.contains(cell)) { - uploadCell = findIfPresent(cell, preloadedChoices); - } - if (uploadCell != null) { - cell.setChoiceId(uploadCell.getChoiceId()); - } - } - } - ChoiceCell findIfPresent(ChoiceCell cell, HashSet set) { if (set.contains(cell)) { for (ChoiceCell setCell : set) { @@ -631,6 +503,25 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return null; } + private void updatePendingTasksWithCurrentPreloads() { + for (Task task : this.transactionQueue.getTasksAsList()) { + if (task.getState() == Task.IN_PROGRESS || task.getState() == Task.CANCELED) { + continue; + } + + if (task instanceof PreloadChoicesOperation) { + PreloadChoicesOperation preloadOp = (PreloadChoicesOperation) task; + preloadOp.setLoadedCells(this.preloadedChoices); + } else if (task instanceof PresentChoiceSetOperation) { + PresentChoiceSetOperation presentOp = (PresentChoiceSetOperation) task; + presentOp.setLoadedCells(this.preloadedChoices); + } else if (task instanceof DeleteChoicesOperation) { + DeleteChoicesOperation deleteOp = (DeleteChoicesOperation) task; + deleteOp.setLoadedCells(this.preloadedChoices); + } + } + } + // LISTENERS private void addListeners() { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesCompletionListener.java new file mode 100644 index 000000000..5a34dc9dc --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesCompletionListener.java @@ -0,0 +1,13 @@ +package com.smartdevicelink.managers.screen.choiceset; + +import java.util.HashSet; + +public interface PreloadChoicesCompletionListener { + + /** + * Returns whether an preload choices operation was successful or not along with which choice cells are loaded + * @param success - Boolean that is True if Operation was a success, False otherwise. + * @param loadedChoiceCells - A set of the ChoiceCells that are loaded + */ + void onComplete(boolean success, HashSet loadedChoiceCells); +} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java index 8f47a2880..5a4d14957 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java @@ -70,13 +70,14 @@ class PreloadChoicesOperation extends Task { private final WindowCapability defaultMainWindowCapability; private final String displayName; private final HashSet cellsToUpload; - private final CompletionListener completionListener; + private final PreloadChoicesCompletionListener completionListener; private boolean isRunning; private final boolean isVROptional; private boolean choiceError = false; + private HashSet loadedCells; PreloadChoicesOperation(ISdl internalInterface, FileManager fileManager, String displayName, WindowCapability defaultMainWindowCapability, - Boolean isVROptional, LinkedHashSet cellsToPreload, CompletionListener listener) { + Boolean isVROptional, LinkedHashSet cellsToPreload, PreloadChoicesCompletionListener listener, HashSet loadedCells) { super("PreloadChoicesOperation"); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); @@ -85,11 +86,13 @@ class PreloadChoicesOperation extends Task { this.isVROptional = isVROptional; this.cellsToUpload = cellsToPreload; this.completionListener = listener; + this.loadedCells = loadedCells; } @Override public void onExecute() { DebugTool.logInfo(TAG, "Choice Operation: Executing preload choices operation"); + this.cellsToUpload.removeAll(this.loadedCells); preloadCellArtworks(new CompletionListener() { @Override public void onComplete(boolean success) { @@ -151,7 +154,7 @@ class PreloadChoicesOperation extends Task { if (choiceRPCs.size() == 0) { DebugTool.logError(TAG, " All Choice cells to send are null, so the choice set will not be shown"); - completionListener.onComplete(true); + completionListener.onComplete(true, loadedCells); isRunning = false; return; } @@ -167,7 +170,7 @@ class PreloadChoicesOperation extends Task { public void onFinished() { isRunning = false; DebugTool.logInfo(TAG, "Finished pre loading choice cells"); - completionListener.onComplete(!choiceError); + completionListener.onComplete(!choiceError, loadedCells); choiceError = false; PreloadChoicesOperation.super.onFinished(); } @@ -177,14 +180,25 @@ class PreloadChoicesOperation extends Task { if (!response.getSuccess()) { DebugTool.logError(TAG, "There was an error uploading a choice cell: " + response.getInfo() + " resultCode: " + response.getResultCode()); choiceError = true; + } else { + loadedCells.add(cellFromChoiceId(correlationId)); } } }); } else { DebugTool.logError(TAG, "Internal Interface null in preload choice operation"); isRunning = false; - completionListener.onComplete(false); + completionListener.onComplete(false, loadedCells); + } + } + + private ChoiceCell cellFromChoiceId(int choiceId) { + for (ChoiceCell cell : this.cellsToUpload) { + if (cell.getChoiceId() == choiceId) { + return cell; + } } + return null; } private CreateInteractionChoiceSet choiceFromCell(ChoiceCell cell) { @@ -259,6 +273,14 @@ class PreloadChoicesOperation extends Task { return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, name); } + public void setLoadedCells(HashSet loadedCells) { + this.loadedCells = loadedCells; + } + + public HashSet getLoadedCells() { + return this.loadedCells; + } + List artworksToUpload() { List artworksToUpload = new ArrayList<>(); for (ChoiceCell cell : cellsToUpload) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java index 2f84734d5..564da2012 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java @@ -316,6 +316,14 @@ class PresentChoiceSetOperation extends Task { return this.choiceSet; } + public void setLoadedCells(HashSet loadedCells) { + this.loadedCells = loadedCells; + } + + public HashSet getLoadedCells() { + return this.loadedCells; + } + // HELPERS void updateChoiceSetChoicesId() { -- cgit v1.2.1 From f851102346739230d49ad1f52fa053f2cf5284a8 Mon Sep 17 00:00:00 2001 From: Henigan Date: Mon, 19 Jul 2021 09:21:14 -0400 Subject: Add uniqueness updated to preloadChoicesOp --- .../screen/choiceset/PreloadChoicesOperation.java | 106 ++++++++++++++++++++- 1 file changed, 103 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java index 5a4d14957..64397a553 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java @@ -54,10 +54,12 @@ import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.TextFieldName; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.util.DebugTool; +import com.smartdevicelink.util.Version; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; @@ -69,7 +71,7 @@ class PreloadChoicesOperation extends Task { private final WeakReference fileManager; private final WindowCapability defaultMainWindowCapability; private final String displayName; - private final HashSet cellsToUpload; + private final ArrayList cellsToUpload; private final PreloadChoicesCompletionListener completionListener; private boolean isRunning; private final boolean isVROptional; @@ -84,7 +86,7 @@ class PreloadChoicesOperation extends Task { this.displayName = displayName; this.defaultMainWindowCapability = defaultMainWindowCapability; this.isVROptional = isVROptional; - this.cellsToUpload = cellsToPreload; + this.cellsToUpload = new ArrayList<>(cellsToPreload); this.completionListener = listener; this.loadedCells = loadedCells; } @@ -92,7 +94,7 @@ class PreloadChoicesOperation extends Task { @Override public void onExecute() { DebugTool.logInfo(TAG, "Choice Operation: Executing preload choices operation"); - this.cellsToUpload.removeAll(this.loadedCells); + updateCellsBasedOnLoadedChoices(); preloadCellArtworks(new CompletionListener() { @Override public void onComplete(boolean success) { @@ -293,4 +295,102 @@ class PreloadChoicesOperation extends Task { } return artworksToUpload; } + + void updateCellsBasedOnLoadedChoices() { + if (internalInterface.get().getProtocolVersion().getMajor() >= 7 && internalInterface.get().getProtocolVersion().getMinor() >= 1) { + addUniqueNamesToCells(cellsToUpload); + } else { + ArrayList strippedCellsCopy = (ArrayList) removeUnusedProperties(cellsToUpload); + addUniqueNamesBasedOnStrippedCells(strippedCellsCopy, cellsToUpload); + } + cellsToUpload.removeAll(loadedCells); + } + + List removeUnusedProperties(List choiceCells) { + List strippedCellsClone = cloneChoiceCellList(choiceCells); + //Clone Cells + for (ChoiceCell cell : strippedCellsClone) { + // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI + cell.setVoiceCommands(null); + + if (!hasImageFieldOfName(ImageFieldName.choiceImage)) { + cell.setArtwork(null); + } + if (!hasTextFieldOfName(TextFieldName.secondaryText)) { + cell.setSecondaryText(null); + } + if (!hasTextFieldOfName(TextFieldName.tertiaryText)) { + cell.setTertiaryText(null); + } + if (!hasImageFieldOfName(ImageFieldName.choiceSecondaryImage)) { + cell.setSecondaryArtwork(null); + } + } + return strippedCellsClone; + } + + private List cloneChoiceCellList(List originalList) { + if (originalList == null) { + return null; + } + List clone = new ArrayList<>(); + for (ChoiceCell choiceCell : originalList) { + clone.add(choiceCell.clone()); + } + return clone; + } + + private boolean hasImageFieldOfName(ImageFieldName imageFieldName) { + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, imageFieldName); + } + + private boolean hasTextFieldOfName(TextFieldName textFieldName) { + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, textFieldName); + } + + /** + * Checks if 2 or more cells have the same text/title. In case this condition is true, this function will handle the presented issue by adding "(count)". + * E.g. Choices param contains 2 cells with text/title "Address" will be handled by updating the uniqueText/uniqueTitle of the second cell to "Address (2)". + * @param choices The list of choiceCells to be uploaded. + */ + void addUniqueNamesToCells(List choices) { + HashMap dictCounter = new HashMap<>(); + + for (ChoiceCell cell : choices) { + String cellName = cell.getText(); + Integer counter = dictCounter.get(cellName); + + if (counter != null) { + dictCounter.put(cellName, ++counter); + cell.setUniqueText(cell.getText() + " (" + counter + ")"); + } else { + dictCounter.put(cellName, 1); + } + } + } + + void addUniqueNamesBasedOnStrippedCells(List strippedCells, List unstrippedCells) { + if (strippedCells == null || unstrippedCells == null || strippedCells.size() != unstrippedCells.size()) { + return; + } + // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary + HashMap dictCounter = new HashMap<>(); + for (int i = 0; i < strippedCells.size(); i++) { + ChoiceCell cell = strippedCells.get(i); + Integer counter = dictCounter.get(cell); + if (counter != null) { + counter++; + dictCounter.put(cell, counter); + } else { + dictCounter.put(cell, 1); + } + + counter = dictCounter.get(cell); + + if (counter > 1) { + unstrippedCells.get(i).setUniqueText(unstrippedCells.get(i).getText() + " (" + counter + ")"); + } + } + } + } \ No newline at end of file -- cgit v1.2.1 From 580edf98690f65682f8b67263fecfbf3a5e9958e Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 19 Jul 2021 11:28:00 -0400 Subject: Fix issue in MenuCell.hashCode() --- .../main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index 7e67f9ec4..c81c5b5ae 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -447,8 +447,8 @@ public class MenuCell implements Cloneable { int result = 1; result += ((getTitle() == null) ? 0 : Integer.rotateLeft(getTitle().hashCode(), 1)); result += ((getIcon() == null) ? 0 : Integer.rotateLeft(getIcon().hashCode(), 2)); - result += ((getVoiceCommands() == null) ? 0 : Integer.rotateLeft(getVoiceCommands().hashCode(), 3)); - result += ((getSubCells() == null) ? 0 : Integer.rotateLeft(getSubCells().hashCode(), 4)); + result += ((getVoiceCommands() == null || getVoiceCommands().isEmpty()) ? 0 : Integer.rotateLeft(getVoiceCommands().hashCode(), 3)); + result += ((getSubCells() == null || getSubCells().isEmpty()) ? 0 : Integer.rotateLeft(getSubCells().hashCode(), 4)); result += ((getSecondaryText() == null) ? 0 : Integer.rotateLeft(getSecondaryText().hashCode(), 1)); result += ((getTertiaryText() == null) ? 0 : Integer.rotateLeft(getTertiaryText().hashCode(), 1)); result += ((getSecondaryArtwork() == null) ? 0 : Integer.rotateLeft(getSecondaryArtwork().hashCode(), 2)); -- cgit v1.2.1 From a140e49566eace9e247003edbe285c3ddd045eda Mon Sep 17 00:00:00 2001 From: Robert Henigan Date: Tue, 20 Jul 2021 13:07:25 -0400 Subject: Notify ChoiceSetManager of failed preloads (#1716) * Notify ChoiceSetManager of failed preloads * Move Completion Listener to the BaseCSManager Co-authored-by: Henigan --- .../screen/choiceset/BaseChoiceSetManager.java | 15 ++++++++++++-- .../screen/choiceset/PreloadChoicesOperation.java | 24 ++++++++++++++++------ 2 files changed, 31 insertions(+), 8 deletions(-) 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 893909718..4a60fa2a4 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 @@ -223,9 +223,9 @@ abstract class BaseChoiceSetManager extends BaseSubManager { pendingPreloadChoices.addAll(choicesToUpload); if (fileManager.get() != null) { - PreloadChoicesOperation preloadChoicesOperation = new PreloadChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, new CompletionListener() { + PreloadChoicesOperation preloadChoicesOperation = new PreloadChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, new BaseChoiceSetManager.ChoiceSetCompletionListener() { @Override - public void onComplete(boolean success) { + public void onComplete(boolean success, HashSet failedChoiceCells) { if (success) { preloadedChoices.addAll(choicesToUpload); pendingPreloadChoices.removeAll(choicesToUpload); @@ -234,6 +234,9 @@ abstract class BaseChoiceSetManager extends BaseSubManager { } } else { DebugTool.logError(TAG, "There was an error pre loading choice cells"); + choicesToUpload.removeAll(failedChoiceCells); + preloadedChoices.addAll(choicesToUpload); + pendingPreloadChoices.removeAll(choicesToUpload); if (listener != null) { listener.onComplete(false); } @@ -671,6 +674,14 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return null; } + /** + * Interface that sends a list of ChoiceCells that failed to preload, to allow BaseChoioceSetManager + * to stop keeping track of them for their onButtonEventListener + */ + interface ChoiceSetCompletionListener { + void onComplete(boolean success, HashSet failedChoiceCells); + } + // LISTENERS private void addListeners() { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java index 8f47a2880..d98ae3517 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java @@ -70,13 +70,13 @@ class PreloadChoicesOperation extends Task { private final WindowCapability defaultMainWindowCapability; private final String displayName; private final HashSet cellsToUpload; - private final CompletionListener completionListener; + private final BaseChoiceSetManager.ChoiceSetCompletionListener completionListener; private boolean isRunning; private final boolean isVROptional; private boolean choiceError = false; PreloadChoicesOperation(ISdl internalInterface, FileManager fileManager, String displayName, WindowCapability defaultMainWindowCapability, - Boolean isVROptional, LinkedHashSet cellsToPreload, CompletionListener listener) { + Boolean isVROptional, LinkedHashSet cellsToPreload, BaseChoiceSetManager.ChoiceSetCompletionListener listener) { super("PreloadChoicesOperation"); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); @@ -141,7 +141,7 @@ class PreloadChoicesOperation extends Task { private void preloadCells() { isRunning = true; - List choiceRPCs = new ArrayList<>(cellsToUpload.size()); + final List choiceRPCs = new ArrayList<>(cellsToUpload.size()); for (ChoiceCell cell : cellsToUpload) { CreateInteractionChoiceSet csCell = choiceFromCell(cell); if (csCell != null) { @@ -151,13 +151,15 @@ class PreloadChoicesOperation extends Task { if (choiceRPCs.size() == 0) { DebugTool.logError(TAG, " All Choice cells to send are null, so the choice set will not be shown"); - completionListener.onComplete(true); + completionListener.onComplete(true, null); isRunning = false; return; } if (internalInterface.get() != null) { internalInterface.get().sendRPCs(choiceRPCs, new OnMultipleRequestListener() { + final HashSet failedChoiceCells = new HashSet<>(); + @Override public void onUpdate(int remainingRequests) { @@ -167,7 +169,7 @@ class PreloadChoicesOperation extends Task { public void onFinished() { isRunning = false; DebugTool.logInfo(TAG, "Finished pre loading choice cells"); - completionListener.onComplete(!choiceError); + completionListener.onComplete(!choiceError, !choiceError ? null : failedChoiceCells); choiceError = false; PreloadChoicesOperation.super.onFinished(); } @@ -177,13 +179,23 @@ class PreloadChoicesOperation extends Task { if (!response.getSuccess()) { DebugTool.logError(TAG, "There was an error uploading a choice cell: " + response.getInfo() + " resultCode: " + response.getResultCode()); choiceError = true; + for (CreateInteractionChoiceSet choiceSet : choiceRPCs) { + if (choiceSet.getCorrelationID() == correlationId) { + int choiceId = choiceSet.getChoiceSet().get(0).getChoiceID(); + for (ChoiceCell cell : cellsToUpload) { + if (cell.getChoiceId() == choiceId) { + failedChoiceCells.add(cell); + } + } + } + } } } }); } else { DebugTool.logError(TAG, "Internal Interface null in preload choice operation"); isRunning = false; - completionListener.onComplete(false); + completionListener.onComplete(false, cellsToUpload); } } -- cgit v1.2.1 From 6c4adfec912e7446d34cf58ac49749caa124d5a0 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 20 Jul 2021 16:30:05 -0400 Subject: Fix issue in MenuCell.hashCode() --- .../main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index c81c5b5ae..dbbd3f1a2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -448,7 +448,7 @@ public class MenuCell implements Cloneable { result += ((getTitle() == null) ? 0 : Integer.rotateLeft(getTitle().hashCode(), 1)); result += ((getIcon() == null) ? 0 : Integer.rotateLeft(getIcon().hashCode(), 2)); result += ((getVoiceCommands() == null || getVoiceCommands().isEmpty()) ? 0 : Integer.rotateLeft(getVoiceCommands().hashCode(), 3)); - result += ((getSubCells() == null || getSubCells().isEmpty()) ? 0 : Integer.rotateLeft(getSubCells().hashCode(), 4)); + result += ((getSubCells() == null) ? 0 : Integer.rotateLeft(getSubCells().hashCode(), 4)); result += ((getSecondaryText() == null) ? 0 : Integer.rotateLeft(getSecondaryText().hashCode(), 1)); result += ((getTertiaryText() == null) ? 0 : Integer.rotateLeft(getTertiaryText().hashCode(), 1)); result += ((getSecondaryArtwork() == null) ? 0 : Integer.rotateLeft(getSecondaryArtwork().hashCode(), 2)); -- cgit v1.2.1 From 37ceba1cfe8afffc9674c1b6e39cf80a4f14c2dc Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 20 Jul 2021 16:54:53 -0400 Subject: Update comments --- .../com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index a93a1b01a..db8e30bb3 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -220,7 +220,10 @@ class MenuReplaceOperation extends Task { MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; + // RPCs for cells on the main menu level. They could be AddCommands or AddSubMenus depending on whether the cell has child cells or not. final List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); + + // RPCs for cells on the second menu level (one level deep). They could be AddCommands or AddSubMenus. final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); sendRPCs(mainMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { -- cgit v1.2.1 From 931b9d7b8cc409681a539abe67dab77f08a36c13 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 20 Jul 2021 17:01:35 -0400 Subject: Treat cells with empty sub cells as AddSubMenus --- .../managers/screen/menu/MenuReplaceUtilities.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index a3834b577..54e299155 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -118,7 +118,7 @@ class MenuReplaceUtilities { for (int updateCellsIndex = 0; updateCellsIndex < cells.size(); updateCellsIndex++) { MenuCell addCell = cells.get(updateCellsIndex); if (mainCell.equals(addCell)) { - if (addCell.getSubCells() != null && !addCell.getSubCells().isEmpty()) { + if (isSubMenuCell(addCell)) { commands.add(subMenuCommandForMenuCell(addCell, fileManager, windowCapability, menuInteger, defaultSubmenuLayout)); } else { commands.add(commandForMenuCell(addCell, fileManager, windowCapability, menuInteger)); @@ -133,7 +133,7 @@ class MenuReplaceUtilities { static List subMenuCommandsForCells(List cells, FileManager fileManager, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { List commands = new ArrayList<>(); for (MenuCell cell : cells) { - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { commands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); } } @@ -145,11 +145,13 @@ class MenuReplaceUtilities { for (int cellIndex = 0; cellIndex < cells.size(); cellIndex++) { MenuCell cell = cells.get(cellIndex); - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + if (isSubMenuCell(cell)) { commands.add(subMenuCommandForMenuCell(cell, fileManager, windowCapability, cellIndex, defaultSubmenuLayout)); // Recursively grab the commands for all the sub cells - commands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); + if (!cell.getSubCells().isEmpty()) { + commands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); + } } else { commands.add(commandForMenuCell(cell, fileManager, windowCapability, cellIndex)); } @@ -287,6 +289,10 @@ class MenuReplaceUtilities { } } + private static boolean isSubMenuCell(MenuCell menuCell) { + return menuCell.getSubCells() != null; + } + static void sendRPCs(final List requests, ISdl internalInterface, final SendingRPCsCompletionListener listener) { final Map errors = new HashMap<>(); if (requests == null || requests.isEmpty()) { @@ -320,5 +326,4 @@ class MenuReplaceUtilities { } }); } - } \ No newline at end of file -- cgit v1.2.1 From 07d6c0672f7e6bb689be34b65ddfed81d0a2770b Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 21 Jul 2021 15:43:14 -0400 Subject: Treat cells with empty sub cells as AddSubMenus part2 --- .../managers/screen/menu/BaseMenuManager.java | 8 +++++--- .../managers/screen/menu/MenuReplaceOperation.java | 3 ++- .../managers/screen/menu/MenuReplaceUtilities.java | 16 ++++++++-------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 66a8d754a..29a08d91f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -62,6 +62,8 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.isSubMenuCell; + abstract class BaseMenuManager extends BaseSubManager { private static final String TAG = "BaseMenuManager"; static final int menuCellIdMin = 1; @@ -239,7 +241,7 @@ abstract class BaseMenuManager extends BaseSubManager { } } - if (cell != null && (cell.getSubCells() == null || cell.getSubCells().isEmpty())) { + if (cell != null && (!isSubMenuCell(cell))) { DebugTool.logError(DebugTool.TAG, String.format("The cell %s does not contain any sub cells, so no submenu can be opened", cell.getTitle())); return false; } else if (cell != null && foundClonedCell == null) { @@ -399,7 +401,7 @@ abstract class BaseMenuManager extends BaseSubManager { cell.getMenuSelectionListener().onTriggered(command.getTriggerSource()); return true; } - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { // for each cell, if it has sub cells, recursively loop through those as well return callListenerForCells(cell.getSubCells(), command); } @@ -453,7 +455,7 @@ abstract class BaseMenuManager extends BaseSubManager { if (parentId != parentIdNotFound) { cell.setParentCellId(parentId); } - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index db8e30bb3..da6ed7288 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -24,6 +24,7 @@ import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addM import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandIdForRPCRequest; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.isSubMenuCell; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.positionForRPCRequest; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.removeMenuCellFromList; @@ -292,7 +293,7 @@ class MenuReplaceOperation extends Task { return; } - if (oldKeptCells.get(startIndex) != null && oldKeptCells.get(startIndex).getSubCells() != null && !oldKeptCells.get(startIndex).getSubCells().isEmpty()) { + if (oldKeptCells.get(startIndex) != null && isSubMenuCell(oldKeptCells.get(startIndex)) && !oldKeptCells.get(startIndex).getSubCells().isEmpty()) { DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); // If both old and new menu cells are empty. Then nothing needs to be done. diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 54e299155..ec715c904 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -46,7 +46,7 @@ class MenuReplaceUtilities { artworks.add(cell.getSecondaryArtwork()); } } - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { artworks.addAll(findAllArtworksToBeUploadedFromCells(cell.getSubCells(), fileManager, windowCapability)); } } @@ -62,7 +62,7 @@ class MenuReplaceUtilities { ImageFieldName mainMenuImageFieldName = isSecondaryImage ? ImageFieldName.menuCommandSecondaryImage : ImageFieldName.cmdIcon; ImageFieldName subMenuImageFieldName = isSecondaryImage ? ImageFieldName.menuSubMenuSecondaryImage : ImageFieldName.subMenuIcon; - boolean supportsImage = cell.getSubCells() != null && !cell.getSubCells().isEmpty() ? hasImageFieldOfName(windowCapability, subMenuImageFieldName) : hasImageFieldOfName(windowCapability, mainMenuImageFieldName); + boolean supportsImage = isSubMenuCell(cell) ? hasImageFieldOfName(windowCapability, subMenuImageFieldName) : hasImageFieldOfName(windowCapability, mainMenuImageFieldName); if (!supportsImage) { return false; } @@ -98,7 +98,7 @@ class MenuReplaceUtilities { static List deleteCommandsForCells(List cells) { List deletes = new ArrayList<>(); for (MenuCell cell : cells) { - if (cell.getSubCells() == null || cell.getSubCells().isEmpty()) { + if (!isSubMenuCell(cell)) { DeleteCommand delete = new DeleteCommand(cell.getCellId()); deletes.add(delete); } else { @@ -213,7 +213,7 @@ class MenuReplaceUtilities { // If the cell id matches the command id, remove it from the list and return menuCellList.remove(menuCell); return true; - } else if (menuCell.getSubCells() != null && !menuCell.getSubCells().isEmpty()) { + } else if (isSubMenuCell(menuCell) && !menuCell.getSubCells().isEmpty()) { // If the menu cell has sub cells, we need to recurse and check the sub cells List newList = menuCell.getSubCells(); boolean foundAndRemovedItem = removeMenuCellFromList(newList, commandId); @@ -232,7 +232,7 @@ class MenuReplaceUtilities { if (cell.getCellId() == commandId) { addedCell = cell; break; - } else if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { + } else if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { boolean success = addMenuRequestWithCommandId(commandId, position, cell.getSubCells(), mainMenuList); if (success) { return true; @@ -256,7 +256,7 @@ class MenuReplaceUtilities { // If we found the correct submenu, insert it into that submenu insertMenuCell(cell, menuCell.getSubCells(), position); return true; - } else if (menuCell.getSubCells() != null && !menuCell.getSubCells().isEmpty()) { + } else if (isSubMenuCell(menuCell) && !menuCell.getSubCells().isEmpty()) { // Check the sub cells of this cell to see if any of those have cell ids that match the parent cell id List newList = menuCell.getSubCells(); boolean foundAndAddedItem = addMenuCell(cell, newList, position); @@ -277,7 +277,7 @@ class MenuReplaceUtilities { private static void insertMenuCell(MenuCell cell, List cellList, int position) { MenuCell cellToInsert = cell; - if (cellToInsert.getSubCells() != null) { + if (isSubMenuCell(cellToInsert)) { // We should not add the subCells automatically when adding a parent cell cellToInsert = cell.clone(); cellToInsert.getSubCells().clear(); @@ -289,7 +289,7 @@ class MenuReplaceUtilities { } } - private static boolean isSubMenuCell(MenuCell menuCell) { + static boolean isSubMenuCell(MenuCell menuCell) { return menuCell.getSubCells() != null; } -- cgit v1.2.1 From 9d457e50c38ca622eb92ca272e19e5363c17d951 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 21 Jul 2021 16:33:14 -0400 Subject: Fix issue in MenuCell.hashCode() --- .../java/com/smartdevicelink/managers/screen/menu/MenuCell.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index dbbd3f1a2..0f6e532a2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -449,9 +449,10 @@ public class MenuCell implements Cloneable { result += ((getIcon() == null) ? 0 : Integer.rotateLeft(getIcon().hashCode(), 2)); result += ((getVoiceCommands() == null || getVoiceCommands().isEmpty()) ? 0 : Integer.rotateLeft(getVoiceCommands().hashCode(), 3)); result += ((getSubCells() == null) ? 0 : Integer.rotateLeft(getSubCells().hashCode(), 4)); - result += ((getSecondaryText() == null) ? 0 : Integer.rotateLeft(getSecondaryText().hashCode(), 1)); - result += ((getTertiaryText() == null) ? 0 : Integer.rotateLeft(getTertiaryText().hashCode(), 1)); - result += ((getSecondaryArtwork() == null) ? 0 : Integer.rotateLeft(getSecondaryArtwork().hashCode(), 2)); + result += ((getSecondaryText() == null) ? 0 : Integer.rotateLeft(getSecondaryText().hashCode(), 5)); + result += ((getTertiaryText() == null) ? 0 : Integer.rotateLeft(getTertiaryText().hashCode(), 6)); + result += ((getSecondaryArtwork() == null) ? 0 : Integer.rotateLeft(getSecondaryArtwork().hashCode(), 7)); + result += ((getMenuSelectionListener() == null) ? 0 : Integer.rotateLeft(getMenuSelectionListener().hashCode(), 8)); return result; } -- cgit v1.2.1 From 7ccb1679a9591700108c2e932b4c26d02e77f5d0 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 21 Jul 2021 17:09:49 -0400 Subject: Fix unit tests --- .../managers/screen/menu/MenuManagerTests.java | 58 ++-------------------- 1 file changed, 5 insertions(+), 53 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index e2736e8ba..858a4db50 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -99,6 +99,11 @@ public class MenuManagerTests { private MenuManager menuManager; private List cells; private MenuCell mainCell1, mainCell4; + private final MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); + private final MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); + private final MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); + private final MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); + private final MenuSelectionListener menuSelectionListenerE = mock(MenuSelectionListener.class); // SETUP / HELPERS @@ -630,12 +635,6 @@ public class MenuManagerTests { } private List createDynamicMenu1() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); @@ -649,13 +648,6 @@ public class MenuManagerTests { } private List createDynamicMenu1New() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerE = mock(MenuSelectionListener.class); - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); @@ -671,12 +663,6 @@ public class MenuManagerTests { } private List createDynamicMenu2() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); @@ -690,11 +676,6 @@ public class MenuManagerTests { } private List createDynamicMenu2New() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); @@ -706,11 +687,6 @@ public class MenuManagerTests { } private List createDynamicMenu3() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); @@ -738,12 +714,6 @@ public class MenuManagerTests { } private List createDynamicMenu4() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); @@ -757,12 +727,6 @@ public class MenuManagerTests { } private List createDynamicMenu4New() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); @@ -776,12 +740,6 @@ public class MenuManagerTests { } private List createDynamicMenu5() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); @@ -795,12 +753,6 @@ public class MenuManagerTests { } private List createDynamicMenu5New() { - - MenuSelectionListener menuSelectionListenerA = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerB = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerC = mock(MenuSelectionListener.class); - MenuSelectionListener menuSelectionListenerD = mock(MenuSelectionListener.class); - MenuCell A = new MenuCell("A", null, null, menuSelectionListenerA); MenuCell B = new MenuCell("B", null, null, menuSelectionListenerB); -- cgit v1.2.1 From 08ec2f076ac47c106b5ca0759e74a9244f5f5e6a Mon Sep 17 00:00:00 2001 From: RHeniz Date: Thu, 22 Jul 2021 16:17:21 -0400 Subject: update tests and remove old operations --- .../choiceset/PreloadChoicesOperationTests.java | 193 ---------- .../PreloadPresentChoicesOperationTests.java | 391 ++++++++++++++++++++ .../choiceset/PresentChoiceSetOperationTests.java | 295 --------------- .../screen/choiceset/PreloadChoicesOperation.java | 398 -------------------- .../choiceset/PresentChoiceSetOperation.java | 408 --------------------- 5 files changed, 391 insertions(+), 1294 deletions(-) delete mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java delete mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java delete mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java delete mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java deleted file mode 100644 index 1730d6585..000000000 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperationTests.java +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (c) 2019 Livio, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following - * disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * Neither the name of the Livio Inc. nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Created by brettywhite on 6/12/19 1:52 PM - * - */ - -package com.smartdevicelink.managers.screen.choiceset; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.smartdevicelink.managers.ISdl; -import com.smartdevicelink.managers.file.FileManager; -import com.smartdevicelink.managers.file.filetypes.SdlArtwork; -import com.smartdevicelink.managers.file.filetypes.SdlFile; -import com.smartdevicelink.proxy.rpc.ImageField; -import com.smartdevicelink.proxy.rpc.TextField; -import com.smartdevicelink.proxy.rpc.WindowCapability; -import com.smartdevicelink.proxy.rpc.enums.CharacterSet; -import com.smartdevicelink.proxy.rpc.enums.FileType; -import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; -import com.smartdevicelink.proxy.rpc.enums.ImageType; -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 java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; - -import static junit.framework.TestCase.assertEquals; -import static junit.framework.TestCase.assertFalse; -import static junit.framework.TestCase.assertNotNull; -import static junit.framework.TestCase.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -@RunWith(AndroidJUnit4.class) -public class PreloadChoicesOperationTests { - - private PreloadChoicesOperation preloadChoicesOperation; - private PreloadChoicesOperation preloadChoicesOperationNullCapability; - private PreloadChoicesOperation preloadChoicesOperationEmptyCapability; - - - @Before - public void setUp() throws Exception { - - ChoiceCell cell1 = new ChoiceCell("cell 1"); - ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK); - LinkedHashSet cellsToPreload = new LinkedHashSet<>(); - cellsToPreload.add(cell1); - cellsToPreload.add(cell2); - - ImageField imageField = new ImageField(ImageFieldName.choiceImage, Arrays.asList(FileType.GRAPHIC_PNG, FileType.GRAPHIC_JPEG)); - ImageField imageField2 = new ImageField(); - imageField2.setName(ImageFieldName.choiceSecondaryImage); - TextField textField = new TextField(TextFieldName.menuName, CharacterSet.CID1SET, 2, 2); - - TextField textField2 = new TextField(); - TextField textField3 = new TextField(); - - textField2.setName(TextFieldName.secondaryText); - textField3.setName(TextFieldName.tertiaryText); - - - WindowCapability windowCapability = new WindowCapability(); - windowCapability.setImageFields(Arrays.asList(imageField, imageField2)); - windowCapability.setImageTypeSupported(Arrays.asList(ImageType.STATIC, ImageType.DYNAMIC)); - windowCapability.setTextFields(Arrays.asList(textField, textField2, textField3)); - - ISdl internalInterface = mock(ISdl.class); - FileManager fileManager = mock(FileManager.class); - - // We still want the mock fileManager to use the real implementation for fileNeedsUpload() - when(fileManager.fileNeedsUpload(any(SdlFile.class))).thenCallRealMethod(); - - preloadChoicesOperation = new PreloadChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null, null); - } - - /** - * Sets up PreloadChoicesOperation with WindowCapability being null - */ - public void setUpNullWindowCapability() { - - ChoiceCell cell1 = new ChoiceCell("cell 1"); - ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK); - LinkedHashSet cellsToPreload = new LinkedHashSet<>(); - cellsToPreload.add(cell1); - cellsToPreload.add(cell2); - - ISdl internalInterface = mock(ISdl.class); - FileManager fileManager = mock(FileManager.class); - preloadChoicesOperationNullCapability = new PreloadChoicesOperation(internalInterface, fileManager, null, null, true, cellsToPreload, null, null); - } - - /** - * Sets up PreloadChoicesOperation with an Capability not being set - * certain imageFields and TextFields - */ - public void setUpEmptyWindowCapability() { - - ChoiceCell cell1 = new ChoiceCell("cell 1"); - ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK); - LinkedHashSet cellsToPreload = new LinkedHashSet<>(); - cellsToPreload.add(cell1); - cellsToPreload.add(cell2); - - ImageField imageField = new ImageField(); - imageField.setName(ImageFieldName.alertIcon); - - TextField textField = new TextField(); - textField.setName(TextFieldName.mainField1); - - WindowCapability windowCapability = new WindowCapability(); - windowCapability.setImageFields(Collections.singletonList(imageField)); - windowCapability.setTextFields(Collections.singletonList(textField)); - - ISdl internalInterface = mock(ISdl.class); - FileManager fileManager = mock(FileManager.class); - preloadChoicesOperationEmptyCapability = new PreloadChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null, null); - } - - @Test - public void testArtworksToUpload() { - List artworksToUpload = preloadChoicesOperation.artworksToUpload(); - assertNotNull(artworksToUpload); - assertEquals(artworksToUpload.size(), 1); - } - - /** - * Testing shouldSend method's with varying WindowCapability set. - */ - @Test - public void testShouldSendText() { - - setUpNullWindowCapability(); - assertTrue(preloadChoicesOperationNullCapability.shouldSendChoicePrimaryImage()); - assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceSecondaryImage()); - assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceSecondaryText()); - assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceTertiaryText()); - assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceText()); - - - assertTrue(preloadChoicesOperation.shouldSendChoicePrimaryImage()); - assertTrue(preloadChoicesOperation.shouldSendChoiceSecondaryImage()); - assertTrue(preloadChoicesOperation.shouldSendChoiceSecondaryText()); - assertTrue(preloadChoicesOperation.shouldSendChoiceTertiaryText()); - assertTrue(preloadChoicesOperation.shouldSendChoiceText()); - - setUpEmptyWindowCapability(); - assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoicePrimaryImage()); - assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceSecondaryImage()); - assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceSecondaryText()); - assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceTertiaryText()); - assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceText()); - } - -} diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java new file mode 100644 index 000000000..fbfeb3d6f --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java @@ -0,0 +1,391 @@ +package com.smartdevicelink.managers.screen.choiceset; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.livio.taskmaster.Queue; +import com.livio.taskmaster.Task; +import com.livio.taskmaster.Taskmaster; +import com.smartdevicelink.managers.ISdl; +import com.smartdevicelink.managers.file.FileManager; +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.CancelInteraction; +import com.smartdevicelink.proxy.rpc.ImageField; +import com.smartdevicelink.proxy.rpc.KeyboardProperties; +import com.smartdevicelink.proxy.rpc.PerformInteraction; +import com.smartdevicelink.proxy.rpc.SdlMsgVersion; +import com.smartdevicelink.proxy.rpc.TextField; +import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.CharacterSet; +import com.smartdevicelink.proxy.rpc.enums.FileType; +import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; +import com.smartdevicelink.proxy.rpc.enums.ImageType; +import com.smartdevicelink.proxy.rpc.enums.InteractionMode; +import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout; +import com.smartdevicelink.proxy.rpc.enums.KeypressMode; +import com.smartdevicelink.proxy.rpc.enums.Language; +import com.smartdevicelink.proxy.rpc.enums.LayoutMode; +import com.smartdevicelink.proxy.rpc.enums.TextFieldName; +import com.smartdevicelink.test.TestValues; +import com.smartdevicelink.util.Version; + +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.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; + +import static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertNotNull; +import static junit.framework.TestCase.assertNull; +import static junit.framework.TestCase.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(AndroidJUnit4.class) +public class PreloadPresentChoicesOperationTests { + + private PreloadPresentChoicesOperation preloadChoicesOperation; + private PreloadPresentChoicesOperation preloadChoicesOperationNullCapability; + private PreloadPresentChoicesOperation preloadChoicesOperationEmptyCapability; + + private PreloadPresentChoicesOperation presentChoicesOperation; + private ChoiceSet choiceSet; + private ISdl internalInterface; + private KeyboardListener keyboardListener; + private ChoiceSetSelectionListener choiceSetSelectionListener; + + private Taskmaster taskmaster; + private Queue queue; + + @Before + public void setUp() throws Exception { + ChoiceCell cell1 = new ChoiceCell("cell 1"); + ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK); + LinkedHashSet cellsToPreload = new LinkedHashSet<>(); + cellsToPreload.add(cell1); + cellsToPreload.add(cell2); + + ImageField imageField = new ImageField(ImageFieldName.choiceImage, Arrays.asList(FileType.GRAPHIC_PNG, FileType.GRAPHIC_JPEG)); + ImageField imageField2 = new ImageField(); + imageField2.setName(ImageFieldName.choiceSecondaryImage); + TextField textField = new TextField(TextFieldName.menuName, CharacterSet.CID1SET, 2, 2); + + TextField textField2 = new TextField(); + TextField textField3 = new TextField(); + + textField2.setName(TextFieldName.secondaryText); + textField3.setName(TextFieldName.tertiaryText); + + + WindowCapability windowCapability = new WindowCapability(); + windowCapability.setImageFields(Arrays.asList(imageField, imageField2)); + windowCapability.setImageTypeSupported(Arrays.asList(ImageType.STATIC, ImageType.DYNAMIC)); + windowCapability.setTextFields(Arrays.asList(textField, textField2, textField3)); + + internalInterface = mock(ISdl.class); + FileManager fileManager = mock(FileManager.class); + + // We still want the mock fileManager to use the real implementation for fileNeedsUpload() + when(fileManager.fileNeedsUpload(any(SdlFile.class))).thenCallRealMethod(); + + preloadChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null, null); + + keyboardListener = mock(KeyboardListener.class); + choiceSetSelectionListener = mock(ChoiceSetSelectionListener.class); + + ChoiceCell cell = new ChoiceCell("Cell1"); + cell.setChoiceId(0); + choiceSet = new ChoiceSet("Test", Collections.singletonList(cell), choiceSetSelectionListener); + + taskmaster = new Taskmaster.Builder().build(); + queue = taskmaster.createQueue("test", 100, false); + taskmaster.start(); + } + + private KeyboardProperties getKeyBoardProperties() { + KeyboardProperties properties = new KeyboardProperties(); + properties.setLanguage(Language.EN_US); + properties.setKeyboardLayout(KeyboardLayout.QWERTZ); + properties.setKeypressMode(KeypressMode.RESEND_CURRENT_ENTRY); + return properties; + } + + /** + * Sets up PreloadChoicesOperation with WindowCapability being null + */ + public void setUpNullWindowCapability() { + + ChoiceCell cell1 = new ChoiceCell("cell 1"); + ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK); + LinkedHashSet cellsToPreload = new LinkedHashSet<>(); + cellsToPreload.add(cell1); + cellsToPreload.add(cell2); + + ISdl internalInterface = mock(ISdl.class); + FileManager fileManager = mock(FileManager.class); + preloadChoicesOperationNullCapability = new PreloadPresentChoicesOperation(internalInterface, fileManager, null, null, true, cellsToPreload, null, null); + } + + /** + * Sets up PreloadChoicesOperation with an Capability not being set + * certain imageFields and TextFields + */ + public void setUpEmptyWindowCapability() { + + ChoiceCell cell1 = new ChoiceCell("cell 1"); + ChoiceCell cell2 = new ChoiceCell("cell 2", null, TestValues.GENERAL_ARTWORK); + LinkedHashSet cellsToPreload = new LinkedHashSet<>(); + cellsToPreload.add(cell1); + cellsToPreload.add(cell2); + + ImageField imageField = new ImageField(); + imageField.setName(ImageFieldName.alertIcon); + + TextField textField = new TextField(); + textField.setName(TextFieldName.mainField1); + + WindowCapability windowCapability = new WindowCapability(); + windowCapability.setImageFields(Collections.singletonList(imageField)); + windowCapability.setTextFields(Collections.singletonList(textField)); + + ISdl internalInterface = mock(ISdl.class); + FileManager fileManager = mock(FileManager.class); + preloadChoicesOperationEmptyCapability = new PreloadPresentChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null, null); + } + + @Test + public void testArtworksToUpload() { + List artworksToUpload = preloadChoicesOperation.artworksToUpload(); + assertNotNull(artworksToUpload); + assertEquals(artworksToUpload.size(), 1); + } + + /** + * Testing shouldSend method's with varying WindowCapability set. + */ + @Test + public void testShouldSendText() { + + setUpNullWindowCapability(); + assertTrue(preloadChoicesOperationNullCapability.shouldSendChoicePrimaryImage()); + assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceSecondaryImage()); + assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceSecondaryText()); + assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceTertiaryText()); + assertTrue(preloadChoicesOperationNullCapability.shouldSendChoiceText()); + + + assertTrue(preloadChoicesOperation.shouldSendChoicePrimaryImage()); + assertTrue(preloadChoicesOperation.shouldSendChoiceSecondaryImage()); + assertTrue(preloadChoicesOperation.shouldSendChoiceSecondaryText()); + assertTrue(preloadChoicesOperation.shouldSendChoiceTertiaryText()); + assertTrue(preloadChoicesOperation.shouldSendChoiceText()); + + setUpEmptyWindowCapability(); + assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoicePrimaryImage()); + assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceSecondaryImage()); + assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceSecondaryText()); + assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceTertiaryText()); + assertFalse(preloadChoicesOperationEmptyCapability.shouldSendChoiceText()); + } + + + @Test + public void testGetLayoutMode() { + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(7, 1)); + // First we will check knowing our keyboard listener is NOT NULL + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + + assertEquals(presentChoicesOperation.getLayoutMode(), LayoutMode.LIST_WITH_SEARCH); + presentChoicesOperation.keyboardListener = null; + assertEquals(presentChoicesOperation.getLayoutMode(), LayoutMode.LIST_ONLY); + } + + @Test + public void testGetPerformInteraction() { + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(7, 1)); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + + PerformInteraction pi = presentChoicesOperation.getPerformInteraction(); + assertEquals(pi.getInitialText(), "Test"); + assertNull(pi.getHelpPrompt()); + assertNull(pi.getTimeoutPrompt()); + assertNull(pi.getVrHelp()); + assertEquals(pi.getTimeout(), Integer.valueOf(10000)); + assertEquals(pi.getCancelID(), TestValues.GENERAL_INTEGER); + assertEquals(presentChoicesOperation.getLayoutMode(), LayoutMode.LIST_WITH_SEARCH); + } + + @Test + public void testSetSelectedCellWithId() { + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(7, 1)); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + + assertNull(presentChoicesOperation.selectedCellRow); + presentChoicesOperation.setSelectedCellWithId(0); + assertEquals(presentChoicesOperation.selectedCellRow, Integer.valueOf(0)); + } + + private void sleep() { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Test + public void testCancelingChoiceSetSuccessfullyIfThreadIsRunning() { + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + queue.add(presentChoicesOperation, false); + + sleep(); + + assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); + + choiceSet.cancel(); + Answer cancelInteractionAnswer = new Answer() { + @Override + public Void answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + CancelInteraction cancelInteraction = (CancelInteraction) args[0]; + + assertEquals(cancelInteraction.getCancelID(), TestValues.GENERAL_INTEGER); + assertEquals(cancelInteraction.getInteractionFunctionID().intValue(), FunctionID.PERFORM_INTERACTION.getId()); + + RPCResponse response = new RPCResponse(FunctionID.CANCEL_INTERACTION.toString()); + response.setSuccess(true); + cancelInteraction.getOnRPCResponseListener().onResponse(0, response); + + return null; + } + }; + doAnswer(cancelInteractionAnswer).when(internalInterface).sendRPC(any(CancelInteraction.class)); + + verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class)); + verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); + + assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); + } + + @Test + public void testCancelingChoiceSetUnsuccessfullyIfThreadIsRunning() { + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + queue.add(presentChoicesOperation, false); + sleep(); + + assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); + + choiceSet.cancel(); + Answer cancelInteractionAnswer = new Answer() { + @Override + public Void answer(InvocationOnMock invocation) { + Object[] args = invocation.getArguments(); + CancelInteraction cancelInteraction = (CancelInteraction) args[0]; + + assertEquals(cancelInteraction.getCancelID(), TestValues.GENERAL_INTEGER); + assertEquals(cancelInteraction.getInteractionFunctionID().intValue(), FunctionID.PERFORM_INTERACTION.getId()); + + RPCResponse response = new RPCResponse(FunctionID.CANCEL_INTERACTION.toString()); + response.setSuccess(false); + cancelInteraction.getOnRPCResponseListener().onResponse(0, response); + + return null; + } + }; + doAnswer(cancelInteractionAnswer).when(internalInterface).sendRPC(any(CancelInteraction.class)); + + verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class)); + verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); + + assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); + } + + @Test + public void testCancelingChoiceSetIfThreadHasFinished() { + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER,null); + presentChoicesOperation.finishOperation(); + + assertEquals(Task.FINISHED, presentChoicesOperation.getState()); + + choiceSet.cancel(); + verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); + + assertEquals(Task.FINISHED, presentChoicesOperation.getState()); + } + + @Test + public void testCancelingChoiceSetIfThreadHasNotYetRun() { + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + + assertEquals(Task.BLOCKED, presentChoicesOperation.getState()); + + choiceSet.cancel(); + + // Once the operation has started + queue.add(presentChoicesOperation, false); + sleep(); + + assertEquals(Task.CANCELED, presentChoicesOperation.getState()); + + // Make sure neither a `CancelInteraction` or `PerformInteraction` RPC is ever sent + verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); + verify(internalInterface, never()).sendRPC(any(PerformInteraction.class)); + } + + @Test + public void testCancelingChoiceSetIfHeadUnitDoesNotSupportFeature() { + // Cancel Interaction is only supported on RPC specs v.6.0.0+ + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3)); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + queue.add(presentChoicesOperation, false); + sleep(); + + assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); + + choiceSet.cancel(); + + verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); + verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); + } + + @Test + public void testCancelingChoiceSetIfHeadUnitDoesNotSupportFeatureButThreadIsNotRunning() { + // Cancel Interaction is only supported on RPC specs v.6.0.0+ + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3)); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + + assertEquals(Task.BLOCKED, presentChoicesOperation.getState()); + + choiceSet.cancel(); + + verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); + + // Once the operation has started + queue.add(presentChoicesOperation, false); + sleep(); + + assertEquals(Task.CANCELED, presentChoicesOperation.getState()); + + // Make sure neither a `CancelInteraction` or `PerformInteraction` RPC is ever sent + verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); + verify(internalInterface, never()).sendRPC(any(PerformInteraction.class)); + } +} diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java deleted file mode 100644 index a942c6de1..000000000 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperationTests.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 2019 Livio, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following - * disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * Neither the name of the Livio Inc. nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Created by brettywhite on 6/12/19 1:52 PM - * - */ - -package com.smartdevicelink.managers.screen.choiceset; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.livio.taskmaster.Queue; -import com.livio.taskmaster.Task; -import com.livio.taskmaster.Taskmaster; -import com.smartdevicelink.managers.ISdl; -import com.smartdevicelink.protocol.enums.FunctionID; -import com.smartdevicelink.proxy.RPCResponse; -import com.smartdevicelink.proxy.rpc.CancelInteraction; -import com.smartdevicelink.proxy.rpc.KeyboardProperties; -import com.smartdevicelink.proxy.rpc.PerformInteraction; -import com.smartdevicelink.proxy.rpc.SdlMsgVersion; -import com.smartdevicelink.proxy.rpc.enums.InteractionMode; -import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout; -import com.smartdevicelink.proxy.rpc.enums.KeypressMode; -import com.smartdevicelink.proxy.rpc.enums.Language; -import com.smartdevicelink.proxy.rpc.enums.LayoutMode; -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.Collections; - -import static junit.framework.TestCase.assertEquals; -import static junit.framework.TestCase.assertNull; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -@RunWith(AndroidJUnit4.class) -public class PresentChoiceSetOperationTests { - - private PresentChoiceSetOperation presentChoiceSetOperation; - private ChoiceSet choiceSet; - private ISdl internalInterface; - private KeyboardListener keyboardListener; - private ChoiceSetSelectionListener choiceSetSelectionListener; - - private Taskmaster taskmaster; - private Queue queue; - - @Before - public void setUp() throws Exception { - - internalInterface = mock(ISdl.class); - - keyboardListener = mock(KeyboardListener.class); - choiceSetSelectionListener = mock(ChoiceSetSelectionListener.class); - - ChoiceCell cell1 = new ChoiceCell("Cell1"); - cell1.setChoiceId(0); - choiceSet = new ChoiceSet("Test", Collections.singletonList(cell1), choiceSetSelectionListener); - - taskmaster = new Taskmaster.Builder().build(); - queue = taskmaster.createQueue("test", 100, false); - taskmaster.start(); - } - - - private KeyboardProperties getKeyBoardProperties() { - KeyboardProperties properties = new KeyboardProperties(); - properties.setLanguage(Language.EN_US); - properties.setKeyboardLayout(KeyboardLayout.QWERTZ); - properties.setKeypressMode(KeypressMode.RESEND_CURRENT_ENTRY); - return properties; - } - - @Test - public void testGetLayoutMode() { - // First we will check knowing our keyboard listener is NOT NULL - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); - - assertEquals(presentChoiceSetOperation.getLayoutMode(), LayoutMode.LIST_WITH_SEARCH); - presentChoiceSetOperation.keyboardListener = null; - assertEquals(presentChoiceSetOperation.getLayoutMode(), LayoutMode.LIST_ONLY); - } - - @Test - public void testGetPerformInteraction() { - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); - - PerformInteraction pi = presentChoiceSetOperation.getPerformInteraction(); - assertEquals(pi.getInitialText(), "Test"); - assertNull(pi.getHelpPrompt()); - assertNull(pi.getTimeoutPrompt()); - assertNull(pi.getVrHelp()); - assertEquals(pi.getTimeout(), Integer.valueOf(10000)); - assertEquals(pi.getCancelID(), TestValues.GENERAL_INTEGER); - assertEquals(presentChoiceSetOperation.getLayoutMode(), LayoutMode.LIST_WITH_SEARCH); - } - - @Test - public void testSetSelectedCellWithId() { - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); - - assertNull(presentChoiceSetOperation.selectedCellRow); - presentChoiceSetOperation.setSelectedCellWithId(0); - assertEquals(presentChoiceSetOperation.selectedCellRow, Integer.valueOf(0)); - } - - private void sleep() { - try { - Thread.sleep(100); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - @Test - public void testCancelingChoiceSetSuccessfullyIfThreadIsRunning() { - when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); - queue.add(presentChoiceSetOperation, false); - - sleep(); - - assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState()); - - choiceSet.cancel(); - Answer cancelInteractionAnswer = new Answer() { - @Override - public Void answer(InvocationOnMock invocation) { - Object[] args = invocation.getArguments(); - CancelInteraction cancelInteraction = (CancelInteraction) args[0]; - - assertEquals(cancelInteraction.getCancelID(), TestValues.GENERAL_INTEGER); - assertEquals(cancelInteraction.getInteractionFunctionID().intValue(), FunctionID.PERFORM_INTERACTION.getId()); - - RPCResponse response = new RPCResponse(FunctionID.CANCEL_INTERACTION.toString()); - response.setSuccess(true); - cancelInteraction.getOnRPCResponseListener().onResponse(0, response); - - return null; - } - }; - doAnswer(cancelInteractionAnswer).when(internalInterface).sendRPC(any(CancelInteraction.class)); - - verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class)); - verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); - - assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState()); - } - - @Test - public void testCancelingChoiceSetUnsuccessfullyIfThreadIsRunning() { - when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); - queue.add(presentChoiceSetOperation, false); - sleep(); - - assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState()); - - choiceSet.cancel(); - Answer cancelInteractionAnswer = new Answer() { - @Override - public Void answer(InvocationOnMock invocation) { - Object[] args = invocation.getArguments(); - CancelInteraction cancelInteraction = (CancelInteraction) args[0]; - - assertEquals(cancelInteraction.getCancelID(), TestValues.GENERAL_INTEGER); - assertEquals(cancelInteraction.getInteractionFunctionID().intValue(), FunctionID.PERFORM_INTERACTION.getId()); - - RPCResponse response = new RPCResponse(FunctionID.CANCEL_INTERACTION.toString()); - response.setSuccess(false); - cancelInteraction.getOnRPCResponseListener().onResponse(0, response); - - return null; - } - }; - doAnswer(cancelInteractionAnswer).when(internalInterface).sendRPC(any(CancelInteraction.class)); - - verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class)); - verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); - - assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState()); - } - - @Test - public void testCancelingChoiceSetIfThreadHasFinished() { - when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER,null); - presentChoiceSetOperation.finishOperation(); - - assertEquals(Task.FINISHED, presentChoiceSetOperation.getState()); - - choiceSet.cancel(); - verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); - - assertEquals(Task.FINISHED, presentChoiceSetOperation.getState()); - } - - @Test - public void testCancelingChoiceSetIfThreadHasNotYetRun() { - when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); - - assertEquals(Task.BLOCKED, presentChoiceSetOperation.getState()); - - choiceSet.cancel(); - - // Once the operation has started - queue.add(presentChoiceSetOperation, false); - sleep(); - - assertEquals(Task.CANCELED, presentChoiceSetOperation.getState()); - - // Make sure neither a `CancelInteraction` or `PerformInteraction` RPC is ever sent - verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); - verify(internalInterface, never()).sendRPC(any(PerformInteraction.class)); - } - - @Test - public void testCancelingChoiceSetIfHeadUnitDoesNotSupportFeature() { - // Cancel Interaction is only supported on RPC specs v.6.0.0+ - when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3)); - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); - queue.add(presentChoiceSetOperation, false); - sleep(); - - assertEquals(Task.IN_PROGRESS, presentChoiceSetOperation.getState()); - - choiceSet.cancel(); - - verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); - verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); - } - - @Test - public void testCancelingChoiceSetIfHeadUnitDoesNotSupportFeatureButThreadIsNotRunning() { - // Cancel Interaction is only supported on RPC specs v.6.0.0+ - when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3)); - presentChoiceSetOperation = new PresentChoiceSetOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); - - assertEquals(Task.BLOCKED, presentChoiceSetOperation.getState()); - - choiceSet.cancel(); - - verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); - - // Once the operation has started - queue.add(presentChoiceSetOperation, false); - sleep(); - - assertEquals(Task.CANCELED, presentChoiceSetOperation.getState()); - - // Make sure neither a `CancelInteraction` or `PerformInteraction` RPC is ever sent - verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); - verify(internalInterface, never()).sendRPC(any(PerformInteraction.class)); - } -} \ No newline at end of file diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java deleted file mode 100644 index c7a42c15a..000000000 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesOperation.java +++ /dev/null @@ -1,398 +0,0 @@ -/* - * Copyright (c) 2019 Livio, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following - * disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * Neither the name of the Livio Inc. nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Created by brettywhite on 6/12/19 1:52 PM - * - */ - -package com.smartdevicelink.managers.screen.choiceset; - -import androidx.annotation.NonNull; - -import com.livio.taskmaster.Task; -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.proxy.RPCResponse; -import com.smartdevicelink.proxy.rpc.Choice; -import com.smartdevicelink.proxy.rpc.CreateInteractionChoiceSet; -import com.smartdevicelink.proxy.rpc.Image; -import com.smartdevicelink.proxy.rpc.WindowCapability; -import com.smartdevicelink.proxy.rpc.enums.DisplayType; -import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; -import com.smartdevicelink.proxy.rpc.enums.TextFieldName; -import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; -import com.smartdevicelink.util.DebugTool; -import com.smartdevicelink.util.Version; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; - -class PreloadChoicesOperation extends Task { - private static final String TAG = "PreloadChoicesOperation"; - private final WeakReference internalInterface; - private final WeakReference fileManager; - private final WindowCapability defaultMainWindowCapability; - private final String displayName; - private final ArrayList cellsToUpload; - private final PreloadChoicesCompletionListener completionListener; - private boolean isRunning; - private final boolean isVROptional; - private boolean choiceError = false; - private HashSet loadedCells; - - PreloadChoicesOperation(ISdl internalInterface, FileManager fileManager, String displayName, WindowCapability defaultMainWindowCapability, - Boolean isVROptional, LinkedHashSet cellsToPreload, PreloadChoicesCompletionListener listener, HashSet loadedCells) { - super("PreloadChoicesOperation"); - this.internalInterface = new WeakReference<>(internalInterface); - this.fileManager = new WeakReference<>(fileManager); - this.displayName = displayName; - this.defaultMainWindowCapability = defaultMainWindowCapability; - this.isVROptional = isVROptional; - this.cellsToUpload = new ArrayList<>(cellsToPreload); - this.completionListener = listener; - this.loadedCells = loadedCells; - } - - @Override - public void onExecute() { - DebugTool.logInfo(TAG, "Choice Operation: Executing preload choices operation"); - updateCellsBasedOnLoadedChoices(); - preloadCellArtworks(new CompletionListener() { - @Override - public void onComplete(boolean success) { - preloadCells(); - } - }); - } - - void removeChoicesFromUpload(HashSet choices) { - if (isRunning) { - return; - } - cellsToUpload.removeAll(choices); - } - - private void preloadCellArtworks(@NonNull final CompletionListener listener) { - isRunning = true; - - List artworksToUpload = artworksToUpload(); - - if (artworksToUpload.size() == 0) { - DebugTool.logInfo(TAG, "Choice Preload: No Choice Artworks to upload"); - listener.onComplete(true); - isRunning = false; - return; - } - - if (fileManager.get() != null) { - fileManager.get().uploadArtworks(artworksToUpload, new MultipleFileCompletionListener() { - @Override - public void onComplete(Map errors) { - if (errors != null && errors.size() > 0) { - DebugTool.logError(TAG, "Error uploading choice cell Artworks: " + errors.toString()); - listener.onComplete(false); - isRunning = false; - } else { - DebugTool.logInfo(TAG, "Choice Artworks Uploaded"); - listener.onComplete(true); - isRunning = false; - } - } - }); - } else { - DebugTool.logError(TAG, "File manager is null in choice preload operation"); - listener.onComplete(false); - isRunning = false; - } - } - - private void preloadCells() { - isRunning = true; - List choiceRPCs = new ArrayList<>(cellsToUpload.size()); - for (ChoiceCell cell : cellsToUpload) { - CreateInteractionChoiceSet csCell = choiceFromCell(cell); - if (csCell != null) { - choiceRPCs.add(csCell); - } - } - - if (choiceRPCs.size() == 0) { - DebugTool.logError(TAG, " All Choice cells to send are null, so the choice set will not be shown"); - completionListener.onComplete(true, loadedCells); - isRunning = false; - return; - } - - if (internalInterface.get() != null) { - internalInterface.get().sendRPCs(choiceRPCs, new OnMultipleRequestListener() { - final HashSet failedChoiceCells = new HashSet<>(); - - @Override - public void onUpdate(int remainingRequests) { - - } - - @Override - public void onFinished() { - isRunning = false; - DebugTool.logInfo(TAG, "Finished pre loading choice cells"); - completionListener.onComplete(!choiceError, loadedCells); - choiceError = false; - PreloadChoicesOperation.super.onFinished(); - } - - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (!response.getSuccess()) { - DebugTool.logError(TAG, "There was an error uploading a choice cell: " + response.getInfo() + " resultCode: " + response.getResultCode()); - choiceError = true; - } else { - loadedCells.add(cellFromChoiceId(correlationId)); - } - } - }); - } else { - DebugTool.logError(TAG, "Internal Interface null in preload choice operation"); - isRunning = false; - completionListener.onComplete(false, loadedCells); - } - } - - private ChoiceCell cellFromChoiceId(int choiceId) { - for (ChoiceCell cell : this.cellsToUpload) { - if (cell.getChoiceId() == choiceId) { - return cell; - } - } - return null; - } - - private CreateInteractionChoiceSet choiceFromCell(ChoiceCell cell) { - - List vrCommands; - if (cell.getVoiceCommands() == null) { - vrCommands = isVROptional ? null : Collections.singletonList(String.valueOf(cell.getChoiceId())); - } else { - vrCommands = cell.getVoiceCommands(); - } - - String menuName = shouldSendChoiceText() ? cell.getUniqueText() : null; - - if (menuName == null) { - DebugTool.logError(TAG, "Could not convert Choice Cell to CreateInteractionChoiceSet. It will not be shown. Cell: " + cell.toString()); - return null; - } - - String secondaryText = shouldSendChoiceSecondaryText() ? cell.getSecondaryText() : null; - String tertiaryText = shouldSendChoiceTertiaryText() ? cell.getTertiaryText() : null; - - Image image = shouldSendChoicePrimaryImage() && cell.getArtwork() != null ? cell.getArtwork().getImageRPC() : null; - Image secondaryImage = shouldSendChoiceSecondaryImage() && cell.getSecondaryArtwork() != null ? cell.getSecondaryArtwork().getImageRPC() : null; - - Choice choice = new Choice(cell.getChoiceId(), menuName); - choice.setVrCommands(vrCommands); - choice.setSecondaryText(secondaryText); - choice.setTertiaryText(tertiaryText); - choice.setIgnoreAddingVRItems(true); - - if (fileManager.get() != null) { - if (image != null && (cell.getArtwork().isStaticIcon() || fileManager.get().hasUploadedFile(cell.getArtwork()))) { - choice.setImage(image); - } - if (secondaryImage != null && (cell.getSecondaryArtwork().isStaticIcon() || fileManager.get().hasUploadedFile(cell.getSecondaryArtwork()))) { - choice.setSecondaryImage(secondaryImage); - } - } - - return new CreateInteractionChoiceSet(choice.getChoiceID(), Collections.singletonList(choice)); - } - - // HELPERS - boolean shouldSendChoiceText() { - if (this.displayName != null && this.displayName.equals(DisplayType.GEN3_8_INCH.toString())) { - return true; - } - return templateSupportsTextField(TextFieldName.menuName); - } - - boolean shouldSendChoiceSecondaryText() { - return templateSupportsTextField(TextFieldName.secondaryText); - } - - boolean shouldSendChoiceTertiaryText() { - return templateSupportsTextField(TextFieldName.tertiaryText); - } - - boolean shouldSendChoicePrimaryImage() { - return templateSupportsImageField(ImageFieldName.choiceImage); - } - - boolean shouldSendChoiceSecondaryImage() { - return templateSupportsImageField(ImageFieldName.choiceSecondaryImage); - } - - boolean templateSupportsTextField(TextFieldName name) { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, name); - } - - boolean templateSupportsImageField(ImageFieldName name) { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, name); - } - - public void setLoadedCells(HashSet loadedCells) { - this.loadedCells = loadedCells; - } - - public HashSet getLoadedCells() { - return this.loadedCells; - } - - List artworksToUpload() { - List artworksToUpload = new ArrayList<>(); - for (ChoiceCell cell : cellsToUpload) { - if (shouldSendChoicePrimaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getArtwork())) { - artworksToUpload.add(cell.getArtwork()); - } - if (shouldSendChoiceSecondaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork())) { - artworksToUpload.add(cell.getSecondaryArtwork()); - } - } - return artworksToUpload; - } - - void updateCellsBasedOnLoadedChoices() { - if (internalInterface.get().getProtocolVersion().getMajor() >= 7 && internalInterface.get().getProtocolVersion().getMinor() >= 1) { - addUniqueNamesToCells(cellsToUpload); - } else { - ArrayList strippedCellsCopy = (ArrayList) removeUnusedProperties(cellsToUpload); - addUniqueNamesBasedOnStrippedCells(strippedCellsCopy, cellsToUpload); - } - cellsToUpload.removeAll(loadedCells); - } - - List removeUnusedProperties(List choiceCells) { - List strippedCellsClone = cloneChoiceCellList(choiceCells); - //Clone Cells - for (ChoiceCell cell : strippedCellsClone) { - // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI - cell.setVoiceCommands(null); - - if (!hasImageFieldOfName(ImageFieldName.choiceImage)) { - cell.setArtwork(null); - } - if (!hasTextFieldOfName(TextFieldName.secondaryText)) { - cell.setSecondaryText(null); - } - if (!hasTextFieldOfName(TextFieldName.tertiaryText)) { - cell.setTertiaryText(null); - } - if (!hasImageFieldOfName(ImageFieldName.choiceSecondaryImage)) { - cell.setSecondaryArtwork(null); - } - } - return strippedCellsClone; - } - - private List cloneChoiceCellList(List originalList) { - if (originalList == null) { - return null; - } - List clone = new ArrayList<>(); - for (ChoiceCell choiceCell : originalList) { - clone.add(choiceCell.clone()); - } - return clone; - } - - private boolean hasImageFieldOfName(ImageFieldName imageFieldName) { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, imageFieldName); - } - - private boolean hasTextFieldOfName(TextFieldName textFieldName) { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, textFieldName); - } - - /** - * Checks if 2 or more cells have the same text/title. In case this condition is true, this function will handle the presented issue by adding "(count)". - * E.g. Choices param contains 2 cells with text/title "Address" will be handled by updating the uniqueText/uniqueTitle of the second cell to "Address (2)". - * @param choices The list of choiceCells to be uploaded. - */ - void addUniqueNamesToCells(List choices) { - HashMap dictCounter = new HashMap<>(); - - for (ChoiceCell cell : choices) { - String cellName = cell.getText(); - Integer counter = dictCounter.get(cellName); - - if (counter != null) { - dictCounter.put(cellName, ++counter); - cell.setUniqueText(cell.getText() + " (" + counter + ")"); - } else { - dictCounter.put(cellName, 1); - } - } - } - - void addUniqueNamesBasedOnStrippedCells(List strippedCells, List unstrippedCells) { - if (strippedCells == null || unstrippedCells == null || strippedCells.size() != unstrippedCells.size()) { - return; - } - // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary - HashMap dictCounter = new HashMap<>(); - for (int i = 0; i < strippedCells.size(); i++) { - ChoiceCell cell = strippedCells.get(i); - Integer counter = dictCounter.get(cell); - if (counter != null) { - counter++; - dictCounter.put(cell, counter); - } else { - dictCounter.put(cell, 1); - } - - counter = dictCounter.get(cell); - - if (counter > 1) { - unstrippedCells.get(i).setUniqueText(unstrippedCells.get(i).getText() + " (" + counter + ")"); - } - } - } - -} \ No newline at end of file diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java deleted file mode 100644 index 564da2012..000000000 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PresentChoiceSetOperation.java +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright (c) 2019 Livio, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following - * disclaimer in the documentation and/or other materials provided with the - * distribution. - * - * Neither the name of the Livio Inc. nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - * Created by brettywhite on 6/12/19 1:52 PM - * - */ - -package com.smartdevicelink.managers.screen.choiceset; - -import com.livio.taskmaster.Task; -import com.smartdevicelink.managers.CompletionListener; -import com.smartdevicelink.managers.ISdl; -import com.smartdevicelink.protocol.enums.FunctionID; -import com.smartdevicelink.proxy.RPCNotification; -import com.smartdevicelink.proxy.RPCResponse; -import com.smartdevicelink.proxy.rpc.CancelInteraction; -import com.smartdevicelink.proxy.rpc.KeyboardProperties; -import com.smartdevicelink.proxy.rpc.OnKeyboardInput; -import com.smartdevicelink.proxy.rpc.PerformInteraction; -import com.smartdevicelink.proxy.rpc.PerformInteractionResponse; -import com.smartdevicelink.proxy.rpc.SdlMsgVersion; -import com.smartdevicelink.proxy.rpc.SetGlobalProperties; -import com.smartdevicelink.proxy.rpc.enums.InteractionMode; -import com.smartdevicelink.proxy.rpc.enums.KeyboardEvent; -import com.smartdevicelink.proxy.rpc.enums.LayoutMode; -import com.smartdevicelink.proxy.rpc.enums.Result; -import com.smartdevicelink.proxy.rpc.enums.TriggerSource; -import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener; -import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener; -import com.smartdevicelink.util.DebugTool; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - -class PresentChoiceSetOperation extends Task { - private static final String TAG = "PresentChoiceSetOperation"; - private final WeakReference internalInterface; - private final ChoiceSet choiceSet; - private final Integer cancelID; - private final InteractionMode presentationMode; - private final KeyboardProperties originalKeyboardProperties; - private KeyboardProperties keyboardProperties; - private ChoiceCell selectedCell; - private TriggerSource selectedTriggerSource; - private boolean updatedKeyboardProperties; - private OnRPCNotificationListener keyboardRPCListener; - private final ChoiceSetSelectionListener choiceSetSelectionListener; - Integer selectedCellRow; - KeyboardListener keyboardListener; - final SdlMsgVersion sdlMsgVersion; - private HashSet loadedCells; - - PresentChoiceSetOperation(ISdl internalInterface, ChoiceSet choiceSet, InteractionMode mode, - KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, ChoiceSetSelectionListener choiceSetSelectionListener, Integer cancelID, HashSet loadedCells) { - super("PresentChoiceSetOperation"); - this.internalInterface = new WeakReference<>(internalInterface); - this.keyboardListener = keyboardListener; - this.choiceSet = choiceSet; - this.choiceSet.canceledListener = new ChoiceSetCanceledListener() { - @Override - public void onChoiceSetCanceled() { - cancelInteraction(); - } - }; - this.presentationMode = mode; - this.cancelID = cancelID; - this.originalKeyboardProperties = originalKeyboardProperties; - this.keyboardProperties = originalKeyboardProperties; - this.selectedCellRow = null; - this.choiceSetSelectionListener = choiceSetSelectionListener; - this.sdlMsgVersion = internalInterface.getSdlMsgVersion(); - this.loadedCells = loadedCells; - } - - @Override - public void onExecute() { - DebugTool.logInfo(TAG, "Choice Operation: Executing present choice set operation"); - addListeners(); - start(); - } - - private void start() { - if (getState() == Task.CANCELED) { - finishOperation(); - return; - } - - HashSet choiceSetCells = new HashSet<>(this.choiceSet.getChoices()); - if (!choiceSetCells.containsAll(this.loadedCells)) { - this.choiceSetSelectionListener.onError("Choices not available for presentation"); - finishOperation(); - return; - } - - updateChoiceSetChoicesId(); - - // Check if we're using a keyboard (searchable) choice set and setup keyboard properties if we need to - if (keyboardListener != null && choiceSet.getCustomKeyboardConfiguration() != null) { - keyboardProperties = choiceSet.getCustomKeyboardConfiguration(); - updatedKeyboardProperties = true; - } - - updateKeyboardProperties(new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (getState() == Task.CANCELED) { - finishOperation(); - return; - } - presentChoiceSet(); - } - }); - } - - // SENDING REQUESTS - - private void updateKeyboardProperties(final CompletionListener listener) { - if (keyboardProperties == null) { - if (listener != null) { - listener.onComplete(false); - } - return; - } - SetGlobalProperties setGlobalProperties = new SetGlobalProperties(); - setGlobalProperties.setKeyboardProperties(keyboardProperties); - setGlobalProperties.setOnRPCResponseListener(new OnRPCResponseListener() { - @Override - public void onResponse(int correlationId, RPCResponse response) { - - if (!response.getSuccess()) { - if (listener != null) { - listener.onComplete(false); - } - DebugTool.logError(TAG, "Error Setting keyboard properties in present choice set operation"); - return; - } - - updatedKeyboardProperties = true; - - if (listener != null) { - listener.onComplete(true); - } - DebugTool.logInfo(TAG, "Success Setting keyboard properties in present choice set operation"); - } - }); - if (internalInterface.get() != null) { - internalInterface.get().sendRPC(setGlobalProperties); - } else { - DebugTool.logError(TAG, "Internal interface null - present choice set op - choice"); - } - } - - private void presentChoiceSet() { - PerformInteraction pi = getPerformInteraction(); - pi.setOnRPCResponseListener(new OnRPCResponseListener() { - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (!response.getSuccess()) { - DebugTool.logError(TAG, "Presenting Choice set failed: " + response.getInfo()); - - if (choiceSetSelectionListener != null) { - choiceSetSelectionListener.onError(response.getInfo()); - } - finishOperation(); - return; - } - - PerformInteractionResponse performInteractionResponse = (PerformInteractionResponse) response; - setSelectedCellWithId(performInteractionResponse.getChoiceID()); - selectedTriggerSource = performInteractionResponse.getTriggerSource(); - - if (choiceSetSelectionListener != null && selectedCell != null && selectedTriggerSource != null && selectedCellRow != null) { - choiceSetSelectionListener.onChoiceSelected(selectedCell, selectedTriggerSource, selectedCellRow); - } - - finishOperation(); - } - }); - if (internalInterface.get() != null) { - internalInterface.get().sendRPC(pi); - } else { - DebugTool.logError(TAG, "Internal Interface null when presenting choice set in operation"); - } - } - - void finishOperation() { - if (updatedKeyboardProperties) { - // We need to reset the keyboard properties - SetGlobalProperties setGlobalProperties = new SetGlobalProperties(); - setGlobalProperties.setKeyboardProperties(originalKeyboardProperties); - setGlobalProperties.setOnRPCResponseListener(new OnRPCResponseListener() { - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (response.getSuccess()) { - updatedKeyboardProperties = false; - DebugTool.logInfo(TAG, "Successfully reset choice keyboard properties to original config"); - } else { - DebugTool.logError(TAG, "Failed to reset choice keyboard properties to original config " + response.getResultCode() + ", " + response.getInfo()); - } - PresentChoiceSetOperation.super.onFinished(); - } - }); - - if (internalInterface.get() != null) { - internalInterface.get().sendRPC(setGlobalProperties); - internalInterface.get().removeOnRPCNotificationListener(FunctionID.ON_KEYBOARD_INPUT, keyboardRPCListener); - } else { - DebugTool.logError(TAG, "Internal Interface null when finishing choice keyboard reset"); - } - } else { - PresentChoiceSetOperation.super.onFinished(); - } - } - - /* - * Cancels the choice set. If the choice set has not yet been sent to Core, it will not be sent. If the choice set is already presented on Core, the choice set will be dismissed using the `CancelInteraction` RPC. - */ - private void cancelInteraction() { - if ((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 (sdlMsgVersion.getMajorVersion() < 6) { - DebugTool.logWarning(TAG, "Canceling a presented choice set is not supported on this head unit"); - return; - } - - DebugTool.logInfo(TAG, "Canceling the presented choice set interaction."); - - CancelInteraction cancelInteraction = new CancelInteraction(FunctionID.PERFORM_INTERACTION.getId(), cancelID); - cancelInteraction.setOnRPCResponseListener(new OnRPCResponseListener() { - @Override - public void onResponse(int correlationId, RPCResponse response) { - DebugTool.logInfo(TAG, "Canceled the presented choice set " + ((response.getResultCode() == Result.SUCCESS) ? "successfully" : "unsuccessfully")); - } - }); - - if (internalInterface.get() != null) { - internalInterface.get().sendRPC(cancelInteraction); - } else { - DebugTool.logError(TAG, "Internal interface null - could not send cancel interaction for choice set"); - } - } else { - DebugTool.logInfo(TAG, "Canceling a choice set that has not yet been sent to Core"); - this.cancelTask(); - } - } - - // GETTERS - - PerformInteraction getPerformInteraction() { - PerformInteraction pi = new PerformInteraction(choiceSet.getTitle(), presentationMode, getChoiceIds()); - pi.setInitialPrompt(choiceSet.getInitialPrompt()); - pi.setHelpPrompt(choiceSet.getHelpPrompt()); - pi.setTimeoutPrompt(choiceSet.getTimeoutPrompt()); - pi.setVrHelp(choiceSet.getVrHelpList()); - pi.setTimeout(choiceSet.getTimeout() * 1000); - pi.setInteractionLayout(getLayoutMode()); - pi.setCancelID(cancelID); - return pi; - } - - LayoutMode getLayoutMode() { - switch (choiceSet.getLayout()) { - case CHOICE_SET_LAYOUT_LIST: - return keyboardListener != null ? LayoutMode.LIST_WITH_SEARCH : LayoutMode.LIST_ONLY; - case CHOICE_SET_LAYOUT_TILES: - return keyboardListener != null ? LayoutMode.ICON_WITH_SEARCH : LayoutMode.ICON_ONLY; - } - return LayoutMode.LIST_ONLY; // default - } - - private List getChoiceIds() { - - List choiceIds = new ArrayList<>(choiceSet.getChoices().size()); - for (ChoiceCell cell : choiceSet.getChoices()) { - choiceIds.add(cell.getChoiceId()); - } - return choiceIds; - } - - public ChoiceSet getChoiceSet() { - return this.choiceSet; - } - - public void setLoadedCells(HashSet loadedCells) { - this.loadedCells = loadedCells; - } - - public HashSet getLoadedCells() { - return this.loadedCells; - } - - // HELPERS - - void updateChoiceSetChoicesId() { - for (ChoiceCell cell : this.choiceSet.getChoices()) { - for (ChoiceCell loadedCell : this.loadedCells) { - if (loadedCell.equals(cell)) { - cell.setChoiceId(loadedCell.getChoiceId()); - } - } - } - } - - void setSelectedCellWithId(Integer cellId) { - if (choiceSet.getChoices() != null && cellId != null) { - List cells = choiceSet.getChoices(); - for (int i = 0; i < cells.size(); i++) { - if (cells.get(i).getChoiceId() == cellId) { - selectedCell = cells.get(i); - selectedCellRow = i; - return; - } - } - } - } - - // LISTENERS - - private void addListeners() { - - keyboardRPCListener = new OnRPCNotificationListener() { - @Override - public void onNotified(RPCNotification notification) { - if (getState() == Task.CANCELED) { - finishOperation(); - return; - } - - if (keyboardListener == null) { - DebugTool.logError(TAG, "Received Keyboard Input But Listener is null"); - return; - } - - OnKeyboardInput onKeyboard = (OnKeyboardInput) notification; - keyboardListener.onKeyboardDidSendEvent(onKeyboard.getEvent(), onKeyboard.getData()); - - if (onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_VOICE) || onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_SUBMITTED)) { - // Submit Voice or Text - keyboardListener.onUserDidSubmitInput(onKeyboard.getData(), onKeyboard.getEvent()); - } else if (onKeyboard.getEvent().equals(KeyboardEvent.KEYPRESS)) { - // Notify of Keypress - keyboardListener.updateAutocompleteWithInput(onKeyboard.getData(), new KeyboardAutocompleteCompletionListener() { - @Override - public void onUpdatedAutoCompleteList(List updatedAutoCompleteList) { - keyboardProperties.setAutoCompleteList(updatedAutoCompleteList != null ? updatedAutoCompleteList : new ArrayList()); - keyboardProperties.setAutoCompleteText(updatedAutoCompleteList != null && !updatedAutoCompleteList.isEmpty() ? updatedAutoCompleteList.get(0) : null); - updateKeyboardProperties(null); - } - }); - - keyboardListener.updateCharacterSetWithInput(onKeyboard.getData(), new KeyboardCharacterSetCompletionListener() { - @Override - public void onUpdatedCharacterSet(List updatedCharacterSet) { - keyboardProperties.setLimitedCharacterList(updatedCharacterSet); - updateKeyboardProperties(null); - } - }); - } else if (onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_ABORTED) || onKeyboard.getEvent().equals(KeyboardEvent.ENTRY_CANCELLED)) { - // Notify of abort / Cancellation - keyboardListener.onKeyboardDidAbortWithReason(onKeyboard.getEvent()); - } else if (onKeyboard.getEvent().equals(KeyboardEvent.INPUT_KEY_MASK_ENABLED) || onKeyboard.getEvent().equals(KeyboardEvent.INPUT_KEY_MASK_DISABLED)) { - keyboardListener.onKeyboardDidUpdateInputMask(onKeyboard.getEvent()); - } - } - }; - - if (internalInterface.get() != null) { - internalInterface.get().addOnRPCNotificationListener(FunctionID.ON_KEYBOARD_INPUT, keyboardRPCListener); - } else { - DebugTool.logError(TAG, "Present Choice Set Keyboard Listener Not Added"); - } - } -} \ No newline at end of file -- cgit v1.2.1 From f7fef99c10e76925a79b5f6806b1d8db6ba6131e Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 23 Jul 2021 10:38:57 -0400 Subject: Fix unit tests --- .../screen/choiceset/PreloadPresentChoicesOperationTests.java | 4 ++++ .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 2 -- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java index fbfeb3d6f..bd13643a3 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java @@ -39,6 +39,7 @@ import org.mockito.stubbing.Answer; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; @@ -251,6 +252,7 @@ public class PreloadPresentChoicesOperationTests { public void testCancelingChoiceSetSuccessfullyIfThreadIsRunning() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + presentChoicesOperation.setLoadedCells(new HashSet()); queue.add(presentChoicesOperation, false); sleep(); @@ -286,6 +288,7 @@ public class PreloadPresentChoicesOperationTests { public void testCancelingChoiceSetUnsuccessfullyIfThreadIsRunning() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + presentChoicesOperation.setLoadedCells(new HashSet()); queue.add(presentChoicesOperation, false); sleep(); @@ -355,6 +358,7 @@ public class PreloadPresentChoicesOperationTests { // Cancel Interaction is only supported on RPC specs v.6.0.0+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3)); presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + presentChoicesOperation.setLoadedCells(new HashSet()); queue.add(presentChoicesOperation, false); sleep(); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 52d2b88e5..83045b809 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -202,8 +202,6 @@ public class PreloadPresentChoicesOperation extends Task { if (internalInterface.get() != null) { internalInterface.get().sendRPCs(choiceRPCs, new OnMultipleRequestListener() { - final HashSet failedChoiceCells = new HashSet<>(); - @Override public void onUpdate(int remainingRequests) { -- cgit v1.2.1 From fdb5240e0b3057d6e8b126d7e72f3b3035a25282 Mon Sep 17 00:00:00 2001 From: Henigan Date: Mon, 26 Jul 2021 09:53:09 -0400 Subject: remove extra preload call --- .../managers/screen/choiceset/BaseChoiceSetManager.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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 c7127458c..02172a47c 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 @@ -276,7 +276,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return; } - preloadChoices(choiceSet.getChoices(), null); + updateIdsOnChoiceSet(choiceSet); preloadChoices(choiceSet.getChoices(), new CompletionListener() { @Override @@ -493,6 +493,13 @@ abstract class BaseChoiceSetManager extends BaseSubManager { } } + void updateIdsOnChoiceSet(ChoiceSet choiceSet) { + for (ChoiceCell cell : choiceSet.getChoices()) { + cell.setChoiceId(this.nextChoiceId); + this.nextChoiceId++; + } + } + ChoiceCell findIfPresent(ChoiceCell cell, HashSet set) { if (set.contains(cell)) { for (ChoiceCell setCell : set) { -- cgit v1.2.1 From 8f8473efe83ca99cb5f93536b594b878ac53bff4 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 26 Jul 2021 15:15:29 -0400 Subject: Menu cells uniqueness initial implementation --- .../managers/screen/menu/BaseMenuManager.java | 95 +++++++++++++++++----- .../managers/screen/menu/MenuCell.java | 19 ++--- .../managers/screen/menu/MenuReplaceUtilities.java | 4 +- 3 files changed, 83 insertions(+), 35 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 29a08d91f..2a09498a7 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -40,7 +40,6 @@ 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.file.filetypes.SdlArtwork; import com.smartdevicelink.managers.lifecycle.OnSystemCapabilityListener; import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager; import com.smartdevicelink.protocol.enums.FunctionID; @@ -59,6 +58,7 @@ import com.smartdevicelink.util.DebugTool; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -168,32 +168,18 @@ abstract class BaseMenuManager extends BaseSubManager { * @param cells - the menu cells that are to be sent to the head unit, including their sub-cells. */ public void setMenuCells(@NonNull List cells) { - // Create a deep copy of the list so future changes by developers don't affect the algorithm logic - final List clonedCells = cloneMenuCellsList(cells); - - // HashSet order doesn't matter / doesn't allow duplicates - HashSet titleCheckSet = new HashSet<>(); - HashSet allMenuVoiceCommands = new HashSet<>(); - int voiceCommandCount = 0; - for (MenuCell cell : clonedCells) { - titleCheckSet.add(cell.getTitle()); - if (cell.getVoiceCommands() != null) { - allMenuVoiceCommands.addAll(cell.getVoiceCommands()); - voiceCommandCount += cell.getVoiceCommands().size(); - } + // If we're running on a connection < RPC 7.1, we need to de-duplicate cells because presenting them will fail if we have the same cell primary text. + if (cells != null && internalInterface.getSdlMsgVersion() != null && (internalInterface.getSdlMsgVersion().getMajorVersion() < 7 || (internalInterface.getSdlMsgVersion().getMajorVersion() == 7 && internalInterface.getSdlMsgVersion().getMinorVersion() == 0))) { + addUniqueNamesToCells(cells); } - // Check for duplicate titles - if (titleCheckSet.size() != clonedCells.size()) { - DebugTool.logError(TAG, "Not all cell titles are unique. The menu will not be set"); + // Check for cell lists with completely duplicate information, or any duplicate voiceCommands and return if it fails (logs are in the called method). + if (cells != null && !menuCellsAreUnique(cells, new ArrayList())) { return; } - // Check for duplicate voice commands - if (allMenuVoiceCommands.size() != voiceCommandCount) { - DebugTool.logError(TAG, "Attempted to create a menu with duplicate voice commands. Voice commands must be unique. The menu will not be set"); - return; - } + // Create a deep copy of the list so future changes by developers don't affect the algorithm logic + final List clonedCells = cloneMenuCellsList(cells); updateIdsOnMenuCells(clonedCells, parentIdNotFound); @@ -460,4 +446,69 @@ abstract class BaseMenuManager extends BaseSubManager { } } } + + private void addUniqueNamesToCells(List cells) { + HashMap titlesMap = new HashMap<>(); + + for (MenuCell cell : cells) { + String cellTitle = cell.getTitle(); + Integer counter = titlesMap.get(cellTitle); + + if (counter == null) { + titlesMap.put(cellTitle, 1); + cell.setUniqueTitle(cellTitle); + } else { + titlesMap.put(cellTitle, ++counter); + cell.setUniqueTitle(cellTitle + " (" + counter + ")"); + } + + if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + addUniqueNamesToCells(cell.getSubCells()); + } + } + } + + /** + Check for cell lists with completely duplicate information, or any duplicate voiceCommands + @param cells The cells you will be adding + @return Boolean that indicates whether menuCells are unique or not + */ + private boolean menuCellsAreUnique(List cells, ArrayList allVoiceCommands) { + // Check all voice commands for identical items and check each list of cells for identical cells + HashSet identicalCellsCheckSet = new HashSet<>(); + + for (MenuCell cell : cells) { + identicalCellsCheckSet.add(cell); + + // Recursively check the sub-cell lists to see if they are all unique as well. If anything is not, this will chain back up the list to return false. + if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + boolean subCellsAreUnique = menuCellsAreUnique(cell.getSubCells(), allVoiceCommands); + + if (!subCellsAreUnique) { + DebugTool.logError(TAG, "Not all subCells are unique. The menu will not be set."); + return false; + } + } + + // Voice commands have to be identical across all lists + if (cell.getVoiceCommands() != null) { + allVoiceCommands.addAll(cell.getVoiceCommands()); + } + } + + // Check for duplicate cells + if (identicalCellsCheckSet.size() != cells.size()) { + DebugTool.logError(TAG, "Not all cells are unique. The menu will not be set."); + return false; + } + + // All the VR commands must be unique + HashSet voiceCommandsSet = new HashSet<>(allVoiceCommands); + if (allVoiceCommands.size() != voiceCommandsSet.size()) { + DebugTool.logError(TAG, "Attempted to create a menu with duplicate voice commands. Voice commands must be unique. The menu will not be set"); + return false; + } + + return true; + } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index 0f6e532a2..362a11a38 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -122,7 +122,6 @@ public class MenuCell implements Cloneable { @Deprecated public MenuCell(@NonNull String title, @Nullable SdlArtwork icon, @Nullable List voiceCommands, @Nullable MenuSelectionListener listener) { setTitle(title); // title is the only required param - setUniqueTitle(title); setIcon(icon); setVoiceCommands(voiceCommands); setMenuSelectionListener(listener); @@ -143,7 +142,6 @@ public class MenuCell implements Cloneable { */ public MenuCell(@NonNull String title, @Nullable String secondaryText, @Nullable String tertiaryText, @Nullable SdlArtwork icon, @Nullable SdlArtwork secondaryArtwork, @Nullable List voiceCommands, @Nullable MenuSelectionListener listener) { setTitle(title); // title is the only required param - setUniqueTitle(title); setSecondaryText(secondaryText); setTertiaryText(tertiaryText); setIcon(icon); @@ -169,7 +167,6 @@ public class MenuCell implements Cloneable { @Deprecated public MenuCell(@NonNull String title, @Nullable MenuLayout subMenuLayout, @Nullable SdlArtwork icon, @Nullable List subCells) { setTitle(title); // title is the only required param - setUniqueTitle(title); setSubMenuLayout(subMenuLayout); setIcon(icon); setSubCells(subCells); @@ -192,7 +189,6 @@ public class MenuCell implements Cloneable { */ public MenuCell(@NonNull String title, @Nullable String secondaryText, @Nullable String tertiaryText, @Nullable MenuLayout subMenuLayout, @Nullable SdlArtwork icon, @Nullable SdlArtwork secondaryArtwork, @Nullable List subCells) { setTitle(title); // title is the only required param - setUniqueTitle(title); setSecondaryText(secondaryText); setTertiaryText(tertiaryText); setSubMenuLayout(subMenuLayout); @@ -446,13 +442,14 @@ public class MenuCell implements Cloneable { public int hashCode() { int result = 1; result += ((getTitle() == null) ? 0 : Integer.rotateLeft(getTitle().hashCode(), 1)); - result += ((getIcon() == null) ? 0 : Integer.rotateLeft(getIcon().hashCode(), 2)); - result += ((getVoiceCommands() == null || getVoiceCommands().isEmpty()) ? 0 : Integer.rotateLeft(getVoiceCommands().hashCode(), 3)); - result += ((getSubCells() == null) ? 0 : Integer.rotateLeft(getSubCells().hashCode(), 4)); - result += ((getSecondaryText() == null) ? 0 : Integer.rotateLeft(getSecondaryText().hashCode(), 5)); - result += ((getTertiaryText() == null) ? 0 : Integer.rotateLeft(getTertiaryText().hashCode(), 6)); - result += ((getSecondaryArtwork() == null) ? 0 : Integer.rotateLeft(getSecondaryArtwork().hashCode(), 7)); - result += ((getMenuSelectionListener() == null) ? 0 : Integer.rotateLeft(getMenuSelectionListener().hashCode(), 8)); + result += ((getUniqueTitle() == null) ? 0 : Integer.rotateLeft(getUniqueTitle().hashCode(), 2)); + result += ((getIcon() == null) ? 0 : Integer.rotateLeft(getIcon().hashCode(), 3)); + result += ((getVoiceCommands() == null || getVoiceCommands().isEmpty()) ? 0 : Integer.rotateLeft(getVoiceCommands().hashCode(), 4)); + result += ((getSubCells() == null) ? 0 : Integer.rotateLeft(getSubCells().hashCode(), 5)); + result += ((getSecondaryText() == null) ? 0 : Integer.rotateLeft(getSecondaryText().hashCode(), 6)); + result += ((getTertiaryText() == null) ? 0 : Integer.rotateLeft(getTertiaryText().hashCode(), 7)); + result += ((getSecondaryArtwork() == null) ? 0 : Integer.rotateLeft(getSecondaryArtwork().hashCode(), 9)); + result += ((getMenuSelectionListener() == null) ? 0 : Integer.rotateLeft(getMenuSelectionListener().hashCode(), 9)); return result; } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index ec715c904..99996606b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -162,7 +162,7 @@ class MenuReplaceUtilities { static AddCommand commandForMenuCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position) { AddCommand command = new AddCommand(cell.getCellId()); - MenuParams params = new MenuParams(cell.getTitle()); + MenuParams params = new MenuParams(cell.getUniqueTitle()); params.setSecondaryText((cell.getSecondaryText() != null && !cell.getSecondaryText().isEmpty() && hasTextFieldOfName(windowCapability, TextFieldName.menuCommandSecondaryText)) ? cell.getSecondaryText() : null); params.setTertiaryText((cell.getTertiaryText() != null && !cell.getTertiaryText().isEmpty() && hasTextFieldOfName(windowCapability, TextFieldName.menuCommandTertiaryText)) ? cell.getTertiaryText() : null); params.setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null); @@ -197,7 +197,7 @@ class MenuReplaceUtilities { submenuLayout = defaultSubmenuLayout; } - return new AddSubMenu(cell.getCellId(), cell.getTitle()) + return new AddSubMenu(cell.getCellId(), cell.getUniqueTitle()) .setParentID(cell.getParentCellId() != parentIdNotFound ? cell.getParentCellId() : null) .setSecondaryText((cell.getSecondaryText() != null && !cell.getSecondaryText().isEmpty() && hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuSecondaryText)) ? cell.getSecondaryText() : null) .setTertiaryText((cell.getTertiaryText() != null && !cell.getTertiaryText().isEmpty() && hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuTertiaryText)) ? cell.getTertiaryText() : null) -- cgit v1.2.1 From fcb693e2ca8517b4e96d3238ab34d902302e3513 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 26 Jul 2021 15:45:03 -0400 Subject: Fix unit tests --- .../managers/screen/menu/MenuCellTests.java | 4 -- .../managers/screen/menu/MenuManagerTests.java | 50 +++++++++++----------- 2 files changed, 25 insertions(+), 29 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java index 39c5153ee..1b1a7bd6d 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java @@ -97,20 +97,16 @@ public class MenuCellTests { assertEquals(menuCell3.getIcon(), TestValues.GENERAL_ARTWORK); assertEquals(menuCell3.getVoiceCommands(), TestValues.GENERAL_STRING_LIST); assertEquals(menuCell3.getMenuSelectionListener(), menuSelectionListener); - assertEquals(menuCell3.getUniqueTitle(), TestValues.GENERAL_STRING); MenuCell menuCell4 = new MenuCell(TestValues.GENERAL_STRING, null, null, menuSelectionListener); assertEquals(menuCell4.getTitle(), TestValues.GENERAL_STRING); assertEquals(menuCell4.getMenuSelectionListener(), menuSelectionListener); - assertEquals(menuCell4.getUniqueTitle(), TestValues.GENERAL_STRING); MenuCell menuCell5 = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_MENU_LAYOUT, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_MENUCELL_LIST); assertEquals(menuCell5.getTitle(), TestValues.GENERAL_STRING); assertEquals(menuCell5.getIcon(), TestValues.GENERAL_ARTWORK); assertEquals(menuCell5.getSubMenuLayout(), TestValues.GENERAL_MENU_LAYOUT); assertEquals(menuCell5.getSubCells(), TestValues.GENERAL_MENUCELL_LIST); - assertEquals(menuCell5.getUniqueTitle(), TestValues.GENERAL_STRING); - MenuCell menuCell6 = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_STRING, TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_ARTWORK, TestValues.GENERAL_STRING_LIST, menuSelectionListener); assertEquals(menuCell6.getTitle(), TestValues.GENERAL_STRING); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index 858a4db50..358155d7c 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -289,6 +289,11 @@ public class MenuManagerTests { assertEquals(menuManager.currentMenuCells.size(), 4); + menuManager.setMenuCells(newMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + // this happens in the menu manager but lets make sure its behaving DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); @@ -299,11 +304,6 @@ public class MenuManagerTests { assertEquals(runScore.getOldStatus(), oldMenuStatus); assertEquals(runScore.getUpdatedStatus(), newMenuStatus); - menuManager.setMenuCells(newMenu); - - // Sleep to give time to Taskmaster to run the operations - sleep(); - assertEquals(5, menuManager.currentMenuCells.size()); List oldKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getOldStatus(), KEEP); List newKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getUpdatedStatus(), KEEP); @@ -334,6 +334,11 @@ public class MenuManagerTests { assertEquals(4, menuManager.currentMenuCells.size()); + menuManager.setMenuCells(newMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + // this happens in the menu manager but lets make sure its behaving DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); @@ -344,11 +349,6 @@ public class MenuManagerTests { assertEquals(runScore.getOldStatus(), oldMenuScore); assertEquals(runScore.getUpdatedStatus(), newMenuScore); - menuManager.setMenuCells(newMenu); - - // Sleep to give time to Taskmaster to run the operations - sleep(); - assertEquals(3, menuManager.currentMenuCells.size()); List oldKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getOldStatus(), KEEP); List newKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getUpdatedStatus(), KEEP); @@ -379,6 +379,11 @@ public class MenuManagerTests { assertEquals(menuManager.currentMenuCells.size(), 3); + menuManager.setMenuCells(newMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + // this happens in the menu manager but lets make sure its behaving DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); @@ -389,11 +394,6 @@ public class MenuManagerTests { assertEquals(runScore.getOldStatus(), oldMenuStatus); assertEquals(runScore.getUpdatedStatus(), newMenuStatus); - menuManager.setMenuCells(newMenu); - - // Sleep to give time to Taskmaster to run the operations - sleep(); - assertEquals(menuManager.currentMenuCells.size(), 3); List oldKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getOldStatus(), KEEP); List newKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getUpdatedStatus(), KEEP); @@ -424,6 +424,11 @@ public class MenuManagerTests { assertEquals(menuManager.currentMenuCells.size(), 4); + menuManager.setMenuCells(newMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + // this happens in the menu manager but lets make sure its behaving DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); @@ -434,11 +439,6 @@ public class MenuManagerTests { assertEquals(runScore.getOldStatus(), oldMenuStatus); assertEquals(runScore.getUpdatedStatus(), newMenuStatus); - menuManager.setMenuCells(newMenu); - - // Sleep to give time to Taskmaster to run the operations - sleep(); - assertEquals(menuManager.currentMenuCells.size(), 4); List oldKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getOldStatus(), KEEP); List newKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getUpdatedStatus(), KEEP); @@ -469,6 +469,11 @@ public class MenuManagerTests { assertEquals(menuManager.currentMenuCells.size(), 4); + menuManager.setMenuCells(newMenu); + + // Sleep to give time to Taskmaster to run the operations + sleep(); + // this happens in the menu manager but lets make sure its behaving DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); @@ -479,11 +484,6 @@ public class MenuManagerTests { assertEquals(runScore.getOldStatus(), oldMenuStatus); assertEquals(runScore.getUpdatedStatus(), newMenuStatus); - menuManager.setMenuCells(newMenu); - - // Sleep to give time to Taskmaster to run the operations - sleep(); - assertEquals(menuManager.currentMenuCells.size(), 4); List oldKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getOldStatus(), KEEP); List newKeeps = filterMenuCellsWithStatusList(menuManager.currentMenuCells, runScore.getUpdatedStatus(), KEEP); -- cgit v1.2.1 From af8ecdfb3e17968440c46348151cb3d3e0d95468 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 26 Jul 2021 15:54:18 -0400 Subject: Set default UniqueTitle value in MenuCell constructors --- .../main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index 362a11a38..6b6d9d2c5 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -122,6 +122,7 @@ public class MenuCell implements Cloneable { @Deprecated public MenuCell(@NonNull String title, @Nullable SdlArtwork icon, @Nullable List voiceCommands, @Nullable MenuSelectionListener listener) { setTitle(title); // title is the only required param + setUniqueTitle(title); setIcon(icon); setVoiceCommands(voiceCommands); setMenuSelectionListener(listener); @@ -142,6 +143,7 @@ public class MenuCell implements Cloneable { */ public MenuCell(@NonNull String title, @Nullable String secondaryText, @Nullable String tertiaryText, @Nullable SdlArtwork icon, @Nullable SdlArtwork secondaryArtwork, @Nullable List voiceCommands, @Nullable MenuSelectionListener listener) { setTitle(title); // title is the only required param + setUniqueTitle(title); setSecondaryText(secondaryText); setTertiaryText(tertiaryText); setIcon(icon); @@ -167,6 +169,7 @@ public class MenuCell implements Cloneable { @Deprecated public MenuCell(@NonNull String title, @Nullable MenuLayout subMenuLayout, @Nullable SdlArtwork icon, @Nullable List subCells) { setTitle(title); // title is the only required param + setUniqueTitle(title); setSubMenuLayout(subMenuLayout); setIcon(icon); setSubCells(subCells); @@ -189,6 +192,7 @@ public class MenuCell implements Cloneable { */ public MenuCell(@NonNull String title, @Nullable String secondaryText, @Nullable String tertiaryText, @Nullable MenuLayout subMenuLayout, @Nullable SdlArtwork icon, @Nullable SdlArtwork secondaryArtwork, @Nullable List subCells) { setTitle(title); // title is the only required param + setUniqueTitle(title); setSecondaryText(secondaryText); setTertiaryText(tertiaryText); setSubMenuLayout(subMenuLayout); -- cgit v1.2.1 From 1d96d84f44adaad00188332755efd46694da34c1 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 26 Jul 2021 15:55:30 -0400 Subject: Print errors map if something wrong happens --- .../managers/screen/menu/MenuReplaceOperation.java | 24 +++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index da6ed7288..f2c08a11c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -13,6 +13,8 @@ import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.util.DebugTool; +import org.json.JSONException; + import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -184,7 +186,7 @@ class MenuReplaceOperation extends Task { @Override public void onComplete(boolean success, Map errors) { if (!success) { - DebugTool.logWarning(TAG, "Unable to delete all old menu commands" + errors); + DebugTool.logWarning(TAG, "Unable to delete all old menu commands. " + convertErrorsMapToString(errors)); } else { DebugTool.logInfo(TAG, "Finished deleting old menu"); } @@ -231,7 +233,7 @@ class MenuReplaceOperation extends Task { @Override public void onComplete(boolean success, Map errors) { if (!success) { - DebugTool.logError(TAG, "Failed to send main menu commands" + errors); + DebugTool.logError(TAG, "Failed to send main menu commands. " + convertErrorsMapToString(errors)); listener.onComplete(false); return; } @@ -244,7 +246,7 @@ class MenuReplaceOperation extends Task { @Override public void onComplete(boolean success, Map errors) { if (!success) { - DebugTool.logError(TAG, "Failed to send sub menu commands" + errors); + DebugTool.logError(TAG, "Failed to send sub menu commands. " + convertErrorsMapToString(errors)); } else { DebugTool.logInfo(TAG, "Finished updating menu"); } @@ -351,6 +353,22 @@ class MenuReplaceOperation extends Task { } } + private String convertErrorsMapToString(Map errors) { + if (errors == null) { + return null; + } + StringBuilder stringBuilder = new StringBuilder(); + for (RPCRequest request : errors.keySet()) { + stringBuilder.append(errors.get(request)); + try { + stringBuilder.append(request.serializeJSON().toString(4)); + } catch (JSONException e) { + e.printStackTrace(); + } + } + return stringBuilder.toString(); + } + void setMenuConfiguration(MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } -- cgit v1.2.1 From bb898a0cbe7897e72352148e4f68b69dec9bd2f0 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 27 Jul 2021 15:33:56 -0400 Subject: Make cells take which properties are available into account for uniqueness --- .../managers/screen/menu/BaseMenuManager.java | 145 ++++++++++++++++----- 1 file changed, 114 insertions(+), 31 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 2a09498a7..ccb4dc1cf 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -50,9 +50,11 @@ import com.smartdevicelink.proxy.rpc.OnHMIStatus; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.DisplayType; import com.smartdevicelink.proxy.rpc.enums.HMILevel; +import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows; import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType; import com.smartdevicelink.proxy.rpc.enums.SystemContext; +import com.smartdevicelink.proxy.rpc.enums.TextFieldName; import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener; import com.smartdevicelink.util.DebugTool; @@ -62,6 +64,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; +import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; +import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.isSubMenuCell; abstract class BaseMenuManager extends BaseSubManager { @@ -168,16 +172,26 @@ abstract class BaseMenuManager extends BaseSubManager { * @param cells - the menu cells that are to be sent to the head unit, including their sub-cells. */ public void setMenuCells(@NonNull List cells) { - // If we're running on a connection < RPC 7.1, we need to de-duplicate cells because presenting them will fail if we have the same cell primary text. - if (cells != null && internalInterface.getSdlMsgVersion() != null && (internalInterface.getSdlMsgVersion().getMajorVersion() < 7 || (internalInterface.getSdlMsgVersion().getMajorVersion() == 7 && internalInterface.getSdlMsgVersion().getMinorVersion() == 0))) { - addUniqueNamesToCells(cells); - } - // Check for cell lists with completely duplicate information, or any duplicate voiceCommands and return if it fails (logs are in the called method). - if (cells != null && !menuCellsAreUnique(cells, new ArrayList())) { + if (cells != null && !menuCellsAreUnique(cloneMenuCellsList(cells), new ArrayList())) { return; } + // If we're running on a connection < RPC 7.1, we need to de-duplicate cells because presenting them will fail if we have the same cell primary text. + boolean duplicateTitlesNotSupported = cells != null && internalInterface.getSdlMsgVersion() != null && (internalInterface.getSdlMsgVersion().getMajorVersion() < 7 || (internalInterface.getSdlMsgVersion().getMajorVersion() == 7 && internalInterface.getSdlMsgVersion().getMinorVersion() == 0)); + if (duplicateTitlesNotSupported) { + addUniqueNamesToCellsWithDuplicatePrimaryText(cells); + } else { + // On > RPC 7.1, at this point all cells are unique when considering all properties, + // but we also need to check if any cells will _appear_ as duplicates when displayed on the screen. + // To check that, we'll remove properties from the set cells based on the system capabilities + // (we probably don't need to consider them changing between now and when they're actually sent to the HU unless the menu layout changes) + // and check for uniqueness again. Then we'll add unique identifiers to primary text if there are duplicates. + // Then we transfer the primary text identifiers back to the main cells and add those to an operation to be sent. + List strippedCellsClone = removeUnusedProperties(cells); + addUniqueNamesBasedOnStrippedCells(strippedCellsClone, cells); + } + // Create a deep copy of the list so future changes by developers don't affect the algorithm logic final List clonedCells = cloneMenuCellsList(cells); @@ -447,41 +461,25 @@ abstract class BaseMenuManager extends BaseSubManager { } } - private void addUniqueNamesToCells(List cells) { - HashMap titlesMap = new HashMap<>(); - - for (MenuCell cell : cells) { - String cellTitle = cell.getTitle(); - Integer counter = titlesMap.get(cellTitle); - - if (counter == null) { - titlesMap.put(cellTitle, 1); - cell.setUniqueTitle(cellTitle); - } else { - titlesMap.put(cellTitle, ++counter); - cell.setUniqueTitle(cellTitle + " (" + counter + ")"); - } - - if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { - addUniqueNamesToCells(cell.getSubCells()); - } - } - } - /** - Check for cell lists with completely duplicate information, or any duplicate voiceCommands - @param cells The cells you will be adding - @return Boolean that indicates whether menuCells are unique or not + * Check for cell lists with completely duplicate information, or any duplicate voiceCommands + * + * @param cells List of MenuCell's you will be adding + * @param allVoiceCommands List of String's for VoiceCommands (Used for recursive calls to check voiceCommands of the cells) + * @return Boolean that indicates whether menuCells are unique or not */ private boolean menuCellsAreUnique(List cells, ArrayList allVoiceCommands) { // Check all voice commands for identical items and check each list of cells for identical cells HashSet identicalCellsCheckSet = new HashSet<>(); for (MenuCell cell : cells) { + // We don't want the UniqueTitle to be considered in uniqueness check + cell.setUniqueTitle(null); + identicalCellsCheckSet.add(cell); // Recursively check the sub-cell lists to see if they are all unique as well. If anything is not, this will chain back up the list to return false. - if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + if (isSubMenuCell(cell) && cell.getSubCells().size() > 0) { boolean subCellsAreUnique = menuCellsAreUnique(cell.getSubCells(), allVoiceCommands); if (!subCellsAreUnique) { @@ -511,4 +509,89 @@ abstract class BaseMenuManager extends BaseSubManager { return true; } + + private void addUniqueNamesToCellsWithDuplicatePrimaryText(List cells) { + HashMap countsMap = new HashMap<>(); + + for (MenuCell cell : cells) { + String cellTitle = cell.getTitle(); + Integer counter = countsMap.get(cellTitle); + + if (counter != null) { + countsMap.put(cellTitle, ++counter); + cell.setUniqueTitle(cellTitle + " (" + counter + ")"); + } else { + countsMap.put(cellTitle, 1); + cell.setUniqueTitle(cellTitle); + } + + if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + addUniqueNamesToCellsWithDuplicatePrimaryText(cell.getSubCells()); + } + } + } + + void addUniqueNamesBasedOnStrippedCells(List strippedCells, List originalCells) { + if (strippedCells == null || originalCells == null || strippedCells.size() != originalCells.size()) { + return; + } + // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary + HashMap countsMap = new HashMap<>(); + for (int i = 0; i < strippedCells.size(); i++) { + MenuCell cell = strippedCells.get(i); + Integer counter = countsMap.get(cell); + if (counter != null) { + countsMap.put(cell, ++counter); + originalCells.get(i).setUniqueTitle(originalCells.get(i).getTitle() + " (" + counter + ")"); + } else { + countsMap.put(cell, 1); + originalCells.get(i).setUniqueTitle(originalCells.get(i).getTitle()); + } + + if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + addUniqueNamesBasedOnStrippedCells(cell.getSubCells(), originalCells.get(i).getSubCells()); + } + } + } + + List removeUnusedProperties(List cells) { + if (cells == null) { + return null; + } + List removePropertiesClone = cloneMenuCellsList(cells); + for (MenuCell cell : removePropertiesClone) { + // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI + cell.setVoiceCommands(null); + + // Don't check ImageFieldName.subMenuIcon because it was added in 7.0 when the feature was added in 5.0. + // Just assume that if cmdIcon is not available, the submenu icon is not either. + if (!hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { + cell.setIcon(null); + } + // Check for subMenu fields supported + if (isSubMenuCell(cell)) { + if (!hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuSecondaryText)) { + cell.setSecondaryText(null); + } + if (!hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuTertiaryText)) { + cell.setTertiaryText(null); + } + if (!hasImageFieldOfName(windowCapability, ImageFieldName.menuSubMenuSecondaryImage)) { + cell.setSecondaryArtwork(null); + } + cell.setSubCells(removeUnusedProperties(cell.getSubCells())); + } else { + if (!hasTextFieldOfName(windowCapability, TextFieldName.menuCommandSecondaryText)) { + cell.setSecondaryText(null); + } + if (!hasTextFieldOfName(windowCapability, TextFieldName.menuCommandTertiaryText)) { + cell.setTertiaryText(null); + } + if (!hasImageFieldOfName(windowCapability, ImageFieldName.menuCommandSecondaryImage)) { + cell.setSecondaryArtwork(null); + } + } + } + return removePropertiesClone; + } } -- cgit v1.2.1 From b778150fe8f024f86a31143f708cb286fa8cc46c Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 27 Jul 2021 16:11:16 -0400 Subject: Dont take none visual fields into account when chechin uniqueness --- .../com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index ccb4dc1cf..6e5c8c779 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -473,8 +473,9 @@ abstract class BaseMenuManager extends BaseSubManager { HashSet identicalCellsCheckSet = new HashSet<>(); for (MenuCell cell : cells) { - // We don't want the UniqueTitle to be considered in uniqueness check + // We don't want the UniqueTitle & listener to be considered in uniqueness check cell.setUniqueTitle(null); + cell.setMenuSelectionListener(null); identicalCellsCheckSet.add(cell); @@ -562,6 +563,8 @@ abstract class BaseMenuManager extends BaseSubManager { for (MenuCell cell : removePropertiesClone) { // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI cell.setVoiceCommands(null); + cell.setUniqueTitle(null); + cell.setMenuSelectionListener(null); // Don't check ImageFieldName.subMenuIcon because it was added in 7.0 when the feature was added in 5.0. // Just assume that if cmdIcon is not available, the submenu icon is not either. -- cgit v1.2.1 From 65ea02ad346c85b36605b1ea46b095ef4f01cee5 Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 28 Jul 2021 10:31:48 -0400 Subject: Align to ios and fix unit tests --- .../PreloadPresentChoicesOperationTests.java | 97 ++-- .../screen/choiceset/BaseChoiceSetManager.java | 36 +- .../choiceset/PreloadPresentChoicesOperation.java | 531 ++++++++++++--------- 3 files changed, 389 insertions(+), 275 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java index bd13643a3..dbb983af9 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java @@ -66,6 +66,7 @@ public class PreloadPresentChoicesOperationTests { private PreloadPresentChoicesOperation presentChoicesOperation; private ChoiceSet choiceSet; private ISdl internalInterface; + private FileManager fileManager; private KeyboardListener keyboardListener; private ChoiceSetSelectionListener choiceSetSelectionListener; @@ -98,7 +99,7 @@ public class PreloadPresentChoicesOperationTests { windowCapability.setTextFields(Arrays.asList(textField, textField2, textField3)); internalInterface = mock(ISdl.class); - FileManager fileManager = mock(FileManager.class); + fileManager = mock(FileManager.class); // We still want the mock fileManager to use the real implementation for fileNeedsUpload() when(fileManager.fileNeedsUpload(any(SdlFile.class))).thenCallRealMethod(); @@ -137,7 +138,6 @@ public class PreloadPresentChoicesOperationTests { cellsToPreload.add(cell2); ISdl internalInterface = mock(ISdl.class); - FileManager fileManager = mock(FileManager.class); preloadChoicesOperationNullCapability = new PreloadPresentChoicesOperation(internalInterface, fileManager, null, null, true, cellsToPreload, null, null); } @@ -164,7 +164,6 @@ public class PreloadPresentChoicesOperationTests { windowCapability.setTextFields(Collections.singletonList(textField)); ISdl internalInterface = mock(ISdl.class); - FileManager fileManager = mock(FileManager.class); preloadChoicesOperationEmptyCapability = new PreloadPresentChoicesOperation(internalInterface, fileManager, null, windowCapability, true, cellsToPreload, null, null); } @@ -208,7 +207,9 @@ public class PreloadPresentChoicesOperationTests { public void testGetLayoutMode() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(7, 1)); // First we will check knowing our keyboard listener is NOT NULL - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + WindowCapability windowCapability = new WindowCapability(); + HashSet loadedCells = new HashSet<>(); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null); assertEquals(presentChoicesOperation.getLayoutMode(), LayoutMode.LIST_WITH_SEARCH); presentChoicesOperation.keyboardListener = null; @@ -218,7 +219,9 @@ public class PreloadPresentChoicesOperationTests { @Test public void testGetPerformInteraction() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(7, 1)); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + WindowCapability windowCapability = new WindowCapability(); + HashSet loadedCells = new HashSet<>(); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null); PerformInteraction pi = presentChoicesOperation.getPerformInteraction(); assertEquals(pi.getInitialText(), "Test"); @@ -233,7 +236,9 @@ public class PreloadPresentChoicesOperationTests { @Test public void testSetSelectedCellWithId() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(7, 1)); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + WindowCapability windowCapability = new WindowCapability(); + HashSet loadedCells = new HashSet<>(); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null); assertNull(presentChoicesOperation.selectedCellRow); presentChoicesOperation.setSelectedCellWithId(0); @@ -251,7 +256,21 @@ public class PreloadPresentChoicesOperationTests { @Test public void testCancelingChoiceSetSuccessfullyIfThreadIsRunning() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + WindowCapability windowCapability = new WindowCapability(); + HashSet loadedCells = new HashSet<>(); + PreloadChoicesCompletionListener listener = new PreloadChoicesCompletionListener() { + @Override + public void onComplete(boolean success, HashSet loadedChoiceCells) { + choiceSet.cancel(); + sleep(); + + verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class)); + verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); + + assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); + } + }; + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, listener); presentChoicesOperation.setLoadedCells(new HashSet()); queue.add(presentChoicesOperation, false); @@ -259,7 +278,6 @@ public class PreloadPresentChoicesOperationTests { assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); - choiceSet.cancel(); Answer cancelInteractionAnswer = new Answer() { @Override public Void answer(InvocationOnMock invocation) { @@ -277,24 +295,32 @@ public class PreloadPresentChoicesOperationTests { } }; doAnswer(cancelInteractionAnswer).when(internalInterface).sendRPC(any(CancelInteraction.class)); - - verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class)); - verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); - - assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); } @Test public void testCancelingChoiceSetUnsuccessfullyIfThreadIsRunning() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + WindowCapability windowCapability = new WindowCapability(); + HashSet loadedCells = new HashSet<>(); + PreloadChoicesCompletionListener listener = new PreloadChoicesCompletionListener() { + @Override + public void onComplete(boolean success, HashSet loadedChoiceCells) { + choiceSet.cancel(); + sleep(); + + verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class)); + verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); + + assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); + } + }; + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, listener); presentChoicesOperation.setLoadedCells(new HashSet()); queue.add(presentChoicesOperation, false); sleep(); assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); - choiceSet.cancel(); Answer cancelInteractionAnswer = new Answer() { @Override public Void answer(InvocationOnMock invocation) { @@ -312,17 +338,14 @@ public class PreloadPresentChoicesOperationTests { } }; doAnswer(cancelInteractionAnswer).when(internalInterface).sendRPC(any(CancelInteraction.class)); - - verify(internalInterface, times(1)).sendRPC(any(CancelInteraction.class)); - verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); - - assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); } @Test public void testCancelingChoiceSetIfThreadHasFinished() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER,null); + WindowCapability windowCapability = new WindowCapability(); + HashSet loadedCells = new HashSet<>(); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER,null, windowCapability, true, loadedCells, null); presentChoicesOperation.finishOperation(); assertEquals(Task.FINISHED, presentChoicesOperation.getState()); @@ -336,7 +359,9 @@ public class PreloadPresentChoicesOperationTests { @Test public void testCancelingChoiceSetIfThreadHasNotYetRun() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + WindowCapability windowCapability = new WindowCapability(); + HashSet loadedCells = new HashSet<>(); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null); assertEquals(Task.BLOCKED, presentChoicesOperation.getState()); @@ -357,24 +382,32 @@ public class PreloadPresentChoicesOperationTests { public void testCancelingChoiceSetIfHeadUnitDoesNotSupportFeature() { // Cancel Interaction is only supported on RPC specs v.6.0.0+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3)); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); - presentChoicesOperation.setLoadedCells(new HashSet()); - queue.add(presentChoicesOperation, false); - sleep(); - - assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); + WindowCapability windowCapability = new WindowCapability(); + HashSet loadedCells = new HashSet<>(); + PreloadChoicesCompletionListener listener = new PreloadChoicesCompletionListener() { + @Override + public void onComplete(boolean success, HashSet loadedChoiceCells) { + choiceSet.cancel(); + sleep(); - choiceSet.cancel(); + assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); - verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); - verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); + verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); + verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); + } + }; + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, listener); + presentChoicesOperation.setLoadedCells(new HashSet()); + queue.add(presentChoicesOperation, false); } @Test public void testCancelingChoiceSetIfHeadUnitDoesNotSupportFeatureButThreadIsNotRunning() { // Cancel Interaction is only supported on RPC specs v.6.0.0+ when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3)); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, choiceSet, InteractionMode.MANUAL_ONLY, null, null, choiceSetSelectionListener, TestValues.GENERAL_INTEGER, null); + WindowCapability windowCapability = new WindowCapability(); + HashSet loadedCells = new HashSet<>(); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null); assertEquals(Task.BLOCKED, presentChoicesOperation.getState()); 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 02172a47c..012cd816e 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 @@ -211,7 +211,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { updateIdsOnChoices(choicesToUpload); if (fileManager.get() != null) { - PreloadPresentChoicesOperation preloadChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, new PreloadChoicesCompletionListener() { + PreloadPresentChoicesOperation preloadChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, this.preloadedChoices, new PreloadChoicesCompletionListener() { @Override public void onComplete(boolean success, HashSet loadedCells) { preloadedChoices = loadedCells; @@ -223,7 +223,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { listener.onComplete(success); } } - }, this.preloadedChoices); + }); transactionQueue.add(preloadChoicesOperation, false); } else { @@ -322,17 +322,31 @@ abstract class BaseChoiceSetManager extends BaseSubManager { PreloadPresentChoicesOperation presentOp; - if (keyboardListener == null) { - // Non-searchable choice set - DebugTool.logInfo(TAG, "Creating non-searchable choice set"); - presentOp = new PreloadPresentChoicesOperation(internalInterface, choiceSet, mode, null, null, privateChoiceListener, getNextCancelId(), this.preloadedChoices); + PreloadChoicesCompletionListener listener = new PreloadChoicesCompletionListener() { + @Override + public void onComplete(boolean success, HashSet loadedChoiceCells) { + + } + }; + + if (fileManager.get() != null) { + if (keyboardListener == null) { + // Non-searchable choice set + DebugTool.logInfo(TAG, "Creating non-searchable choice set"); + // public PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, ChoiceSet choiceSet, InteractionMode mode, + // KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, Integer cancelID, String displayName, WindowCapability windowCapability, + // Boolean isVROptional, HashSet loadedCells, PreloadChoicesCompletionListener listener) { + presentOp = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), choiceSet, mode, null, null, getNextCancelId(), displayName, defaultMainWindowCapability, isVROptional, this.preloadedChoices, listener); + } else { + // Searchable choice set + DebugTool.logInfo(TAG, "Creating searchable choice set"); + presentOp = new PreloadPresentChoicesOperation(internalInterface, this.fileManager.get(), choiceSet, mode, keyboardConfiguration, keyboardListener, getNextCancelId(), displayName, defaultMainWindowCapability, isVROptional, this.preloadedChoices, listener); + } + + transactionQueue.add(presentOp, false); } else { - // Searchable choice set - DebugTool.logInfo(TAG, "Creating searchable choice set"); - presentOp = new PreloadPresentChoicesOperation(internalInterface, choiceSet, mode, keyboardConfiguration, keyboardListener, privateChoiceListener, getNextCancelId(), this.preloadedChoices); + DebugTool.logError(TAG, "File Manager was null in preload choice operation"); } - - transactionQueue.add(presentOp, false); } /** diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 83045b809..42fd883e2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -60,7 +60,6 @@ public class PreloadPresentChoicesOperation extends Task { private final String displayName; private final ArrayList cellsToUpload; private final PreloadChoicesCompletionListener completionListener; - private boolean isRunning; private final boolean isVROptional; private boolean choiceError = false; private HashSet loadedCells; @@ -73,13 +72,23 @@ public class PreloadPresentChoicesOperation extends Task { private TriggerSource selectedTriggerSource; private boolean updatedKeyboardProperties; private OnRPCNotificationListener keyboardRPCListener; - private final ChoiceSetSelectionListener choiceSetSelectionListener; Integer selectedCellRow; KeyboardListener keyboardListener; final SdlMsgVersion sdlMsgVersion; + private enum SDLPreloadPresentChoicesOperationState { + NOT_STARTED, + UPLOADING_IMAGES, + UPLOADING_CHOICES, + UPDATING_KEYBOARD_PROPERTIES, + PRESENTING_CHOICES, + CANCELLING_PRESENT_CHOICES, + RESETTING_KEYBOARD_PROPERTIES, + FINISHING + } + private SDLPreloadPresentChoicesOperationState currentState; public PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, String displayName, WindowCapability defaultWindowCapability, - Boolean isVROptional, LinkedHashSet cellsToPreload, PreloadChoicesCompletionListener listener, HashSet loadedCells) { + Boolean isVROptional, LinkedHashSet cellsToPreload, HashSet loadedCells, PreloadChoicesCompletionListener listener) { super("PreloadPresentChoiceOperation"); this.opType = OperationType.PRELOAD; this.internalInterface = new WeakReference<>(internalInterface); @@ -96,13 +105,14 @@ public class PreloadPresentChoicesOperation extends Task { this.originalKeyboardProperties = null; this.keyboardProperties = null; this.selectedCellRow = null; - this.choiceSetSelectionListener = null; this.sdlMsgVersion = internalInterface.getSdlMsgVersion(); this.loadedCells = loadedCells; + this.currentState = SDLPreloadPresentChoicesOperationState.NOT_STARTED; } - public PreloadPresentChoicesOperation(ISdl internalInterface, ChoiceSet choiceSet, InteractionMode mode, - KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, ChoiceSetSelectionListener choiceSetSelectionListener, Integer cancelID, HashSet loadedCells) { + public PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, ChoiceSet choiceSet, InteractionMode mode, + KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, Integer cancelID, String displayName, WindowCapability windowCapability, + Boolean isVROptional, HashSet loadedCells, PreloadChoicesCompletionListener listener) { super("PreloadPresentChoiceOperation"); this.opType = OperationType.PRESENT; this.internalInterface = new WeakReference<>(internalInterface); @@ -119,45 +129,80 @@ public class PreloadPresentChoicesOperation extends Task { this.originalKeyboardProperties = originalKeyboardProperties; this.keyboardProperties = originalKeyboardProperties; this.selectedCellRow = null; - this.choiceSetSelectionListener = choiceSetSelectionListener; this.sdlMsgVersion = internalInterface.getSdlMsgVersion(); - this.fileManager = null; - this.displayName = null; - this.defaultMainWindowCapability = null; - this.isVROptional = true; + this.fileManager = new WeakReference<>(fileManager); + this.displayName = displayName; + this.defaultMainWindowCapability = windowCapability; + this.isVROptional = isVROptional; this.cellsToUpload = null; - this.completionListener = null; + this.completionListener = listener; this.loadedCells = loadedCells; + this.currentState = SDLPreloadPresentChoicesOperationState.NOT_STARTED; } @Override public void onExecute() { - if (this.opType == OperationType.PRELOAD) { - DebugTool.logInfo(TAG, "Choice Operation: Executing preload choices operation"); - updateCellsBasedOnLoadedChoices(); - preloadCellArtworks(new CompletionListener() { - @Override - public void onComplete(boolean success) { - preloadCells(); - } - }); - } else if(this.opType == OperationType.PRESENT) { - DebugTool.logInfo(TAG, "Choice Operation: Executing present choice set operation"); - addListeners(); - start(); + if (this.getState() == CANCELED) { + return; } + DebugTool.logInfo(TAG, "Choice Operation: Executing preload choices operation"); + // Enforce unique cells and remove cells that are already loaded + updateCellsBasedOnLoadedChoices(); + // Start uploading cell artworks, then cells themselves, then determine if we want to present, then update keyboard properties if necessary, then present the choice set, then revert keyboard properties if necessary + preloadCellArtworks(new CompletionListener() { + @Override + public void onComplete(boolean success) { + // If some artworks failed to upload, we are still going to try to load the cells + if (getState()==CANCELED || !success) { + finishOperation(); + } + preloadCells(new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (getState()==CANCELED || !success) { + finishOperation(); + } + + if (choiceSet == null) { + finishOperation(); + } + DebugTool.logInfo(TAG, "Choice Operation: Executing present choice set operation"); + updateKeyboardProperties(new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (getState()==CANCELED || !success) { + finishOperation(); + } + presentChoiceSet(new CompletionListener() { + @Override + public void onComplete(boolean success) { + resetKeyboardProperties(new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (!success) { + finishOperation(); + } + } + }); + } + }); + } + }); + } + }); + } + }); } //PRELOAD OPERATION METHODS private void preloadCellArtworks(@NonNull final CompletionListener listener) { - isRunning = true; + this.currentState = SDLPreloadPresentChoicesOperationState.UPLOADING_IMAGES; List artworksToUpload = artworksToUpload(); if (artworksToUpload.size() == 0) { DebugTool.logInfo(TAG, "Choice Preload: No Choice Artworks to upload"); listener.onComplete(true); - isRunning = false; return; } @@ -168,23 +213,21 @@ public class PreloadPresentChoicesOperation extends Task { if (errors != null && errors.size() > 0) { DebugTool.logError(TAG, "Error uploading choice cell Artworks: " + errors.toString()); listener.onComplete(false); - isRunning = false; } else { DebugTool.logInfo(TAG, "Choice Artworks Uploaded"); listener.onComplete(true); - isRunning = false; } } }); } else { DebugTool.logError(TAG, "File manager is null in choice preload operation"); listener.onComplete(false); - isRunning = false; } } - private void preloadCells() { - isRunning = true; + private void preloadCells(@NonNull final CompletionListener listener) { + this.currentState = SDLPreloadPresentChoicesOperationState.UPLOADING_CHOICES; + List choiceRPCs = new ArrayList<>(cellsToUpload.size()); for (ChoiceCell cell : cellsToUpload) { CreateInteractionChoiceSet csCell = choiceFromCell(cell); @@ -195,8 +238,7 @@ public class PreloadPresentChoicesOperation extends Task { if (choiceRPCs.size() == 0) { DebugTool.logError(TAG, " All Choice cells to send are null, so the choice set will not be shown"); - completionListener.onComplete(true, loadedCells); - isRunning = false; + listener.onComplete(false); return; } @@ -209,11 +251,9 @@ public class PreloadPresentChoicesOperation extends Task { @Override public void onFinished() { - isRunning = false; DebugTool.logInfo(TAG, "Finished pre loading choice cells"); - completionListener.onComplete(!choiceError, loadedCells); + listener.onComplete(!choiceError); choiceError = false; - PreloadPresentChoicesOperation.super.onFinished(); } @Override @@ -228,63 +268,196 @@ public class PreloadPresentChoicesOperation extends Task { }); } else { DebugTool.logError(TAG, "Internal Interface null in preload choice operation"); - isRunning = false; - completionListener.onComplete(false, loadedCells); + listener.onComplete(!choiceError); } } - boolean shouldSendChoiceText() { - if (this.displayName != null && this.displayName.equals(DisplayType.GEN3_8_INCH.toString())) { - return true; + private void updateKeyboardProperties(final CompletionListener listener) { + this.currentState = SDLPreloadPresentChoicesOperationState.UPDATING_KEYBOARD_PROPERTIES; + if (keyboardListener == null) { + if (listener != null) { + listener.onComplete(false); + } + return; } - return templateSupportsTextField(TextFieldName.menuName); - } - boolean shouldSendChoiceSecondaryText() { - return templateSupportsTextField(TextFieldName.secondaryText); - } + addListeners(); - boolean shouldSendChoiceTertiaryText() { - return templateSupportsTextField(TextFieldName.tertiaryText); - } + if (keyboardListener != null && choiceSet.getCustomKeyboardConfiguration() != null) { + keyboardProperties = choiceSet.getCustomKeyboardConfiguration(); + updatedKeyboardProperties = true; + } - boolean shouldSendChoicePrimaryImage() { - return templateSupportsImageField(ImageFieldName.choiceImage); - } + SetGlobalProperties setGlobalProperties = new SetGlobalProperties(); + setGlobalProperties.setKeyboardProperties(keyboardProperties); + setGlobalProperties.setOnRPCResponseListener(new OnRPCResponseListener() { + @Override + public void onResponse(int correlationId, RPCResponse response) { - boolean shouldSendChoiceSecondaryImage() { - return templateSupportsImageField(ImageFieldName.choiceSecondaryImage); - } + if (!response.getSuccess()) { + if (listener != null) { + listener.onComplete(false); + } + DebugTool.logError(TAG, "Error Setting keyboard properties in present choice set operation"); + return; + } - boolean templateSupportsTextField(TextFieldName name) { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, name); - } + updatedKeyboardProperties = true; - boolean templateSupportsImageField(ImageFieldName name) { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, name); + if (listener != null) { + listener.onComplete(true); + } + DebugTool.logInfo(TAG, "Success Setting keyboard properties in present choice set operation"); + } + }); + if (internalInterface.get() != null) { + internalInterface.get().sendRPC(setGlobalProperties); + } else { + DebugTool.logError(TAG, "Internal interface null - present choice set op - choice"); + listener.onComplete(false); + } } - public void setLoadedCells(HashSet loadedCells) { - this.loadedCells = loadedCells; + void resetKeyboardProperties(final CompletionListener listener) { + this.currentState = SDLPreloadPresentChoicesOperationState.RESETTING_KEYBOARD_PROPERTIES; + if (this.keyboardListener == null || this.originalKeyboardProperties == null) { + if(listener != null) { + listener.onComplete(false); + return; + } + } + SetGlobalProperties setProperties = new SetGlobalProperties(); + setProperties.setKeyboardProperties(this.originalKeyboardProperties); + setProperties.setOnRPCResponseListener(new OnRPCResponseListener() { + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + updatedKeyboardProperties = false; + DebugTool.logInfo(TAG, "Successfully reset choice keyboard properties to original config"); + } else { + DebugTool.logError(TAG, "Failed to reset choice keyboard properties to original config " + response.getResultCode() + ", " + response.getInfo()); + } + if (listener != null) { + listener.onComplete(response.getSuccess()); + } + } + }); + + if (internalInterface.get() != null) { + internalInterface.get().sendRPC(setProperties); + internalInterface.get().removeOnRPCNotificationListener(FunctionID.ON_KEYBOARD_INPUT, keyboardRPCListener); + } else { + DebugTool.logError(TAG, "Internal Interface null when finishing choice keyboard reset"); + listener.onComplete(false); + } } - public HashSet getLoadedCells() { - return this.loadedCells; + private void presentChoiceSet(final CompletionListener listener) { + this.currentState = SDLPreloadPresentChoicesOperationState.PRESENTING_CHOICES; + PerformInteraction pi = getPerformInteraction(); + pi.setOnRPCResponseListener(new OnRPCResponseListener() { + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (!response.getSuccess()) { + DebugTool.logError(TAG, "Presenting Choice set failed: " + response.getInfo()); + + if (listener != null) { + listener.onComplete(false); + } + finishOperation(); + return; + } + + PerformInteractionResponse performInteractionResponse = (PerformInteractionResponse) response; + setSelectedCellWithId(performInteractionResponse.getChoiceID()); + selectedTriggerSource = performInteractionResponse.getTriggerSource(); + + if (listener != null && selectedCell != null && selectedTriggerSource != null && selectedCellRow != null) { + listener.onComplete(true); + } + } + }); + if (internalInterface.get() != null) { + internalInterface.get().sendRPC(pi); + } else { + DebugTool.logError(TAG, "Internal Interface null when presenting choice set in operation"); + listener.onComplete(false); + } } - List artworksToUpload() { - List artworksToUpload = new ArrayList<>(); - for (ChoiceCell cell : cellsToUpload) { - if (shouldSendChoicePrimaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getArtwork())) { - artworksToUpload.add(cell.getArtwork()); + private void cancelInteraction() { + 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 (this.currentState != SDLPreloadPresentChoicesOperationState.PRESENTING_CHOICES) { + DebugTool.logInfo(TAG, "Canceling the operation before a present."); + this.cancelTask(); + return; + }else if (sdlMsgVersion.getMajorVersion() < 6) { + DebugTool.logWarning(TAG, "Canceling a presented choice set is not supported on this head unit"); + this.cancelTask(); + return; } - if (shouldSendChoiceSecondaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork())) { - artworksToUpload.add(cell.getSecondaryArtwork()); + + DebugTool.logInfo(TAG, "Canceling the presented choice set interaction."); + + CancelInteraction cancelInteraction = new CancelInteraction(FunctionID.PERFORM_INTERACTION.getId(), cancelID); + cancelInteraction.setOnRPCResponseListener(new OnRPCResponseListener() { + @Override + public void onResponse(int correlationId, RPCResponse response) { + if (response.getSuccess()) { + DebugTool.logInfo(TAG, "Canceled the presented choice set " + ((response.getResultCode() == Result.SUCCESS) ? "successfully" : "unsuccessfully")); + } else { + DebugTool.logError(TAG, "Error canceling the presented choice set: " + correlationId + " with error: " + response.getInfo()); + } + } + }); + + if (internalInterface.get() != null) { + internalInterface.get().sendRPC(cancelInteraction); + } else { + DebugTool.logError(TAG, "Internal interface null - could not send cancel interaction for choice set"); + } + } else { + DebugTool.logInfo(TAG, "Canceling a choice set that has not yet been sent to Core"); + this.cancelTask(); + } + } + + //Present Helpers + void setSelectedCellWithId(Integer cellId) { + if (choiceSet.getChoices() != null && cellId != null) { + List cells = choiceSet.getChoices(); + for (int i = 0; i < cells.size(); i++) { + if (cells.get(i).getChoiceId() == cellId) { + selectedCell = cells.get(i); + selectedCellRow = i; + return; + } } } - return artworksToUpload; } + PerformInteraction getPerformInteraction() { + if (this.choiceSet == null) { + return new PerformInteraction(); + } + PerformInteraction pi = new PerformInteraction(choiceSet.getTitle(), presentationMode, getChoiceIds()); + pi.setInitialPrompt(choiceSet.getInitialPrompt()); + pi.setHelpPrompt(choiceSet.getHelpPrompt()); + pi.setTimeoutPrompt(choiceSet.getTimeoutPrompt()); + pi.setVrHelp(choiceSet.getVrHelpList()); + pi.setTimeout(choiceSet.getTimeout() * 1000); + pi.setInteractionLayout(getLayoutMode()); + pi.setCancelID(cancelID); + return pi; + } + + //Choice Uniqueness void updateCellsBasedOnLoadedChoices() { if (internalInterface.get().getProtocolVersion().getMajor() >= 7 && internalInterface.get().getProtocolVersion().getMinor() >= 1) { addUniqueNamesToCells(cellsToUpload); @@ -382,6 +555,17 @@ public class PreloadPresentChoicesOperation extends Task { } } + //Finding Cells + private ChoiceCell cellFromChoiceId(int choiceId) { + for (ChoiceCell cell : this.cellsToUpload) { + if (cell.getChoiceId() == choiceId) { + return cell; + } + } + return null; + } + + //Assembling Choice RPCs private CreateInteractionChoiceSet choiceFromCell(ChoiceCell cell) { List vrCommands; @@ -422,49 +606,30 @@ public class PreloadPresentChoicesOperation extends Task { return new CreateInteractionChoiceSet(choice.getChoiceID(), Collections.singletonList(choice)); } - private ChoiceCell cellFromChoiceId(int choiceId) { - for (ChoiceCell cell : this.cellsToUpload) { - if (cell.getChoiceId() == choiceId) { - return cell; - } + boolean shouldSendChoiceText() { + if (this.displayName != null && this.displayName.equals(DisplayType.GEN3_8_INCH.toString())) { + return true; } - return null; + return templateSupportsTextField(TextFieldName.menuName); } - //PRESENT OPERATION METHODS - private void start() { - if (getState() == Task.CANCELED) { - finishOperation(); - return; - } - - HashSet choiceSetCells = new HashSet<>(this.choiceSet.getChoices()); - if (!choiceSetCells.containsAll(this.loadedCells)) { - this.choiceSetSelectionListener.onError("Choices not available for presentation"); - finishOperation(); - return; - } + boolean shouldSendChoiceSecondaryText() { + return templateSupportsTextField(TextFieldName.secondaryText); + } - updateChoiceSetChoicesId(); + boolean shouldSendChoiceTertiaryText() { + return templateSupportsTextField(TextFieldName.tertiaryText); + } - // Check if we're using a keyboard (searchable) choice set and setup keyboard properties if we need to - if (keyboardListener != null && choiceSet.getCustomKeyboardConfiguration() != null) { - keyboardProperties = choiceSet.getCustomKeyboardConfiguration(); - updatedKeyboardProperties = true; - } + boolean shouldSendChoicePrimaryImage() { + return templateSupportsImageField(ImageFieldName.choiceImage); + } - updateKeyboardProperties(new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (getState() == Task.CANCELED) { - finishOperation(); - return; - } - presentChoiceSet(); - } - }); + boolean shouldSendChoiceSecondaryImage() { + return templateSupportsImageField(ImageFieldName.choiceSecondaryImage); } + //SDL Notifications private void addListeners() { keyboardRPCListener = new OnRPCNotificationListener() { @@ -520,59 +685,33 @@ public class PreloadPresentChoicesOperation extends Task { } } - void updateChoiceSetChoicesId() { - for (ChoiceCell cell : this.choiceSet.getChoices()) { - for (ChoiceCell loadedCell : this.loadedCells) { - if (loadedCell.equals(cell)) { - cell.setChoiceId(loadedCell.getChoiceId()); - } - } - } + boolean templateSupportsTextField(TextFieldName name) { + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, name); } - private void presentChoiceSet() { - PerformInteraction pi = getPerformInteraction(); - pi.setOnRPCResponseListener(new OnRPCResponseListener() { - @Override - public void onResponse(int correlationId, RPCResponse response) { - if (!response.getSuccess()) { - DebugTool.logError(TAG, "Presenting Choice set failed: " + response.getInfo()); - - if (choiceSetSelectionListener != null) { - choiceSetSelectionListener.onError(response.getInfo()); - } - finishOperation(); - return; - } + boolean templateSupportsImageField(ImageFieldName name) { + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, name); + } - PerformInteractionResponse performInteractionResponse = (PerformInteractionResponse) response; - setSelectedCellWithId(performInteractionResponse.getChoiceID()); - selectedTriggerSource = performInteractionResponse.getTriggerSource(); + public void setLoadedCells(HashSet loadedCells) { + this.loadedCells = loadedCells; + } - if (choiceSetSelectionListener != null && selectedCell != null && selectedTriggerSource != null && selectedCellRow != null) { - choiceSetSelectionListener.onChoiceSelected(selectedCell, selectedTriggerSource, selectedCellRow); - } + public HashSet getLoadedCells() { + return this.loadedCells; + } - finishOperation(); + List artworksToUpload() { + List artworksToUpload = new ArrayList<>(); + for (ChoiceCell cell : cellsToUpload) { + if (shouldSendChoicePrimaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getArtwork())) { + artworksToUpload.add(cell.getArtwork()); + } + if (shouldSendChoiceSecondaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getSecondaryArtwork())) { + artworksToUpload.add(cell.getSecondaryArtwork()); } - }); - if (internalInterface.get() != null) { - internalInterface.get().sendRPC(pi); - } else { - DebugTool.logError(TAG, "Internal Interface null when presenting choice set in operation"); } - } - - PerformInteraction getPerformInteraction() { - PerformInteraction pi = new PerformInteraction(choiceSet.getTitle(), presentationMode, getChoiceIds()); - pi.setInitialPrompt(choiceSet.getInitialPrompt()); - pi.setHelpPrompt(choiceSet.getHelpPrompt()); - pi.setTimeoutPrompt(choiceSet.getTimeoutPrompt()); - pi.setVrHelp(choiceSet.getVrHelpList()); - pi.setTimeout(choiceSet.getTimeout() * 1000); - pi.setInteractionLayout(getLayoutMode()); - pi.setCancelID(cancelID); - return pi; + return artworksToUpload; } LayoutMode getLayoutMode() { @@ -594,90 +733,18 @@ public class PreloadPresentChoicesOperation extends Task { return choiceIds; } - void setSelectedCellWithId(Integer cellId) { - if (choiceSet.getChoices() != null && cellId != null) { - List cells = choiceSet.getChoices(); - for (int i = 0; i < cells.size(); i++) { - if (cells.get(i).getChoiceId() == cellId) { - selectedCell = cells.get(i); - selectedCellRow = i; - return; - } - } - } - } + void finishOperation() { + this.currentState = SDLPreloadPresentChoicesOperationState.FINISHING; - private void updateKeyboardProperties(final CompletionListener listener) { - if (keyboardProperties == null) { - if (listener != null) { - listener.onComplete(false); - } - return; + if (this.completionListener != null) { + this.completionListener.onComplete(false, loadedCells); } - SetGlobalProperties setGlobalProperties = new SetGlobalProperties(); - setGlobalProperties.setKeyboardProperties(keyboardProperties); - setGlobalProperties.setOnRPCResponseListener(new OnRPCResponseListener() { - @Override - public void onResponse(int correlationId, RPCResponse response) { - - if (!response.getSuccess()) { - if (listener != null) { - listener.onComplete(false); - } - DebugTool.logError(TAG, "Error Setting keyboard properties in present choice set operation"); - return; - } - - updatedKeyboardProperties = true; - if (listener != null) { - listener.onComplete(true); - } - DebugTool.logInfo(TAG, "Success Setting keyboard properties in present choice set operation"); - } - }); - if (internalInterface.get() != null) { - internalInterface.get().sendRPC(setGlobalProperties); - } else { - DebugTool.logError(TAG, "Internal interface null - present choice set op - choice"); + if (this.choiceSet == null || this.choiceSet.getChoiceSetSelectionListener() == null) { + DebugTool.logWarning(TAG, ""); } - } - - private void cancelInteraction() { - 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 (sdlMsgVersion.getMajorVersion() < 6) { - DebugTool.logWarning(TAG, "Canceling a presented choice set is not supported on this head unit"); - return; - } - DebugTool.logInfo(TAG, "Canceling the presented choice set interaction."); - - CancelInteraction cancelInteraction = new CancelInteraction(FunctionID.PERFORM_INTERACTION.getId(), cancelID); - cancelInteraction.setOnRPCResponseListener(new OnRPCResponseListener() { - @Override - public void onResponse(int correlationId, RPCResponse response) { - DebugTool.logInfo(TAG, "Canceled the presented choice set " + ((response.getResultCode() == Result.SUCCESS) ? "successfully" : "unsuccessfully")); - } - }); - if (internalInterface.get() != null) { - internalInterface.get().sendRPC(cancelInteraction); - } else { - DebugTool.logError(TAG, "Internal interface null - could not send cancel interaction for choice set"); - } - } else { - DebugTool.logInfo(TAG, "Canceling a choice set that has not yet been sent to Core"); - this.cancelTask(); - } - } - - void finishOperation() { if (updatedKeyboardProperties) { // We need to reset the keyboard properties SetGlobalProperties setGlobalProperties = new SetGlobalProperties(); -- cgit v1.2.1 From 6ae2d95d8b174ca7f884ffcc3546328642106571 Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 28 Jul 2021 11:14:06 -0400 Subject: Fix copyright --- .../java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 892af338b..d19bae619 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -18,7 +18,7 @@ * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPREFSS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * 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 -- cgit v1.2.1 From 5b1532c809b4c04ba99073bb5d6645cf854a42eb Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 28 Jul 2021 11:29:16 -0400 Subject: Add test for canceled state --- .../choiceset/PreloadPresentChoicesOperationTests.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java index dbb983af9..2e8906165 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java @@ -356,6 +356,22 @@ public class PreloadPresentChoicesOperationTests { assertEquals(Task.FINISHED, presentChoicesOperation.getState()); } + @Test + public void testCancelingChoiceSetIfThreadHasCanceled() { + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); + WindowCapability windowCapability = new WindowCapability(); + HashSet loadedCells = new HashSet<>(); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER,null, windowCapability, true, loadedCells, null); + presentChoicesOperation.cancelTask(); + + assertEquals(Task.CANCELED, presentChoicesOperation.getState()); + + choiceSet.cancel(); + verify(internalInterface, never()).sendRPC(any(CancelInteraction.class)); + + assertEquals(Task.CANCELED, presentChoicesOperation.getState()); + } + @Test public void testCancelingChoiceSetIfThreadHasNotYetRun() { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); -- cgit v1.2.1 From 9b9ad754e7d58783078d49d33af584ea14ac7a78 Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 28 Jul 2021 14:56:52 -0400 Subject: Fix some listeners and tests --- .../PreloadPresentChoicesOperationTests.java | 20 +++++++++--------- .../screen/choiceset/BaseChoiceSetManager.java | 13 +++--------- .../choiceset/PreloadPresentChoicesOperation.java | 24 +++++++++++++++++----- 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java index 2e8906165..1815f6abe 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java @@ -209,7 +209,7 @@ public class PreloadPresentChoicesOperationTests { // First we will check knowing our keyboard listener is NOT NULL WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null, null); assertEquals(presentChoicesOperation.getLayoutMode(), LayoutMode.LIST_WITH_SEARCH); presentChoicesOperation.keyboardListener = null; @@ -221,7 +221,7 @@ public class PreloadPresentChoicesOperationTests { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(7, 1)); WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null, null); PerformInteraction pi = presentChoicesOperation.getPerformInteraction(); assertEquals(pi.getInitialText(), "Test"); @@ -238,7 +238,7 @@ public class PreloadPresentChoicesOperationTests { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(7, 1)); WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, getKeyBoardProperties(), keyboardListener, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null, null); assertNull(presentChoicesOperation.selectedCellRow); presentChoicesOperation.setSelectedCellWithId(0); @@ -270,7 +270,7 @@ public class PreloadPresentChoicesOperationTests { assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); } }; - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, listener); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, listener, null); presentChoicesOperation.setLoadedCells(new HashSet()); queue.add(presentChoicesOperation, false); @@ -314,7 +314,7 @@ public class PreloadPresentChoicesOperationTests { assertEquals(Task.IN_PROGRESS, presentChoicesOperation.getState()); } }; - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, listener); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, listener, null); presentChoicesOperation.setLoadedCells(new HashSet()); queue.add(presentChoicesOperation, false); sleep(); @@ -345,7 +345,7 @@ public class PreloadPresentChoicesOperationTests { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER,null, windowCapability, true, loadedCells, null); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER,null, windowCapability, true, loadedCells, null, null); presentChoicesOperation.finishOperation(); assertEquals(Task.FINISHED, presentChoicesOperation.getState()); @@ -361,7 +361,7 @@ public class PreloadPresentChoicesOperationTests { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER,null, windowCapability, true, loadedCells, null); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER,null, windowCapability, true, loadedCells, null, null); presentChoicesOperation.cancelTask(); assertEquals(Task.CANCELED, presentChoicesOperation.getState()); @@ -377,7 +377,7 @@ public class PreloadPresentChoicesOperationTests { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null, null); assertEquals(Task.BLOCKED, presentChoicesOperation.getState()); @@ -412,7 +412,7 @@ public class PreloadPresentChoicesOperationTests { verify(internalInterface, times(1)).sendRPC(any(PerformInteraction.class)); } }; - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, listener); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, listener, null); presentChoicesOperation.setLoadedCells(new HashSet()); queue.add(presentChoicesOperation, false); } @@ -423,7 +423,7 @@ public class PreloadPresentChoicesOperationTests { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3)); WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null); + presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER, null, windowCapability, true, loadedCells, null, null); assertEquals(Task.BLOCKED, presentChoicesOperation.getState()); 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 012cd816e..81dc88f22 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 @@ -297,7 +297,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { } // Pass back the information to the developer - ChoiceSetSelectionListener privateChoiceListener = new ChoiceSetSelectionListener() { + ChoiceSetSelectionListener listener = new ChoiceSetSelectionListener() { @Override public void onChoiceSelected(ChoiceCell choiceCell, TriggerSource triggerSource, int rowIndex) { if (choiceSet != null && choiceSet.getChoiceSetSelectionListener() != null) { @@ -322,13 +322,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { PreloadPresentChoicesOperation presentOp; - PreloadChoicesCompletionListener listener = new PreloadChoicesCompletionListener() { - @Override - public void onComplete(boolean success, HashSet loadedChoiceCells) { - - } - }; - if (fileManager.get() != null) { if (keyboardListener == null) { // Non-searchable choice set @@ -336,11 +329,11 @@ abstract class BaseChoiceSetManager extends BaseSubManager { // public PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, ChoiceSet choiceSet, InteractionMode mode, // KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, Integer cancelID, String displayName, WindowCapability windowCapability, // Boolean isVROptional, HashSet loadedCells, PreloadChoicesCompletionListener listener) { - presentOp = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), choiceSet, mode, null, null, getNextCancelId(), displayName, defaultMainWindowCapability, isVROptional, this.preloadedChoices, listener); + presentOp = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), choiceSet, mode, null, null, getNextCancelId(), displayName, defaultMainWindowCapability, isVROptional, this.preloadedChoices, null, listener); } else { // Searchable choice set DebugTool.logInfo(TAG, "Creating searchable choice set"); - presentOp = new PreloadPresentChoicesOperation(internalInterface, this.fileManager.get(), choiceSet, mode, keyboardConfiguration, keyboardListener, getNextCancelId(), displayName, defaultMainWindowCapability, isVROptional, this.preloadedChoices, listener); + presentOp = new PreloadPresentChoicesOperation(internalInterface, this.fileManager.get(), choiceSet, mode, keyboardConfiguration, keyboardListener, getNextCancelId(), displayName, defaultMainWindowCapability, isVROptional, this.preloadedChoices, null, listener); } transactionQueue.add(presentOp, false); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 42fd883e2..0b91d7239 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -60,6 +60,7 @@ public class PreloadPresentChoicesOperation extends Task { private final String displayName; private final ArrayList cellsToUpload; private final PreloadChoicesCompletionListener completionListener; + private final ChoiceSetSelectionListener selectionListener; private final boolean isVROptional; private boolean choiceError = false; private HashSet loadedCells; @@ -108,11 +109,12 @@ public class PreloadPresentChoicesOperation extends Task { this.sdlMsgVersion = internalInterface.getSdlMsgVersion(); this.loadedCells = loadedCells; this.currentState = SDLPreloadPresentChoicesOperationState.NOT_STARTED; + this.selectionListener = null; } public PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, ChoiceSet choiceSet, InteractionMode mode, KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, Integer cancelID, String displayName, WindowCapability windowCapability, - Boolean isVROptional, HashSet loadedCells, PreloadChoicesCompletionListener listener) { + Boolean isVROptional, HashSet loadedCells, PreloadChoicesCompletionListener preloadListener, ChoiceSetSelectionListener listener) { super("PreloadPresentChoiceOperation"); this.opType = OperationType.PRESENT; this.internalInterface = new WeakReference<>(internalInterface); @@ -135,7 +137,8 @@ public class PreloadPresentChoicesOperation extends Task { this.defaultMainWindowCapability = windowCapability; this.isVROptional = isVROptional; this.cellsToUpload = null; - this.completionListener = listener; + this.completionListener = preloadListener; + this.selectionListener = listener; this.loadedCells = loadedCells; this.currentState = SDLPreloadPresentChoicesOperationState.NOT_STARTED; } @@ -361,6 +364,9 @@ public class PreloadPresentChoicesOperation extends Task { if (!response.getSuccess()) { DebugTool.logError(TAG, "Presenting Choice set failed: " + response.getInfo()); + if (selectionListener != null) { + selectionListener.onError(response.getInfo()); + } if (listener != null) { listener.onComplete(false); } @@ -372,8 +378,11 @@ public class PreloadPresentChoicesOperation extends Task { setSelectedCellWithId(performInteractionResponse.getChoiceID()); selectedTriggerSource = performInteractionResponse.getTriggerSource(); - if (listener != null && selectedCell != null && selectedTriggerSource != null && selectedCellRow != null) { - listener.onComplete(true); + if (selectionListener != null && selectedCell != null && selectedTriggerSource != null && selectedCellRow != null) { + selectionListener.onChoiceSelected(selectedCell, selectedTriggerSource, selectedCellRow); + if (listener != null) { + listener.onComplete(true); + } } } }); @@ -381,7 +390,12 @@ public class PreloadPresentChoicesOperation extends Task { internalInterface.get().sendRPC(pi); } else { DebugTool.logError(TAG, "Internal Interface null when presenting choice set in operation"); - listener.onComplete(false); + if (selectionListener != null) { + selectionListener.onError("Internal Interface null"); + } + if (listener != null) { + listener.onComplete(false); + } } } -- cgit v1.2.1 From 2a7f1fae427dec3c4aebb6e447d282ccf1afbcc4 Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 28 Jul 2021 15:56:12 -0400 Subject: Add unit tests --- .../screen/choiceset/ChoiceSetManagerTests.java | 54 ++++++++++++++++++++++ .../screen/choiceset/BaseChoiceSetManager.java | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java index cf1b71c38..55ca71074 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java @@ -38,6 +38,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.livio.taskmaster.Taskmaster; import com.smartdevicelink.managers.BaseSubManager; +import com.smartdevicelink.managers.CompletionListener; import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.managers.file.FileManager; import com.smartdevicelink.proxy.rpc.ImageField; @@ -224,6 +225,59 @@ public class ChoiceSetManagerTests { assertNotSame(cell3.getChoiceId(), 2000000000); } + @Test + public void preloadChoicesAddsToQueue() { + ChoiceCell cell1 = new ChoiceCell("test"); + ChoiceCell cell2 = new ChoiceCell("test2"); + ChoiceCell cell3 = new ChoiceCell("test3"); + ArrayList cellSet = new ArrayList<>(); + cellSet.add(cell1); + cellSet.add(cell2); + cellSet.add(cell3); + csm.preloadChoices(cellSet, new CompletionListener() { + @Override + public void onComplete(boolean success) { + + } + }); + assertEquals(csm.transactionQueue.getTasksAsList().size(), 1); + } + + @Test + public void preloadChoicesQueueEmptyWhenNoChoiceCells() { + ArrayList cellSet = new ArrayList<>(); + csm.preloadChoices(cellSet, new CompletionListener() { + @Override + public void onComplete(boolean success) { + + } + }); + assertEquals(csm.transactionQueue.getTasksAsList().size(), 0); + } + + @Test + public void testPreloadChoicesQueueEmptyIfFileManagerNull() { + ChoiceCell cell1 = new ChoiceCell("test"); + ChoiceCell cell2 = new ChoiceCell("test2"); + ChoiceCell cell3 = new ChoiceCell("test3"); + ArrayList cellSet = new ArrayList<>(); + cellSet.add(cell1); + cellSet.add(cell2); + cellSet.add(cell3); + + ISdl internalInterface = mock(ISdl.class); + when(internalInterface.getTaskmaster()).thenReturn(taskmaster); + FileManager fileManager = null; + ChoiceSetManager newCSM = new ChoiceSetManager(internalInterface, fileManager); + newCSM.preloadChoices(cellSet, new CompletionListener() { + @Override + public void onComplete(boolean success) { + + } + }); + assertEquals(csm.transactionQueue.getTasksAsList().size(), 0); + } + @Test public void testPresentingKeyboardShouldReturnCancelIDIfKeyboardCanBeSent() { ISdl internalInterface = mock(ISdl.class); 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 81dc88f22..d9c3f21cc 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 @@ -199,7 +199,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return; } - final LinkedHashSet choicesToUpload = (LinkedHashSet) choices; + final LinkedHashSet choicesToUpload = new LinkedHashSet<>(choices); if (choicesToUpload.size() == 0) { if (listener != null) { -- cgit v1.2.1 From 7fa4b85c99143df0b27a65a740c8b4017f3977a3 Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 28 Jul 2021 16:56:47 -0400 Subject: Fix manager and op constructors --- .../managers/screen/choiceset/BaseChoiceSetManager.java | 11 +---------- .../screen/choiceset/PreloadPresentChoicesOperation.java | 15 ++++++--------- 2 files changed, 7 insertions(+), 19 deletions(-) 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 d9c3f21cc..ebb423f96 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 @@ -278,16 +278,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { updateIdsOnChoiceSet(choiceSet); - preloadChoices(choiceSet.getChoices(), new CompletionListener() { - @Override - public void onComplete(boolean success) { - if (!success) { - choiceSet.getChoiceSetSelectionListener().onError("There was an error pre-loading choice set choices"); - } else { - sendPresentOperation(choiceSet, keyboardListener, mode); - } - } - }); + sendPresentOperation(choiceSet, keyboardListener, mode); } private void sendPresentOperation(final ChoiceSet choiceSet, KeyboardListener keyboardListener, InteractionMode mode) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 0b91d7239..acade567a 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -47,12 +47,6 @@ import java.util.Map; public class PreloadPresentChoicesOperation extends Task { - private enum OperationType{ - PRELOAD, - PRESENT - } - - private final OperationType opType; private static final String TAG = "PreloadPresentChoicesOperation"; private final WeakReference internalInterface; private final WeakReference fileManager; @@ -91,7 +85,6 @@ public class PreloadPresentChoicesOperation extends Task { public PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, String displayName, WindowCapability defaultWindowCapability, Boolean isVROptional, LinkedHashSet cellsToPreload, HashSet loadedCells, PreloadChoicesCompletionListener listener) { super("PreloadPresentChoiceOperation"); - this.opType = OperationType.PRELOAD; this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); this.displayName = displayName; @@ -116,7 +109,6 @@ public class PreloadPresentChoicesOperation extends Task { KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, Integer cancelID, String displayName, WindowCapability windowCapability, Boolean isVROptional, HashSet loadedCells, PreloadChoicesCompletionListener preloadListener, ChoiceSetSelectionListener listener) { super("PreloadPresentChoiceOperation"); - this.opType = OperationType.PRESENT; this.internalInterface = new WeakReference<>(internalInterface); this.keyboardListener = keyboardListener; this.choiceSet = choiceSet; @@ -136,7 +128,7 @@ public class PreloadPresentChoicesOperation extends Task { this.displayName = displayName; this.defaultMainWindowCapability = windowCapability; this.isVROptional = isVROptional; - this.cellsToUpload = null; + this.cellsToUpload = new ArrayList<>(choiceSet.getChoices()); this.completionListener = preloadListener; this.selectionListener = listener; this.loadedCells = loadedCells; @@ -158,16 +150,19 @@ public class PreloadPresentChoicesOperation extends Task { // If some artworks failed to upload, we are still going to try to load the cells if (getState()==CANCELED || !success) { finishOperation(); + return; } preloadCells(new CompletionListener() { @Override public void onComplete(boolean success) { if (getState()==CANCELED || !success) { finishOperation(); + return; } if (choiceSet == null) { finishOperation(); + return; } DebugTool.logInfo(TAG, "Choice Operation: Executing present choice set operation"); updateKeyboardProperties(new CompletionListener() { @@ -175,6 +170,7 @@ public class PreloadPresentChoicesOperation extends Task { public void onComplete(boolean success) { if (getState()==CANCELED || !success) { finishOperation(); + return; } presentChoiceSet(new CompletionListener() { @Override @@ -184,6 +180,7 @@ public class PreloadPresentChoicesOperation extends Task { public void onComplete(boolean success) { if (!success) { finishOperation(); + return; } } }); -- cgit v1.2.1 From 0cec04470f2169755c011669b757113b923e4d4d Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 29 Jul 2021 09:19:44 -0400 Subject: remove comment and fix issue with keyboard --- .../managers/screen/choiceset/BaseChoiceSetManager.java | 3 --- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) 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 ebb423f96..2dc841711 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 @@ -317,9 +317,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { if (keyboardListener == null) { // Non-searchable choice set DebugTool.logInfo(TAG, "Creating non-searchable choice set"); - // public PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, ChoiceSet choiceSet, InteractionMode mode, - // KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, Integer cancelID, String displayName, WindowCapability windowCapability, - // Boolean isVROptional, HashSet loadedCells, PreloadChoicesCompletionListener listener) { presentOp = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), choiceSet, mode, null, null, getNextCancelId(), displayName, defaultMainWindowCapability, isVROptional, this.preloadedChoices, null, listener); } else { // Searchable choice set diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index acade567a..77284df23 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -165,6 +165,7 @@ public class PreloadPresentChoicesOperation extends Task { return; } DebugTool.logInfo(TAG, "Choice Operation: Executing present choice set operation"); + updateKeyboardProperties(new CompletionListener() { @Override public void onComplete(boolean success) { @@ -276,7 +277,7 @@ public class PreloadPresentChoicesOperation extends Task { this.currentState = SDLPreloadPresentChoicesOperationState.UPDATING_KEYBOARD_PROPERTIES; if (keyboardListener == null) { if (listener != null) { - listener.onComplete(false); + listener.onComplete(true); } return; } -- cgit v1.2.1 From 4eb2a150f1ba38a632488938dbeb9ab08ab17156 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 29 Jul 2021 09:52:29 -0400 Subject: Fix completion listeners to prevent early finishOp --- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 77284df23..32a119e28 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -239,7 +239,7 @@ public class PreloadPresentChoicesOperation extends Task { if (choiceRPCs.size() == 0) { DebugTool.logError(TAG, " All Choice cells to send are null, so the choice set will not be shown"); - listener.onComplete(false); + listener.onComplete(true); return; } @@ -323,7 +323,7 @@ public class PreloadPresentChoicesOperation extends Task { this.currentState = SDLPreloadPresentChoicesOperationState.RESETTING_KEYBOARD_PROPERTIES; if (this.keyboardListener == null || this.originalKeyboardProperties == null) { if(listener != null) { - listener.onComplete(false); + listener.onComplete(true); return; } } -- cgit v1.2.1 From bfb0350fff4e3292eb4689a2a9e790a93fe20933 Mon Sep 17 00:00:00 2001 From: "Iryna Lytvynenko (GitHub)" Date: Tue, 3 Aug 2021 21:34:49 +0300 Subject: [SDL-0236] Update mismatch in TireStatus structure (#1707) * [0236] - Update TireStatus structure * [0236] - Update tests * [0236] - Rollback of CRLF formatting * [0236] - Fix pr comments * [0236] - Fix PR changes * [0236] - Fix Pr comments * [0236] 'tire status' - update rpc version from 7.1.0 to 8.0.0 * 0236- tire status: add @Nullable modifiers to the single tire status, and fix comments. * Return SingleTireStatus (UNKNOWN) instead of null Co-authored-by: kboskin Co-authored-by: leonid l lokhmatov Co-authored-by: Yaroslav Lutsenko (GitHub) Co-authored-by: Yaroslav Lutsenko (GitHub) <86004378+YaroslavLutsenko@users.noreply.github.com> --- .../managers/lifecycle/BaseLifecycleManager.java | 2 +- .../com/smartdevicelink/proxy/rpc/TireStatus.java | 320 ++++++++++++++++----- 2 files changed, 246 insertions(+), 76 deletions(-) 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 9cf72f7cd..2e4651d56 100644 --- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java @@ -104,7 +104,7 @@ import java.util.concurrent.CopyOnWriteArrayList; abstract class BaseLifecycleManager { static final String TAG = "Lifecycle Manager"; - public static final Version MAX_SUPPORTED_RPC_VERSION = new Version(7, 1, 0); + public static final Version MAX_SUPPORTED_RPC_VERSION = new Version(8, 0, 0); // Protected Correlation IDs private final int REGISTER_APP_INTERFACE_CORRELATION_ID = 65529, diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/TireStatus.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/TireStatus.java index 5db768cb5..68c47cf50 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/TireStatus.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/TireStatus.java @@ -34,82 +34,131 @@ package com.smartdevicelink.proxy.rpc; import androidx.annotation.NonNull; import com.smartdevicelink.proxy.RPCStruct; +import com.smartdevicelink.proxy.rpc.enums.ComponentVolumeStatus; import com.smartdevicelink.proxy.rpc.enums.WarningLightStatus; +import com.smartdevicelink.util.DebugTool; import java.util.Hashtable; /** - *

The status and pressure of the tires.

- *

Parameter List:

+ * The status and pressure of the tires. + * + *

Parameter List

* * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - * - *
Param NameTypeDescriptionVersion
PressureTellTaleWarningLightStatusStatus of the Tire Pressure TellTaleSmartDeviceLink 2.0
LeftFrontSingleTireStatusThe status of the left front tire.SmartDeviceLink 2.0
RightFrontSingleTireStatusThe status of the right front tire.SmartDeviceLink 2.0
LeftRearSingleTireStatusThe status of the left rear tire.SmartDeviceLink 2.0
RightRearSingleTireStatusThe status of the right rear tireSmartDeviceLink 2.0
InnerLeftRearSingleTireStatusThe status of the inner left rear tire.SmartDeviceLink 2.0
InnerRightRearSingleTireStatusThe status of the inner right rear tire.SmartDeviceLink 2.0
- *

- * @since SmartDeviceLink 2.0 + * + * Param Name + * Type + * Description + * Required + * Notes + * Version Available + * + * + * pressureTelltale + * WarningLightStatus + * Status of the Tire Pressure Telltale. See WarningLightStatus. + * N + * + * + * @since SmartDeviceLink 2.0.0 + * + * + * + * leftFront + * SingleTireStatus + * The status of the left front tire. + * N + * + * + * @since SmartDeviceLink 2.0.0 + * + * + * + * + * rightFront + * SingleTireStatus + * The status of the right front tire. + * N + * + * + * @since SmartDeviceLink 2.0.0 + * + * + * + * leftRear + * SingleTireStatus + * The status of the left rear tire. + * N + * + * + * @since SmartDeviceLink 2.0.0 + * + * + * + * rightRear + * SingleTireStatus + * The status of the right rear tire. + * N + * + * + * @since SmartDeviceLink 2.0.0 + * + * + * + * innerLeftRear + * SingleTireStatus + * The status of the inner left rear. + * N + * + * + * @since SmartDeviceLink 2.0.0 + * + * + * + * innerRightRear + * SingleTireStatus + * The status of the inner right rear. + * N + * + * + * @since SmartDeviceLink 2.0.0 + * + * + * * - * @see WarningLightStatus - * @see SingleTireStatus - * @see GetVehicleData - * @see OnVehicleData + * @since SmartDeviceLink 2.0.0 */ - public class TireStatus extends RPCStruct { + private static final String TAG = "TireStatus"; + /** + * @since SmartDeviceLink 2.0.0 + */ public static final String KEY_PRESSURE_TELL_TALE = "pressureTelltale"; + /** + * @since SmartDeviceLink 2.0.0 + */ public static final String KEY_LEFT_FRONT = "leftFront"; + /** + * @since SmartDeviceLink 2.0.0 + */ public static final String KEY_RIGHT_FRONT = "rightFront"; + /** + * @since SmartDeviceLink 2.0.0 + */ public static final String KEY_LEFT_REAR = "leftRear"; + /** + * @since SmartDeviceLink 2.0.0 + */ public static final String KEY_INNER_LEFT_REAR = "innerLeftRear"; + /** + * @since SmartDeviceLink 2.0.0 + */ public static final String KEY_INNER_RIGHT_REAR = "innerRightRear"; + /** + * @since SmartDeviceLink 2.0.0 + */ public static final String KEY_RIGHT_REAR = "rightRear"; - public TireStatus() { } @@ -125,7 +174,7 @@ public class TireStatus extends RPCStruct { /** * Constructs a new TireStatus object * - * @param pressureTellTale Status of the Tire Pressure TellTale + * @param pressureTelltale Status of the Tire Pressure TellTale * @param leftFront The status of the left front tire. * @param rightFront The status of the right front tire. * @param leftRear The status of the left rear tire. @@ -133,9 +182,9 @@ public class TireStatus extends RPCStruct { * @param innerLeftRear The status of the inner left rear tire. * @param innerRightRear The status of the inner right rear tire. */ - public TireStatus(@NonNull WarningLightStatus pressureTellTale, @NonNull SingleTireStatus leftFront, @NonNull SingleTireStatus rightFront, @NonNull SingleTireStatus leftRear, @NonNull SingleTireStatus rightRear, @NonNull SingleTireStatus innerLeftRear, @NonNull SingleTireStatus innerRightRear) { + public TireStatus(WarningLightStatus pressureTelltale, SingleTireStatus leftFront, SingleTireStatus rightFront, SingleTireStatus leftRear, SingleTireStatus rightRear, SingleTireStatus innerLeftRear, SingleTireStatus innerRightRear) { this(); - setPressureTelltale(pressureTellTale); + setPressureTelltale(pressureTelltale); setLeftFront(leftFront); setRightFront(rightFront); setLeftRear(leftRear); @@ -176,60 +225,181 @@ public class TireStatus extends RPCStruct { * @return the status of the tire pressure Telltale. */ public WarningLightStatus getPressureTelltale() { - return (WarningLightStatus) getObject(WarningLightStatus.class, KEY_PRESSURE_TELL_TALE); + WarningLightStatus warningLightStatus = (WarningLightStatus) getObject(WarningLightStatus.class, KEY_PRESSURE_TELL_TALE); + if (warningLightStatus == null) { + WarningLightStatus newWarningLightStatus = WarningLightStatus.NOT_USED; + setValue(KEY_PRESSURE_TELL_TALE, newWarningLightStatus); + warningLightStatus = newWarningLightStatus; + DebugTool.logWarning(TAG, "TireStatus.pressureTelltale was null and will be set to .notUsed. In the future, this will change to be nullable."); + } + return warningLightStatus; } - public TireStatus setLeftFront(@NonNull SingleTireStatus leftFront) { + /** + * Sets the leftFront. + * + * @param leftFront The status of the left front tire. + * @since SmartDeviceLink 2.0.0 + */ + public TireStatus setLeftFront(SingleTireStatus leftFront) { setValue(KEY_LEFT_FRONT, leftFront); return this; } + /** + * Gets the leftFront. + * + * @return SingleTireStatus The status of the left front tire. + * @since SmartDeviceLink 2.0.0 + */ public SingleTireStatus getLeftFront() { - return (SingleTireStatus) getObject(SingleTireStatus.class, KEY_LEFT_FRONT); + SingleTireStatus tireStatus = (SingleTireStatus) getObject(SingleTireStatus.class, KEY_LEFT_FRONT); + if (tireStatus == null) { + SingleTireStatus newTireStatus = new SingleTireStatus().setStatus(ComponentVolumeStatus.UNKNOWN); + setValue(KEY_LEFT_FRONT, newTireStatus); + tireStatus = newTireStatus; + DebugTool.logWarning(TAG, "TireStatus.leftFront was null and will be set to .unknown. In the future, this will change to be nullable."); + } + return tireStatus; } - public TireStatus setRightFront(@NonNull SingleTireStatus rightFront) { + /** + * Sets the rightFront. + * + * @param rightFront The status of the right front tire. + * @since SmartDeviceLink 2.0.0 + */ + public TireStatus setRightFront(SingleTireStatus rightFront) { setValue(KEY_RIGHT_FRONT, rightFront); return this; } + /** + * Gets the rightFront. + * + * @return SingleTireStatus The status of the right front tire. + * @since SmartDeviceLink 2.0.0 + */ public SingleTireStatus getRightFront() { - return (SingleTireStatus) getObject(SingleTireStatus.class, KEY_RIGHT_FRONT); + SingleTireStatus tireStatus = (SingleTireStatus) getObject(SingleTireStatus.class, KEY_RIGHT_FRONT); + if (tireStatus == null) { + SingleTireStatus newTireStatus = new SingleTireStatus().setStatus(ComponentVolumeStatus.UNKNOWN); + setValue(KEY_RIGHT_FRONT, newTireStatus); + tireStatus = newTireStatus; + DebugTool.logWarning(TAG, "TireStatus.rightFront was null and will be set to .unknown. In the future, this will change to be nullable."); + } + return tireStatus; } - public TireStatus setLeftRear(@NonNull SingleTireStatus leftRear) { + /** + * Sets the leftRear. + * + * @param leftRear The status of the left rear tire. + * @since SmartDeviceLink 2.0.0 + */ + public TireStatus setLeftRear(SingleTireStatus leftRear) { setValue(KEY_LEFT_REAR, leftRear); return this; } + /** + * Gets the leftRear. + * + * @return SingleTireStatus The status of the left rear tire. + * @since SmartDeviceLink 2.0.0 + */ public SingleTireStatus getLeftRear() { - return (SingleTireStatus) getObject(SingleTireStatus.class, KEY_LEFT_REAR); + SingleTireStatus tireStatus = (SingleTireStatus) getObject(SingleTireStatus.class, KEY_LEFT_REAR); + if (tireStatus == null) { + SingleTireStatus newTireStatus = new SingleTireStatus().setStatus(ComponentVolumeStatus.UNKNOWN); + setValue(KEY_LEFT_REAR, newTireStatus); + tireStatus = newTireStatus; + DebugTool.logWarning(TAG, "TireStatus.leftRear was null and will be set to .unknown. In the future, this will change to be nullable."); + } + return tireStatus; } - public TireStatus setRightRear(@NonNull SingleTireStatus rightRear) { + /** + * Sets the rightRear. + * + * @param rightRear The status of the right rear tire. + * @since SmartDeviceLink 2.0.0 + */ + public TireStatus setRightRear(SingleTireStatus rightRear) { setValue(KEY_RIGHT_REAR, rightRear); return this; } + /** + * Gets the rightRear. + * + * @return SingleTireStatus The status of the right rear tire. + * @since SmartDeviceLink 2.0.0 + */ public SingleTireStatus getRightRear() { - return (SingleTireStatus) getObject(SingleTireStatus.class, KEY_RIGHT_REAR); + SingleTireStatus tireStatus = (SingleTireStatus) getObject(SingleTireStatus.class, KEY_RIGHT_REAR); + if (tireStatus == null) { + SingleTireStatus newTireStatus = new SingleTireStatus().setStatus(ComponentVolumeStatus.UNKNOWN); + setValue(KEY_RIGHT_REAR, newTireStatus); + tireStatus = newTireStatus; + DebugTool.logWarning(TAG, "TireStatus.rightRear was null and will be set to .unknown. In the future, this will change to be nullable."); + } + return tireStatus; } - public TireStatus setInnerLeftRear(@NonNull SingleTireStatus innerLeftRear) { + /** + * Sets the innerLeftRear. + * + * @param innerLeftRear The status of the inner left rear. + * @since SmartDeviceLink 2.0.0 + */ + public TireStatus setInnerLeftRear(SingleTireStatus innerLeftRear) { setValue(KEY_INNER_LEFT_REAR, innerLeftRear); return this; } + /** + * Gets the innerLeftRear. + * + * @return SingleTireStatus The status of the inner left rear. + * @since SmartDeviceLink 2.0.0 + */ public SingleTireStatus getInnerLeftRear() { - return (SingleTireStatus) getObject(SingleTireStatus.class, KEY_INNER_LEFT_REAR); + SingleTireStatus tireStatus = (SingleTireStatus) getObject(SingleTireStatus.class, KEY_INNER_LEFT_REAR); + if (tireStatus == null) { + SingleTireStatus newTireStatus = new SingleTireStatus().setStatus(ComponentVolumeStatus.UNKNOWN); + setValue(KEY_INNER_LEFT_REAR, newTireStatus); + tireStatus = newTireStatus; + DebugTool.logWarning(TAG, "TireStatus.innerLeftRear was null and will be set to .unknown. In the future, this will change to be nullable."); + } + return tireStatus; } - public TireStatus setInnerRightRear(@NonNull SingleTireStatus innerRightRear) { + /** + * Sets the innerRightRear. + * + * @param innerRightRear The status of the inner right rear. + * @since SmartDeviceLink 2.0.0 + */ + public TireStatus setInnerRightRear(SingleTireStatus innerRightRear) { setValue(KEY_INNER_RIGHT_REAR, innerRightRear); return this; } + /** + * Gets the innerRightRear. + * + * @return SingleTireStatus The status of the inner right rear. + * @since SmartDeviceLink 2.0.0 + */ public SingleTireStatus getInnerRightRear() { - return (SingleTireStatus) getObject(SingleTireStatus.class, KEY_INNER_RIGHT_REAR); + SingleTireStatus tireStatus = (SingleTireStatus) getObject(SingleTireStatus.class, KEY_INNER_RIGHT_REAR); + if (tireStatus == null) { + SingleTireStatus newTireStatus = new SingleTireStatus().setStatus(ComponentVolumeStatus.UNKNOWN); + setValue(KEY_INNER_RIGHT_REAR, newTireStatus); + tireStatus = newTireStatus; + DebugTool.logWarning(TAG, "TireStatus.innerRightRear was null and will be set to .unknown. In the future, this will change to be nullable."); + } + return tireStatus; } } -- cgit v1.2.1 From 77edb104953af9944838a820a87518fa7400aac1 Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 4 Aug 2021 15:40:46 -0400 Subject: Add SecurityQuery and BinaryQueryHeader --- .../test/protocol/BinaryQueryHeaderTests.java | 118 +++++++++++++++++ .../managers/audio/AudioStreamManager.java | 2 +- .../managers/video/VideoStreamManager.java | 2 +- .../managers/lifecycle/BaseLifecycleManager.java | 2 +- .../smartdevicelink/protocol/BaseSdlPacket.java | 5 + .../protocol/BinaryQueryHeader.java | 139 +++++++++++++++++++++ .../smartdevicelink/protocol/ProtocolMessage.java | 5 + .../smartdevicelink/protocol/SdlPacketFactory.java | 10 ++ .../smartdevicelink/protocol/SdlProtocolBase.java | 19 ++- .../smartdevicelink/protocol/SecurityQuery.java | 102 +++++++++++++++ .../protocol/enums/QueryErrorCode.java | 58 +++++++++ .../smartdevicelink/protocol/enums/QueryID.java | 36 ++++++ .../smartdevicelink/protocol/enums/QueryType.java | 38 ++++++ .../smartdevicelink/session/BaseSdlSession.java | 23 +++- 14 files changed, 546 insertions(+), 13 deletions(-) create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java create mode 100644 base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java create mode 100644 base/src/main/java/com/smartdevicelink/protocol/SecurityQuery.java create mode 100644 base/src/main/java/com/smartdevicelink/protocol/enums/QueryErrorCode.java create mode 100644 base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java create mode 100644 base/src/main/java/com/smartdevicelink/protocol/enums/QueryType.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java new file mode 100644 index 000000000..33c567c01 --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java @@ -0,0 +1,118 @@ +package com.smartdevicelink.test.protocol; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.smartdevicelink.protocol.BinaryQueryHeader; +import com.smartdevicelink.protocol.enums.QueryID; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +@RunWith(AndroidJUnit4.class) +public class BinaryQueryHeaderTests { + + public static final byte QUERY_TYPE_REQUEST = 0x00; + public static final byte QUERY_TYPE_RESPONSE = 0x01; + public static final byte QUERY_TYPE_NOTIFICATION = 0x02; + + public static BinaryQueryHeader createDummyBqh() { + BinaryQueryHeader bqh = new BinaryQueryHeader(); + bqh.setCorrelationID(123); + bqh.setQueryID(QueryID.SEND_HANDSHAKE_DATA.getValue()); + bqh.setQueryType(QUERY_TYPE_REQUEST); + bqh.setBulkData(null); + bqh.setJsonSize(0); + return bqh; + } + + public BinaryQueryHeader safeParse(byte[] array) { + try { + return BinaryQueryHeader.parseBinaryQueryHeader(array); + } catch (Exception e) { + return null; + } + } + + @Test + public void testAssemblyAndParse() { + BinaryQueryHeader bqh = createDummyBqh(); + + byte[] bqhBytes = bqh.assembleHeaderBytes(); + assertNotNull(bqhBytes); + + BinaryQueryHeader parsedBqh = BinaryQueryHeader.parseBinaryQueryHeader(bqhBytes); + assertNotNull(parsedBqh); + + assertEquals(bqh.getCorrelationID(), parsedBqh.getCorrelationID()); + assertEquals(bqh.getQueryID(), parsedBqh.getQueryID()); + assertEquals(bqh.getQueryType(), parsedBqh.getQueryType()); + assertEquals(bqh.getBulkData(), parsedBqh.getBulkData()); + assertEquals(bqh.getJsonData(), parsedBqh.getJsonData()); + assertEquals(bqh.getJsonSize(), parsedBqh.getJsonSize()); + } + + @Test + public void testCorruptHeader() { + BinaryQueryHeader bqh = createDummyBqh(); + bqh.setJsonSize(5); + bqh.setJsonData(new byte[5]); + bqh.setJsonSize(Integer.MAX_VALUE); + + assertNotEquals(bqh.getJsonData().length, bqh.getJsonSize()); + + byte[] bqhBytes = bqh.assembleHeaderBytes(); + + assertNull(safeParse(bqhBytes)); + + int size = bqhBytes.length; + for (int i = 0; i < size; i++) { + bqhBytes[i] = (byte) 0x99; + } + + assertNull(safeParse(bqhBytes)); + BinaryQueryHeader head = BinaryQueryHeader.parseBinaryQueryHeader(bqhBytes); + assertNull(head); + } + + @Test + public void testErrorCodeAssemble() { + BinaryQueryHeader bqh = new BinaryQueryHeader(); + bqh.setCorrelationID(123); + bqh.setQueryID(QueryID.SEND_INTERNAL_ERROR.getValue()); + bqh.setQueryType(QUERY_TYPE_NOTIFICATION); + bqh.setBulkData(null); + bqh.setErrorCode(2); + bqh.setJsonSize(0); + + byte[] bqhBytes = bqh.assembleHeaderBytes(); + assertNotNull(bqhBytes); + + BinaryQueryHeader parsedBqh = BinaryQueryHeader.parseBinaryQueryHeader(bqhBytes); + assertNotNull(parsedBqh); + + assertEquals(bqh.getCorrelationID(), parsedBqh.getCorrelationID()); + assertEquals(bqh.getQueryID(), parsedBqh.getQueryID()); + assertEquals(bqh.getQueryType(), parsedBqh.getQueryType()); + assertEquals(bqh.getBulkData(), parsedBqh.getBulkData()); + assertEquals(bqh.getJsonData(), parsedBqh.getJsonData()); + assertEquals(bqh.getJsonSize(), parsedBqh.getJsonSize()); + assertEquals(bqh.getErrorCode(), parsedBqh.getErrorCode()); + } + + @Test + public void testJsonSetException() { + try { + BinaryQueryHeader bqh = createDummyBqh(); + bqh.setJsonData(null); + fail("Setting JSON data to null should have thrown an exception"); + } catch (Exception e) { + //Pass + } + } +} diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java index 667522372..c28401b07 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java @@ -530,7 +530,7 @@ public class AudioStreamManager extends BaseAudioStreamManager { IStreamListener streamListener = new IStreamListener() { @Override public void sendStreamPacket(ProtocolMessage pm) { - session.sendMessage(pm); + session.sendMessage(pm, null); } }; diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java index cce131baa..b3899c539 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java @@ -1065,7 +1065,7 @@ public class VideoStreamManager extends BaseVideoStreamManager { IStreamListener iStreamListener = new IStreamListener() { @Override public void sendStreamPacket(ProtocolMessage pm) { - session.sendMessage(pm); + session.sendMessage(pm, null); } }; 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 9cf72f7cd..8a783862b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java @@ -866,7 +866,7 @@ abstract class BaseLifecycleManager { pm.setPriorityCoefficient(1); } - session.sendMessage(pm); + session.sendMessage(pm, null); } catch (OutOfMemoryError e) { DebugTool.logError(TAG,"Error attempting to send RPC message.", e); diff --git a/base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java b/base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java index 9694d3f06..fdbba048d 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java +++ b/base/src/main/java/com/smartdevicelink/protocol/BaseSdlPacket.java @@ -264,10 +264,15 @@ class BaseSdlPacket { this.priorityCoefficient = priority; } + @Deprecated public int getPrioirtyCoefficient() { return this.priorityCoefficient; } + public int getPriorityCoefficient() { + return this.priorityCoefficient; + } + public void setTransportRecord(TransportRecord transportRecord) { this.transportRecord = transportRecord; } diff --git a/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java b/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java new file mode 100644 index 000000000..7a118a142 --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java @@ -0,0 +1,139 @@ +package com.smartdevicelink.protocol; + +import androidx.annotation.RestrictTo; + +import com.smartdevicelink.util.BitConverter; +import com.smartdevicelink.util.DebugTool; + +@RestrictTo(RestrictTo.Scope.LIBRARY) +public class BinaryQueryHeader { + private static final String TAG = "BinaryQueryHeader"; + + private byte _queryType; + private int _queryID; + private int _correlationID; + private int _jsonSize; + private int _errorCode; + + private byte[] _jsonData; + private byte[] _bulkData; + + public BinaryQueryHeader() { + } + + public static BinaryQueryHeader parseBinaryQueryHeader(byte[] binHeader) { + BinaryQueryHeader msg = new BinaryQueryHeader(); + + byte QUERY_Type = (byte) (binHeader[0] >>> 4); + msg.setQueryType(QUERY_Type); + + int _queryID = (BitConverter.intFromByteArray(binHeader, 0) & 0x0FFFFFFF); + msg.setQueryID(_queryID); + + int corrID = BitConverter.intFromByteArray(binHeader, 4); + msg.setCorrelationID(corrID); + + int _jsonSize = BitConverter.intFromByteArray(binHeader, 8); + msg.setJsonSize(_jsonSize); + + if (msg.getQueryType() == 0x20 && msg.getQueryID() == 0x02) { + int _errorCode = BitConverter.intFromByteArray(binHeader, binHeader.length-1); + msg.setErrorCode(_errorCode); + } + + try { + if (_jsonSize > 0) { + byte[] _jsonData = new byte[_jsonSize]; + System.arraycopy(binHeader, 12, _jsonData, 0, _jsonSize); + msg.setJsonData(_jsonData); + } + + if (binHeader.length - _jsonSize - 12 > 0) { + byte[] _bulkData; + if (msg.getQueryType() == 0x20 && msg.getQueryID() == 0x02) { + _bulkData = new byte[binHeader.length - _jsonSize - 12 - 1]; + System.arraycopy(binHeader, 12 + _jsonSize, _bulkData, 0, _bulkData.length - 1); + } else { + _bulkData = new byte[binHeader.length - _jsonSize - 12]; + System.arraycopy(binHeader, 12 + _jsonSize, _bulkData, 0, _bulkData.length); + } + msg.setBulkData(_bulkData); + } + + } catch (OutOfMemoryError | ArrayIndexOutOfBoundsException e) { + DebugTool.logError(TAG, "Unable to process data to form header"); + return null; + } + + return msg; + } + + public byte[] assembleHeaderBytes() { + int binHeader = _queryID; + binHeader &= 0xFFFFFFFF >>> 4; + binHeader |= (_queryType << 28); + + byte[] ret = new byte[12]; + System.arraycopy(BitConverter.intToByteArray(binHeader), 0, ret, 0, 4); + System.arraycopy(BitConverter.intToByteArray(_correlationID), 0, ret, 4, 4); + System.arraycopy(BitConverter.intToByteArray(_jsonSize), 0, ret, 8, 4); + return ret; + } + + public byte getQueryType() { + return _queryType; + } + + public void setQueryType(byte _queryType) { + this._queryType = _queryType; + } + + public int getQueryID() { + return _queryID; + } + + public void setQueryID(int _queryID) { + this._queryID = _queryID; + } + + public int getCorrelationID() { + return _correlationID; + } + + public void setCorrelationID(int _correlationID) { + this._correlationID = _correlationID; + } + + public int getJsonSize() { + return _jsonSize; + } + + public void setJsonSize(int _jsonSize) { + this._jsonSize = _jsonSize; + } + + public int getErrorCode() { + return _errorCode; + } + + public void setErrorCode(int _errorCode) { + this._errorCode = _errorCode; + } + + public byte[] getJsonData() { + return _jsonData; + } + + public void setJsonData(byte[] _jsonData) { + this._jsonData = new byte[this._jsonSize]; + System.arraycopy(_jsonData, 0, this._jsonData, 0, _jsonSize); + } + + public byte[] getBulkData() { + return _bulkData; + } + + public void setBulkData(byte[] _bulkData) { + this._bulkData = _bulkData; + } +} diff --git a/base/src/main/java/com/smartdevicelink/protocol/ProtocolMessage.java b/base/src/main/java/com/smartdevicelink/protocol/ProtocolMessage.java index 8a1968f36..9e2ab38d0 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/ProtocolMessage.java +++ b/base/src/main/java/com/smartdevicelink/protocol/ProtocolMessage.java @@ -182,7 +182,12 @@ public class ProtocolMessage { this.priorityCoefficient = priority; } + @Deprecated public int getPrioirtyCoefficient() { return this.priorityCoefficient; } + + public int getPriorityCoefficient() { + return this.priorityCoefficient; + } } // end-class \ No newline at end of file diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java b/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java index 364dc4e30..75d122da5 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java @@ -144,4 +144,14 @@ public class SdlPacketFactory { return msg; } + public static BinaryQueryHeader createBinaryQueryHeader(byte queryType, int queryId, int corrID, int jsonSize) { + BinaryQueryHeader msg = new BinaryQueryHeader(); + msg.setQueryType(queryType); + msg.setQueryID(queryId); + msg.setCorrelationID(corrID); + msg.setJsonSize(jsonSize); + + return msg; + } + } diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java index 5cac1302c..b1723dc6a 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java @@ -562,7 +562,7 @@ public class SdlProtocolBase { } } // end-method - public void sendMessage(ProtocolMessage protocolMsg) { + public void sendMessage(ProtocolMessage protocolMsg, SecurityQuery securityQuery) { SessionType sessionType = protocolMsg.getSessionType(); byte sessionID = protocolMsg.getSessionID(); boolean requiresEncryption = protocolMsg.getPayloadProtected(); @@ -571,12 +571,19 @@ public class SdlProtocolBase { if (protocolVersion.getMajor() > 1 && sessionType != SessionType.NAV && sessionType != SessionType.PCM) { if (sessionType.eq(SessionType.CONTROL)) { final byte[] secureData = protocolMsg.getData().clone(); - data = new byte[headerSize + secureData.length]; - final BinaryFrameHeader binFrameHeader = - SdlPacketFactory.createBinaryFrameHeader(protocolMsg.getRPCType(), protocolMsg.getFunctionID(), protocolMsg.getCorrID(), 0); - System.arraycopy(binFrameHeader.assembleHeaderBytes(), 0, data, 0, headerSize); - System.arraycopy(secureData, 0, data, headerSize, secureData.length); + if (securityQuery != null) { + data = new byte[12 + secureData.length]; + final BinaryQueryHeader binQueryHeader = SdlPacketFactory.createBinaryQueryHeader(securityQuery.getQueryType().getValue(), securityQuery.getQueryType().getValue(), securityQuery.getCorrelationId(), securityQuery.getJsonSize()); + System.arraycopy(binQueryHeader.assembleHeaderBytes(), 0, data, 0, 12); + System.arraycopy(secureData, 0, data, 12, secureData.length); + } else { + data = new byte[headerSize + secureData.length]; + final BinaryFrameHeader binFrameHeader = + SdlPacketFactory.createBinaryFrameHeader(protocolMsg.getRPCType(), protocolMsg.getFunctionID(), protocolMsg.getCorrID(), 0); + System.arraycopy(binFrameHeader.assembleHeaderBytes(), 0, data, 0, headerSize); + System.arraycopy(secureData, 0, data, headerSize, secureData.length); + } } else if (protocolMsg.getBulkData() != null) { data = new byte[12 + protocolMsg.getJsonSize() + protocolMsg.getBulkData().length]; sessionType = SessionType.BULK_DATA; diff --git a/base/src/main/java/com/smartdevicelink/protocol/SecurityQuery.java b/base/src/main/java/com/smartdevicelink/protocol/SecurityQuery.java new file mode 100644 index 000000000..e00a21137 --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/protocol/SecurityQuery.java @@ -0,0 +1,102 @@ +package com.smartdevicelink.protocol; + +import androidx.annotation.RestrictTo; + +import com.smartdevicelink.protocol.enums.QueryErrorCode; +import com.smartdevicelink.protocol.enums.QueryID; +import com.smartdevicelink.protocol.enums.QueryType; + +@RestrictTo(RestrictTo.Scope.LIBRARY) +public class SecurityQuery { + + private QueryType _queryType = QueryType.INVALID_QUERY_TYPE; + private QueryID _queryID = QueryID.INVALID_QUERY_ID; + private int _correlationId; + private int _jsonSize; + private QueryErrorCode _queryErrorCode = QueryErrorCode.ERROR_SUCCESS; + + private byte[] _data = null; + private byte[] _bulkData = null; + + public SecurityQuery() { + } + + public QueryType getQueryType() { + return this._queryType; + } + + public void setQueryType(QueryType queryType) { + this._queryType = queryType; + } + + public QueryID getQueryID() { + return this._queryID; + } + + public void setQueryID(QueryID queryID) { + this._queryID = queryID; + } + + public int getCorrelationId() { + return this._correlationId; + } + + public void setCorrelationId(int msgId) { + this._correlationId = msgId; + } + + public int getJsonSize() { + return this._jsonSize; + } + + public void setJsonSize(int jsonSize) { + this._jsonSize = jsonSize; + } + + public QueryErrorCode getQueryErrorCode() { + return this._queryErrorCode; + } + + public void setQueryErrorCode(QueryErrorCode queryErrorCode) { + this._queryErrorCode = queryErrorCode; + } + + public byte[] getData() { + return _data; + } + + public void setData(byte[] data) { + this._data = data; + this._jsonSize = data.length; + } + + public void setData(byte[] data, int offset, int length) { + if (this._data != null) + this._data = null; + this._data = new byte[length]; + System.arraycopy(data, offset, this._data, 0, length); + this._jsonSize = 0; + } + + public byte[] getBulkData() { + return _bulkData; + } + + public void setBulkDataNoCopy(byte[] bulkData) { + this._bulkData = bulkData; + } + + public void setBulkData(byte[] bulkData) { + if (this._bulkData != null) + this._bulkData = null; + this._bulkData = new byte[bulkData.length]; + System.arraycopy(bulkData, 0, this._bulkData, 0, bulkData.length); + } + + public void setBulkData(byte[] bulkData, int length) { + if (this._bulkData != null) + this._bulkData = null; + this._bulkData = new byte[length]; + System.arraycopy(bulkData, 0, this._bulkData, 0, length); + } +} diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryErrorCode.java b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryErrorCode.java new file mode 100644 index 000000000..269c5021e --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryErrorCode.java @@ -0,0 +1,58 @@ +package com.smartdevicelink.protocol.enums; + +import com.smartdevicelink.util.ByteEnumer; + +import java.util.Vector; + +public class QueryErrorCode extends ByteEnumer { + + private static final Vector theList = new Vector<>(); + + public static Vector getList() { + return theList; + } + + protected QueryErrorCode(byte value, String name) { + super(value, name); + } + + public final static QueryErrorCode ERROR_SUCCESS = new QueryErrorCode((byte) 0x00, "ERROR_SUCCESS"); + public final static QueryErrorCode ERROR_INVALID_QUERY_SIZE = new QueryErrorCode((byte) 0x01, "ERROR_INVALID_QUERY_SIZE"); + public final static QueryErrorCode ERROR_INVALID_QUERY_ID = new QueryErrorCode((byte) 0x02, "ERROR_INVALID_QUERY_ID"); + public final static QueryErrorCode ERROR_NOT_SUPPORTED = new QueryErrorCode((byte) 0x03, "ERROR_NOT_SUPPORTED"); + public final static QueryErrorCode ERROR_SERVICE_ALREADY_PROTECTED = new QueryErrorCode((byte) 0x04, "ERROR_SERVICE_ALREADY_PROTECTED"); + public final static QueryErrorCode ERROR_SERVICE_NOT_PROTECTED = new QueryErrorCode((byte) 0x05, "ERROR_SERVICE_NOT_PROTECTED"); + public final static QueryErrorCode ERROR_DECRYPTION_FAILED = new QueryErrorCode((byte) 0x06, "ERROR_DECRYPTION_FAILED"); + public final static QueryErrorCode ERROR_ENCRYPTION_FAILED = new QueryErrorCode((byte) 0x07, "ERROR_ENCRYPTION_FAILED"); + public final static QueryErrorCode ERROR_SSL_INVALID_DATA = new QueryErrorCode((byte) 0x08, "ERROR_SSL_INVALID_DATA"); + public final static QueryErrorCode ERROR_HANDSHAKE_FAILED = new QueryErrorCode((byte) 0x09, "ERROR_HANDSHAKE_FAILED"); + public final static QueryErrorCode INVALID_CERT = new QueryErrorCode((byte) 0x0A, "INVALID_CERT"); + public final static QueryErrorCode EXPIRED_CERT = new QueryErrorCode((byte) 0x0B, "EXPIRED_CERT"); + public final static QueryErrorCode ERROR_INTERNAL = new QueryErrorCode((byte) 0xFF, "ERROR_INTERNAL"); + public final static QueryErrorCode ERROR_UNKNOWN_INTERNAL_ERROR = new QueryErrorCode((byte) 0xFE, "ERROR_UNKNOWN_INTERNAL_ERROR"); + + static { + theList.addElement(ERROR_SUCCESS); + theList.addElement(ERROR_INVALID_QUERY_SIZE); + theList.addElement(ERROR_INVALID_QUERY_ID); + theList.addElement(ERROR_NOT_SUPPORTED); + theList.addElement(ERROR_SERVICE_ALREADY_PROTECTED); + theList.addElement(ERROR_SERVICE_NOT_PROTECTED); + theList.addElement(ERROR_DECRYPTION_FAILED); + theList.addElement(ERROR_ENCRYPTION_FAILED); + theList.addElement(ERROR_SSL_INVALID_DATA); + theList.addElement(ERROR_HANDSHAKE_FAILED); + theList.addElement(INVALID_CERT); + theList.addElement(EXPIRED_CERT); + theList.addElement(ERROR_INTERNAL); + theList.addElement(ERROR_UNKNOWN_INTERNAL_ERROR); + } + + public static QueryErrorCode valueOf(byte passedByte) { + return (QueryErrorCode) get(theList, passedByte); + } + + public static QueryErrorCode[] values() { + return theList.toArray(new QueryErrorCode[theList.size()]); + } +} diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java new file mode 100644 index 000000000..3668932d1 --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java @@ -0,0 +1,36 @@ +package com.smartdevicelink.protocol.enums; + +import com.smartdevicelink.util.ByteEnumer; + +import java.util.Vector; + +public class QueryID extends ByteEnumer { + + private static final Vector theList = new Vector<>(); + + public static Vector getList() { + return theList; + } + + protected QueryID(byte value, String name) { + super(value, name); + } + + public final static QueryID SEND_HANDSHAKE_DATA = new QueryID((byte) 0x01, "SEND_HANDSHAKE_DATA"); + public final static QueryID SEND_INTERNAL_ERROR = new QueryID((byte) 0x02, "SEND_INTERNAL_ERROR"); + public final static QueryID INVALID_QUERY_ID = new QueryID((byte) 0xFF, "INVALID_QUERY_ID"); + + static { + theList.addElement(SEND_HANDSHAKE_DATA); + theList.addElement(SEND_INTERNAL_ERROR); + theList.addElement(INVALID_QUERY_ID); + } + + public static QueryID valueOf(byte passedByte) { + return (QueryID) get(theList, passedByte); + } + + public static QueryID[] values() { + return theList.toArray(new QueryID[theList.size()]); + } +} diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryType.java b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryType.java new file mode 100644 index 000000000..543fb56c6 --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryType.java @@ -0,0 +1,38 @@ +package com.smartdevicelink.protocol.enums; + +import com.smartdevicelink.util.ByteEnumer; + +import java.util.Vector; + +public class QueryType extends ByteEnumer { + + private static final Vector theList = new Vector<>(); + + public static Vector getList() { + return theList; + } + + protected QueryType(byte value, String name) { + super(value, name); + } + + public final static QueryType REQUEST = new QueryType((byte) 0x00, "REQUEST"); + public final static QueryType RESPONSE = new QueryType((byte) 0x10, "RESPONSE"); + public final static QueryType NOTIFICATION = new QueryType((byte) 0x20, "NOTIFICATION"); + public final static QueryType INVALID_QUERY_TYPE = new QueryType((byte) 0xFF, "INVALID_QUERY_TYPE"); + + static { + theList.addElement(REQUEST); + theList.addElement(RESPONSE); + theList.addElement(NOTIFICATION); + theList.addElement(INVALID_QUERY_TYPE); + } + + public static QueryType valueOf(byte passedByte) { + return (QueryType) get(theList, passedByte); + } + + public static QueryType[] values() { + return theList.toArray(new QueryType[theList.size()]); + } +} \ No newline at end of file diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index 8b35541b4..b15411781 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -41,7 +41,10 @@ import com.smartdevicelink.protocol.ISdlServiceListener; import com.smartdevicelink.protocol.ProtocolMessage; import com.smartdevicelink.protocol.SdlPacket; import com.smartdevicelink.protocol.SdlProtocolBase; +import com.smartdevicelink.protocol.SecurityQuery; import com.smartdevicelink.protocol.enums.ControlFrameTags; +import com.smartdevicelink.protocol.enums.QueryID; +import com.smartdevicelink.protocol.enums.QueryType; import com.smartdevicelink.protocol.enums.SessionType; import com.smartdevicelink.proxy.RPCMessage; import com.smartdevicelink.proxy.rpc.VehicleType; @@ -147,11 +150,11 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ } - public void sendMessage(ProtocolMessage msg) { + public void sendMessage(ProtocolMessage msg, SecurityQuery securityQuery) { if (sdlProtocol == null) { return; } - sdlProtocol.sendMessage(msg); + sdlProtocol.sendMessage(msg, securityQuery); } public TransportType getCurrentTransportType() { @@ -193,13 +196,18 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ byte[] dataToRead = new byte[4096]; - Integer iNumBytes = sdlSecurity.runHandshake(data, dataToRead); + Integer iNumBytes = null; + + if (data[3] == QueryID.SEND_HANDSHAKE_DATA.getValue()) { + iNumBytes = sdlSecurity.runHandshake(data, dataToRead); + } if (iNumBytes == null || iNumBytes <= 0) return; byte[] returnBytes = new byte[iNumBytes]; System.arraycopy(dataToRead, 0, returnBytes, 0, iNumBytes); + ProtocolMessage protocolMessage = new ProtocolMessage(); protocolMessage.setSessionType(SessionType.CONTROL); protocolMessage.setData(returnBytes); @@ -207,9 +215,16 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ protocolMessage.setVersion((byte) sdlProtocol.getProtocolVersion().getMajor()); protocolMessage.setSessionID((byte) this.sessionId); + SecurityQuery securityQuery = new SecurityQuery(); + securityQuery.setQueryID(QueryID.SEND_HANDSHAKE_DATA); + securityQuery.setQueryType(QueryType.REQUEST); + securityQuery.setCorrelationId(msg.getCorrID()); + securityQuery.setJsonSize(msg.getJsonSize()); + securityQuery.setData(returnBytes); + //sdlSecurity.hs(); - sendMessage(protocolMessage); + sendMessage(msg, securityQuery); } /** -- cgit v1.2.1 From 472a81c49f00731974352a9e70f5d43ed1b32ea3 Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 4 Aug 2021 15:51:16 -0400 Subject: Remove ErrorCode test --- .../test/protocol/BinaryQueryHeaderTests.java | 25 ---------------------- 1 file changed, 25 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java index 33c567c01..c4c5d7001 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java @@ -80,31 +80,6 @@ public class BinaryQueryHeaderTests { assertNull(head); } - @Test - public void testErrorCodeAssemble() { - BinaryQueryHeader bqh = new BinaryQueryHeader(); - bqh.setCorrelationID(123); - bqh.setQueryID(QueryID.SEND_INTERNAL_ERROR.getValue()); - bqh.setQueryType(QUERY_TYPE_NOTIFICATION); - bqh.setBulkData(null); - bqh.setErrorCode(2); - bqh.setJsonSize(0); - - byte[] bqhBytes = bqh.assembleHeaderBytes(); - assertNotNull(bqhBytes); - - BinaryQueryHeader parsedBqh = BinaryQueryHeader.parseBinaryQueryHeader(bqhBytes); - assertNotNull(parsedBqh); - - assertEquals(bqh.getCorrelationID(), parsedBqh.getCorrelationID()); - assertEquals(bqh.getQueryID(), parsedBqh.getQueryID()); - assertEquals(bqh.getQueryType(), parsedBqh.getQueryType()); - assertEquals(bqh.getBulkData(), parsedBqh.getBulkData()); - assertEquals(bqh.getJsonData(), parsedBqh.getJsonData()); - assertEquals(bqh.getJsonSize(), parsedBqh.getJsonSize()); - assertEquals(bqh.getErrorCode(), parsedBqh.getErrorCode()); - } - @Test public void testJsonSetException() { try { -- cgit v1.2.1 From 9db39f9c37533beddd954fa6f4851951f070e4fc Mon Sep 17 00:00:00 2001 From: Henigan Date: Mon, 9 Aug 2021 09:40:25 -0400 Subject: Code Review Fixes --- .../PreloadPresentChoicesOperationTests.java | 6 ++--- .../screen/choiceset/BaseChoiceSetManager.java | 2 +- .../screen/choiceset/DeleteChoicesOperation.java | 4 ---- .../PreloadChoicesCompletionListener.java | 13 ---------- .../choiceset/PreloadPresentChoicesOperation.java | 28 +++++++++++----------- 5 files changed, 18 insertions(+), 35 deletions(-) delete mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesCompletionListener.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java index 1815f6abe..14a621c49 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java @@ -258,7 +258,7 @@ public class PreloadPresentChoicesOperationTests { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - PreloadChoicesCompletionListener listener = new PreloadChoicesCompletionListener() { + PreloadPresentChoicesOperation.PreloadChoicesCompletionListener listener = new PreloadPresentChoicesOperation.PreloadChoicesCompletionListener() { @Override public void onComplete(boolean success, HashSet loadedChoiceCells) { choiceSet.cancel(); @@ -302,7 +302,7 @@ public class PreloadPresentChoicesOperationTests { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - PreloadChoicesCompletionListener listener = new PreloadChoicesCompletionListener() { + PreloadPresentChoicesOperation.PreloadChoicesCompletionListener listener = new PreloadPresentChoicesOperation.PreloadChoicesCompletionListener() { @Override public void onComplete(boolean success, HashSet loadedChoiceCells) { choiceSet.cancel(); @@ -400,7 +400,7 @@ public class PreloadPresentChoicesOperationTests { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3)); WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - PreloadChoicesCompletionListener listener = new PreloadChoicesCompletionListener() { + PreloadPresentChoicesOperation.PreloadChoicesCompletionListener listener = new PreloadPresentChoicesOperation.PreloadChoicesCompletionListener() { @Override public void onComplete(boolean success, HashSet loadedChoiceCells) { choiceSet.cancel(); 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 2dc841711..66ba0944f 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 @@ -211,7 +211,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { updateIdsOnChoices(choicesToUpload); if (fileManager.get() != null) { - PreloadPresentChoicesOperation preloadChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, this.preloadedChoices, new PreloadChoicesCompletionListener() { + PreloadPresentChoicesOperation preloadChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, this.preloadedChoices, new PreloadPresentChoicesOperation.PreloadChoicesCompletionListener() { @Override public void onComplete(boolean success, HashSet loadedCells) { preloadedChoices = loadedCells; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java index 80058cb69..c424942dc 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java @@ -124,10 +124,6 @@ class DeleteChoicesOperation extends Task { this.loadedCells = new HashSet<>(loadedCells); } - public HashSet getLoadedCells() { - return new HashSet<>(this.loadedCells); - } - private void updateCellsToDelete() { HashSet updatedCellsToDelete = new HashSet<>(this.cellsToDelete); updatedCellsToDelete.retainAll(loadedCells); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesCompletionListener.java deleted file mode 100644 index 5a34dc9dc..000000000 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadChoicesCompletionListener.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.smartdevicelink.managers.screen.choiceset; - -import java.util.HashSet; - -public interface PreloadChoicesCompletionListener { - - /** - * Returns whether an preload choices operation was successful or not along with which choice cells are loaded - * @param success - Boolean that is True if Operation was a success, False otherwise. - * @param loadedChoiceCells - A set of the ChoiceCells that are loaded - */ - void onComplete(boolean success, HashSet loadedChoiceCells); -} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 32a119e28..57acfb3b2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -45,7 +45,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -public class PreloadPresentChoicesOperation extends Task { +class PreloadPresentChoicesOperation extends Task { private static final String TAG = "PreloadPresentChoicesOperation"; private final WeakReference internalInterface; @@ -82,7 +82,7 @@ public class PreloadPresentChoicesOperation extends Task { } private SDLPreloadPresentChoicesOperationState currentState; - public PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, String displayName, WindowCapability defaultWindowCapability, + PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, String displayName, WindowCapability defaultWindowCapability, Boolean isVROptional, LinkedHashSet cellsToPreload, HashSet loadedCells, PreloadChoicesCompletionListener listener) { super("PreloadPresentChoiceOperation"); this.internalInterface = new WeakReference<>(internalInterface); @@ -105,7 +105,7 @@ public class PreloadPresentChoicesOperation extends Task { this.selectionListener = null; } - public PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, ChoiceSet choiceSet, InteractionMode mode, + PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, ChoiceSet choiceSet, InteractionMode mode, KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, Integer cancelID, String displayName, WindowCapability windowCapability, Boolean isVROptional, HashSet loadedCells, PreloadChoicesCompletionListener preloadListener, ChoiceSetSelectionListener listener) { super("PreloadPresentChoiceOperation"); @@ -195,7 +195,7 @@ public class PreloadPresentChoicesOperation extends Task { }); } - //PRELOAD OPERATION METHODS + // Preload operation methods private void preloadCellArtworks(@NonNull final CompletionListener listener) { this.currentState = SDLPreloadPresentChoicesOperationState.UPLOADING_IMAGES; @@ -440,7 +440,11 @@ public class PreloadPresentChoicesOperation extends Task { } } - //Present Helpers + interface PreloadChoicesCompletionListener { + void onComplete(boolean success, HashSet loadedChoiceCells); + } + + // Present Helpers void setSelectedCellWithId(Integer cellId) { if (choiceSet.getChoices() != null && cellId != null) { List cells = choiceSet.getChoices(); @@ -469,7 +473,7 @@ public class PreloadPresentChoicesOperation extends Task { return pi; } - //Choice Uniqueness + // Choice Uniqueness void updateCellsBasedOnLoadedChoices() { if (internalInterface.get().getProtocolVersion().getMajor() >= 7 && internalInterface.get().getProtocolVersion().getMinor() >= 1) { addUniqueNamesToCells(cellsToUpload); @@ -482,7 +486,7 @@ public class PreloadPresentChoicesOperation extends Task { List removeUnusedProperties(List choiceCells) { List strippedCellsClone = cloneChoiceCellList(choiceCells); - //Clone Cells + // Clone Cells for (ChoiceCell cell : strippedCellsClone) { // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI cell.setVoiceCommands(null); @@ -567,7 +571,7 @@ public class PreloadPresentChoicesOperation extends Task { } } - //Finding Cells + // Finding Cells private ChoiceCell cellFromChoiceId(int choiceId) { for (ChoiceCell cell : this.cellsToUpload) { if (cell.getChoiceId() == choiceId) { @@ -577,7 +581,7 @@ public class PreloadPresentChoicesOperation extends Task { return null; } - //Assembling Choice RPCs + // Assembling Choice RPCs private CreateInteractionChoiceSet choiceFromCell(ChoiceCell cell) { List vrCommands; @@ -641,7 +645,7 @@ public class PreloadPresentChoicesOperation extends Task { return templateSupportsImageField(ImageFieldName.choiceSecondaryImage); } - //SDL Notifications + // SDL Notifications private void addListeners() { keyboardRPCListener = new OnRPCNotificationListener() { @@ -709,10 +713,6 @@ public class PreloadPresentChoicesOperation extends Task { this.loadedCells = loadedCells; } - public HashSet getLoadedCells() { - return this.loadedCells; - } - List artworksToUpload() { List artworksToUpload = new ArrayList<>(); for (ChoiceCell cell : cellsToUpload) { -- cgit v1.2.1 From 8cffaa0b2f7e8cffb724605b48cccd7a69fc26b7 Mon Sep 17 00:00:00 2001 From: Henigan Date: Mon, 9 Aug 2021 09:45:20 -0400 Subject: Add copyright --- .../choiceset/PreloadPresentChoicesOperation.java | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 57acfb3b2..851aea3a5 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -1,3 +1,38 @@ +/* + * Copyright (c) 2021 Livio, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following + * disclaimer in the documentation and/or other materials provided with the + * distribution. + * + * Neither the name of the Livio Inc. nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * Created by rhenigan on 8/9/21 1:52 PM + * + */ + package com.smartdevicelink.managers.screen.choiceset; import androidx.annotation.NonNull; -- cgit v1.2.1 From 134ee9e85fadf02c2247025c92c10b7082ee833a Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 11:38:47 -0400 Subject: Add DynamicMenuUpdateAlgorithm.compatibilityRunScoreWithOldMenuCells() --- .../managers/screen/menu/MenuManagerTests.java | 10 +++++----- .../managers/screen/menu/DynamicMenuUpdateAlgorithm.java | 6 +++++- .../managers/screen/menu/MenuReplaceOperation.java | 12 ++++++------ 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index 358155d7c..a6beadd0a 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -295,7 +295,7 @@ public class MenuManagerTests { sleep(); // this happens in the menu manager but lets make sure its behaving - DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); + DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldMenu, newMenu); List oldMenuStatus = Arrays.asList(KEEP, KEEP, KEEP, KEEP); List newMenuStatus = Arrays.asList(KEEP, KEEP, KEEP, KEEP, ADD); @@ -340,7 +340,7 @@ public class MenuManagerTests { sleep(); // this happens in the menu manager but lets make sure its behaving - DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); + DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldMenu, newMenu); List oldMenuScore = Arrays.asList(KEEP, KEEP, KEEP, DELETE); List newMenuScore = Arrays.asList(KEEP, KEEP, KEEP); @@ -385,7 +385,7 @@ public class MenuManagerTests { sleep(); // this happens in the menu manager but lets make sure its behaving - DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); + DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldMenu, newMenu); List oldMenuStatus = Arrays.asList(DELETE, DELETE, DELETE); List newMenuStatus = Arrays.asList(ADD, ADD, ADD); @@ -430,7 +430,7 @@ public class MenuManagerTests { sleep(); // this happens in the menu manager but lets make sure its behaving - DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); + DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldMenu, newMenu); List oldMenuStatus = Arrays.asList(KEEP, DELETE, KEEP, DELETE); List newMenuStatus = Arrays.asList(ADD, KEEP, ADD, KEEP); @@ -475,7 +475,7 @@ public class MenuManagerTests { sleep(); // this happens in the menu manager but lets make sure its behaving - DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldMenu, newMenu); + DynamicMenuUpdateRunScore runScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldMenu, newMenu); List oldMenuStatus = Arrays.asList(DELETE, KEEP, KEEP, KEEP); List newMenuStatus = Arrays.asList(KEEP, KEEP, KEEP, ADD); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index 8bf6b3718..9e019669a 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -14,7 +14,11 @@ class DynamicMenuUpdateAlgorithm { KEEP // Marks the cell to be kept } - static DynamicMenuUpdateRunScore compareOldMenuCells(List oldMenuCells, List updatedMenuCells) { + static DynamicMenuUpdateRunScore compatibilityRunScoreWithOldMenuCells(List oldMenuCells, List updatedMenuCells) { + return new DynamicMenuUpdateRunScore(buildAllDeleteStatusesForMenu(oldMenuCells), buildAllAddStatusesForMenu(updatedMenuCells), updatedMenuCells.size()); + } + + static DynamicMenuUpdateRunScore dynamicRunScoreOldMenuCells(List oldMenuCells, List updatedMenuCells) { if (!oldMenuCells.isEmpty() && updatedMenuCells.isEmpty()) { return new DynamicMenuUpdateRunScore(buildAllDeleteStatusesForMenu(oldMenuCells), new ArrayList(), 0); } else if (oldMenuCells.isEmpty() && !updatedMenuCells.isEmpty()) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index f2c08a11c..5711e0d2d 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -81,12 +81,12 @@ class MenuReplaceOperation extends Task { private void updateMenuCells(final CompletionListener listener) { DynamicMenuUpdateRunScore runScore; - if (isDynamicMenuUpdateActive) { - DebugTool.logInfo(TAG, "Dynamic menu update is active. Running the algorithm to find the best run score"); - runScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(currentMenu, updatedMenu); + if (!isDynamicMenuUpdateActive) { + DebugTool.logInfo(TAG, "Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); + runScore = DynamicMenuUpdateAlgorithm.compatibilityRunScoreWithOldMenuCells(currentMenu, updatedMenu); } else { - DebugTool.logInfo(TAG, "Dynamic menu update is not active. Forcing the deletion of all old cells and adding new ones"); - runScore = new DynamicMenuUpdateRunScore(buildAllDeleteStatusesForMenu(currentMenu), buildAllAddStatusesForMenu(updatedMenu), updatedMenu.size()); + DebugTool.logInfo(TAG, "Dynamic menu update active. Running the algorithm to find the best way to delete / add cells."); + runScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(currentMenu, updatedMenu); } // If both old and new menu cells are empty. Then nothing needs to be done. @@ -296,7 +296,7 @@ class MenuReplaceOperation extends Task { } if (oldKeptCells.get(startIndex) != null && isSubMenuCell(oldKeptCells.get(startIndex)) && !oldKeptCells.get(startIndex).getSubCells().isEmpty()) { - DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.compareOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); + DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); // If both old and new menu cells are empty. Then nothing needs to be done. if (tempScore == null) { -- cgit v1.2.1 From 59a78a110aabb46a78d6ec336768c1d9cbf8f7d1 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 12:00:49 -0400 Subject: Replace null score with empty score --- .../screen/menu/DynamicMenuUpdateAlgorithm.java | 13 ++++++++----- .../managers/screen/menu/MenuCell.java | 19 +++++++++++++++++++ .../managers/screen/menu/MenuReplaceOperation.java | 4 ++-- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index 9e019669a..a56a733f4 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -20,17 +20,20 @@ class DynamicMenuUpdateAlgorithm { static DynamicMenuUpdateRunScore dynamicRunScoreOldMenuCells(List oldMenuCells, List updatedMenuCells) { if (!oldMenuCells.isEmpty() && updatedMenuCells.isEmpty()) { + // Deleting all cells return new DynamicMenuUpdateRunScore(buildAllDeleteStatusesForMenu(oldMenuCells), new ArrayList(), 0); } else if (oldMenuCells.isEmpty() && !updatedMenuCells.isEmpty()) { + // No cells to delete return new DynamicMenuUpdateRunScore(new ArrayList(), buildAllAddStatusesForMenu(updatedMenuCells), updatedMenuCells.size()); - } else if (oldMenuCells.isEmpty()) { - return null; + } else if (oldMenuCells.isEmpty() && updatedMenuCells.isEmpty()) { + // Empty menu to empty menu + return new DynamicMenuUpdateRunScore(new ArrayList(), new ArrayList(), 0); } return startCompareAtRun(0, oldMenuCells, updatedMenuCells); } private static DynamicMenuUpdateRunScore startCompareAtRun(int startRun, List oldMenuCells, List updatedMenuCells) { - DynamicMenuUpdateRunScore bestScore = null; + DynamicMenuUpdateRunScore bestScore = new DynamicMenuUpdateRunScore(new ArrayList(), new ArrayList(), 0); for (int run = startRun; run < oldMenuCells.size(); run++) { // Set the menu status as a 1-1 array, start off will oldMenus = all Deletes, newMenu = all Adds @@ -43,7 +46,7 @@ class DynamicMenuUpdateAlgorithm { // if a match if found we mark the index at match for both the old and the new status to // keep since we do not want to send RPCs for those cases for (int newCellIndex = startIndex; newCellIndex < updatedMenuCells.size(); newCellIndex++) { - if (oldMenuCells.get(oldCellIndex).equals(updatedMenuCells.get(newCellIndex))) { + if (oldMenuCells.get(oldCellIndex).equalsWithUniqueTitle(updatedMenuCells.get(newCellIndex))) { oldMenuStatus.set(oldCellIndex, MenuCellState.KEEP); newMenuStatus.set(newCellIndex, MenuCellState.KEEP); startIndex = newCellIndex + 1; @@ -68,7 +71,7 @@ class DynamicMenuUpdateAlgorithm { } // if we haven't create the bestScore object or if the current score beats the old score then we will create a new bestScore - if (bestScore == null || numberOfAdds < bestScore.getScore()) { + if (bestScore.getScore() == 0 || numberOfAdds < bestScore.getScore()) { bestScore = new DynamicMenuUpdateRunScore(oldMenuStatus, newMenuStatus, numberOfAdds); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index 6b6d9d2c5..ca43bd882 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -476,6 +476,25 @@ public class MenuCell implements Cloneable { return hashCode() == o.hashCode(); } + /** + * Uses our custom hashCode for MenuCell objects + * + * @param o - The object to compare + * @return boolean of whether the objects are the same or not + */ + public boolean equalsWithUniqueTitle(Object o) { + // @todo fix later + if (o == null) { + return false; + } + // if this is the same memory address, its the same + if (this == o) return true; + // if this is not an instance of this class, not the same + if (!(o instanceof MenuCell)) return false; + // if we get to this point, create the hashes and compare them + return hashCode() == o.hashCode(); + } + /** * Creates a deep copy of the object * diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 5711e0d2d..5c4b42413 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -90,7 +90,7 @@ class MenuReplaceOperation extends Task { } // If both old and new menu cells are empty. Then nothing needs to be done. - if (runScore == null) { + if (runScore.getScore() == 0) { listener.onComplete(true); return; } @@ -299,7 +299,7 @@ class MenuReplaceOperation extends Task { DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); // If both old and new menu cells are empty. Then nothing needs to be done. - if (tempScore == null) { + if (tempScore.getScore() == 0) { // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); return; -- cgit v1.2.1 From 2801877e0885f43ce306f1268ec6c1ac670b86a5 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 12:19:31 -0400 Subject: Add DynamicMenuUpdateRunScore.isEmpty() --- .../managers/screen/menu/DynamicMenuUpdateAlgorithm.java | 2 +- .../managers/screen/menu/DynamicMenuUpdateRunScore.java | 4 ++++ .../smartdevicelink/managers/screen/menu/MenuReplaceOperation.java | 4 ++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index a56a733f4..aa2f815d9 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -71,7 +71,7 @@ class DynamicMenuUpdateAlgorithm { } // if we haven't create the bestScore object or if the current score beats the old score then we will create a new bestScore - if (bestScore.getScore() == 0 || numberOfAdds < bestScore.getScore()) { + if (bestScore.isEmpty() || numberOfAdds < bestScore.getScore()) { bestScore = new DynamicMenuUpdateRunScore(oldMenuStatus, newMenuStatus, numberOfAdds); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java index 2d5e75462..892fd1a8d 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateRunScore.java @@ -70,4 +70,8 @@ class DynamicMenuUpdateRunScore { public int getScore() { return score; } + + boolean isEmpty() { + return oldStatus.size() == 0 && updatedStatus.size() == 0 && score == 0; + } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 5c4b42413..069357900 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -90,7 +90,7 @@ class MenuReplaceOperation extends Task { } // If both old and new menu cells are empty. Then nothing needs to be done. - if (runScore.getScore() == 0) { + if (runScore.isEmpty()) { listener.onComplete(true); return; } @@ -299,7 +299,7 @@ class MenuReplaceOperation extends Task { DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); // If both old and new menu cells are empty. Then nothing needs to be done. - if (tempScore.getScore() == 0) { + if (tempScore.isEmpty()) { // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); return; -- cgit v1.2.1 From d2ed5b020c30e7dcd184c23630bdc5ced91ab0b4 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 16:28:31 -0400 Subject: Fix MenuCell.equalsWithUniqueTitle() --- .../managers/screen/menu/MenuCell.java | 31 +++++++++++----------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index ca43bd882..46682b3df 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -446,19 +446,23 @@ public class MenuCell implements Cloneable { public int hashCode() { int result = 1; result += ((getTitle() == null) ? 0 : Integer.rotateLeft(getTitle().hashCode(), 1)); - result += ((getUniqueTitle() == null) ? 0 : Integer.rotateLeft(getUniqueTitle().hashCode(), 2)); - result += ((getIcon() == null) ? 0 : Integer.rotateLeft(getIcon().hashCode(), 3)); - result += ((getVoiceCommands() == null || getVoiceCommands().isEmpty()) ? 0 : Integer.rotateLeft(getVoiceCommands().hashCode(), 4)); - result += ((getSubCells() == null) ? 0 : Integer.rotateLeft(getSubCells().hashCode(), 5)); - result += ((getSecondaryText() == null) ? 0 : Integer.rotateLeft(getSecondaryText().hashCode(), 6)); - result += ((getTertiaryText() == null) ? 0 : Integer.rotateLeft(getTertiaryText().hashCode(), 7)); - result += ((getSecondaryArtwork() == null) ? 0 : Integer.rotateLeft(getSecondaryArtwork().hashCode(), 9)); - result += ((getMenuSelectionListener() == null) ? 0 : Integer.rotateLeft(getMenuSelectionListener().hashCode(), 9)); + result += ((getIcon() == null) ? 0 : Integer.rotateLeft(getIcon().hashCode(), 2)); + result += ((getVoiceCommands() == null || getVoiceCommands().isEmpty()) ? 0 : Integer.rotateLeft(getVoiceCommands().hashCode(), 3)); + result += ((getSubCells() == null) ? 0 : Integer.rotateLeft(getSubCells().hashCode(), 4)); + result += ((getSecondaryText() == null) ? 0 : Integer.rotateLeft(getSecondaryText().hashCode(), 5)); + result += ((getTertiaryText() == null) ? 0 : Integer.rotateLeft(getTertiaryText().hashCode(), 6)); + result += ((getSecondaryArtwork() == null) ? 0 : Integer.rotateLeft(getSecondaryArtwork().hashCode(), 7)); + return result; + } + + private int hashCodeWithUniqueTitle() { + int result = hashCode(); + result += ((getUniqueTitle() == null) ? 0 : Integer.rotateLeft(getUniqueTitle().hashCode(), 8)); return result; } /** - * Uses our custom hashCode for MenuCell objects, but does NOT compare the listener objects + * Uses our custom hashCode for MenuCell objects * * @param o - The object to compare * @return boolean of whether the objects are the same or not @@ -477,22 +481,19 @@ public class MenuCell implements Cloneable { } /** - * Uses our custom hashCode for MenuCell objects + * Uses our custom hashCode for MenuCell objects. This method takes UniqueTitle into consideration when doing the equality check * * @param o - The object to compare * @return boolean of whether the objects are the same or not */ - public boolean equalsWithUniqueTitle(Object o) { - // @todo fix later + boolean equalsWithUniqueTitle(MenuCell o) { if (o == null) { return false; } // if this is the same memory address, its the same if (this == o) return true; - // if this is not an instance of this class, not the same - if (!(o instanceof MenuCell)) return false; // if we get to this point, create the hashes and compare them - return hashCode() == o.hashCode(); + return hashCodeWithUniqueTitle() == o.hashCodeWithUniqueTitle(); } /** -- cgit v1.2.1 From 3c7c06f68fede5ec6a0b577e8b40ff86676f2fa5 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 16:59:00 -0400 Subject: Split shouldCellIncludeImage() --- .../managers/screen/menu/MenuReplaceUtilities.java | 31 +++++++++------------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 99996606b..14b1ed940 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -54,21 +54,16 @@ class MenuReplaceUtilities { return artworks; } - static boolean shouldCellIncludeImage(MenuCell cell, FileManager fileManager, WindowCapability windowCapability, boolean isSecondaryImage) { - SdlArtwork artwork = isSecondaryImage ? cell.getSecondaryArtwork() : cell.getIcon(); - if (artwork == null) { - return false; - } - - ImageFieldName mainMenuImageFieldName = isSecondaryImage ? ImageFieldName.menuCommandSecondaryImage : ImageFieldName.cmdIcon; - ImageFieldName subMenuImageFieldName = isSecondaryImage ? ImageFieldName.menuSubMenuSecondaryImage : ImageFieldName.subMenuIcon; - boolean supportsImage = isSubMenuCell(cell) ? hasImageFieldOfName(windowCapability, subMenuImageFieldName) : hasImageFieldOfName(windowCapability, mainMenuImageFieldName); - if (!supportsImage) { - return false; - } + // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image + static boolean shouldCellIncludeImageFromCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability) { + boolean supportsImage = isSubMenuCell(cell) ? hasImageFieldOfName(windowCapability, ImageFieldName.subMenuIcon) : hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); + return cell.getIcon() != null && supportsImage && (fileManager.hasUploadedFile(cell.getIcon()) || cell.getIcon().isStaticIcon()); + } - // If the icon has been uploaded, or if the icon is a static icon, the cell should include the image - return (fileManager.hasUploadedFile(artwork) || artwork.isStaticIcon()); + // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image + static boolean shouldCellIncludeSecondaryImageFromCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability) { + boolean supportsImage = isSubMenuCell(cell) ? hasImageFieldOfName(windowCapability, ImageFieldName.menuSubMenuSecondaryImage) : hasImageFieldOfName(windowCapability, ImageFieldName.menuCommandSecondaryImage); + return cell.getSecondaryArtwork() != null && supportsImage && (fileManager.hasUploadedFile(cell.getSecondaryArtwork()) || cell.getSecondaryArtwork().isStaticIcon()); } static int commandIdForRPCRequest(RPCRequest request) { @@ -174,19 +169,19 @@ class MenuReplaceUtilities { } else { command.setVrCommands(null); } - boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && shouldCellIncludeImage(cell, fileManager, windowCapability, false); + boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && shouldCellIncludeImageFromCell(cell, fileManager, windowCapability); command.setCmdIcon(shouldCellIncludePrimaryImage ? cell.getIcon().getImageRPC() : null); - boolean shouldCellIncludeSecondaryImage = cell.getSecondaryArtwork() != null && shouldCellIncludeImage(cell, fileManager, windowCapability, true); + boolean shouldCellIncludeSecondaryImage = cell.getSecondaryArtwork() != null && shouldCellIncludeSecondaryImageFromCell(cell, fileManager, windowCapability); command.setSecondaryImage(shouldCellIncludeSecondaryImage ? cell.getSecondaryArtwork().getImageRPC() : null); return command; } static AddSubMenu subMenuCommandForMenuCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position, MenuLayout defaultSubmenuLayout) { - boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && cell.getIcon().getImageRPC() != null && shouldCellIncludeImage(cell, fileManager, windowCapability, false); + boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && cell.getIcon().getImageRPC() != null && shouldCellIncludeImageFromCell(cell, fileManager, windowCapability); Image icon = (shouldCellIncludePrimaryImage ? cell.getIcon().getImageRPC() : null); - boolean shouldCellIncludeSecondaryImage = cell.getSecondaryArtwork() != null && cell.getSecondaryArtwork().getImageRPC() != null && shouldCellIncludeImage(cell, fileManager, windowCapability, true); + boolean shouldCellIncludeSecondaryImage = cell.getSecondaryArtwork() != null && cell.getSecondaryArtwork().getImageRPC() != null && shouldCellIncludeSecondaryImageFromCell(cell, fileManager, windowCapability); Image secondaryIcon = (shouldCellIncludeSecondaryImage ? cell.getSecondaryArtwork().getImageRPC() : null); MenuLayout submenuLayout; -- cgit v1.2.1 From 6f1c3d84d0203a29625bd84496b195964a98065d Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 17:13:23 -0400 Subject: Add getSubMenuLayout() to hashcode --- .../java/com/smartdevicelink/managers/screen/menu/MenuCell.java | 3 ++- .../smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index 46682b3df..93d6d1477 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -452,12 +452,13 @@ public class MenuCell implements Cloneable { result += ((getSecondaryText() == null) ? 0 : Integer.rotateLeft(getSecondaryText().hashCode(), 5)); result += ((getTertiaryText() == null) ? 0 : Integer.rotateLeft(getTertiaryText().hashCode(), 6)); result += ((getSecondaryArtwork() == null) ? 0 : Integer.rotateLeft(getSecondaryArtwork().hashCode(), 7)); + result += ((getSubMenuLayout() == null) ? 0 : Integer.rotateLeft(getSubMenuLayout().hashCode(), 8)); return result; } private int hashCodeWithUniqueTitle() { int result = hashCode(); - result += ((getUniqueTitle() == null) ? 0 : Integer.rotateLeft(getUniqueTitle().hashCode(), 8)); + result += ((getUniqueTitle() == null) ? 0 : Integer.rotateLeft(getUniqueTitle().hashCode(), 9)); return result; } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 14b1ed940..5707a21a7 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -93,11 +93,11 @@ class MenuReplaceUtilities { static List deleteCommandsForCells(List cells) { List deletes = new ArrayList<>(); for (MenuCell cell : cells) { - if (!isSubMenuCell(cell)) { - DeleteCommand delete = new DeleteCommand(cell.getCellId()); + if (isSubMenuCell(cell)) { + DeleteSubMenu delete = new DeleteSubMenu(cell.getCellId()); deletes.add(delete); } else { - DeleteSubMenu delete = new DeleteSubMenu(cell.getCellId()); + DeleteCommand delete = new DeleteCommand(cell.getCellId()); deletes.add(delete); } } -- cgit v1.2.1 From a34fe69d06a7819881d13106b92635079ab36dd3 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 17:20:14 -0400 Subject: Fix unit tests --- .../managers/screen/menu/MenuReplaceUtilitiesTests.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java index 0e1dec624..f7b36f536 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java @@ -222,31 +222,31 @@ public class MenuReplaceUtilitiesTests { menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(true); - assertTrue(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability, false)); + assertTrue(MenuReplaceUtilities.shouldCellIncludeImageFromCell(menuCell, fileManager, windowCapability)); // Case 2 - Image are not supported menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); windowCapability = createWindowCapability(false, false); fileManager = createMockFileManager(true); - assertFalse(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability, false)); + assertFalse(MenuReplaceUtilities.shouldCellIncludeImageFromCell(menuCell, fileManager, windowCapability)); // Case 3 - Artwork is null menuCell = new MenuCell(TestValues.GENERAL_STRING, null, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(true); - assertFalse(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability, false)); + assertFalse(MenuReplaceUtilities.shouldCellIncludeImageFromCell(menuCell, fileManager, windowCapability)); // Case 4 - Artwork has not been uploaded menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(false); - assertFalse(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability, false)); + assertFalse(MenuReplaceUtilities.shouldCellIncludeImageFromCell(menuCell, fileManager, windowCapability)); // Case 5 - Artwork is static icon menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK_STATIC, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(false); - assertTrue(MenuReplaceUtilities.shouldCellIncludeImage(menuCell, fileManager, windowCapability, false)); + assertTrue(MenuReplaceUtilities.shouldCellIncludeImageFromCell(menuCell, fileManager, windowCapability)); } private WindowCapability createWindowCapability (boolean supportsCmdIcon, boolean supportsSubMenuIcon) { -- cgit v1.2.1 From 1a1d2df248481587d416c163c3d167e405c1ca29 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 17:32:39 -0400 Subject: Fix error messages TAG --- .../smartdevicelink/managers/screen/menu/BaseMenuManager.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 6e5c8c779..97a9c3a1b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -172,6 +172,11 @@ abstract class BaseMenuManager extends BaseSubManager { * @param cells - the menu cells that are to be sent to the head unit, including their sub-cells. */ public void setMenuCells(@NonNull List cells) { + if (this.menuCells.equals(cells)) { + DebugTool.logError("The set menu cells are identical to previously set menu cells. Skipping..."); + } else { + + } // Check for cell lists with completely duplicate information, or any duplicate voiceCommands and return if it fails (logs are in the called method). if (cells != null && !menuCellsAreUnique(cloneMenuCellsList(cells), new ArrayList())) { return; @@ -242,10 +247,10 @@ abstract class BaseMenuManager extends BaseSubManager { } if (cell != null && (!isSubMenuCell(cell))) { - DebugTool.logError(DebugTool.TAG, String.format("The cell %s does not contain any sub cells, so no submenu can be opened", cell.getTitle())); + DebugTool.logError(TAG, String.format("The cell %s does not contain any sub cells, so no submenu can be opened", cell.getTitle())); return false; } else if (cell != null && foundClonedCell == null) { - DebugTool.logError(DebugTool.TAG, "This cell has not been sent to the head unit, so no submenu can be opened. Make sure that the cell exists in the SDLManager.menu array"); + DebugTool.logError(TAG, "This cell has not been sent to the head unit, so no submenu can be opened. Make sure that the cell exists in the SDLManager.menu array"); return false; } else if (internalInterface.getSdlMsgVersion().getMajorVersion() < 6) { DebugTool.logWarning(TAG, "The openSubmenu method is not supported on this head unit."); -- cgit v1.2.1 From f4d2d117c2d580e36c61fda28887b862d001a0a6 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 17:37:10 -0400 Subject: Remove code added by mistake --- .../com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 97a9c3a1b..9b820ce46 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -172,11 +172,6 @@ abstract class BaseMenuManager extends BaseSubManager { * @param cells - the menu cells that are to be sent to the head unit, including their sub-cells. */ public void setMenuCells(@NonNull List cells) { - if (this.menuCells.equals(cells)) { - DebugTool.logError("The set menu cells are identical to previously set menu cells. Skipping..."); - } else { - - } // Check for cell lists with completely duplicate information, or any duplicate voiceCommands and return if it fails (logs are in the called method). if (cells != null && !menuCellsAreUnique(cloneMenuCellsList(cells), new ArrayList())) { return; -- cgit v1.2.1 From 1102e4338b4f0454b5f40c06c890110023458c42 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 17:44:18 -0400 Subject: Remove not applicable unit test --- .../managers/screen/menu/MenuManagerTests.java | 27 ---------------------- 1 file changed, 27 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index a6beadd0a..7aef97e56 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -491,33 +491,6 @@ public class MenuManagerTests { assertEquals(3, oldKeeps.size()); } - @Test - public void testSettingNullMenu() { - // Make sure we can send an empty menu with no issues - // start fresh - menuManager.currentMenuCells = new ArrayList<>(); - menuManager.menuCells = new ArrayList<>(); - - sendFakeCoreOnHMIFullNotifications(); - - // send new cells. They should set the old way - List oldMenu = createDynamicMenu1(); - List newMenu = null; - menuManager.setMenuCells(oldMenu); - - // Sleep to give time to Taskmaster to run the operations - sleep(); - - assertEquals(4, menuManager.currentMenuCells.size()); - - menuManager.setMenuCells(newMenu); - - // Sleep to give time to Taskmaster to run the operations - sleep(); - - assertEquals(0, menuManager.currentMenuCells.size()); - } - @Test public void testClearingMenu() { // Make sure we can send an empty menu with no issues -- cgit v1.2.1 From 8ca18b6831048eed7fe04c8a076b3a0276c79015 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 18:03:29 -0400 Subject: Simplify setMenuCells() --- .../managers/screen/menu/BaseMenuManager.java | 29 ++++++++-------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 9b820ce46..3655fc01e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -172,32 +172,23 @@ abstract class BaseMenuManager extends BaseSubManager { * @param cells - the menu cells that are to be sent to the head unit, including their sub-cells. */ public void setMenuCells(@NonNull List cells) { - // Check for cell lists with completely duplicate information, or any duplicate voiceCommands and return if it fails (logs are in the called method). - if (cells != null && !menuCellsAreUnique(cloneMenuCellsList(cells), new ArrayList())) { + if (cells == null) { + DebugTool.logError(TAG, "Cells list is null. Skipping..."); return; } - // If we're running on a connection < RPC 7.1, we need to de-duplicate cells because presenting them will fail if we have the same cell primary text. - boolean duplicateTitlesNotSupported = cells != null && internalInterface.getSdlMsgVersion() != null && (internalInterface.getSdlMsgVersion().getMajorVersion() < 7 || (internalInterface.getSdlMsgVersion().getMajorVersion() == 7 && internalInterface.getSdlMsgVersion().getMinorVersion() == 0)); - if (duplicateTitlesNotSupported) { - addUniqueNamesToCellsWithDuplicatePrimaryText(cells); - } else { - // On > RPC 7.1, at this point all cells are unique when considering all properties, - // but we also need to check if any cells will _appear_ as duplicates when displayed on the screen. - // To check that, we'll remove properties from the set cells based on the system capabilities - // (we probably don't need to consider them changing between now and when they're actually sent to the HU unless the menu layout changes) - // and check for uniqueness again. Then we'll add unique identifiers to primary text if there are duplicates. - // Then we transfer the primary text identifiers back to the main cells and add those to an operation to be sent. - List strippedCellsClone = removeUnusedProperties(cells); - addUniqueNamesBasedOnStrippedCells(strippedCellsClone, cells); + if (this.menuCells.equals(cells)) { + DebugTool.logError(TAG, "The set menu cells are identical to previously set menu cells. Skipping..."); + return; + } else if (!menuCellsAreUnique(cells, new ArrayList())) { + DebugTool.logError(TAG, "Not all set menu cells are unique, but that is required"); + return; } // Create a deep copy of the list so future changes by developers don't affect the algorithm logic - final List clonedCells = cloneMenuCellsList(cells); - - updateIdsOnMenuCells(clonedCells, parentIdNotFound); + this.menuCells = cloneMenuCellsList(cells); - menuCells = new ArrayList<>(clonedCells); + updateIdsOnMenuCells(menuCells, parentIdNotFound); boolean isDynamicMenuUpdateActive = isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType); Task operation = new MenuReplaceOperation(internalInterface, fileManager.get(), windowCapability, menuConfiguration, currentMenuCells, menuCells, isDynamicMenuUpdateActive, new MenuManagerCompletionListener() { -- cgit v1.2.1 From abb32c54568efe8f1e6b9d37c4815e796643f139 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 18:08:34 -0400 Subject: Move applying unique titles to MenuReplaceOperation --- .../managers/screen/menu/BaseMenuManager.java | 87 ----------------- .../managers/screen/menu/MenuReplaceOperation.java | 104 +++++++++++++++++++++ 2 files changed, 104 insertions(+), 87 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 3655fc01e..7ad00a8cf 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -501,91 +501,4 @@ abstract class BaseMenuManager extends BaseSubManager { return true; } - - private void addUniqueNamesToCellsWithDuplicatePrimaryText(List cells) { - HashMap countsMap = new HashMap<>(); - - for (MenuCell cell : cells) { - String cellTitle = cell.getTitle(); - Integer counter = countsMap.get(cellTitle); - - if (counter != null) { - countsMap.put(cellTitle, ++counter); - cell.setUniqueTitle(cellTitle + " (" + counter + ")"); - } else { - countsMap.put(cellTitle, 1); - cell.setUniqueTitle(cellTitle); - } - - if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { - addUniqueNamesToCellsWithDuplicatePrimaryText(cell.getSubCells()); - } - } - } - - void addUniqueNamesBasedOnStrippedCells(List strippedCells, List originalCells) { - if (strippedCells == null || originalCells == null || strippedCells.size() != originalCells.size()) { - return; - } - // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary - HashMap countsMap = new HashMap<>(); - for (int i = 0; i < strippedCells.size(); i++) { - MenuCell cell = strippedCells.get(i); - Integer counter = countsMap.get(cell); - if (counter != null) { - countsMap.put(cell, ++counter); - originalCells.get(i).setUniqueTitle(originalCells.get(i).getTitle() + " (" + counter + ")"); - } else { - countsMap.put(cell, 1); - originalCells.get(i).setUniqueTitle(originalCells.get(i).getTitle()); - } - - if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { - addUniqueNamesBasedOnStrippedCells(cell.getSubCells(), originalCells.get(i).getSubCells()); - } - } - } - - List removeUnusedProperties(List cells) { - if (cells == null) { - return null; - } - List removePropertiesClone = cloneMenuCellsList(cells); - for (MenuCell cell : removePropertiesClone) { - // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI - cell.setVoiceCommands(null); - cell.setUniqueTitle(null); - cell.setMenuSelectionListener(null); - - // Don't check ImageFieldName.subMenuIcon because it was added in 7.0 when the feature was added in 5.0. - // Just assume that if cmdIcon is not available, the submenu icon is not either. - if (!hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { - cell.setIcon(null); - } - // Check for subMenu fields supported - if (isSubMenuCell(cell)) { - if (!hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuSecondaryText)) { - cell.setSecondaryText(null); - } - if (!hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuTertiaryText)) { - cell.setTertiaryText(null); - } - if (!hasImageFieldOfName(windowCapability, ImageFieldName.menuSubMenuSecondaryImage)) { - cell.setSecondaryArtwork(null); - } - cell.setSubCells(removeUnusedProperties(cell.getSubCells())); - } else { - if (!hasTextFieldOfName(windowCapability, TextFieldName.menuCommandSecondaryText)) { - cell.setSecondaryText(null); - } - if (!hasTextFieldOfName(windowCapability, TextFieldName.menuCommandTertiaryText)) { - cell.setTertiaryText(null); - } - if (!hasImageFieldOfName(windowCapability, ImageFieldName.menuCommandSecondaryImage)) { - cell.setSecondaryArtwork(null); - } - } - } - return removePropertiesClone; - } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 069357900..18df21de7 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -10,16 +10,21 @@ import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuC import com.smartdevicelink.proxy.RPCRequest; import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.WindowCapability; +import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; +import com.smartdevicelink.proxy.rpc.enums.TextFieldName; import com.smartdevicelink.util.DebugTool; import org.json.JSONException; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; +import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.buildAllAddStatusesForMenu; import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.buildAllDeleteStatusesForMenu; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addMenuRequestWithCommandId; @@ -369,6 +374,105 @@ class MenuReplaceOperation extends Task { return stringBuilder.toString(); } + private List cloneMenuCellsList(List originalList) { + if (originalList == null) { + return new ArrayList<>(); + } + + List clone = new ArrayList<>(); + for (MenuCell menuCell : originalList) { + clone.add(menuCell.clone()); + } + return clone; + } + + private void addUniqueNamesToCellsWithDuplicatePrimaryText(List cells) { + HashMap countsMap = new HashMap<>(); + + for (MenuCell cell : cells) { + String cellTitle = cell.getTitle(); + Integer counter = countsMap.get(cellTitle); + + if (counter != null) { + countsMap.put(cellTitle, ++counter); + cell.setUniqueTitle(cellTitle + " (" + counter + ")"); + } else { + countsMap.put(cellTitle, 1); + cell.setUniqueTitle(cellTitle); + } + + if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + addUniqueNamesToCellsWithDuplicatePrimaryText(cell.getSubCells()); + } + } + } + + void addUniqueNamesBasedOnStrippedCells(List strippedCells, List originalCells) { + if (strippedCells == null || originalCells == null || strippedCells.size() != originalCells.size()) { + return; + } + // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary + HashMap countsMap = new HashMap<>(); + for (int i = 0; i < strippedCells.size(); i++) { + MenuCell cell = strippedCells.get(i); + Integer counter = countsMap.get(cell); + if (counter != null) { + countsMap.put(cell, ++counter); + originalCells.get(i).setUniqueTitle(originalCells.get(i).getTitle() + " (" + counter + ")"); + } else { + countsMap.put(cell, 1); + originalCells.get(i).setUniqueTitle(originalCells.get(i).getTitle()); + } + + if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + addUniqueNamesBasedOnStrippedCells(cell.getSubCells(), originalCells.get(i).getSubCells()); + } + } + } + + List removeUnusedProperties(List cells) { + if (cells == null) { + return null; + } + List removePropertiesClone = cloneMenuCellsList(cells); + for (MenuCell cell : removePropertiesClone) { + // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI + cell.setVoiceCommands(null); + cell.setUniqueTitle(null); + cell.setMenuSelectionListener(null); + + // Don't check ImageFieldName.subMenuIcon because it was added in 7.0 when the feature was added in 5.0. + // Just assume that if cmdIcon is not available, the submenu icon is not either. + if (!hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { + cell.setIcon(null); + } + // Check for subMenu fields supported + if (isSubMenuCell(cell)) { + if (!hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuSecondaryText)) { + cell.setSecondaryText(null); + } + if (!hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuTertiaryText)) { + cell.setTertiaryText(null); + } + if (!hasImageFieldOfName(windowCapability, ImageFieldName.menuSubMenuSecondaryImage)) { + cell.setSecondaryArtwork(null); + } + cell.setSubCells(removeUnusedProperties(cell.getSubCells())); + } else { + if (!hasTextFieldOfName(windowCapability, TextFieldName.menuCommandSecondaryText)) { + cell.setSecondaryText(null); + } + if (!hasTextFieldOfName(windowCapability, TextFieldName.menuCommandTertiaryText)) { + cell.setTertiaryText(null); + } + if (!hasImageFieldOfName(windowCapability, ImageFieldName.menuCommandSecondaryImage)) { + cell.setSecondaryArtwork(null); + } + } + } + return removePropertiesClone; + } + void setMenuConfiguration(MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } -- cgit v1.2.1 From 6ff352f22d687b8d95a81276966b0507e103582f Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 9 Aug 2021 18:09:13 -0400 Subject: Remove unused imports --- .../managers/screen/menu/BaseMenuManager.java | 9 ++----- .../managers/screen/menu/MenuReplaceOperation.java | 28 ++++++++++------------ 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 7ad00a8cf..11bc93a0f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -32,6 +32,8 @@ package com.smartdevicelink.managers.screen.menu; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.isSubMenuCell; + import androidx.annotation.NonNull; import com.livio.taskmaster.Queue; @@ -50,24 +52,17 @@ import com.smartdevicelink.proxy.rpc.OnHMIStatus; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.DisplayType; import com.smartdevicelink.proxy.rpc.enums.HMILevel; -import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows; import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType; import com.smartdevicelink.proxy.rpc.enums.SystemContext; -import com.smartdevicelink.proxy.rpc.enums.TextFieldName; import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener; import com.smartdevicelink.util.DebugTool; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.List; -import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; -import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.isSubMenuCell; - abstract class BaseMenuManager extends BaseSubManager { private static final String TAG = "BaseMenuManager"; static final int menuCellIdMin = 1; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 18df21de7..b7a7b4b1b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -1,5 +1,18 @@ package com.smartdevicelink.managers.screen.menu; +import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; +import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addMenuRequestWithCommandId; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandIdForRPCRequest; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.isSubMenuCell; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.positionForRPCRequest; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.removeMenuCellFromList; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.sendRPCs; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; + import com.livio.taskmaster.Task; import com.smartdevicelink.managers.CompletionListener; import com.smartdevicelink.managers.ISdl; @@ -23,21 +36,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; -import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; -import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.buildAllAddStatusesForMenu; -import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.buildAllDeleteStatusesForMenu; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addMenuRequestWithCommandId; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandIdForRPCRequest; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.isSubMenuCell; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.positionForRPCRequest; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.removeMenuCellFromList; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.sendRPCs; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; - /** * Created by Bilal Alsharifi on 1/20/21. */ -- cgit v1.2.1 From cd1611dfe340d16ee30dd8d871be8524c66fd45d Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 10 Aug 2021 10:16:42 -0400 Subject: Rename a private method --- .../managers/screen/menu/MenuReplaceUtilitiesTests.java | 10 +++++----- .../managers/screen/menu/MenuReplaceUtilities.java | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java index f7b36f536..c08a07311 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java @@ -222,31 +222,31 @@ public class MenuReplaceUtilitiesTests { menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(true); - assertTrue(MenuReplaceUtilities.shouldCellIncludeImageFromCell(menuCell, fileManager, windowCapability)); + assertTrue(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(menuCell, fileManager, windowCapability)); // Case 2 - Image are not supported menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); windowCapability = createWindowCapability(false, false); fileManager = createMockFileManager(true); - assertFalse(MenuReplaceUtilities.shouldCellIncludeImageFromCell(menuCell, fileManager, windowCapability)); + assertFalse(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(menuCell, fileManager, windowCapability)); // Case 3 - Artwork is null menuCell = new MenuCell(TestValues.GENERAL_STRING, null, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(true); - assertFalse(MenuReplaceUtilities.shouldCellIncludeImageFromCell(menuCell, fileManager, windowCapability)); + assertFalse(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(menuCell, fileManager, windowCapability)); // Case 4 - Artwork has not been uploaded menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(false); - assertFalse(MenuReplaceUtilities.shouldCellIncludeImageFromCell(menuCell, fileManager, windowCapability)); + assertFalse(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(menuCell, fileManager, windowCapability)); // Case 5 - Artwork is static icon menuCell = new MenuCell(TestValues.GENERAL_STRING, TestValues.GENERAL_ARTWORK_STATIC, voiceCommands, null); windowCapability = createWindowCapability(true, true); fileManager = createMockFileManager(false); - assertTrue(MenuReplaceUtilities.shouldCellIncludeImageFromCell(menuCell, fileManager, windowCapability)); + assertTrue(MenuReplaceUtilities.shouldCellIncludePrimaryImageFromCell(menuCell, fileManager, windowCapability)); } private WindowCapability createWindowCapability (boolean supportsCmdIcon, boolean supportsSubMenuIcon) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 5707a21a7..d8e4ed635 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -55,7 +55,7 @@ class MenuReplaceUtilities { } // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image - static boolean shouldCellIncludeImageFromCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability) { + static boolean shouldCellIncludePrimaryImageFromCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability) { boolean supportsImage = isSubMenuCell(cell) ? hasImageFieldOfName(windowCapability, ImageFieldName.subMenuIcon) : hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); return cell.getIcon() != null && supportsImage && (fileManager.hasUploadedFile(cell.getIcon()) || cell.getIcon().isStaticIcon()); } @@ -169,7 +169,7 @@ class MenuReplaceUtilities { } else { command.setVrCommands(null); } - boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && shouldCellIncludeImageFromCell(cell, fileManager, windowCapability); + boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && shouldCellIncludePrimaryImageFromCell(cell, fileManager, windowCapability); command.setCmdIcon(shouldCellIncludePrimaryImage ? cell.getIcon().getImageRPC() : null); boolean shouldCellIncludeSecondaryImage = cell.getSecondaryArtwork() != null && shouldCellIncludeSecondaryImageFromCell(cell, fileManager, windowCapability); @@ -179,7 +179,7 @@ class MenuReplaceUtilities { } static AddSubMenu subMenuCommandForMenuCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position, MenuLayout defaultSubmenuLayout) { - boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && cell.getIcon().getImageRPC() != null && shouldCellIncludeImageFromCell(cell, fileManager, windowCapability); + boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && cell.getIcon().getImageRPC() != null && shouldCellIncludePrimaryImageFromCell(cell, fileManager, windowCapability); Image icon = (shouldCellIncludePrimaryImage ? cell.getIcon().getImageRPC() : null); boolean shouldCellIncludeSecondaryImage = cell.getSecondaryArtwork() != null && cell.getSecondaryArtwork().getImageRPC() != null && shouldCellIncludeSecondaryImageFromCell(cell, fileManager, windowCapability); Image secondaryIcon = (shouldCellIncludeSecondaryImage ? cell.getSecondaryArtwork().getImageRPC() : null); -- cgit v1.2.1 From 6ff10a158a7218587f144f5944d1fbc1f089bbd3 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 10 Aug 2021 10:22:57 -0400 Subject: Move cloneMenuCellsList() to MenuReplaceOperation --- .../managers/screen/menu/BaseMenuManager.java | 13 +------------ .../managers/screen/menu/MenuReplaceUtilities.java | 12 ++++++++++++ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 11bc93a0f..33f004171 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -32,6 +32,7 @@ package com.smartdevicelink.managers.screen.menu; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.cloneMenuCellsList; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.isSubMenuCell; import androidx.annotation.NonNull; @@ -365,18 +366,6 @@ abstract class BaseMenuManager extends BaseSubManager { internalInterface.addOnRPCNotificationListener(FunctionID.ON_COMMAND, commandListener); } - private List cloneMenuCellsList(List originalList) { - if (originalList == null) { - return new ArrayList<>(); - } - - List clone = new ArrayList<>(); - for (MenuCell menuCell : originalList) { - clone.add(menuCell.clone()); - } - return clone; - } - private boolean callListenerForCells(List cells, OnCommand command) { if (cells == null || cells.isEmpty() || command == null) { return false; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index d8e4ed635..5db56dec7 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -288,6 +288,18 @@ class MenuReplaceUtilities { return menuCell.getSubCells() != null; } + static List cloneMenuCellsList(List originalList) { + if (originalList == null) { + return new ArrayList<>(); + } + + List clone = new ArrayList<>(); + for (MenuCell menuCell : originalList) { + clone.add(menuCell.clone()); + } + return clone; + } + static void sendRPCs(final List requests, ISdl internalInterface, final SendingRPCsCompletionListener listener) { final Map errors = new HashMap<>(); if (requests == null || requests.isEmpty()) { -- cgit v1.2.1 From e7814e641f459794afff636ee592aee1f6051005 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 10 Aug 2021 11:09:31 -0400 Subject: Update Uniqueness helper methods --- .../managers/screen/menu/MenuReplaceOperation.java | 110 ++++++++++----------- 1 file changed, 50 insertions(+), 60 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index b7a7b4b1b..d3084d494 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -3,6 +3,7 @@ package com.smartdevicelink.managers.screen.menu; import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addMenuRequestWithCommandId; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.cloneMenuCellsList; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandIdForRPCRequest; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; @@ -47,6 +48,8 @@ class MenuReplaceOperation extends Task { private WindowCapability windowCapability; private List currentMenu; private final List updatedMenu; + private List currentStrippedMenu; + private List updatedStrippedMenu; private final boolean isDynamicMenuUpdateActive; private final MenuManagerCompletionListener operationCompletionListener; private MenuConfiguration menuConfiguration; @@ -372,78 +375,23 @@ class MenuReplaceOperation extends Task { return stringBuilder.toString(); } - private List cloneMenuCellsList(List originalList) { - if (originalList == null) { - return new ArrayList<>(); - } - - List clone = new ArrayList<>(); - for (MenuCell menuCell : originalList) { - clone.add(menuCell.clone()); - } - return clone; - } - - private void addUniqueNamesToCellsWithDuplicatePrimaryText(List cells) { - HashMap countsMap = new HashMap<>(); - - for (MenuCell cell : cells) { - String cellTitle = cell.getTitle(); - Integer counter = countsMap.get(cellTitle); - - if (counter != null) { - countsMap.put(cellTitle, ++counter); - cell.setUniqueTitle(cellTitle + " (" + counter + ")"); - } else { - countsMap.put(cellTitle, 1); - cell.setUniqueTitle(cellTitle); - } - - if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { - addUniqueNamesToCellsWithDuplicatePrimaryText(cell.getSubCells()); - } - } - } - - void addUniqueNamesBasedOnStrippedCells(List strippedCells, List originalCells) { - if (strippedCells == null || originalCells == null || strippedCells.size() != originalCells.size()) { - return; - } - // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary - HashMap countsMap = new HashMap<>(); - for (int i = 0; i < strippedCells.size(); i++) { - MenuCell cell = strippedCells.get(i); - Integer counter = countsMap.get(cell); - if (counter != null) { - countsMap.put(cell, ++counter); - originalCells.get(i).setUniqueTitle(originalCells.get(i).getTitle() + " (" + counter + ")"); - } else { - countsMap.put(cell, 1); - originalCells.get(i).setUniqueTitle(originalCells.get(i).getTitle()); - } - - if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { - addUniqueNamesBasedOnStrippedCells(cell.getSubCells(), originalCells.get(i).getSubCells()); - } - } - } - - List removeUnusedProperties(List cells) { + List cellsWithRemovedPropertiesFromCells(List cells, WindowCapability windowCapability) { if (cells == null) { return null; } + List removePropertiesClone = cloneMenuCellsList(cells); + for (MenuCell cell : removePropertiesClone) { // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI cell.setVoiceCommands(null); - cell.setUniqueTitle(null); - cell.setMenuSelectionListener(null); // Don't check ImageFieldName.subMenuIcon because it was added in 7.0 when the feature was added in 5.0. // Just assume that if cmdIcon is not available, the submenu icon is not either. if (!hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { cell.setIcon(null); } + // Check for subMenu fields supported if (isSubMenuCell(cell)) { if (!hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuSecondaryText)) { @@ -455,7 +403,7 @@ class MenuReplaceOperation extends Task { if (!hasImageFieldOfName(windowCapability, ImageFieldName.menuSubMenuSecondaryImage)) { cell.setSecondaryArtwork(null); } - cell.setSubCells(removeUnusedProperties(cell.getSubCells())); + cell.setSubCells(cellsWithRemovedPropertiesFromCells(cell.getSubCells(), windowCapability)); } else { if (!hasTextFieldOfName(windowCapability, TextFieldName.menuCommandSecondaryText)) { cell.setSecondaryText(null); @@ -471,6 +419,48 @@ class MenuReplaceOperation extends Task { return removePropertiesClone; } + private void addUniqueNamesToCells(List menuCells, boolean supportsMenuUniqueness) { + if (menuCells == null) { + return; + } + + // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary + HashMap dictCounter = new HashMap<>(); + + for (MenuCell cell : menuCells) { + String key = supportsMenuUniqueness ? String.valueOf(cell.hashCode()) : cell.getTitle(); + Integer counter = dictCounter.get(key); + + if (counter != null) { + dictCounter.put(key, ++counter); + } else { + dictCounter.put(key, 1); + } + + counter = dictCounter.get(key); + if (counter != null && counter > 1) { + cell.setUniqueTitle(cell.getTitle() + " (" + counter + ")"); + } + + if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + addUniqueNamesToCells(cell.getSubCells(), supportsMenuUniqueness); + } + } + } + + private void applyUniqueNamesOnCells(List fromMenuCells, List toMenuCells) { + if (fromMenuCells.size() != toMenuCells.size()) { + return; + } + + for (int i = 0; i < fromMenuCells.size(); i++) { + toMenuCells.get(i).setUniqueTitle(fromMenuCells.get(i).getUniqueTitle()); + if (isSubMenuCell(fromMenuCells.get(i)) && !fromMenuCells.get(i).getSubCells().isEmpty()) { + applyUniqueNamesOnCells(fromMenuCells.get(i).getSubCells(), toMenuCells.get(i).getSubCells()); + } + } + } + void setMenuConfiguration(MenuConfiguration menuConfiguration) { this.menuConfiguration = menuConfiguration; } -- cgit v1.2.1 From e1a08eae615988c2a8fc730890b813c4326ffb38 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 10 Aug 2021 16:27:36 -0400 Subject: Extract artwork uploading logic into a separate operation --- .../managers/screen/menu/MenuReplaceOperation.java | 97 +++++++++++++++------- 1 file changed, 68 insertions(+), 29 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index d3084d494..dfaa9d4b2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -23,6 +23,7 @@ import com.smartdevicelink.managers.file.filetypes.SdlArtwork; import com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; import com.smartdevicelink.proxy.RPCRequest; import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.rpc.SdlMsgVersion; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; @@ -85,17 +86,26 @@ class MenuReplaceOperation extends Task { } private void updateMenuCells(final CompletionListener listener) { - DynamicMenuUpdateRunScore runScore; + this.updatedStrippedMenu = cellsWithRemovedPropertiesFromCells(updatedMenu, windowCapability); + this.currentStrippedMenu = cellsWithRemovedPropertiesFromCells(currentMenu, windowCapability); + + // Check if head unit supports cells with duplicate titles + SdlMsgVersion rpcVersion = internalInterface.get().getSdlMsgVersion(); + boolean supportsMenuUniqueness = rpcVersion.getMajorVersion() > 7 || (rpcVersion.getMajorVersion() == 7 && rpcVersion.getMinorVersion() > 0); + addUniqueNamesToCells(updatedStrippedMenu, supportsMenuUniqueness); + applyUniqueNamesOnCells(updatedStrippedMenu, updatedMenu); + + DynamicMenuUpdateRunScore runScore; if (!isDynamicMenuUpdateActive) { DebugTool.logInfo(TAG, "Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); runScore = DynamicMenuUpdateAlgorithm.compatibilityRunScoreWithOldMenuCells(currentMenu, updatedMenu); } else { DebugTool.logInfo(TAG, "Dynamic menu update active. Running the algorithm to find the best way to delete / add cells."); - runScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(currentMenu, updatedMenu); + runScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(currentMenu, updatedStrippedMenu); } - // If both old and new menu cells are empty. Then nothing needs to be done. + // If both old and new menu cells are empty, nothing needs to be done. if (runScore.isEmpty()) { listener.onComplete(true); return; @@ -104,6 +114,7 @@ class MenuReplaceOperation extends Task { List deleteMenuStatus = runScore.getOldStatus(); List addMenuStatus = runScore.getUpdatedStatus(); + // Drop the cells into buckets based on the run score final List cellsToDelete = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.DELETE); final List cellsToAdd = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.ADD); @@ -116,36 +127,64 @@ class MenuReplaceOperation extends Task { // We will transfer the ids for subCells later transferCellIDFromOldCells(oldKeeps, newKeeps); - // Upload the Artworks - List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability); - if (!artworksToBeUploaded.isEmpty() && fileManager.get() != null) { - fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { - @Override - public void onComplete(Map errors) { - if (errors != null && !errors.isEmpty()) { - DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); - } else { - DebugTool.logInfo(TAG, "Menu Artworks Uploaded"); - } - updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, new CompletionListener() { - @Override - public void onComplete(boolean success) { - updateSubMenuWithOldKeptCells(oldKeeps, newKeeps, 0, listener); - } - }); + // Upload the Artworks, then we will start updating the main menu + uploadMenuArtworks(new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (getState() == Task.CANCELED) { + return; } - }); - } else { - // Cells have no artwork to load - updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, new CompletionListener() { - @Override - public void onComplete(boolean success) { - updateSubMenuWithOldKeptCells(oldKeeps, newKeeps, 0, listener); + + if (!success) { + listener.onComplete(false); + return; } - }); - } + + updateMenuWithCellsToDelete(cellsToDelete, cellsToAdd, new CompletionListener() { + @Override + public void onComplete(boolean success) { + if (getState() == Task.CANCELED) { + return; + } + + if (!success) { + listener.onComplete(false); + return; + } + + updateSubMenuWithOldKeptCells(oldKeeps, newKeeps, 0, listener); + } + }); + } + }); } + private void uploadMenuArtworks(final CompletionListener listener) { + List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability); + if (artworksToBeUploaded.isEmpty()) { + listener.onComplete(true); + return; + } + + if (fileManager.get() == null) { + listener.onComplete(false); + return; + } + + fileManager.get().uploadArtworks(artworksToBeUploaded, new MultipleFileCompletionListener() { + @Override + public void onComplete(Map errors) { + if (errors != null && !errors.isEmpty()) { + DebugTool.logError(TAG, "Error uploading Menu Artworks: " + errors.toString()); + listener.onComplete(false); + } else { + DebugTool.logInfo(TAG, "Menu artwork upload completed, beginning upload of main menu"); + listener.onComplete(true); + } + } + }); + } + /** * Takes the main menu cells to delete and add, and deletes the current menu cells, then adds the new menu cells in the correct locations * -- cgit v1.2.1 From 168e0cf94b09139efca597c771c9b6a815d6505c Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 10 Aug 2021 16:35:06 -0400 Subject: Reorder methods --- .../managers/screen/menu/MenuReplaceOperation.java | 128 ++++++++++----------- 1 file changed, 62 insertions(+), 66 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index dfaa9d4b2..05cd282e1 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -184,7 +184,7 @@ class MenuReplaceOperation extends Task { } }); } - + /** * Takes the main menu cells to delete and add, and deletes the current menu cells, then adds the new menu cells in the correct locations * @@ -196,6 +196,10 @@ class MenuReplaceOperation extends Task { sendDeleteCurrentMenu(deleteCells, new CompletionListener() { @Override public void onComplete(boolean success) { + if (getState() == Task.CANCELED) { + return; + } + sendNewMenuCells(addCells, new CompletionListener() { @Override public void onComplete(boolean success) { @@ -211,16 +215,69 @@ class MenuReplaceOperation extends Task { } /** - * Send Delete RPCs for given menu cells + * Takes the submenu cells that are old keeps and new keeps and determines which cells need to be deleted or added * - * @param deleteMenuCells The menu cells to be deleted - * @param listener A CompletionListener called when the RPCs are finished with an error if any failed + * @param oldKeptCells The old kept cells + * @param newKeptCells The new kept cells + * @param startIndex The index of the main menu to use + * @param listener A CompletionListener called when complete */ - private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { + private void updateSubMenuWithOldKeptCells(final List oldKeptCells, final List newKeptCells, final int startIndex, final CompletionListener listener) { if (getState() == Task.CANCELED) { return; } + if (oldKeptCells.isEmpty() || startIndex >= oldKeptCells.size()) { + listener.onComplete(true); + return; + } + + if (oldKeptCells.get(startIndex) != null && isSubMenuCell(oldKeptCells.get(startIndex)) && !oldKeptCells.get(startIndex).getSubCells().isEmpty()) { + DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); + + // If both old and new menu cells are empty. Then nothing needs to be done. + if (tempScore.isEmpty()) { + // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements + updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + return; + } + + List deleteMenuStatus = tempScore.getOldStatus(); + List addMenuStatus = tempScore.getUpdatedStatus(); + + final List cellsToDelete = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.DELETE); + final List cellsToAdd = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.ADD); + + final List oldKeeps = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.KEEP); + final List newKeeps = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.KEEP); + + transferCellIDFromOldCells(oldKeeps, newKeeps); + + sendDeleteCurrentMenu(cellsToDelete, new CompletionListener() { + @Override + public void onComplete(boolean success) { + sendNewMenuCells(cellsToAdd, new CompletionListener() { + @Override + public void onComplete(boolean success) { + // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements + updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + } + }); + } + }); + } else { + // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements + updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + } + } + + /** + * Send Delete RPCs for given menu cells + * + * @param deleteMenuCells The menu cells to be deleted + * @param listener A CompletionListener called when the RPCs are finished with an error if any failed + */ + private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { if (deleteMenuCells == null || deleteMenuCells.isEmpty()) { listener.onComplete(true); return; @@ -256,10 +313,6 @@ class MenuReplaceOperation extends Task { * @param listener A CompletionListener called when the RPCs are finished with an error if any failed */ private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - if (newMenuCells == null || newMenuCells.isEmpty()) { DebugTool.logInfo(TAG, "There are no cells to update."); listener.onComplete(true); @@ -322,63 +375,6 @@ class MenuReplaceOperation extends Task { }); } - /** - * Takes the submenu cells that are old keeps and new keeps and determines which cells need to be deleted or added - * - * @param oldKeptCells The old kept cells - * @param newKeptCells The new kept cells - * @param startIndex The index of the main menu to use - * @param listener A CompletionListener called when complete - */ - private void updateSubMenuWithOldKeptCells(final List oldKeptCells, final List newKeptCells, final int startIndex, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - - if (oldKeptCells.isEmpty() || startIndex >= oldKeptCells.size()) { - listener.onComplete(true); - return; - } - - if (oldKeptCells.get(startIndex) != null && isSubMenuCell(oldKeptCells.get(startIndex)) && !oldKeptCells.get(startIndex).getSubCells().isEmpty()) { - DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); - - // If both old and new menu cells are empty. Then nothing needs to be done. - if (tempScore.isEmpty()) { - // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements - updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); - return; - } - - List deleteMenuStatus = tempScore.getOldStatus(); - List addMenuStatus = tempScore.getUpdatedStatus(); - - final List cellsToDelete = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.DELETE); - final List cellsToAdd = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.ADD); - - final List oldKeeps = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.KEEP); - final List newKeeps = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.KEEP); - - transferCellIDFromOldCells(oldKeeps, newKeeps); - - sendDeleteCurrentMenu(cellsToDelete, new CompletionListener() { - @Override - public void onComplete(boolean success) { - sendNewMenuCells(cellsToAdd, new CompletionListener() { - @Override - public void onComplete(boolean success) { - // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements - updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); - } - }); - } - }); - } else { - // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements - updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); - } - } - private List filterMenuCellsWithStatusList(List menuCells, List statusList, MenuCellState menuCellState) { List filteredCells = new ArrayList<>(); for (int index = 0; index < statusList.size(); index++) { -- cgit v1.2.1 From e4d7cedf9a0d2837cef9945956b05c394ebde8a0 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 10 Aug 2021 16:49:45 -0400 Subject: Return from operation when it is cancelled --- .../managers/screen/menu/MenuReplaceOperation.java | 32 ++++++++++++++-------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 05cd282e1..d0580f3cb 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -125,7 +125,7 @@ class MenuReplaceOperation extends Task { // Since we are creating a new menu but keeping old cells, we must first transfer the old cellIDs to the new menu kept cells. // This is needed for the onCommands to still work // We will transfer the ids for subCells later - transferCellIDFromOldCells(oldKeeps, newKeeps); + transferCellIDsFromOldCells(oldKeeps, newKeeps); // Upload the Artworks, then we will start updating the main menu uploadMenuArtworks(new CompletionListener() { @@ -223,10 +223,6 @@ class MenuReplaceOperation extends Task { * @param listener A CompletionListener called when complete */ private void updateSubMenuWithOldKeptCells(final List oldKeptCells, final List newKeptCells, final int startIndex, final CompletionListener listener) { - if (getState() == Task.CANCELED) { - return; - } - if (oldKeptCells.isEmpty() || startIndex >= oldKeptCells.size()) { listener.onComplete(true); return; @@ -251,14 +247,30 @@ class MenuReplaceOperation extends Task { final List oldKeeps = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.KEEP); final List newKeeps = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.KEEP); - transferCellIDFromOldCells(oldKeeps, newKeeps); + transferCellIDsFromOldCells(oldKeeps, newKeeps); sendDeleteCurrentMenu(cellsToDelete, new CompletionListener() { @Override public void onComplete(boolean success) { + if (getState() == Task.CANCELED) { + return; + } + + if (!success) { + listener.onComplete(false); + } + sendNewMenuCells(cellsToAdd, new CompletionListener() { @Override public void onComplete(boolean success) { + if (getState() == Task.CANCELED) { + return; + } + + if (!success) { + listener.onComplete(false); + } + // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); } @@ -266,7 +278,7 @@ class MenuReplaceOperation extends Task { } }); } else { - // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements + // There are no sub cells, we can skip to the next index. updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); } } @@ -336,10 +348,6 @@ class MenuReplaceOperation extends Task { return; } - if (getState() == Task.CANCELED) { - return; - } - sendRPCs(subMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { @Override public void onComplete(boolean success, Map errors) { @@ -385,7 +393,7 @@ class MenuReplaceOperation extends Task { return filteredCells; } - private void transferCellIDFromOldCells(List oldCells, List newCells) { + private void transferCellIDsFromOldCells(List oldCells, List newCells) { if (oldCells == null || oldCells.isEmpty()) { return; } -- cgit v1.2.1 From e3ee21b4d45a663a9738b37e5d768ffb60a9b0b1 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 10 Aug 2021 17:00:09 -0400 Subject: Rename some methods to better reflect what they do --- .../screen/menu/MenuReplaceUtilitiesTests.java | 28 +++++++++++----------- .../managers/screen/menu/MenuReplaceOperation.java | 10 ++++---- .../managers/screen/menu/MenuReplaceUtilities.java | 10 ++++---- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java index c08a07311..795fd66cc 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java @@ -79,21 +79,21 @@ public class MenuReplaceUtilitiesTests { // Delete cell c4 menuCellToDelete = actualMenuCellList.get(3); - cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + cellRemoved = MenuReplaceUtilities.removeCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); assertTrue(cellRemoved); expectedMenuCellList.remove(3); assertEquals(expectedMenuCellList, actualMenuCellList); assertEquals(3, actualMenuCellList.size()); // Delete cell c4 again - removal should fail and list should not change - cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + cellRemoved = MenuReplaceUtilities.removeCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); assertFalse(cellRemoved); assertEquals(expectedMenuCellList, actualMenuCellList); assertEquals(3, actualMenuCellList.size()); // Delete cell c3 menuCellToDelete = actualMenuCellList.get(2); - cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + cellRemoved = MenuReplaceUtilities.removeCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); assertTrue(cellRemoved); expectedMenuCellList.remove(2); assertEquals(expectedMenuCellList, actualMenuCellList); @@ -101,7 +101,7 @@ public class MenuReplaceUtilitiesTests { // Delete cell c2-2-2 menuCellToDelete = actualMenuCellList.get(1).getSubCells().get(1).getSubCells().get(1); - cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + cellRemoved = MenuReplaceUtilities.removeCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); assertTrue(cellRemoved); expectedMenuCellList.get(1).getSubCells().get(1).getSubCells().remove(1); assertEquals(expectedMenuCellList, actualMenuCellList); @@ -110,7 +110,7 @@ public class MenuReplaceUtilitiesTests { // Delete cell c2-2-1 menuCellToDelete = actualMenuCellList.get(1).getSubCells().get(1).getSubCells().get(0); - cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + cellRemoved = MenuReplaceUtilities.removeCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); assertTrue(cellRemoved); expectedMenuCellList.get(1).getSubCells().get(1).getSubCells().remove(0); assertEquals(expectedMenuCellList, actualMenuCellList); @@ -119,7 +119,7 @@ public class MenuReplaceUtilitiesTests { // Delete cell c2-2 menuCellToDelete = actualMenuCellList.get(1).getSubCells().get(1); - cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + cellRemoved = MenuReplaceUtilities.removeCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); assertTrue(cellRemoved); expectedMenuCellList.get(1).getSubCells().remove(1); assertEquals(expectedMenuCellList, actualMenuCellList); @@ -128,7 +128,7 @@ public class MenuReplaceUtilitiesTests { // Delete cell c2-1 menuCellToDelete = actualMenuCellList.get(1).getSubCells().get(0); - cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + cellRemoved = MenuReplaceUtilities.removeCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); assertTrue(cellRemoved); expectedMenuCellList.get(1).getSubCells().remove(0); assertEquals(expectedMenuCellList, actualMenuCellList); @@ -137,7 +137,7 @@ public class MenuReplaceUtilitiesTests { // Delete cell c2 menuCellToDelete = actualMenuCellList.get(1); - cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + cellRemoved = MenuReplaceUtilities.removeCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); assertTrue(cellRemoved); expectedMenuCellList.remove(1); assertEquals(expectedMenuCellList, actualMenuCellList); @@ -145,7 +145,7 @@ public class MenuReplaceUtilitiesTests { // Delete cell c1 menuCellToDelete = actualMenuCellList.get(0); - cellRemoved = MenuReplaceUtilities.removeMenuCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); + cellRemoved = MenuReplaceUtilities.removeCellFromList(actualMenuCellList, menuCellToDelete.getCellId()); assertTrue(cellRemoved); expectedMenuCellList.remove(0); assertEquals(expectedMenuCellList, actualMenuCellList); @@ -162,7 +162,7 @@ public class MenuReplaceUtilitiesTests { // Add cell c5 menuCellToAdd = newMenuList.get(0); - cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 4, newMenuList, actualMenuCellList); + cellAdded = MenuReplaceUtilities.addCellWithCellId(menuCellToAdd.getCellId(), 4, newMenuList, actualMenuCellList); assertTrue(cellAdded); expectedMenuCellList.add(4, cloneMenuCellAndRemoveSubCells(menuCellToAdd)); assertEquals(expectedMenuCellList, actualMenuCellList); @@ -171,7 +171,7 @@ public class MenuReplaceUtilitiesTests { // Add cell c5-1 menuCellToAdd = newMenuList.get(0).getSubCells().get(0); - cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 0, newMenuList, actualMenuCellList); + cellAdded = MenuReplaceUtilities.addCellWithCellId(menuCellToAdd.getCellId(), 0, newMenuList, actualMenuCellList); assertTrue(cellAdded); expectedMenuCellList.get(4).getSubCells().add(0, cloneMenuCellAndRemoveSubCells(menuCellToAdd)); assertEquals(expectedMenuCellList, actualMenuCellList); @@ -180,7 +180,7 @@ public class MenuReplaceUtilitiesTests { // Add cell c5-1-1 menuCellToAdd = newMenuList.get(0).getSubCells().get(0).getSubCells().get(0); - cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 0, newMenuList, actualMenuCellList); + cellAdded = MenuReplaceUtilities.addCellWithCellId(menuCellToAdd.getCellId(), 0, newMenuList, actualMenuCellList); assertTrue(cellAdded); expectedMenuCellList.get(4).getSubCells().get(0).getSubCells().add(0, cloneMenuCellAndRemoveSubCells(menuCellToAdd)); assertEquals(expectedMenuCellList, actualMenuCellList); @@ -190,7 +190,7 @@ public class MenuReplaceUtilitiesTests { // Add cell c5-2 menuCellToAdd = newMenuList.get(0).getSubCells().get(1); - cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 1, newMenuList, actualMenuCellList); + cellAdded = MenuReplaceUtilities.addCellWithCellId(menuCellToAdd.getCellId(), 1, newMenuList, actualMenuCellList); assertTrue(cellAdded); expectedMenuCellList.get(4).getSubCells().add(1, cloneMenuCellAndRemoveSubCells(menuCellToAdd)); assertEquals(expectedMenuCellList, actualMenuCellList); @@ -201,7 +201,7 @@ public class MenuReplaceUtilitiesTests { // Add cell c5-2-1 menuCellToAdd = newMenuList.get(0).getSubCells().get(1).getSubCells().get(0); - cellAdded = MenuReplaceUtilities.addMenuRequestWithCommandId(menuCellToAdd.getCellId(), 0, newMenuList, actualMenuCellList); + cellAdded = MenuReplaceUtilities.addCellWithCellId(menuCellToAdd.getCellId(), 0, newMenuList, actualMenuCellList); assertTrue(cellAdded); expectedMenuCellList.get(4).getSubCells().get(1).getSubCells().add(0, cloneMenuCellAndRemoveSubCells(menuCellToAdd)); assertEquals(expectedMenuCellList, actualMenuCellList); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index d0580f3cb..5ee00cb1d 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -2,7 +2,7 @@ package com.smartdevicelink.managers.screen.menu; import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addMenuRequestWithCommandId; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addCellWithCellId; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.cloneMenuCellsList; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandIdForRPCRequest; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; @@ -10,7 +10,7 @@ import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.find import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.isSubMenuCell; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.positionForRPCRequest; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.removeMenuCellFromList; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.removeCellFromList; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.sendRPCs; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; @@ -312,7 +312,7 @@ class MenuReplaceOperation extends Task { if (response.getSuccess()) { // Find the id of the successful request and remove it from the current menu list wherever it may have been int commandId = commandIdForRPCRequest(request); - removeMenuCellFromList(currentMenu, commandId); + removeCellFromList(currentMenu, commandId); } } }); @@ -365,7 +365,7 @@ class MenuReplaceOperation extends Task { // Find the id of the successful request and add it from the current menu list wherever it needs to be int commandId = commandIdForRPCRequest(request); int position = positionForRPCRequest(request); - addMenuRequestWithCommandId(commandId, position, newMenuCells, currentMenu); + addCellWithCellId(commandId, position, newMenuCells, currentMenu); } } }); @@ -377,7 +377,7 @@ class MenuReplaceOperation extends Task { // Find the id of the successful request and add it from the current menu list wherever it needs to be int commandId = commandIdForRPCRequest(request); int position = positionForRPCRequest(request); - addMenuRequestWithCommandId(commandId, position, newMenuCells, currentMenu); + addCellWithCellId(commandId, position, newMenuCells, currentMenu); } } }); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 5db56dec7..6c673278b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -202,7 +202,7 @@ class MenuReplaceUtilities { .setSecondaryImage(secondaryIcon); } - static boolean removeMenuCellFromList(List menuCellList, int commandId) { + static boolean removeCellFromList(List menuCellList, int commandId) { for (MenuCell menuCell : menuCellList) { if (menuCell.getCellId() == commandId) { // If the cell id matches the command id, remove it from the list and return @@ -211,7 +211,7 @@ class MenuReplaceUtilities { } else if (isSubMenuCell(menuCell) && !menuCell.getSubCells().isEmpty()) { // If the menu cell has sub cells, we need to recurse and check the sub cells List newList = menuCell.getSubCells(); - boolean foundAndRemovedItem = removeMenuCellFromList(newList, commandId); + boolean foundAndRemovedItem = removeCellFromList(newList, commandId); if (foundAndRemovedItem) { menuCell.setSubCells(newList); return true; @@ -221,14 +221,14 @@ class MenuReplaceUtilities { return false; } - static boolean addMenuRequestWithCommandId(int commandId, int position, List newMenuList, List mainMenuList) { + static boolean addCellWithCellId(int cellId, int position, List newMenuList, List mainMenuList) { MenuCell addedCell = null; for (MenuCell cell : newMenuList) { - if (cell.getCellId() == commandId) { + if (cell.getCellId() == cellId) { addedCell = cell; break; } else if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { - boolean success = addMenuRequestWithCommandId(commandId, position, cell.getSubCells(), mainMenuList); + boolean success = addCellWithCellId(cellId, position, cell.getSubCells(), mainMenuList); if (success) { return true; } -- cgit v1.2.1 From c2609d0b79a8dfc9e869e32d7dc86f61ff0b7771 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 10 Aug 2021 17:19:17 -0400 Subject: Print more readable errors --- .../com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java | 2 ++ .../com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 5ee00cb1d..2e64599bb 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -409,11 +409,13 @@ class MenuReplaceOperation extends Task { StringBuilder stringBuilder = new StringBuilder(); for (RPCRequest request : errors.keySet()) { stringBuilder.append(errors.get(request)); + stringBuilder.append(System.getProperty("line.separator")); try { stringBuilder.append(request.serializeJSON().toString(4)); } catch (JSONException e) { e.printStackTrace(); } + stringBuilder.append(System.getProperty("line.separator")); } return stringBuilder.toString(); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 6c673278b..cec51a418 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -327,7 +327,7 @@ class MenuReplaceUtilities { } } if (!response.getSuccess()) { - errors.put(request, "Failed to send RPC. Result: " + response.getResultCode() + " Info: " + response.getInfo()); + errors.put(request, "Failed to send RPC. Result: " + response.getResultCode() + ". Info: " + response.getInfo()); } listener.onResponse(request, response); } -- cgit v1.2.1 From dca0119b642a6706abaaa149b6a6cf3d4faf8110 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 10 Aug 2021 17:25:30 -0400 Subject: Fix issue in menuCellsAreUnique --- .../com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 33f004171..82aafd50f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -448,10 +448,6 @@ abstract class BaseMenuManager extends BaseSubManager { HashSet identicalCellsCheckSet = new HashSet<>(); for (MenuCell cell : cells) { - // We don't want the UniqueTitle & listener to be considered in uniqueness check - cell.setUniqueTitle(null); - cell.setMenuSelectionListener(null); - identicalCellsCheckSet.add(cell); // Recursively check the sub-cell lists to see if they are all unique as well. If anything is not, this will chain back up the list to return false. @@ -472,7 +468,7 @@ abstract class BaseMenuManager extends BaseSubManager { // Check for duplicate cells if (identicalCellsCheckSet.size() != cells.size()) { - DebugTool.logError(TAG, "Not all cells are unique. The menu will not be set."); + DebugTool.logError(TAG, "Not all cells are unique. Cells in each list (such as main menu or sub cell list) must have some differentiating property other than the sub cells within a cell. The menu will not be set."); return false; } -- cgit v1.2.1 From d0b391b38e46c80362c0f686b80f1dfa54c2d372 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 10 Aug 2021 18:05:59 -0400 Subject: Fix issue with unique titles --- .../smartdevicelink/managers/screen/menu/MenuReplaceOperation.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 2e64599bb..1951f3fc7 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -478,13 +478,10 @@ class MenuReplaceOperation extends Task { if (counter != null) { dictCounter.put(key, ++counter); + cell.setUniqueTitle(cell.getTitle() + " (" + counter + ")"); } else { dictCounter.put(key, 1); - } - - counter = dictCounter.get(key); - if (counter != null && counter > 1) { - cell.setUniqueTitle(cell.getTitle() + " (" + counter + ")"); + cell.setUniqueTitle(cell.getTitle()); } if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { -- cgit v1.2.1 From 4cac955dfa93ee678f3a18b15c10c08cc85bac6b Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 10 Aug 2021 18:54:34 -0400 Subject: Remove unused imports --- .../managers/screen/menu/MenuReplaceUtilities.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index cec51a418..cfc91f1c6 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -1,5 +1,9 @@ package com.smartdevicelink.managers.screen.menu; +import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; +import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; + import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.managers.file.FileManager; import com.smartdevicelink.managers.file.filetypes.SdlArtwork; @@ -22,10 +26,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; -import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; - /** * Created by Bilal Alsharifi on 1/25/21. */ -- cgit v1.2.1 From e09ce8f5ff8c00f42f2cfeaeeca3b8543bf32216 Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 11 Aug 2021 09:06:19 -0400 Subject: Fix bit manipulation --- .../test/protocol/BinaryQueryHeaderTests.java | 39 ++++++++++++++++++++++ .../protocol/BinaryQueryHeader.java | 11 +++--- 2 files changed, 43 insertions(+), 7 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java index c4c5d7001..660334e32 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java @@ -4,6 +4,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.smartdevicelink.protocol.BinaryQueryHeader; import com.smartdevicelink.protocol.enums.QueryID; +import com.smartdevicelink.util.BitConverter; import org.junit.Test; import org.junit.runner.RunWith; @@ -39,6 +40,44 @@ public class BinaryQueryHeaderTests { } } + @Test + public void testCorrectParsing() { + byte[] array = new byte[12]; + array[0] = 1; + array[1] = 0; + array[2] = 0; + array[3] = 2; + array[4] = 0; + array[5] = 0; + array[6] = 0; + array[7] = 3; + array[8] = 0; + array[9] = 0; + array[10] = 0; + array[11] = 0; + + BinaryQueryHeader parsedBqh = BinaryQueryHeader.parseBinaryQueryHeader(array); + assertEquals(parsedBqh.getQueryType(), 0x1); + assertEquals(parsedBqh.getQueryID(), 2); + assertEquals(parsedBqh.getCorrelationID(), 3); + assertEquals(parsedBqh.getJsonSize(), 0); + } + + @Test + public void testCorrectHeaderAssembly() { + BinaryQueryHeader dummyBqh = new BinaryQueryHeader(); + dummyBqh.setQueryType((byte) 1); + dummyBqh.setQueryID(2); + dummyBqh.setCorrelationID(3); + dummyBqh.setJsonSize(0); + + byte[] assembledHeader = dummyBqh.assembleHeaderBytes(); + assertEquals(dummyBqh.getQueryType(), assembledHeader[0]); + assertEquals(dummyBqh.getQueryID(), BitConverter.intFromByteArray(assembledHeader, 0) & 0x00FFFFFF); + assertEquals(dummyBqh.getCorrelationID(), BitConverter.intFromByteArray(assembledHeader, 4)); + assertEquals(dummyBqh.getJsonSize(), BitConverter.intFromByteArray(assembledHeader, 8)); + } + @Test public void testAssemblyAndParse() { BinaryQueryHeader bqh = createDummyBqh(); diff --git a/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java b/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java index 7a118a142..50781f2bf 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java +++ b/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java @@ -24,10 +24,10 @@ public class BinaryQueryHeader { public static BinaryQueryHeader parseBinaryQueryHeader(byte[] binHeader) { BinaryQueryHeader msg = new BinaryQueryHeader(); - byte QUERY_Type = (byte) (binHeader[0] >>> 4); + byte QUERY_Type = (byte) (binHeader[0]); msg.setQueryType(QUERY_Type); - int _queryID = (BitConverter.intFromByteArray(binHeader, 0) & 0x0FFFFFFF); + int _queryID = (BitConverter.intFromByteArray(binHeader, 0) & 0x00FFFFFF); msg.setQueryID(_queryID); int corrID = BitConverter.intFromByteArray(binHeader, 4); @@ -69,12 +69,9 @@ public class BinaryQueryHeader { } public byte[] assembleHeaderBytes() { - int binHeader = _queryID; - binHeader &= 0xFFFFFFFF >>> 4; - binHeader |= (_queryType << 28); - byte[] ret = new byte[12]; - System.arraycopy(BitConverter.intToByteArray(binHeader), 0, ret, 0, 4); + ret[0] = _queryType; + System.arraycopy(BitConverter.intToByteArray(_queryID), 1, ret, 1, 3); System.arraycopy(BitConverter.intToByteArray(_correlationID), 0, ret, 4, 4); System.arraycopy(BitConverter.intToByteArray(_jsonSize), 0, ret, 8, 4); return ret; -- cgit v1.2.1 From 827ecd8d26ceae1010800d5a9173fb976c32aa53 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 11 Aug 2021 11:55:05 -0400 Subject: Fix not using the stripped menu --- .../managers/screen/menu/MenuReplaceOperation.java | 32 ++++++++++++---------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 1951f3fc7..6a15d4d3d 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -49,8 +49,6 @@ class MenuReplaceOperation extends Task { private WindowCapability windowCapability; private List currentMenu; private final List updatedMenu; - private List currentStrippedMenu; - private List updatedStrippedMenu; private final boolean isDynamicMenuUpdateActive; private final MenuManagerCompletionListener operationCompletionListener; private MenuConfiguration menuConfiguration; @@ -86,23 +84,27 @@ class MenuReplaceOperation extends Task { } private void updateMenuCells(final CompletionListener listener) { - this.updatedStrippedMenu = cellsWithRemovedPropertiesFromCells(updatedMenu, windowCapability); - this.currentStrippedMenu = cellsWithRemovedPropertiesFromCells(currentMenu, windowCapability); - - // Check if head unit supports cells with duplicate titles - SdlMsgVersion rpcVersion = internalInterface.get().getSdlMsgVersion(); - boolean supportsMenuUniqueness = rpcVersion.getMajorVersion() > 7 || (rpcVersion.getMajorVersion() == 7 && rpcVersion.getMinorVersion() > 0); - - addUniqueNamesToCells(updatedStrippedMenu, supportsMenuUniqueness); - applyUniqueNamesOnCells(updatedStrippedMenu, updatedMenu); - DynamicMenuUpdateRunScore runScore; if (!isDynamicMenuUpdateActive) { DebugTool.logInfo(TAG, "Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); runScore = DynamicMenuUpdateAlgorithm.compatibilityRunScoreWithOldMenuCells(currentMenu, updatedMenu); } else { DebugTool.logInfo(TAG, "Dynamic menu update active. Running the algorithm to find the best way to delete / add cells."); - runScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(currentMenu, updatedStrippedMenu); + + // Strip the "current menu" and the new menu of properties that are not displayed on the head unit + List updatedStrippedMenu = cellsWithRemovedPropertiesFromCells(updatedMenu, windowCapability); + List currentStrippedMenu = cellsWithRemovedPropertiesFromCells(currentMenu, windowCapability); + + // Check if head unit supports cells with duplicate titles + SdlMsgVersion rpcVersion = internalInterface.get().getSdlMsgVersion(); + boolean supportsMenuUniqueness = rpcVersion.getMajorVersion() > 7 || (rpcVersion.getMajorVersion() == 7 && rpcVersion.getMinorVersion() > 0); + + // Generate unique names and ensure that all menus we are tracking have them so that we can properly compare when using the dynamic algorithm + generateUniqueNamesForCells(updatedStrippedMenu, supportsMenuUniqueness); + applyUniqueNamesOnCells(updatedStrippedMenu, updatedMenu); + applyUniqueNamesOnCells(currentMenu, currentStrippedMenu); + + runScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(currentStrippedMenu, updatedStrippedMenu); } // If both old and new menu cells are empty, nothing needs to be done. @@ -464,7 +466,7 @@ class MenuReplaceOperation extends Task { return removePropertiesClone; } - private void addUniqueNamesToCells(List menuCells, boolean supportsMenuUniqueness) { + private void generateUniqueNamesForCells(List menuCells, boolean supportsMenuUniqueness) { if (menuCells == null) { return; } @@ -485,7 +487,7 @@ class MenuReplaceOperation extends Task { } if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { - addUniqueNamesToCells(cell.getSubCells(), supportsMenuUniqueness); + generateUniqueNamesForCells(cell.getSubCells(), supportsMenuUniqueness); } } } -- cgit v1.2.1 From 9e0c9c87883206ea2f4f08f7db036aa2614b5782 Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 11 Aug 2021 13:46:56 -0400 Subject: Update QueryID to byte array enum --- .../test/protocol/BinaryQueryHeaderTests.java | 2 +- .../smartdevicelink/protocol/SecurityQuery.java | 4 +- .../smartdevicelink/protocol/enums/QueryID.java | 81 ++++++++++++++++++---- .../smartdevicelink/session/BaseSdlSession.java | 12 +++- 4 files changed, 82 insertions(+), 17 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java index 660334e32..50631d726 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java @@ -25,7 +25,7 @@ public class BinaryQueryHeaderTests { public static BinaryQueryHeader createDummyBqh() { BinaryQueryHeader bqh = new BinaryQueryHeader(); bqh.setCorrelationID(123); - bqh.setQueryID(QueryID.SEND_HANDSHAKE_DATA.getValue()); + bqh.setQueryID(BitConverter.intFromByteArray(QueryID.SEND_HANDSHAKE_DATA.getValue(), 0)); bqh.setQueryType(QUERY_TYPE_REQUEST); bqh.setBulkData(null); bqh.setJsonSize(0); diff --git a/base/src/main/java/com/smartdevicelink/protocol/SecurityQuery.java b/base/src/main/java/com/smartdevicelink/protocol/SecurityQuery.java index e00a21137..5ed989bc2 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SecurityQuery.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SecurityQuery.java @@ -9,8 +9,8 @@ import com.smartdevicelink.protocol.enums.QueryType; @RestrictTo(RestrictTo.Scope.LIBRARY) public class SecurityQuery { - private QueryType _queryType = QueryType.INVALID_QUERY_TYPE; - private QueryID _queryID = QueryID.INVALID_QUERY_ID; + private QueryType _queryType; + private QueryID _queryID; private int _correlationId; private int _jsonSize; private QueryErrorCode _queryErrorCode = QueryErrorCode.ERROR_SUCCESS; diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java index 3668932d1..f9d90160b 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java +++ b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java @@ -1,10 +1,10 @@ package com.smartdevicelink.protocol.enums; -import com.smartdevicelink.util.ByteEnumer; - +import java.util.Enumeration; +import java.util.Objects; import java.util.Vector; -public class QueryID extends ByteEnumer { +public class QueryID { private static final Vector theList = new Vector<>(); @@ -12,13 +12,12 @@ public class QueryID extends ByteEnumer { return theList; } - protected QueryID(byte value, String name) { - super(value, name); - } - - public final static QueryID SEND_HANDSHAKE_DATA = new QueryID((byte) 0x01, "SEND_HANDSHAKE_DATA"); - public final static QueryID SEND_INTERNAL_ERROR = new QueryID((byte) 0x02, "SEND_INTERNAL_ERROR"); - public final static QueryID INVALID_QUERY_ID = new QueryID((byte) 0xFF, "INVALID_QUERY_ID"); + public static final byte[] sendHandshakeDataByteArray= {0x0, 0x0, 0x0, 0x0, 0x0, 0x1}; + public static final byte[] sendInternalErrorByteArray= {0x0, 0x0, 0x0, 0x0, 0x0, 0x2}; + public static final byte[] invalidQueryIdByteArray= {0xF, 0xF, 0xF, 0xF, 0xF, 0xF}; + public final static QueryID SEND_HANDSHAKE_DATA = new QueryID(sendHandshakeDataByteArray, "SEND_HANDSHAKE_DATA"); + public final static QueryID SEND_INTERNAL_ERROR = new QueryID(sendInternalErrorByteArray, "SEND_INTERNAL_ERROR"); + public final static QueryID INVALID_QUERY_ID = new QueryID(invalidQueryIdByteArray, "INVALID_QUERY_ID"); static { theList.addElement(SEND_HANDSHAKE_DATA); @@ -26,8 +25,66 @@ public class QueryID extends ByteEnumer { theList.addElement(INVALID_QUERY_ID); } - public static QueryID valueOf(byte passedByte) { - return (QueryID) get(theList, passedByte); + protected QueryID(byte[] value, String name) { + this.value = value; + this.name = name; + } + + private final byte[] value; + private final String name; + + public byte[] getValue() { + return value; + } + + public String getName() { + return name; + } + + public boolean equals(QueryID other) { + return Objects.equals(name, other.getName()); + } + + public boolean eq(QueryID other) { + return equals(other); + } + + public byte[] value() { + return value; + } + + public static QueryID get(Vector theList, byte[] value) { + Enumeration enumer = theList.elements(); + while (enumer.hasMoreElements()) { + try { + QueryID current = (QueryID) enumer.nextElement(); + if (current.getValue() == value) { + return current; + } + } catch (ClassCastException e) { + return null; + } + } + return null; + } + + public static QueryID get(Vector theList, String name) { + Enumeration enumer = theList.elements(); + while (enumer.hasMoreElements()) { + try { + QueryID current = (QueryID) enumer.nextElement(); + if (current.getName().equals(name)) { + return current; + } + } catch (ClassCastException e) { + return null; + } + } + return null; + } + + public static QueryID valueOf(byte[] passedByteArray) { + return (QueryID) get(theList, passedByteArray); } public static QueryID[] values() { diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index b15411781..0c5228fa4 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -190,6 +190,10 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ protected void processControlService(ProtocolMessage msg) { if (sdlSecurity == null) return; + + byte[] securityQueryHeader = new byte[12]; + System.arraycopy(msg.getData(), 0, securityQueryHeader, 0, 12); + int iLen = msg.getData().length - 12; byte[] data = new byte[iLen]; System.arraycopy(msg.getData(), 12, data, 0, iLen); @@ -198,8 +202,12 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ Integer iNumBytes = null; - if (data[3] == QueryID.SEND_HANDSHAKE_DATA.getValue()) { + byte[] queryID = new byte[3]; + System.arraycopy(securityQueryHeader, 1, queryID, 0, 3); + if (queryID == QueryID.SEND_HANDSHAKE_DATA.getValue() && securityQueryHeader[0] == QueryType.REQUEST.value()) { iNumBytes = sdlSecurity.runHandshake(data, dataToRead); + } else if (queryID == QueryID.SEND_INTERNAL_ERROR.value()) { + DebugTool.logError(TAG, "Internal Error processing control service"); } if (iNumBytes == null || iNumBytes <= 0) @@ -217,7 +225,7 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ SecurityQuery securityQuery = new SecurityQuery(); securityQuery.setQueryID(QueryID.SEND_HANDSHAKE_DATA); - securityQuery.setQueryType(QueryType.REQUEST); + securityQuery.setQueryType(QueryType.RESPONSE); securityQuery.setCorrelationId(msg.getCorrID()); securityQuery.setJsonSize(msg.getJsonSize()); securityQuery.setData(returnBytes); -- cgit v1.2.1 From 75a07319e608081c3ab2a352dee3d324c137ea67 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 11 Aug 2021 16:51:22 -0400 Subject: Move cell Id handeling to operation --- .../managers/screen/menu/BaseMenuManager.java | 18 -------------- .../managers/screen/menu/MenuReplaceOperation.java | 4 ++++ .../managers/screen/menu/MenuReplaceUtilities.java | 28 +++++++++++++++++++++- 3 files changed, 31 insertions(+), 19 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 82aafd50f..6497da6ce 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -66,7 +66,6 @@ import java.util.List; abstract class BaseMenuManager extends BaseSubManager { private static final String TAG = "BaseMenuManager"; - static final int menuCellIdMin = 1; static final int parentIdNotFound = 2000000000; final WeakReference fileManager; @@ -83,12 +82,10 @@ abstract class BaseMenuManager extends BaseSubManager { OnSystemCapabilityListener onDisplaysCapabilityListener; WindowCapability windowCapability; private Queue transactionQueue; - int lastMenuId; BaseMenuManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) { super(internalInterface); - this.lastMenuId = menuCellIdMin; this.menuConfiguration = new MenuConfiguration(null, null); this.menuCells = new ArrayList<>(); this.currentMenuCells = new ArrayList<>(); @@ -108,7 +105,6 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void dispose() { - lastMenuId = menuCellIdMin; menuCells = new ArrayList<>(); currentMenuCells = new ArrayList<>(); currentHMILevel = HMILevel.HMI_NONE; @@ -184,8 +180,6 @@ abstract class BaseMenuManager extends BaseSubManager { // Create a deep copy of the list so future changes by developers don't affect the algorithm logic this.menuCells = cloneMenuCellsList(cells); - updateIdsOnMenuCells(menuCells, parentIdNotFound); - boolean isDynamicMenuUpdateActive = isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType); Task operation = new MenuReplaceOperation(internalInterface, fileManager.get(), windowCapability, menuConfiguration, currentMenuCells, menuCells, isDynamicMenuUpdateActive, new MenuManagerCompletionListener() { @Override @@ -424,18 +418,6 @@ abstract class BaseMenuManager extends BaseSubManager { return true; } - private void updateIdsOnMenuCells(List menuCells, int parentId) { - for (MenuCell cell : menuCells) { - cell.setCellId(lastMenuId++); - if (parentId != parentIdNotFound) { - cell.setParentCellId(parentId); - } - if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { - updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); - } - } - } - /** * Check for cell lists with completely duplicate information, or any duplicate voiceCommands * diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 6a15d4d3d..b709e1159 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -2,6 +2,7 @@ package com.smartdevicelink.managers.screen.menu; import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; +import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addCellWithCellId; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.cloneMenuCellsList; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandIdForRPCRequest; @@ -13,6 +14,7 @@ import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.posi import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.removeCellFromList; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.sendRPCs; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.updateIdsOnMenuCells; import com.livio.taskmaster.Task; import com.smartdevicelink.managers.CompletionListener; @@ -84,6 +86,8 @@ class MenuReplaceOperation extends Task { } private void updateMenuCells(final CompletionListener listener) { + updateIdsOnMenuCells(updatedMenu, parentIdNotFound); + DynamicMenuUpdateRunScore runScore; if (!isDynamicMenuUpdateActive) { DebugTool.logInfo(TAG, "Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index cfc91f1c6..c96bbced1 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -30,6 +30,33 @@ import java.util.Map; * Created by Bilal Alsharifi on 1/25/21. */ class MenuReplaceUtilities { + private static int menuId = 0; + + static void setNextMenuId(int nextMenuId) { + menuId = nextMenuId; + } + + static int getNextMenuId() { + return ++menuId; + } + + /** + * Assign cell ids on an array of menu cells given a parent id (or no parent id) + * @param menuCells The array of menu cells to update + * @param parentId The parent id to assign if needed + */ + static void updateIdsOnMenuCells(List menuCells, int parentId) { + for (MenuCell cell : menuCells) { + cell.setCellId(getNextMenuId()); + if (parentId != parentIdNotFound) { + cell.setParentCellId(parentId); + } + if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); + } + } + } + static List findAllArtworksToBeUploadedFromCells(List cells, FileManager fileManager, WindowCapability windowCapability) { // Make sure we can use images in the menus if (!hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { @@ -267,7 +294,6 @@ class MenuReplaceUtilities { insertMenuCell(cell, menuCellList, position); return true; } - } private static void insertMenuCell(MenuCell cell, List cellList, int position) { -- cgit v1.2.1 From 96c50faf6ae9cbc13a3f91207e6c334ba08a55d6 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 11 Aug 2021 16:59:13 -0400 Subject: Simplify addMenuCell() --- .../managers/screen/menu/MenuReplaceUtilities.java | 44 +++++++++++----------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index c96bbced1..e1eb75302 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -268,32 +268,32 @@ class MenuReplaceUtilities { } private static boolean addMenuCell(MenuCell cell, List menuCellList, int position) { - if (cell.getParentCellId() != parentIdNotFound) { - // If the cell has a parent id, we need to find the cell with a matching cell id and insert it into its submenu - for (MenuCell menuCell : menuCellList) { - if (menuCell.getCellId() == cell.getParentCellId()) { - if (menuCell.getSubCells() == null) { - menuCell.setSubCells(new ArrayList()); - } - // If we found the correct submenu, insert it into that submenu - insertMenuCell(cell, menuCell.getSubCells(), position); - return true; - } else if (isSubMenuCell(menuCell) && !menuCell.getSubCells().isEmpty()) { - // Check the sub cells of this cell to see if any of those have cell ids that match the parent cell id - List newList = menuCell.getSubCells(); - boolean foundAndAddedItem = addMenuCell(cell, newList, position); - if (foundAndAddedItem) { - menuCell.setSubCells(newList); - return true; - } - } - } - return false; - } else { + if (cell.getParentCellId() == parentIdNotFound) { // The cell does not have a parent id, just insert it into the main menu insertMenuCell(cell, menuCellList, position); return true; } + + // If the cell has a parent id, we need to find the cell with a matching cell id and insert it into its submenu + for (MenuCell menuCell : menuCellList) { + if (menuCell.getCellId() == cell.getParentCellId()) { + if (menuCell.getSubCells() == null) { + menuCell.setSubCells(new ArrayList()); + } + // If we found the correct submenu, insert it into that submenu + insertMenuCell(cell, menuCell.getSubCells(), position); + return true; + } else if (isSubMenuCell(menuCell) && !menuCell.getSubCells().isEmpty()) { + // Check the sub cells of this cell to see if any of those have cell ids that match the parent cell id + List newList = menuCell.getSubCells(); + boolean foundAndAddedItem = addMenuCell(cell, newList, position); + if (foundAndAddedItem) { + menuCell.setSubCells(newList); + return true; + } + } + } + return false; } private static void insertMenuCell(MenuCell cell, List cellList, int position) { -- cgit v1.2.1 From 7bd4615e7b50354430eefbc20715dde0ff966c76 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 11 Aug 2021 17:13:10 -0400 Subject: Fix unit tests --- .../managers/screen/menu/MenuManagerTests.java | 2 -- .../managers/screen/menu/MenuReplaceOperationTests.java | 16 +--------------- .../managers/screen/menu/MenuReplaceUtilitiesTests.java | 16 +--------------- .../managers/screen/menu/MenuReplaceUtilities.java | 5 ----- 4 files changed, 2 insertions(+), 37 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index 7aef97e56..b76964ce2 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -71,7 +71,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState; import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState.ADD; import static com.smartdevicelink.managers.screen.menu.DynamicMenuUpdateAlgorithm.MenuCellState.DELETE; @@ -187,7 +186,6 @@ public class MenuManagerTests { assertEquals(BaseSubManager.SETTING_UP, menuManager.getState()); assertEquals(SystemContext.SYSCTXT_MAIN, menuManager.currentSystemContext); assertEquals(DynamicMenuUpdatesMode.ON_WITH_COMPAT_MODE, menuManager.dynamicMenuUpdatesMode); - assertEquals(menuCellIdMin, menuManager.lastMenuId); assertTrue(menuManager.menuCells.isEmpty()); assertTrue(menuManager.currentMenuCells.isEmpty()); assertNull(menuManager.menuConfiguration.getMenuLayout()); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java index f49b455fb..0079a6a99 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java @@ -63,8 +63,8 @@ import java.util.List; import java.util.Random; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.updateIdsOnMenuCells; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -75,8 +75,6 @@ import static org.mockito.Mockito.when; @RunWith(AndroidJUnit4.class) public class MenuReplaceOperationTests { - static int lastMenuId = menuCellIdMin; - private Handler mainHandler; private Taskmaster taskmaster; private Queue transactionQueue; @@ -176,18 +174,6 @@ public class MenuReplaceOperationTests { return windowCapability; } - private void updateIdsOnMenuCells(List menuCells, int parentId) { - for (MenuCell cell : menuCells) { - cell.setCellId(lastMenuId++); - if (parentId != parentIdNotFound) { - cell.setParentCellId(parentId); - } - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); - } - } - } - // Asserts on Taskmaster threads will fail silently so we need to do the assertions on main thread if the code is triggered from Taskmaster private void assertOnMainThread(Runnable runnable) { mainHandler.post(runnable); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java index 795fd66cc..85e57fee0 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java @@ -50,8 +50,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.menuCellIdMin; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.updateIdsOnMenuCells; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; @@ -64,8 +64,6 @@ import static org.mockito.Mockito.when; */ @RunWith(AndroidJUnit4.class) public class MenuReplaceUtilitiesTests { - static int lastMenuId = menuCellIdMin; - @Before public void setUp() throws Exception { } @@ -342,16 +340,4 @@ public class MenuReplaceUtilitiesTests { return newMenuList ; } - - private void updateIdsOnMenuCells(List menuCells, int parentId) { - for (MenuCell cell : menuCells) { - cell.setCellId(lastMenuId++); - if (parentId != parentIdNotFound) { - cell.setParentCellId(parentId); - } - if (cell.getSubCells() != null && !cell.getSubCells().isEmpty()) { - updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); - } - } - } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index e1eb75302..3579425ca 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -31,11 +31,6 @@ import java.util.Map; */ class MenuReplaceUtilities { private static int menuId = 0; - - static void setNextMenuId(int nextMenuId) { - menuId = nextMenuId; - } - static int getNextMenuId() { return ++menuId; } -- cgit v1.2.1 From f6c75c0ed415086f515573dd97201563e8dd7644 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 12 Aug 2021 13:28:37 -0400 Subject: Fix issue with applying unique names in compatibility mode --- .../managers/screen/menu/MenuReplaceOperation.java | 29 +++++++++++----------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index b709e1159..d717f328a 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -88,26 +88,25 @@ class MenuReplaceOperation extends Task { private void updateMenuCells(final CompletionListener listener) { updateIdsOnMenuCells(updatedMenu, parentIdNotFound); + // Strip the "current menu" and the new menu of properties that are not displayed on the head unit + List updatedStrippedMenu = cellsWithRemovedPropertiesFromCells(updatedMenu, windowCapability); + List currentStrippedMenu = cellsWithRemovedPropertiesFromCells(currentMenu, windowCapability); + + // Check if head unit supports cells with duplicate titles + SdlMsgVersion rpcVersion = internalInterface.get().getSdlMsgVersion(); + boolean supportsMenuUniqueness = rpcVersion.getMajorVersion() > 7 || (rpcVersion.getMajorVersion() == 7 && rpcVersion.getMinorVersion() > 0); + + // Generate unique names and ensure that all menus we are tracking have them so that we can properly compare when using the dynamic algorithm + generateUniqueNamesForCells(updatedStrippedMenu, supportsMenuUniqueness); + applyUniqueNamesOnCells(updatedStrippedMenu, updatedMenu); + applyUniqueNamesOnCells(currentMenu, currentStrippedMenu); + DynamicMenuUpdateRunScore runScore; if (!isDynamicMenuUpdateActive) { DebugTool.logInfo(TAG, "Dynamic menu update inactive. Forcing the deletion of all old cells and adding all new ones, even if they're the same."); - runScore = DynamicMenuUpdateAlgorithm.compatibilityRunScoreWithOldMenuCells(currentMenu, updatedMenu); + runScore = DynamicMenuUpdateAlgorithm.compatibilityRunScoreWithOldMenuCells(currentStrippedMenu, updatedStrippedMenu); } else { DebugTool.logInfo(TAG, "Dynamic menu update active. Running the algorithm to find the best way to delete / add cells."); - - // Strip the "current menu" and the new menu of properties that are not displayed on the head unit - List updatedStrippedMenu = cellsWithRemovedPropertiesFromCells(updatedMenu, windowCapability); - List currentStrippedMenu = cellsWithRemovedPropertiesFromCells(currentMenu, windowCapability); - - // Check if head unit supports cells with duplicate titles - SdlMsgVersion rpcVersion = internalInterface.get().getSdlMsgVersion(); - boolean supportsMenuUniqueness = rpcVersion.getMajorVersion() > 7 || (rpcVersion.getMajorVersion() == 7 && rpcVersion.getMinorVersion() > 0); - - // Generate unique names and ensure that all menus we are tracking have them so that we can properly compare when using the dynamic algorithm - generateUniqueNamesForCells(updatedStrippedMenu, supportsMenuUniqueness); - applyUniqueNamesOnCells(updatedStrippedMenu, updatedMenu); - applyUniqueNamesOnCells(currentMenu, currentStrippedMenu); - runScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(currentStrippedMenu, updatedStrippedMenu); } -- cgit v1.2.1 From fe5928a4bebccd187cb63f854a66d21b5db73e21 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 12 Aug 2021 13:57:46 -0400 Subject: Fix Lib response --- .../test/protocol/BinaryQueryHeaderTests.java | 2 +- .../managers/audio/AudioStreamManager.java | 2 +- .../managers/video/VideoStreamManager.java | 2 +- .../managers/lifecycle/BaseLifecycleManager.java | 2 +- .../smartdevicelink/protocol/SdlProtocolBase.java | 17 ++------ .../smartdevicelink/protocol/enums/QueryID.java | 14 +++++-- .../smartdevicelink/session/BaseSdlSession.java | 49 ++++++++++++++-------- 7 files changed, 49 insertions(+), 39 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java index 50631d726..a8ab052d8 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java @@ -25,7 +25,7 @@ public class BinaryQueryHeaderTests { public static BinaryQueryHeader createDummyBqh() { BinaryQueryHeader bqh = new BinaryQueryHeader(); bqh.setCorrelationID(123); - bqh.setQueryID(BitConverter.intFromByteArray(QueryID.SEND_HANDSHAKE_DATA.getValue(), 0)); + bqh.setQueryID(QueryID.SEND_HANDSHAKE_DATA.getIntValue()); bqh.setQueryType(QUERY_TYPE_REQUEST); bqh.setBulkData(null); bqh.setJsonSize(0); diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java index c28401b07..667522372 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/audio/AudioStreamManager.java @@ -530,7 +530,7 @@ public class AudioStreamManager extends BaseAudioStreamManager { IStreamListener streamListener = new IStreamListener() { @Override public void sendStreamPacket(ProtocolMessage pm) { - session.sendMessage(pm, null); + session.sendMessage(pm); } }; diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java index b3899c539..cce131baa 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/video/VideoStreamManager.java @@ -1065,7 +1065,7 @@ public class VideoStreamManager extends BaseVideoStreamManager { IStreamListener iStreamListener = new IStreamListener() { @Override public void sendStreamPacket(ProtocolMessage pm) { - session.sendMessage(pm, null); + session.sendMessage(pm); } }; 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 8a783862b..9cf72f7cd 100644 --- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java @@ -866,7 +866,7 @@ abstract class BaseLifecycleManager { pm.setPriorityCoefficient(1); } - session.sendMessage(pm, null); + session.sendMessage(pm); } catch (OutOfMemoryError e) { DebugTool.logError(TAG,"Error attempting to send RPC message.", e); diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java index b1723dc6a..b49c82da3 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java @@ -562,7 +562,7 @@ public class SdlProtocolBase { } } // end-method - public void sendMessage(ProtocolMessage protocolMsg, SecurityQuery securityQuery) { + public void sendMessage(ProtocolMessage protocolMsg) { SessionType sessionType = protocolMsg.getSessionType(); byte sessionID = protocolMsg.getSessionID(); boolean requiresEncryption = protocolMsg.getPayloadProtected(); @@ -571,19 +571,8 @@ public class SdlProtocolBase { if (protocolVersion.getMajor() > 1 && sessionType != SessionType.NAV && sessionType != SessionType.PCM) { if (sessionType.eq(SessionType.CONTROL)) { final byte[] secureData = protocolMsg.getData().clone(); - - if (securityQuery != null) { - data = new byte[12 + secureData.length]; - final BinaryQueryHeader binQueryHeader = SdlPacketFactory.createBinaryQueryHeader(securityQuery.getQueryType().getValue(), securityQuery.getQueryType().getValue(), securityQuery.getCorrelationId(), securityQuery.getJsonSize()); - System.arraycopy(binQueryHeader.assembleHeaderBytes(), 0, data, 0, 12); - System.arraycopy(secureData, 0, data, 12, secureData.length); - } else { - data = new byte[headerSize + secureData.length]; - final BinaryFrameHeader binFrameHeader = - SdlPacketFactory.createBinaryFrameHeader(protocolMsg.getRPCType(), protocolMsg.getFunctionID(), protocolMsg.getCorrID(), 0); - System.arraycopy(binFrameHeader.assembleHeaderBytes(), 0, data, 0, headerSize); - System.arraycopy(secureData, 0, data, headerSize, secureData.length); - } + data = new byte[headerSize + secureData.length]; + System.arraycopy(secureData, 0, data, 0, secureData.length); } else if (protocolMsg.getBulkData() != null) { data = new byte[12 + protocolMsg.getJsonSize() + protocolMsg.getBulkData().length]; sessionType = SessionType.BULK_DATA; diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java index f9d90160b..7e6a8c89c 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java +++ b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java @@ -1,5 +1,7 @@ package com.smartdevicelink.protocol.enums; +import com.smartdevicelink.util.BitConverter; + import java.util.Enumeration; import java.util.Objects; import java.util.Vector; @@ -12,9 +14,9 @@ public class QueryID { return theList; } - public static final byte[] sendHandshakeDataByteArray= {0x0, 0x0, 0x0, 0x0, 0x0, 0x1}; - public static final byte[] sendInternalErrorByteArray= {0x0, 0x0, 0x0, 0x0, 0x0, 0x2}; - public static final byte[] invalidQueryIdByteArray= {0xF, 0xF, 0xF, 0xF, 0xF, 0xF}; + private static final byte[] sendHandshakeDataByteArray= {(byte) 0x00, (byte) 0x00, (byte) 0x01}; + private static final byte[] sendInternalErrorByteArray= {(byte) 0x00, (byte) 0x00, (byte) 0x02}; + private static final byte[] invalidQueryIdByteArray= {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; public final static QueryID SEND_HANDSHAKE_DATA = new QueryID(sendHandshakeDataByteArray, "SEND_HANDSHAKE_DATA"); public final static QueryID SEND_INTERNAL_ERROR = new QueryID(sendInternalErrorByteArray, "SEND_INTERNAL_ERROR"); public final static QueryID INVALID_QUERY_ID = new QueryID(invalidQueryIdByteArray, "INVALID_QUERY_ID"); @@ -37,6 +39,12 @@ public class QueryID { return value; } + public int getIntValue() { + byte[] copy = new byte[4]; + System.arraycopy(value, 0, copy, 1, 3); + return BitConverter.intFromByteArray(copy, 0); + } + public String getName() { return name; } diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index 0c5228fa4..9cfaa21a9 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -36,10 +36,12 @@ import androidx.annotation.RestrictTo; import com.smartdevicelink.exception.SdlException; import com.smartdevicelink.managers.lifecycle.RpcConverter; +import com.smartdevicelink.protocol.BinaryQueryHeader; import com.smartdevicelink.protocol.ISdlProtocol; import com.smartdevicelink.protocol.ISdlServiceListener; import com.smartdevicelink.protocol.ProtocolMessage; import com.smartdevicelink.protocol.SdlPacket; +import com.smartdevicelink.protocol.SdlPacketFactory; import com.smartdevicelink.protocol.SdlProtocolBase; import com.smartdevicelink.protocol.SecurityQuery; import com.smartdevicelink.protocol.enums.ControlFrameTags; @@ -55,10 +57,12 @@ import com.smartdevicelink.security.SdlSecurityBase; import com.smartdevicelink.streaming.video.VideoStreamingParameters; import com.smartdevicelink.transport.BaseTransportConfig; import com.smartdevicelink.transport.enums.TransportType; +import com.smartdevicelink.util.BitConverter; import com.smartdevicelink.util.DebugTool; import com.smartdevicelink.util.SystemInfo; import com.smartdevicelink.util.Version; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.ListIterator; @@ -150,11 +154,11 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ } - public void sendMessage(ProtocolMessage msg, SecurityQuery securityQuery) { + public void sendMessage(ProtocolMessage msg) { if (sdlProtocol == null) { return; } - sdlProtocol.sendMessage(msg, securityQuery); + sdlProtocol.sendMessage(msg); } public TransportType getCurrentTransportType() { @@ -204,17 +208,33 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ byte[] queryID = new byte[3]; System.arraycopy(securityQueryHeader, 1, queryID, 0, 3); - if (queryID == QueryID.SEND_HANDSHAKE_DATA.getValue() && securityQueryHeader[0] == QueryType.REQUEST.value()) { - iNumBytes = sdlSecurity.runHandshake(data, dataToRead); - } else if (queryID == QueryID.SEND_INTERNAL_ERROR.value()) { - DebugTool.logError(TAG, "Internal Error processing control service"); - } - if (iNumBytes == null || iNumBytes <= 0) + if (!Arrays.equals(queryID, QueryID.SEND_HANDSHAKE_DATA.getValue()) + || (securityQueryHeader[0] != QueryType.NOTIFICATION.value() && securityQueryHeader[0] != QueryType.REQUEST.value())) { return; + } + + iNumBytes = sdlSecurity.runHandshake(data, dataToRead); + SecurityQuery securityQuery = new SecurityQuery(); + + if (iNumBytes == null || iNumBytes <= 0) { + DebugTool.logError(TAG, "Internal Error processing control service"); + + securityQuery.setQueryID(QueryID.SEND_INTERNAL_ERROR); + securityQuery.setQueryType(QueryType.NOTIFICATION); + securityQuery.setCorrelationId(msg.getCorrID()); + securityQuery.setJsonSize(0); + } else { + securityQuery.setQueryID(QueryID.SEND_HANDSHAKE_DATA); + securityQuery.setQueryType(QueryType.RESPONSE); + securityQuery.setCorrelationId(msg.getCorrID()); + securityQuery.setJsonSize(0); + } - byte[] returnBytes = new byte[iNumBytes]; - System.arraycopy(dataToRead, 0, returnBytes, 0, iNumBytes); + byte[] returnBytes = new byte[iNumBytes + 12]; + BinaryQueryHeader binaryQueryHeader = SdlPacketFactory.createBinaryQueryHeader(securityQuery.getQueryType().getValue(), securityQuery.getQueryID().getIntValue(), securityQuery.getCorrelationId(), securityQuery.getJsonSize()); + System.arraycopy(binaryQueryHeader.assembleHeaderBytes(), 0, returnBytes, 0, 12); + System.arraycopy(dataToRead, 0, returnBytes, 12, iNumBytes); ProtocolMessage protocolMessage = new ProtocolMessage(); protocolMessage.setSessionType(SessionType.CONTROL); @@ -223,16 +243,9 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ protocolMessage.setVersion((byte) sdlProtocol.getProtocolVersion().getMajor()); protocolMessage.setSessionID((byte) this.sessionId); - SecurityQuery securityQuery = new SecurityQuery(); - securityQuery.setQueryID(QueryID.SEND_HANDSHAKE_DATA); - securityQuery.setQueryType(QueryType.RESPONSE); - securityQuery.setCorrelationId(msg.getCorrID()); - securityQuery.setJsonSize(msg.getJsonSize()); - securityQuery.setData(returnBytes); - //sdlSecurity.hs(); - sendMessage(msg, securityQuery); + sendMessage(protocolMessage); } /** -- cgit v1.2.1 From 89b3a6c45cdd8433b34697c84cb898f752603cf3 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 12 Aug 2021 15:14:07 -0400 Subject: Fix potential NPE --- .../java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 6497da6ce..26e2f2816 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -169,7 +169,7 @@ abstract class BaseMenuManager extends BaseSubManager { return; } - if (this.menuCells.equals(cells)) { + if (cells.equals(this.menuCells)) { DebugTool.logError(TAG, "The set menu cells are identical to previously set menu cells. Skipping..."); return; } else if (!menuCellsAreUnique(cells, new ArrayList())) { -- cgit v1.2.1 From a2cee42e4183f452427f2df64ec02d65f016e448 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 12 Aug 2021 16:31:24 -0400 Subject: remove SecurityQuery in favor of BQH --- .../test/protocol/BinaryQueryHeaderTests.java | 25 +++-- .../protocol/BinaryQueryHeader.java | 42 +++++---- .../smartdevicelink/protocol/SdlPacketFactory.java | 4 +- .../smartdevicelink/protocol/SecurityQuery.java | 102 --------------------- .../smartdevicelink/protocol/enums/QueryID.java | 3 +- .../smartdevicelink/session/BaseSdlSession.java | 42 +++++---- 6 files changed, 63 insertions(+), 155 deletions(-) delete mode 100644 base/src/main/java/com/smartdevicelink/protocol/SecurityQuery.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java index a8ab052d8..f55816f99 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java @@ -4,6 +4,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.smartdevicelink.protocol.BinaryQueryHeader; import com.smartdevicelink.protocol.enums.QueryID; +import com.smartdevicelink.protocol.enums.QueryType; import com.smartdevicelink.util.BitConverter; import org.junit.Test; @@ -18,15 +19,11 @@ import static org.junit.Assert.fail; @RunWith(AndroidJUnit4.class) public class BinaryQueryHeaderTests { - public static final byte QUERY_TYPE_REQUEST = 0x00; - public static final byte QUERY_TYPE_RESPONSE = 0x01; - public static final byte QUERY_TYPE_NOTIFICATION = 0x02; - public static BinaryQueryHeader createDummyBqh() { BinaryQueryHeader bqh = new BinaryQueryHeader(); bqh.setCorrelationID(123); - bqh.setQueryID(QueryID.SEND_HANDSHAKE_DATA.getIntValue()); - bqh.setQueryType(QUERY_TYPE_REQUEST); + bqh.setQueryID(QueryID.SEND_HANDSHAKE_DATA); + bqh.setQueryType(QueryType.REQUEST); bqh.setBulkData(null); bqh.setJsonSize(0); return bqh; @@ -43,7 +40,7 @@ public class BinaryQueryHeaderTests { @Test public void testCorrectParsing() { byte[] array = new byte[12]; - array[0] = 1; + array[0] = 0; array[1] = 0; array[2] = 0; array[3] = 2; @@ -57,8 +54,8 @@ public class BinaryQueryHeaderTests { array[11] = 0; BinaryQueryHeader parsedBqh = BinaryQueryHeader.parseBinaryQueryHeader(array); - assertEquals(parsedBqh.getQueryType(), 0x1); - assertEquals(parsedBqh.getQueryID(), 2); + assertEquals(parsedBqh.getQueryType(), QueryType.REQUEST); + assertEquals(parsedBqh.getQueryID(), QueryID.SEND_INTERNAL_ERROR); assertEquals(parsedBqh.getCorrelationID(), 3); assertEquals(parsedBqh.getJsonSize(), 0); } @@ -66,14 +63,16 @@ public class BinaryQueryHeaderTests { @Test public void testCorrectHeaderAssembly() { BinaryQueryHeader dummyBqh = new BinaryQueryHeader(); - dummyBqh.setQueryType((byte) 1); - dummyBqh.setQueryID(2); + dummyBqh.setQueryType(QueryType.REQUEST); + dummyBqh.setQueryID(QueryID.SEND_HANDSHAKE_DATA); dummyBqh.setCorrelationID(3); dummyBqh.setJsonSize(0); byte[] assembledHeader = dummyBqh.assembleHeaderBytes(); - assertEquals(dummyBqh.getQueryType(), assembledHeader[0]); - assertEquals(dummyBqh.getQueryID(), BitConverter.intFromByteArray(assembledHeader, 0) & 0x00FFFFFF); + assertEquals(dummyBqh.getQueryType(), QueryType.valueOf(assembledHeader[0])); + byte[] queryIDFromHeader = new byte[3]; + System.arraycopy(assembledHeader, 1, queryIDFromHeader, 0, 3); + assertEquals(dummyBqh.getQueryID(), QueryID.valueOf(queryIDFromHeader)); assertEquals(dummyBqh.getCorrelationID(), BitConverter.intFromByteArray(assembledHeader, 4)); assertEquals(dummyBqh.getJsonSize(), BitConverter.intFromByteArray(assembledHeader, 8)); } diff --git a/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java b/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java index 50781f2bf..ea474ee62 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java +++ b/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java @@ -2,6 +2,9 @@ package com.smartdevicelink.protocol; import androidx.annotation.RestrictTo; +import com.smartdevicelink.protocol.enums.QueryErrorCode; +import com.smartdevicelink.protocol.enums.QueryID; +import com.smartdevicelink.protocol.enums.QueryType; import com.smartdevicelink.util.BitConverter; import com.smartdevicelink.util.DebugTool; @@ -9,14 +12,14 @@ import com.smartdevicelink.util.DebugTool; public class BinaryQueryHeader { private static final String TAG = "BinaryQueryHeader"; - private byte _queryType; - private int _queryID; + private QueryType _queryType; + private QueryID _queryID; private int _correlationID; private int _jsonSize; - private int _errorCode; + private QueryErrorCode _errorCode; - private byte[] _jsonData; - private byte[] _bulkData; + private byte[] _jsonData = null; + private byte[] _bulkData = null; public BinaryQueryHeader() { } @@ -25,10 +28,11 @@ public class BinaryQueryHeader { BinaryQueryHeader msg = new BinaryQueryHeader(); byte QUERY_Type = (byte) (binHeader[0]); - msg.setQueryType(QUERY_Type); + msg.setQueryType(QueryType.valueOf(QUERY_Type)); - int _queryID = (BitConverter.intFromByteArray(binHeader, 0) & 0x00FFFFFF); - msg.setQueryID(_queryID); + byte[] _queryID = new byte[3]; + System.arraycopy(binHeader, 1, _queryID, 0, 3); + msg.setQueryID(QueryID.valueOf(_queryID)); int corrID = BitConverter.intFromByteArray(binHeader, 4); msg.setCorrelationID(corrID); @@ -36,9 +40,9 @@ public class BinaryQueryHeader { int _jsonSize = BitConverter.intFromByteArray(binHeader, 8); msg.setJsonSize(_jsonSize); - if (msg.getQueryType() == 0x20 && msg.getQueryID() == 0x02) { + if (msg.getQueryType() == QueryType.NOTIFICATION && msg.getQueryID() == QueryID.SEND_INTERNAL_ERROR) { int _errorCode = BitConverter.intFromByteArray(binHeader, binHeader.length-1); - msg.setErrorCode(_errorCode); + msg.setErrorCode(QueryErrorCode.valueOf((byte) _errorCode)); } try { @@ -50,7 +54,7 @@ public class BinaryQueryHeader { if (binHeader.length - _jsonSize - 12 > 0) { byte[] _bulkData; - if (msg.getQueryType() == 0x20 && msg.getQueryID() == 0x02) { + if (msg.getQueryType() == QueryType.NOTIFICATION && msg.getQueryID() == QueryID.SEND_INTERNAL_ERROR) { _bulkData = new byte[binHeader.length - _jsonSize - 12 - 1]; System.arraycopy(binHeader, 12 + _jsonSize, _bulkData, 0, _bulkData.length - 1); } else { @@ -70,26 +74,26 @@ public class BinaryQueryHeader { public byte[] assembleHeaderBytes() { byte[] ret = new byte[12]; - ret[0] = _queryType; - System.arraycopy(BitConverter.intToByteArray(_queryID), 1, ret, 1, 3); + ret[0] = _queryType.getValue(); + System.arraycopy(_queryID.getValue(), 0, ret, 1, 3); System.arraycopy(BitConverter.intToByteArray(_correlationID), 0, ret, 4, 4); System.arraycopy(BitConverter.intToByteArray(_jsonSize), 0, ret, 8, 4); return ret; } - public byte getQueryType() { + public QueryType getQueryType() { return _queryType; } - public void setQueryType(byte _queryType) { + public void setQueryType(QueryType _queryType) { this._queryType = _queryType; } - public int getQueryID() { + public QueryID getQueryID() { return _queryID; } - public void setQueryID(int _queryID) { + public void setQueryID(QueryID _queryID) { this._queryID = _queryID; } @@ -109,11 +113,11 @@ public class BinaryQueryHeader { this._jsonSize = _jsonSize; } - public int getErrorCode() { + public QueryErrorCode getErrorCode() { return _errorCode; } - public void setErrorCode(int _errorCode) { + public void setErrorCode(QueryErrorCode _errorCode) { this._errorCode = _errorCode; } diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java b/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java index 75d122da5..eaad041ca 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java @@ -35,6 +35,8 @@ import androidx.annotation.RestrictTo; import com.smartdevicelink.protocol.enums.ControlFrameTags; import com.smartdevicelink.protocol.enums.FrameDataControlFrameType; +import com.smartdevicelink.protocol.enums.QueryID; +import com.smartdevicelink.protocol.enums.QueryType; import com.smartdevicelink.protocol.enums.SessionType; import com.smartdevicelink.util.BitConverter; @@ -144,7 +146,7 @@ public class SdlPacketFactory { return msg; } - public static BinaryQueryHeader createBinaryQueryHeader(byte queryType, int queryId, int corrID, int jsonSize) { + public static BinaryQueryHeader createBinaryQueryHeader(QueryType queryType, QueryID queryId, int corrID, int jsonSize) { BinaryQueryHeader msg = new BinaryQueryHeader(); msg.setQueryType(queryType); msg.setQueryID(queryId); diff --git a/base/src/main/java/com/smartdevicelink/protocol/SecurityQuery.java b/base/src/main/java/com/smartdevicelink/protocol/SecurityQuery.java deleted file mode 100644 index 5ed989bc2..000000000 --- a/base/src/main/java/com/smartdevicelink/protocol/SecurityQuery.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.smartdevicelink.protocol; - -import androidx.annotation.RestrictTo; - -import com.smartdevicelink.protocol.enums.QueryErrorCode; -import com.smartdevicelink.protocol.enums.QueryID; -import com.smartdevicelink.protocol.enums.QueryType; - -@RestrictTo(RestrictTo.Scope.LIBRARY) -public class SecurityQuery { - - private QueryType _queryType; - private QueryID _queryID; - private int _correlationId; - private int _jsonSize; - private QueryErrorCode _queryErrorCode = QueryErrorCode.ERROR_SUCCESS; - - private byte[] _data = null; - private byte[] _bulkData = null; - - public SecurityQuery() { - } - - public QueryType getQueryType() { - return this._queryType; - } - - public void setQueryType(QueryType queryType) { - this._queryType = queryType; - } - - public QueryID getQueryID() { - return this._queryID; - } - - public void setQueryID(QueryID queryID) { - this._queryID = queryID; - } - - public int getCorrelationId() { - return this._correlationId; - } - - public void setCorrelationId(int msgId) { - this._correlationId = msgId; - } - - public int getJsonSize() { - return this._jsonSize; - } - - public void setJsonSize(int jsonSize) { - this._jsonSize = jsonSize; - } - - public QueryErrorCode getQueryErrorCode() { - return this._queryErrorCode; - } - - public void setQueryErrorCode(QueryErrorCode queryErrorCode) { - this._queryErrorCode = queryErrorCode; - } - - public byte[] getData() { - return _data; - } - - public void setData(byte[] data) { - this._data = data; - this._jsonSize = data.length; - } - - public void setData(byte[] data, int offset, int length) { - if (this._data != null) - this._data = null; - this._data = new byte[length]; - System.arraycopy(data, offset, this._data, 0, length); - this._jsonSize = 0; - } - - public byte[] getBulkData() { - return _bulkData; - } - - public void setBulkDataNoCopy(byte[] bulkData) { - this._bulkData = bulkData; - } - - public void setBulkData(byte[] bulkData) { - if (this._bulkData != null) - this._bulkData = null; - this._bulkData = new byte[bulkData.length]; - System.arraycopy(bulkData, 0, this._bulkData, 0, bulkData.length); - } - - public void setBulkData(byte[] bulkData, int length) { - if (this._bulkData != null) - this._bulkData = null; - this._bulkData = new byte[length]; - System.arraycopy(bulkData, 0, this._bulkData, 0, length); - } -} diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java index 7e6a8c89c..e24481340 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java +++ b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java @@ -2,6 +2,7 @@ package com.smartdevicelink.protocol.enums; import com.smartdevicelink.util.BitConverter; +import java.util.Arrays; import java.util.Enumeration; import java.util.Objects; import java.util.Vector; @@ -66,7 +67,7 @@ public class QueryID { while (enumer.hasMoreElements()) { try { QueryID current = (QueryID) enumer.nextElement(); - if (current.getValue() == value) { + if (Arrays.equals(current.getValue(), value)) { return current; } } catch (ClassCastException e) { diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index 9cfaa21a9..93c3358a2 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -41,10 +41,9 @@ import com.smartdevicelink.protocol.ISdlProtocol; import com.smartdevicelink.protocol.ISdlServiceListener; import com.smartdevicelink.protocol.ProtocolMessage; import com.smartdevicelink.protocol.SdlPacket; -import com.smartdevicelink.protocol.SdlPacketFactory; import com.smartdevicelink.protocol.SdlProtocolBase; -import com.smartdevicelink.protocol.SecurityQuery; import com.smartdevicelink.protocol.enums.ControlFrameTags; +import com.smartdevicelink.protocol.enums.QueryErrorCode; import com.smartdevicelink.protocol.enums.QueryID; import com.smartdevicelink.protocol.enums.QueryType; import com.smartdevicelink.protocol.enums.SessionType; @@ -195,8 +194,7 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ if (sdlSecurity == null) return; - byte[] securityQueryHeader = new byte[12]; - System.arraycopy(msg.getData(), 0, securityQueryHeader, 0, 12); + BinaryQueryHeader receivedHeader = BinaryQueryHeader.parseBinaryQueryHeader(msg.getData().clone()); int iLen = msg.getData().length - 12; byte[] data = new byte[iLen]; @@ -206,34 +204,40 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ Integer iNumBytes = null; - byte[] queryID = new byte[3]; - System.arraycopy(securityQueryHeader, 1, queryID, 0, 3); + if (receivedHeader.getQueryID() == QueryID.SEND_INTERNAL_ERROR + && receivedHeader.getQueryType() == QueryType.NOTIFICATION) { + if (receivedHeader.getErrorCode() != null) { + DebugTool.logError(TAG, "Client internal error: " + receivedHeader.getErrorCode().getName()); + } else { + DebugTool.logError(TAG, "Client internal error: No information provided"); + } + return; + } - if (!Arrays.equals(queryID, QueryID.SEND_HANDSHAKE_DATA.getValue()) - || (securityQueryHeader[0] != QueryType.NOTIFICATION.value() && securityQueryHeader[0] != QueryType.REQUEST.value())) { + if (receivedHeader.getQueryID() != QueryID.SEND_HANDSHAKE_DATA + && (receivedHeader.getQueryType() != QueryType.NOTIFICATION || receivedHeader.getQueryType() != QueryType.REQUEST)) { return; } iNumBytes = sdlSecurity.runHandshake(data, dataToRead); - SecurityQuery securityQuery = new SecurityQuery(); + BinaryQueryHeader responseHeader = new BinaryQueryHeader(); if (iNumBytes == null || iNumBytes <= 0) { DebugTool.logError(TAG, "Internal Error processing control service"); - securityQuery.setQueryID(QueryID.SEND_INTERNAL_ERROR); - securityQuery.setQueryType(QueryType.NOTIFICATION); - securityQuery.setCorrelationId(msg.getCorrID()); - securityQuery.setJsonSize(0); + responseHeader.setQueryID(QueryID.SEND_INTERNAL_ERROR); + responseHeader.setQueryType(QueryType.NOTIFICATION); + responseHeader.setCorrelationID(msg.getCorrID()); + responseHeader.setJsonSize(0); } else { - securityQuery.setQueryID(QueryID.SEND_HANDSHAKE_DATA); - securityQuery.setQueryType(QueryType.RESPONSE); - securityQuery.setCorrelationId(msg.getCorrID()); - securityQuery.setJsonSize(0); + responseHeader.setQueryID(QueryID.SEND_HANDSHAKE_DATA); + responseHeader.setQueryType(QueryType.RESPONSE); + responseHeader.setCorrelationID(msg.getCorrID()); + responseHeader.setJsonSize(0); } byte[] returnBytes = new byte[iNumBytes + 12]; - BinaryQueryHeader binaryQueryHeader = SdlPacketFactory.createBinaryQueryHeader(securityQuery.getQueryType().getValue(), securityQuery.getQueryID().getIntValue(), securityQuery.getCorrelationId(), securityQuery.getJsonSize()); - System.arraycopy(binaryQueryHeader.assembleHeaderBytes(), 0, returnBytes, 0, 12); + System.arraycopy(responseHeader.assembleHeaderBytes(), 0, returnBytes, 0, 12); System.arraycopy(dataToRead, 0, returnBytes, 12, iNumBytes); ProtocolMessage protocolMessage = new ProtocolMessage(); -- cgit v1.2.1 From 7a3557996fad3426173318471a1de6813cdee38e Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Thu, 12 Aug 2021 16:42:21 -0400 Subject: Remove unnecessary line --- .../com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java | 1 - 1 file changed, 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index d717f328a..0a1f92e03 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -99,7 +99,6 @@ class MenuReplaceOperation extends Task { // Generate unique names and ensure that all menus we are tracking have them so that we can properly compare when using the dynamic algorithm generateUniqueNamesForCells(updatedStrippedMenu, supportsMenuUniqueness); applyUniqueNamesOnCells(updatedStrippedMenu, updatedMenu); - applyUniqueNamesOnCells(currentMenu, currentStrippedMenu); DynamicMenuUpdateRunScore runScore; if (!isDynamicMenuUpdateActive) { -- cgit v1.2.1 From 25951389c6a11db8d612c64ad2fe3de7259cb460 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 12 Aug 2021 16:43:17 -0400 Subject: Refactor to SecurityQueryPayload --- .../test/protocol/BinaryQueryHeaderTests.java | 131 ------------------- .../test/protocol/SecurityQueryPayloadTests.java | 131 +++++++++++++++++++ .../protocol/BinaryQueryHeader.java | 140 --------------------- .../smartdevicelink/protocol/SdlPacketFactory.java | 10 -- .../protocol/SecurityQueryPayload.java | 139 ++++++++++++++++++++ .../smartdevicelink/session/BaseSdlSession.java | 9 +- 6 files changed, 273 insertions(+), 287 deletions(-) delete mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/SecurityQueryPayloadTests.java delete mode 100644 base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java create mode 100644 base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java deleted file mode 100644 index f55816f99..000000000 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/BinaryQueryHeaderTests.java +++ /dev/null @@ -1,131 +0,0 @@ -package com.smartdevicelink.test.protocol; - -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.smartdevicelink.protocol.BinaryQueryHeader; -import com.smartdevicelink.protocol.enums.QueryID; -import com.smartdevicelink.protocol.enums.QueryType; -import com.smartdevicelink.util.BitConverter; - -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.fail; - -@RunWith(AndroidJUnit4.class) -public class BinaryQueryHeaderTests { - - public static BinaryQueryHeader createDummyBqh() { - BinaryQueryHeader bqh = new BinaryQueryHeader(); - bqh.setCorrelationID(123); - bqh.setQueryID(QueryID.SEND_HANDSHAKE_DATA); - bqh.setQueryType(QueryType.REQUEST); - bqh.setBulkData(null); - bqh.setJsonSize(0); - return bqh; - } - - public BinaryQueryHeader safeParse(byte[] array) { - try { - return BinaryQueryHeader.parseBinaryQueryHeader(array); - } catch (Exception e) { - return null; - } - } - - @Test - public void testCorrectParsing() { - byte[] array = new byte[12]; - array[0] = 0; - array[1] = 0; - array[2] = 0; - array[3] = 2; - array[4] = 0; - array[5] = 0; - array[6] = 0; - array[7] = 3; - array[8] = 0; - array[9] = 0; - array[10] = 0; - array[11] = 0; - - BinaryQueryHeader parsedBqh = BinaryQueryHeader.parseBinaryQueryHeader(array); - assertEquals(parsedBqh.getQueryType(), QueryType.REQUEST); - assertEquals(parsedBqh.getQueryID(), QueryID.SEND_INTERNAL_ERROR); - assertEquals(parsedBqh.getCorrelationID(), 3); - assertEquals(parsedBqh.getJsonSize(), 0); - } - - @Test - public void testCorrectHeaderAssembly() { - BinaryQueryHeader dummyBqh = new BinaryQueryHeader(); - dummyBqh.setQueryType(QueryType.REQUEST); - dummyBqh.setQueryID(QueryID.SEND_HANDSHAKE_DATA); - dummyBqh.setCorrelationID(3); - dummyBqh.setJsonSize(0); - - byte[] assembledHeader = dummyBqh.assembleHeaderBytes(); - assertEquals(dummyBqh.getQueryType(), QueryType.valueOf(assembledHeader[0])); - byte[] queryIDFromHeader = new byte[3]; - System.arraycopy(assembledHeader, 1, queryIDFromHeader, 0, 3); - assertEquals(dummyBqh.getQueryID(), QueryID.valueOf(queryIDFromHeader)); - assertEquals(dummyBqh.getCorrelationID(), BitConverter.intFromByteArray(assembledHeader, 4)); - assertEquals(dummyBqh.getJsonSize(), BitConverter.intFromByteArray(assembledHeader, 8)); - } - - @Test - public void testAssemblyAndParse() { - BinaryQueryHeader bqh = createDummyBqh(); - - byte[] bqhBytes = bqh.assembleHeaderBytes(); - assertNotNull(bqhBytes); - - BinaryQueryHeader parsedBqh = BinaryQueryHeader.parseBinaryQueryHeader(bqhBytes); - assertNotNull(parsedBqh); - - assertEquals(bqh.getCorrelationID(), parsedBqh.getCorrelationID()); - assertEquals(bqh.getQueryID(), parsedBqh.getQueryID()); - assertEquals(bqh.getQueryType(), parsedBqh.getQueryType()); - assertEquals(bqh.getBulkData(), parsedBqh.getBulkData()); - assertEquals(bqh.getJsonData(), parsedBqh.getJsonData()); - assertEquals(bqh.getJsonSize(), parsedBqh.getJsonSize()); - } - - @Test - public void testCorruptHeader() { - BinaryQueryHeader bqh = createDummyBqh(); - bqh.setJsonSize(5); - bqh.setJsonData(new byte[5]); - bqh.setJsonSize(Integer.MAX_VALUE); - - assertNotEquals(bqh.getJsonData().length, bqh.getJsonSize()); - - byte[] bqhBytes = bqh.assembleHeaderBytes(); - - assertNull(safeParse(bqhBytes)); - - int size = bqhBytes.length; - for (int i = 0; i < size; i++) { - bqhBytes[i] = (byte) 0x99; - } - - assertNull(safeParse(bqhBytes)); - BinaryQueryHeader head = BinaryQueryHeader.parseBinaryQueryHeader(bqhBytes); - assertNull(head); - } - - @Test - public void testJsonSetException() { - try { - BinaryQueryHeader bqh = createDummyBqh(); - bqh.setJsonData(null); - fail("Setting JSON data to null should have thrown an exception"); - } catch (Exception e) { - //Pass - } - } -} diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/SecurityQueryPayloadTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/SecurityQueryPayloadTests.java new file mode 100644 index 000000000..cc0cd914a --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/SecurityQueryPayloadTests.java @@ -0,0 +1,131 @@ +package com.smartdevicelink.test.protocol; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.smartdevicelink.protocol.SecurityQueryPayload; +import com.smartdevicelink.protocol.enums.QueryID; +import com.smartdevicelink.protocol.enums.QueryType; +import com.smartdevicelink.util.BitConverter; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; + +@RunWith(AndroidJUnit4.class) +public class SecurityQueryPayloadTests { + + public static SecurityQueryPayload createDummyBqh() { + SecurityQueryPayload bqh = new SecurityQueryPayload(); + bqh.setCorrelationID(123); + bqh.setQueryID(QueryID.SEND_HANDSHAKE_DATA); + bqh.setQueryType(QueryType.REQUEST); + bqh.setBulkData(null); + bqh.setJsonSize(0); + return bqh; + } + + public SecurityQueryPayload safeParse(byte[] array) { + try { + return SecurityQueryPayload.parseBinaryQueryHeader(array); + } catch (Exception e) { + return null; + } + } + + @Test + public void testCorrectParsing() { + byte[] array = new byte[12]; + array[0] = 0; + array[1] = 0; + array[2] = 0; + array[3] = 2; + array[4] = 0; + array[5] = 0; + array[6] = 0; + array[7] = 3; + array[8] = 0; + array[9] = 0; + array[10] = 0; + array[11] = 0; + + SecurityQueryPayload parsedBqh = SecurityQueryPayload.parseBinaryQueryHeader(array); + assertEquals(parsedBqh.getQueryType(), QueryType.REQUEST); + assertEquals(parsedBqh.getQueryID(), QueryID.SEND_INTERNAL_ERROR); + assertEquals(parsedBqh.getCorrelationID(), 3); + assertEquals(parsedBqh.getJsonSize(), 0); + } + + @Test + public void testCorrectHeaderAssembly() { + SecurityQueryPayload dummyBqh = new SecurityQueryPayload(); + dummyBqh.setQueryType(QueryType.REQUEST); + dummyBqh.setQueryID(QueryID.SEND_HANDSHAKE_DATA); + dummyBqh.setCorrelationID(3); + dummyBqh.setJsonSize(0); + + byte[] assembledHeader = dummyBqh.assembleHeaderBytes(); + assertEquals(dummyBqh.getQueryType(), QueryType.valueOf(assembledHeader[0])); + byte[] queryIDFromHeader = new byte[3]; + System.arraycopy(assembledHeader, 1, queryIDFromHeader, 0, 3); + assertEquals(dummyBqh.getQueryID(), QueryID.valueOf(queryIDFromHeader)); + assertEquals(dummyBqh.getCorrelationID(), BitConverter.intFromByteArray(assembledHeader, 4)); + assertEquals(dummyBqh.getJsonSize(), BitConverter.intFromByteArray(assembledHeader, 8)); + } + + @Test + public void testAssemblyAndParse() { + SecurityQueryPayload bqh = createDummyBqh(); + + byte[] bqhBytes = bqh.assembleHeaderBytes(); + assertNotNull(bqhBytes); + + SecurityQueryPayload parsedBqh = SecurityQueryPayload.parseBinaryQueryHeader(bqhBytes); + assertNotNull(parsedBqh); + + assertEquals(bqh.getCorrelationID(), parsedBqh.getCorrelationID()); + assertEquals(bqh.getQueryID(), parsedBqh.getQueryID()); + assertEquals(bqh.getQueryType(), parsedBqh.getQueryType()); + assertEquals(bqh.getBulkData(), parsedBqh.getBulkData()); + assertEquals(bqh.getJsonData(), parsedBqh.getJsonData()); + assertEquals(bqh.getJsonSize(), parsedBqh.getJsonSize()); + } + + @Test + public void testCorruptHeader() { + SecurityQueryPayload bqh = createDummyBqh(); + bqh.setJsonSize(5); + bqh.setJsonData(new byte[5]); + bqh.setJsonSize(Integer.MAX_VALUE); + + assertNotEquals(bqh.getJsonData().length, bqh.getJsonSize()); + + byte[] bqhBytes = bqh.assembleHeaderBytes(); + + assertNull(safeParse(bqhBytes)); + + int size = bqhBytes.length; + for (int i = 0; i < size; i++) { + bqhBytes[i] = (byte) 0x99; + } + + assertNull(safeParse(bqhBytes)); + SecurityQueryPayload head = SecurityQueryPayload.parseBinaryQueryHeader(bqhBytes); + assertNull(head); + } + + @Test + public void testJsonSetException() { + try { + SecurityQueryPayload bqh = createDummyBqh(); + bqh.setJsonData(null); + fail("Setting JSON data to null should have thrown an exception"); + } catch (Exception e) { + //Pass + } + } +} diff --git a/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java b/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java deleted file mode 100644 index ea474ee62..000000000 --- a/base/src/main/java/com/smartdevicelink/protocol/BinaryQueryHeader.java +++ /dev/null @@ -1,140 +0,0 @@ -package com.smartdevicelink.protocol; - -import androidx.annotation.RestrictTo; - -import com.smartdevicelink.protocol.enums.QueryErrorCode; -import com.smartdevicelink.protocol.enums.QueryID; -import com.smartdevicelink.protocol.enums.QueryType; -import com.smartdevicelink.util.BitConverter; -import com.smartdevicelink.util.DebugTool; - -@RestrictTo(RestrictTo.Scope.LIBRARY) -public class BinaryQueryHeader { - private static final String TAG = "BinaryQueryHeader"; - - private QueryType _queryType; - private QueryID _queryID; - private int _correlationID; - private int _jsonSize; - private QueryErrorCode _errorCode; - - private byte[] _jsonData = null; - private byte[] _bulkData = null; - - public BinaryQueryHeader() { - } - - public static BinaryQueryHeader parseBinaryQueryHeader(byte[] binHeader) { - BinaryQueryHeader msg = new BinaryQueryHeader(); - - byte QUERY_Type = (byte) (binHeader[0]); - msg.setQueryType(QueryType.valueOf(QUERY_Type)); - - byte[] _queryID = new byte[3]; - System.arraycopy(binHeader, 1, _queryID, 0, 3); - msg.setQueryID(QueryID.valueOf(_queryID)); - - int corrID = BitConverter.intFromByteArray(binHeader, 4); - msg.setCorrelationID(corrID); - - int _jsonSize = BitConverter.intFromByteArray(binHeader, 8); - msg.setJsonSize(_jsonSize); - - if (msg.getQueryType() == QueryType.NOTIFICATION && msg.getQueryID() == QueryID.SEND_INTERNAL_ERROR) { - int _errorCode = BitConverter.intFromByteArray(binHeader, binHeader.length-1); - msg.setErrorCode(QueryErrorCode.valueOf((byte) _errorCode)); - } - - try { - if (_jsonSize > 0) { - byte[] _jsonData = new byte[_jsonSize]; - System.arraycopy(binHeader, 12, _jsonData, 0, _jsonSize); - msg.setJsonData(_jsonData); - } - - if (binHeader.length - _jsonSize - 12 > 0) { - byte[] _bulkData; - if (msg.getQueryType() == QueryType.NOTIFICATION && msg.getQueryID() == QueryID.SEND_INTERNAL_ERROR) { - _bulkData = new byte[binHeader.length - _jsonSize - 12 - 1]; - System.arraycopy(binHeader, 12 + _jsonSize, _bulkData, 0, _bulkData.length - 1); - } else { - _bulkData = new byte[binHeader.length - _jsonSize - 12]; - System.arraycopy(binHeader, 12 + _jsonSize, _bulkData, 0, _bulkData.length); - } - msg.setBulkData(_bulkData); - } - - } catch (OutOfMemoryError | ArrayIndexOutOfBoundsException e) { - DebugTool.logError(TAG, "Unable to process data to form header"); - return null; - } - - return msg; - } - - public byte[] assembleHeaderBytes() { - byte[] ret = new byte[12]; - ret[0] = _queryType.getValue(); - System.arraycopy(_queryID.getValue(), 0, ret, 1, 3); - System.arraycopy(BitConverter.intToByteArray(_correlationID), 0, ret, 4, 4); - System.arraycopy(BitConverter.intToByteArray(_jsonSize), 0, ret, 8, 4); - return ret; - } - - public QueryType getQueryType() { - return _queryType; - } - - public void setQueryType(QueryType _queryType) { - this._queryType = _queryType; - } - - public QueryID getQueryID() { - return _queryID; - } - - public void setQueryID(QueryID _queryID) { - this._queryID = _queryID; - } - - public int getCorrelationID() { - return _correlationID; - } - - public void setCorrelationID(int _correlationID) { - this._correlationID = _correlationID; - } - - public int getJsonSize() { - return _jsonSize; - } - - public void setJsonSize(int _jsonSize) { - this._jsonSize = _jsonSize; - } - - public QueryErrorCode getErrorCode() { - return _errorCode; - } - - public void setErrorCode(QueryErrorCode _errorCode) { - this._errorCode = _errorCode; - } - - public byte[] getJsonData() { - return _jsonData; - } - - public void setJsonData(byte[] _jsonData) { - this._jsonData = new byte[this._jsonSize]; - System.arraycopy(_jsonData, 0, this._jsonData, 0, _jsonSize); - } - - public byte[] getBulkData() { - return _bulkData; - } - - public void setBulkData(byte[] _bulkData) { - this._bulkData = _bulkData; - } -} diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java b/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java index eaad041ca..98b1e35da 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java @@ -146,14 +146,4 @@ public class SdlPacketFactory { return msg; } - public static BinaryQueryHeader createBinaryQueryHeader(QueryType queryType, QueryID queryId, int corrID, int jsonSize) { - BinaryQueryHeader msg = new BinaryQueryHeader(); - msg.setQueryType(queryType); - msg.setQueryID(queryId); - msg.setCorrelationID(corrID); - msg.setJsonSize(jsonSize); - - return msg; - } - } diff --git a/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java b/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java new file mode 100644 index 000000000..67f45171e --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java @@ -0,0 +1,139 @@ +package com.smartdevicelink.protocol; + +import androidx.annotation.RestrictTo; + +import com.smartdevicelink.protocol.enums.QueryErrorCode; +import com.smartdevicelink.protocol.enums.QueryID; +import com.smartdevicelink.protocol.enums.QueryType; +import com.smartdevicelink.util.BitConverter; +import com.smartdevicelink.util.DebugTool; + +@RestrictTo(RestrictTo.Scope.LIBRARY) +public class SecurityQueryPayload { + private static final String TAG = "BinaryQueryHeader"; + + private QueryType _queryType; + private QueryID _queryID; + private int _correlationID; + private int _jsonSize; + private QueryErrorCode _errorCode; + + private byte[] _jsonData = null; + private byte[] _bulkData = null; + + public SecurityQueryPayload() { + } + + public static SecurityQueryPayload parseBinaryQueryHeader(byte[] binHeader) { + SecurityQueryPayload msg = new SecurityQueryPayload(); + + byte QUERY_Type = (byte) (binHeader[0]); + msg.setQueryType(QueryType.valueOf(QUERY_Type)); + + byte[] _queryID = new byte[3]; + System.arraycopy(binHeader, 1, _queryID, 0, 3); + msg.setQueryID(QueryID.valueOf(_queryID)); + + int corrID = BitConverter.intFromByteArray(binHeader, 4); + msg.setCorrelationID(corrID); + + int _jsonSize = BitConverter.intFromByteArray(binHeader, 8); + msg.setJsonSize(_jsonSize); + + if (msg.getQueryType() == QueryType.NOTIFICATION && msg.getQueryID() == QueryID.SEND_INTERNAL_ERROR) { + msg.setErrorCode(QueryErrorCode.valueOf(binHeader[binHeader.length - 1])); + } + + try { + if (_jsonSize > 0) { + byte[] _jsonData = new byte[_jsonSize]; + System.arraycopy(binHeader, 12, _jsonData, 0, _jsonSize); + msg.setJsonData(_jsonData); + } + + if (binHeader.length - _jsonSize - 12 > 0) { + byte[] _bulkData; + if (msg.getQueryType() == QueryType.NOTIFICATION && msg.getQueryID() == QueryID.SEND_INTERNAL_ERROR) { + _bulkData = new byte[binHeader.length - _jsonSize - 12 - 1]; + System.arraycopy(binHeader, 12 + _jsonSize, _bulkData, 0, _bulkData.length - 1); + } else { + _bulkData = new byte[binHeader.length - _jsonSize - 12]; + System.arraycopy(binHeader, 12 + _jsonSize, _bulkData, 0, _bulkData.length); + } + msg.setBulkData(_bulkData); + } + + } catch (OutOfMemoryError | ArrayIndexOutOfBoundsException e) { + DebugTool.logError(TAG, "Unable to process data to form header"); + return null; + } + + return msg; + } + + public byte[] assembleHeaderBytes() { + byte[] ret = new byte[12]; + ret[0] = _queryType.getValue(); + System.arraycopy(_queryID.getValue(), 0, ret, 1, 3); + System.arraycopy(BitConverter.intToByteArray(_correlationID), 0, ret, 4, 4); + System.arraycopy(BitConverter.intToByteArray(_jsonSize), 0, ret, 8, 4); + return ret; + } + + public QueryType getQueryType() { + return _queryType; + } + + public void setQueryType(QueryType _queryType) { + this._queryType = _queryType; + } + + public QueryID getQueryID() { + return _queryID; + } + + public void setQueryID(QueryID _queryID) { + this._queryID = _queryID; + } + + public int getCorrelationID() { + return _correlationID; + } + + public void setCorrelationID(int _correlationID) { + this._correlationID = _correlationID; + } + + public int getJsonSize() { + return _jsonSize; + } + + public void setJsonSize(int _jsonSize) { + this._jsonSize = _jsonSize; + } + + public QueryErrorCode getErrorCode() { + return _errorCode; + } + + public void setErrorCode(QueryErrorCode _errorCode) { + this._errorCode = _errorCode; + } + + public byte[] getJsonData() { + return _jsonData; + } + + public void setJsonData(byte[] _jsonData) { + this._jsonData = new byte[this._jsonSize]; + System.arraycopy(_jsonData, 0, this._jsonData, 0, _jsonSize); + } + + public byte[] getBulkData() { + return _bulkData; + } + + public void setBulkData(byte[] _bulkData) { + this._bulkData = _bulkData; + } +} diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index 93c3358a2..6be68e506 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -36,14 +36,13 @@ import androidx.annotation.RestrictTo; import com.smartdevicelink.exception.SdlException; import com.smartdevicelink.managers.lifecycle.RpcConverter; -import com.smartdevicelink.protocol.BinaryQueryHeader; +import com.smartdevicelink.protocol.SecurityQueryPayload; import com.smartdevicelink.protocol.ISdlProtocol; import com.smartdevicelink.protocol.ISdlServiceListener; import com.smartdevicelink.protocol.ProtocolMessage; import com.smartdevicelink.protocol.SdlPacket; import com.smartdevicelink.protocol.SdlProtocolBase; import com.smartdevicelink.protocol.enums.ControlFrameTags; -import com.smartdevicelink.protocol.enums.QueryErrorCode; import com.smartdevicelink.protocol.enums.QueryID; import com.smartdevicelink.protocol.enums.QueryType; import com.smartdevicelink.protocol.enums.SessionType; @@ -56,12 +55,10 @@ import com.smartdevicelink.security.SdlSecurityBase; import com.smartdevicelink.streaming.video.VideoStreamingParameters; import com.smartdevicelink.transport.BaseTransportConfig; import com.smartdevicelink.transport.enums.TransportType; -import com.smartdevicelink.util.BitConverter; import com.smartdevicelink.util.DebugTool; import com.smartdevicelink.util.SystemInfo; import com.smartdevicelink.util.Version; -import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.ListIterator; @@ -194,7 +191,7 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ if (sdlSecurity == null) return; - BinaryQueryHeader receivedHeader = BinaryQueryHeader.parseBinaryQueryHeader(msg.getData().clone()); + SecurityQueryPayload receivedHeader = SecurityQueryPayload.parseBinaryQueryHeader(msg.getData().clone()); int iLen = msg.getData().length - 12; byte[] data = new byte[iLen]; @@ -220,7 +217,7 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ } iNumBytes = sdlSecurity.runHandshake(data, dataToRead); - BinaryQueryHeader responseHeader = new BinaryQueryHeader(); + SecurityQueryPayload responseHeader = new SecurityQueryPayload(); if (iNumBytes == null || iNumBytes <= 0) { DebugTool.logError(TAG, "Internal Error processing control service"); -- cgit v1.2.1 From 55508208890888b7c13fa7455e0ea23441f4650a Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 12 Aug 2021 17:19:47 -0400 Subject: Add enum unit tests --- .../java/com/smartdevicelink/test/Validator.java | 24 ++++++ .../test/protocol/enums/QueryTypeTests.java | 97 ++++++++++++++++++++++ 2 files changed, 121 insertions(+) create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryTypeTests.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java index e8d89c818..7eadcbe55 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java @@ -3,6 +3,7 @@ package com.smartdevicelink.test; import com.smartdevicelink.managers.file.filetypes.SdlFile; import com.smartdevicelink.protocol.enums.FrameDataControlFrameType; import com.smartdevicelink.protocol.enums.FrameType; +import com.smartdevicelink.protocol.enums.QueryType; import com.smartdevicelink.protocol.enums.SessionType; import com.smartdevicelink.proxy.rpc.*; import com.smartdevicelink.proxy.rpc.enums.AppServiceType; @@ -126,6 +127,29 @@ public class Validator { return true; } + public static boolean validateQueryTypeArray(QueryType[] array1, QueryType[] array2) { + + if (array1 == null) { + return (array2 == null); + } + + if (array2 == null) { + return (array1 == null); + } + + if (array1.length != array2.length) { + return false; + } + + for (int i = 0; i < array1.length; i++) { + if (array1[i] != array2[i]) { + return false; + } + } + + return true; + } + public static boolean validateFrameDataControlFrameTypeArray(FrameDataControlFrameType[] array1, FrameDataControlFrameType[] array2) { if (array1 == null) { diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryTypeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryTypeTests.java new file mode 100644 index 000000000..13d606cff --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryTypeTests.java @@ -0,0 +1,97 @@ +package com.smartdevicelink.test.protocol.enums; + +import com.smartdevicelink.protocol.enums.QueryType; +import com.smartdevicelink.test.Validator; + +import junit.framework.TestCase; + +import java.util.Vector; + +public class QueryTypeTests extends TestCase { + + private Vector list = QueryType.getList(); + + public void testValidEnums() { + final byte REQUEST_BYTE = (byte) 0x00; + final String REQUEST_STRING = "REQUEST"; + + final byte RESPONSE_BYTE = (byte) 0x10; + final String RESPONSE_STRING = "RESPONSE"; + + final byte NOTIFICATION_BYTE = (byte) 0x20; + final String NOTIFICATION_STRING = "NOTIFICATION"; + + final byte INVALID_QUERY_TYPE_BYTE = (byte) 0xFF; + final String INVALID_QUERY_TYPE_STRING = "INVALID_QUERY_TYPE"; + + try { + assertNotNull("QueryType list returned null", list); + + QueryType enumRequest = (QueryType) QueryType.get(list, REQUEST_BYTE); + QueryType enumResponse = (QueryType) QueryType.get(list, RESPONSE_BYTE); + QueryType enumNotification = (QueryType) QueryType.get(list, NOTIFICATION_BYTE); + QueryType enumInvalidQueryType = (QueryType) QueryType.get(list, INVALID_QUERY_TYPE_BYTE); + + assertNotNull("Request byte match returned null", enumRequest); + assertNotNull("Response byte match returned null", enumResponse); + assertNotNull("Notification byte match returned null", enumNotification); + assertNotNull("Invalid Query Type byte match returned null", enumInvalidQueryType); + + enumRequest = (QueryType) QueryType.get(list, REQUEST_STRING); + enumResponse = (QueryType) QueryType.get(list, RESPONSE_STRING); + enumNotification = (QueryType) QueryType.get(list, NOTIFICATION_STRING); + enumInvalidQueryType = (QueryType) QueryType.get(list, INVALID_QUERY_TYPE_STRING); + + assertNotNull("Request string match returned null", enumRequest); + assertNotNull("Response string match returned null", enumResponse); + assertNotNull("Notification string match returned null", enumNotification); + assertNotNull("Invalid Query string byte match returned null", enumInvalidQueryType); + + + }catch (NullPointerException exception) { + fail("Null enum list throws NullPointerException."); + } + } + + public void testInvalidEnum() { + + final byte INVALID_BYTE = (byte) 0xAB; + final String INVALID_STRING = "Invalid"; + + try { + QueryType enumInvalid = (QueryType) QueryType.get(list, INVALID_BYTE); + assertNull("Invalid byte match didn't return null", enumInvalid); + + enumInvalid = (QueryType) QueryType.get(list, INVALID_STRING); + assertNull("Invalid string match didn't return null", enumInvalid); + } catch (IllegalArgumentException exception) { + fail("Invalid enum throws IllegalArgumentException."); + } + } + + public void testNullEnum() { + try { + QueryType enumNull = (QueryType) QueryType.get(list, null); + assertNull("Null lookup returns a value", enumNull); + } catch (NullPointerException exception) { + fail("Null string throws NullPointerException."); + } + } + + public void testListEnum() { + Vector enumTestList = new Vector<>(); + enumTestList.add(QueryType.REQUEST); + enumTestList.add(QueryType.RESPONSE); + enumTestList.add(QueryType.NOTIFICATION); + enumTestList.add(QueryType.INVALID_QUERY_TYPE); + + assertTrue("List does not match enum test list.", + list.containsAll(enumTestList) && + enumTestList.containsAll(list)); + + QueryType[] enumValueArray = QueryType.values(); + QueryType[] enumTestArray = {QueryType.REQUEST, QueryType.RESPONSE, QueryType.NOTIFICATION, QueryType.INVALID_QUERY_TYPE}; + assertTrue("Array does not match enum values array.", + Validator.validateQueryTypeArray(enumValueArray, enumTestArray)); + } +} -- cgit v1.2.1 From 380383147d57a6cc2af726c78cb481ed4dee4a74 Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 13 Aug 2021 08:06:32 -0400 Subject: Add QueryErrorCodeTests --- .../java/com/smartdevicelink/test/Validator.java | 24 +++ .../test/protocol/enums/QueryErrorCodeTests.java | 189 +++++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryErrorCodeTests.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java index 7eadcbe55..a6ca6385c 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java @@ -3,6 +3,7 @@ package com.smartdevicelink.test; import com.smartdevicelink.managers.file.filetypes.SdlFile; import com.smartdevicelink.protocol.enums.FrameDataControlFrameType; import com.smartdevicelink.protocol.enums.FrameType; +import com.smartdevicelink.protocol.enums.QueryErrorCode; import com.smartdevicelink.protocol.enums.QueryType; import com.smartdevicelink.protocol.enums.SessionType; import com.smartdevicelink.proxy.rpc.*; @@ -150,6 +151,29 @@ public class Validator { return true; } + public static boolean validateQueryErrorCodeArray(QueryErrorCode[] array1, QueryErrorCode[] array2) { + + if (array1 == null) { + return (array2 == null); + } + + if (array2 == null) { + return (array1 == null); + } + + if (array1.length != array2.length) { + return false; + } + + for (int i = 0; i < array1.length; i++) { + if (array1[i] != array2[i]) { + return false; + } + } + + return true; + } + public static boolean validateFrameDataControlFrameTypeArray(FrameDataControlFrameType[] array1, FrameDataControlFrameType[] array2) { if (array1 == null) { diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryErrorCodeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryErrorCodeTests.java new file mode 100644 index 000000000..a3daf76af --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryErrorCodeTests.java @@ -0,0 +1,189 @@ +package com.smartdevicelink.test.protocol.enums; + +import com.smartdevicelink.protocol.enums.QueryErrorCode; +import com.smartdevicelink.test.Validator; + +import junit.framework.TestCase; + +import java.util.Vector; + +public class QueryErrorCodeTests extends TestCase { + + private Vector list = QueryErrorCode.getList(); + + public void testValidEnums() { + final byte ERROR_SUCCESS_BYTE = (byte) 0x00; + final String ERROR_SUCCESS_STRING = "ERROR_SUCCESS"; + + final byte ERROR_INVALID_QUERY_SIZE_BYTE = (byte) 0x01; + final String ERROR_INVALID_QUERY_SIZE_STRING = "ERROR_INVALID_QUERY_SIZE"; + + final byte ERROR_INVALID_QUERY_ID_BYTE = (byte) 0x02; + final String ERROR_INVALID_QUERY_ID_STRING = "ERROR_INVALID_QUERY_ID"; + + final byte ERROR_NOT_SUPPORTED_BYTE = (byte) 0x03; + final String ERROR_NOT_SUPPORTED_STRING = "ERROR_NOT_SUPPORTED"; + + final byte ERROR_SERVICE_ALREADY_PROTECTED_BYTE = (byte) 0x04; + final String ERROR_SERVICE_ALREADY_PROTECTED_STRING = "ERROR_SERVICE_ALREADY_PROTECTED"; + + final byte ERROR_SERVICE_NOT_PROTECTED_BYTE = (byte) 0x05; + final String ERROR_SERVICE_NOT_PROTECTED_STRING = "ERROR_SERVICE_NOT_PROTECTED"; + + final byte ERROR_DECRYPTION_FAILED_BYTE = (byte) 0x06; + final String ERROR_DECRYPTION_FAILED_STRING = "ERROR_DECRYPTION_FAILED"; + + final byte ERROR_ENCRYPTION_FAILED_BYTE = (byte) 0x07; + final String ERROR_ENCRYPTION_FAILED_STRING = "ERROR_ENCRYPTION_FAILED"; + + final byte ERROR_SSL_INVALID_DATA_BYTE = (byte) 0x08; + final String ERROR_SSL_INVALID_DATA_STRING = "ERROR_SSL_INVALID_DATA"; + + final byte ERROR_HANDSHAKE_FAILED_BYTE = (byte) 0x09; + final String ERROR_HANDSHAKE_FAILED_STRING = "ERROR_HANDSHAKE_FAILED"; + + final byte INVALID_CERT_BYTE = (byte) 0x0A; + final String INVALID_CERT_STRING = "INVALID_CERT"; + + final byte EXPIRED_CERT_BYTE = (byte) 0x0B; + final String EXPIRED_CERT_STRING = "EXPIRED_CERT"; + + final byte ERROR_INTERNAL_BYTE = (byte) 0xFF; + final String ERROR_INTERNAL_STRING = "ERROR_INTERNAL"; + + final byte ERROR_UNKNOWN_INTERNAL_ERROR_BYTE = (byte) 0xFE; + final String ERROR_UNKNOWN_INTERNAL_ERROR_STRING = "ERROR_UNKNOWN_INTERNAL_ERROR"; + + try { + assertNotNull("QueryErrorCode list returned null", list); + + QueryErrorCode enumSuccess = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SUCCESS_BYTE); + QueryErrorCode enumInvalidQuerySize = (QueryErrorCode) QueryErrorCode.get(list, ERROR_INVALID_QUERY_SIZE_BYTE); + QueryErrorCode enumInvalidQueryID = (QueryErrorCode) QueryErrorCode.get(list, ERROR_INVALID_QUERY_ID_BYTE); + QueryErrorCode enumNotSupported = (QueryErrorCode) QueryErrorCode.get(list, ERROR_NOT_SUPPORTED_BYTE); + QueryErrorCode enumServiceAlreadyProtected = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SERVICE_ALREADY_PROTECTED_BYTE); + QueryErrorCode enumServiceNotProtected = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SERVICE_NOT_PROTECTED_BYTE); + QueryErrorCode enumDecryptionFailed = (QueryErrorCode) QueryErrorCode.get(list, ERROR_DECRYPTION_FAILED_BYTE); + QueryErrorCode enumEncryptionFailed = (QueryErrorCode) QueryErrorCode.get(list, ERROR_ENCRYPTION_FAILED_BYTE); + QueryErrorCode enumSSLInvalidData = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SSL_INVALID_DATA_BYTE); + QueryErrorCode enumHandshakeFailed = (QueryErrorCode) QueryErrorCode.get(list, ERROR_HANDSHAKE_FAILED_BYTE); + QueryErrorCode enumInvalidCert = (QueryErrorCode) QueryErrorCode.get(list, INVALID_CERT_BYTE); + QueryErrorCode enumExpiredCert = (QueryErrorCode) QueryErrorCode.get(list, EXPIRED_CERT_BYTE); + QueryErrorCode enumInternal = (QueryErrorCode) QueryErrorCode.get(list, ERROR_INTERNAL_BYTE); + QueryErrorCode enumUnknownInternalError = (QueryErrorCode) QueryErrorCode.get(list, ERROR_UNKNOWN_INTERNAL_ERROR_BYTE); + + assertNotNull("Success byte match returned null", enumSuccess); + assertNotNull("Invalid Query Size byte match returned null", enumInvalidQuerySize); + assertNotNull("Invalid Query ID byte match returned null", enumInvalidQueryID); + assertNotNull("Not Supported byte match returned null", enumNotSupported); + assertNotNull("Service Already Protected byte match returned null", enumServiceAlreadyProtected); + assertNotNull("Service Not Protected byte match returned null", enumServiceNotProtected); + assertNotNull("Decryption Failed byte match returned null", enumDecryptionFailed); + assertNotNull("Encryption Failed byte match returned null", enumEncryptionFailed); + assertNotNull("SSL Invalid Data byte match returned null", enumSSLInvalidData); + assertNotNull("Handshake Failed byte match returned null", enumHandshakeFailed); + assertNotNull("Invalid Cert byte match returned null", enumInvalidCert); + assertNotNull("Expired Cert byte match returned null", enumExpiredCert); + assertNotNull("Internal byte match returned null", enumInternal); + assertNotNull("Unknown Internal byte match returned null", enumUnknownInternalError); + + enumSuccess = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SUCCESS_STRING); + enumInvalidQuerySize = (QueryErrorCode) QueryErrorCode.get(list, ERROR_INVALID_QUERY_SIZE_STRING); + enumInvalidQueryID = (QueryErrorCode) QueryErrorCode.get(list, ERROR_INVALID_QUERY_ID_STRING); + enumNotSupported = (QueryErrorCode) QueryErrorCode.get(list, ERROR_NOT_SUPPORTED_STRING); + enumServiceAlreadyProtected = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SERVICE_ALREADY_PROTECTED_STRING); + enumServiceNotProtected = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SERVICE_NOT_PROTECTED_STRING); + enumDecryptionFailed = (QueryErrorCode) QueryErrorCode.get(list, ERROR_DECRYPTION_FAILED_STRING); + enumEncryptionFailed = (QueryErrorCode) QueryErrorCode.get(list, ERROR_ENCRYPTION_FAILED_STRING); + enumSSLInvalidData = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SSL_INVALID_DATA_STRING); + enumHandshakeFailed = (QueryErrorCode) QueryErrorCode.get(list, ERROR_HANDSHAKE_FAILED_STRING); + enumInvalidCert = (QueryErrorCode) QueryErrorCode.get(list, INVALID_CERT_STRING); + enumExpiredCert = (QueryErrorCode) QueryErrorCode.get(list, EXPIRED_CERT_STRING); + enumInternal = (QueryErrorCode) QueryErrorCode.get(list, ERROR_INTERNAL_STRING); + enumUnknownInternalError = (QueryErrorCode) QueryErrorCode.get(list, ERROR_UNKNOWN_INTERNAL_ERROR_STRING); + + assertNotNull("Success string match returned null", enumSuccess); + assertNotNull("Invalid Query Size string match returned null", enumInvalidQuerySize); + assertNotNull("Invalid Query ID string match returned null", enumInvalidQueryID); + assertNotNull("Not Supported string match returned null", enumNotSupported); + assertNotNull("Service Already Protected string match returned null", enumServiceAlreadyProtected); + assertNotNull("Service Not Protected string match returned null", enumServiceNotProtected); + assertNotNull("Decryption Failed string match returned null", enumDecryptionFailed); + assertNotNull("Encryption Failed string match returned null", enumEncryptionFailed); + assertNotNull("SSL Invalid Data string match returned null", enumSSLInvalidData); + assertNotNull("Handshake Failed string match returned null", enumHandshakeFailed); + assertNotNull("Invalid Cert string match returned null", enumInvalidCert); + assertNotNull("Expired Cert string match returned null", enumExpiredCert); + assertNotNull("Internal string match returned null", enumInternal); + assertNotNull("Unknown Internal string match returned null", enumUnknownInternalError); + } catch (NullPointerException exception) { + fail("Null enum list throws NullPointerException."); + } + } + + public void testInvalidEnum() { + final byte INVALID_BYTE = (byte) 0xAB; + final String INVALID_STRING = "Invalid"; + + try { + QueryErrorCode enumInvalid = (QueryErrorCode) QueryErrorCode.get(list, INVALID_BYTE); + assertNull("Invalid byte match didn't return null", enumInvalid); + + enumInvalid = (QueryErrorCode) QueryErrorCode.get(list, INVALID_STRING); + assertNull("Invalid byte match didn't return null", enumInvalid); + } catch (IllegalArgumentException exception) { + fail("Invalid enum throws IllegalArgumentException."); + } + } + + public void testNullEnum() { + try { + QueryErrorCode enumNull = (QueryErrorCode) QueryErrorCode.get(list, null); + assertNull("Null lookup returns a value", enumNull); + } catch (NullPointerException exception) { + fail("Null string throws NullPointerException."); + } + } + + public void testListEnum() { + Vector enumTestList = new Vector<>(); + enumTestList.add(QueryErrorCode.ERROR_SUCCESS); + enumTestList.add(QueryErrorCode.ERROR_INVALID_QUERY_SIZE); + enumTestList.add(QueryErrorCode.ERROR_INVALID_QUERY_ID); + enumTestList.add(QueryErrorCode.ERROR_NOT_SUPPORTED); + enumTestList.add(QueryErrorCode.ERROR_SERVICE_ALREADY_PROTECTED); + enumTestList.add(QueryErrorCode.ERROR_SERVICE_NOT_PROTECTED); + enumTestList.add(QueryErrorCode.ERROR_DECRYPTION_FAILED); + enumTestList.add(QueryErrorCode.ERROR_ENCRYPTION_FAILED); + enumTestList.add(QueryErrorCode.ERROR_SSL_INVALID_DATA); + enumTestList.add(QueryErrorCode.ERROR_HANDSHAKE_FAILED); + enumTestList.add(QueryErrorCode.INVALID_CERT); + enumTestList.add(QueryErrorCode.EXPIRED_CERT); + enumTestList.add(QueryErrorCode.ERROR_INTERNAL); + enumTestList.add(QueryErrorCode.ERROR_UNKNOWN_INTERNAL_ERROR); + + assertTrue("List does not match enum test list.", + list.containsAll(enumTestList) && + enumTestList.containsAll(list)); + + QueryErrorCode[] enumValueArray = QueryErrorCode.values(); + QueryErrorCode[] enumTestArray = { + QueryErrorCode.ERROR_SUCCESS, + QueryErrorCode.ERROR_INVALID_QUERY_SIZE, + QueryErrorCode.ERROR_INVALID_QUERY_ID, + QueryErrorCode.ERROR_NOT_SUPPORTED, + QueryErrorCode.ERROR_SERVICE_ALREADY_PROTECTED, + QueryErrorCode.ERROR_SERVICE_NOT_PROTECTED, + QueryErrorCode.ERROR_DECRYPTION_FAILED, + QueryErrorCode.ERROR_ENCRYPTION_FAILED, + QueryErrorCode.ERROR_SSL_INVALID_DATA, + QueryErrorCode.ERROR_HANDSHAKE_FAILED, + QueryErrorCode.INVALID_CERT, + QueryErrorCode.EXPIRED_CERT, + QueryErrorCode.ERROR_INTERNAL, + QueryErrorCode.ERROR_UNKNOWN_INTERNAL_ERROR + }; + assertTrue("Array does not match enum values array.", + Validator.validateQueryErrorCodeArray(enumValueArray, enumTestArray)); + } +} -- cgit v1.2.1 From 197937e93d40b1447d250d3a6e45cc51179288c7 Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 13 Aug 2021 08:25:48 -0400 Subject: Add QueryID Tests --- .../java/com/smartdevicelink/test/Validator.java | 24 ++++++ .../test/protocol/enums/QueryIDTests.java | 91 ++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryIDTests.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java index a6ca6385c..fd2e6fc30 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java @@ -4,6 +4,7 @@ import com.smartdevicelink.managers.file.filetypes.SdlFile; import com.smartdevicelink.protocol.enums.FrameDataControlFrameType; import com.smartdevicelink.protocol.enums.FrameType; import com.smartdevicelink.protocol.enums.QueryErrorCode; +import com.smartdevicelink.protocol.enums.QueryID; import com.smartdevicelink.protocol.enums.QueryType; import com.smartdevicelink.protocol.enums.SessionType; import com.smartdevicelink.proxy.rpc.*; @@ -151,6 +152,29 @@ public class Validator { return true; } + public static boolean validateQueryIDArray(QueryID[] array1, QueryID[] array2) { + + if (array1 == null) { + return (array2 == null); + } + + if (array2 == null) { + return (array1 == null); + } + + if (array1.length != array2.length) { + return false; + } + + for (int i = 0; i < array1.length; i++) { + if (array1[i] != array2[i]) { + return false; + } + } + + return true; + } + public static boolean validateQueryErrorCodeArray(QueryErrorCode[] array1, QueryErrorCode[] array2) { if (array1 == null) { diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryIDTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryIDTests.java new file mode 100644 index 000000000..90118eb17 --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryIDTests.java @@ -0,0 +1,91 @@ +package com.smartdevicelink.test.protocol.enums; + +import com.smartdevicelink.protocol.enums.QueryID; +import com.smartdevicelink.test.Validator; + +import junit.framework.TestCase; + +import java.util.Vector; + +public class QueryIDTests extends TestCase { + + private Vector list = QueryID.getList(); + + public void testValidEnums() { + final byte[] SEND_HANDSHAKE_DATA_BYTES = {(byte) 0x00, (byte) 0x00, (byte) 0x01}; + final String SEND_HANDSHAKE_DATA_STRING = "SEND_HANDSHAKE_DATA"; + + final byte[] SEND_INTERNAL_ERROR_BYTES = {(byte) 0x00, (byte) 0x00, (byte) 0x02}; + final String SEND_INTERNAL_ERROR_STRING = "SEND_INTERNAL_ERROR"; + + final byte[] INVALID_QUERY_ID_BYTES = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + final String INVALID_QUERY_ID_STRING = "INVALID_QUERY_ID"; + + try { + assertNotNull("QueryID list returned null", list); + + QueryID enumHandshakeData = (QueryID) QueryID.get(list, SEND_HANDSHAKE_DATA_BYTES); + QueryID enumInternalError = (QueryID) QueryID.get(list, SEND_INTERNAL_ERROR_BYTES); + QueryID enumInvalidQueryId = (QueryID) QueryID.get(list, INVALID_QUERY_ID_BYTES); + + assertNotNull("Send Handshake Data byte match returned null", enumHandshakeData); + assertNotNull("Send Internal Error byte match returned null", enumInternalError); + assertNotNull("Send Invalid QueryID byte match returned null", enumInvalidQueryId); + + enumHandshakeData = (QueryID) QueryID.get(list, SEND_HANDSHAKE_DATA_STRING); + enumInternalError = (QueryID) QueryID.get(list, SEND_INTERNAL_ERROR_STRING); + enumInvalidQueryId = (QueryID) QueryID.get(list, INVALID_QUERY_ID_STRING); + + assertNotNull("Send Handshake Data string match returned null", enumHandshakeData); + assertNotNull("Send Internal Error string match returned null", enumInternalError); + assertNotNull("Send Invalid QueryID string match returned null", enumInvalidQueryId); + } catch(NullPointerException exception) { + fail("Null enum list throws NullPointerException."); + } + } + + public void testInvalidEnum() { + + final byte[] INVALID_BYTE_ARRAY = {(byte) 0xAB, (byte) 0xAB, (byte) 0xAB}; + final String INVALID_STRING = "Invalid"; + + try { + QueryID enumInvalid = (QueryID) QueryID.get(list, INVALID_BYTE_ARRAY); + assertNull("Invalid byte[] match didn't return null", enumInvalid); + + enumInvalid = (QueryID) QueryID.get(list, INVALID_STRING); + assertNull("Invalid string match didn't return null", enumInvalid); + } catch (IllegalArgumentException exception) { + fail("Invalid enum throws IllegalArgumentException."); + } + } + + public void testNullEnum() { + try { + QueryID enumNull = (QueryID) QueryID.get(list, (String) null); + assertNull("Null lookup returns a null string value", enumNull); + + enumNull = (QueryID) QueryID.get(list, (byte[]) null); + assertNull("Null lookup returns a null byte[] value", enumNull); + }catch (NullPointerException exception) { + fail("Null string throws NullPointerException."); + } + } + + public void testListEnum() { + Vector enumTestList = new Vector<>(); + enumTestList.add(QueryID.SEND_HANDSHAKE_DATA); + enumTestList.add(QueryID.SEND_INTERNAL_ERROR); + enumTestList.add(QueryID.INVALID_QUERY_ID); + + assertTrue("List does not match enum test list.", + list.containsAll(enumTestList) && + enumTestList.containsAll(list)); + + QueryID[] enumValueArray = QueryID.values(); + QueryID[] enumTestArray = {QueryID.SEND_HANDSHAKE_DATA, QueryID.SEND_INTERNAL_ERROR, QueryID.INVALID_QUERY_ID}; + assertTrue("Array does not match enum values array.", + Validator.validateQueryIDArray(enumValueArray, enumTestArray)); + } + +} -- cgit v1.2.1 From a9c8fd82b8bca1b9c623dc19f84f33eff11bce61 Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 13 Aug 2021 09:11:16 -0400 Subject: Fix bulkData and add null check --- .../main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java | 3 +-- base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java b/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java index 67f45171e..c2553482b 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java @@ -55,11 +55,10 @@ public class SecurityQueryPayload { byte[] _bulkData; if (msg.getQueryType() == QueryType.NOTIFICATION && msg.getQueryID() == QueryID.SEND_INTERNAL_ERROR) { _bulkData = new byte[binHeader.length - _jsonSize - 12 - 1]; - System.arraycopy(binHeader, 12 + _jsonSize, _bulkData, 0, _bulkData.length - 1); } else { _bulkData = new byte[binHeader.length - _jsonSize - 12]; - System.arraycopy(binHeader, 12 + _jsonSize, _bulkData, 0, _bulkData.length); } + System.arraycopy(binHeader, 12 + _jsonSize, _bulkData, 0, _bulkData.length); msg.setBulkData(_bulkData); } diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index 6be68e506..276e29ae2 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -192,6 +192,10 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ return; SecurityQueryPayload receivedHeader = SecurityQueryPayload.parseBinaryQueryHeader(msg.getData().clone()); + if (receivedHeader == null) { + DebugTool.logError(TAG, "Malformed Security Query Header"); + return; + } int iLen = msg.getData().length - 12; byte[] data = new byte[iLen]; -- cgit v1.2.1 From 0cae83de5bce8ba93f8e4d03c25f87d160f14a8e Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 13 Aug 2021 14:12:49 -0400 Subject: Move listener to manager and add finishes --- .../PreloadPresentChoicesOperationTests.java | 6 +++--- .../screen/choiceset/BaseChoiceSetManager.java | 8 ++++++-- .../choiceset/DeleteChoicesCompletionListener.java | 13 ------------- .../screen/choiceset/DeleteChoicesOperation.java | 4 ++-- .../choiceset/PreloadPresentChoicesOperation.java | 20 ++++++++++---------- 5 files changed, 21 insertions(+), 30 deletions(-) delete mode 100644 base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesCompletionListener.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java index 14a621c49..515638a00 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java @@ -258,7 +258,7 @@ public class PreloadPresentChoicesOperationTests { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - PreloadPresentChoicesOperation.PreloadChoicesCompletionListener listener = new PreloadPresentChoicesOperation.PreloadChoicesCompletionListener() { + BaseChoiceSetManager.ChoicesOperationCompletionListener listener = new BaseChoiceSetManager.ChoicesOperationCompletionListener() { @Override public void onComplete(boolean success, HashSet loadedChoiceCells) { choiceSet.cancel(); @@ -302,7 +302,7 @@ public class PreloadPresentChoicesOperationTests { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(6, 0)); WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - PreloadPresentChoicesOperation.PreloadChoicesCompletionListener listener = new PreloadPresentChoicesOperation.PreloadChoicesCompletionListener() { + BaseChoiceSetManager.ChoicesOperationCompletionListener listener = new BaseChoiceSetManager.ChoicesOperationCompletionListener() { @Override public void onComplete(boolean success, HashSet loadedChoiceCells) { choiceSet.cancel(); @@ -400,7 +400,7 @@ public class PreloadPresentChoicesOperationTests { when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(5, 3)); WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); - PreloadPresentChoicesOperation.PreloadChoicesCompletionListener listener = new PreloadPresentChoicesOperation.PreloadChoicesCompletionListener() { + BaseChoiceSetManager.ChoicesOperationCompletionListener listener = new BaseChoiceSetManager.ChoicesOperationCompletionListener() { @Override public void onComplete(boolean success, HashSet loadedChoiceCells) { choiceSet.cancel(); 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 66ba0944f..76f4a7b7c 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 @@ -187,6 +187,10 @@ abstract class BaseChoiceSetManager extends BaseSubManager { transactionQueue.add(checkChoiceVR, false); } + interface ChoicesOperationCompletionListener { + void onComplete(boolean success, HashSet loadedChoiceCells); + } + /** * Preload choices to improve performance while presenting a choice set at a later time * @@ -211,7 +215,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { updateIdsOnChoices(choicesToUpload); if (fileManager.get() != null) { - PreloadPresentChoicesOperation preloadChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, this.preloadedChoices, new PreloadPresentChoicesOperation.PreloadChoicesCompletionListener() { + PreloadPresentChoicesOperation preloadChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, this.preloadedChoices, new ChoicesOperationCompletionListener() { @Override public void onComplete(boolean success, HashSet loadedCells) { preloadedChoices = loadedCells; @@ -243,7 +247,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return; } - DeleteChoicesOperation deleteChoicesOperation = new DeleteChoicesOperation(internalInterface, new HashSet<>(preloadedChoices), preloadedChoices, new DeleteChoicesCompletionListener() { + DeleteChoicesOperation deleteChoicesOperation = new DeleteChoicesOperation(internalInterface, new HashSet<>(preloadedChoices), preloadedChoices, new ChoicesOperationCompletionListener() { @Override public void onComplete(boolean success, HashSet updatedLoadedChoiceCells) { if (!success) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesCompletionListener.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesCompletionListener.java deleted file mode 100644 index 346814fb4..000000000 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesCompletionListener.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.smartdevicelink.managers.screen.choiceset; - -import java.util.HashSet; - -public interface DeleteChoicesCompletionListener { - - /** - * Returns whether an DeleteChoices operation was successful or not along with which choice cells are loaded - * @param success - Boolean that is True if Operation was a success, False otherwise. - * @param loadedChoiceCells - A set of the ChoiceCells that are loaded - */ - void onComplete(boolean success, HashSet loadedChoiceCells); -} diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java index c424942dc..b71ee4f14 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java @@ -51,11 +51,11 @@ class DeleteChoicesOperation extends Task { private static final String TAG = "DeleteChoicesOperation"; private final WeakReference internalInterface; private HashSet cellsToDelete; - private final DeleteChoicesCompletionListener completionListener; + private final BaseChoiceSetManager.ChoicesOperationCompletionListener completionListener; private HashSet loadedCells; private boolean completionSuccess = false; - DeleteChoicesOperation(ISdl internalInterface, HashSet cellsToDelete, HashSet loadedCells, DeleteChoicesCompletionListener completionListener) { + DeleteChoicesOperation(ISdl internalInterface, HashSet cellsToDelete, HashSet loadedCells, BaseChoiceSetManager.ChoicesOperationCompletionListener completionListener) { super("DeleteChoicesOperation"); this.internalInterface = new WeakReference<>(internalInterface); this.cellsToDelete = cellsToDelete; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 851aea3a5..71fd79fd6 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -88,7 +88,7 @@ class PreloadPresentChoicesOperation extends Task { private final WindowCapability defaultMainWindowCapability; private final String displayName; private final ArrayList cellsToUpload; - private final PreloadChoicesCompletionListener completionListener; + private final BaseChoiceSetManager.ChoicesOperationCompletionListener completionListener; private final ChoiceSetSelectionListener selectionListener; private final boolean isVROptional; private boolean choiceError = false; @@ -118,7 +118,7 @@ class PreloadPresentChoicesOperation extends Task { private SDLPreloadPresentChoicesOperationState currentState; PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, String displayName, WindowCapability defaultWindowCapability, - Boolean isVROptional, LinkedHashSet cellsToPreload, HashSet loadedCells, PreloadChoicesCompletionListener listener) { + Boolean isVROptional, LinkedHashSet cellsToPreload, HashSet loadedCells, BaseChoiceSetManager.ChoicesOperationCompletionListener listener) { super("PreloadPresentChoiceOperation"); this.internalInterface = new WeakReference<>(internalInterface); this.fileManager = new WeakReference<>(fileManager); @@ -141,8 +141,8 @@ class PreloadPresentChoicesOperation extends Task { } PreloadPresentChoicesOperation(ISdl internalInterface, FileManager fileManager, ChoiceSet choiceSet, InteractionMode mode, - KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, Integer cancelID, String displayName, WindowCapability windowCapability, - Boolean isVROptional, HashSet loadedCells, PreloadChoicesCompletionListener preloadListener, ChoiceSetSelectionListener listener) { + KeyboardProperties originalKeyboardProperties, KeyboardListener keyboardListener, Integer cancelID, String displayName, WindowCapability windowCapability, + Boolean isVROptional, HashSet loadedCells, BaseChoiceSetManager.ChoicesOperationCompletionListener preloadListener, ChoiceSetSelectionListener listener) { super("PreloadPresentChoiceOperation"); this.internalInterface = new WeakReference<>(internalInterface); this.keyboardListener = keyboardListener; @@ -359,6 +359,7 @@ class PreloadPresentChoicesOperation extends Task { if (this.keyboardListener == null || this.originalKeyboardProperties == null) { if(listener != null) { listener.onComplete(true); + finishOperation(); return; } } @@ -375,6 +376,9 @@ class PreloadPresentChoicesOperation extends Task { } if (listener != null) { listener.onComplete(response.getSuccess()); + if (response.getSuccess()) { + onFinished(); + } } } }); @@ -475,10 +479,6 @@ class PreloadPresentChoicesOperation extends Task { } } - interface PreloadChoicesCompletionListener { - void onComplete(boolean success, HashSet loadedChoiceCells); - } - // Present Helpers void setSelectedCellWithId(Integer cellId) { if (choiceSet.getChoices() != null && cellId != null) { @@ -805,7 +805,7 @@ class PreloadPresentChoicesOperation extends Task { } else { DebugTool.logError(TAG, "Failed to reset choice keyboard properties to original config " + response.getResultCode() + ", " + response.getInfo()); } - PreloadPresentChoicesOperation.super.onFinished(); + onFinished(); } }); @@ -816,7 +816,7 @@ class PreloadPresentChoicesOperation extends Task { DebugTool.logError(TAG, "Internal Interface null when finishing choice keyboard reset"); } } else { - PreloadPresentChoicesOperation.super.onFinished(); + onFinished(); } } } -- cgit v1.2.1 From 372076351ac693712270ccf6e8e1d009714c7b57 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 13 Aug 2021 14:58:53 -0400 Subject: Transfer listeners from new kepts cells to old kep cells --- .../managers/screen/menu/BaseMenuManager.java | 5 +---- .../managers/screen/menu/MenuReplaceOperation.java | 13 +++++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 26e2f2816..4cc00e518 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -169,10 +169,7 @@ abstract class BaseMenuManager extends BaseSubManager { return; } - if (cells.equals(this.menuCells)) { - DebugTool.logError(TAG, "The set menu cells are identical to previously set menu cells. Skipping..."); - return; - } else if (!menuCellsAreUnique(cells, new ArrayList())) { + if (!menuCellsAreUnique(cells, new ArrayList())) { DebugTool.logError(TAG, "Not all set menu cells are unique, but that is required"); return; } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 0a1f92e03..121bf70f7 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -131,6 +131,8 @@ class MenuReplaceOperation extends Task { // We will transfer the ids for subCells later transferCellIDsFromOldCells(oldKeeps, newKeeps); + transferListenersFromNewCells(newKeeps, oldKeeps); + // Upload the Artworks, then we will start updating the main menu uploadMenuArtworks(new CompletionListener() { @Override @@ -253,6 +255,8 @@ class MenuReplaceOperation extends Task { transferCellIDsFromOldCells(oldKeeps, newKeeps); + transferListenersFromNewCells(newKeeps, oldKeeps); + sendDeleteCurrentMenu(cellsToDelete, new CompletionListener() { @Override public void onComplete(boolean success) { @@ -406,6 +410,15 @@ class MenuReplaceOperation extends Task { } } + private void transferListenersFromNewCells(List newCells, List oldCells) { + if (oldCells == null || oldCells.isEmpty()) { + return; + } + for (int i = 0; i < newCells.size(); i++) { + oldCells.get(i).setMenuSelectionListener(newCells.get(i).getMenuSelectionListener()); + } + } + private String convertErrorsMapToString(Map errors) { if (errors == null) { return null; -- cgit v1.2.1 From 93c1e12de1948be7fa635cd0a37079a52c3bb9a5 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 13 Aug 2021 15:13:40 -0400 Subject: Fix broken unit test --- .../screen/menu/MenuReplaceOperationTests.java | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java index 0079a6a99..31adc36d0 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java @@ -32,6 +32,15 @@ package com.smartdevicelink.managers.screen.menu; +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.verify; +import static org.mockito.Mockito.when; + import android.os.Handler; import androidx.test.ext.junit.runners.AndroidJUnit4; @@ -45,10 +54,12 @@ import com.smartdevicelink.managers.file.filetypes.SdlArtwork; import com.smartdevicelink.proxy.RPCMessage; import com.smartdevicelink.proxy.RPCRequest; import com.smartdevicelink.proxy.RPCResponse; +import com.smartdevicelink.proxy.rpc.SdlMsgVersion; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.test.TestValues; +import com.smartdevicelink.util.Version; import org.junit.Before; import org.junit.Test; @@ -62,17 +73,6 @@ import java.util.Arrays; import java.util.List; import java.util.Random; -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.updateIdsOnMenuCells; -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.verify; -import static org.mockito.Mockito.when; - @RunWith(AndroidJUnit4.class) public class MenuReplaceOperationTests { private Handler mainHandler; @@ -99,7 +99,6 @@ public class MenuReplaceOperationTests { MenuCell menuCell2 = new MenuCell("cell 2", TestValues.GENERAL_ARTWORK, null, null); final List updatedMenu = Arrays.asList(menuCell1, menuCell2); - updateIdsOnMenuCells(updatedMenu, parentIdNotFound); MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, currentMenu, updatedMenu, true, new MenuManagerCompletionListener() { @Override public void onComplete(final boolean success, final List currentMenuCells) { @@ -140,6 +139,7 @@ public class MenuReplaceOperationTests { } }; doAnswer(answer).when(internalInterface).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); + when(internalInterface.getSdlMsgVersion()).thenReturn(new SdlMsgVersion(new Version(7, 1, 0))); return internalInterface; } -- cgit v1.2.1 From 2fb7b0d89619ab1b66218c12277e17cd5f3b1ced Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 13 Aug 2021 15:21:08 -0400 Subject: Reorder tests --- .../managers/screen/menu/MenuReplaceOperationTests.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java index 31adc36d0..ee47f49b8 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java @@ -93,11 +93,12 @@ public class MenuReplaceOperationTests { FileManager fileManager = createFileManagerMock(); WindowCapability windowCapability = createWindowCapability(true, true); MenuConfiguration menuConfiguration = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); - List currentMenu = new ArrayList<>(); + MenuCell menuCell1_1 = new MenuCell("cell 1_1", TestValues.GENERAL_ARTWORK, null, null); MenuCell menuCell1 = new MenuCell("cell 1", null, TestValues.GENERAL_ARTWORK, Arrays.asList(menuCell1_1)); MenuCell menuCell2 = new MenuCell("cell 2", TestValues.GENERAL_ARTWORK, null, null); + final List currentMenu = new ArrayList<>(); final List updatedMenu = Arrays.asList(menuCell1, menuCell2); MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, currentMenu, updatedMenu, true, new MenuManagerCompletionListener() { @Override -- cgit v1.2.1 From 9791fcf8f105b7213b18d48e9ec087dea2d1b27d Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 13 Aug 2021 15:28:02 -0400 Subject: update finishOperation --- .../choiceset/PreloadPresentChoicesOperation.java | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 71fd79fd6..e9caab338 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -184,19 +184,19 @@ class PreloadPresentChoicesOperation extends Task { public void onComplete(boolean success) { // If some artworks failed to upload, we are still going to try to load the cells if (getState()==CANCELED || !success) { - finishOperation(); + finishOperation(false); return; } preloadCells(new CompletionListener() { @Override public void onComplete(boolean success) { if (getState()==CANCELED || !success) { - finishOperation(); + finishOperation(false); return; } if (choiceSet == null) { - finishOperation(); + finishOperation(false); return; } DebugTool.logInfo(TAG, "Choice Operation: Executing present choice set operation"); @@ -205,7 +205,7 @@ class PreloadPresentChoicesOperation extends Task { @Override public void onComplete(boolean success) { if (getState()==CANCELED || !success) { - finishOperation(); + finishOperation(false); return; } presentChoiceSet(new CompletionListener() { @@ -215,7 +215,7 @@ class PreloadPresentChoicesOperation extends Task { @Override public void onComplete(boolean success) { if (!success) { - finishOperation(); + finishOperation(false); return; } } @@ -359,7 +359,7 @@ class PreloadPresentChoicesOperation extends Task { if (this.keyboardListener == null || this.originalKeyboardProperties == null) { if(listener != null) { listener.onComplete(true); - finishOperation(); + finishOperation(true); return; } } @@ -377,7 +377,7 @@ class PreloadPresentChoicesOperation extends Task { if (listener != null) { listener.onComplete(response.getSuccess()); if (response.getSuccess()) { - onFinished(); + finishOperation(true); } } } @@ -407,7 +407,7 @@ class PreloadPresentChoicesOperation extends Task { if (listener != null) { listener.onComplete(false); } - finishOperation(); + finishOperation(false); return; } @@ -687,7 +687,7 @@ class PreloadPresentChoicesOperation extends Task { @Override public void onNotified(RPCNotification notification) { if (getState() == Task.CANCELED) { - finishOperation(); + finishOperation(false); return; } @@ -780,11 +780,11 @@ class PreloadPresentChoicesOperation extends Task { return choiceIds; } - void finishOperation() { + void finishOperation(boolean success) { this.currentState = SDLPreloadPresentChoicesOperationState.FINISHING; if (this.completionListener != null) { - this.completionListener.onComplete(false, loadedCells); + this.completionListener.onComplete(success, loadedCells); } if (this.choiceSet == null || this.choiceSet.getChoiceSetSelectionListener() == null) { -- cgit v1.2.1 From 511eca3ffd203f120cacde6ed457dfc0182666cd Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 13 Aug 2021 15:34:30 -0400 Subject: update finishOpertion --- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index e9caab338..8bc55b11b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -214,10 +214,8 @@ class PreloadPresentChoicesOperation extends Task { resetKeyboardProperties(new CompletionListener() { @Override public void onComplete(boolean success) { - if (!success) { - finishOperation(false); - return; - } + finishOperation(success); + return; } }); } -- cgit v1.2.1 From b7b1c47862c300dd5b4217115c1bbe0eff8eb8e9 Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 13 Aug 2021 15:40:29 -0400 Subject: Fix unit test --- .../managers/screen/choiceset/PreloadPresentChoicesOperationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java index 515638a00..dc0138374 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java @@ -346,7 +346,7 @@ public class PreloadPresentChoicesOperationTests { WindowCapability windowCapability = new WindowCapability(); HashSet loadedCells = new HashSet<>(); presentChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager, choiceSet, InteractionMode.MANUAL_ONLY, null, null, TestValues.GENERAL_INTEGER,null, windowCapability, true, loadedCells, null, null); - presentChoicesOperation.finishOperation(); + presentChoicesOperation.finishOperation(false); assertEquals(Task.FINISHED, presentChoicesOperation.getState()); -- cgit v1.2.1 From ce763033e40233eb6e9ac38bcefebdc3177b60be Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 13 Aug 2021 15:45:57 -0400 Subject: Add unit tests for issue #1723 --- .../screen/menu/MenuReplaceOperationTests.java | 51 +++++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java index ee47f49b8..f0441a322 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java @@ -55,6 +55,7 @@ import com.smartdevicelink.proxy.RPCMessage; import com.smartdevicelink.proxy.RPCRequest; import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.SdlMsgVersion; +import com.smartdevicelink.proxy.rpc.TextField; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; @@ -91,7 +92,7 @@ public class MenuReplaceOperationTests { public void testSuccess() { final ISdl internalInterface = createISdlMock(); FileManager fileManager = createFileManagerMock(); - WindowCapability windowCapability = createWindowCapability(true, true); + WindowCapability windowCapability = createWindowCapability(true, true, new ArrayList()); MenuConfiguration menuConfiguration = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); MenuCell menuCell1_1 = new MenuCell("cell 1_1", TestValues.GENERAL_ARTWORK, null, null); @@ -116,6 +117,51 @@ public class MenuReplaceOperationTests { transactionQueue.add(operation, false); } + @Test + public void testSwitchingCellsOrder() { + // This unit test is for this bug https://github.com/smartdevicelink/sdl_java_suite/issues/1723 + final ISdl internalInterface = createISdlMock(); + final FileManager fileManager = createFileManagerMock(); + final WindowCapability windowCapability = createWindowCapability(true, true, new ArrayList()); + final MenuConfiguration menuConfiguration = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); + + MenuSelectionListener listener = null; + final MenuCell menuCell1 = new MenuCell("A", "SecondaryText", null, null, null, null, listener); + final MenuCell menuCell2 = new MenuCell("A", null, null, null, null, null, listener); + final MenuCell menuCell3 = new MenuCell("C", null, null, null, null, null, listener); + + MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, new ArrayList(), Arrays.asList(menuCell1, menuCell2, menuCell3), true, new MenuManagerCompletionListener() { + @Override + public void onComplete(final boolean success, final List currentMenuCells1) { + assertOnMainThread(new Runnable() { + @Override + public void run() { + assertTrue(success); + verify(internalInterface, Mockito.times(1)).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); + + MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, new ArrayList(), Arrays.asList(menuCell2, menuCell1), true, new MenuManagerCompletionListener() { + @Override + public void onComplete(final boolean success, final List currentMenuCells2) { + assertOnMainThread(new Runnable() { + @Override + public void run() { + assertTrue(success); + assertEquals(2, currentMenuCells2.size()); + assertEquals("A", currentMenuCells2.get(0).getUniqueTitle()); + assertEquals("A (2)", currentMenuCells2.get(1).getUniqueTitle()); + verify(internalInterface, Mockito.times(2)).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); + } + }); + } + }); + transactionQueue.add(operation, false); + } + }); + } + }); + transactionQueue.add(operation, false); + } + private ISdl createISdlMock() { final ISdl internalInterface = mock(ISdl.class); @@ -163,8 +209,9 @@ public class MenuReplaceOperationTests { return fileManager; } - private WindowCapability createWindowCapability(boolean supportsList, boolean supportsTile) { + private WindowCapability createWindowCapability(boolean supportsList, boolean supportsTile, ArrayList supportedTextFields) { WindowCapability windowCapability = new WindowCapability(); + windowCapability.setTextFields(supportedTextFields); windowCapability.setMenuLayoutsAvailable(new ArrayList()); if (supportsList) { windowCapability.getMenuLayoutsAvailable().add(MenuLayout.LIST); -- cgit v1.2.1 From c120a6c66ddae62ade892d513b9eae2a56e1dbcc Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 13 Aug 2021 16:30:28 -0400 Subject: Add more unit tests --- .../screen/menu/MenuReplaceOperationTests.java | 52 ++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java index f0441a322..08f81130e 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java @@ -58,6 +58,7 @@ import com.smartdevicelink.proxy.rpc.SdlMsgVersion; import com.smartdevicelink.proxy.rpc.TextField; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; +import com.smartdevicelink.proxy.rpc.enums.TriggerSource; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.test.TestValues; import com.smartdevicelink.util.Version; @@ -162,6 +163,57 @@ public class MenuReplaceOperationTests { transactionQueue.add(operation, false); } + @Test + public void testResendingSameCellWithDifferentListener() { + final ISdl internalInterface = createISdlMock(); + final FileManager fileManager = createFileManagerMock(); + final WindowCapability windowCapability = createWindowCapability(true, true, new ArrayList()); + final MenuConfiguration menuConfiguration = new MenuConfiguration(MenuLayout.LIST, MenuLayout.LIST); + + final MenuSelectionListener listener1 = new MenuSelectionListener() { + @Override + public void onTriggered(TriggerSource trigger) {} + }; + final MenuSelectionListener listener2 = new MenuSelectionListener() { + @Override + public void onTriggered(TriggerSource trigger) {} + }; + final MenuCell menuCell1 = new MenuCell("A", null, null, null, null, null, listener1); + final MenuCell menuCell2 = new MenuCell("A", null, null, null, null, null, listener2); + + MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, new ArrayList(), Arrays.asList(menuCell1), true, new MenuManagerCompletionListener() { + @Override + public void onComplete(final boolean success, final List currentMenuCells1) { + assertOnMainThread(new Runnable() { + @Override + public void run() { + assertTrue(success); + assertEquals(1, currentMenuCells1.size()); + assertEquals(listener1, currentMenuCells1.get(0).getMenuSelectionListener()); + verify(internalInterface, Mockito.times(1)).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); + + MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, currentMenuCells1, Arrays.asList(menuCell2), true, new MenuManagerCompletionListener() { + @Override + public void onComplete(final boolean success, final List currentMenuCells2) { + assertOnMainThread(new Runnable() { + @Override + public void run() { + assertTrue(success); + assertEquals(1, currentMenuCells2.size()); + assertEquals(listener2, currentMenuCells2.get(0).getMenuSelectionListener()); + verify(internalInterface, Mockito.times(1)).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); + } + }); + } + }); + transactionQueue.add(operation, false); + } + }); + } + }); + transactionQueue.add(operation, false); + } + private ISdl createISdlMock() { final ISdl internalInterface = mock(ISdl.class); -- cgit v1.2.1 From 757a09cfa018d9501fa3fc918d4f91beba3b5385 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Fri, 13 Aug 2021 17:16:52 -0400 Subject: Fix unit tests --- .../managers/screen/menu/MenuReplaceOperationTests.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java index 08f81130e..284f1efd5 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperationTests.java @@ -33,6 +33,7 @@ package com.smartdevicelink.managers.screen.menu; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.cloneMenuCellsList; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -101,7 +102,7 @@ public class MenuReplaceOperationTests { MenuCell menuCell2 = new MenuCell("cell 2", TestValues.GENERAL_ARTWORK, null, null); final List currentMenu = new ArrayList<>(); - final List updatedMenu = Arrays.asList(menuCell1, menuCell2); + final List updatedMenu = cloneMenuCellsList(Arrays.asList(menuCell1, menuCell2)); MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, currentMenu, updatedMenu, true, new MenuManagerCompletionListener() { @Override public void onComplete(final boolean success, final List currentMenuCells) { @@ -131,16 +132,21 @@ public class MenuReplaceOperationTests { final MenuCell menuCell2 = new MenuCell("A", null, null, null, null, null, listener); final MenuCell menuCell3 = new MenuCell("C", null, null, null, null, null, listener); - MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, new ArrayList(), Arrays.asList(menuCell1, menuCell2, menuCell3), true, new MenuManagerCompletionListener() { + MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, new ArrayList(), cloneMenuCellsList(Arrays.asList(menuCell1, menuCell2, menuCell3)), true, new MenuManagerCompletionListener() { @Override public void onComplete(final boolean success, final List currentMenuCells1) { assertOnMainThread(new Runnable() { @Override public void run() { assertTrue(success); + assertEquals(3, currentMenuCells1.size()); + assertEquals("A", currentMenuCells1.get(0).getUniqueTitle()); + assertEquals("A (2)", currentMenuCells1.get(1).getUniqueTitle()); + assertEquals("C", currentMenuCells1.get(2).getUniqueTitle()); + verify(internalInterface, Mockito.times(1)).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); - MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, new ArrayList(), Arrays.asList(menuCell2, menuCell1), true, new MenuManagerCompletionListener() { + MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, currentMenuCells1, cloneMenuCellsList(Arrays.asList(menuCell2, menuCell1)), true, new MenuManagerCompletionListener() { @Override public void onComplete(final boolean success, final List currentMenuCells2) { assertOnMainThread(new Runnable() { @@ -181,7 +187,7 @@ public class MenuReplaceOperationTests { final MenuCell menuCell1 = new MenuCell("A", null, null, null, null, null, listener1); final MenuCell menuCell2 = new MenuCell("A", null, null, null, null, null, listener2); - MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, new ArrayList(), Arrays.asList(menuCell1), true, new MenuManagerCompletionListener() { + MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, new ArrayList(), cloneMenuCellsList(Arrays.asList(menuCell1)), true, new MenuManagerCompletionListener() { @Override public void onComplete(final boolean success, final List currentMenuCells1) { assertOnMainThread(new Runnable() { @@ -192,7 +198,7 @@ public class MenuReplaceOperationTests { assertEquals(listener1, currentMenuCells1.get(0).getMenuSelectionListener()); verify(internalInterface, Mockito.times(1)).sendRPCs(any(List.class), any(OnMultipleRequestListener.class)); - MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, currentMenuCells1, Arrays.asList(menuCell2), true, new MenuManagerCompletionListener() { + MenuReplaceOperation operation = new MenuReplaceOperation(internalInterface, fileManager, windowCapability, menuConfiguration, currentMenuCells1, cloneMenuCellsList(Arrays.asList(menuCell2)), true, new MenuManagerCompletionListener() { @Override public void onComplete(final boolean success, final List currentMenuCells2) { assertOnMainThread(new Runnable() { -- cgit v1.2.1 From c26675575f68b844cd467ae051efb466bb067406 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 17 Aug 2021 09:50:40 -0400 Subject: Move isSubMenuCell() to MenuCell class --- .../managers/screen/menu/BaseMenuManager.java | 7 +++--- .../managers/screen/menu/MenuCell.java | 4 ++++ .../managers/screen/menu/MenuReplaceOperation.java | 9 ++++--- .../managers/screen/menu/MenuReplaceUtilities.java | 28 ++++++++++------------ 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 4cc00e518..964c999ec 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -33,7 +33,6 @@ package com.smartdevicelink.managers.screen.menu; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.cloneMenuCellsList; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.isSubMenuCell; import androidx.annotation.NonNull; @@ -219,7 +218,7 @@ abstract class BaseMenuManager extends BaseSubManager { } } - if (cell != null && (!isSubMenuCell(cell))) { + if (cell != null && (!cell.isSubMenuCell())) { DebugTool.logError(TAG, String.format("The cell %s does not contain any sub cells, so no submenu can be opened", cell.getTitle())); return false; } else if (cell != null && foundClonedCell == null) { @@ -367,7 +366,7 @@ abstract class BaseMenuManager extends BaseSubManager { cell.getMenuSelectionListener().onTriggered(command.getTriggerSource()); return true; } - if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) { // for each cell, if it has sub cells, recursively loop through those as well return callListenerForCells(cell.getSubCells(), command); } @@ -430,7 +429,7 @@ abstract class BaseMenuManager extends BaseSubManager { identicalCellsCheckSet.add(cell); // Recursively check the sub-cell lists to see if they are all unique as well. If anything is not, this will chain back up the list to return false. - if (isSubMenuCell(cell) && cell.getSubCells().size() > 0) { + if (cell.isSubMenuCell() && cell.getSubCells().size() > 0) { boolean subCellsAreUnique = menuCellsAreUnique(cell.getSubCells(), allVoiceCommands); if (!subCellsAreUnique) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index 93d6d1477..e4c8ac47b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -432,6 +432,10 @@ public class MenuCell implements Cloneable { // HELPER + boolean isSubMenuCell() { + return getSubCells() != null; + } + /** * Note: You should compare using the {@link #equals(Object)} method.
* Hash the parameters of the object and return the result for comparison diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 121bf70f7..51ada9d19 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -8,7 +8,6 @@ import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.clon import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandIdForRPCRequest; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.findAllArtworksToBeUploadedFromCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.isSubMenuCell; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.mainMenuCommandsForCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.positionForRPCRequest; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.removeCellFromList; @@ -234,7 +233,7 @@ class MenuReplaceOperation extends Task { return; } - if (oldKeptCells.get(startIndex) != null && isSubMenuCell(oldKeptCells.get(startIndex)) && !oldKeptCells.get(startIndex).getSubCells().isEmpty()) { + if (oldKeptCells.get(startIndex) != null && oldKeptCells.get(startIndex).isSubMenuCell() && !oldKeptCells.get(startIndex).getSubCells().isEmpty()) { DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); // If both old and new menu cells are empty. Then nothing needs to be done. @@ -455,7 +454,7 @@ class MenuReplaceOperation extends Task { } // Check for subMenu fields supported - if (isSubMenuCell(cell)) { + if (cell.isSubMenuCell()) { if (!hasTextFieldOfName(windowCapability, TextFieldName.menuSubMenuSecondaryText)) { cell.setSecondaryText(null); } @@ -501,7 +500,7 @@ class MenuReplaceOperation extends Task { cell.setUniqueTitle(cell.getTitle()); } - if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) { generateUniqueNamesForCells(cell.getSubCells(), supportsMenuUniqueness); } } @@ -514,7 +513,7 @@ class MenuReplaceOperation extends Task { for (int i = 0; i < fromMenuCells.size(); i++) { toMenuCells.get(i).setUniqueTitle(fromMenuCells.get(i).getUniqueTitle()); - if (isSubMenuCell(fromMenuCells.get(i)) && !fromMenuCells.get(i).getSubCells().isEmpty()) { + if (fromMenuCells.get(i).isSubMenuCell() && !fromMenuCells.get(i).getSubCells().isEmpty()) { applyUniqueNamesOnCells(fromMenuCells.get(i).getSubCells(), toMenuCells.get(i).getSubCells()); } } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 3579425ca..1c0546ad0 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -46,7 +46,7 @@ class MenuReplaceUtilities { if (parentId != parentIdNotFound) { cell.setParentCellId(parentId); } - if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) { updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); } } @@ -68,7 +68,7 @@ class MenuReplaceUtilities { artworks.add(cell.getSecondaryArtwork()); } } - if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) { artworks.addAll(findAllArtworksToBeUploadedFromCells(cell.getSubCells(), fileManager, windowCapability)); } } @@ -78,13 +78,13 @@ class MenuReplaceUtilities { // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image static boolean shouldCellIncludePrimaryImageFromCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability) { - boolean supportsImage = isSubMenuCell(cell) ? hasImageFieldOfName(windowCapability, ImageFieldName.subMenuIcon) : hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); + boolean supportsImage = cell.isSubMenuCell() ? hasImageFieldOfName(windowCapability, ImageFieldName.subMenuIcon) : hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon); return cell.getIcon() != null && supportsImage && (fileManager.hasUploadedFile(cell.getIcon()) || cell.getIcon().isStaticIcon()); } // If there is an icon and the icon has been uploaded, or if the icon is a static icon, it should include the image static boolean shouldCellIncludeSecondaryImageFromCell(MenuCell cell, FileManager fileManager, WindowCapability windowCapability) { - boolean supportsImage = isSubMenuCell(cell) ? hasImageFieldOfName(windowCapability, ImageFieldName.menuSubMenuSecondaryImage) : hasImageFieldOfName(windowCapability, ImageFieldName.menuCommandSecondaryImage); + boolean supportsImage = cell.isSubMenuCell() ? hasImageFieldOfName(windowCapability, ImageFieldName.menuSubMenuSecondaryImage) : hasImageFieldOfName(windowCapability, ImageFieldName.menuCommandSecondaryImage); return cell.getSecondaryArtwork() != null && supportsImage && (fileManager.hasUploadedFile(cell.getSecondaryArtwork()) || cell.getSecondaryArtwork().isStaticIcon()); } @@ -115,7 +115,7 @@ class MenuReplaceUtilities { static List deleteCommandsForCells(List cells) { List deletes = new ArrayList<>(); for (MenuCell cell : cells) { - if (isSubMenuCell(cell)) { + if (cell.isSubMenuCell()) { DeleteSubMenu delete = new DeleteSubMenu(cell.getCellId()); deletes.add(delete); } else { @@ -135,7 +135,7 @@ class MenuReplaceUtilities { for (int updateCellsIndex = 0; updateCellsIndex < cells.size(); updateCellsIndex++) { MenuCell addCell = cells.get(updateCellsIndex); if (mainCell.equals(addCell)) { - if (isSubMenuCell(addCell)) { + if (addCell.isSubMenuCell()) { commands.add(subMenuCommandForMenuCell(addCell, fileManager, windowCapability, menuInteger, defaultSubmenuLayout)); } else { commands.add(commandForMenuCell(addCell, fileManager, windowCapability, menuInteger)); @@ -150,7 +150,7 @@ class MenuReplaceUtilities { static List subMenuCommandsForCells(List cells, FileManager fileManager, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { List commands = new ArrayList<>(); for (MenuCell cell : cells) { - if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) { commands.addAll(allCommandsForCells(cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout)); } } @@ -162,7 +162,7 @@ class MenuReplaceUtilities { for (int cellIndex = 0; cellIndex < cells.size(); cellIndex++) { MenuCell cell = cells.get(cellIndex); - if (isSubMenuCell(cell)) { + if (cell.isSubMenuCell()) { commands.add(subMenuCommandForMenuCell(cell, fileManager, windowCapability, cellIndex, defaultSubmenuLayout)); // Recursively grab the commands for all the sub cells @@ -230,7 +230,7 @@ class MenuReplaceUtilities { // If the cell id matches the command id, remove it from the list and return menuCellList.remove(menuCell); return true; - } else if (isSubMenuCell(menuCell) && !menuCell.getSubCells().isEmpty()) { + } else if (menuCell.isSubMenuCell() && !menuCell.getSubCells().isEmpty()) { // If the menu cell has sub cells, we need to recurse and check the sub cells List newList = menuCell.getSubCells(); boolean foundAndRemovedItem = removeCellFromList(newList, commandId); @@ -249,7 +249,7 @@ class MenuReplaceUtilities { if (cell.getCellId() == cellId) { addedCell = cell; break; - } else if (isSubMenuCell(cell) && !cell.getSubCells().isEmpty()) { + } else if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) { boolean success = addCellWithCellId(cellId, position, cell.getSubCells(), mainMenuList); if (success) { return true; @@ -278,7 +278,7 @@ class MenuReplaceUtilities { // If we found the correct submenu, insert it into that submenu insertMenuCell(cell, menuCell.getSubCells(), position); return true; - } else if (isSubMenuCell(menuCell) && !menuCell.getSubCells().isEmpty()) { + } else if (menuCell.isSubMenuCell() && !menuCell.getSubCells().isEmpty()) { // Check the sub cells of this cell to see if any of those have cell ids that match the parent cell id List newList = menuCell.getSubCells(); boolean foundAndAddedItem = addMenuCell(cell, newList, position); @@ -293,7 +293,7 @@ class MenuReplaceUtilities { private static void insertMenuCell(MenuCell cell, List cellList, int position) { MenuCell cellToInsert = cell; - if (isSubMenuCell(cellToInsert)) { + if (cellToInsert.isSubMenuCell()) { // We should not add the subCells automatically when adding a parent cell cellToInsert = cell.clone(); cellToInsert.getSubCells().clear(); @@ -305,10 +305,6 @@ class MenuReplaceUtilities { } } - static boolean isSubMenuCell(MenuCell menuCell) { - return menuCell.getSubCells() != null; - } - static List cloneMenuCellsList(List originalList) { if (originalList == null) { return new ArrayList<>(); -- cgit v1.2.1 From 1f4cfcc589dc892bed0e8d000a3b3b722508ff7b Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 18 Aug 2021 11:52:38 -0400 Subject: Code Review Fixes --- .../main/java/com/smartdevicelink/protocol/SdlPacketFactory.java | 2 -- .../java/com/smartdevicelink/protocol/enums/QueryErrorCode.java | 3 +++ .../main/java/com/smartdevicelink/protocol/enums/QueryID.java | 9 ++++++--- .../main/java/com/smartdevicelink/protocol/enums/QueryType.java | 3 +++ 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java b/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java index 98b1e35da..364dc4e30 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SdlPacketFactory.java @@ -35,8 +35,6 @@ import androidx.annotation.RestrictTo; import com.smartdevicelink.protocol.enums.ControlFrameTags; import com.smartdevicelink.protocol.enums.FrameDataControlFrameType; -import com.smartdevicelink.protocol.enums.QueryID; -import com.smartdevicelink.protocol.enums.QueryType; import com.smartdevicelink.protocol.enums.SessionType; import com.smartdevicelink.util.BitConverter; diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryErrorCode.java b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryErrorCode.java index 269c5021e..83f7d8552 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryErrorCode.java +++ b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryErrorCode.java @@ -1,9 +1,12 @@ package com.smartdevicelink.protocol.enums; +import androidx.annotation.RestrictTo; + import com.smartdevicelink.util.ByteEnumer; import java.util.Vector; +@RestrictTo(RestrictTo.Scope.LIBRARY) public class QueryErrorCode extends ByteEnumer { private static final Vector theList = new Vector<>(); diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java index e24481340..da1e3fab3 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java +++ b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java @@ -1,5 +1,7 @@ package com.smartdevicelink.protocol.enums; +import androidx.annotation.RestrictTo; + import com.smartdevicelink.util.BitConverter; import java.util.Arrays; @@ -7,6 +9,7 @@ import java.util.Enumeration; import java.util.Objects; import java.util.Vector; +@RestrictTo(RestrictTo.Scope.LIBRARY) public class QueryID { private static final Vector theList = new Vector<>(); @@ -15,9 +18,9 @@ public class QueryID { return theList; } - private static final byte[] sendHandshakeDataByteArray= {(byte) 0x00, (byte) 0x00, (byte) 0x01}; - private static final byte[] sendInternalErrorByteArray= {(byte) 0x00, (byte) 0x00, (byte) 0x02}; - private static final byte[] invalidQueryIdByteArray= {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + private static final byte[] sendHandshakeDataByteArray = {(byte) 0x00, (byte) 0x00, (byte) 0x01}; + private static final byte[] sendInternalErrorByteArray = {(byte) 0x00, (byte) 0x00, (byte) 0x02}; + private static final byte[] invalidQueryIdByteArray = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; public final static QueryID SEND_HANDSHAKE_DATA = new QueryID(sendHandshakeDataByteArray, "SEND_HANDSHAKE_DATA"); public final static QueryID SEND_INTERNAL_ERROR = new QueryID(sendInternalErrorByteArray, "SEND_INTERNAL_ERROR"); public final static QueryID INVALID_QUERY_ID = new QueryID(invalidQueryIdByteArray, "INVALID_QUERY_ID"); diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryType.java b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryType.java index 543fb56c6..ce26e005b 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryType.java +++ b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryType.java @@ -1,9 +1,12 @@ package com.smartdevicelink.protocol.enums; +import androidx.annotation.RestrictTo; + import com.smartdevicelink.util.ByteEnumer; import java.util.Vector; +@RestrictTo(RestrictTo.Scope.LIBRARY) public class QueryType extends ByteEnumer { private static final Vector theList = new Vector<>(); -- cgit v1.2.1 From 607892db6c0e7227e8fba1ed38994e74f4e02a4e Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 19 Aug 2021 16:37:02 -0400 Subject: Fixes based on lib teams reviews --- .../managers/screen/choiceset/ChoiceCellTests.java | 6 - .../screen/choiceset/ChoiceSetManagerTests.java | 4 - .../screen/choiceset/BaseChoiceSetManager.java | 22 ---- .../managers/screen/choiceset/ChoiceCell.java | 35 ++++-- .../choiceset/PreloadPresentChoicesOperation.java | 131 ++++++++++++++------- 5 files changed, 114 insertions(+), 84 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java index 0dde875ea..b2c362c53 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java @@ -61,7 +61,6 @@ public class ChoiceCellTests { choiceCell.setVoiceCommands(TestValues.GENERAL_STRING_LIST); choiceCell.setArtwork(artwork); choiceCell.setSecondaryArtwork(artwork); - choiceCell.setUniqueText(TestValues.GENERAL_STRING); // use getters and assert equality assertEquals(choiceCell.getText(), TestValues.GENERAL_STRING); @@ -121,11 +120,6 @@ public class ChoiceCellTests { choiceCell3.setSecondaryText(TestValues.GENERAL_STRING); choiceCell3.setTertiaryText(TestValues.GENERAL_STRING); - //UniqueText should not be taken into consideration when checking equality - choiceCell.setUniqueText(TestValues.GENERAL_STRING); - choiceCell2.setUniqueText(TestValues.GENERAL_STRING); - choiceCell3.setUniqueText(TestValues.GENERAL_STRING); - // Make sure our overridden method works, even though these are different objects in memory assertTrue(choiceCell.equals(choiceCell2)); assertFalse(choiceCell.equals(choiceCell3)); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java index 55ca71074..6c7d26f6e 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java @@ -104,8 +104,6 @@ public class ChoiceSetManagerTests { assertEquals(csm.getState(), BaseSubManager.SETTING_UP); assertEquals(csm.currentSystemContext, SystemContext.SYSCTXT_MAIN); assertEquals(csm.currentHMILevel, HMILevel.HMI_NONE); - assertEquals(csm.choiceCellIdMin, 1); - assertEquals(csm.nextChoiceId, 1); assertFalse(csm.isVROptional); assertNotNull(csm.fileManager); assertNotNull(csm.preloadedChoices); @@ -124,7 +122,6 @@ public class ChoiceSetManagerTests { assertNull(csm.defaultMainWindowCapability); assertEquals(csm.transactionQueue.getTasksAsList().size(), 0); - assertEquals(csm.nextChoiceId, 1); assertFalse(csm.isVROptional); @@ -218,7 +215,6 @@ public class ChoiceSetManagerTests { assertEquals(cell1.getChoiceId(), 2000000000); assertEquals(cell2.getChoiceId(), 2000000000); assertEquals(cell3.getChoiceId(), 2000000000); - csm.updateIdsOnChoices(cellSet); // We are looking for unique IDs assertNotSame(cell1.getChoiceId(), 2000000000); assertNotSame(cell2.getChoiceId(), 2000000000); 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 76f4a7b7c..50aa40b38 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 @@ -101,9 +101,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { PresentKeyboardOperation currentlyPresentedKeyboardOperation; - int nextChoiceId; int nextCancelId; - final int choiceCellIdMin = 1; private final int choiceCellCancelIdMin = 101; private final int choiceCellCancelIdMax = 200; boolean isVROptional; @@ -124,7 +122,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { // setting/instantiating class vars this.fileManager = new WeakReference<>(fileManager); preloadedChoices = new HashSet<>(); - nextChoiceId = choiceCellIdMin; nextCancelId = choiceCellCancelIdMin; isVROptional = false; @@ -150,7 +147,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { preloadedChoices = null; isVROptional = false; - nextChoiceId = choiceCellIdMin; nextCancelId = choiceCellCancelIdMin; // remove listeners @@ -212,8 +208,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return; } - updateIdsOnChoices(choicesToUpload); - if (fileManager.get() != null) { PreloadPresentChoicesOperation preloadChoicesOperation = new PreloadPresentChoicesOperation(internalInterface, fileManager.get(), displayName, defaultMainWindowCapability, isVROptional, choicesToUpload, this.preloadedChoices, new ChoicesOperationCompletionListener() { @Override @@ -280,8 +274,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return; } - updateIdsOnChoiceSet(choiceSet); - sendPresentOperation(choiceSet, keyboardListener, mode); } @@ -485,20 +477,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return clone; } - void updateIdsOnChoices(LinkedHashSet choices) { - for (ChoiceCell cell : choices) { - cell.setChoiceId(this.nextChoiceId); - this.nextChoiceId++; - } - } - - void updateIdsOnChoiceSet(ChoiceSet choiceSet) { - for (ChoiceCell cell : choiceSet.getChoices()) { - cell.setChoiceId(this.nextChoiceId); - this.nextChoiceId++; - } - } - ChoiceCell findIfPresent(ChoiceCell cell, HashSet set) { if (set.contains(cell)) { for (ChoiceCell setCell : set) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java index 7d59bf555..46d478000 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCell.java @@ -41,10 +41,10 @@ import java.util.ArrayList; import java.util.List; public class ChoiceCell implements Cloneable{ - private String text, secondaryText, tertiaryText, uniqueText; + private String text, secondaryText, tertiaryText; private List voiceCommands; private SdlArtwork artwork, secondaryArtwork; - private Integer choiceId; + private Integer choiceId, uniqueTextId; /** * MAX ID for cells - Cannot use Integer.MAX_INT as the value is too high. @@ -58,7 +58,7 @@ public class ChoiceCell implements Cloneable{ */ public ChoiceCell(@NonNull String text) { setText(text); - setUniqueText(text); + setUniqueTextId(1); setChoiceId(MAX_ID); } @@ -71,7 +71,7 @@ public class ChoiceCell implements Cloneable{ */ public ChoiceCell(@NonNull String text, List voiceCommands, SdlArtwork artwork) { setText(text); - setUniqueText(text); + setUniqueTextId(1); setVoiceCommands(voiceCommands); setArtwork(artwork); setChoiceId(MAX_ID); @@ -91,7 +91,7 @@ public class ChoiceCell implements Cloneable{ setText(text); setSecondaryText(secondaryText); setTertiaryText(tertiaryText); - setUniqueText(text); + setUniqueTextId(1); setVoiceCommands(voiceCommands); setArtwork(artwork); setSecondaryArtwork(secondaryArtwork); @@ -238,20 +238,28 @@ public class ChoiceCell implements Cloneable{ * Attempting to use cells that are exactly the same (all text and artwork fields are the same) * will not cause this to be used. This will not be used when connected to modules supporting RPC 7.1+. * - * @param uniqueText - the uniqueText to be used in place of primaryText when core does not support identical names for ChoiceSets + * @param uniqueTextId - the uniqueTextId to be appended to primaryText when core does not support identical names for ChoiceSets */ - void setUniqueText(String uniqueText) { - this.uniqueText = uniqueText; + void setUniqueTextId(Integer uniqueTextId) { + this.uniqueTextId = uniqueTextId; } /** * NOTE: USED INTERNALLY - * Get the uniqueText that was used in place of primaryText + * Get the uniqueTextId that was used to append to primaryText * - * @return the uniqueText for this Choice Cell + * @return the uniqueTextId for this Choice Cell */ + Integer getUniqueTextId() { + return uniqueTextId; + } + String getUniqueText() { - return uniqueText; + if (this.uniqueTextId != 1) { + return this.text + " (" + this.uniqueTextId + ")"; + } else { + return this.text; + } } @Override @@ -292,7 +300,7 @@ public class ChoiceCell implements Cloneable{ @NonNull public String toString() { return "ChoiceCell: ID: " + this.choiceId + " Text: " + text + " - Secondary Text: " + secondaryText + " - Tertiary Text: " + tertiaryText + " " + - (text.equals(uniqueText) ? "" : "| Unique Text: " + uniqueText) + " | Artwork Names: " + ((getArtwork() == null || getArtwork().getName() == null) ? "Primary Art null" : getArtwork().getName()) + (uniqueTextId == 1 ? "" : "| Unique Text: " + uniqueTextId) + " | Artwork Names: " + ((getArtwork() == null || getArtwork().getName() == null) ? "Primary Art null" : getArtwork().getName()) + " Secondary Art - " + ((getSecondaryArtwork() == null || getSecondaryArtwork().getName() == null) ? "Secondary Art null" : getSecondaryArtwork().getName()) + " | Voice Commands Size: " + ((getVoiceCommands() == null) ? 0 : getVoiceCommands().size()); } @@ -315,6 +323,9 @@ public class ChoiceCell implements Cloneable{ if (this.voiceCommands != null) { clone.voiceCommands = new ArrayList<>(voiceCommands); } + if (this.uniqueTextId != null) { + clone.uniqueTextId = this.uniqueTextId; + } return clone; } catch (CloneNotSupportedException e) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 8bc55b11b..04f73c8ed 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -74,6 +74,7 @@ import com.smartdevicelink.util.DebugTool; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; @@ -94,6 +95,7 @@ class PreloadPresentChoicesOperation extends Task { private boolean choiceError = false; private HashSet loadedCells; private final ChoiceSet choiceSet; + private static Integer choiceId = 0; private final Integer cancelID; private final InteractionMode presentationMode; private final KeyboardProperties originalKeyboardProperties; @@ -177,7 +179,13 @@ class PreloadPresentChoicesOperation extends Task { } DebugTool.logInfo(TAG, "Choice Operation: Executing preload choices operation"); // Enforce unique cells and remove cells that are already loaded - updateCellsBasedOnLoadedChoices(); + this.cellsToUpload.removeAll(loadedCells); + this.assignIdsToCells(this.cellsToUpload); + makeCellsToUploadUnique(this.cellsToUpload, this.choiceSet, this.loadedCells, this.defaultMainWindowCapability); + + if (this.choiceSet != null) { + updateChoiceSet(this.choiceSet, this.loadedCells, new HashSet<>(this.cellsToUpload)); + } // Start uploading cell artworks, then cells themselves, then determine if we want to present, then update keyboard properties if necessary, then present the choice set, then revert keyboard properties if necessary preloadCellArtworks(new CompletionListener() { @Override @@ -262,7 +270,7 @@ class PreloadPresentChoicesOperation extends Task { private void preloadCells(@NonNull final CompletionListener listener) { this.currentState = SDLPreloadPresentChoicesOperationState.UPLOADING_CHOICES; - List choiceRPCs = new ArrayList<>(cellsToUpload.size()); + final List choiceRPCs = new ArrayList<>(cellsToUpload.size()); for (ChoiceCell cell : cellsToUpload) { CreateInteractionChoiceSet csCell = choiceFromCell(cell); if (csCell != null) { @@ -296,7 +304,11 @@ class PreloadPresentChoicesOperation extends Task { DebugTool.logError(TAG, "There was an error uploading a choice cell: " + response.getInfo() + " resultCode: " + response.getResultCode()); choiceError = true; } else { - loadedCells.add(cellFromChoiceId(correlationId)); + for (CreateInteractionChoiceSet rpc : choiceRPCs) { + if (correlationId == rpc.getCorrelationID()) { + loadedCells.add(cellFromChoiceId(rpc.getChoiceSet().get(0).getChoiceID())); + } + } } } }); @@ -506,15 +518,52 @@ class PreloadPresentChoicesOperation extends Task { return pi; } + private void assignIdsToCells(ArrayList cells) { + for (ChoiceCell cell : cells) { + cell.setChoiceId(this.nextChoiceId()); + } + } + + private void setNextChoiceId(int nextChoiceId) { + choiceId = nextChoiceId; + } + + private int nextChoiceId() { + return ++choiceId; + } + // Choice Uniqueness - void updateCellsBasedOnLoadedChoices() { - if (internalInterface.get().getProtocolVersion().getMajor() >= 7 && internalInterface.get().getProtocolVersion().getMinor() >= 1) { - addUniqueNamesToCells(cellsToUpload); - } else { - ArrayList strippedCellsCopy = (ArrayList) removeUnusedProperties(cellsToUpload); - addUniqueNamesBasedOnStrippedCells(strippedCellsCopy, cellsToUpload); + void makeCellsToUploadUnique(ArrayList cellsToUpload, ChoiceSet choiceSet, HashSet loadedCells, WindowCapability windowCapability) { + if (cellsToUpload.size() == 0) { + return; + } + + ArrayList strippedCellsToUpload = (ArrayList) cellsToUpload.clone(); + ArrayList strippedLoadedCells = (ArrayList) loadedCells.clone(); + boolean supportsChoiceUniqueness = (internalInterface.get().getProtocolVersion().getMajor() >= 7 && internalInterface.get().getProtocolVersion().getMinor() >= 1); + if (supportsChoiceUniqueness) { + removeUnusedProperties(strippedCellsToUpload); + removeUnusedProperties(strippedLoadedCells); + } + + addUniqueNamesToCells(strippedCellsToUpload, strippedLoadedCells, supportsChoiceUniqueness); + transferUniqueNamesFromCells(strippedCellsToUpload, cellsToUpload); + } + + private void updateChoiceSet(ChoiceSet choiceSet, HashSet loadedCells, HashSet cellsToUpload) { + ArrayList choiceSetCells = new ArrayList<>(); + for (ChoiceCell cell : choiceSet.getChoices()) { + if (loadedCells.contains(cell) || cellsToUpload.contains(cell)) { + choiceSetCells.add(cell); + } + } + this.choiceSet.setChoices((List) choiceSetCells.clone()); + } + + private void transferUniqueNamesFromCells(ArrayList fromCells, ArrayList toCells) { + for (int i = 0; i< fromCells.size(); i++) { + toCells.get(i).setUniqueTextId(fromCells.get(i).getUniqueTextId()); } - cellsToUpload.removeAll(loadedCells); } List removeUnusedProperties(List choiceCells) { @@ -562,44 +611,46 @@ class PreloadPresentChoicesOperation extends Task { /** * Checks if 2 or more cells have the same text/title. In case this condition is true, this function will handle the presented issue by adding "(count)". * E.g. Choices param contains 2 cells with text/title "Address" will be handled by updating the uniqueText/uniqueTitle of the second cell to "Address (2)". - * @param choices The list of choiceCells to be uploaded. */ - void addUniqueNamesToCells(List choices) { - HashMap dictCounter = new HashMap<>(); - - for (ChoiceCell cell : choices) { - String cellName = cell.getText(); - Integer counter = dictCounter.get(cellName); - - if (counter != null) { - dictCounter.put(cellName, ++counter); - cell.setUniqueText(cell.getText() + " (" + counter + ")"); + void addUniqueNamesToCells(List cellsToUpload, List loadedCells, boolean supportsChoiceUniqueness) { + HashMap> dictCounter = new HashMap<>(); + + for (ChoiceCell loadedCell : loadedCells) { + Object cellKey = supportsChoiceUniqueness ? loadedCell : loadedCell.getText(); + int cellUniqueId = loadedCell.getUniqueTextId(); + if (dictCounter.get(cellKey) == null) { + ArrayList uniqueIds = new ArrayList<>(); + uniqueIds.add(cellUniqueId); + dictCounter.put(cellKey, uniqueIds); } else { - dictCounter.put(cellName, 1); + dictCounter.get(cellKey).add(cellUniqueId); } } - } - void addUniqueNamesBasedOnStrippedCells(List strippedCells, List unstrippedCells) { - if (strippedCells == null || unstrippedCells == null || strippedCells.size() != unstrippedCells.size()) { - return; - } - // Tracks how many of each cell primary text there are so that we can append numbers to make each unique as necessary - HashMap dictCounter = new HashMap<>(); - for (int i = 0; i < strippedCells.size(); i++) { - ChoiceCell cell = strippedCells.get(i); - Integer counter = dictCounter.get(cell); - if (counter != null) { - counter++; - dictCounter.put(cell, counter); - } else { - dictCounter.put(cell, 1); + for (Object cellKey : dictCounter.keySet()) { + if (dictCounter.get(cellKey) != null) { + Collections.sort(dictCounter.get(cellKey)); } + } - counter = dictCounter.get(cell); + for (ChoiceCell cell : cellsToUpload) { + Object cellKey = supportsChoiceUniqueness ? cell : cell.getText(); + if (dictCounter.get(cellKey) == null) { + ArrayList uniqueTextIds = new ArrayList<>(); + uniqueTextIds.add(cell.getUniqueTextId()); + dictCounter.put(cellKey, uniqueTextIds); + } else { + ArrayList uniqueIds = dictCounter.get(cellKey); + Integer lowestMissingUniqueId = uniqueIds.get(uniqueIds.size() - 1); + for (int i = 1; i < dictCounter.get(cellKey).size() + 1; i ++) { + if (i != dictCounter.get(cellKey).get(i -1)) { + lowestMissingUniqueId = i; + break; + } + } - if (counter > 1) { - unstrippedCells.get(i).setUniqueText(unstrippedCells.get(i).getText() + " (" + counter + ")"); + cell.setUniqueTextId(lowestMissingUniqueId); + dictCounter.get(cellKey).add(cell.getUniqueTextId() - 1, cell.getUniqueTextId()); } } } -- cgit v1.2.1 From 79ddfdab497363b2b9392f75e00b8df043b69b0d Mon Sep 17 00:00:00 2001 From: Henigan Date: Mon, 23 Aug 2021 15:08:47 -0400 Subject: BugFixes --- .../managers/screen/choiceset/BaseChoiceSetManager.java | 2 +- .../screen/choiceset/PreloadPresentChoicesOperation.java | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) 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 50aa40b38..533c0b8e3 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 @@ -241,7 +241,7 @@ abstract class BaseChoiceSetManager extends BaseSubManager { return; } - DeleteChoicesOperation deleteChoicesOperation = new DeleteChoicesOperation(internalInterface, new HashSet<>(preloadedChoices), preloadedChoices, new ChoicesOperationCompletionListener() { + DeleteChoicesOperation deleteChoicesOperation = new DeleteChoicesOperation(internalInterface, new HashSet<>(choices), preloadedChoices, new ChoicesOperationCompletionListener() { @Override public void onComplete(boolean success, HashSet updatedLoadedChoiceCells) { if (!success) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 04f73c8ed..ce277078c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -539,7 +539,7 @@ class PreloadPresentChoicesOperation extends Task { } ArrayList strippedCellsToUpload = (ArrayList) cellsToUpload.clone(); - ArrayList strippedLoadedCells = (ArrayList) loadedCells.clone(); + ArrayList strippedLoadedCells = new ArrayList<>((HashSet) loadedCells.clone()); boolean supportsChoiceUniqueness = (internalInterface.get().getProtocolVersion().getMajor() >= 7 && internalInterface.get().getProtocolVersion().getMinor() >= 1); if (supportsChoiceUniqueness) { removeUnusedProperties(strippedCellsToUpload); @@ -627,12 +627,6 @@ class PreloadPresentChoicesOperation extends Task { } } - for (Object cellKey : dictCounter.keySet()) { - if (dictCounter.get(cellKey) != null) { - Collections.sort(dictCounter.get(cellKey)); - } - } - for (ChoiceCell cell : cellsToUpload) { Object cellKey = supportsChoiceUniqueness ? cell : cell.getText(); if (dictCounter.get(cellKey) == null) { @@ -641,8 +635,8 @@ class PreloadPresentChoicesOperation extends Task { dictCounter.put(cellKey, uniqueTextIds); } else { ArrayList uniqueIds = dictCounter.get(cellKey); - Integer lowestMissingUniqueId = uniqueIds.get(uniqueIds.size() - 1); - for (int i = 1; i < dictCounter.get(cellKey).size() + 1; i ++) { + Integer lowestMissingUniqueId = uniqueIds.get(uniqueIds.size() - 1) + 1; + for (int i = 1; i < dictCounter.get(cellKey).size() + 1; i++) { if (i != dictCounter.get(cellKey).get(i -1)) { lowestMissingUniqueId = i; break; -- cgit v1.2.1 From 272ecbb83e97b1222c17f7fea67cede6d1331512 Mon Sep 17 00:00:00 2001 From: Henigan Date: Mon, 23 Aug 2021 15:18:06 -0400 Subject: Fix method sig with unused params --- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index ce277078c..34612eccd 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -181,7 +181,7 @@ class PreloadPresentChoicesOperation extends Task { // Enforce unique cells and remove cells that are already loaded this.cellsToUpload.removeAll(loadedCells); this.assignIdsToCells(this.cellsToUpload); - makeCellsToUploadUnique(this.cellsToUpload, this.choiceSet, this.loadedCells, this.defaultMainWindowCapability); + makeCellsToUploadUnique(this.cellsToUpload); if (this.choiceSet != null) { updateChoiceSet(this.choiceSet, this.loadedCells, new HashSet<>(this.cellsToUpload)); @@ -533,7 +533,7 @@ class PreloadPresentChoicesOperation extends Task { } // Choice Uniqueness - void makeCellsToUploadUnique(ArrayList cellsToUpload, ChoiceSet choiceSet, HashSet loadedCells, WindowCapability windowCapability) { + void makeCellsToUploadUnique(ArrayList cellsToUpload) { if (cellsToUpload.size() == 0) { return; } -- cgit v1.2.1 From f6ce845c5ed5e3c8ddfec5c141083e522196ccf3 Mon Sep 17 00:00:00 2001 From: Henigan Date: Mon, 23 Aug 2021 15:32:29 -0400 Subject: Update unit tests --- .../managers/screen/choiceset/ChoiceCellTests.java | 21 ++++++++++++++++++++- .../screen/choiceset/ChoiceSetManagerTests.java | 20 -------------------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java index b2c362c53..a761b1f88 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceCellTests.java @@ -61,6 +61,7 @@ public class ChoiceCellTests { choiceCell.setVoiceCommands(TestValues.GENERAL_STRING_LIST); choiceCell.setArtwork(artwork); choiceCell.setSecondaryArtwork(artwork); + choiceCell.setUniqueTextId(TestValues.GENERAL_INT); // use getters and assert equality assertEquals(choiceCell.getText(), TestValues.GENERAL_STRING); @@ -70,7 +71,7 @@ public class ChoiceCellTests { assertEquals(choiceCell.getArtwork(), artwork); assertEquals(choiceCell.getSecondaryArtwork(), artwork); assertEquals(choiceCell.getChoiceId(), MAX_ID); - assertEquals(choiceCell.getUniqueText(), TestValues.GENERAL_STRING); + assertEquals(choiceCell.getUniqueTextId(), TestValues.GENERAL_INTEGER); } @Test @@ -125,4 +126,22 @@ public class ChoiceCellTests { assertFalse(choiceCell.equals(choiceCell3)); } + + @Test + public void testGetUniqueCellText() { + ChoiceCell choiceCell = new ChoiceCell("Test"); + ChoiceCell choiceCell2 = new ChoiceCell("Test"); + choiceCell2.setUniqueTextId(2); + ChoiceCell choiceCell3 = new ChoiceCell("Test"); + choiceCell3.setUniqueTextId(3); + + assertEquals((int) choiceCell.getUniqueTextId(), 1); + assertEquals(choiceCell.getUniqueText(), "Test"); + + assertEquals((int) choiceCell2.getUniqueTextId(), 2); + assertEquals(choiceCell2.getUniqueText(), "Test (2)"); + + assertEquals((int) choiceCell3.getUniqueTextId(), 3); + assertEquals(choiceCell3.getUniqueText(), "Test (3)"); + } } diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java index 6c7d26f6e..7c9f2c792 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java @@ -201,26 +201,6 @@ public class ChoiceSetManagerTests { assertNull(csm.findIfPresent(cell3, cellSet)); } - @Test - public void testUpdateIdsOnChoices() { - - ChoiceCell cell1 = new ChoiceCell("test"); - ChoiceCell cell2 = new ChoiceCell("test2"); - ChoiceCell cell3 = new ChoiceCell("test3"); - LinkedHashSet cellSet = new LinkedHashSet<>(); - cellSet.add(cell1); - cellSet.add(cell2); - cellSet.add(cell3); - // Cells are initially set to MAX_ID - assertEquals(cell1.getChoiceId(), 2000000000); - assertEquals(cell2.getChoiceId(), 2000000000); - assertEquals(cell3.getChoiceId(), 2000000000); - // We are looking for unique IDs - assertNotSame(cell1.getChoiceId(), 2000000000); - assertNotSame(cell2.getChoiceId(), 2000000000); - assertNotSame(cell3.getChoiceId(), 2000000000); - } - @Test public void preloadChoicesAddsToQueue() { ChoiceCell cell1 = new ChoiceCell("test"); -- cgit v1.2.1 From 3a3f43f1f68fbb7006e10b637f89901b6a55b6e6 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 26 Aug 2021 15:41:01 -0400 Subject: Fix some dev review comments --- .../choiceset/PreloadPresentChoicesOperation.java | 28 ++++++++-------------- 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 34612eccd..3483d16d6 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -540,7 +540,7 @@ class PreloadPresentChoicesOperation extends Task { ArrayList strippedCellsToUpload = (ArrayList) cellsToUpload.clone(); ArrayList strippedLoadedCells = new ArrayList<>((HashSet) loadedCells.clone()); - boolean supportsChoiceUniqueness = (internalInterface.get().getProtocolVersion().getMajor() >= 7 && internalInterface.get().getProtocolVersion().getMinor() >= 1); + boolean supportsChoiceUniqueness = (sdlMsgVersion.getMajorVersion() >= 7 && sdlMsgVersion.getMinorVersion() >= 1); if (supportsChoiceUniqueness) { removeUnusedProperties(strippedCellsToUpload); removeUnusedProperties(strippedLoadedCells); @@ -573,16 +573,16 @@ class PreloadPresentChoicesOperation extends Task { // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI cell.setVoiceCommands(null); - if (!hasImageFieldOfName(ImageFieldName.choiceImage)) { + if (!shouldSendChoicePrimaryImage()) { cell.setArtwork(null); } - if (!hasTextFieldOfName(TextFieldName.secondaryText)) { + if (!shouldSendChoiceSecondaryText()) { cell.setSecondaryText(null); } - if (!hasTextFieldOfName(TextFieldName.tertiaryText)) { + if (!shouldSendChoiceTertiaryText()) { cell.setTertiaryText(null); } - if (!hasImageFieldOfName(ImageFieldName.choiceSecondaryImage)) { + if (!shouldSendChoiceSecondaryImage()) { cell.setSecondaryArtwork(null); } } @@ -704,23 +704,23 @@ class PreloadPresentChoicesOperation extends Task { if (this.displayName != null && this.displayName.equals(DisplayType.GEN3_8_INCH.toString())) { return true; } - return templateSupportsTextField(TextFieldName.menuName); + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, TextFieldName.menuName); } boolean shouldSendChoiceSecondaryText() { - return templateSupportsTextField(TextFieldName.secondaryText); + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, TextFieldName.secondaryText); } boolean shouldSendChoiceTertiaryText() { - return templateSupportsTextField(TextFieldName.tertiaryText); + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, TextFieldName.tertiaryText); } boolean shouldSendChoicePrimaryImage() { - return templateSupportsImageField(ImageFieldName.choiceImage); + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.choiceImage); } boolean shouldSendChoiceSecondaryImage() { - return templateSupportsImageField(ImageFieldName.choiceSecondaryImage); + return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.choiceSecondaryImage); } // SDL Notifications @@ -779,14 +779,6 @@ class PreloadPresentChoicesOperation extends Task { } } - boolean templateSupportsTextField(TextFieldName name) { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, name); - } - - boolean templateSupportsImageField(ImageFieldName name) { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, name); - } - public void setLoadedCells(HashSet loadedCells) { this.loadedCells = loadedCells; } -- cgit v1.2.1 From bd8401bbe9a0bf3c70119e8e08f2ffa775139bb8 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 26 Aug 2021 16:35:24 -0400 Subject: Dev review feedback and choiceId fix --- .../screen/choiceset/DeleteChoicesOperation.java | 15 +++++++-------- .../choiceset/PreloadPresentChoicesOperation.java | 19 ++++++++++++++++++- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java index b71ee4f14..11916227c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java @@ -68,6 +68,10 @@ class DeleteChoicesOperation extends Task { DebugTool.logInfo(TAG, "Choice Operation: Executing delete choices operation"); updateCellsToDelete(); if (this.cellsToDelete == null || this.cellsToDelete.isEmpty()) { + if (completionListener != null) { + completionListener.onComplete(true, loadedCells); + DebugTool.logInfo(TAG, "No cells were provided to delete"); + } DeleteChoicesOperation.super.onFinished(); } sendDeletions(); @@ -88,7 +92,7 @@ class DeleteChoicesOperation extends Task { @Override public void onFinished() { if (completionListener != null) { - completionSuccess = true; + completionListener.onComplete(true, loadedCells); } DebugTool.logInfo(TAG, "Successfully deleted choices"); @@ -99,7 +103,7 @@ class DeleteChoicesOperation extends Task { public void onResponse(int correlationId, RPCResponse response) { if (!response.getSuccess()) { if (completionListener != null) { - completionSuccess = false; + completionListener.onComplete(false, loadedCells); } DebugTool.logError(TAG, "Failed to delete choice: " + response.getInfo() + " | Corr ID: " + correlationId); @@ -117,6 +121,7 @@ class DeleteChoicesOperation extends Task { completionListener.onComplete(true, this.loadedCells); } DebugTool.logInfo(TAG, "No Choices to delete, continue"); + DeleteChoicesOperation.super.onFinished(); } } @@ -149,12 +154,6 @@ class DeleteChoicesOperation extends Task { return null; } - @Override - protected void onFinished() { - this.completionListener.onComplete(completionSuccess, this.loadedCells); - super.onFinished(); - } - List createDeleteSets() { List deleteChoices = new ArrayList<>(cellsToDelete.size()); for (ChoiceCell cell : cellsToDelete) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 3483d16d6..cf618c1ec 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -80,6 +80,7 @@ import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; class PreloadPresentChoicesOperation extends Task { @@ -279,7 +280,9 @@ class PreloadPresentChoicesOperation extends Task { } if (choiceRPCs.size() == 0) { - DebugTool.logError(TAG, " All Choice cells to send are null, so the choice set will not be shown"); + if (keyboardListener != null) { + DebugTool.logError(TAG, " All Choice cells to send are null, so the choice set will not be shown"); + } listener.onComplete(true); return; } @@ -529,6 +532,20 @@ class PreloadPresentChoicesOperation extends Task { } private int nextChoiceId() { + boolean maxIds = false; + if (choiceId == 65535) { + choiceId = 0; + maxIds = true; + } + if (maxIds) { + ArrayList usedIds = new ArrayList<>(); + for (ChoiceCell cell : loadedCells) { + usedIds.add(cell.getChoiceId()); + } + while (usedIds.contains(choiceId + 1)) { + ++choiceId; + } + } return ++choiceId; } -- cgit v1.2.1 From b75b6a3ea07dcc286ab2c0ec1da720d4e376cdeb Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 27 Aug 2021 09:19:34 -0400 Subject: Make reachedMaxIds static --- .../screen/choiceset/PreloadPresentChoicesOperation.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index cf618c1ec..a617d212e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -97,6 +97,7 @@ class PreloadPresentChoicesOperation extends Task { private HashSet loadedCells; private final ChoiceSet choiceSet; private static Integer choiceId = 0; + private static Boolean reachedMaxIds = false; private final Integer cancelID; private final InteractionMode presentationMode; private final KeyboardProperties originalKeyboardProperties; @@ -205,11 +206,10 @@ class PreloadPresentChoicesOperation extends Task { } if (choiceSet == null) { - finishOperation(false); + finishOperation(true); return; } DebugTool.logInfo(TAG, "Choice Operation: Executing present choice set operation"); - updateKeyboardProperties(new CompletionListener() { @Override public void onComplete(boolean success) { @@ -532,12 +532,11 @@ class PreloadPresentChoicesOperation extends Task { } private int nextChoiceId() { - boolean maxIds = false; if (choiceId == 65535) { choiceId = 0; - maxIds = true; + reachedMaxIds = true; } - if (maxIds) { + if (reachedMaxIds) { ArrayList usedIds = new ArrayList<>(); for (ChoiceCell cell : loadedCells) { usedIds.add(cell.getChoiceId()); -- cgit v1.2.1 From f9430c1bef62bac6ae64793ce9e41e9720a39e76 Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 27 Aug 2021 14:08:45 -0400 Subject: Change check to use choiceSet --- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index a617d212e..22c463aa3 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -280,7 +280,7 @@ class PreloadPresentChoicesOperation extends Task { } if (choiceRPCs.size() == 0) { - if (keyboardListener != null) { + if (choiceSet == null) { DebugTool.logError(TAG, " All Choice cells to send are null, so the choice set will not be shown"); } listener.onComplete(true); -- cgit v1.2.1 From e027b8670e4cf0563d08531296704266a0865215 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 30 Aug 2021 10:39:01 -0400 Subject: Fix issue in MenuCell.hashCode() sub cells value --- .../java/com/smartdevicelink/managers/screen/menu/MenuCell.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java index e4c8ac47b..eaf91ae76 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuCell.java @@ -450,12 +450,12 @@ public class MenuCell implements Cloneable { public int hashCode() { int result = 1; result += ((getTitle() == null) ? 0 : Integer.rotateLeft(getTitle().hashCode(), 1)); - result += ((getIcon() == null) ? 0 : Integer.rotateLeft(getIcon().hashCode(), 2)); - result += ((getVoiceCommands() == null || getVoiceCommands().isEmpty()) ? 0 : Integer.rotateLeft(getVoiceCommands().hashCode(), 3)); - result += ((getSubCells() == null) ? 0 : Integer.rotateLeft(getSubCells().hashCode(), 4)); + result += ((getIcon() == null || getIcon().getName() == null) ? 0 : Integer.rotateLeft(getIcon().getName().hashCode(), 2)); + result += ((getVoiceCommands() == null) ? 0 : Integer.rotateLeft(getVoiceCommands().hashCode(), 3)); + result += ((getSubCells() == null) ? 0 : Integer.rotateLeft(1, 4)); result += ((getSecondaryText() == null) ? 0 : Integer.rotateLeft(getSecondaryText().hashCode(), 5)); result += ((getTertiaryText() == null) ? 0 : Integer.rotateLeft(getTertiaryText().hashCode(), 6)); - result += ((getSecondaryArtwork() == null) ? 0 : Integer.rotateLeft(getSecondaryArtwork().hashCode(), 7)); + result += ((getSecondaryArtwork() == null || getSecondaryArtwork().getName() == null) ? 0 : Integer.rotateLeft(getSecondaryArtwork().getName().hashCode(), 7)); result += ((getSubMenuLayout() == null) ? 0 : Integer.rotateLeft(getSubMenuLayout().hashCode(), 8)); return result; } -- cgit v1.2.1 From 5a96e3455d545c53bf805359e12bbf7ae21a5b1e Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 30 Aug 2021 11:20:11 -0400 Subject: Move some methods to MenuReplaceUtilities --- .../screen/menu/MenuReplaceUtilitiesTests.java | 6 ++-- .../managers/screen/menu/BaseMenuManager.java | 1 + .../managers/screen/menu/MenuReplaceOperation.java | 40 +++++++--------------- .../managers/screen/menu/MenuReplaceUtilities.java | 33 ++++++++++++++++-- 4 files changed, 47 insertions(+), 33 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java index 85e57fee0..61587dbdc 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilitiesTests.java @@ -51,7 +51,7 @@ import java.util.Arrays; import java.util.List; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.updateIdsOnMenuCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addIdsToMenuCells; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertTrue; @@ -306,7 +306,7 @@ public class MenuReplaceUtilitiesTests { MenuCell menuCell4 = new MenuCell("c4", subMenuLayout, sdlArtwork, new ArrayList<>(Arrays.asList(menuCell4_1, menuCell4_2))); List menuCellList = new ArrayList<>(Arrays.asList(menuCell1, menuCell2, menuCell3, menuCell4)); - updateIdsOnMenuCells(menuCellList, parentIdNotFound); + addIdsToMenuCells(menuCellList, parentIdNotFound); return menuCellList ; } @@ -336,7 +336,7 @@ public class MenuReplaceUtilitiesTests { MenuCell menuCell5 = new MenuCell("c5", subMenuLayout, sdlArtwork, new ArrayList<>(Arrays.asList(menuCell5_1, menuCell5_2))); List newMenuList = new ArrayList<>(Arrays.asList(menuCell5)); - updateIdsOnMenuCells(newMenuList, parentIdNotFound); + addIdsToMenuCells(newMenuList, parentIdNotFound); return newMenuList ; } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 964c999ec..42e4ac439 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -182,6 +182,7 @@ abstract class BaseMenuManager extends BaseSubManager { public void onComplete(boolean success, List currentMenuCells) { BaseMenuManager.this.currentMenuCells = currentMenuCells; updateMenuReplaceOperationsWithNewCurrentMenu(); + DebugTool.logInfo(TAG, "Finished updating menu"); } }); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 51ada9d19..48beae8d1 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -13,7 +13,9 @@ import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.posi import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.removeCellFromList; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.sendRPCs; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.updateIdsOnMenuCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addIdsToMenuCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.transferCellIDsFromCells; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.transferCellListenersFromCells; import com.livio.taskmaster.Task; import com.smartdevicelink.managers.CompletionListener; @@ -85,7 +87,7 @@ class MenuReplaceOperation extends Task { } private void updateMenuCells(final CompletionListener listener) { - updateIdsOnMenuCells(updatedMenu, parentIdNotFound); + addIdsToMenuCells(updatedMenu, parentIdNotFound); // Strip the "current menu" and the new menu of properties that are not displayed on the head unit List updatedStrippedMenu = cellsWithRemovedPropertiesFromCells(updatedMenu, windowCapability); @@ -125,12 +127,12 @@ class MenuReplaceOperation extends Task { final List oldKeeps = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.KEEP); final List newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); - // Since we are creating a new menu but keeping old cells, we must first transfer the old cellIDs to the new menu kept cells. - // This is needed for the onCommands to still work + // Old kept cells ids need to be moved to the new kept cells so that submenu changes have correct parent ids // We will transfer the ids for subCells later - transferCellIDsFromOldCells(oldKeeps, newKeeps); + transferCellIDsFromCells(oldKeeps, newKeeps); - transferListenersFromNewCells(newKeeps, oldKeeps); + // Transfer new cells' listeners to the old cells, which are stored in the current menu + transferCellListenersFromCells(newKeeps, oldKeeps); // Upload the Artworks, then we will start updating the main menu uploadMenuArtworks(new CompletionListener() { @@ -249,12 +251,12 @@ class MenuReplaceOperation extends Task { final List cellsToDelete = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.DELETE); final List cellsToAdd = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.ADD); - final List oldKeeps = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.KEEP); - final List newKeeps = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.KEEP); + final List oldSubcellKeeps = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.KEEP); + final List newSubcellKeeps = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.KEEP); - transferCellIDsFromOldCells(oldKeeps, newKeeps); + transferCellIDsFromCells(oldSubcellKeeps, newSubcellKeeps); - transferListenersFromNewCells(newKeeps, oldKeeps); + transferCellListenersFromCells(newSubcellKeeps, oldSubcellKeeps); sendDeleteCurrentMenu(cellsToDelete, new CompletionListener() { @Override @@ -400,24 +402,6 @@ class MenuReplaceOperation extends Task { return filteredCells; } - private void transferCellIDsFromOldCells(List oldCells, List newCells) { - if (oldCells == null || oldCells.isEmpty()) { - return; - } - for (int i = 0; i < newCells.size(); i++) { - newCells.get(i).setCellId(oldCells.get(i).getCellId()); - } - } - - private void transferListenersFromNewCells(List newCells, List oldCells) { - if (oldCells == null || oldCells.isEmpty()) { - return; - } - for (int i = 0; i < newCells.size(); i++) { - oldCells.get(i).setMenuSelectionListener(newCells.get(i).getMenuSelectionListener()); - } - } - private String convertErrorsMapToString(Map errors) { if (errors == null) { return null; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 1c0546ad0..62ea8dc2e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -40,18 +40,47 @@ class MenuReplaceUtilities { * @param menuCells The array of menu cells to update * @param parentId The parent id to assign if needed */ - static void updateIdsOnMenuCells(List menuCells, int parentId) { + static void addIdsToMenuCells(List menuCells, int parentId) { for (MenuCell cell : menuCells) { cell.setCellId(getNextMenuId()); if (parentId != parentIdNotFound) { cell.setParentCellId(parentId); } if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) { - updateIdsOnMenuCells(cell.getSubCells(), cell.getCellId()); + addIdsToMenuCells(cell.getSubCells(), cell.getCellId()); } } } + static void transferCellIDsFromCells(List fromCells, List toCells) { + if (fromCells == null || toCells == null || fromCells.isEmpty() || fromCells.size() != toCells.size()) { + return; + } + for (int i = 0; i < toCells.size(); i++) { + toCells.get(i).setCellId(fromCells.get(i).getCellId()); + } + + // Update parent ids + for (MenuCell cell : toCells) { + if (!cell.isSubMenuCell()) { + continue; + } + + for (MenuCell subCell : cell.getSubCells()) { + subCell.setParentCellId(cell.getCellId()); + } + } + } + + static void transferCellListenersFromCells(List fromCells, List toCells) { + if (fromCells == null || toCells == null || fromCells.isEmpty() || fromCells.size() != toCells.size()) { + return; + } + for (int i = 0; i < fromCells.size(); i++) { + toCells.get(i).setMenuSelectionListener(fromCells.get(i).getMenuSelectionListener()); + } + } + static List findAllArtworksToBeUploadedFromCells(List cells, FileManager fileManager, WindowCapability windowCapability) { // Make sure we can use images in the menus if (!hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { -- cgit v1.2.1 From 8628aca85f1ef7997fb7fd22e7e62aedc9f13c30 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 30 Aug 2021 11:26:25 -0400 Subject: Fix unit tests --- .../java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java index 1b1a7bd6d..8088538e1 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuCellTests.java @@ -154,8 +154,8 @@ public class MenuCellTests { menuCell1_1.setTitle("new title"); - // Make sure sub cells are compared - assertNotEquals(menuCell1, menuCell2); + // Make sure sub cells are not compared + assertEquals(menuCell1, menuCell2); } @Test -- cgit v1.2.1 From dc0c53fe1dead96816b070e335ee04deb5706cd3 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 30 Aug 2021 12:07:41 -0400 Subject: Rename some methods --- .../managers/screen/menu/MenuReplaceOperation.java | 59 ++++++++++++---------- .../managers/screen/menu/MenuReplaceUtilities.java | 2 +- 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 48beae8d1..db74e2543 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -200,14 +200,14 @@ class MenuReplaceOperation extends Task { * @param listener A CompletionListener called when complete */ private void updateMenuWithCellsToDelete(List deleteCells, final List addCells, final CompletionListener listener) { - sendDeleteCurrentMenu(deleteCells, new CompletionListener() { + sendDeleteMenuCells(deleteCells, new CompletionListener() { @Override public void onComplete(boolean success) { if (getState() == Task.CANCELED) { return; } - sendNewMenuCells(addCells, new CompletionListener() { + sendAddMenuCells(addCells, updatedMenu, new CompletionListener() { @Override public void onComplete(boolean success) { if (!success) { @@ -226,39 +226,39 @@ class MenuReplaceOperation extends Task { * * @param oldKeptCells The old kept cells * @param newKeptCells The new kept cells - * @param startIndex The index of the main menu to use - * @param listener A CompletionListener called when complete + * @param index The index of the main menu to use + * @param listener The listener to call when all submenu updates are complete */ - private void updateSubMenuWithOldKeptCells(final List oldKeptCells, final List newKeptCells, final int startIndex, final CompletionListener listener) { - if (oldKeptCells.isEmpty() || startIndex >= oldKeptCells.size()) { + private void updateSubMenuWithOldKeptCells(final List oldKeptCells, final List newKeptCells, final int index, final CompletionListener listener) { + if (oldKeptCells.isEmpty() || index >= oldKeptCells.size()) { listener.onComplete(true); return; } - if (oldKeptCells.get(startIndex) != null && oldKeptCells.get(startIndex).isSubMenuCell() && !oldKeptCells.get(startIndex).getSubCells().isEmpty()) { - DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldKeptCells.get(startIndex).getSubCells(), newKeptCells.get(startIndex).getSubCells()); + if (oldKeptCells.get(index) != null && oldKeptCells.get(index).isSubMenuCell() && !oldKeptCells.get(index).getSubCells().isEmpty()) { + DynamicMenuUpdateRunScore tempScore = DynamicMenuUpdateAlgorithm.dynamicRunScoreOldMenuCells(oldKeptCells.get(index).getSubCells(), newKeptCells.get(index).getSubCells()); // If both old and new menu cells are empty. Then nothing needs to be done. if (tempScore.isEmpty()) { // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements - updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, index + 1, listener); return; } List deleteMenuStatus = tempScore.getOldStatus(); List addMenuStatus = tempScore.getUpdatedStatus(); - final List cellsToDelete = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.DELETE); - final List cellsToAdd = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.ADD); + final List cellsToDelete = filterMenuCellsWithStatusList(oldKeptCells.get(index).getSubCells(), deleteMenuStatus, MenuCellState.DELETE); + final List cellsToAdd = filterMenuCellsWithStatusList(newKeptCells.get(index).getSubCells(), addMenuStatus, MenuCellState.ADD); - final List oldSubcellKeeps = filterMenuCellsWithStatusList(oldKeptCells.get(startIndex).getSubCells(), deleteMenuStatus, MenuCellState.KEEP); - final List newSubcellKeeps = filterMenuCellsWithStatusList(newKeptCells.get(startIndex).getSubCells(), addMenuStatus, MenuCellState.KEEP); + final List oldSubcellKeeps = filterMenuCellsWithStatusList(oldKeptCells.get(index).getSubCells(), deleteMenuStatus, MenuCellState.KEEP); + final List newSubcellKeeps = filterMenuCellsWithStatusList(newKeptCells.get(index).getSubCells(), addMenuStatus, MenuCellState.KEEP); - transferCellIDsFromCells(oldSubcellKeeps, newSubcellKeeps); + //transferCellIDsFromCells(oldSubcellKeeps, newSubcellKeeps); @todo transferCellListenersFromCells(newSubcellKeeps, oldSubcellKeeps); - sendDeleteCurrentMenu(cellsToDelete, new CompletionListener() { + sendDeleteMenuCells(cellsToDelete, new CompletionListener() { @Override public void onComplete(boolean success) { if (getState() == Task.CANCELED) { @@ -269,7 +269,7 @@ class MenuReplaceOperation extends Task { listener.onComplete(false); } - sendNewMenuCells(cellsToAdd, new CompletionListener() { + sendAddMenuCells(cellsToAdd, newKeptCells.get(index).getSubCells(), new CompletionListener() { @Override public void onComplete(boolean success) { if (getState() == Task.CANCELED) { @@ -281,14 +281,14 @@ class MenuReplaceOperation extends Task { } // After the first set of submenu cells were added and deleted we must find the next set of sub cells until we loop through all the elements - updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, index + 1, listener); } }); } }); } else { // There are no sub cells, we can skip to the next index. - updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, startIndex + 1, listener); + updateSubMenuWithOldKeptCells(oldKeptCells, newKeptCells, index + 1, listener); } } @@ -298,7 +298,7 @@ class MenuReplaceOperation extends Task { * @param deleteMenuCells The menu cells to be deleted * @param listener A CompletionListener called when the RPCs are finished with an error if any failed */ - private void sendDeleteCurrentMenu(List deleteMenuCells, final CompletionListener listener) { + private void sendDeleteMenuCells(List deleteMenuCells, final CompletionListener listener) { if (deleteMenuCells == null || deleteMenuCells.isEmpty()) { listener.onComplete(true); return; @@ -330,11 +330,12 @@ class MenuReplaceOperation extends Task { /** * Send Add RPCs for given new menu cells compared to old menu cells * - * @param newMenuCells The new menu cells we want displayed + * @param addMenuCells The new menu cells we want displayed + * @param fullMenu The full menu from which the addMenuCells come. This allows us to grab the positions from that menu for the new cells * @param listener A CompletionListener called when the RPCs are finished with an error if any failed */ - private void sendNewMenuCells(final List newMenuCells, final CompletionListener listener) { - if (newMenuCells == null || newMenuCells.isEmpty()) { + private void sendAddMenuCells(final List addMenuCells, final List fullMenu, final CompletionListener listener) { + if (addMenuCells == null || addMenuCells.isEmpty()) { DebugTool.logInfo(TAG, "There are no cells to update."); listener.onComplete(true); return; @@ -343,10 +344,10 @@ class MenuReplaceOperation extends Task { MenuLayout defaultSubmenuLayout = menuConfiguration != null ? menuConfiguration.getSubMenuLayout() : null; // RPCs for cells on the main menu level. They could be AddCommands or AddSubMenus depending on whether the cell has child cells or not. - final List mainMenuCommands = mainMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, updatedMenu, defaultSubmenuLayout); + final List mainMenuCommands = mainMenuCommandsForCells(addMenuCells, fileManager.get(), fullMenu, windowCapability, defaultSubmenuLayout); // RPCs for cells on the second menu level (one level deep). They could be AddCommands or AddSubMenus. - final List subMenuCommands = subMenuCommandsForCells(newMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); + final List subMenuCommands = subMenuCommandsForCells(addMenuCells, fileManager.get(), windowCapability, defaultSubmenuLayout); sendRPCs(mainMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { @Override @@ -355,6 +356,10 @@ class MenuReplaceOperation extends Task { DebugTool.logError(TAG, "Failed to send main menu commands. " + convertErrorsMapToString(errors)); listener.onComplete(false); return; + } else if (subMenuCommands.isEmpty()) { + DebugTool.logInfo(TAG, "Finished sending new cells"); + listener.onComplete(true); + return; } sendRPCs(subMenuCommands, internalInterface.get(), new SendingRPCsCompletionListener() { @@ -363,7 +368,7 @@ class MenuReplaceOperation extends Task { if (!success) { DebugTool.logError(TAG, "Failed to send sub menu commands. " + convertErrorsMapToString(errors)); } else { - DebugTool.logInfo(TAG, "Finished updating menu"); + DebugTool.logInfo(TAG, "Finished sending new cells"); } listener.onComplete(success); } @@ -374,7 +379,7 @@ class MenuReplaceOperation extends Task { // Find the id of the successful request and add it from the current menu list wherever it needs to be int commandId = commandIdForRPCRequest(request); int position = positionForRPCRequest(request); - addCellWithCellId(commandId, position, newMenuCells, currentMenu); + addCellWithCellId(commandId, position, addMenuCells, currentMenu); } } }); @@ -386,7 +391,7 @@ class MenuReplaceOperation extends Task { // Find the id of the successful request and add it from the current menu list wherever it needs to be int commandId = commandIdForRPCRequest(request); int position = positionForRPCRequest(request); - addCellWithCellId(commandId, position, newMenuCells, currentMenu); + addCellWithCellId(commandId, position, addMenuCells, currentMenu); } } }); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 62ea8dc2e..a8b642cc9 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -155,7 +155,7 @@ class MenuReplaceUtilities { return deletes; } - static List mainMenuCommandsForCells(List cells, FileManager fileManager, WindowCapability windowCapability, List menu, MenuLayout defaultSubmenuLayout) { + static List mainMenuCommandsForCells(List cells, FileManager fileManager, List menu, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) { List commands = new ArrayList<>(); // We need the index to use it as position so we will use this type of loop -- cgit v1.2.1 From 3502c2230856edb77e3499321abaca9f0a925224 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 30 Aug 2021 12:27:14 -0400 Subject: Call transferCellIDsFromCells for subcells --- .../com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index db74e2543..fdb20d586 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -254,7 +254,7 @@ class MenuReplaceOperation extends Task { final List oldSubcellKeeps = filterMenuCellsWithStatusList(oldKeptCells.get(index).getSubCells(), deleteMenuStatus, MenuCellState.KEEP); final List newSubcellKeeps = filterMenuCellsWithStatusList(newKeptCells.get(index).getSubCells(), addMenuStatus, MenuCellState.KEEP); - //transferCellIDsFromCells(oldSubcellKeeps, newSubcellKeeps); @todo + transferCellIDsFromCells(oldSubcellKeeps, newSubcellKeeps); transferCellListenersFromCells(newSubcellKeeps, oldSubcellKeeps); -- cgit v1.2.1 From 85d5512073474c62b719e0ea4880496442a77c93 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 30 Aug 2021 16:24:14 -0400 Subject: Remove extra whitespaces --- .../smartdevicelink/managers/screen/menu/MenuReplaceOperation.java | 2 +- .../smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java | 4 +++- .../com/smartdevicelink/managers/screen/menu/MenuShowOperation.java | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index fdb20d586..0e4f288f1 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -4,6 +4,7 @@ import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtilit import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; import static com.smartdevicelink.managers.screen.menu.BaseMenuManager.parentIdNotFound; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addCellWithCellId; +import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addIdsToMenuCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.cloneMenuCellsList; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.commandIdForRPCRequest; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.deleteCommandsForCells; @@ -13,7 +14,6 @@ import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.posi import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.removeCellFromList; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.sendRPCs; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.subMenuCommandsForCells; -import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.addIdsToMenuCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.transferCellIDsFromCells; import static com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities.transferCellListenersFromCells; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index a8b642cc9..15ae7231b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -31,14 +31,16 @@ import java.util.Map; */ class MenuReplaceUtilities { private static int menuId = 0; + static int getNextMenuId() { return ++menuId; } /** * Assign cell ids on an array of menu cells given a parent id (or no parent id) + * * @param menuCells The array of menu cells to update - * @param parentId The parent id to assign if needed + * @param parentId The parent id to assign if needed */ static void addIdsToMenuCells(List menuCells, int parentId) { for (MenuCell cell : menuCells) { diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java index b5172cf99..a685ca966 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuShowOperation.java @@ -78,7 +78,7 @@ class MenuShowOperation extends Task { if (response.getSuccess()) { DebugTool.logInfo(TAG, "Successfully opened application menu"); } else { - DebugTool.logError(TAG, "Open Menu Request Failed. Result code: " + response.getResultCode() + ". Info: "+ response.getInfo()); + DebugTool.logError(TAG, "Open Menu Request Failed. Result code: " + response.getResultCode() + ". Info: " + response.getInfo()); } onFinished(); } -- cgit v1.2.1 From 05b6219e3a616677a7e11fd092ca46f9b8736b4e Mon Sep 17 00:00:00 2001 From: Kostiantyn Boskin Date: Tue, 31 Aug 2021 16:49:24 +0300 Subject: Feature/0293 oem android specific (#1604) * [0293] - Implement Android specific part * [0293] - Fix tag name * [0293] - Fix hardware version * [0293] - Fix supported_vehicle_type.xml * [0293] - Applied logs * [0293] - Remove the check * [0293] - Remove the check * [0293] - Remove the check * [0293] - Remove the check * [0293] - Handle NPE * [0293] - Add mocks * [0293] - Update the deserialization function * [0293] - Update the deserialization function, fix shared preferences * [0293] - Update the logic * [0293] - Update parsing method * [0293] - Migrate to separate class * [0293] - Fix tests * [0293] - Fix tests * [0293] - Fix tests * [0293] - Fix comments * [0293] - Rename variables * [0293] - Apply latest patch * [0293] - Fix comments * Rollbacking the patch This reverts commit 23bc6d5cdb06df88fea3ff7d747c1a51fdd6a146. * Revert "[0293]" This reverts commit a77b24ceedcfe9dd5598554a232d4e815e14f334. * [0293] - Update according to the latest revision * [0293] - Remove javadoc * [0293] - Align variables with Livio proposal * Apply suggestions from code review [0293] - Fix pr comments Co-authored-by: Julian Kast * [0296] - Fix pr comments * [0296] - Fix pr comments * [0293] - Fix pr comments * [0293] - Fix PR comments * [0293] - Fix javaSE project compilation * Apply suggestions from code review Co-authored-by: Julian Kast * [0293] - Fix pr comments * [0293] - Fix protocol version * [0293] - Fix protocol version in header * Increment ROUTER_SERVICE_VERSION_NUMBER * fixup! Increment ROUTER_SERVICE_VERSION_NUMBER * Fix crash in broadcast receiver Added missing device null check * Add missing vehicle type check * Update onSystemInfoReceived logic * Update old router service check * Fix foreground service icon for unsupported app * Add vehicle type check to SDL receiver Also, common functions were moved out to AndroidTools class * Include vehicle type into the ping intent In this case corresponding SDL receiver will be able to check if vehicle type is supported by specific OEM app or not * Fix extracting metadata from manifest * Fix initial protocol version for StartService SDL core will respond with negotiated version: Core with V1-4 will respond with max own supported version and won't include VI into ack bson parameters Core with V5+ will respond with negotiated protocol version and include VI into ack bson parameters if core supports it * Replace broadcast receiver with callback * fix notifications on disconnect * fix connection after disconnect * replace ISessionListener * Revert changes in the SdlManagerListener * Remove changes in the 'HelloSdlApp' * Fix logic in the deserialization vehicle method * Update after comment discussion * Update after comment discussion * Revert changes * Remove ISessionListener and lifecycle onError call before onClose * Update unit test and SdlService * Fix shutting down the RPC service * Add a version check * Add some Java Docs and apply suggested changes * Fix saving vehicle type * Fix Java Docs * Fix Java Docs * Fix some potential NPE * Refactor some names * Refactor to more generic approach * Remove vehicleType param from callback * Add return statement * Code Review Fixes * Code Review Fixes * Fix some potential NPE * Fix checkIfVehicleSupported Co-authored-by: kboskin Co-authored-by: Julian Kast Co-authored-by: Andrii Kalinich Co-authored-by: Vadym Luchko (gitHub) Co-authored-by: Iryna Lytvynenko (GitHub) Co-authored-by: Yaroslav Lutsenko (GitHub) Co-authored-by: Yaroslav Lutsenko (GitHub) <86004378+YaroslavLutsenko@users.noreply.github.com> --- .../test/rpc/datatypes/VehicleTypeTest.java | 14 ++ .../smartdevicelink/test/util/SdlAppInfoTests.java | 119 ++++++++++++- .../test/utl/AndroidToolsTests.java | 37 ++++ .../androidTest/res/xml/supported_vehicle_type.xml | 12 ++ .../managers/lifecycle/LifecycleManager.java | 27 +++ .../transport/SdlBroadcastReceiver.java | 47 ++++- .../transport/SdlRouterService.java | 90 +++++++--- .../transport/USBAccessoryAttachmentActivity.java | 2 +- .../transport/utl/SdlDeviceListener.java | 103 ++++++++++- .../com/smartdevicelink/util/AndroidTools.java | 169 +++++++++++++++++- .../smartdevicelink/util/IntegrationValidator.java | 2 +- .../java/com/smartdevicelink/util/SdlAppInfo.java | 193 ++++++++++++++++++++- android/sdl_android/src/main/res/values/sdl.xml | 3 +- .../managers/lifecycle/BaseLifecycleManager.java | 9 + .../smartdevicelink/protocol/SdlProtocolBase.java | 12 ++ .../com/smartdevicelink/proxy/rpc/VehicleType.java | 13 ++ .../smartdevicelink/session/BaseSdlSession.java | 17 ++ .../transport/TransportConstants.java | 1 + 18 files changed, 815 insertions(+), 55 deletions(-) create mode 100644 android/sdl_android/src/androidTest/res/xml/supported_vehicle_type.xml diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VehicleTypeTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VehicleTypeTest.java index a77de7473..9bb432e51 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VehicleTypeTest.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/rpc/datatypes/VehicleTypeTest.java @@ -8,7 +8,10 @@ import junit.framework.TestCase; import org.json.JSONException; import org.json.JSONObject; +import org.junit.Assert; +import java.util.HashMap; +import java.util.Hashtable; import java.util.Iterator; public class VehicleTypeTest extends TestCase { @@ -72,4 +75,15 @@ public class VehicleTypeTest extends TestCase { fail(TestValues.JSON_FAIL); } } + + public void testHashMapConstructor() { + Hashtable store = msg.getStore(); + HashMap hashMap = new HashMap(store); + VehicleType type = new VehicleType(hashMap); + + Assert.assertEquals(type.getMake(), msg.getMake()); + Assert.assertEquals(type.getModel(), msg.getModel()); + Assert.assertEquals(type.getModelYear(), msg.getModelYear()); + Assert.assertEquals(type.getTrim(), msg.getTrim()); + } } \ No newline at end of file diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/util/SdlAppInfoTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/util/SdlAppInfoTests.java index 7909d4997..1dfa0700b 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/util/SdlAppInfoTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/util/SdlAppInfoTests.java @@ -42,6 +42,8 @@ import android.os.Bundle; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.smartdevicelink.R; +import com.smartdevicelink.proxy.rpc.VehicleType; +import com.smartdevicelink.test.TestValues; import com.smartdevicelink.util.SdlAppInfo; import org.junit.Before; @@ -49,10 +51,12 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.List; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertNotNull; @@ -85,7 +89,7 @@ public class SdlAppInfoTests { @Test public void testConstructorWithDefaultData() { - SdlAppInfo info = new SdlAppInfo(defaultResolveInfo, defaultPackageInfo); + SdlAppInfo info = new SdlAppInfo(defaultResolveInfo, defaultPackageInfo, context); assertNotNull(info); @@ -105,10 +109,10 @@ public class SdlAppInfoTests { */ @Test public void testCompareVersion() { - SdlAppInfo defaultInfo = new SdlAppInfo(defaultResolveInfo, defaultPackageInfo); + SdlAppInfo defaultInfo = new SdlAppInfo(defaultResolveInfo, defaultPackageInfo, context); int newVersion = context.getResources().getInteger(R.integer.sdl_router_service_version_value) + 1; - SdlAppInfo testInfo = new SdlAppInfo(createResolveInfo(newVersion, "com.smartdevicelink.test2", "com.smartdevicelink.test2.SdlRouterService", false), defaultPackageInfo); + SdlAppInfo testInfo = new SdlAppInfo(createResolveInfo(newVersion, "com.smartdevicelink.test2", "com.smartdevicelink.test2.SdlRouterService", false), defaultPackageInfo, context); List infos = new ArrayList<>(); infos.add(defaultInfo); @@ -126,10 +130,10 @@ public class SdlAppInfoTests { */ @Test public void testCompareVersionAndCustom() { - SdlAppInfo defaultInfo = new SdlAppInfo(defaultResolveInfo, defaultPackageInfo); + SdlAppInfo defaultInfo = new SdlAppInfo(defaultResolveInfo, defaultPackageInfo, context); int newVersion = context.getResources().getInteger(R.integer.sdl_router_service_version_value) + 1; - SdlAppInfo testInfo = new SdlAppInfo(createResolveInfo(newVersion, "com.smartdevicelink.test2", "com.smartdevicelink.test2.SdlRouterService", true), defaultPackageInfo); + SdlAppInfo testInfo = new SdlAppInfo(createResolveInfo(newVersion, "com.smartdevicelink.test2", "com.smartdevicelink.test2.SdlRouterService", true), defaultPackageInfo, context); List infos = new ArrayList<>(); infos.add(defaultInfo); @@ -147,12 +151,12 @@ public class SdlAppInfoTests { */ @Test public void testCompareUpdatedTime() { - SdlAppInfo defaultInfo = new SdlAppInfo(defaultResolveInfo, defaultPackageInfo); + SdlAppInfo defaultInfo = new SdlAppInfo(defaultResolveInfo, defaultPackageInfo, context); PackageInfo packageInfo = new PackageInfo(); packageInfo.firstInstallTime = defaultPackageInfo.firstInstallTime; packageInfo.lastUpdateTime = defaultPackageInfo.lastUpdateTime + 500; - SdlAppInfo testInfo = new SdlAppInfo(defaultResolveInfo, packageInfo); + SdlAppInfo testInfo = new SdlAppInfo(defaultResolveInfo, packageInfo, context); List infos = new ArrayList<>(); infos.add(defaultInfo); @@ -179,5 +183,106 @@ public class SdlAppInfoTests { return info; } + @Test + public void testDeserializeVehicleInfo() { + VehicleType type = new VehicleType(); + type.setMake("SDL"); + type.setModel("Car"); + type.setModelYear("2019"); + type.setTrim("GT"); + List deserializedList = SdlAppInfo.deserializeSupportedVehicles(getInstrumentation().getContext().getResources().getXml(com.smartdevicelink.test.R.xml.supported_vehicle_type)); + assertTrue(deserializedList.contains(type)); + assertEquals(1, deserializedList.size()); + } + + @Test + public void testVehicleTypeSupported() { + // tests check with all params + VehicleType type1 = new VehicleType(); + type1.setMake(TestValues.GENERAL_STRING); + type1.setModel(TestValues.GENERAL_STRING); + type1.setMake(TestValues.GENERAL_STRING); + type1.setTrim(TestValues.GENERAL_STRING); + + VehicleType type2 = new VehicleType(); + type2.setMake(TestValues.GENERAL_STRING); + type2.setModel(TestValues.GENERAL_STRING); + type2.setModelYear(TestValues.GENERAL_INTEGER.toString()); + type2.setTrim(TestValues.GENERAL_STRING); + + List supportedVehicleList = Arrays.asList(type1, type2); + assertTrue(SdlAppInfo.checkIfVehicleSupported(supportedVehicleList, type2)); + + // tests check with not all params in connectedVehicle + VehicleType connectedVehicle = new VehicleType(); + + // make only param + connectedVehicle.setMake(TestValues.GENERAL_STRING); + assertTrue(SdlAppInfo.checkIfVehicleSupported(supportedVehicleList, connectedVehicle)); + + // make and model params + connectedVehicle.setModel(TestValues.GENERAL_STRING); + assertTrue(SdlAppInfo.checkIfVehicleSupported(supportedVehicleList, connectedVehicle)); + + // make, model and year params + connectedVehicle.setModelYear(TestValues.GENERAL_STRING); + assertTrue(SdlAppInfo.checkIfVehicleSupported(supportedVehicleList, connectedVehicle)); + + // make, model and trim params + connectedVehicle.setModelYear(null); + connectedVehicle.setTrim(TestValues.GENERAL_STRING); + assertTrue(SdlAppInfo.checkIfVehicleSupported(supportedVehicleList, connectedVehicle)); + + // tests check with not all params in supportedVehicle + VehicleType supportedVehicle = new VehicleType(); + supportedVehicle.setMake(TestValues.GENERAL_STRING); + + // make param only + assertTrue(SdlAppInfo.checkIfVehicleSupported(Collections.singletonList(supportedVehicle), connectedVehicle)); + + // make and model params + supportedVehicle.setModel(TestValues.GENERAL_STRING); + assertTrue(SdlAppInfo.checkIfVehicleSupported(Collections.singletonList(supportedVehicle), connectedVehicle)); + + // make, model and trim params + supportedVehicle.setTrim(TestValues.GENERAL_STRING); + assertTrue(SdlAppInfo.checkIfVehicleSupported(Collections.singletonList(supportedVehicle), connectedVehicle)); + + // make, model and trim params + supportedVehicle.setTrim(TestValues.GENERAL_STRING); + assertTrue(SdlAppInfo.checkIfVehicleSupported(Collections.singletonList(supportedVehicle), connectedVehicle)); + + // make, model and trim params + connectedVehicle.setTrim(null); + connectedVehicle.setModelYear(TestValues.GENERAL_INTEGER.toString()); + supportedVehicle.setModelYear(TestValues.GENERAL_INTEGER.toString()); + assertTrue(SdlAppInfo.checkIfVehicleSupported(Collections.singletonList(supportedVehicle), connectedVehicle)); + } + + @Test + public void testVehicleTypeNotSupported() { + VehicleType type1 = new VehicleType(); + + type1.setModel(TestValues.GENERAL_STRING); + type1.setMake(TestValues.GENERAL_INTEGER.toString()); + type1.setTrim(TestValues.GENERAL_STRING); + type1.setModelYear(TestValues.GENERAL_STRING); + + VehicleType type2 = new VehicleType(); + + type2.setModel(TestValues.GENERAL_STRING); + type2.setMake(TestValues.GENERAL_INTEGER.toString()); + type2.setTrim(TestValues.GENERAL_STRING); + type2.setModelYear(TestValues.GENERAL_STRING); + + VehicleType type3 = new VehicleType(); + + type3.setModel(TestValues.GENERAL_STRING); + type3.setMake(TestValues.GENERAL_STRING); + type3.setTrim(TestValues.GENERAL_STRING); + type3.setModelYear(TestValues.GENERAL_INTEGER.toString()); + + assertFalse(SdlAppInfo.checkIfVehicleSupported(Arrays.asList(type1, type2), type3)); + } } diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/utl/AndroidToolsTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/utl/AndroidToolsTests.java index 1ad01a1c3..c4104b585 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/utl/AndroidToolsTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/utl/AndroidToolsTests.java @@ -1,17 +1,26 @@ package com.smartdevicelink.test.utl; import android.content.ComponentName; +import android.content.Context; +import android.content.SharedPreferences; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.smartdevicelink.proxy.rpc.VehicleType; import com.smartdevicelink.util.AndroidTools; import junit.framework.Assert; +import org.json.JSONException; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mockito; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; @RunWith(AndroidJUnit4.class) public class AndroidToolsTests { @@ -39,4 +48,32 @@ public class AndroidToolsTests { } + @Test + public void testVehicleTypeSave() throws JSONException { + Context mMockContext = mock(Context.class); + VehicleType mMockVehicleType = new VehicleType(); + String mAddress = "1234"; + + mMockVehicleType.setMake("Ford"); + mMockVehicleType.setTrim("GT"); + mMockVehicleType.setModel("Mustang"); + mMockVehicleType.setModelYear("2019"); + + SharedPreferences.Editor editor = mock(SharedPreferences.Editor.class); + when(editor.commit()).thenReturn(true); + + SharedPreferences sharedPrefs = Mockito.mock(SharedPreferences.class); + when(mMockContext.getSharedPreferences(anyString(), anyInt())).thenReturn(sharedPrefs); + when(sharedPrefs.edit()).thenReturn(editor); + when(sharedPrefs.getString(mAddress, null)).thenReturn(mMockVehicleType.serializeJSON().toString()); + + AndroidTools.saveVehicleType(mMockContext, mMockVehicleType, mAddress); + VehicleType type = new VehicleType(AndroidTools.getVehicleTypeFromPrefs(mMockContext, mAddress)); + + org.junit.Assert.assertEquals(type.getMake(), mMockVehicleType.getMake()); + org.junit.Assert.assertEquals(type.getModel(), mMockVehicleType.getModel()); + org.junit.Assert.assertEquals(type.getModelYear(), mMockVehicleType.getModelYear()); + org.junit.Assert.assertEquals(type.getTrim(), mMockVehicleType.getTrim()); + } + } diff --git a/android/sdl_android/src/androidTest/res/xml/supported_vehicle_type.xml b/android/sdl_android/src/androidTest/res/xml/supported_vehicle_type.xml new file mode 100644 index 000000000..36e2fd169 --- /dev/null +++ b/android/sdl_android/src/androidTest/res/xml/supported_vehicle_type.xml @@ -0,0 +1,12 @@ + + + + + + \ No newline at end of file diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java index 1b54b0e1f..6d4fddb3d 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lifecycle/LifecycleManager.java @@ -40,6 +40,7 @@ import com.smartdevicelink.exception.SdlException; import com.smartdevicelink.exception.SdlExceptionCause; import com.smartdevicelink.protocol.ISdlServiceListener; import com.smartdevicelink.protocol.enums.SessionType; +import com.smartdevicelink.proxy.rpc.VehicleType; import com.smartdevicelink.proxy.rpc.enums.SdlDisconnectedReason; import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType; import com.smartdevicelink.security.SdlSecurityBase; @@ -49,9 +50,12 @@ import com.smartdevicelink.transport.BaseTransportConfig; import com.smartdevicelink.transport.MultiplexTransportConfig; import com.smartdevicelink.transport.TCPTransportConfig; import com.smartdevicelink.transport.enums.TransportType; +import com.smartdevicelink.transport.utl.TransportRecord; +import com.smartdevicelink.util.AndroidTools; import com.smartdevicelink.util.DebugTool; import java.lang.ref.WeakReference; +import java.util.List; /** * The lifecycle manager creates a central point for all SDL session logic to converge. It should only be used by @@ -98,6 +102,29 @@ public class LifecycleManager extends BaseLifecycleManager { } } + @Override + void saveVehicleType(String address, VehicleType type) { + AndroidTools.saveVehicleType(contextWeakReference.get(), type, address); + } + + @Override + void saveVehicleType(List activeTransports, VehicleType type) { + if (activeTransports == null || activeTransports.isEmpty() || type == null) { + DebugTool.logWarning(TAG, "Unable to save vehicle type"); + return; + } + + for (TransportRecord record: activeTransports) { + if (record.getType() == TransportType.BLUETOOTH) { + String address = record.getAddress(); + if (address != null && !address.isEmpty()) { + saveVehicleType(address, type); + } + break; + } + } + } + @RestrictTo(RestrictTo.Scope.LIBRARY) public void setContext(Context context) { this.contextWeakReference = new WeakReference<>(context); diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java index c86cb6d94..588b905cc 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlBroadcastReceiver.java @@ -49,6 +49,7 @@ import android.util.AndroidRuntimeException; import androidx.annotation.CallSuper; +import com.smartdevicelink.proxy.rpc.VehicleType; import com.smartdevicelink.transport.RouterServiceValidator.TrustedListCallback; import com.smartdevicelink.transport.enums.TransportType; import com.smartdevicelink.transport.utl.SdlDeviceListener; @@ -58,6 +59,8 @@ import com.smartdevicelink.util.IntegrationValidator; import com.smartdevicelink.util.SdlAppInfo; import com.smartdevicelink.util.ServiceFinder; +import java.util.HashMap; +import java.util.Hashtable; import java.util.List; import java.util.Locale; import java.util.Vector; @@ -145,6 +148,15 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { DebugTool.logError(TAG, "You cannot use the default SdlRouterService class, it must be extended in your project. THIS WILL THROW AN EXCEPTION IN FUTURE RELEASES!!"); } + HashMap vehicleInfoStore = (HashMap) intent.getSerializableExtra(TransportConstants.VEHICLE_INFO_EXTRA); + + VehicleType vehicleType; + if (vehicleInfoStore == null || vehicleInfoStore.isEmpty()) { + vehicleType = null; + } else { + vehicleType = new VehicleType(vehicleInfoStore); + } + //This will only be true if we are being told to reopen our SDL service because SDL is enabled if (action.equalsIgnoreCase(TransportConstants.START_ROUTER_SERVICE_ACTION)) { if (intent.hasExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_EXTRA)) { @@ -178,7 +190,7 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { } else if (intent.getBooleanExtra(TransportConstants.PING_ROUTER_SERVICE_EXTRA, false)) { //We were told to wake up our router services boolean altServiceWake = intent.getBooleanExtra(TransportConstants.BIND_REQUEST_TYPE_ALT_TRANSPORT, false); - didStart = wakeUpRouterService(context, false, altServiceWake, device); + didStart = wakeUpRouterService(context, false, altServiceWake, device, vehicleType); } } @@ -189,7 +201,7 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { if (!didStart) { DebugTool.logInfo(TAG, "attempting to wake up router service"); - didStart = wakeUpRouterService(context, true, false, device); + didStart = wakeUpRouterService(context, true, false, device, vehicleType); } //So even though we started our own version, on some older phones we find that two services are started up so we want to make sure we send our version that we are working with @@ -211,8 +223,10 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { * @param componentName the router service that should be started * @param altTransportWake if the alt transport flag should be set. Only used in debug * @param device the connected bluetooth device + * @param confirmedDevice if the device is confirmed + * @param vehicleType vehicle params retrieved from connected device */ - private static void startRouterService(Context context, ComponentName componentName, boolean altTransportWake, BluetoothDevice device, boolean confirmedDevice) { + private static void startRouterService(Context context, ComponentName componentName, boolean altTransportWake, BluetoothDevice device, boolean confirmedDevice, VehicleType vehicleType) { if (componentName == null) { return; } @@ -232,6 +246,17 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { serviceIntent.putExtra(TransportConstants.CONFIRMED_SDL_DEVICE, confirmedDevice); } + if (vehicleType == null) { + final String address = device != null ? device.getAddress() : null; + Hashtable store = AndroidTools.getVehicleTypeFromPrefs(context, address); + if (store != null) { + vehicleType = new VehicleType(store); + } + } + if (vehicleType != null) { + serviceIntent.putExtra(TransportConstants.VEHICLE_INFO_EXTRA, vehicleType.getStore()); + } + try { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { context.startService(serviceIntent); @@ -254,7 +279,7 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { } } - private boolean wakeUpRouterService(final Context context, final boolean ping, final boolean altTransportWake, final BluetoothDevice device) { + private boolean wakeUpRouterService(final Context context, final boolean ping, final boolean altTransportWake, final BluetoothDevice device, final VehicleType vehicleType) { new ServiceFinder(context, context.getPackageName(), new ServiceFinder.ServiceFinderCallback() { @Override public void onComplete(Vector routerServices) { @@ -263,7 +288,7 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { if (runningBluetoothServicePackage.isEmpty()) { //If there isn't a service running we should try to start one //We will try to sort the SDL enabled apps and find the one that's been installed the longest - final List sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator()); + final List sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator(), vehicleType); synchronized (DEVICE_LISTENER_LOCK) { final boolean sdlDeviceListenerEnabled = SdlDeviceListener.isFeatureSupported(sdlAppInfoList); if (sdlDeviceListenerEnabled) { @@ -294,7 +319,7 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { } if (sdlAppInfoList != null && !sdlAppInfoList.isEmpty()) { - startRouterService(context, sdlAppInfoList.get(0).getRouterServiceComponentName(), altTransportWake, device, false); + startRouterService(context, sdlAppInfoList.get(0).getRouterServiceComponentName(), altTransportWake, device, false, vehicleType); } else { DebugTool.logInfo(TAG, "No SDL Router Services found"); DebugTool.logInfo(TAG, "WARNING: This application has not specified its SdlRouterService correctly in the manifest. THIS WILL THROW AN EXCEPTION IN FUTURE RELEASES!!"); @@ -564,10 +589,16 @@ public abstract class SdlBroadcastReceiver extends BroadcastReceiver { synchronized (DEVICE_LISTENER_LOCK) { sdlDeviceListener = null; if (context != null) { - final List sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator()); + VehicleType vehicleType = null; + final String address = bluetoothDevice != null ? bluetoothDevice.getAddress() : null; + Hashtable store = AndroidTools.getVehicleTypeFromPrefs(context, address); + if (store != null) { + vehicleType = new VehicleType(store); + } + final List sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator(), vehicleType); if (sdlAppInfoList != null && !sdlAppInfoList.isEmpty()) { ComponentName routerService = sdlAppInfoList.get(0).getRouterServiceComponentName(); - startRouterService(context, routerService, false, bluetoothDevice, true); + startRouterService(context, routerService, false, bluetoothDevice, true, vehicleType); } } } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java index af63c9219..be68e8c13 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java @@ -89,6 +89,7 @@ import com.smartdevicelink.protocol.enums.FunctionID; import com.smartdevicelink.protocol.enums.MessageType; import com.smartdevicelink.protocol.enums.SessionType; import com.smartdevicelink.proxy.rpc.UnregisterAppInterface; +import com.smartdevicelink.proxy.rpc.VehicleType; import com.smartdevicelink.transport.enums.TransportType; import com.smartdevicelink.transport.utl.ByteAraryMessageAssembler; import com.smartdevicelink.transport.utl.ByteArrayMessageSpliter; @@ -141,7 +142,7 @@ public class SdlRouterService extends Service { /** * NOTE: DO NOT MODIFY THIS UNLESS YOU KNOW WHAT YOU'RE DOING. */ - protected static final int ROUTER_SERVICE_VERSION_NUMBER = 14; + protected static final int ROUTER_SERVICE_VERSION_NUMBER = 15; private static final String ROUTER_SERVICE_PROCESS = "com.smartdevicelink.router"; @@ -201,6 +202,7 @@ public class SdlRouterService extends Service { private boolean wrongProcess = false; private boolean initPassed = false; private boolean hasCalledStartForeground = false; + boolean firstStart = true; public static HashMap registeredApps; private SparseArray bluetoothSessionMap, usbSessionMap, tcpSessionMap; @@ -212,6 +214,7 @@ public class SdlRouterService extends Service { private static Messenger altTransportService = null; private boolean startSequenceComplete = false; + private VehicleType receivedVehicleType; private ExecutorService packetExecutor = null; ConcurrentHashMap packetWriteTaskMasterMap = null; @@ -1096,6 +1099,28 @@ public class SdlRouterService extends Service { DebugTool.logError(TAG, "Service isn't exported. Shutting down"); return false; } + + ComponentName name = new ComponentName(this, this.getClass()); + SdlAppInfo currentAppInfo = null; + + List sdlAppInfoList = AndroidTools.querySdlAppInfo(getApplicationContext(), new SdlAppInfo.BestRouterComparator(), null); + for (SdlAppInfo appInfo : sdlAppInfoList) { + if (appInfo.getRouterServiceComponentName().equals(name)) { + currentAppInfo = appInfo; + break; + } + } + + if (currentAppInfo == null) { + DebugTool.logError(TAG, "AppInfo for current package is not available. Shutting down"); + return false; + } + + if (!SdlAppInfo.checkIfVehicleSupported(currentAppInfo.getSupportedVehicles(), receivedVehicleType)) { + DebugTool.logError(TAG, "Received vehicle data is not supported. Shutting down"); + return false; + } + return true; } @@ -1118,37 +1143,13 @@ public class SdlRouterService extends Service { hasCalledStartForeground = true; resetForegroundTimeOut(FOREGROUND_TIMEOUT / 1000); } - - - if (!initCheck()) { // Run checks on process and permissions - deployNextRouterService(); - closeSelf(); - return; - } - initPassed = true; - - - synchronized (REGISTERED_APPS_LOCK) { - registeredApps = new HashMap(); - } - closing = false; - - synchronized (SESSION_LOCK) { - this.bluetoothSessionMap = new SparseArray(); - this.sessionHashIdMap = new SparseIntArray(); - this.cleanedSessionMap = new SparseIntArray(); - } - - packetExecutor = Executors.newSingleThreadExecutor(); - - startUpSequence(); } /** * The method will attempt to start up the next router service in line based on the sorting criteria of best router service. */ protected void deployNextRouterService() { - List sdlAppInfoList = AndroidTools.querySdlAppInfo(getApplicationContext(), new SdlAppInfo.BestRouterComparator()); + List sdlAppInfoList = AndroidTools.querySdlAppInfo(getApplicationContext(), new SdlAppInfo.BestRouterComparator(), null); if (sdlAppInfoList != null && !sdlAppInfoList.isEmpty()) { ComponentName name = new ComponentName(this, this.getClass()); SdlAppInfo info; @@ -1250,6 +1251,37 @@ public class SdlRouterService extends Service { @SuppressLint({"NewApi", "MissingPermission"}) @Override public int onStartCommand(Intent intent, int flags, int startId) { + if (intent != null && intent.hasExtra(TransportConstants.VEHICLE_INFO_EXTRA)) { + receivedVehicleType = new VehicleType( + (HashMap) intent.getSerializableExtra(TransportConstants.VEHICLE_INFO_EXTRA) + ); + } + // Only trusting the first intent received to start the RouterService and run initial checks to avoid a case where an app could send incorrect data after the spp connection has started. + if (firstStart) { + firstStart = false; + if (!initCheck()) { // Run checks on process and permissions + deployNextRouterService(); + closeSelf(); + return START_REDELIVER_INTENT; + } + initPassed = true; + + + synchronized (REGISTERED_APPS_LOCK) { + registeredApps = new HashMap(); + } + closing = false; + + synchronized (SESSION_LOCK) { + this.bluetoothSessionMap = new SparseArray(); + this.sessionHashIdMap = new SparseIntArray(); + this.cleanedSessionMap = new SparseIntArray(); + } + + packetExecutor = Executors.newSingleThreadExecutor(); + + startUpSequence(); + } if (intent != null) { if (intent.getBooleanExtra(FOREGROUND_EXTRA, false)) { hasCalledStartForeground = false; @@ -1756,6 +1788,9 @@ public class SdlRouterService extends Service { startService.putExtra(TransportConstants.FORCE_TRANSPORT_CONNECTED, true); startService.putExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_APP_PACKAGE, getBaseContext().getPackageName()); startService.putExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_CMP_NAME, new ComponentName(this, this.getClass())); + if (receivedVehicleType != null) { + startService.putExtra(TransportConstants.VEHICLE_INFO_EXTRA, receivedVehicleType.getStore()); + } if (record != null && record.getType() != null) { startService.putExtra(TransportConstants.START_ROUTER_SERVICE_TRANSPORT_CONNECTED, record.getType().toString()); @@ -2833,6 +2868,9 @@ public class SdlRouterService extends Service { pingIntent.putExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_APP_PACKAGE, getBaseContext().getPackageName()); pingIntent.putExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_CMP_NAME, new ComponentName(SdlRouterService.this, SdlRouterService.this.getClass())); pingIntent.putExtra(TransportConstants.START_ROUTER_SERVICE_SDL_ENABLED_PING, true); + if (receivedVehicleType != null) { + pingIntent.putExtra(TransportConstants.VEHICLE_INFO_EXTRA, receivedVehicleType.getStore()); + } } private void startClientPings() { diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/USBAccessoryAttachmentActivity.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/USBAccessoryAttachmentActivity.java index 604e3b435..8e54de9a1 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/USBAccessoryAttachmentActivity.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/USBAccessoryAttachmentActivity.java @@ -143,7 +143,7 @@ public class USBAccessoryAttachmentActivity extends Activity { //If there isn't a service running we should try to start one //We will try to sort the SDL enabled apps and find the one that's been installed the longest Intent serviceIntent; - List sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator()); + List sdlAppInfoList = AndroidTools.querySdlAppInfo(context, new SdlAppInfo.BestRouterComparator(), null); if (sdlAppInfoList != null && !sdlAppInfoList.isEmpty()) { SdlAppInfo optimalRouterService = sdlAppInfoList.get(0); diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java index 23cdd9857..41ada091d 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/utl/SdlDeviceListener.java @@ -42,13 +42,22 @@ import android.os.Message; import androidx.annotation.NonNull; +import com.smartdevicelink.protocol.SdlPacket; +import com.smartdevicelink.protocol.SdlPacketFactory; +import com.smartdevicelink.protocol.enums.ControlFrameTags; +import com.smartdevicelink.protocol.enums.SessionType; +import com.smartdevicelink.proxy.rpc.VehicleType; import com.smartdevicelink.transport.MultiplexBaseTransport; import com.smartdevicelink.transport.MultiplexBluetoothTransport; import com.smartdevicelink.transport.SdlRouterService; +import com.smartdevicelink.util.AndroidTools; +import com.smartdevicelink.util.BitConverter; import com.smartdevicelink.util.DebugTool; import com.smartdevicelink.util.SdlAppInfo; +import com.smartdevicelink.util.Version; import java.lang.ref.WeakReference; +import java.util.Hashtable; import java.util.List; @@ -59,6 +68,9 @@ public class SdlDeviceListener { private static final String SDL_DEVICE_STATUS_SHARED_PREFS = "sdl.device.status"; private static final Object LOCK = new Object(), RUNNING_LOCK = new Object(); + // If increasing MAX PROTOCOL VERSION major version, make sure to alter it in SdlProtocolBase + private static final Version MAX_PROTOCOL_VERSION = new Version(5, 4, 0); + private final WeakReference contextWeakReference; private final Callback callback; private BluetoothDevice connectedDevice; @@ -152,16 +164,10 @@ public class SdlDeviceListener { case SdlRouterService.MESSAGE_STATE_CHANGE: switch (msg.arg1) { case MultiplexBaseTransport.STATE_CONNECTED: - if(sdlListener.connectedDevice == null) { + if (sdlListener.connectedDevice == null) { sdlListener.connectedDevice = sdlListener.bluetoothTransport.getConnectedDevice(); } - sdlListener.setSDLConnectedStatus(sdlListener.contextWeakReference.get(), sdlListener.connectedDevice.getAddress(), true); - boolean keepConnectionOpen = sdlListener.callback.onTransportConnected(sdlListener.contextWeakReference.get(), sdlListener.connectedDevice); - if (!keepConnectionOpen) { - sdlListener.bluetoothTransport.stop(); - sdlListener.bluetoothTransport = null; - sdlListener.timeoutHandler.removeCallbacks(sdlListener.timeoutRunner); - } + sendStartService(); break; case MultiplexBaseTransport.STATE_NONE: // We've just lost the connection @@ -174,9 +180,90 @@ public class SdlDeviceListener { break; case com.smartdevicelink.transport.SdlRouterService.MESSAGE_READ: + onPacketRead((SdlPacket) msg.obj); break; } } + + /** + * This method will start the RPC service so that we can retrieve vehicle info + */ + public void sendStartService() { + SdlDeviceListener sdlListener = this.provider.get(); + SdlPacket serviceProbe = SdlPacketFactory.createStartSession(SessionType.RPC, 0x00, (byte)0x01, (byte)0x00, false); + serviceProbe.putTag(ControlFrameTags.RPC.StartService.PROTOCOL_VERSION, MAX_PROTOCOL_VERSION.toString()); + byte[] constructed = serviceProbe.constructPacket(); + if (sdlListener.bluetoothTransport != null && sdlListener.bluetoothTransport.getState() == MultiplexBluetoothTransport.STATE_CONNECTED) { + sdlListener.bluetoothTransport.write(constructed, 0, constructed.length); + } else { + sdlListener.callback.onTransportError(sdlListener.connectedDevice); + } + } + + /** + * This method will pull vehicle data out of a StartServiceACK and then end the RPC session + * + * @param packet - info received from connected module + */ + public void onPacketRead(SdlPacket packet) { + SdlDeviceListener sdlListener = this.provider.get(); + VehicleType vehicleType = null; + if (packet.getFrameInfo() == SdlPacket.FRAME_INFO_START_SERVICE_ACK) { + int hashID; + if (packet.getVersion() >= 5) { + vehicleType = getVehicleType(packet); + hashID = (Integer) packet.getTag(ControlFrameTags.RPC.StartServiceACK.HASH_ID); + } else { + hashID = BitConverter.intFromByteArray(packet.getPayload(), 0); + } + byte[] stopService = SdlPacketFactory.createEndSession(SessionType.RPC, (byte)packet.getSessionId(), 0, (byte)packet.getVersion(), hashID).constructPacket(); + if (sdlListener.bluetoothTransport != null && sdlListener.bluetoothTransport.getState() == MultiplexBluetoothTransport.STATE_CONNECTED) { + sdlListener.bluetoothTransport.write(stopService, 0, stopService.length); + } + notifyConnection(vehicleType); + } + } + + /** + * Retrieves vehicle type from the StartServiceACK + * + * @param packet - info received from connected module + * @return vehicle type of connected module + */ + private VehicleType getVehicleType(SdlPacket packet) { + String make = (String) packet.getTag(ControlFrameTags.RPC.StartServiceACK.MAKE); + String model = (String) packet.getTag(ControlFrameTags.RPC.StartServiceACK.MODEL); + String modelYear = (String) packet.getTag(ControlFrameTags.RPC.StartServiceACK.MODEL_YEAR); + String vehicleTrim = (String) packet.getTag(ControlFrameTags.RPC.StartServiceACK.TRIM); + if (make != null) { + // checking if tags have come from core + VehicleType type = new VehicleType(); + type.setMake(make); + type.setModel(model); + type.setModelYear(modelYear); + type.setTrim(vehicleTrim); + return type; + } else { + return null; + } + } + + /** + * Notify connection about vehicle type. + * + * @param vehicleType the information about the type of vehicle + */ + public void notifyConnection(VehicleType vehicleType) { + SdlDeviceListener sdlListener = this.provider.get(); + sdlListener.setSDLConnectedStatus(sdlListener.contextWeakReference.get(), sdlListener.connectedDevice.getAddress(), true); + AndroidTools.saveVehicleType(sdlListener.contextWeakReference.get(), vehicleType, sdlListener.connectedDevice.getAddress()); + boolean keepConnectionOpen = sdlListener.callback.onTransportConnected(sdlListener.contextWeakReference.get(), sdlListener.connectedDevice); + if (!keepConnectionOpen) { + sdlListener.bluetoothTransport.stop(); + sdlListener.bluetoothTransport = null; + sdlListener.timeoutHandler.removeCallbacks(sdlListener.timeoutRunner); + } + } } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java b/android/sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java index 9109dc3ad..1f4ba2de0 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/util/AndroidTools.java @@ -36,18 +36,28 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.BatteryManager; +import android.os.Bundle; +import androidx.annotation.Nullable; +import com.smartdevicelink.marshal.JsonRPCMarshaller; +import com.smartdevicelink.proxy.rpc.VehicleType; import com.smartdevicelink.transport.TransportConstants; +import org.json.JSONException; +import org.json.JSONObject; + import java.io.BufferedInputStream; import java.io.IOException; import java.net.URL; @@ -56,9 +66,16 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Hashtable; import java.util.List; + public class AndroidTools { + + private static final String TAG = "AndroidTools"; + private static final String SDL_DEVICE_VEHICLES_PREFS = "sdl.device.vehicles"; + private static final Object VEHICLE_PREF_LOCK = new Object(); + /** * Check to see if a component is exported * @@ -97,7 +114,6 @@ public class AndroidTools { return sdlMultiList; } - /** * Finds all SDL apps via their SdlRouterService manifest entry. It will return the metadata associated with that router service. * @@ -105,6 +121,7 @@ public class AndroidTools { * @param comparator the Comparator to sort the resulting list. If null is supplied, they will be returned as they are from the system * @return the sorted list of SdlAppInfo objects that represent SDL apps */ + @Deprecated public static List querySdlAppInfo(Context context, Comparator comparator) { List sdlAppInfoList = new ArrayList<>(); Intent intent = new Intent(TransportConstants.ROUTER_SERVICE_ACTION); @@ -118,7 +135,7 @@ public class AndroidTools { PackageInfo packageInfo; try { packageInfo = packageManager.getPackageInfo(info.serviceInfo.packageName, 0); - sdlAppInfoList.add(new SdlAppInfo(info, packageInfo)); + sdlAppInfoList.add(new SdlAppInfo(info, packageInfo, context)); } catch (NameNotFoundException e) { //Package was not found, likely a sign the resolve info can't be trusted. } @@ -134,6 +151,54 @@ public class AndroidTools { return sdlAppInfoList; } + /** + * Finds all SDL apps via their SdlRouterService manifest entry. It will return the metadata associated with that router service. + * + * @param context a context instance to obtain the package manager + * @param comparator the Comparator to sort the resulting list. If null is supplied, they will be returned as they are from the system + * @return the sorted list of SdlAppInfo objects that represent SDL apps + */ + public static List querySdlAppInfo(Context context, Comparator comparator, VehicleType type) { + List sdlAppInfoList = new ArrayList<>(); + Intent intent = new Intent(TransportConstants.ROUTER_SERVICE_ACTION); + List resolveInfoList = context.getPackageManager().queryIntentServices(intent, PackageManager.GET_META_DATA); + + if (resolveInfoList != null && resolveInfoList.size() > 0) { + PackageManager packageManager = context.getPackageManager(); + if (packageManager != null) { + + for (ResolveInfo info : resolveInfoList) { + PackageInfo packageInfo; + try { + packageInfo = packageManager.getPackageInfo(info.serviceInfo.packageName, 0); + SdlAppInfo appInformation = new SdlAppInfo(info, packageInfo, context); + sdlAppInfoList.add(appInformation); + + } catch (NameNotFoundException e) { + //Package was not found, likely a sign the resolve info can't be trusted. + } + + } + } + + List sdlAppInfoListVehicleType = new ArrayList<>(); + + for (SdlAppInfo appInformation : sdlAppInfoList) { + if (appInformation.routerServiceVersion < 15) { + sdlAppInfoListVehicleType.add(appInformation); + } else if (SdlAppInfo.checkIfVehicleSupported(appInformation.supportedVehicles, type)) { + sdlAppInfoListVehicleType.add(appInformation); + } + } + sdlAppInfoList = sdlAppInfoListVehicleType; + + if (comparator != null) { + Collections.sort(sdlAppInfoList, comparator); + } + } + return sdlAppInfoList; + } + /** * Sends the provided intent to the specified destinations making it an explicit intent, rather @@ -204,4 +269,104 @@ public class AndroidTools { } return false; } + + /** + * Saves the mac address along with vehicle details into user's shared prefs. + * + * @param context a context instance to obtain the shared preferences. + * @param vehicleType a RPCStruct that describes the type of vehicle the mobile phone is connected with. + * @param address a string containing the Bluetooth Mac address of the connected vehicle. + */ + public static void saveVehicleType(Context context, VehicleType vehicleType, String address) { + synchronized (VEHICLE_PREF_LOCK) { + if (vehicleType == null || address == null || context == null) { + DebugTool.logWarning(TAG, String.format("Unable to save vehicle type. Context is %1$s, Address is %2$s and VehicleType is %3$s", context, address, vehicleType)); + return; + } + try { + SharedPreferences preferences = context.getSharedPreferences(SDL_DEVICE_VEHICLES_PREFS, Context.MODE_PRIVATE); + + String jsonString = vehicleType.serializeJSON().toString(); + SharedPreferences.Editor editor = preferences.edit(); + editor.putString(address, jsonString); + editor.commit(); + } catch (JSONException e) { + DebugTool.logError(TAG, "Unable to serialize vehicle type to JSON.", e); + } + } + } + + /** + * Retrieves the vehicle details by the mac address of the connected vehicle. + * + * @param context a context instance to obtain the shared preferences. + * @param address a string containing the Bluetooth Mac address of the connected vehicle. + * @return The Hashtable to be used to construct VehicleTyp + */ + public static @Nullable Hashtable getVehicleTypeFromPrefs(Context context, String address) { + synchronized (VEHICLE_PREF_LOCK) { + if (context == null || address == null) { + DebugTool.logWarning(TAG, String.format("Unable to get vehicle type from prefs. Context is %1$s and Address is %2$s", context, address)); + return null; + } + try { + SharedPreferences preferences = context.getSharedPreferences(SDL_DEVICE_VEHICLES_PREFS, Context.MODE_PRIVATE); + String storedVehicleTypeSerialized = preferences.getString(address, null); + + if (storedVehicleTypeSerialized == null) { + return null; + } else { + JSONObject object = new JSONObject(storedVehicleTypeSerialized); + return JsonRPCMarshaller.deserializeJSONObject(object); + } + } catch (JSONException e) { + DebugTool.logError(TAG, "Unable to deserialize stored vehicle type.", e); + return null; + } + } + } + + /** + * Retrieves the list of vehicle types that are set in the manifest. + * + * @param context a context to access Android system services through. + * @param component a component name of a LocalRouterService. + * @param manifestFieldId a string resources id that indicates an unique name for the vehicle data in the manifest. + * @return The list of vehicle types, or null if an error occurred or field was not found. + */ + public static @Nullable List getVehicleTypesFromManifest(Context context, ComponentName component, int manifestFieldId) { + if (context == null || component == null) { + DebugTool.logWarning(TAG, String.format("Unable to get vehicle type from manifest. Context is %1$s and Component is %2$s", context, component)); + return null; + } + try { + PackageManager pm = context.getPackageManager(); + Resources resources = context.getResources(); + if (pm == null || resources == null) { + DebugTool.logWarning(TAG, String.format("Unable to get vehicle type from manifest. PackageManager is %1$s and Resources is %2$s", pm, resources)); + return null; + } + + Bundle metaData = pm.getServiceInfo(component, PackageManager.GET_META_DATA).metaData; + if (metaData == null) { + DebugTool.logError(TAG, "metaData isn't available."); + return null; + } + + int xmlFieldId = metaData.getInt(resources.getString(manifestFieldId)); + if (xmlFieldId == 0) { + DebugTool.logError(TAG, "Field with id " + manifestFieldId + " was not found in manifest"); + return null; + } + + XmlResourceParser parser = resources.getXml(xmlFieldId); + return SdlAppInfo.deserializeSupportedVehicles(parser); + } catch (PackageManager.NameNotFoundException e) { + DebugTool.logError(TAG, "Failed to get OEM vehicle data filter: " + e.getMessage()+ " - assume vehicle data is supported"); + return null; + } catch (Resources.NotFoundException ex) { + DebugTool.logError(TAG, "Failed to find resource: " + ex.getMessage()+ " - assume vehicle data is supported"); + return null; + } + } } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/util/IntegrationValidator.java b/android/sdl_android/src/main/java/com/smartdevicelink/util/IntegrationValidator.java index ba15ff4aa..8e904a45f 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/util/IntegrationValidator.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/util/IntegrationValidator.java @@ -198,7 +198,7 @@ public class IntegrationValidator { boolean serviceFilterHasAction = false; String className = localRouterClass.getName(); - List services = AndroidTools.querySdlAppInfo(context, null); + List services = AndroidTools.querySdlAppInfo(context, null, null); for (SdlAppInfo sdlAppInfo : services) { if (sdlAppInfo != null && sdlAppInfo.getRouterServiceComponentName() != null && className.equals((sdlAppInfo.getRouterServiceComponentName().getClassName()))) { diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/util/SdlAppInfo.java b/android/sdl_android/src/main/java/com/smartdevicelink/util/SdlAppInfo.java index d49e63575..7d5ade055 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/util/SdlAppInfo.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/util/SdlAppInfo.java @@ -33,11 +33,23 @@ package com.smartdevicelink.util; import android.content.ComponentName; +import android.content.Context; import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; import android.os.Bundle; +import com.smartdevicelink.proxy.rpc.VehicleType; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.ArrayList; import java.util.Comparator; +import java.util.List; /** * Created by Joey Grover on 2/2/18. @@ -49,15 +61,17 @@ public class SdlAppInfo { //FIXME we shouldn't be duplicating constants, but this currently keeps us from needing a context instance. private static final String SDL_ROUTER_VERSION_METADATA = "sdl_router_version"; private static final String SDL_CUSTOM_ROUTER_METADATA = "sdl_custom_router"; + private static final String SDL_OEM_VEHICLE_TYPE_METADATA = "sdl_oem_vehicle_type"; String packageName; ComponentName routerServiceComponentName; int routerServiceVersion = 4; //We use this as a default and assume if the number doesn't exist in meta data it is because the app hasn't updated. boolean isCustomRouterService = false; + List supportedVehicles = new ArrayList<>(); long lastUpdateTime; - + @Deprecated public SdlAppInfo(ResolveInfo resolveInfo, PackageInfo packageInfo) { if (resolveInfo.serviceInfo != null) { @@ -89,6 +103,75 @@ public class SdlAppInfo { } } + public SdlAppInfo(ResolveInfo resolveInfo, PackageInfo packageInfo, Context context) { + if (resolveInfo.serviceInfo != null) { + + this.packageName = resolveInfo.serviceInfo.packageName; + this.routerServiceComponentName = new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name); + + Bundle metadata = resolveInfo.serviceInfo.metaData; + if (metadata != null) { + + if (metadata.containsKey(SDL_ROUTER_VERSION_METADATA)) { + this.routerServiceVersion = metadata.getInt(SDL_ROUTER_VERSION_METADATA); + } + + if (metadata.containsKey(SDL_CUSTOM_ROUTER_METADATA)) { + this.isCustomRouterService = metadata.getBoolean(SDL_CUSTOM_ROUTER_METADATA); + } + + if (metadata.containsKey(SDL_OEM_VEHICLE_TYPE_METADATA)) { + if (context == null) { + DebugTool.logWarning(TAG, "Unable to deserialize supported vehicles: supplied Context is null"); + return; + } + + String contextPackageName = context.getPackageName(); + if (contextPackageName == null || packageName == null) { + DebugTool.logWarning(TAG, String.format("Unable to deserialize supported vehicles. ContextPackageName: %1$s and PackageName: %2$s", contextPackageName, packageName)); + return; + } + + Resources resources = null; + if (!contextPackageName.equals(packageName)) { + try { + Context appContext = context.createPackageContext(packageName, 0); + if (appContext == null){ + DebugTool.logError(TAG, "Failed to create context with the given package name"); + return; + } + resources = appContext.getResources(); + } catch (PackageManager.NameNotFoundException e) { + DebugTool.logError(TAG, "Failed to create context with the given package name: " + e.getMessage()); + } + } else { + resources = context.getResources(); + } + + if (resources != null) { + try { + XmlResourceParser parser = resources.getXml(metadata.getInt(SDL_OEM_VEHICLE_TYPE_METADATA)); + this.supportedVehicles = deserializeSupportedVehicles(parser); + } catch (Resources.NotFoundException ex) { + DebugTool.logError(TAG, "Failed to find resource: " + ex.getMessage()); + } + } + } + } else { + DebugTool.logWarning(TAG, packageName + " has not supplied metadata with their router service!"); + } + } + + if (packageInfo != null) { + this.lastUpdateTime = packageInfo.lastUpdateTime; + if (this.lastUpdateTime <= 0) { + this.lastUpdateTime = packageInfo.firstInstallTime; + } + } else { + this.lastUpdateTime = 0; + } + } + public int getRouterServiceVersion() { return routerServiceVersion; } @@ -122,11 +205,119 @@ public class SdlAppInfo { builder.append("\nLast updated: "); builder.append(this.lastUpdateTime); + builder.append("\nVehicle make list: "); + builder.append(this.supportedVehicles); + builder.append("\n-------- Sdl App Info End------"); return builder.toString(); } + /** + * Retrieves the list of vehicle types that are set in the xml file. + * + * @param parser The xml parsing interface for the vehicle types xml file. + * @return The list of vehicle types. + */ + public static List deserializeSupportedVehicles(XmlResourceParser parser) { + List vehicleMakesList = new ArrayList<>(); + if (parser == null) { + return vehicleMakesList; + } + try { + int eventType = parser.getEventType(); + while (eventType != XmlPullParser.END_DOCUMENT) { + if (eventType == XmlPullParser.START_TAG) { + String tagName = parser.getName(); + if (tagName != null && tagName.equalsIgnoreCase("vehicle-type")) { + VehicleType vehicleMake = new VehicleType(); + String make = parser.getAttributeValue(null, "make"); + if (make != null) { + vehicleMake.setMake(make); + String model = parser.getAttributeValue(null, "model"); + String modelYear = parser.getAttributeValue(null, "modelYear"); + String trim = parser.getAttributeValue(null, "trim"); + + if (model == null && modelYear == null && trim == null) { + vehicleMakesList.add(vehicleMake); + } else if (model != null){ + vehicleMake.setModel(model); + if (modelYear != null) { + vehicleMake.setModelYear(modelYear); + } + if (trim != null) { + vehicleMake.setTrim(trim); + } + vehicleMakesList.add(vehicleMake); + } + } + } + } + eventType = parser.next(); + } + } catch (XmlPullParserException e) { + DebugTool.logError(TAG, "Failed to parse xml file", e); + } catch (IOException e) { + DebugTool.logError(TAG, "Failed to find next element in the xml file", e); + } + return vehicleMakesList; + } + + /** + * Check to see if a vehicle type is supported. + * + * @param supportedVehicleList the list of supported vehicle types. + * @param connectedVehicle the vehicle type to check. + * @return true if vehicle type is supported. + */ + public static boolean checkIfVehicleSupported(List supportedVehicleList, VehicleType connectedVehicle) { + if (supportedVehicleList == null || supportedVehicleList.isEmpty() || connectedVehicle == null || connectedVehicle.getStore() == null|| connectedVehicle.getStore().isEmpty()) { + return true; + } + if (supportedVehicleList.contains(connectedVehicle)) { + return true; + } + for (VehicleType supportedVehicle: supportedVehicleList) { + boolean areVehicleMakesEqual = CompareUtils.areStringsEqual(supportedVehicle.getMake(), connectedVehicle.getMake(), true, false); + if (areVehicleMakesEqual) { + String supportedVehicleModel = supportedVehicle.getModel(); + String connectedVehicleModel = connectedVehicle.getModel(); + if (supportedVehicleModel != null && connectedVehicleModel != null) { + if (connectedVehicleModel.equalsIgnoreCase(supportedVehicleModel)) { + boolean ret = true; + String supportedVehicleModelYear = supportedVehicle.getModelYear(); + String connectedVehicleModelYear = connectedVehicle.getModelYear(); + if (supportedVehicleModelYear != null && connectedVehicleModelYear != null) { + ret = connectedVehicleModelYear.equalsIgnoreCase(supportedVehicleModelYear); + } + String supportedVehicleTrim = supportedVehicle.getTrim(); + String connectedVehicleTrim = connectedVehicle.getTrim(); + if (supportedVehicleTrim != null && connectedVehicleTrim != null) { + ret &= connectedVehicleTrim.equalsIgnoreCase(supportedVehicleTrim); + } + if (ret) { + // We found matches and return or continue iteration otherwise + return true; + } + } + } else { + // Return true if only make is defined and it matches + return true; + } + } + } + return false; + } + + /** + * Gets app's supported vehicle types. + * + * @return List a list of supported vehicle types. + */ + public List getSupportedVehicles() { + return supportedVehicles; + } + /** * This comparator will sort a list to find the best router service to start out of the known SDL enabled apps */ diff --git a/android/sdl_android/src/main/res/values/sdl.xml b/android/sdl_android/src/main/res/values/sdl.xml index 1d5f0897a..7bb919f0b 100644 --- a/android/sdl_android/src/main/res/values/sdl.xml +++ b/android/sdl_android/src/main/res/values/sdl.xml @@ -2,7 +2,8 @@ sdl_router_version - 14 + 15 sdl_custom_router + sdl_oem_vehicle_type 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 2e4651d56..6602e7085 100644 --- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseLifecycleManager.java @@ -90,6 +90,7 @@ import com.smartdevicelink.session.ISdlSessionListener; import com.smartdevicelink.session.SdlSession; import com.smartdevicelink.streaming.video.VideoStreamingParameters; import com.smartdevicelink.transport.BaseTransportConfig; +import com.smartdevicelink.transport.utl.TransportRecord; import com.smartdevicelink.util.CorrelationIdGenerator; import com.smartdevicelink.util.DebugTool; import com.smartdevicelink.util.FileUtls; @@ -398,6 +399,7 @@ abstract class BaseLifecycleManager { VehicleType vehicleType = raiResponse.getVehicleType(); String systemSoftwareVersion = raiResponse.getSystemSoftwareVersion(); if (vehicleType != null || systemSoftwareVersion != null) { + saveVehicleType(session.getActiveTransports(), vehicleType); SystemInfo systemInfo = new SystemInfo(vehicleType, systemSoftwareVersion, null); boolean validSystemInfo = lifecycleListener.onSystemInfoReceived(systemInfo); if (!validSystemInfo) { @@ -946,6 +948,7 @@ abstract class BaseLifecycleManager { if (systemInfo != null && lifecycleListener != null) { didCheckSystemInfo = true; + saveVehicleType(session.getActiveTransports(), systemInfo.getVehicleType()); boolean validSystemInfo = lifecycleListener.onSystemInfoReceived(systemInfo); if (!validSystemInfo) { DebugTool.logWarning(TAG, "Disconnecting from head unit, the system info was not accepted."); @@ -1325,6 +1328,12 @@ abstract class BaseLifecycleManager { abstract void cycle(SdlDisconnectedReason disconnectedReason); + void saveVehicleType(String address, VehicleType type){ + } + + void saveVehicleType(List activeTransports, VehicleType type) { + } + void onTransportDisconnected(String info, boolean availablePrimary, BaseTransportConfig transportConfig) { } diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java index 5cac1302c..14222a548 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java @@ -1566,5 +1566,17 @@ public class SdlProtocolBase { } // end-method } // end-class + /** + * This method will return copy of active transports + * + * @return a list of active transports + * */ + public List getActiveTransports() { + List records = new ArrayList<>(); + for (TransportRecord record : activeTransports.values()) { + records.add(new TransportRecord(record.getType(), record.getAddress())); + } + return records; + } } diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/VehicleType.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/VehicleType.java index a05216661..8841126c3 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/VehicleType.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/VehicleType.java @@ -33,6 +33,7 @@ package com.smartdevicelink.proxy.rpc; import com.smartdevicelink.proxy.RPCStruct; +import java.util.HashMap; import java.util.Hashtable; /** @@ -110,6 +111,18 @@ public class VehicleType extends RPCStruct { super(hash); } + /** + *

+ * Constructs a new VehicleType object indicated by the Hashtable + * parameter + *

+ * + * @param hash The Hashtable to use + */ + public VehicleType(HashMap hash) { + super(new Hashtable<>(hash)); + } + /** * get the make of the vehicle * diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index 8b35541b4..73c6bf0b3 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -32,6 +32,7 @@ package com.smartdevicelink.session; +import androidx.annotation.Nullable; import androidx.annotation.RestrictTo; import com.smartdevicelink.exception.SdlException; @@ -52,10 +53,12 @@ import com.smartdevicelink.security.SdlSecurityBase; import com.smartdevicelink.streaming.video.VideoStreamingParameters; import com.smartdevicelink.transport.BaseTransportConfig; import com.smartdevicelink.transport.enums.TransportType; +import com.smartdevicelink.transport.utl.TransportRecord; import com.smartdevicelink.util.DebugTool; import com.smartdevicelink.util.SystemInfo; import com.smartdevicelink.util.Version; +import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.ListIterator; @@ -415,4 +418,18 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ public boolean isTransportForServiceAvailable(SessionType sessionType) { return sdlProtocol != null && sdlProtocol.isTransportForServiceAvailable(sessionType); } + + /** + * Retrieves list of the active transports + * + * @return a list of active transports + * */ + @Nullable + public List getActiveTransports() { + if (this.sdlProtocol != null) { + return this.sdlProtocol.getActiveTransports(); + } else { + return null; + } + } } diff --git a/base/src/main/java/com/smartdevicelink/transport/TransportConstants.java b/base/src/main/java/com/smartdevicelink/transport/TransportConstants.java index d2a45fcb9..79a055bc9 100644 --- a/base/src/main/java/com/smartdevicelink/transport/TransportConstants.java +++ b/base/src/main/java/com/smartdevicelink/transport/TransportConstants.java @@ -44,6 +44,7 @@ public class TransportConstants { public static final String ROUTER_SERVICE_ACTION = "com.smartdevicelink.router.service"; public static final String FOREGROUND_EXTRA = "foreground"; public static final String CONFIRMED_SDL_DEVICE = "confirmed_sdl_device"; + public static final String VEHICLE_INFO_EXTRA = "vehicle_info"; public static final String BIND_LOCATION_PACKAGE_NAME_EXTRA = "BIND_LOCATION_PACKAGE_NAME_EXTRA"; public static final String BIND_LOCATION_CLASS_NAME_EXTRA = "BIND_LOCATION_CLASS_NAME_EXTRA"; -- cgit v1.2.1 From 0a29f6816911c71683a5009da9f43021149ae0b7 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 31 Aug 2021 11:45:23 -0400 Subject: Add more menu manager tests --- .../managers/screen/menu/MenuManagerTests.java | 34 ++++++++++++++++++++++ .../managers/screen/menu/BaseMenuManager.java | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index b76964ce2..08993cc96 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -225,6 +225,40 @@ public class MenuManagerTests { assertEquals(cells, menuManager.currentMenuCells); } + @Test + public void testSettingNullMenu() { + menuManager.setMenuCells(null); + assertEquals(HMILevel.HMI_NONE, menuManager.currentHMILevel); + assertTrue(menuManager.currentMenuCells.isEmpty()); + + // The Menu Manager should send new menu once HMI full occurs + sendFakeCoreOnHMIFullNotifications(); + + // Listener should be triggered - which sets new HMI level and should proceed to send our pending update + assertEquals(HMILevel.HMI_FULL, menuManager.currentHMILevel); + + assertTrue(menuManager.currentMenuCells.isEmpty()); + } + + @Test + public void testSettingNonUniqueCells() { + MenuSelectionListener listener = null; + MenuCell cell1 = new MenuCell("cell", null, null, listener); + MenuCell cell2 = new MenuCell("cell", null, null, listener); + + menuManager.setMenuCells(Arrays.asList(cell1, cell2)); + assertEquals(HMILevel.HMI_NONE, menuManager.currentHMILevel); + assertTrue(menuManager.currentMenuCells.isEmpty()); + + // The Menu Manager should send new menu once HMI full occurs + sendFakeCoreOnHMIFullNotifications(); + + // Listener should be triggered - which sets new HMI level and should proceed to send our pending update + assertEquals(HMILevel.HMI_FULL, menuManager.currentHMILevel); + + assertTrue(menuManager.transactionQueue.getTasksAsList().isEmpty()); + } + @Test public void testUpdatingOldWay() { // Force Menu Manager to use the old way of deleting / sending all diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 42e4ac439..dbc7251ea 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -80,7 +80,7 @@ abstract class BaseMenuManager extends BaseSubManager { OnRPCNotificationListener commandListener; OnSystemCapabilityListener onDisplaysCapabilityListener; WindowCapability windowCapability; - private Queue transactionQueue; + Queue transactionQueue; BaseMenuManager(@NonNull ISdl internalInterface, @NonNull FileManager fileManager) { super(internalInterface); -- cgit v1.2.1 From 8356439986c1f3feb1b66d9b64e9832841b2e69c Mon Sep 17 00:00:00 2001 From: Robert Henigan Date: Wed, 1 Sep 2021 11:08:36 -0400 Subject: Update Handler constructors from deprecated versions (#1726) * Update Handler constructors from deprecated * Change to mainLooper * Add null checks for myLooper to prep looper * Cleanup where looper was already prepared * Replace mainLooper with myLooper for non UI tasks Co-authored-by: Henigan --- .../smartdevicelink/encoder/VirtualDisplayEncoder.java | 2 +- .../managers/lockscreen/LockScreenManager.java | 3 ++- .../protocol/heartbeat/HeartbeatMonitor.java | 2 +- .../transport/MultiplexBluetoothTransport.java | 5 ++++- .../com/smartdevicelink/transport/SdlRouterService.java | 15 ++++++++++++--- .../com/smartdevicelink/transport/TransportManager.java | 8 ++++---- 6 files changed, 24 insertions(+), 11 deletions(-) diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java b/android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java index 7c8df5b98..3eba6af30 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/encoder/VirtualDisplayEncoder.java @@ -337,7 +337,7 @@ public class VirtualDisplayEncoder { Looper.prepare(); // create a Handler for this thread - mHandler = new Handler() { + mHandler = new Handler(Looper.myLooper()) { public void handleMessage(Message msg) { switch (msg.what) { case MSG_TICK: { diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java index 6f0e68aba..97e8c8bca 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/lockscreen/LockScreenManager.java @@ -38,6 +38,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.graphics.Bitmap; import android.os.Handler; +import android.os.Looper; import androidx.annotation.RestrictTo; @@ -227,7 +228,7 @@ public class LockScreenManager extends BaseSubManager { mLockScreenShouldBeAutoDismissed = false; } if (!receivedFirstDDNotification) { - new Handler().postDelayed(new Runnable() { + new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { @Override public void run() { launchLockScreenActivity(); diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/protocol/heartbeat/HeartbeatMonitor.java b/android/sdl_android/src/main/java/com/smartdevicelink/protocol/heartbeat/HeartbeatMonitor.java index c60cdaadf..831e59da4 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/protocol/heartbeat/HeartbeatMonitor.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/protocol/heartbeat/HeartbeatMonitor.java @@ -155,7 +155,7 @@ public class HeartbeatMonitor implements IHeartbeatMonitor { Looper.prepare(); mHeartbeatThreadLooper = Looper.myLooper(); - mHeartbeatThreadHandler = new Handler(); + mHeartbeatThreadHandler = new Handler(mHeartbeatThreadLooper); mIsAckReceived = true; isHeartbeatReceived = true; diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java index fba01a561..31cea21c8 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/MultiplexBluetoothTransport.java @@ -348,7 +348,10 @@ public class MultiplexBluetoothTransport extends MultiplexBaseTransport { } private void timerDelayRemoveDialog(final BluetoothSocket sock) { - timeOutHandler = new Handler(); + if (Looper.myLooper() == null) { + Looper.prepare(); + } + timeOutHandler = new Handler(Looper.myLooper()); socketRunnable = new Runnable() { public void run() { //Log.e(TAG, "BLUETOOTH SOCKET CONNECT TIMEOUT - ATTEMPT TO CLOSE SOCKET"); diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java index be68e8c13..7f6eb2b40 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java @@ -1442,7 +1442,10 @@ public class SdlRouterService extends Service { public void resetForegroundTimeOut(long delay) { synchronized (FOREGROUND_NOTIFICATION_LOCK) { if (foregroundTimeoutHandler == null) { - foregroundTimeoutHandler = new Handler(); + if (Looper.myLooper() == null) { + Looper.prepare(); + } + foregroundTimeoutHandler = new Handler(Looper.myLooper()); } if (foregroundTimeoutRunnable == null) { foregroundTimeoutRunnable = new Runnable() { @@ -2536,7 +2539,10 @@ public class SdlRouterService extends Service { * This method is used to check for the newest version of this class to make sure the latest and greatest is up and running. */ private void startAltTransportTimer() { - altTransportTimerHandler = new Handler(); + if (Looper.myLooper() == null) { + Looper.prepare(); + } + altTransportTimerHandler = new Handler(Looper.myLooper()); altTransportTimerRunnable = new Runnable() { public void run() { altTransportTimerHandler = null; @@ -3095,7 +3101,10 @@ public class SdlRouterService extends Service { this.messenger = messenger; this.sessionIds = new Vector(); this.queues = new ConcurrentHashMap<>(); - queueWaitHandler = new Handler(); + if (Looper.myLooper() == null) { + Looper.prepare(); + } + queueWaitHandler = new Handler(Looper.myLooper()); registeredTransports = new SparseArray>(); awaitingSession = new Vector<>(); setDeathNote(); //messaging Version diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java index a1392aace..6c3050301 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/TransportManager.java @@ -402,10 +402,10 @@ public class TransportManager extends TransportManagerBase { return; //Already in legacy mode } + if (Looper.myLooper() == null) { + Looper.prepare(); + } if (transportListener.onLegacyModeEnabled(info)) { - if (Looper.myLooper() == null) { - Looper.prepare(); - } legacyBluetoothHandler = new LegacyBluetoothHandler(this); legacyBluetoothTransport = new MultiplexBluetoothTransport(legacyBluetoothHandler); if (contextWeakReference.get() != null) { @@ -415,7 +415,7 @@ public class TransportManager extends TransportManagerBase { contextWeakReference.get().registerReceiver(legacyDisconnectReceiver, intentFilter); } } else { - new Handler().postDelayed(new Runnable() { + new Handler(Looper.myLooper()).postDelayed(new Runnable() { @Override public void run() { transportListener.onError(info + " - Legacy mode unacceptable; shutting down."); -- cgit v1.2.1 From 7a9911edb713a0e96cc4fc30d593800fb5975ae9 Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 1 Sep 2021 15:20:28 -0400 Subject: Align to latest iOS PR --- .../java/com/smartdevicelink/test/Validator.java | 13 +- .../test/protocol/SecurityQueryPayloadTests.java | 27 ++- .../test/protocol/enums/QueryErrorCodeTests.java | 189 --------------------- .../test/protocol/enums/QueryIDTests.java | 91 ---------- .../test/protocol/enums/QueryTypeTests.java | 97 ----------- .../enums/SecurityQueryErrorCodeTests.java | 189 +++++++++++++++++++++ .../test/protocol/enums/SecurityQueryIDTests.java | 91 ++++++++++ .../protocol/enums/SecurityQueryTypeTests.java | 97 +++++++++++ .../protocol/SecurityQueryPayload.java | 79 +++++---- .../protocol/enums/QueryErrorCode.java | 61 ------- .../smartdevicelink/protocol/enums/QueryID.java | 105 ------------ .../smartdevicelink/protocol/enums/QueryType.java | 41 ----- .../protocol/enums/SecurityQueryErrorCode.java | 61 +++++++ .../protocol/enums/SecurityQueryID.java | 105 ++++++++++++ .../protocol/enums/SecurityQueryType.java | 41 +++++ .../smartdevicelink/session/BaseSdlSession.java | 38 +++-- 16 files changed, 671 insertions(+), 654 deletions(-) delete mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryErrorCodeTests.java delete mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryIDTests.java delete mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryTypeTests.java create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryErrorCodeTests.java create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryIDTests.java create mode 100644 android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryTypeTests.java delete mode 100644 base/src/main/java/com/smartdevicelink/protocol/enums/QueryErrorCode.java delete mode 100644 base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java delete mode 100644 base/src/main/java/com/smartdevicelink/protocol/enums/QueryType.java create mode 100644 base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryErrorCode.java create mode 100644 base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryID.java create mode 100644 base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryType.java diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java index fd2e6fc30..094b29794 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/Validator.java @@ -3,9 +3,9 @@ package com.smartdevicelink.test; import com.smartdevicelink.managers.file.filetypes.SdlFile; import com.smartdevicelink.protocol.enums.FrameDataControlFrameType; import com.smartdevicelink.protocol.enums.FrameType; -import com.smartdevicelink.protocol.enums.QueryErrorCode; -import com.smartdevicelink.protocol.enums.QueryID; -import com.smartdevicelink.protocol.enums.QueryType; +import com.smartdevicelink.protocol.enums.SecurityQueryErrorCode; +import com.smartdevicelink.protocol.enums.SecurityQueryID; +import com.smartdevicelink.protocol.enums.SecurityQueryType; import com.smartdevicelink.protocol.enums.SessionType; import com.smartdevicelink.proxy.rpc.*; import com.smartdevicelink.proxy.rpc.enums.AppServiceType; @@ -13,7 +13,6 @@ import com.smartdevicelink.proxy.rpc.enums.DefrostZone; import com.smartdevicelink.proxy.rpc.enums.FileType; import com.smartdevicelink.proxy.rpc.enums.HMILevel; import com.smartdevicelink.proxy.rpc.enums.HmiZoneCapabilities; -import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout; import com.smartdevicelink.proxy.rpc.enums.PRNDL; import com.smartdevicelink.proxy.rpc.enums.PrerecordedSpeech; import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities; @@ -129,7 +128,7 @@ public class Validator { return true; } - public static boolean validateQueryTypeArray(QueryType[] array1, QueryType[] array2) { + public static boolean validateQueryTypeArray(SecurityQueryType[] array1, SecurityQueryType[] array2) { if (array1 == null) { return (array2 == null); @@ -152,7 +151,7 @@ public class Validator { return true; } - public static boolean validateQueryIDArray(QueryID[] array1, QueryID[] array2) { + public static boolean validateQueryIDArray(SecurityQueryID[] array1, SecurityQueryID[] array2) { if (array1 == null) { return (array2 == null); @@ -175,7 +174,7 @@ public class Validator { return true; } - public static boolean validateQueryErrorCodeArray(QueryErrorCode[] array1, QueryErrorCode[] array2) { + public static boolean validateQueryErrorCodeArray(SecurityQueryErrorCode[] array1, SecurityQueryErrorCode[] array2) { if (array1 == null) { return (array2 == null); diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/SecurityQueryPayloadTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/SecurityQueryPayloadTests.java index cc0cd914a..8c0c36041 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/SecurityQueryPayloadTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/SecurityQueryPayloadTests.java @@ -3,8 +3,8 @@ package com.smartdevicelink.test.protocol; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.smartdevicelink.protocol.SecurityQueryPayload; -import com.smartdevicelink.protocol.enums.QueryID; -import com.smartdevicelink.protocol.enums.QueryType; +import com.smartdevicelink.protocol.enums.SecurityQueryID; +import com.smartdevicelink.protocol.enums.SecurityQueryType; import com.smartdevicelink.util.BitConverter; import org.junit.Test; @@ -22,8 +22,8 @@ public class SecurityQueryPayloadTests { public static SecurityQueryPayload createDummyBqh() { SecurityQueryPayload bqh = new SecurityQueryPayload(); bqh.setCorrelationID(123); - bqh.setQueryID(QueryID.SEND_HANDSHAKE_DATA); - bqh.setQueryType(QueryType.REQUEST); + bqh.setQueryID(SecurityQueryID.SEND_HANDSHAKE_DATA); + bqh.setQueryType(SecurityQueryType.REQUEST); bqh.setBulkData(null); bqh.setJsonSize(0); return bqh; @@ -54,8 +54,8 @@ public class SecurityQueryPayloadTests { array[11] = 0; SecurityQueryPayload parsedBqh = SecurityQueryPayload.parseBinaryQueryHeader(array); - assertEquals(parsedBqh.getQueryType(), QueryType.REQUEST); - assertEquals(parsedBqh.getQueryID(), QueryID.SEND_INTERNAL_ERROR); + assertEquals(parsedBqh.getQueryType(), SecurityQueryType.REQUEST); + assertEquals(parsedBqh.getQueryID(), SecurityQueryID.SEND_INTERNAL_ERROR); assertEquals(parsedBqh.getCorrelationID(), 3); assertEquals(parsedBqh.getJsonSize(), 0); } @@ -63,16 +63,16 @@ public class SecurityQueryPayloadTests { @Test public void testCorrectHeaderAssembly() { SecurityQueryPayload dummyBqh = new SecurityQueryPayload(); - dummyBqh.setQueryType(QueryType.REQUEST); - dummyBqh.setQueryID(QueryID.SEND_HANDSHAKE_DATA); + dummyBqh.setQueryType(SecurityQueryType.REQUEST); + dummyBqh.setQueryID(SecurityQueryID.SEND_HANDSHAKE_DATA); dummyBqh.setCorrelationID(3); dummyBqh.setJsonSize(0); byte[] assembledHeader = dummyBqh.assembleHeaderBytes(); - assertEquals(dummyBqh.getQueryType(), QueryType.valueOf(assembledHeader[0])); + assertEquals(dummyBqh.getQueryType(), SecurityQueryType.valueOf(assembledHeader[0])); byte[] queryIDFromHeader = new byte[3]; System.arraycopy(assembledHeader, 1, queryIDFromHeader, 0, 3); - assertEquals(dummyBqh.getQueryID(), QueryID.valueOf(queryIDFromHeader)); + assertEquals(dummyBqh.getQueryID(), SecurityQueryID.valueOf(queryIDFromHeader)); assertEquals(dummyBqh.getCorrelationID(), BitConverter.intFromByteArray(assembledHeader, 4)); assertEquals(dummyBqh.getJsonSize(), BitConverter.intFromByteArray(assembledHeader, 8)); } @@ -98,15 +98,10 @@ public class SecurityQueryPayloadTests { @Test public void testCorruptHeader() { SecurityQueryPayload bqh = createDummyBqh(); - bqh.setJsonSize(5); - bqh.setJsonData(new byte[5]); - bqh.setJsonSize(Integer.MAX_VALUE); - - assertNotEquals(bqh.getJsonData().length, bqh.getJsonSize()); byte[] bqhBytes = bqh.assembleHeaderBytes(); - assertNull(safeParse(bqhBytes)); + assertNotNull(safeParse(bqhBytes)); int size = bqhBytes.length; for (int i = 0; i < size; i++) { diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryErrorCodeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryErrorCodeTests.java deleted file mode 100644 index a3daf76af..000000000 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryErrorCodeTests.java +++ /dev/null @@ -1,189 +0,0 @@ -package com.smartdevicelink.test.protocol.enums; - -import com.smartdevicelink.protocol.enums.QueryErrorCode; -import com.smartdevicelink.test.Validator; - -import junit.framework.TestCase; - -import java.util.Vector; - -public class QueryErrorCodeTests extends TestCase { - - private Vector list = QueryErrorCode.getList(); - - public void testValidEnums() { - final byte ERROR_SUCCESS_BYTE = (byte) 0x00; - final String ERROR_SUCCESS_STRING = "ERROR_SUCCESS"; - - final byte ERROR_INVALID_QUERY_SIZE_BYTE = (byte) 0x01; - final String ERROR_INVALID_QUERY_SIZE_STRING = "ERROR_INVALID_QUERY_SIZE"; - - final byte ERROR_INVALID_QUERY_ID_BYTE = (byte) 0x02; - final String ERROR_INVALID_QUERY_ID_STRING = "ERROR_INVALID_QUERY_ID"; - - final byte ERROR_NOT_SUPPORTED_BYTE = (byte) 0x03; - final String ERROR_NOT_SUPPORTED_STRING = "ERROR_NOT_SUPPORTED"; - - final byte ERROR_SERVICE_ALREADY_PROTECTED_BYTE = (byte) 0x04; - final String ERROR_SERVICE_ALREADY_PROTECTED_STRING = "ERROR_SERVICE_ALREADY_PROTECTED"; - - final byte ERROR_SERVICE_NOT_PROTECTED_BYTE = (byte) 0x05; - final String ERROR_SERVICE_NOT_PROTECTED_STRING = "ERROR_SERVICE_NOT_PROTECTED"; - - final byte ERROR_DECRYPTION_FAILED_BYTE = (byte) 0x06; - final String ERROR_DECRYPTION_FAILED_STRING = "ERROR_DECRYPTION_FAILED"; - - final byte ERROR_ENCRYPTION_FAILED_BYTE = (byte) 0x07; - final String ERROR_ENCRYPTION_FAILED_STRING = "ERROR_ENCRYPTION_FAILED"; - - final byte ERROR_SSL_INVALID_DATA_BYTE = (byte) 0x08; - final String ERROR_SSL_INVALID_DATA_STRING = "ERROR_SSL_INVALID_DATA"; - - final byte ERROR_HANDSHAKE_FAILED_BYTE = (byte) 0x09; - final String ERROR_HANDSHAKE_FAILED_STRING = "ERROR_HANDSHAKE_FAILED"; - - final byte INVALID_CERT_BYTE = (byte) 0x0A; - final String INVALID_CERT_STRING = "INVALID_CERT"; - - final byte EXPIRED_CERT_BYTE = (byte) 0x0B; - final String EXPIRED_CERT_STRING = "EXPIRED_CERT"; - - final byte ERROR_INTERNAL_BYTE = (byte) 0xFF; - final String ERROR_INTERNAL_STRING = "ERROR_INTERNAL"; - - final byte ERROR_UNKNOWN_INTERNAL_ERROR_BYTE = (byte) 0xFE; - final String ERROR_UNKNOWN_INTERNAL_ERROR_STRING = "ERROR_UNKNOWN_INTERNAL_ERROR"; - - try { - assertNotNull("QueryErrorCode list returned null", list); - - QueryErrorCode enumSuccess = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SUCCESS_BYTE); - QueryErrorCode enumInvalidQuerySize = (QueryErrorCode) QueryErrorCode.get(list, ERROR_INVALID_QUERY_SIZE_BYTE); - QueryErrorCode enumInvalidQueryID = (QueryErrorCode) QueryErrorCode.get(list, ERROR_INVALID_QUERY_ID_BYTE); - QueryErrorCode enumNotSupported = (QueryErrorCode) QueryErrorCode.get(list, ERROR_NOT_SUPPORTED_BYTE); - QueryErrorCode enumServiceAlreadyProtected = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SERVICE_ALREADY_PROTECTED_BYTE); - QueryErrorCode enumServiceNotProtected = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SERVICE_NOT_PROTECTED_BYTE); - QueryErrorCode enumDecryptionFailed = (QueryErrorCode) QueryErrorCode.get(list, ERROR_DECRYPTION_FAILED_BYTE); - QueryErrorCode enumEncryptionFailed = (QueryErrorCode) QueryErrorCode.get(list, ERROR_ENCRYPTION_FAILED_BYTE); - QueryErrorCode enumSSLInvalidData = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SSL_INVALID_DATA_BYTE); - QueryErrorCode enumHandshakeFailed = (QueryErrorCode) QueryErrorCode.get(list, ERROR_HANDSHAKE_FAILED_BYTE); - QueryErrorCode enumInvalidCert = (QueryErrorCode) QueryErrorCode.get(list, INVALID_CERT_BYTE); - QueryErrorCode enumExpiredCert = (QueryErrorCode) QueryErrorCode.get(list, EXPIRED_CERT_BYTE); - QueryErrorCode enumInternal = (QueryErrorCode) QueryErrorCode.get(list, ERROR_INTERNAL_BYTE); - QueryErrorCode enumUnknownInternalError = (QueryErrorCode) QueryErrorCode.get(list, ERROR_UNKNOWN_INTERNAL_ERROR_BYTE); - - assertNotNull("Success byte match returned null", enumSuccess); - assertNotNull("Invalid Query Size byte match returned null", enumInvalidQuerySize); - assertNotNull("Invalid Query ID byte match returned null", enumInvalidQueryID); - assertNotNull("Not Supported byte match returned null", enumNotSupported); - assertNotNull("Service Already Protected byte match returned null", enumServiceAlreadyProtected); - assertNotNull("Service Not Protected byte match returned null", enumServiceNotProtected); - assertNotNull("Decryption Failed byte match returned null", enumDecryptionFailed); - assertNotNull("Encryption Failed byte match returned null", enumEncryptionFailed); - assertNotNull("SSL Invalid Data byte match returned null", enumSSLInvalidData); - assertNotNull("Handshake Failed byte match returned null", enumHandshakeFailed); - assertNotNull("Invalid Cert byte match returned null", enumInvalidCert); - assertNotNull("Expired Cert byte match returned null", enumExpiredCert); - assertNotNull("Internal byte match returned null", enumInternal); - assertNotNull("Unknown Internal byte match returned null", enumUnknownInternalError); - - enumSuccess = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SUCCESS_STRING); - enumInvalidQuerySize = (QueryErrorCode) QueryErrorCode.get(list, ERROR_INVALID_QUERY_SIZE_STRING); - enumInvalidQueryID = (QueryErrorCode) QueryErrorCode.get(list, ERROR_INVALID_QUERY_ID_STRING); - enumNotSupported = (QueryErrorCode) QueryErrorCode.get(list, ERROR_NOT_SUPPORTED_STRING); - enumServiceAlreadyProtected = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SERVICE_ALREADY_PROTECTED_STRING); - enumServiceNotProtected = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SERVICE_NOT_PROTECTED_STRING); - enumDecryptionFailed = (QueryErrorCode) QueryErrorCode.get(list, ERROR_DECRYPTION_FAILED_STRING); - enumEncryptionFailed = (QueryErrorCode) QueryErrorCode.get(list, ERROR_ENCRYPTION_FAILED_STRING); - enumSSLInvalidData = (QueryErrorCode) QueryErrorCode.get(list, ERROR_SSL_INVALID_DATA_STRING); - enumHandshakeFailed = (QueryErrorCode) QueryErrorCode.get(list, ERROR_HANDSHAKE_FAILED_STRING); - enumInvalidCert = (QueryErrorCode) QueryErrorCode.get(list, INVALID_CERT_STRING); - enumExpiredCert = (QueryErrorCode) QueryErrorCode.get(list, EXPIRED_CERT_STRING); - enumInternal = (QueryErrorCode) QueryErrorCode.get(list, ERROR_INTERNAL_STRING); - enumUnknownInternalError = (QueryErrorCode) QueryErrorCode.get(list, ERROR_UNKNOWN_INTERNAL_ERROR_STRING); - - assertNotNull("Success string match returned null", enumSuccess); - assertNotNull("Invalid Query Size string match returned null", enumInvalidQuerySize); - assertNotNull("Invalid Query ID string match returned null", enumInvalidQueryID); - assertNotNull("Not Supported string match returned null", enumNotSupported); - assertNotNull("Service Already Protected string match returned null", enumServiceAlreadyProtected); - assertNotNull("Service Not Protected string match returned null", enumServiceNotProtected); - assertNotNull("Decryption Failed string match returned null", enumDecryptionFailed); - assertNotNull("Encryption Failed string match returned null", enumEncryptionFailed); - assertNotNull("SSL Invalid Data string match returned null", enumSSLInvalidData); - assertNotNull("Handshake Failed string match returned null", enumHandshakeFailed); - assertNotNull("Invalid Cert string match returned null", enumInvalidCert); - assertNotNull("Expired Cert string match returned null", enumExpiredCert); - assertNotNull("Internal string match returned null", enumInternal); - assertNotNull("Unknown Internal string match returned null", enumUnknownInternalError); - } catch (NullPointerException exception) { - fail("Null enum list throws NullPointerException."); - } - } - - public void testInvalidEnum() { - final byte INVALID_BYTE = (byte) 0xAB; - final String INVALID_STRING = "Invalid"; - - try { - QueryErrorCode enumInvalid = (QueryErrorCode) QueryErrorCode.get(list, INVALID_BYTE); - assertNull("Invalid byte match didn't return null", enumInvalid); - - enumInvalid = (QueryErrorCode) QueryErrorCode.get(list, INVALID_STRING); - assertNull("Invalid byte match didn't return null", enumInvalid); - } catch (IllegalArgumentException exception) { - fail("Invalid enum throws IllegalArgumentException."); - } - } - - public void testNullEnum() { - try { - QueryErrorCode enumNull = (QueryErrorCode) QueryErrorCode.get(list, null); - assertNull("Null lookup returns a value", enumNull); - } catch (NullPointerException exception) { - fail("Null string throws NullPointerException."); - } - } - - public void testListEnum() { - Vector enumTestList = new Vector<>(); - enumTestList.add(QueryErrorCode.ERROR_SUCCESS); - enumTestList.add(QueryErrorCode.ERROR_INVALID_QUERY_SIZE); - enumTestList.add(QueryErrorCode.ERROR_INVALID_QUERY_ID); - enumTestList.add(QueryErrorCode.ERROR_NOT_SUPPORTED); - enumTestList.add(QueryErrorCode.ERROR_SERVICE_ALREADY_PROTECTED); - enumTestList.add(QueryErrorCode.ERROR_SERVICE_NOT_PROTECTED); - enumTestList.add(QueryErrorCode.ERROR_DECRYPTION_FAILED); - enumTestList.add(QueryErrorCode.ERROR_ENCRYPTION_FAILED); - enumTestList.add(QueryErrorCode.ERROR_SSL_INVALID_DATA); - enumTestList.add(QueryErrorCode.ERROR_HANDSHAKE_FAILED); - enumTestList.add(QueryErrorCode.INVALID_CERT); - enumTestList.add(QueryErrorCode.EXPIRED_CERT); - enumTestList.add(QueryErrorCode.ERROR_INTERNAL); - enumTestList.add(QueryErrorCode.ERROR_UNKNOWN_INTERNAL_ERROR); - - assertTrue("List does not match enum test list.", - list.containsAll(enumTestList) && - enumTestList.containsAll(list)); - - QueryErrorCode[] enumValueArray = QueryErrorCode.values(); - QueryErrorCode[] enumTestArray = { - QueryErrorCode.ERROR_SUCCESS, - QueryErrorCode.ERROR_INVALID_QUERY_SIZE, - QueryErrorCode.ERROR_INVALID_QUERY_ID, - QueryErrorCode.ERROR_NOT_SUPPORTED, - QueryErrorCode.ERROR_SERVICE_ALREADY_PROTECTED, - QueryErrorCode.ERROR_SERVICE_NOT_PROTECTED, - QueryErrorCode.ERROR_DECRYPTION_FAILED, - QueryErrorCode.ERROR_ENCRYPTION_FAILED, - QueryErrorCode.ERROR_SSL_INVALID_DATA, - QueryErrorCode.ERROR_HANDSHAKE_FAILED, - QueryErrorCode.INVALID_CERT, - QueryErrorCode.EXPIRED_CERT, - QueryErrorCode.ERROR_INTERNAL, - QueryErrorCode.ERROR_UNKNOWN_INTERNAL_ERROR - }; - assertTrue("Array does not match enum values array.", - Validator.validateQueryErrorCodeArray(enumValueArray, enumTestArray)); - } -} diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryIDTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryIDTests.java deleted file mode 100644 index 90118eb17..000000000 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryIDTests.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.smartdevicelink.test.protocol.enums; - -import com.smartdevicelink.protocol.enums.QueryID; -import com.smartdevicelink.test.Validator; - -import junit.framework.TestCase; - -import java.util.Vector; - -public class QueryIDTests extends TestCase { - - private Vector list = QueryID.getList(); - - public void testValidEnums() { - final byte[] SEND_HANDSHAKE_DATA_BYTES = {(byte) 0x00, (byte) 0x00, (byte) 0x01}; - final String SEND_HANDSHAKE_DATA_STRING = "SEND_HANDSHAKE_DATA"; - - final byte[] SEND_INTERNAL_ERROR_BYTES = {(byte) 0x00, (byte) 0x00, (byte) 0x02}; - final String SEND_INTERNAL_ERROR_STRING = "SEND_INTERNAL_ERROR"; - - final byte[] INVALID_QUERY_ID_BYTES = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; - final String INVALID_QUERY_ID_STRING = "INVALID_QUERY_ID"; - - try { - assertNotNull("QueryID list returned null", list); - - QueryID enumHandshakeData = (QueryID) QueryID.get(list, SEND_HANDSHAKE_DATA_BYTES); - QueryID enumInternalError = (QueryID) QueryID.get(list, SEND_INTERNAL_ERROR_BYTES); - QueryID enumInvalidQueryId = (QueryID) QueryID.get(list, INVALID_QUERY_ID_BYTES); - - assertNotNull("Send Handshake Data byte match returned null", enumHandshakeData); - assertNotNull("Send Internal Error byte match returned null", enumInternalError); - assertNotNull("Send Invalid QueryID byte match returned null", enumInvalidQueryId); - - enumHandshakeData = (QueryID) QueryID.get(list, SEND_HANDSHAKE_DATA_STRING); - enumInternalError = (QueryID) QueryID.get(list, SEND_INTERNAL_ERROR_STRING); - enumInvalidQueryId = (QueryID) QueryID.get(list, INVALID_QUERY_ID_STRING); - - assertNotNull("Send Handshake Data string match returned null", enumHandshakeData); - assertNotNull("Send Internal Error string match returned null", enumInternalError); - assertNotNull("Send Invalid QueryID string match returned null", enumInvalidQueryId); - } catch(NullPointerException exception) { - fail("Null enum list throws NullPointerException."); - } - } - - public void testInvalidEnum() { - - final byte[] INVALID_BYTE_ARRAY = {(byte) 0xAB, (byte) 0xAB, (byte) 0xAB}; - final String INVALID_STRING = "Invalid"; - - try { - QueryID enumInvalid = (QueryID) QueryID.get(list, INVALID_BYTE_ARRAY); - assertNull("Invalid byte[] match didn't return null", enumInvalid); - - enumInvalid = (QueryID) QueryID.get(list, INVALID_STRING); - assertNull("Invalid string match didn't return null", enumInvalid); - } catch (IllegalArgumentException exception) { - fail("Invalid enum throws IllegalArgumentException."); - } - } - - public void testNullEnum() { - try { - QueryID enumNull = (QueryID) QueryID.get(list, (String) null); - assertNull("Null lookup returns a null string value", enumNull); - - enumNull = (QueryID) QueryID.get(list, (byte[]) null); - assertNull("Null lookup returns a null byte[] value", enumNull); - }catch (NullPointerException exception) { - fail("Null string throws NullPointerException."); - } - } - - public void testListEnum() { - Vector enumTestList = new Vector<>(); - enumTestList.add(QueryID.SEND_HANDSHAKE_DATA); - enumTestList.add(QueryID.SEND_INTERNAL_ERROR); - enumTestList.add(QueryID.INVALID_QUERY_ID); - - assertTrue("List does not match enum test list.", - list.containsAll(enumTestList) && - enumTestList.containsAll(list)); - - QueryID[] enumValueArray = QueryID.values(); - QueryID[] enumTestArray = {QueryID.SEND_HANDSHAKE_DATA, QueryID.SEND_INTERNAL_ERROR, QueryID.INVALID_QUERY_ID}; - assertTrue("Array does not match enum values array.", - Validator.validateQueryIDArray(enumValueArray, enumTestArray)); - } - -} diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryTypeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryTypeTests.java deleted file mode 100644 index 13d606cff..000000000 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/QueryTypeTests.java +++ /dev/null @@ -1,97 +0,0 @@ -package com.smartdevicelink.test.protocol.enums; - -import com.smartdevicelink.protocol.enums.QueryType; -import com.smartdevicelink.test.Validator; - -import junit.framework.TestCase; - -import java.util.Vector; - -public class QueryTypeTests extends TestCase { - - private Vector list = QueryType.getList(); - - public void testValidEnums() { - final byte REQUEST_BYTE = (byte) 0x00; - final String REQUEST_STRING = "REQUEST"; - - final byte RESPONSE_BYTE = (byte) 0x10; - final String RESPONSE_STRING = "RESPONSE"; - - final byte NOTIFICATION_BYTE = (byte) 0x20; - final String NOTIFICATION_STRING = "NOTIFICATION"; - - final byte INVALID_QUERY_TYPE_BYTE = (byte) 0xFF; - final String INVALID_QUERY_TYPE_STRING = "INVALID_QUERY_TYPE"; - - try { - assertNotNull("QueryType list returned null", list); - - QueryType enumRequest = (QueryType) QueryType.get(list, REQUEST_BYTE); - QueryType enumResponse = (QueryType) QueryType.get(list, RESPONSE_BYTE); - QueryType enumNotification = (QueryType) QueryType.get(list, NOTIFICATION_BYTE); - QueryType enumInvalidQueryType = (QueryType) QueryType.get(list, INVALID_QUERY_TYPE_BYTE); - - assertNotNull("Request byte match returned null", enumRequest); - assertNotNull("Response byte match returned null", enumResponse); - assertNotNull("Notification byte match returned null", enumNotification); - assertNotNull("Invalid Query Type byte match returned null", enumInvalidQueryType); - - enumRequest = (QueryType) QueryType.get(list, REQUEST_STRING); - enumResponse = (QueryType) QueryType.get(list, RESPONSE_STRING); - enumNotification = (QueryType) QueryType.get(list, NOTIFICATION_STRING); - enumInvalidQueryType = (QueryType) QueryType.get(list, INVALID_QUERY_TYPE_STRING); - - assertNotNull("Request string match returned null", enumRequest); - assertNotNull("Response string match returned null", enumResponse); - assertNotNull("Notification string match returned null", enumNotification); - assertNotNull("Invalid Query string byte match returned null", enumInvalidQueryType); - - - }catch (NullPointerException exception) { - fail("Null enum list throws NullPointerException."); - } - } - - public void testInvalidEnum() { - - final byte INVALID_BYTE = (byte) 0xAB; - final String INVALID_STRING = "Invalid"; - - try { - QueryType enumInvalid = (QueryType) QueryType.get(list, INVALID_BYTE); - assertNull("Invalid byte match didn't return null", enumInvalid); - - enumInvalid = (QueryType) QueryType.get(list, INVALID_STRING); - assertNull("Invalid string match didn't return null", enumInvalid); - } catch (IllegalArgumentException exception) { - fail("Invalid enum throws IllegalArgumentException."); - } - } - - public void testNullEnum() { - try { - QueryType enumNull = (QueryType) QueryType.get(list, null); - assertNull("Null lookup returns a value", enumNull); - } catch (NullPointerException exception) { - fail("Null string throws NullPointerException."); - } - } - - public void testListEnum() { - Vector enumTestList = new Vector<>(); - enumTestList.add(QueryType.REQUEST); - enumTestList.add(QueryType.RESPONSE); - enumTestList.add(QueryType.NOTIFICATION); - enumTestList.add(QueryType.INVALID_QUERY_TYPE); - - assertTrue("List does not match enum test list.", - list.containsAll(enumTestList) && - enumTestList.containsAll(list)); - - QueryType[] enumValueArray = QueryType.values(); - QueryType[] enumTestArray = {QueryType.REQUEST, QueryType.RESPONSE, QueryType.NOTIFICATION, QueryType.INVALID_QUERY_TYPE}; - assertTrue("Array does not match enum values array.", - Validator.validateQueryTypeArray(enumValueArray, enumTestArray)); - } -} diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryErrorCodeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryErrorCodeTests.java new file mode 100644 index 000000000..0b6cd3f61 --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryErrorCodeTests.java @@ -0,0 +1,189 @@ +package com.smartdevicelink.test.protocol.enums; + +import com.smartdevicelink.protocol.enums.SecurityQueryErrorCode; +import com.smartdevicelink.test.Validator; + +import junit.framework.TestCase; + +import java.util.Vector; + +public class SecurityQueryErrorCodeTests extends TestCase { + + private Vector list = SecurityQueryErrorCode.getList(); + + public void testValidEnums() { + final byte ERROR_SUCCESS_BYTE = (byte) 0x00; + final String ERROR_SUCCESS_STRING = "ERROR_SUCCESS"; + + final byte ERROR_INVALID_QUERY_SIZE_BYTE = (byte) 0x01; + final String ERROR_INVALID_QUERY_SIZE_STRING = "ERROR_INVALID_QUERY_SIZE"; + + final byte ERROR_INVALID_QUERY_ID_BYTE = (byte) 0x02; + final String ERROR_INVALID_QUERY_ID_STRING = "ERROR_INVALID_QUERY_ID"; + + final byte ERROR_NOT_SUPPORTED_BYTE = (byte) 0x03; + final String ERROR_NOT_SUPPORTED_STRING = "ERROR_NOT_SUPPORTED"; + + final byte ERROR_SERVICE_ALREADY_PROTECTED_BYTE = (byte) 0x04; + final String ERROR_SERVICE_ALREADY_PROTECTED_STRING = "ERROR_SERVICE_ALREADY_PROTECTED"; + + final byte ERROR_SERVICE_NOT_PROTECTED_BYTE = (byte) 0x05; + final String ERROR_SERVICE_NOT_PROTECTED_STRING = "ERROR_SERVICE_NOT_PROTECTED"; + + final byte ERROR_DECRYPTION_FAILED_BYTE = (byte) 0x06; + final String ERROR_DECRYPTION_FAILED_STRING = "ERROR_DECRYPTION_FAILED"; + + final byte ERROR_ENCRYPTION_FAILED_BYTE = (byte) 0x07; + final String ERROR_ENCRYPTION_FAILED_STRING = "ERROR_ENCRYPTION_FAILED"; + + final byte ERROR_SSL_INVALID_DATA_BYTE = (byte) 0x08; + final String ERROR_SSL_INVALID_DATA_STRING = "ERROR_SSL_INVALID_DATA"; + + final byte ERROR_HANDSHAKE_FAILED_BYTE = (byte) 0x09; + final String ERROR_HANDSHAKE_FAILED_STRING = "ERROR_HANDSHAKE_FAILED"; + + final byte INVALID_CERT_BYTE = (byte) 0x0A; + final String INVALID_CERT_STRING = "INVALID_CERT"; + + final byte EXPIRED_CERT_BYTE = (byte) 0x0B; + final String EXPIRED_CERT_STRING = "EXPIRED_CERT"; + + final byte ERROR_INTERNAL_BYTE = (byte) 0xFF; + final String ERROR_INTERNAL_STRING = "ERROR_INTERNAL"; + + final byte ERROR_UNKNOWN_INTERNAL_ERROR_BYTE = (byte) 0xFE; + final String ERROR_UNKNOWN_INTERNAL_ERROR_STRING = "ERROR_UNKNOWN_INTERNAL_ERROR"; + + try { + assertNotNull("QueryErrorCode list returned null", list); + + SecurityQueryErrorCode enumSuccess = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SUCCESS_BYTE); + SecurityQueryErrorCode enumInvalidQuerySize = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_INVALID_QUERY_SIZE_BYTE); + SecurityQueryErrorCode enumInvalidQueryID = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_INVALID_QUERY_ID_BYTE); + SecurityQueryErrorCode enumNotSupported = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_NOT_SUPPORTED_BYTE); + SecurityQueryErrorCode enumServiceAlreadyProtected = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SERVICE_ALREADY_PROTECTED_BYTE); + SecurityQueryErrorCode enumServiceNotProtected = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SERVICE_NOT_PROTECTED_BYTE); + SecurityQueryErrorCode enumDecryptionFailed = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_DECRYPTION_FAILED_BYTE); + SecurityQueryErrorCode enumEncryptionFailed = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_ENCRYPTION_FAILED_BYTE); + SecurityQueryErrorCode enumSSLInvalidData = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SSL_INVALID_DATA_BYTE); + SecurityQueryErrorCode enumHandshakeFailed = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_HANDSHAKE_FAILED_BYTE); + SecurityQueryErrorCode enumInvalidCert = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, INVALID_CERT_BYTE); + SecurityQueryErrorCode enumExpiredCert = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, EXPIRED_CERT_BYTE); + SecurityQueryErrorCode enumInternal = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_INTERNAL_BYTE); + SecurityQueryErrorCode enumUnknownInternalError = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_UNKNOWN_INTERNAL_ERROR_BYTE); + + assertNotNull("Success byte match returned null", enumSuccess); + assertNotNull("Invalid Query Size byte match returned null", enumInvalidQuerySize); + assertNotNull("Invalid Query ID byte match returned null", enumInvalidQueryID); + assertNotNull("Not Supported byte match returned null", enumNotSupported); + assertNotNull("Service Already Protected byte match returned null", enumServiceAlreadyProtected); + assertNotNull("Service Not Protected byte match returned null", enumServiceNotProtected); + assertNotNull("Decryption Failed byte match returned null", enumDecryptionFailed); + assertNotNull("Encryption Failed byte match returned null", enumEncryptionFailed); + assertNotNull("SSL Invalid Data byte match returned null", enumSSLInvalidData); + assertNotNull("Handshake Failed byte match returned null", enumHandshakeFailed); + assertNotNull("Invalid Cert byte match returned null", enumInvalidCert); + assertNotNull("Expired Cert byte match returned null", enumExpiredCert); + assertNotNull("Internal byte match returned null", enumInternal); + assertNotNull("Unknown Internal byte match returned null", enumUnknownInternalError); + + enumSuccess = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SUCCESS_STRING); + enumInvalidQuerySize = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_INVALID_QUERY_SIZE_STRING); + enumInvalidQueryID = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_INVALID_QUERY_ID_STRING); + enumNotSupported = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_NOT_SUPPORTED_STRING); + enumServiceAlreadyProtected = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SERVICE_ALREADY_PROTECTED_STRING); + enumServiceNotProtected = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SERVICE_NOT_PROTECTED_STRING); + enumDecryptionFailed = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_DECRYPTION_FAILED_STRING); + enumEncryptionFailed = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_ENCRYPTION_FAILED_STRING); + enumSSLInvalidData = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_SSL_INVALID_DATA_STRING); + enumHandshakeFailed = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_HANDSHAKE_FAILED_STRING); + enumInvalidCert = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, INVALID_CERT_STRING); + enumExpiredCert = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, EXPIRED_CERT_STRING); + enumInternal = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_INTERNAL_STRING); + enumUnknownInternalError = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, ERROR_UNKNOWN_INTERNAL_ERROR_STRING); + + assertNotNull("Success string match returned null", enumSuccess); + assertNotNull("Invalid Query Size string match returned null", enumInvalidQuerySize); + assertNotNull("Invalid Query ID string match returned null", enumInvalidQueryID); + assertNotNull("Not Supported string match returned null", enumNotSupported); + assertNotNull("Service Already Protected string match returned null", enumServiceAlreadyProtected); + assertNotNull("Service Not Protected string match returned null", enumServiceNotProtected); + assertNotNull("Decryption Failed string match returned null", enumDecryptionFailed); + assertNotNull("Encryption Failed string match returned null", enumEncryptionFailed); + assertNotNull("SSL Invalid Data string match returned null", enumSSLInvalidData); + assertNotNull("Handshake Failed string match returned null", enumHandshakeFailed); + assertNotNull("Invalid Cert string match returned null", enumInvalidCert); + assertNotNull("Expired Cert string match returned null", enumExpiredCert); + assertNotNull("Internal string match returned null", enumInternal); + assertNotNull("Unknown Internal string match returned null", enumUnknownInternalError); + } catch (NullPointerException exception) { + fail("Null enum list throws NullPointerException."); + } + } + + public void testInvalidEnum() { + final byte INVALID_BYTE = (byte) 0xAB; + final String INVALID_STRING = "Invalid"; + + try { + SecurityQueryErrorCode enumInvalid = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, INVALID_BYTE); + assertNull("Invalid byte match didn't return null", enumInvalid); + + enumInvalid = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, INVALID_STRING); + assertNull("Invalid byte match didn't return null", enumInvalid); + } catch (IllegalArgumentException exception) { + fail("Invalid enum throws IllegalArgumentException."); + } + } + + public void testNullEnum() { + try { + SecurityQueryErrorCode enumNull = (SecurityQueryErrorCode) SecurityQueryErrorCode.get(list, null); + assertNull("Null lookup returns a value", enumNull); + } catch (NullPointerException exception) { + fail("Null string throws NullPointerException."); + } + } + + public void testListEnum() { + Vector enumTestList = new Vector<>(); + enumTestList.add(SecurityQueryErrorCode.ERROR_SUCCESS); + enumTestList.add(SecurityQueryErrorCode.ERROR_INVALID_QUERY_SIZE); + enumTestList.add(SecurityQueryErrorCode.ERROR_INVALID_QUERY_ID); + enumTestList.add(SecurityQueryErrorCode.ERROR_NOT_SUPPORTED); + enumTestList.add(SecurityQueryErrorCode.ERROR_SERVICE_ALREADY_PROTECTED); + enumTestList.add(SecurityQueryErrorCode.ERROR_SERVICE_NOT_PROTECTED); + enumTestList.add(SecurityQueryErrorCode.ERROR_DECRYPTION_FAILED); + enumTestList.add(SecurityQueryErrorCode.ERROR_ENCRYPTION_FAILED); + enumTestList.add(SecurityQueryErrorCode.ERROR_SSL_INVALID_DATA); + enumTestList.add(SecurityQueryErrorCode.ERROR_HANDSHAKE_FAILED); + enumTestList.add(SecurityQueryErrorCode.INVALID_CERT); + enumTestList.add(SecurityQueryErrorCode.EXPIRED_CERT); + enumTestList.add(SecurityQueryErrorCode.ERROR_INTERNAL); + enumTestList.add(SecurityQueryErrorCode.ERROR_UNKNOWN_INTERNAL_ERROR); + + assertTrue("List does not match enum test list.", + list.containsAll(enumTestList) && + enumTestList.containsAll(list)); + + SecurityQueryErrorCode[] enumValueArray = SecurityQueryErrorCode.values(); + SecurityQueryErrorCode[] enumTestArray = { + SecurityQueryErrorCode.ERROR_SUCCESS, + SecurityQueryErrorCode.ERROR_INVALID_QUERY_SIZE, + SecurityQueryErrorCode.ERROR_INVALID_QUERY_ID, + SecurityQueryErrorCode.ERROR_NOT_SUPPORTED, + SecurityQueryErrorCode.ERROR_SERVICE_ALREADY_PROTECTED, + SecurityQueryErrorCode.ERROR_SERVICE_NOT_PROTECTED, + SecurityQueryErrorCode.ERROR_DECRYPTION_FAILED, + SecurityQueryErrorCode.ERROR_ENCRYPTION_FAILED, + SecurityQueryErrorCode.ERROR_SSL_INVALID_DATA, + SecurityQueryErrorCode.ERROR_HANDSHAKE_FAILED, + SecurityQueryErrorCode.INVALID_CERT, + SecurityQueryErrorCode.EXPIRED_CERT, + SecurityQueryErrorCode.ERROR_INTERNAL, + SecurityQueryErrorCode.ERROR_UNKNOWN_INTERNAL_ERROR + }; + assertTrue("Array does not match enum values array.", + Validator.validateQueryErrorCodeArray(enumValueArray, enumTestArray)); + } +} diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryIDTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryIDTests.java new file mode 100644 index 000000000..4f73f8139 --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryIDTests.java @@ -0,0 +1,91 @@ +package com.smartdevicelink.test.protocol.enums; + +import com.smartdevicelink.protocol.enums.SecurityQueryID; +import com.smartdevicelink.test.Validator; + +import junit.framework.TestCase; + +import java.util.Vector; + +public class SecurityQueryIDTests extends TestCase { + + private Vector list = SecurityQueryID.getList(); + + public void testValidEnums() { + final byte[] SEND_HANDSHAKE_DATA_BYTES = {(byte) 0x00, (byte) 0x00, (byte) 0x01}; + final String SEND_HANDSHAKE_DATA_STRING = "SEND_HANDSHAKE_DATA"; + + final byte[] SEND_INTERNAL_ERROR_BYTES = {(byte) 0x00, (byte) 0x00, (byte) 0x02}; + final String SEND_INTERNAL_ERROR_STRING = "SEND_INTERNAL_ERROR"; + + final byte[] INVALID_QUERY_ID_BYTES = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + final String INVALID_QUERY_ID_STRING = "INVALID_QUERY_ID"; + + try { + assertNotNull("QueryID list returned null", list); + + SecurityQueryID enumHandshakeData = (SecurityQueryID) SecurityQueryID.get(list, SEND_HANDSHAKE_DATA_BYTES); + SecurityQueryID enumInternalError = (SecurityQueryID) SecurityQueryID.get(list, SEND_INTERNAL_ERROR_BYTES); + SecurityQueryID enumInvalidSecurityQueryId = (SecurityQueryID) SecurityQueryID.get(list, INVALID_QUERY_ID_BYTES); + + assertNotNull("Send Handshake Data byte match returned null", enumHandshakeData); + assertNotNull("Send Internal Error byte match returned null", enumInternalError); + assertNotNull("Send Invalid QueryID byte match returned null", enumInvalidSecurityQueryId); + + enumHandshakeData = (SecurityQueryID) SecurityQueryID.get(list, SEND_HANDSHAKE_DATA_STRING); + enumInternalError = (SecurityQueryID) SecurityQueryID.get(list, SEND_INTERNAL_ERROR_STRING); + enumInvalidSecurityQueryId = (SecurityQueryID) SecurityQueryID.get(list, INVALID_QUERY_ID_STRING); + + assertNotNull("Send Handshake Data string match returned null", enumHandshakeData); + assertNotNull("Send Internal Error string match returned null", enumInternalError); + assertNotNull("Send Invalid QueryID string match returned null", enumInvalidSecurityQueryId); + } catch(NullPointerException exception) { + fail("Null enum list throws NullPointerException."); + } + } + + public void testInvalidEnum() { + + final byte[] INVALID_BYTE_ARRAY = {(byte) 0xAB, (byte) 0xAB, (byte) 0xAB}; + final String INVALID_STRING = "Invalid"; + + try { + SecurityQueryID enumInvalid = (SecurityQueryID) SecurityQueryID.get(list, INVALID_BYTE_ARRAY); + assertNull("Invalid byte[] match didn't return null", enumInvalid); + + enumInvalid = (SecurityQueryID) SecurityQueryID.get(list, INVALID_STRING); + assertNull("Invalid string match didn't return null", enumInvalid); + } catch (IllegalArgumentException exception) { + fail("Invalid enum throws IllegalArgumentException."); + } + } + + public void testNullEnum() { + try { + SecurityQueryID enumNull = (SecurityQueryID) SecurityQueryID.get(list, (String) null); + assertNull("Null lookup returns a null string value", enumNull); + + enumNull = (SecurityQueryID) SecurityQueryID.get(list, (byte[]) null); + assertNull("Null lookup returns a null byte[] value", enumNull); + }catch (NullPointerException exception) { + fail("Null string throws NullPointerException."); + } + } + + public void testListEnum() { + Vector enumTestList = new Vector<>(); + enumTestList.add(SecurityQueryID.SEND_HANDSHAKE_DATA); + enumTestList.add(SecurityQueryID.SEND_INTERNAL_ERROR); + enumTestList.add(SecurityQueryID.INVALID_QUERY_ID); + + assertTrue("List does not match enum test list.", + list.containsAll(enumTestList) && + enumTestList.containsAll(list)); + + SecurityQueryID[] enumValueArray = SecurityQueryID.values(); + SecurityQueryID[] enumTestArray = {SecurityQueryID.SEND_HANDSHAKE_DATA, SecurityQueryID.SEND_INTERNAL_ERROR, SecurityQueryID.INVALID_QUERY_ID}; + assertTrue("Array does not match enum values array.", + Validator.validateQueryIDArray(enumValueArray, enumTestArray)); + } + +} diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryTypeTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryTypeTests.java new file mode 100644 index 000000000..6835da946 --- /dev/null +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/protocol/enums/SecurityQueryTypeTests.java @@ -0,0 +1,97 @@ +package com.smartdevicelink.test.protocol.enums; + +import com.smartdevicelink.protocol.enums.SecurityQueryType; +import com.smartdevicelink.test.Validator; + +import junit.framework.TestCase; + +import java.util.Vector; + +public class SecurityQueryTypeTests extends TestCase { + + private Vector list = SecurityQueryType.getList(); + + public void testValidEnums() { + final byte REQUEST_BYTE = (byte) 0x00; + final String REQUEST_STRING = "REQUEST"; + + final byte RESPONSE_BYTE = (byte) 0x10; + final String RESPONSE_STRING = "RESPONSE"; + + final byte NOTIFICATION_BYTE = (byte) 0x20; + final String NOTIFICATION_STRING = "NOTIFICATION"; + + final byte INVALID_QUERY_TYPE_BYTE = (byte) 0xFF; + final String INVALID_QUERY_TYPE_STRING = "INVALID_QUERY_TYPE"; + + try { + assertNotNull("QueryType list returned null", list); + + SecurityQueryType enumRequest = (SecurityQueryType) SecurityQueryType.get(list, REQUEST_BYTE); + SecurityQueryType enumResponse = (SecurityQueryType) SecurityQueryType.get(list, RESPONSE_BYTE); + SecurityQueryType enumNotification = (SecurityQueryType) SecurityQueryType.get(list, NOTIFICATION_BYTE); + SecurityQueryType enumInvalidSecurityQueryType = (SecurityQueryType) SecurityQueryType.get(list, INVALID_QUERY_TYPE_BYTE); + + assertNotNull("Request byte match returned null", enumRequest); + assertNotNull("Response byte match returned null", enumResponse); + assertNotNull("Notification byte match returned null", enumNotification); + assertNotNull("Invalid Query Type byte match returned null", enumInvalidSecurityQueryType); + + enumRequest = (SecurityQueryType) SecurityQueryType.get(list, REQUEST_STRING); + enumResponse = (SecurityQueryType) SecurityQueryType.get(list, RESPONSE_STRING); + enumNotification = (SecurityQueryType) SecurityQueryType.get(list, NOTIFICATION_STRING); + enumInvalidSecurityQueryType = (SecurityQueryType) SecurityQueryType.get(list, INVALID_QUERY_TYPE_STRING); + + assertNotNull("Request string match returned null", enumRequest); + assertNotNull("Response string match returned null", enumResponse); + assertNotNull("Notification string match returned null", enumNotification); + assertNotNull("Invalid Query string byte match returned null", enumInvalidSecurityQueryType); + + + }catch (NullPointerException exception) { + fail("Null enum list throws NullPointerException."); + } + } + + public void testInvalidEnum() { + + final byte INVALID_BYTE = (byte) 0xAB; + final String INVALID_STRING = "Invalid"; + + try { + SecurityQueryType enumInvalid = (SecurityQueryType) SecurityQueryType.get(list, INVALID_BYTE); + assertNull("Invalid byte match didn't return null", enumInvalid); + + enumInvalid = (SecurityQueryType) SecurityQueryType.get(list, INVALID_STRING); + assertNull("Invalid string match didn't return null", enumInvalid); + } catch (IllegalArgumentException exception) { + fail("Invalid enum throws IllegalArgumentException."); + } + } + + public void testNullEnum() { + try { + SecurityQueryType enumNull = (SecurityQueryType) SecurityQueryType.get(list, null); + assertNull("Null lookup returns a value", enumNull); + } catch (NullPointerException exception) { + fail("Null string throws NullPointerException."); + } + } + + public void testListEnum() { + Vector enumTestList = new Vector<>(); + enumTestList.add(SecurityQueryType.REQUEST); + enumTestList.add(SecurityQueryType.RESPONSE); + enumTestList.add(SecurityQueryType.NOTIFICATION); + enumTestList.add(SecurityQueryType.INVALID_QUERY_TYPE); + + assertTrue("List does not match enum test list.", + list.containsAll(enumTestList) && + enumTestList.containsAll(list)); + + SecurityQueryType[] enumValueArray = SecurityQueryType.values(); + SecurityQueryType[] enumTestArray = {SecurityQueryType.REQUEST, SecurityQueryType.RESPONSE, SecurityQueryType.NOTIFICATION, SecurityQueryType.INVALID_QUERY_TYPE}; + assertTrue("Array does not match enum values array.", + Validator.validateQueryTypeArray(enumValueArray, enumTestArray)); + } +} diff --git a/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java b/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java index c2553482b..c7bc1326d 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SecurityQueryPayload.java @@ -2,9 +2,9 @@ package com.smartdevicelink.protocol; import androidx.annotation.RestrictTo; -import com.smartdevicelink.protocol.enums.QueryErrorCode; -import com.smartdevicelink.protocol.enums.QueryID; -import com.smartdevicelink.protocol.enums.QueryType; +import com.smartdevicelink.protocol.enums.SecurityQueryErrorCode; +import com.smartdevicelink.protocol.enums.SecurityQueryID; +import com.smartdevicelink.protocol.enums.SecurityQueryType; import com.smartdevicelink.util.BitConverter; import com.smartdevicelink.util.DebugTool; @@ -12,53 +12,67 @@ import com.smartdevicelink.util.DebugTool; public class SecurityQueryPayload { private static final String TAG = "BinaryQueryHeader"; - private QueryType _queryType; - private QueryID _queryID; + private SecurityQueryType _securityQueryType; + private SecurityQueryID _securityQueryID; private int _correlationID; private int _jsonSize; - private QueryErrorCode _errorCode; + private SecurityQueryErrorCode _errorCode; private byte[] _jsonData = null; private byte[] _bulkData = null; + private static final int SECURITY_QUERY_HEADER_SIZE = 12; + public SecurityQueryPayload() { } public static SecurityQueryPayload parseBinaryQueryHeader(byte[] binHeader) { + if (binHeader == null || binHeader.length < SECURITY_QUERY_HEADER_SIZE) { + DebugTool.logError(TAG, "Security Payload error: not enough data to form a Security Query Header. Data length: " + (binHeader != null ? binHeader.length : "null")); + return null; + } + SecurityQueryPayload msg = new SecurityQueryPayload(); + //Set QueryType from the first 8 bits byte QUERY_Type = (byte) (binHeader[0]); - msg.setQueryType(QueryType.valueOf(QUERY_Type)); + msg.setQueryType(SecurityQueryType.valueOf(QUERY_Type)); + //Set queryID from the last 24 bits of the first 32 bits byte[] _queryID = new byte[3]; System.arraycopy(binHeader, 1, _queryID, 0, 3); - msg.setQueryID(QueryID.valueOf(_queryID)); + msg.setQueryID(SecurityQueryID.valueOf(_queryID)); + //set correlationID from the 32 bits after the first 32 bits int corrID = BitConverter.intFromByteArray(binHeader, 4); msg.setCorrelationID(corrID); + //set jsonSize from the last 32 bits after the first 64 bits int _jsonSize = BitConverter.intFromByteArray(binHeader, 8); msg.setJsonSize(_jsonSize); - if (msg.getQueryType() == QueryType.NOTIFICATION && msg.getQueryID() == QueryID.SEND_INTERNAL_ERROR) { - msg.setErrorCode(QueryErrorCode.valueOf(binHeader[binHeader.length - 1])); + //If we get an error message we want the error code from the last 8 bits + if (msg.getQueryType() == SecurityQueryType.NOTIFICATION && msg.getQueryID() == SecurityQueryID.SEND_INTERNAL_ERROR) { + msg.setErrorCode(SecurityQueryErrorCode.valueOf(binHeader[binHeader.length - 1])); } try { - if (_jsonSize > 0) { + //Get the JsonData after the header (after 96 bits) based on the jsonData size + if (_jsonSize > 0 && _jsonSize <= (binHeader.length - SECURITY_QUERY_HEADER_SIZE)) { byte[] _jsonData = new byte[_jsonSize]; - System.arraycopy(binHeader, 12, _jsonData, 0, _jsonSize); + System.arraycopy(binHeader, SECURITY_QUERY_HEADER_SIZE, _jsonData, 0, _jsonSize); msg.setJsonData(_jsonData); } - if (binHeader.length - _jsonSize - 12 > 0) { + //Get the binaryData after the header (after 96 bits) and the jsonData size + if (binHeader.length - _jsonSize - SECURITY_QUERY_HEADER_SIZE > 0) { byte[] _bulkData; - if (msg.getQueryType() == QueryType.NOTIFICATION && msg.getQueryID() == QueryID.SEND_INTERNAL_ERROR) { - _bulkData = new byte[binHeader.length - _jsonSize - 12 - 1]; + if (msg.getQueryType() == SecurityQueryType.NOTIFICATION && msg.getQueryID() == SecurityQueryID.SEND_INTERNAL_ERROR) { + _bulkData = new byte[binHeader.length - _jsonSize - SECURITY_QUERY_HEADER_SIZE - 1]; } else { - _bulkData = new byte[binHeader.length - _jsonSize - 12]; + _bulkData = new byte[binHeader.length - _jsonSize - SECURITY_QUERY_HEADER_SIZE]; } - System.arraycopy(binHeader, 12 + _jsonSize, _bulkData, 0, _bulkData.length); + System.arraycopy(binHeader, SECURITY_QUERY_HEADER_SIZE + _jsonSize, _bulkData, 0, _bulkData.length); msg.setBulkData(_bulkData); } @@ -71,28 +85,33 @@ public class SecurityQueryPayload { } public byte[] assembleHeaderBytes() { - byte[] ret = new byte[12]; - ret[0] = _queryType.getValue(); - System.arraycopy(_queryID.getValue(), 0, ret, 1, 3); + // From the properties, create a data buffer + // Query Type - first 8 bits + // Query ID - next 24 bits + // Sequence Number - next 32 bits + // JSON size - next 32 bits + byte[] ret = new byte[SECURITY_QUERY_HEADER_SIZE]; + ret[0] = _securityQueryType.getValue(); + System.arraycopy(_securityQueryID.getValue(), 0, ret, 1, 3); System.arraycopy(BitConverter.intToByteArray(_correlationID), 0, ret, 4, 4); System.arraycopy(BitConverter.intToByteArray(_jsonSize), 0, ret, 8, 4); return ret; } - public QueryType getQueryType() { - return _queryType; + public SecurityQueryType getQueryType() { + return _securityQueryType; } - public void setQueryType(QueryType _queryType) { - this._queryType = _queryType; + public void setQueryType(SecurityQueryType _securityQueryType) { + this._securityQueryType = _securityQueryType; } - public QueryID getQueryID() { - return _queryID; + public SecurityQueryID getQueryID() { + return _securityQueryID; } - public void setQueryID(QueryID _queryID) { - this._queryID = _queryID; + public void setQueryID(SecurityQueryID _securityQueryID) { + this._securityQueryID = _securityQueryID; } public int getCorrelationID() { @@ -111,11 +130,11 @@ public class SecurityQueryPayload { this._jsonSize = _jsonSize; } - public QueryErrorCode getErrorCode() { + public SecurityQueryErrorCode getErrorCode() { return _errorCode; } - public void setErrorCode(QueryErrorCode _errorCode) { + public void setErrorCode(SecurityQueryErrorCode _errorCode) { this._errorCode = _errorCode; } diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryErrorCode.java b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryErrorCode.java deleted file mode 100644 index 83f7d8552..000000000 --- a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryErrorCode.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.smartdevicelink.protocol.enums; - -import androidx.annotation.RestrictTo; - -import com.smartdevicelink.util.ByteEnumer; - -import java.util.Vector; - -@RestrictTo(RestrictTo.Scope.LIBRARY) -public class QueryErrorCode extends ByteEnumer { - - private static final Vector theList = new Vector<>(); - - public static Vector getList() { - return theList; - } - - protected QueryErrorCode(byte value, String name) { - super(value, name); - } - - public final static QueryErrorCode ERROR_SUCCESS = new QueryErrorCode((byte) 0x00, "ERROR_SUCCESS"); - public final static QueryErrorCode ERROR_INVALID_QUERY_SIZE = new QueryErrorCode((byte) 0x01, "ERROR_INVALID_QUERY_SIZE"); - public final static QueryErrorCode ERROR_INVALID_QUERY_ID = new QueryErrorCode((byte) 0x02, "ERROR_INVALID_QUERY_ID"); - public final static QueryErrorCode ERROR_NOT_SUPPORTED = new QueryErrorCode((byte) 0x03, "ERROR_NOT_SUPPORTED"); - public final static QueryErrorCode ERROR_SERVICE_ALREADY_PROTECTED = new QueryErrorCode((byte) 0x04, "ERROR_SERVICE_ALREADY_PROTECTED"); - public final static QueryErrorCode ERROR_SERVICE_NOT_PROTECTED = new QueryErrorCode((byte) 0x05, "ERROR_SERVICE_NOT_PROTECTED"); - public final static QueryErrorCode ERROR_DECRYPTION_FAILED = new QueryErrorCode((byte) 0x06, "ERROR_DECRYPTION_FAILED"); - public final static QueryErrorCode ERROR_ENCRYPTION_FAILED = new QueryErrorCode((byte) 0x07, "ERROR_ENCRYPTION_FAILED"); - public final static QueryErrorCode ERROR_SSL_INVALID_DATA = new QueryErrorCode((byte) 0x08, "ERROR_SSL_INVALID_DATA"); - public final static QueryErrorCode ERROR_HANDSHAKE_FAILED = new QueryErrorCode((byte) 0x09, "ERROR_HANDSHAKE_FAILED"); - public final static QueryErrorCode INVALID_CERT = new QueryErrorCode((byte) 0x0A, "INVALID_CERT"); - public final static QueryErrorCode EXPIRED_CERT = new QueryErrorCode((byte) 0x0B, "EXPIRED_CERT"); - public final static QueryErrorCode ERROR_INTERNAL = new QueryErrorCode((byte) 0xFF, "ERROR_INTERNAL"); - public final static QueryErrorCode ERROR_UNKNOWN_INTERNAL_ERROR = new QueryErrorCode((byte) 0xFE, "ERROR_UNKNOWN_INTERNAL_ERROR"); - - static { - theList.addElement(ERROR_SUCCESS); - theList.addElement(ERROR_INVALID_QUERY_SIZE); - theList.addElement(ERROR_INVALID_QUERY_ID); - theList.addElement(ERROR_NOT_SUPPORTED); - theList.addElement(ERROR_SERVICE_ALREADY_PROTECTED); - theList.addElement(ERROR_SERVICE_NOT_PROTECTED); - theList.addElement(ERROR_DECRYPTION_FAILED); - theList.addElement(ERROR_ENCRYPTION_FAILED); - theList.addElement(ERROR_SSL_INVALID_DATA); - theList.addElement(ERROR_HANDSHAKE_FAILED); - theList.addElement(INVALID_CERT); - theList.addElement(EXPIRED_CERT); - theList.addElement(ERROR_INTERNAL); - theList.addElement(ERROR_UNKNOWN_INTERNAL_ERROR); - } - - public static QueryErrorCode valueOf(byte passedByte) { - return (QueryErrorCode) get(theList, passedByte); - } - - public static QueryErrorCode[] values() { - return theList.toArray(new QueryErrorCode[theList.size()]); - } -} diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java deleted file mode 100644 index da1e3fab3..000000000 --- a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryID.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.smartdevicelink.protocol.enums; - -import androidx.annotation.RestrictTo; - -import com.smartdevicelink.util.BitConverter; - -import java.util.Arrays; -import java.util.Enumeration; -import java.util.Objects; -import java.util.Vector; - -@RestrictTo(RestrictTo.Scope.LIBRARY) -public class QueryID { - - private static final Vector theList = new Vector<>(); - - public static Vector getList() { - return theList; - } - - private static final byte[] sendHandshakeDataByteArray = {(byte) 0x00, (byte) 0x00, (byte) 0x01}; - private static final byte[] sendInternalErrorByteArray = {(byte) 0x00, (byte) 0x00, (byte) 0x02}; - private static final byte[] invalidQueryIdByteArray = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; - public final static QueryID SEND_HANDSHAKE_DATA = new QueryID(sendHandshakeDataByteArray, "SEND_HANDSHAKE_DATA"); - public final static QueryID SEND_INTERNAL_ERROR = new QueryID(sendInternalErrorByteArray, "SEND_INTERNAL_ERROR"); - public final static QueryID INVALID_QUERY_ID = new QueryID(invalidQueryIdByteArray, "INVALID_QUERY_ID"); - - static { - theList.addElement(SEND_HANDSHAKE_DATA); - theList.addElement(SEND_INTERNAL_ERROR); - theList.addElement(INVALID_QUERY_ID); - } - - protected QueryID(byte[] value, String name) { - this.value = value; - this.name = name; - } - - private final byte[] value; - private final String name; - - public byte[] getValue() { - return value; - } - - public int getIntValue() { - byte[] copy = new byte[4]; - System.arraycopy(value, 0, copy, 1, 3); - return BitConverter.intFromByteArray(copy, 0); - } - - public String getName() { - return name; - } - - public boolean equals(QueryID other) { - return Objects.equals(name, other.getName()); - } - - public boolean eq(QueryID other) { - return equals(other); - } - - public byte[] value() { - return value; - } - - public static QueryID get(Vector theList, byte[] value) { - Enumeration enumer = theList.elements(); - while (enumer.hasMoreElements()) { - try { - QueryID current = (QueryID) enumer.nextElement(); - if (Arrays.equals(current.getValue(), value)) { - return current; - } - } catch (ClassCastException e) { - return null; - } - } - return null; - } - - public static QueryID get(Vector theList, String name) { - Enumeration enumer = theList.elements(); - while (enumer.hasMoreElements()) { - try { - QueryID current = (QueryID) enumer.nextElement(); - if (current.getName().equals(name)) { - return current; - } - } catch (ClassCastException e) { - return null; - } - } - return null; - } - - public static QueryID valueOf(byte[] passedByteArray) { - return (QueryID) get(theList, passedByteArray); - } - - public static QueryID[] values() { - return theList.toArray(new QueryID[theList.size()]); - } -} diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryType.java b/base/src/main/java/com/smartdevicelink/protocol/enums/QueryType.java deleted file mode 100644 index ce26e005b..000000000 --- a/base/src/main/java/com/smartdevicelink/protocol/enums/QueryType.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.smartdevicelink.protocol.enums; - -import androidx.annotation.RestrictTo; - -import com.smartdevicelink.util.ByteEnumer; - -import java.util.Vector; - -@RestrictTo(RestrictTo.Scope.LIBRARY) -public class QueryType extends ByteEnumer { - - private static final Vector theList = new Vector<>(); - - public static Vector getList() { - return theList; - } - - protected QueryType(byte value, String name) { - super(value, name); - } - - public final static QueryType REQUEST = new QueryType((byte) 0x00, "REQUEST"); - public final static QueryType RESPONSE = new QueryType((byte) 0x10, "RESPONSE"); - public final static QueryType NOTIFICATION = new QueryType((byte) 0x20, "NOTIFICATION"); - public final static QueryType INVALID_QUERY_TYPE = new QueryType((byte) 0xFF, "INVALID_QUERY_TYPE"); - - static { - theList.addElement(REQUEST); - theList.addElement(RESPONSE); - theList.addElement(NOTIFICATION); - theList.addElement(INVALID_QUERY_TYPE); - } - - public static QueryType valueOf(byte passedByte) { - return (QueryType) get(theList, passedByte); - } - - public static QueryType[] values() { - return theList.toArray(new QueryType[theList.size()]); - } -} \ No newline at end of file diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryErrorCode.java b/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryErrorCode.java new file mode 100644 index 000000000..5601271d3 --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryErrorCode.java @@ -0,0 +1,61 @@ +package com.smartdevicelink.protocol.enums; + +import androidx.annotation.RestrictTo; + +import com.smartdevicelink.util.ByteEnumer; + +import java.util.Vector; + +@RestrictTo(RestrictTo.Scope.LIBRARY) +public class SecurityQueryErrorCode extends ByteEnumer { + + private static final Vector theList = new Vector<>(); + + public static Vector getList() { + return theList; + } + + protected SecurityQueryErrorCode(byte value, String name) { + super(value, name); + } + + public final static SecurityQueryErrorCode ERROR_SUCCESS = new SecurityQueryErrorCode((byte) 0x00, "ERROR_SUCCESS"); + public final static SecurityQueryErrorCode ERROR_INVALID_QUERY_SIZE = new SecurityQueryErrorCode((byte) 0x01, "ERROR_INVALID_QUERY_SIZE"); + public final static SecurityQueryErrorCode ERROR_INVALID_QUERY_ID = new SecurityQueryErrorCode((byte) 0x02, "ERROR_INVALID_QUERY_ID"); + public final static SecurityQueryErrorCode ERROR_NOT_SUPPORTED = new SecurityQueryErrorCode((byte) 0x03, "ERROR_NOT_SUPPORTED"); + public final static SecurityQueryErrorCode ERROR_SERVICE_ALREADY_PROTECTED = new SecurityQueryErrorCode((byte) 0x04, "ERROR_SERVICE_ALREADY_PROTECTED"); + public final static SecurityQueryErrorCode ERROR_SERVICE_NOT_PROTECTED = new SecurityQueryErrorCode((byte) 0x05, "ERROR_SERVICE_NOT_PROTECTED"); + public final static SecurityQueryErrorCode ERROR_DECRYPTION_FAILED = new SecurityQueryErrorCode((byte) 0x06, "ERROR_DECRYPTION_FAILED"); + public final static SecurityQueryErrorCode ERROR_ENCRYPTION_FAILED = new SecurityQueryErrorCode((byte) 0x07, "ERROR_ENCRYPTION_FAILED"); + public final static SecurityQueryErrorCode ERROR_SSL_INVALID_DATA = new SecurityQueryErrorCode((byte) 0x08, "ERROR_SSL_INVALID_DATA"); + public final static SecurityQueryErrorCode ERROR_HANDSHAKE_FAILED = new SecurityQueryErrorCode((byte) 0x09, "ERROR_HANDSHAKE_FAILED"); + public final static SecurityQueryErrorCode INVALID_CERT = new SecurityQueryErrorCode((byte) 0x0A, "INVALID_CERT"); + public final static SecurityQueryErrorCode EXPIRED_CERT = new SecurityQueryErrorCode((byte) 0x0B, "EXPIRED_CERT"); + public final static SecurityQueryErrorCode ERROR_INTERNAL = new SecurityQueryErrorCode((byte) 0xFF, "ERROR_INTERNAL"); + public final static SecurityQueryErrorCode ERROR_UNKNOWN_INTERNAL_ERROR = new SecurityQueryErrorCode((byte) 0xFE, "ERROR_UNKNOWN_INTERNAL_ERROR"); + + static { + theList.addElement(ERROR_SUCCESS); + theList.addElement(ERROR_INVALID_QUERY_SIZE); + theList.addElement(ERROR_INVALID_QUERY_ID); + theList.addElement(ERROR_NOT_SUPPORTED); + theList.addElement(ERROR_SERVICE_ALREADY_PROTECTED); + theList.addElement(ERROR_SERVICE_NOT_PROTECTED); + theList.addElement(ERROR_DECRYPTION_FAILED); + theList.addElement(ERROR_ENCRYPTION_FAILED); + theList.addElement(ERROR_SSL_INVALID_DATA); + theList.addElement(ERROR_HANDSHAKE_FAILED); + theList.addElement(INVALID_CERT); + theList.addElement(EXPIRED_CERT); + theList.addElement(ERROR_INTERNAL); + theList.addElement(ERROR_UNKNOWN_INTERNAL_ERROR); + } + + public static SecurityQueryErrorCode valueOf(byte passedByte) { + return (SecurityQueryErrorCode) get(theList, passedByte); + } + + public static SecurityQueryErrorCode[] values() { + return theList.toArray(new SecurityQueryErrorCode[theList.size()]); + } +} diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryID.java b/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryID.java new file mode 100644 index 000000000..c1b799ec9 --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryID.java @@ -0,0 +1,105 @@ +package com.smartdevicelink.protocol.enums; + +import androidx.annotation.RestrictTo; + +import com.smartdevicelink.util.BitConverter; + +import java.util.Arrays; +import java.util.Enumeration; +import java.util.Objects; +import java.util.Vector; + +@RestrictTo(RestrictTo.Scope.LIBRARY) +public class SecurityQueryID { + + private static final Vector theList = new Vector<>(); + + public static Vector getList() { + return theList; + } + + private static final byte[] sendHandshakeDataByteArray = {(byte) 0x00, (byte) 0x00, (byte) 0x01}; + private static final byte[] sendInternalErrorByteArray = {(byte) 0x00, (byte) 0x00, (byte) 0x02}; + private static final byte[] invalidQueryIdByteArray = {(byte) 0xFF, (byte) 0xFF, (byte) 0xFF}; + public final static SecurityQueryID SEND_HANDSHAKE_DATA = new SecurityQueryID(sendHandshakeDataByteArray, "SEND_HANDSHAKE_DATA"); + public final static SecurityQueryID SEND_INTERNAL_ERROR = new SecurityQueryID(sendInternalErrorByteArray, "SEND_INTERNAL_ERROR"); + public final static SecurityQueryID INVALID_QUERY_ID = new SecurityQueryID(invalidQueryIdByteArray, "INVALID_QUERY_ID"); + + static { + theList.addElement(SEND_HANDSHAKE_DATA); + theList.addElement(SEND_INTERNAL_ERROR); + theList.addElement(INVALID_QUERY_ID); + } + + protected SecurityQueryID(byte[] value, String name) { + this.value = value; + this.name = name; + } + + private final byte[] value; + private final String name; + + public byte[] getValue() { + return value; + } + + public int getIntValue() { + byte[] copy = new byte[4]; + System.arraycopy(value, 0, copy, 1, 3); + return BitConverter.intFromByteArray(copy, 0); + } + + public String getName() { + return name; + } + + public boolean equals(SecurityQueryID other) { + return Objects.equals(name, other.getName()); + } + + public boolean eq(SecurityQueryID other) { + return equals(other); + } + + public byte[] value() { + return value; + } + + public static SecurityQueryID get(Vector theList, byte[] value) { + Enumeration enumer = theList.elements(); + while (enumer.hasMoreElements()) { + try { + SecurityQueryID current = (SecurityQueryID) enumer.nextElement(); + if (Arrays.equals(current.getValue(), value)) { + return current; + } + } catch (ClassCastException e) { + return null; + } + } + return null; + } + + public static SecurityQueryID get(Vector theList, String name) { + Enumeration enumer = theList.elements(); + while (enumer.hasMoreElements()) { + try { + SecurityQueryID current = (SecurityQueryID) enumer.nextElement(); + if (current.getName().equals(name)) { + return current; + } + } catch (ClassCastException e) { + return null; + } + } + return null; + } + + public static SecurityQueryID valueOf(byte[] passedByteArray) { + return (SecurityQueryID) get(theList, passedByteArray); + } + + public static SecurityQueryID[] values() { + return theList.toArray(new SecurityQueryID[theList.size()]); + } +} diff --git a/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryType.java b/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryType.java new file mode 100644 index 000000000..bb021e79c --- /dev/null +++ b/base/src/main/java/com/smartdevicelink/protocol/enums/SecurityQueryType.java @@ -0,0 +1,41 @@ +package com.smartdevicelink.protocol.enums; + +import androidx.annotation.RestrictTo; + +import com.smartdevicelink.util.ByteEnumer; + +import java.util.Vector; + +@RestrictTo(RestrictTo.Scope.LIBRARY) +public class SecurityQueryType extends ByteEnumer { + + private static final Vector theList = new Vector<>(); + + public static Vector getList() { + return theList; + } + + protected SecurityQueryType(byte value, String name) { + super(value, name); + } + + public final static SecurityQueryType REQUEST = new SecurityQueryType((byte) 0x00, "REQUEST"); + public final static SecurityQueryType RESPONSE = new SecurityQueryType((byte) 0x10, "RESPONSE"); + public final static SecurityQueryType NOTIFICATION = new SecurityQueryType((byte) 0x20, "NOTIFICATION"); + public final static SecurityQueryType INVALID_QUERY_TYPE = new SecurityQueryType((byte) 0xFF, "INVALID_QUERY_TYPE"); + + static { + theList.addElement(REQUEST); + theList.addElement(RESPONSE); + theList.addElement(NOTIFICATION); + theList.addElement(INVALID_QUERY_TYPE); + } + + public static SecurityQueryType valueOf(byte passedByte) { + return (SecurityQueryType) get(theList, passedByte); + } + + public static SecurityQueryType[] values() { + return theList.toArray(new SecurityQueryType[theList.size()]); + } +} \ No newline at end of file diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index 276e29ae2..50e45fe8a 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -43,8 +43,8 @@ import com.smartdevicelink.protocol.ProtocolMessage; import com.smartdevicelink.protocol.SdlPacket; import com.smartdevicelink.protocol.SdlProtocolBase; import com.smartdevicelink.protocol.enums.ControlFrameTags; -import com.smartdevicelink.protocol.enums.QueryID; -import com.smartdevicelink.protocol.enums.QueryType; +import com.smartdevicelink.protocol.enums.SecurityQueryID; +import com.smartdevicelink.protocol.enums.SecurityQueryType; import com.smartdevicelink.protocol.enums.SessionType; import com.smartdevicelink.proxy.RPCMessage; import com.smartdevicelink.proxy.rpc.VehicleType; @@ -188,12 +188,18 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ protected void processControlService(ProtocolMessage msg) { - if (sdlSecurity == null) + if (sdlSecurity == null || msg.getData() == null) return; + + if (msg.getData().length < 12) { + DebugTool.logError(TAG, "Security message is malformed, less than 12 bytes. It does not have a security payload header."); + } + // Check the client's message header for any internal errors + // NOTE: Before Core v8.0.0, all these messages will be notifications. In Core v8.0.0 and later, received messages will have the proper query type. Therefore, we cannot do things based only on the query type being request or response. SecurityQueryPayload receivedHeader = SecurityQueryPayload.parseBinaryQueryHeader(msg.getData().clone()); if (receivedHeader == null) { - DebugTool.logError(TAG, "Malformed Security Query Header"); + DebugTool.logError(TAG, "Module Security Query could not convert to object."); return; } @@ -205,34 +211,32 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ Integer iNumBytes = null; - if (receivedHeader.getQueryID() == QueryID.SEND_INTERNAL_ERROR - && receivedHeader.getQueryType() == QueryType.NOTIFICATION) { + // If the query is of type `Notification` and the id represents a client internal error, we abort the response message and the encryptionManager will not be in state ready. + if (receivedHeader.getQueryID() == SecurityQueryID.SEND_INTERNAL_ERROR + && receivedHeader.getQueryType() == SecurityQueryType.NOTIFICATION) { if (receivedHeader.getErrorCode() != null) { - DebugTool.logError(TAG, "Client internal error: " + receivedHeader.getErrorCode().getName()); + DebugTool.logError(TAG, "Security Query module internal error:" + receivedHeader.getErrorCode().getName()); } else { - DebugTool.logError(TAG, "Client internal error: No information provided"); + DebugTool.logError(TAG, "Security Query module error: No information provided"); } return; } - if (receivedHeader.getQueryID() != QueryID.SEND_HANDSHAKE_DATA - && (receivedHeader.getQueryType() != QueryType.NOTIFICATION || receivedHeader.getQueryType() != QueryType.REQUEST)) { - return; - } - iNumBytes = sdlSecurity.runHandshake(data, dataToRead); + + // Assemble a security query payload header for our response SecurityQueryPayload responseHeader = new SecurityQueryPayload(); if (iNumBytes == null || iNumBytes <= 0) { DebugTool.logError(TAG, "Internal Error processing control service"); - responseHeader.setQueryID(QueryID.SEND_INTERNAL_ERROR); - responseHeader.setQueryType(QueryType.NOTIFICATION); + responseHeader.setQueryID(SecurityQueryID.SEND_INTERNAL_ERROR); + responseHeader.setQueryType(SecurityQueryType.NOTIFICATION); responseHeader.setCorrelationID(msg.getCorrID()); responseHeader.setJsonSize(0); } else { - responseHeader.setQueryID(QueryID.SEND_HANDSHAKE_DATA); - responseHeader.setQueryType(QueryType.RESPONSE); + responseHeader.setQueryID(SecurityQueryID.SEND_HANDSHAKE_DATA); + responseHeader.setQueryType(SecurityQueryType.RESPONSE); responseHeader.setCorrelationID(msg.getCorrID()); responseHeader.setJsonSize(0); } -- cgit v1.2.1 From 014a5e4de92e32f0b8da146dec3ba9e30c4e5040 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 2 Sep 2021 09:28:15 -0400 Subject: Add check for handshake data back in --- base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index 50e45fe8a..ee1f8133a 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -222,6 +222,12 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ return; } + if (receivedHeader.getQueryID() != SecurityQueryID.SEND_HANDSHAKE_DATA + || !(receivedHeader.getQueryType() == SecurityQueryType.REQUEST || receivedHeader.getQueryType() == SecurityQueryType.NOTIFICATION)) { + DebugTool.logError(TAG, "Security Query module error: Message is not a SEND_HANDSHAKE_DATA REQUEST"); + return; + } + iNumBytes = sdlSecurity.runHandshake(data, dataToRead); // Assemble a security query payload header for our response -- cgit v1.2.1 From cd00bb13000aadb673b192758fcd6015b1bee615 Mon Sep 17 00:00:00 2001 From: Robert Henigan Date: Fri, 3 Sep 2021 09:36:35 -0400 Subject: Update base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java Co-authored-by: Julian Kast --- base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index ee1f8133a..42459dfd5 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -215,7 +215,7 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ if (receivedHeader.getQueryID() == SecurityQueryID.SEND_INTERNAL_ERROR && receivedHeader.getQueryType() == SecurityQueryType.NOTIFICATION) { if (receivedHeader.getErrorCode() != null) { - DebugTool.logError(TAG, "Security Query module internal error:" + receivedHeader.getErrorCode().getName()); + DebugTool.logError(TAG, "Security Query module internal error: " + receivedHeader.getErrorCode().getName()); } else { DebugTool.logError(TAG, "Security Query module error: No information provided"); } -- cgit v1.2.1 From be3c7155492ab22ce5e462e0fe42b5b2fecfcf7e Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 3 Sep 2021 13:59:11 -0400 Subject: Update choiceId functionality --- .../choiceset/PreloadPresentChoicesOperation.java | 85 ++++++++++++++++++---- 1 file changed, 71 insertions(+), 14 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 22c463aa3..e1c6064c1 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -98,6 +98,7 @@ class PreloadPresentChoicesOperation extends Task { private final ChoiceSet choiceSet; private static Integer choiceId = 0; private static Boolean reachedMaxIds = false; + private static final int MAX_CHOICE_ID = 65535; private final Integer cancelID; private final InteractionMode presentationMode; private final KeyboardProperties originalKeyboardProperties; @@ -179,9 +180,21 @@ class PreloadPresentChoicesOperation extends Task { if (this.getState() == CANCELED) { return; } + + if (this.loadedCells == null || this.loadedCells.isEmpty()) { + choiceId = 0; + reachedMaxIds = false; + } + DebugTool.logInfo(TAG, "Choice Operation: Executing preload choices operation"); // Enforce unique cells and remove cells that are already loaded this.cellsToUpload.removeAll(loadedCells); + + if ((this.loadedCells.size() == MAX_CHOICE_ID) && this.cellsToUpload.size() > 0) { + DebugTool.logError(TAG, "Choice Cells to upload exceed maximum"); + finishOperation(false); + } + this.assignIdsToCells(this.cellsToUpload); makeCellsToUploadUnique(this.cellsToUpload); @@ -522,30 +535,74 @@ class PreloadPresentChoicesOperation extends Task { } private void assignIdsToCells(ArrayList cells) { - for (ChoiceCell cell : cells) { - cell.setChoiceId(this.nextChoiceId()); + ArrayList usedIds = new ArrayList<>(); + for (ChoiceCell cell : loadedCells) { + usedIds.add(cell.getChoiceId()); + } + Collections.sort(usedIds); + ArrayList sortedUsedIds = new ArrayList<>(usedIds); + + //Loop through the cells we need ids for. Get and assign those ids + for (int i = 0; i < cells.size(); i++) { + int cellId = nextChoiceIdBasedOnUsedIds(sortedUsedIds); + cells.get(i).setChoiceId(cellId); + + //Insert the ids into the usedIds sorted arrat in the correct position + for (int j = 0; j < sortedUsedIds.size(); j++) { + if (sortedUsedIds.get(j) > cellId) { + sortedUsedIds.add(j, cellId); + break; + } else if (j == (sortedUsedIds.size() - 1)) { + sortedUsedIds.add(cellId); + break; + } + } } } - private void setNextChoiceId(int nextChoiceId) { - choiceId = nextChoiceId; - } - - private int nextChoiceId() { - if (choiceId == 65535) { + //Find the next available choice is. Takes into account the loaded cells to ensure there are not duplicates + // @param usedIds The already loaded cell ids + // @return The choice id between 0 - 65535, or Not Found if no cell ids were available + private int nextChoiceIdBasedOnUsedIds(ArrayList usedIds) { + //Check if we are entirely full, or if we've advanced beyond the max value, loop back + if (choiceId == MAX_CHOICE_ID) { choiceId = 0; reachedMaxIds = true; } + if (reachedMaxIds) { - ArrayList usedIds = new ArrayList<>(); - for (ChoiceCell cell : loadedCells) { - usedIds.add(cell.getChoiceId()); + // We've looped all the way around, so we need to check loaded cells + // Sort the set of cells by the choice id so that we can more easily check which slots are available + if (usedIds.size() >= (MAX_CHOICE_ID + 1)) { + //If we've maxed out our slots, return the max value + choiceId = MAX_CHOICE_ID; + return choiceId; } - while (usedIds.contains(choiceId + 1)) { - ++choiceId; + + //If the last value isn't the max value, just keep grabbing towards the max value + int lastUsedId = usedIds.get(usedIds.size() - 1); + if (lastUsedId < MAX_CHOICE_ID) { + choiceId = lastUsedId + 1; + return choiceId; } + + //All our easy options are gone. Find and grab an empty slot from within the sorted list + for (int i = 0; i < usedIds.size(); i++) { + int loopId = usedIds.get(i); + if (i != loopId) { + //This slot is open because the cell id does not match an open sorted slot + choiceId = i; + return choiceId; + } + } + + //This *shouldn't* be possible + choiceId = MAX_CHOICE_ID; + return choiceId; + } else { + //We haven't looped all the way around yet, so we'll just take the current number, then advance the item + return choiceId++; } - return ++choiceId; } // Choice Uniqueness -- cgit v1.2.1 From 0c711c849ec7e67bd235391afc1a63e325e9f985 Mon Sep 17 00:00:00 2001 From: Henigan Date: Tue, 7 Sep 2021 10:13:14 -0400 Subject: Cleanup error messaging around queryid and type --- .../src/main/java/com/smartdevicelink/session/BaseSdlSession.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index 42459dfd5..55df1f683 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -222,12 +222,16 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ return; } - if (receivedHeader.getQueryID() != SecurityQueryID.SEND_HANDSHAKE_DATA - || !(receivedHeader.getQueryType() == SecurityQueryType.REQUEST || receivedHeader.getQueryType() == SecurityQueryType.NOTIFICATION)) { + if (receivedHeader.getQueryID() != SecurityQueryID.SEND_HANDSHAKE_DATA) { DebugTool.logError(TAG, "Security Query module error: Message is not a SEND_HANDSHAKE_DATA REQUEST"); return; } + if (receivedHeader.getQueryType() == SecurityQueryType.RESPONSE) { + DebugTool.logError(TAG, "Security Query module error: Message is a response, which is not supported"); + return; + } + iNumBytes = sdlSecurity.runHandshake(data, dataToRead); // Assemble a security query payload header for our response -- cgit v1.2.1 From 925e570ac58ea0adf1a46ad697a97fdc8b9fc845 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 9 Sep 2021 14:17:52 -0400 Subject: Clean up method to update list in place --- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index e1c6064c1..e8e9d871f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -613,7 +613,7 @@ class PreloadPresentChoicesOperation extends Task { ArrayList strippedCellsToUpload = (ArrayList) cellsToUpload.clone(); ArrayList strippedLoadedCells = new ArrayList<>((HashSet) loadedCells.clone()); - boolean supportsChoiceUniqueness = (sdlMsgVersion.getMajorVersion() >= 7 && sdlMsgVersion.getMinorVersion() >= 1); + boolean supportsChoiceUniqueness = !(sdlMsgVersion.getMinorVersion() < 7 || (sdlMsgVersion.getMajorVersion() == 7 && sdlMsgVersion.getMinorVersion() == 0)); if (supportsChoiceUniqueness) { removeUnusedProperties(strippedCellsToUpload); removeUnusedProperties(strippedLoadedCells); @@ -639,7 +639,7 @@ class PreloadPresentChoicesOperation extends Task { } } - List removeUnusedProperties(List choiceCells) { + void removeUnusedProperties(List choiceCells) { List strippedCellsClone = cloneChoiceCellList(choiceCells); // Clone Cells for (ChoiceCell cell : strippedCellsClone) { @@ -659,7 +659,6 @@ class PreloadPresentChoicesOperation extends Task { cell.setSecondaryArtwork(null); } } - return strippedCellsClone; } private List cloneChoiceCellList(List originalList) { -- cgit v1.2.1 From 43e92462b9c146603ef9a13c544c75812228e0d0 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 9 Sep 2021 15:35:38 -0400 Subject: Clone before stripping cells --- .../screen/choiceset/PreloadPresentChoicesOperation.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index e8e9d871f..3750a8155 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -611,8 +611,8 @@ class PreloadPresentChoicesOperation extends Task { return; } - ArrayList strippedCellsToUpload = (ArrayList) cellsToUpload.clone(); - ArrayList strippedLoadedCells = new ArrayList<>((HashSet) loadedCells.clone()); + ArrayList strippedCellsToUpload = cloneChoiceCellList(cellsToUpload); + ArrayList strippedLoadedCells = cloneChoiceCellList(new ArrayList<>(loadedCells)); boolean supportsChoiceUniqueness = !(sdlMsgVersion.getMinorVersion() < 7 || (sdlMsgVersion.getMajorVersion() == 7 && sdlMsgVersion.getMinorVersion() == 0)); if (supportsChoiceUniqueness) { removeUnusedProperties(strippedCellsToUpload); @@ -640,9 +640,7 @@ class PreloadPresentChoicesOperation extends Task { } void removeUnusedProperties(List choiceCells) { - List strippedCellsClone = cloneChoiceCellList(choiceCells); - // Clone Cells - for (ChoiceCell cell : strippedCellsClone) { + for (ChoiceCell cell : choiceCells) { // Strip away fields that cannot be used to determine uniqueness visually including fields not supported by the HMI cell.setVoiceCommands(null); @@ -661,11 +659,11 @@ class PreloadPresentChoicesOperation extends Task { } } - private List cloneChoiceCellList(List originalList) { + private ArrayList cloneChoiceCellList(List originalList) { if (originalList == null) { return null; } - List clone = new ArrayList<>(); + ArrayList clone = new ArrayList<>(); for (ChoiceCell choiceCell : originalList) { clone.add(choiceCell.clone()); } -- cgit v1.2.1 From 1f0cad6474023adb1ecca30f8339d26b78714aaf Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 9 Sep 2021 17:01:47 -0400 Subject: Clean up helper methods --- .../choiceset/PreloadPresentChoicesOperation.java | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 3750a8155..4805cbb03 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -195,8 +195,8 @@ class PreloadPresentChoicesOperation extends Task { finishOperation(false); } - this.assignIdsToCells(this.cellsToUpload); - makeCellsToUploadUnique(this.cellsToUpload); + assignIdsToCells(); + makeCellsToUploadUnique(); if (this.choiceSet != null) { updateChoiceSet(this.choiceSet, this.loadedCells, new HashSet<>(this.cellsToUpload)); @@ -534,20 +534,20 @@ class PreloadPresentChoicesOperation extends Task { return pi; } - private void assignIdsToCells(ArrayList cells) { + private void assignIdsToCells() { ArrayList usedIds = new ArrayList<>(); for (ChoiceCell cell : loadedCells) { usedIds.add(cell.getChoiceId()); } Collections.sort(usedIds); - ArrayList sortedUsedIds = new ArrayList<>(usedIds); + ArrayList sortedUsedIds = (ArrayList) usedIds.clone(); //Loop through the cells we need ids for. Get and assign those ids - for (int i = 0; i < cells.size(); i++) { + for (int i = 0; i < this.cellsToUpload.size(); i++) { int cellId = nextChoiceIdBasedOnUsedIds(sortedUsedIds); - cells.get(i).setChoiceId(cellId); + this.cellsToUpload.get(i).setChoiceId(cellId); - //Insert the ids into the usedIds sorted arrat in the correct position + //Insert the ids into the usedIds sorted array in the correct position for (int j = 0; j < sortedUsedIds.size(); j++) { if (sortedUsedIds.get(j) > cellId) { sortedUsedIds.add(j, cellId); @@ -606,12 +606,12 @@ class PreloadPresentChoicesOperation extends Task { } // Choice Uniqueness - void makeCellsToUploadUnique(ArrayList cellsToUpload) { - if (cellsToUpload.size() == 0) { + void makeCellsToUploadUnique() { + if (this.cellsToUpload.size() == 0) { return; } - ArrayList strippedCellsToUpload = cloneChoiceCellList(cellsToUpload); + ArrayList strippedCellsToUpload = cloneChoiceCellList(this.cellsToUpload); ArrayList strippedLoadedCells = cloneChoiceCellList(new ArrayList<>(loadedCells)); boolean supportsChoiceUniqueness = !(sdlMsgVersion.getMinorVersion() < 7 || (sdlMsgVersion.getMajorVersion() == 7 && sdlMsgVersion.getMinorVersion() == 0)); if (supportsChoiceUniqueness) { @@ -620,7 +620,7 @@ class PreloadPresentChoicesOperation extends Task { } addUniqueNamesToCells(strippedCellsToUpload, strippedLoadedCells, supportsChoiceUniqueness); - transferUniqueNamesFromCells(strippedCellsToUpload, cellsToUpload); + transferUniqueNamesFromCells(strippedCellsToUpload, this.cellsToUpload); } private void updateChoiceSet(ChoiceSet choiceSet, HashSet loadedCells, HashSet cellsToUpload) { -- cgit v1.2.1 From 51016fabd3e08e46471ae500b6dd007d18ad6327 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 9 Sep 2021 17:09:38 -0400 Subject: Remove unused methods --- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 4805cbb03..8e9804f25 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -670,14 +670,6 @@ class PreloadPresentChoicesOperation extends Task { return clone; } - private boolean hasImageFieldOfName(ImageFieldName imageFieldName) { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, imageFieldName); - } - - private boolean hasTextFieldOfName(TextFieldName textFieldName) { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, textFieldName); - } - /** * Checks if 2 or more cells have the same text/title. In case this condition is true, this function will handle the presented issue by adding "(count)". * E.g. Choices param contains 2 cells with text/title "Address" will be handled by updating the uniqueText/uniqueTitle of the second cell to "Address (2)". -- cgit v1.2.1 From 9b33d90a881937a1ca06fc3124a7755141196786 Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 10 Sep 2021 11:41:22 -0400 Subject: Start choiceIds at 1 and handle failed interaction --- .../screen/choiceset/PreloadPresentChoicesOperation.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 8e9804f25..6a4f7e337 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -96,7 +96,7 @@ class PreloadPresentChoicesOperation extends Task { private boolean choiceError = false; private HashSet loadedCells; private final ChoiceSet choiceSet; - private static Integer choiceId = 0; + private static Integer choiceId = 1; private static Boolean reachedMaxIds = false; private static final int MAX_CHOICE_ID = 65535; private final Integer cancelID; @@ -182,7 +182,7 @@ class PreloadPresentChoicesOperation extends Task { } if (this.loadedCells == null || this.loadedCells.isEmpty()) { - choiceId = 0; + choiceId = 1; reachedMaxIds = false; } @@ -446,6 +446,10 @@ class PreloadPresentChoicesOperation extends Task { if (listener != null) { listener.onComplete(true); } + } else { + if (listener != null) { + listener.onComplete(false); + } } } }); @@ -566,7 +570,7 @@ class PreloadPresentChoicesOperation extends Task { private int nextChoiceIdBasedOnUsedIds(ArrayList usedIds) { //Check if we are entirely full, or if we've advanced beyond the max value, loop back if (choiceId == MAX_CHOICE_ID) { - choiceId = 0; + choiceId = 1; reachedMaxIds = true; } -- cgit v1.2.1 From ec430491b5ce2130a59c52554cb4c3161fb30ebd Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 10 Sep 2021 13:43:47 -0400 Subject: Start at choiceSet 0 --- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 6a4f7e337..875bad853 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -96,7 +96,7 @@ class PreloadPresentChoicesOperation extends Task { private boolean choiceError = false; private HashSet loadedCells; private final ChoiceSet choiceSet; - private static Integer choiceId = 1; + private static Integer choiceId = 0; private static Boolean reachedMaxIds = false; private static final int MAX_CHOICE_ID = 65535; private final Integer cancelID; @@ -182,7 +182,7 @@ class PreloadPresentChoicesOperation extends Task { } if (this.loadedCells == null || this.loadedCells.isEmpty()) { - choiceId = 1; + choiceId = 0; reachedMaxIds = false; } @@ -570,7 +570,7 @@ class PreloadPresentChoicesOperation extends Task { private int nextChoiceIdBasedOnUsedIds(ArrayList usedIds) { //Check if we are entirely full, or if we've advanced beyond the max value, loop back if (choiceId == MAX_CHOICE_ID) { - choiceId = 1; + choiceId = 0; reachedMaxIds = true; } -- cgit v1.2.1 From 6a50d935673b5ed3c31d1fe5d2217874c8fa73cd Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 10 Sep 2021 15:27:18 -0400 Subject: Fix uniqueness behavior --- .../managers/screen/choiceset/ChoiceSetManagerTests.java | 6 ------ .../managers/screen/choiceset/BaseChoiceSetManager.java | 7 ------- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 2 +- 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java index 7c9f2c792..cad27bd5c 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java @@ -154,12 +154,6 @@ public class ChoiceSetManagerTests { ChoiceSet choiceSet1 = new ChoiceSet("test", Collections.emptyList(), choiceSetSelectionListener); assertFalse(csm.setUpChoiceSet(choiceSet1)); - // Identical cells will not be allowed - ChoiceCell cell1 = new ChoiceCell("test"); - ChoiceCell cell2 = new ChoiceCell("test"); - ChoiceSet choiceSet2 = new ChoiceSet("test", Arrays.asList(cell1, cell2), choiceSetSelectionListener); - assertFalse(csm.setUpChoiceSet(choiceSet2)); - // cells that have duplicate text will be allowed if there is another property to make them unique because a unique name will be assigned and used ChoiceCell cell3 = new ChoiceCell("test"); cell3.setSecondaryText("text 1"); 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 533c0b8e3..b47228779 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 @@ -589,13 +589,11 @@ abstract class BaseChoiceSetManager extends BaseSubManager { } } - HashSet uniqueChoiceCells = new HashSet<>(); HashSet uniqueVoiceCommands = new HashSet<>(); int allVoiceCommandsCount = 0; int choiceCellWithVoiceCommandCount = 0; for (ChoiceCell cell : choices) { - uniqueChoiceCells.add(cell); // Not using cloned cell here because we set the clone's VoiceCommands to null for visual check only if (cell.getVoiceCommands() != null) { uniqueVoiceCommands.addAll(cell.getVoiceCommands()); @@ -604,11 +602,6 @@ abstract class BaseChoiceSetManager extends BaseSubManager { } } - if (uniqueChoiceCells.size() != choices.size()) { - DebugTool.logError(TAG, "Attempted to create a choice set with a duplicate cell. Cell must have a unique value other than its primary text. The choice set will not be set."); - return false; - } - // All or none of the choices MUST have VR Commands if (choiceCellWithVoiceCommandCount > 0 && choiceCellWithVoiceCommandCount < choices.size()) { DebugTool.logError(TAG, "If using voice recognition commands, all of the choice set cells must have unique VR commands. There are " + uniqueVoiceCommands.size() + " cells with unique voice commands and " + choices.size() + " total cells. The choice set will not be set."); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 875bad853..8291f51f8 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -617,7 +617,7 @@ class PreloadPresentChoicesOperation extends Task { ArrayList strippedCellsToUpload = cloneChoiceCellList(this.cellsToUpload); ArrayList strippedLoadedCells = cloneChoiceCellList(new ArrayList<>(loadedCells)); - boolean supportsChoiceUniqueness = !(sdlMsgVersion.getMinorVersion() < 7 || (sdlMsgVersion.getMajorVersion() == 7 && sdlMsgVersion.getMinorVersion() == 0)); + boolean supportsChoiceUniqueness = !(sdlMsgVersion.getMajorVersion() < 7 || (sdlMsgVersion.getMajorVersion() == 7 && sdlMsgVersion.getMinorVersion() == 0)); if (supportsChoiceUniqueness) { removeUnusedProperties(strippedCellsToUpload); removeUnusedProperties(strippedLoadedCells); -- cgit v1.2.1 From eb90da3933b3f40a9cdcb1d702416cf57ec3de76 Mon Sep 17 00:00:00 2001 From: Henigan Date: Fri, 10 Sep 2021 16:46:36 -0400 Subject: Align method signatures to other libs --- .../choiceset/PreloadPresentChoicesOperation.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 8291f51f8..71a8e5520 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -195,8 +195,8 @@ class PreloadPresentChoicesOperation extends Task { finishOperation(false); } - assignIdsToCells(); - makeCellsToUploadUnique(); + assignIdsToCells(this.cellsToUpload); + makeCellsToUploadUnique(this.cellsToUpload); if (this.choiceSet != null) { updateChoiceSet(this.choiceSet, this.loadedCells, new HashSet<>(this.cellsToUpload)); @@ -538,7 +538,7 @@ class PreloadPresentChoicesOperation extends Task { return pi; } - private void assignIdsToCells() { + private void assignIdsToCells(ArrayList cells) { ArrayList usedIds = new ArrayList<>(); for (ChoiceCell cell : loadedCells) { usedIds.add(cell.getChoiceId()); @@ -547,9 +547,9 @@ class PreloadPresentChoicesOperation extends Task { ArrayList sortedUsedIds = (ArrayList) usedIds.clone(); //Loop through the cells we need ids for. Get and assign those ids - for (int i = 0; i < this.cellsToUpload.size(); i++) { + for (int i = 0; i < cells.size(); i++) { int cellId = nextChoiceIdBasedOnUsedIds(sortedUsedIds); - this.cellsToUpload.get(i).setChoiceId(cellId); + cells.get(i).setChoiceId(cellId); //Insert the ids into the usedIds sorted array in the correct position for (int j = 0; j < sortedUsedIds.size(); j++) { @@ -610,12 +610,12 @@ class PreloadPresentChoicesOperation extends Task { } // Choice Uniqueness - void makeCellsToUploadUnique() { - if (this.cellsToUpload.size() == 0) { + void makeCellsToUploadUnique(ArrayList cellsToUpload) { + if (cellsToUpload.size() == 0) { return; } - ArrayList strippedCellsToUpload = cloneChoiceCellList(this.cellsToUpload); + ArrayList strippedCellsToUpload = cloneChoiceCellList(cellsToUpload); ArrayList strippedLoadedCells = cloneChoiceCellList(new ArrayList<>(loadedCells)); boolean supportsChoiceUniqueness = !(sdlMsgVersion.getMajorVersion() < 7 || (sdlMsgVersion.getMajorVersion() == 7 && sdlMsgVersion.getMinorVersion() == 0)); if (supportsChoiceUniqueness) { @@ -624,7 +624,7 @@ class PreloadPresentChoicesOperation extends Task { } addUniqueNamesToCells(strippedCellsToUpload, strippedLoadedCells, supportsChoiceUniqueness); - transferUniqueNamesFromCells(strippedCellsToUpload, this.cellsToUpload); + transferUniqueNamesFromCells(strippedCellsToUpload, cellsToUpload); } private void updateChoiceSet(ChoiceSet choiceSet, HashSet loadedCells, HashSet cellsToUpload) { -- cgit v1.2.1 From af19401f9c985e3582c183212c4a2b600cbefbee Mon Sep 17 00:00:00 2001 From: Henigan Date: Mon, 13 Sep 2021 09:07:53 -0400 Subject: Start choiceId at 1 --- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 71a8e5520..7cab6f199 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -96,7 +96,8 @@ class PreloadPresentChoicesOperation extends Task { private boolean choiceError = false; private HashSet loadedCells; private final ChoiceSet choiceSet; - private static Integer choiceId = 0; + //Start choiceId at 1 to ensure all HMIs handle it https://github.com/smartdevicelink/generic_hmi/commit/b292fbbec095b9ce11b520d47ec95b6fcff8e247 + private static Integer choiceId = 1; private static Boolean reachedMaxIds = false; private static final int MAX_CHOICE_ID = 65535; private final Integer cancelID; @@ -182,7 +183,7 @@ class PreloadPresentChoicesOperation extends Task { } if (this.loadedCells == null || this.loadedCells.isEmpty()) { - choiceId = 0; + choiceId = 1; reachedMaxIds = false; } @@ -570,7 +571,7 @@ class PreloadPresentChoicesOperation extends Task { private int nextChoiceIdBasedOnUsedIds(ArrayList usedIds) { //Check if we are entirely full, or if we've advanced beyond the max value, loop back if (choiceId == MAX_CHOICE_ID) { - choiceId = 0; + choiceId = 1; reachedMaxIds = true; } -- cgit v1.2.1 From 8fc8e787affb177d0a5235f9ddf43f1a37c8302a Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 13 Sep 2021 09:13:09 -0400 Subject: Update log messages --- .../com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index dbc7251ea..ee980f4a2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -280,11 +280,11 @@ abstract class BaseMenuManager extends BaseSubManager { @Override public void onComplete(boolean success) { if (!success) { - DebugTool.logError(TAG, "Error setting new menu configuration. Will revert to old menu configuration."); - } else { - BaseMenuManager.this.currentMenuConfiguration = menuConfiguration; - updateMenuReplaceOperationsWithNewMenuConfiguration(); + DebugTool.logError(TAG, "Error updating menu configuration."); + return; } + BaseMenuManager.this.currentMenuConfiguration = menuConfiguration; + updateMenuReplaceOperationsWithNewMenuConfiguration(); } }); -- cgit v1.2.1 From 878312ffe7d737d586e332888ddb8c557a6b08b3 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 13 Sep 2021 09:56:45 -0400 Subject: Remove unnecessary call to transferCellIDsFromCells() --- .../com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 0e4f288f1..41e54bc19 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -128,7 +128,6 @@ class MenuReplaceOperation extends Task { final List newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); // Old kept cells ids need to be moved to the new kept cells so that submenu changes have correct parent ids - // We will transfer the ids for subCells later transferCellIDsFromCells(oldKeeps, newKeeps); // Transfer new cells' listeners to the old cells, which are stored in the current menu @@ -254,8 +253,6 @@ class MenuReplaceOperation extends Task { final List oldSubcellKeeps = filterMenuCellsWithStatusList(oldKeptCells.get(index).getSubCells(), deleteMenuStatus, MenuCellState.KEEP); final List newSubcellKeeps = filterMenuCellsWithStatusList(newKeptCells.get(index).getSubCells(), addMenuStatus, MenuCellState.KEEP); - transferCellIDsFromCells(oldSubcellKeeps, newSubcellKeeps); - transferCellListenersFromCells(newSubcellKeeps, oldSubcellKeeps); sendDeleteMenuCells(cellsToDelete, new CompletionListener() { -- cgit v1.2.1 From 2dedb1f5066b080c38c04584ceca1bc8bbd71090 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 13 Sep 2021 11:21:10 -0400 Subject: Fix an issue with calling listeners in BaseMenuManager --- .../com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index ee980f4a2..554f12c2e 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -369,7 +369,10 @@ abstract class BaseMenuManager extends BaseSubManager { } if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) { // for each cell, if it has sub cells, recursively loop through those as well - return callListenerForCells(cell.getSubCells(), command); + boolean success = callListenerForCells(cell.getSubCells(), command); + if (success) { + return true; + } } } -- cgit v1.2.1 From 00edf257a84e6252b8c0207414da4b4eaf5bf538 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 13 Sep 2021 12:12:03 -0400 Subject: Fix unit tests --- .../smartdevicelink/managers/screen/menu/MenuShowOperationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java index 2f9c6e67e..ee90cee49 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuShowOperationTests.java @@ -131,7 +131,7 @@ public class MenuShowOperationTests { private void sleep() { try { - Thread.sleep(100); + Thread.sleep(250); } catch (InterruptedException e) { e.printStackTrace(); } -- cgit v1.2.1 From 712a8497b4942875346e2c5f52f891b115987831 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 13 Sep 2021 13:10:55 -0400 Subject: Fx unit tests --- .../java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java index 08993cc96..6f8bff69d 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/menu/MenuManagerTests.java @@ -782,7 +782,7 @@ public class MenuManagerTests { private void sleep() { try { - Thread.sleep(100); + Thread.sleep(250); } catch (InterruptedException e) { e.printStackTrace(); } -- cgit v1.2.1 From 69e552c1c4a3daaefdafb4ddd6294b83027368b9 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 13 Sep 2021 13:43:56 -0400 Subject: Try to downgrade mockito to fix the failing tests --- android/sdl_android/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/sdl_android/build.gradle b/android/sdl_android/build.gradle index 73665a4b7..1cb6e4229 100644 --- a/android/sdl_android/build.gradle +++ b/android/sdl_android/build.gradle @@ -49,9 +49,9 @@ dependencies { annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.2.0' testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:3.0.0' - androidTestImplementation 'org.mockito:mockito-core:3.0.0' - androidTestImplementation 'org.mockito:mockito-android:3.0.0' + testImplementation 'org.mockito:mockito-core:2.17.0' + androidTestImplementation 'org.mockito:mockito-core:2.17.0' + androidTestImplementation 'org.mockito:mockito-android:2.17.0' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0' -- cgit v1.2.1 From 71dc383da43928d16d49072c01819c52269f0d52 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Mon, 13 Sep 2021 13:55:22 -0400 Subject: Revert last chnage --- android/sdl_android/build.gradle | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/android/sdl_android/build.gradle b/android/sdl_android/build.gradle index 1cb6e4229..73665a4b7 100644 --- a/android/sdl_android/build.gradle +++ b/android/sdl_android/build.gradle @@ -49,9 +49,9 @@ dependencies { annotationProcessor 'androidx.lifecycle:lifecycle-compiler:2.2.0' testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:2.17.0' - androidTestImplementation 'org.mockito:mockito-core:2.17.0' - androidTestImplementation 'org.mockito:mockito-android:2.17.0' + testImplementation 'org.mockito:mockito-core:3.0.0' + androidTestImplementation 'org.mockito:mockito-core:3.0.0' + androidTestImplementation 'org.mockito:mockito-android:3.0.0' androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0' androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0' -- cgit v1.2.1 From c2fec1e25335f6e9134eb8c6da6555587267533a Mon Sep 17 00:00:00 2001 From: Henigan Date: Tue, 14 Sep 2021 08:53:29 -0400 Subject: Fix Review feedback, error logs and delete error --- .../screen/choiceset/BaseChoiceSetManager.java | 2 +- .../screen/choiceset/DeleteChoicesOperation.java | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) 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 b47228779..f970cd045 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 @@ -301,8 +301,8 @@ abstract class BaseChoiceSetManager extends BaseSubManager { choiceSet.getChoiceSetSelectionListener().onError("Incorrect State"); } else { DebugTool.logError(TAG, "Present finished but an unhandled state occurred and callback failed"); + choiceSet.getChoiceSetSelectionListener().onError("callback failed"); } - choiceSet.getChoiceSetSelectionListener().onError(error); } } }; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java index 11916227c..a3814c1d2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/DeleteChoicesOperation.java @@ -53,6 +53,7 @@ class DeleteChoicesOperation extends Task { private HashSet cellsToDelete; private final BaseChoiceSetManager.ChoicesOperationCompletionListener completionListener; private HashSet loadedCells; + private List deleteChoices; private boolean completionSuccess = false; DeleteChoicesOperation(ISdl internalInterface, HashSet cellsToDelete, HashSet loadedCells, BaseChoiceSetManager.ChoicesOperationCompletionListener completionListener) { @@ -79,7 +80,7 @@ class DeleteChoicesOperation extends Task { private void sendDeletions() { - List deleteChoices = createDeleteSets(); + deleteChoices = createDeleteSets(); if (deleteChoices.size() > 0) { @@ -110,7 +111,7 @@ class DeleteChoicesOperation extends Task { DeleteChoicesOperation.super.onFinished(); } else { if (loadedCells != null) { - loadedCells.remove(loadedCellFromChoiceId(correlationId)); + loadedCells.remove(loadedCellFromCorrelationId(deleteChoices, correlationId)); } } } @@ -144,7 +145,20 @@ class DeleteChoicesOperation extends Task { this.cellsToDelete = updatedCellsToDelete; } - private ChoiceCell loadedCellFromChoiceId(int choiceId) { + private ChoiceCell loadedCellFromCorrelationId(List deleteRpcs, int correlationId) { + + Integer choiceId = null; + + for (DeleteInteractionChoiceSet rpc : deleteRpcs) { + if (rpc.getCorrelationID() == correlationId) { + choiceId = rpc.getInteractionChoiceSetID(); + } + } + + if (choiceId == null) { + return null; + } + for (ChoiceCell cell : this.loadedCells) { if (cell.getChoiceId() == choiceId) { return cell; -- cgit v1.2.1 From 7b02f590d60f957e315c13cd9b4ffd8796b7cd34 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 14 Sep 2021 09:12:43 -0400 Subject: Remove DynamicMenuUpdateAlgorithm class header --- .../managers/screen/menu/DynamicMenuUpdateAlgorithm.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index aa2f815d9..0fb8cfb65 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -3,9 +3,6 @@ package com.smartdevicelink.managers.screen.menu; import java.util.ArrayList; import java.util.List; -/** - * Created by Bilal Alsharifi on 1/25/21. - */ class DynamicMenuUpdateAlgorithm { // Cell state that tells the menu manager what it should do with a given SDLMenuCell enum MenuCellState { -- cgit v1.2.1 From 732801d16f2e90cd852c818b57876a985bc031cf Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi <599206+bilal-alsharifi@users.noreply.github.com> Date: Tue, 14 Sep 2021 15:00:39 -0400 Subject: Fix Mockito exception (#1730) * Rewrite HapticInterfaceManagerTest.createViews() without using Mockito * Add missing final keyword * Remove unused import --- .../managers/video/HapticInterfaceManagerTest.java | 59 ++++++++++++++-------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/HapticInterfaceManagerTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/HapticInterfaceManagerTest.java index dc74cb12a..bd75e2440 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/HapticInterfaceManagerTest.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/video/HapticInterfaceManagerTest.java @@ -21,6 +21,7 @@ **************************************************************************************************/ package com.smartdevicelink.managers.video; +import android.content.Context; import android.view.View; import android.view.ViewGroup; import android.widget.Button; @@ -45,9 +46,7 @@ import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.invocation.InvocationOnMock; import org.mockito.junit.MockitoJUnitRunner; -import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.List; @@ -56,7 +55,6 @@ import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.any; -import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -222,36 +220,53 @@ public class HapticInterfaceManagerTest extends TestCase { } private View createViews() { + Context context = InstrumentationRegistry.getInstrumentation().getContext(); - View view = mock(View.class); + final View view = new View(context) { + private int count = 0; - ViewGroup parent1 = mock(ViewGroup.class); - ViewGroup parent2 = mock(ViewGroup.class); + @Override + public boolean isClickable() { + int curCount = count++; + return (curCount >= 1) && (curCount <= 4); + } + }; - when(parent1.getChildCount()).thenReturn(5); + final ViewGroup parent1 = new ViewGroup(context) { + @Override + protected void onLayout(boolean b, int i, int i1, int i2, int i3) {} - when(parent1.getChildAt(0)).thenReturn(view); - when(parent1.getChildAt(1)).thenReturn(view); - when(parent1.getChildAt(2)).thenReturn(view); - when(parent1.getChildAt(3)).thenReturn(parent2); - when(parent1.getChildAt(4)).thenReturn(view); + @Override + public View getChildAt(int index) { + return view; + } - when(parent2.getChildCount()).thenReturn(2); - when(parent2.getChildAt(0)).thenReturn(view); - when(parent2.getChildAt(1)).thenReturn(view); + @Override + public int getChildCount() { + return 2; + } + }; + final ViewGroup parent2 = new ViewGroup(context) { + @Override + protected void onLayout(boolean b, int i, int i1, int i2, int i3) {} - doAnswer(new Answer() { - private int count = 0; + @Override + public View getChildAt(int index) { + if (index == 3) { + return parent1; + } else { + return view; + } + } @Override - public Boolean answer(InvocationOnMock invocation) throws Throwable { - int curCount = count++; - return (curCount >= 1) && (curCount <= 4); + public int getChildCount() { + return 5; } - }).when(view).isClickable(); + }; - return parent1; + return parent2; } -- cgit v1.2.1 From da79fc00896582d76b5f0c102c608746a67dd464 Mon Sep 17 00:00:00 2001 From: Henigan Date: Tue, 14 Sep 2021 15:25:20 -0400 Subject: Change artwork upload to sets to avoid duplicates --- .../screen/choiceset/PreloadPresentChoicesOperationTests.java | 2 +- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java index dc0138374..2970fd2f0 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java @@ -169,7 +169,7 @@ public class PreloadPresentChoicesOperationTests { @Test public void testArtworksToUpload() { - List artworksToUpload = preloadChoicesOperation.artworksToUpload(); + HashSet artworksToUpload = preloadChoicesOperation.artworksToUpload(); assertNotNull(artworksToUpload); assertEquals(artworksToUpload.size(), 1); } diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 7cab6f199..6bef45d77 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -255,7 +255,7 @@ class PreloadPresentChoicesOperation extends Task { private void preloadCellArtworks(@NonNull final CompletionListener listener) { this.currentState = SDLPreloadPresentChoicesOperationState.UPLOADING_IMAGES; - List artworksToUpload = artworksToUpload(); + List artworksToUpload = new ArrayList<>(artworksToUpload()); if (artworksToUpload.size() == 0) { DebugTool.logInfo(TAG, "Choice Preload: No Choice Artworks to upload"); @@ -850,8 +850,8 @@ class PreloadPresentChoicesOperation extends Task { this.loadedCells = loadedCells; } - List artworksToUpload() { - List artworksToUpload = new ArrayList<>(); + HashSet artworksToUpload() { + HashSet artworksToUpload = new HashSet<>(); for (ChoiceCell cell : cellsToUpload) { if (shouldSendChoicePrimaryImage() && fileManager.get() != null && fileManager.get().fileNeedsUpload(cell.getArtwork())) { artworksToUpload.add(cell.getArtwork()); -- cgit v1.2.1 From 52a48dd4a0d9c98c18fb86590866922347d93be0 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi <599206+bilal-alsharifi@users.noreply.github.com> Date: Wed, 15 Sep 2021 12:09:07 -0400 Subject: Apply suggestions from code review Co-authored-by: Julian Kast --- .../managers/screen/menu/DynamicMenuUpdateAlgorithm.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index 0fb8cfb65..c2a504aae 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -4,7 +4,7 @@ import java.util.ArrayList; import java.util.List; class DynamicMenuUpdateAlgorithm { - // Cell state that tells the menu manager what it should do with a given SDLMenuCell + // Cell state that tells the menu manager what it should do with a given MenuCell enum MenuCellState { DELETE, // Marks the cell to be deleted ADD, // Marks the cell to be added @@ -33,7 +33,7 @@ class DynamicMenuUpdateAlgorithm { DynamicMenuUpdateRunScore bestScore = new DynamicMenuUpdateRunScore(new ArrayList(), new ArrayList(), 0); for (int run = startRun; run < oldMenuCells.size(); run++) { - // Set the menu status as a 1-1 array, start off will oldMenus = all Deletes, newMenu = all Adds + // Set the menu status as a 1-1 list, start off will oldMenus = all Deletes, newMenu = all Adds List oldMenuStatus = buildAllDeleteStatusesForMenu(oldMenuCells); List newMenuStatus = buildAllAddStatusesForMenu(updatedMenuCells); @@ -52,7 +52,7 @@ class DynamicMenuUpdateAlgorithm { } } - // // Add RPC are the biggest operation so we need to find the run with the least amount of Adds. + // Add RPC are the biggest operation so we need to find the run with the least amount of Adds. // We will reset the run we use each time a runScore is less than the current score. int numberOfAdds = 0; for (int status = 0; status < newMenuStatus.size(); status++) { @@ -77,8 +77,8 @@ class DynamicMenuUpdateAlgorithm { } /** - * Builds a 1-1 array of Deletes for every element in the array - * @param oldMenu The old menu array + * Builds a 1-1 list of Deletes for every element in the array + * @param oldMenu The old menu list */ static List buildAllDeleteStatusesForMenu (List oldMenu){ List oldMenuStatus = new ArrayList<>(oldMenu.size()); @@ -89,8 +89,8 @@ class DynamicMenuUpdateAlgorithm { } /** - * Builds a 1-1 array of Adds for every element in the array - * @param newMenu The new menu array + * Builds a 1-1 list of Adds for every element in the list + * @param newMenu The new menu list */ static List buildAllAddStatusesForMenu (List newMenu){ List newMenuStatus = new ArrayList<>(newMenu.size()); -- cgit v1.2.1 From afab99f14134bc63c956857cf33da6f02982fd36 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 15 Sep 2021 12:12:21 -0400 Subject: Add missing copyright headers --- .../screen/menu/DynamicMenuUpdateAlgorithm.java | 32 ++++++++++++++++++++++ .../managers/screen/menu/MenuReplaceOperation.java | 32 ++++++++++++++++++++++ .../managers/screen/menu/MenuReplaceUtilities.java | 32 ++++++++++++++++++++++ .../screen/menu/VoiceCommandUpdateOperation.java | 32 ++++++++++++++++++++++ 4 files changed, 128 insertions(+) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java index c2a504aae..b6fa0266c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/DynamicMenuUpdateAlgorithm.java @@ -1,3 +1,35 @@ +/* + * Copyright (c) 2021 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.menu; import java.util.ArrayList; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 41e54bc19..1ee297142 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -1,3 +1,35 @@ +/* + * Copyright (c) 2021 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.menu; import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 15ae7231b..b35df7c20 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -1,3 +1,35 @@ +/* + * Copyright (c) 2021 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.menu; import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java index 59caffbdb..0650ab388 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/VoiceCommandUpdateOperation.java @@ -1,3 +1,35 @@ +/* + * Copyright (c) 2021 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.menu; import com.livio.taskmaster.Task; -- cgit v1.2.1 From ef086d79b72321f7d9a60e5a08aa9a2e8aae7e94 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi <599206+bilal-alsharifi@users.noreply.github.com> Date: Wed, 15 Sep 2021 12:15:07 -0400 Subject: Apply suggestions from code review Co-authored-by: Julian Kast --- .../com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 2 +- .../smartdevicelink/managers/screen/menu/MenuReplaceOperation.java | 2 +- .../smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 554f12c2e..96b4f82c5 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -223,7 +223,7 @@ abstract class BaseMenuManager extends BaseSubManager { DebugTool.logError(TAG, String.format("The cell %s does not contain any sub cells, so no submenu can be opened", cell.getTitle())); return false; } else if (cell != null && foundClonedCell == null) { - DebugTool.logError(TAG, "This cell has not been sent to the head unit, so no submenu can be opened. Make sure that the cell exists in the SDLManager.menu array"); + DebugTool.logError(TAG, "This cell has not been sent to the head unit, so no submenu can be opened. Make sure that the cell exists in the SDLManager.menu list"); return false; } else if (internalInterface.getSdlMsgVersion().getMajorVersion() < 6) { DebugTool.logWarning(TAG, "The openSubmenu method is not supported on this head unit."); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 1ee297142..3a597acbf 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -155,7 +155,7 @@ class MenuReplaceOperation extends Task { final List cellsToDelete = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.DELETE); final List cellsToAdd = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.ADD); - // These arrays should ONLY contain KEEPS. These will be used for SubMenu compares + // These lists should ONLY contain KEEPS. These will be used for SubMenu compares final List oldKeeps = filterMenuCellsWithStatusList(currentMenu, deleteMenuStatus, MenuCellState.KEEP); final List newKeeps = filterMenuCellsWithStatusList(updatedMenu, addMenuStatus, MenuCellState.KEEP); diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index b35df7c20..46daf1620 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -69,9 +69,9 @@ class MenuReplaceUtilities { } /** - * Assign cell ids on an array of menu cells given a parent id (or no parent id) + * Assign cell ids on a list of menu cells given a parent id (or no parent id) * - * @param menuCells The array of menu cells to update + * @param menuCells The list of menu cells to update * @param parentId The parent id to assign if needed */ static void addIdsToMenuCells(List menuCells, int parentId) { -- cgit v1.2.1 From 4b245c39a66429cacc3ee7c182a4b6fa509b8d44 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 16 Sep 2021 14:33:38 -0400 Subject: Fix imports for static methods --- .../screen/choiceset/PreloadPresentChoicesOperation.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 6bef45d77..730f816c8 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -41,6 +41,8 @@ import com.livio.taskmaster.Task; import com.smartdevicelink.managers.CompletionListener; import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.managers.ManagerUtility; +import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; +import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; import com.smartdevicelink.managers.file.FileManager; import com.smartdevicelink.managers.file.MultipleFileCompletionListener; import com.smartdevicelink.managers.file.filetypes.SdlArtwork; @@ -74,13 +76,11 @@ import com.smartdevicelink.util.DebugTool; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; class PreloadPresentChoicesOperation extends Task { @@ -771,23 +771,23 @@ class PreloadPresentChoicesOperation extends Task { if (this.displayName != null && this.displayName.equals(DisplayType.GEN3_8_INCH.toString())) { return true; } - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, TextFieldName.menuName); + return defaultMainWindowCapability == null || hasTextFieldOfName(defaultMainWindowCapability, TextFieldName.menuName); } boolean shouldSendChoiceSecondaryText() { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, TextFieldName.secondaryText); + return defaultMainWindowCapability == null || hasTextFieldOfName(defaultMainWindowCapability, TextFieldName.secondaryText); } boolean shouldSendChoiceTertiaryText() { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName(defaultMainWindowCapability, TextFieldName.tertiaryText); + return defaultMainWindowCapability == null || hasTextFieldOfName(defaultMainWindowCapability, TextFieldName.tertiaryText); } boolean shouldSendChoicePrimaryImage() { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.choiceImage); + return defaultMainWindowCapability == null || hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.choiceImage); } boolean shouldSendChoiceSecondaryImage() { - return defaultMainWindowCapability == null || ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.choiceSecondaryImage); + return defaultMainWindowCapability == null || hasImageFieldOfName(defaultMainWindowCapability, ImageFieldName.choiceSecondaryImage); } // SDL Notifications -- cgit v1.2.1 From 24c54bed0237bc34e97871e52b005cf883eebaa1 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 16 Sep 2021 14:55:32 -0400 Subject: Add uniqueness check back to ChoiceSetManager --- .../managers/screen/choiceset/BaseChoiceSetManager.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 f970cd045..c297f7be2 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 @@ -590,10 +590,13 @@ abstract class BaseChoiceSetManager extends BaseSubManager { } HashSet uniqueVoiceCommands = new HashSet<>(); + HashSet uniqueChoiceCells = new HashSet<>(); + int allVoiceCommandsCount = 0; int choiceCellWithVoiceCommandCount = 0; for (ChoiceCell cell : choices) { + uniqueChoiceCells.add(cell); // Not using cloned cell here because we set the clone's VoiceCommands to null for visual check only if (cell.getVoiceCommands() != null) { uniqueVoiceCommands.addAll(cell.getVoiceCommands()); @@ -602,6 +605,11 @@ abstract class BaseChoiceSetManager extends BaseSubManager { } } + if (uniqueChoiceCells.size() != choices.size()) { + DebugTool.logError(TAG, "Attempted to create a choice set with a duplicate cell. Cell must have a unique value other than its primary text. The choice set will not be set."); + return false; + } + // All or none of the choices MUST have VR Commands if (choiceCellWithVoiceCommandCount > 0 && choiceCellWithVoiceCommandCount < choices.size()) { DebugTool.logError(TAG, "If using voice recognition commands, all of the choice set cells must have unique VR commands. There are " + uniqueVoiceCommands.size() + " cells with unique voice commands and " + choices.size() + " total cells. The choice set will not be set."); -- cgit v1.2.1 From 469c06447b9e85bb25a43fbce8ca61ff2c399873 Mon Sep 17 00:00:00 2001 From: Robert Henigan Date: Thu, 16 Sep 2021 16:03:09 -0400 Subject: Update base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java Co-authored-by: Julian Kast --- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 730f816c8..9ad04c1a2 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -605,7 +605,7 @@ class PreloadPresentChoicesOperation extends Task { choiceId = MAX_CHOICE_ID; return choiceId; } else { - //We haven't looped all the way around yet, so we'll just take the current number, then advance the item + // We haven't looped all the way around yet, so we'll just take the current number, then advance the item return choiceId++; } } -- cgit v1.2.1 From b7cf9555e188c84bb099d3f1480767c7277f7086 Mon Sep 17 00:00:00 2001 From: Robert Henigan Date: Thu, 16 Sep 2021 16:03:26 -0400 Subject: Update base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java Co-authored-by: Julian Kast --- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 9ad04c1a2..43c669617 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -601,7 +601,7 @@ class PreloadPresentChoicesOperation extends Task { } } - //This *shouldn't* be possible + // This *shouldn't* be possible choiceId = MAX_CHOICE_ID; return choiceId; } else { -- cgit v1.2.1 From 0371f9f5bcbdcbecfd5fb643b9f8eba865de60c9 Mon Sep 17 00:00:00 2001 From: Robert Henigan Date: Thu, 16 Sep 2021 16:06:37 -0400 Subject: Apply suggestions from code review Co-authored-by: Julian Kast --- .../choiceset/PreloadPresentChoicesOperationTests.java | 1 - .../choiceset/PreloadPresentChoicesOperation.java | 17 ++++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java index 2970fd2f0..0f3725f53 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperationTests.java @@ -41,7 +41,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedHashSet; -import java.util.List; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 43c669617..4c9d5a3b5 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -40,7 +40,6 @@ import androidx.annotation.NonNull; import com.livio.taskmaster.Task; import com.smartdevicelink.managers.CompletionListener; import com.smartdevicelink.managers.ISdl; -import com.smartdevicelink.managers.ManagerUtility; import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasImageFieldOfName; import static com.smartdevicelink.managers.ManagerUtility.WindowCapabilityUtility.hasTextFieldOfName; import com.smartdevicelink.managers.file.FileManager; @@ -96,7 +95,7 @@ class PreloadPresentChoicesOperation extends Task { private boolean choiceError = false; private HashSet loadedCells; private final ChoiceSet choiceSet; - //Start choiceId at 1 to ensure all HMIs handle it https://github.com/smartdevicelink/generic_hmi/commit/b292fbbec095b9ce11b520d47ec95b6fcff8e247 + // Start choiceId at 1 to ensure all HMIs handle it https://github.com/smartdevicelink/generic_hmi/commit/b292fbbec095b9ce11b520d47ec95b6fcff8e247 private static Integer choiceId = 1; private static Boolean reachedMaxIds = false; private static final int MAX_CHOICE_ID = 65535; @@ -547,12 +546,12 @@ class PreloadPresentChoicesOperation extends Task { Collections.sort(usedIds); ArrayList sortedUsedIds = (ArrayList) usedIds.clone(); - //Loop through the cells we need ids for. Get and assign those ids + // Loop through the cells we need ids for. Get and assign those ids for (int i = 0; i < cells.size(); i++) { int cellId = nextChoiceIdBasedOnUsedIds(sortedUsedIds); cells.get(i).setChoiceId(cellId); - //Insert the ids into the usedIds sorted array in the correct position + // Insert the ids into the usedIds sorted array in the correct position for (int j = 0; j < sortedUsedIds.size(); j++) { if (sortedUsedIds.get(j) > cellId) { sortedUsedIds.add(j, cellId); @@ -565,11 +564,11 @@ class PreloadPresentChoicesOperation extends Task { } } - //Find the next available choice is. Takes into account the loaded cells to ensure there are not duplicates + // Find the next available choice is. Takes into account the loaded cells to ensure there are not duplicates // @param usedIds The already loaded cell ids // @return The choice id between 0 - 65535, or Not Found if no cell ids were available private int nextChoiceIdBasedOnUsedIds(ArrayList usedIds) { - //Check if we are entirely full, or if we've advanced beyond the max value, loop back + // Check if we are entirely full, or if we've advanced beyond the max value, loop back if (choiceId == MAX_CHOICE_ID) { choiceId = 1; reachedMaxIds = true; @@ -584,18 +583,18 @@ class PreloadPresentChoicesOperation extends Task { return choiceId; } - //If the last value isn't the max value, just keep grabbing towards the max value + // If the last value isn't the max value, just keep grabbing towards the max value int lastUsedId = usedIds.get(usedIds.size() - 1); if (lastUsedId < MAX_CHOICE_ID) { choiceId = lastUsedId + 1; return choiceId; } - //All our easy options are gone. Find and grab an empty slot from within the sorted list + // All our easy options are gone. Find and grab an empty slot from within the sorted list for (int i = 0; i < usedIds.size(); i++) { int loopId = usedIds.get(i); if (i != loopId) { - //This slot is open because the cell id does not match an open sorted slot + // This slot is open because the cell id does not match an open sorted slot choiceId = i; return choiceId; } -- cgit v1.2.1 From 83bf81c2b8f865c98b36baa10d036ec3318d1803 Mon Sep 17 00:00:00 2001 From: Henigan Date: Thu, 16 Sep 2021 16:08:01 -0400 Subject: Remove unused imports --- .../managers/screen/choiceset/ChoiceSetManagerTests.java | 8 -------- .../managers/screen/choiceset/BaseChoiceSetManager.java | 4 ---- 2 files changed, 12 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java index cad27bd5c..f26a7a5c5 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/choiceset/ChoiceSetManagerTests.java @@ -41,23 +41,18 @@ import com.smartdevicelink.managers.BaseSubManager; import com.smartdevicelink.managers.CompletionListener; import com.smartdevicelink.managers.ISdl; import com.smartdevicelink.managers.file.FileManager; -import com.smartdevicelink.proxy.rpc.ImageField; import com.smartdevicelink.proxy.rpc.KeyboardCapabilities; import com.smartdevicelink.proxy.rpc.KeyboardLayoutCapability; import com.smartdevicelink.proxy.rpc.KeyboardProperties; import com.smartdevicelink.proxy.rpc.SdlMsgVersion; -import com.smartdevicelink.proxy.rpc.TextField; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.HMILevel; -import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.KeyboardInputMask; import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout; import com.smartdevicelink.proxy.rpc.enums.KeypressMode; import com.smartdevicelink.proxy.rpc.enums.Language; import com.smartdevicelink.proxy.rpc.enums.SystemContext; -import com.smartdevicelink.proxy.rpc.enums.TextFieldName; import com.smartdevicelink.proxy.rpc.enums.TriggerSource; -import com.smartdevicelink.test.TestValues; import org.junit.After; import org.junit.Before; @@ -69,13 +64,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertNotNull; -import static junit.framework.TestCase.assertNotSame; import static junit.framework.TestCase.assertNull; import static junit.framework.TestCase.assertTrue; import static org.mockito.Mockito.doReturn; 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 c297f7be2..0aae71751 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 @@ -42,7 +42,6 @@ import com.livio.taskmaster.Task; import com.smartdevicelink.managers.BaseSubManager; 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.lifecycle.OnSystemCapabilityListener; import com.smartdevicelink.managers.lifecycle.SystemCapabilityManager; @@ -55,7 +54,6 @@ import com.smartdevicelink.proxy.rpc.KeyboardProperties; import com.smartdevicelink.proxy.rpc.OnHMIStatus; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.enums.HMILevel; -import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.InteractionMode; import com.smartdevicelink.proxy.rpc.enums.KeyboardLayout; import com.smartdevicelink.proxy.rpc.enums.KeypressMode; @@ -63,14 +61,12 @@ import com.smartdevicelink.proxy.rpc.enums.Language; import com.smartdevicelink.proxy.rpc.enums.PredefinedWindows; import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType; import com.smartdevicelink.proxy.rpc.enums.SystemContext; -import com.smartdevicelink.proxy.rpc.enums.TextFieldName; import com.smartdevicelink.proxy.rpc.enums.TriggerSource; import com.smartdevicelink.proxy.rpc.listeners.OnRPCNotificationListener; import com.smartdevicelink.util.DebugTool; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; -- cgit v1.2.1 From ffa3e13e14aec7f2a4a03e8cff9ec44ef33416ce Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Fri, 17 Sep 2021 11:43:56 -0400 Subject: Fix NPE in SystemCapabilityManager --- .../managers/lifecycle/BaseSystemCapabilityManager.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java index 54a343043..e76189632 100644 --- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java @@ -135,12 +135,15 @@ abstract class BaseSystemCapabilityManager { // HAX: Issue #1705, Ford Sync bug returning incorrect template name for "NON-MEDIA" (https://github.com/smartdevicelink/sdl_java_suite/issues/1705). List templatesAvailable = display.getTemplatesAvailable(); - for (int i = 0; i < templatesAvailable.size(); i++) { - if (templatesAvailable.get(i).equals("NON_MEDIA")) { - templatesAvailable.set(i, "NON-MEDIA"); - break; + if (templatesAvailable != null) { + for (int i = 0; i < templatesAvailable.size(); i++) { + if (templatesAvailable.get(i).equals("NON_MEDIA")) { + templatesAvailable.set(i, "NON-MEDIA"); + break; + } } } + // copy all available display capabilities defaultWindowCapability.setTemplatesAvailable(templatesAvailable); defaultWindowCapability.setNumCustomPresetsAvailable(display.getNumCustomPresetsAvailable()); -- cgit v1.2.1 From 1b50fdac5b7207ec827a7d4378ff0c3ecc4bb33c Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 21 Sep 2021 10:14:29 -0400 Subject: Use set in findAllArtworksToBeUploadedFromCells() to prevent duplicates --- .../managers/screen/menu/MenuReplaceOperation.java | 2 +- .../managers/screen/menu/MenuReplaceUtilities.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java index 3a597acbf..7db1a85b9 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceOperation.java @@ -198,7 +198,7 @@ class MenuReplaceOperation extends Task { } private void uploadMenuArtworks(final CompletionListener listener) { - List artworksToBeUploaded = findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability); + List artworksToBeUploaded = new ArrayList<>(findAllArtworksToBeUploadedFromCells(updatedMenu, fileManager.get(), windowCapability)); if (artworksToBeUploaded.isEmpty()) { listener.onComplete(true); return; diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index 46daf1620..f274130d3 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -55,8 +55,10 @@ import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * Created by Bilal Alsharifi on 1/25/21. @@ -115,13 +117,13 @@ class MenuReplaceUtilities { } } - static List findAllArtworksToBeUploadedFromCells(List cells, FileManager fileManager, WindowCapability windowCapability) { + static Set findAllArtworksToBeUploadedFromCells(List cells, FileManager fileManager, WindowCapability windowCapability) { // Make sure we can use images in the menus if (!hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) { - return new ArrayList<>(); + return new HashSet<>(); } - List artworks = new ArrayList<>(); + Set artworks = new HashSet<>(); for (MenuCell cell : cells) { if (fileManager != null) { if (fileManager.fileNeedsUpload(cell.getIcon())) { -- cgit v1.2.1 From 7088a04fadfb2dcc651e7146ad06c2c54f4c99ce Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 22 Sep 2021 10:01:21 -0400 Subject: Fix using wrong menu configuration pass through to menu operation --- .../java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java index 96b4f82c5..78d7a96e8 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/BaseMenuManager.java @@ -177,7 +177,7 @@ abstract class BaseMenuManager extends BaseSubManager { this.menuCells = cloneMenuCellsList(cells); boolean isDynamicMenuUpdateActive = isDynamicMenuUpdateActive(dynamicMenuUpdatesMode, displayType); - Task operation = new MenuReplaceOperation(internalInterface, fileManager.get(), windowCapability, menuConfiguration, currentMenuCells, menuCells, isDynamicMenuUpdateActive, new MenuManagerCompletionListener() { + Task operation = new MenuReplaceOperation(internalInterface, fileManager.get(), windowCapability, currentMenuConfiguration, currentMenuCells, menuCells, isDynamicMenuUpdateActive, new MenuManagerCompletionListener() { @Override public void onComplete(boolean success, List currentMenuCells) { BaseMenuManager.this.currentMenuCells = currentMenuCells; -- cgit v1.2.1 From 43eb7cea3f7b1cc56945e93bcd0f647833f633f9 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Thu, 23 Sep 2021 09:51:26 -0400 Subject: Fix NPE --- .../smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java index e76189632..33a778e58 100644 --- a/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/lifecycle/BaseSystemCapabilityManager.java @@ -137,7 +137,7 @@ abstract class BaseSystemCapabilityManager { List templatesAvailable = display.getTemplatesAvailable(); if (templatesAvailable != null) { for (int i = 0; i < templatesAvailable.size(); i++) { - if (templatesAvailable.get(i).equals("NON_MEDIA")) { + if ("NON_MEDIA".equals(templatesAvailable.get(i))) { templatesAvailable.set(i, "NON-MEDIA"); break; } -- cgit v1.2.1 From b46f82ce8edf42bf01c20cc18735240392d9cb91 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Thu, 23 Sep 2021 16:24:13 -0400 Subject: Add logic to clone SdlFile, and prevent multiples of the same file being uploaded via fileManager --- .../managers/file/filetypes/SdlArtwork.java | 12 ++----- .../managers/file/filetypes/SdlFile.java | 19 ++++++++++ .../managers/file/BaseFileManager.java | 41 +++++++--------------- .../managers/file/UploadFileOperation.java | 22 ++++++++++++ 4 files changed, 57 insertions(+), 37 deletions(-) diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java index c8887d4e1..0634cb237 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java @@ -159,16 +159,10 @@ public class SdlArtwork extends SdlFile implements Cloneable { */ @Override public SdlArtwork clone() { - try { - SdlArtwork artwork = (SdlArtwork) super.clone(); - if (artwork != null) { - artwork.imageRPC = artwork.createImageRPC(); - } + SdlArtwork artwork = (SdlArtwork) super.clone(); + if (artwork != null) { + artwork.imageRPC = artwork.createImageRPC(); return artwork; - } catch (CloneNotSupportedException e) { - if (DebugTool.isDebugEnabled()) { - throw new RuntimeException("Clone not supported by super class"); - } } return null; } diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java index 13c7d3119..ef2e259a1 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java @@ -38,6 +38,7 @@ import androidx.annotation.NonNull; import com.smartdevicelink.proxy.rpc.enums.FileType; import com.smartdevicelink.proxy.rpc.enums.StaticIconName; +import com.smartdevicelink.util.DebugTool; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -368,4 +369,22 @@ public class SdlFile { // 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 SdlFile clone() { + try { + SdlFile fileClone = (SdlFile) super.clone(); + return fileClone; + } 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/file/BaseFileManager.java b/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java index 9412fb122..6ffcf14ed 100644 --- a/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java @@ -70,6 +70,8 @@ abstract class BaseFileManager extends BaseSubManager { private HashMap failedFileUploadsCount; private final int maxFileUploadAttempts; private final int maxArtworkUploadAttempts; + String fileManagerCannotOverwriteError = "Cannot overwrite remote file. The remote file system already has a file of this name, and the file manager is set to not automatically overwrite files."; + /** * Constructor for BaseFileManager @@ -405,46 +407,29 @@ abstract class BaseFileManager extends BaseSubManager { return; } - // HAX: [#827](https://github.com/smartdevicelink/sdl_ios/issues/827) Older versions of Core - // had a bug where list files would cache incorrectly. This led to attempted uploads failing - // due to the system thinking they were already there when they were not. This is only needed - // if connecting to Core v4.3.1 or less which corresponds to RPC v4.3.1 or less - Version rpcVersion = new Version(internalInterface.getSdlMsgVersion()); - if (!file.isPersistent() && !hasUploadedFile(file) && new Version(4, 4, 0).isNewerThan(rpcVersion) == 1) { - file.setOverwrite(true); - } - - // Check our overwrite settings and error out if it would overwrite - if (!file.getOverwrite() && mutableRemoteFileNames.contains(file.getName())) { - String errorMessage = "Cannot overwrite remote file. The remote file system already has a file of this name, and the file manager is set to not automatically overwrite files."; - DebugTool.logWarning(TAG, errorMessage); - if (listener != null) { - listener.onComplete(true, bytesAvailable, null, errorMessage); - } - return; - } - // If we didn't error out over the overwrite, then continue on sdl_uploadFilePrivate(file, listener); } private void sdl_uploadFilePrivate(@NonNull final SdlFile file, final FileManagerCompletionListener listener) { - final String fileName = file.getName(); + final SdlFile fileClone = file.clone(); - SdlFileWrapper fileWrapper = new SdlFileWrapper(file, new FileManagerCompletionListener() { + SdlFileWrapper fileWrapper = new SdlFileWrapper(fileClone, new FileManagerCompletionListener() { @Override public void onComplete(boolean success, int bytesAvailable, Collection fileNames, String errorMessage) { if (success) { BaseFileManager.this.bytesAvailable = bytesAvailable; - BaseFileManager.this.mutableRemoteFileNames.add(fileName); - BaseFileManager.this.uploadedEphemeralFileNames.add(fileName); - } else { - incrementFailedUploadCountForFileName(file.getName(), BaseFileManager.this.failedFileUploadsCount); + BaseFileManager.this.mutableRemoteFileNames.add(fileClone.getName()); + if (!file.isPersistent()) { + BaseFileManager.this.uploadedEphemeralFileNames.add(fileClone.getName()); + } + } else if (!fileManagerCannotOverwriteError.equals(errorMessage)) { + incrementFailedUploadCountForFileName(fileClone.getName(), BaseFileManager.this.failedFileUploadsCount); - int maxUploadCount = file instanceof SdlArtwork ? maxArtworkUploadAttempts : maxFileUploadAttempts; - if (canFileBeUploadedAgain(file, maxUploadCount, failedFileUploadsCount)) { + int maxUploadCount = fileClone instanceof SdlArtwork ? maxArtworkUploadAttempts : maxFileUploadAttempts; + if (canFileBeUploadedAgain(fileClone, maxUploadCount, failedFileUploadsCount)) { DebugTool.logInfo(TAG, String.format("Attempting to resend file with name %s after a failed upload attempt", file.getName())); - sdl_uploadFilePrivate(file, listener); + sdl_uploadFilePrivate(fileClone, listener); return; } } diff --git a/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java b/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java index 4a9b785c6..b05c6ff40 100644 --- a/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java @@ -44,6 +44,7 @@ import com.smartdevicelink.proxy.rpc.PutFile; import com.smartdevicelink.proxy.rpc.PutFileResponse; import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener; import com.smartdevicelink.util.DebugTool; +import com.smartdevicelink.util.Version; import java.io.IOException; import java.io.InputStream; @@ -81,6 +82,27 @@ class UploadFileOperation extends Task { return; } + SdlFile file = fileWrapper.getFile(); + // HAX: [#827](https://github.com/smartdevicelink/sdl_ios/issues/827) Older versions of Core + // had a bug where list files would cache incorrectly. This led to attempted uploads failing + // due to the system thinking they were already there when they were not. This is only needed + // if connecting to Core v4.3.1 or less which corresponds to RPC v4.3.1 or less + if (internalInterface.get() != null && fileManager.get() != null) { + Version rpcVersion = new Version(internalInterface.get().getSdlMsgVersion()); + if (!file.isPersistent() && !fileManager.get().hasUploadedFile(file) && new Version(4, 4, 0).isNewerThan(rpcVersion) == 1) { + file.setOverwrite(true); + } + // Check our overwrite settings and error out if it would overwrite + if (!file.getOverwrite() && fileManager.get().mutableRemoteFileNames.contains(file.getName())) { + DebugTool.logWarning(TAG, fileManager.get().fileManagerCannotOverwriteError); + if (this.fileWrapper.getCompletionListener() != null) { + this.fileWrapper.getCompletionListener().onComplete(false, bytesAvailable, null, fileManager.get().fileManagerCannotOverwriteError); + } + onFinished(); + return; + } + } + int mtuSize = 0; if (internalInterface.get() != null) { mtuSize = (int) internalInterface.get().getMtu(SessionType.RPC); -- cgit v1.2.1 From 15b190ddccad57111c6c4cc2532c7db3f54cd6c6 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Fri, 24 Sep 2021 12:12:51 -0400 Subject: Added logic to check if file is no longer on head unit when deleting sdlFiles in operation --- .../smartdevicelink/managers/file/BaseFileManager.java | 10 ++-------- .../managers/file/DeleteFileOperation.java | 18 ++++++++++++++++-- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java b/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java index 6ffcf14ed..9f6f1a60b 100644 --- a/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java @@ -70,7 +70,7 @@ abstract class BaseFileManager extends BaseSubManager { private HashMap failedFileUploadsCount; private final int maxFileUploadAttempts; private final int maxArtworkUploadAttempts; - String fileManagerCannotOverwriteError = "Cannot overwrite remote file. The remote file system already has a file of this name, and the file manager is set to not automatically overwrite files."; + final String fileManagerCannotOverwriteError = "Cannot overwrite remote file. The remote file system already has a file of this name, and the file manager is set to not automatically overwrite files."; /** @@ -196,13 +196,7 @@ abstract class BaseFileManager extends BaseSubManager { } private void deleteRemoteFileWithNamePrivate(@NonNull final String fileName, final FileManagerCompletionListener listener) { - if (!mutableRemoteFileNames.contains(fileName) && listener != null) { - String errorMessage = "No such remote file is currently known"; - listener.onComplete(false, bytesAvailable, mutableRemoteFileNames, errorMessage); - return; - } - - DeleteFileOperation operation = new DeleteFileOperation(internalInterface, fileName, new FileManagerCompletionListener() { + DeleteFileOperation operation = new DeleteFileOperation(internalInterface, fileName, mutableRemoteFileNames, new FileManagerCompletionListener() { @Override public void onComplete(boolean success, int bytesAvailable, Collection fileNames, String errorMessage) { if (success) { diff --git a/base/src/main/java/com/smartdevicelink/managers/file/DeleteFileOperation.java b/base/src/main/java/com/smartdevicelink/managers/file/DeleteFileOperation.java index 21df7f7c4..b732a525f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/file/DeleteFileOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/file/DeleteFileOperation.java @@ -38,8 +38,10 @@ import com.smartdevicelink.proxy.RPCResponse; import com.smartdevicelink.proxy.rpc.DeleteFile; import com.smartdevicelink.proxy.rpc.DeleteFileResponse; import com.smartdevicelink.proxy.rpc.listeners.OnRPCResponseListener; +import com.smartdevicelink.util.DebugTool; import java.lang.ref.WeakReference; +import java.util.Set; /** * Created by Bilal Alsharifi on 12/1/20. @@ -49,12 +51,14 @@ class DeleteFileOperation extends Task { private final WeakReference internalInterface; private String fileName; private FileManagerCompletionListener completionListener; + private Set mutableRemoteFileNames; - DeleteFileOperation(ISdl internalInterface, String fileName, FileManagerCompletionListener completionListener) { + DeleteFileOperation(ISdl internalInterface, String fileName, Set mutableRemoteFileNames, FileManagerCompletionListener completionListener) { super("DeleteFileOperation"); this.internalInterface = new WeakReference<>(internalInterface); this.fileName = fileName; this.completionListener = completionListener; + this.mutableRemoteFileNames = mutableRemoteFileNames; } @Override @@ -66,6 +70,13 @@ class DeleteFileOperation extends Task { if (getState() == Task.CANCELED) { return; } + if (!mutableRemoteFileNames.contains(fileName) && completionListener != null) { + String errorMessage = "File to delete is no longer on the head unit, aborting operation"; + // Returning BaseFileManager.SPACE_AVAILABLE_MAX_VALUE for bytesAvaialble as a placeHolder, it will not get updated in BaseFileManager as long as success returned is false. + completionListener.onComplete(false, BaseFileManager.SPACE_AVAILABLE_MAX_VALUE, mutableRemoteFileNames, errorMessage); + onFinished(); + return; + } deleteFile(); } @@ -77,12 +88,15 @@ class DeleteFileOperation extends Task { public void onResponse(int correlationId, RPCResponse response) { DeleteFileResponse deleteFileResponse = (DeleteFileResponse) response; boolean success = deleteFileResponse.getSuccess(); + String errorMessage = success ? null : response.getInfo() + ": " + response.getResultCode(); + if (errorMessage != null) { + DebugTool.logInfo(TAG, "Error deleting file: " + errorMessage); + } // If spaceAvailable is null, set it to the max value int bytesAvailable = deleteFileResponse.getSpaceAvailable() != null ? deleteFileResponse.getSpaceAvailable() : BaseFileManager.SPACE_AVAILABLE_MAX_VALUE; if (completionListener != null) { - String errorMessage = success ? null : response.getInfo() + ": " + response.getResultCode(); completionListener.onComplete(success, bytesAvailable, null, errorMessage); } -- cgit v1.2.1 From ad9c870124568504d74baeeba105bc716aebfe47 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Fri, 24 Sep 2021 12:40:43 -0400 Subject: Ignore failing test, it passes locally --- .../com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java index 5282456e1..19fc9ccd4 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java @@ -17,6 +17,8 @@ import com.smartdevicelink.streaming.video.VideoStreamingParameters; import junit.framework.TestCase; +import org.junit.Ignore; + import java.nio.ByteBuffer; import java.util.concurrent.FutureTask; @@ -56,6 +58,7 @@ public class SdlRemoteDisplayTest extends TestCase { } + @Ignore @TargetApi(19) public void testTouchEvents() { try { -- cgit v1.2.1 From ff9ebd208acd005eac6184b5afb19fcd8d31cf70 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Fri, 24 Sep 2021 12:55:35 -0400 Subject: remove @Ignore tag added to test --- .../com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java index 19fc9ccd4..c03a6b00b 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java @@ -17,7 +17,6 @@ import com.smartdevicelink.streaming.video.VideoStreamingParameters; import junit.framework.TestCase; -import org.junit.Ignore; import java.nio.ByteBuffer; import java.util.concurrent.FutureTask; @@ -58,7 +57,6 @@ public class SdlRemoteDisplayTest extends TestCase { } - @Ignore @TargetApi(19) public void testTouchEvents() { try { -- cgit v1.2.1 From f432ee5d3012ffdc24f66142c917dd3fbea0dad0 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Fri, 24 Sep 2021 13:23:35 -0400 Subject: Add clone to javaSE SdlFile --- .../managers/file/filetypes/SdlArtwork.java | 12 +++--------- .../managers/file/filetypes/SdlFile.java | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java index affa4ec36..60d2f396f 100644 --- a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java +++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java @@ -159,16 +159,10 @@ public class SdlArtwork extends SdlFile implements Cloneable { */ @Override public SdlArtwork clone() { - try { - SdlArtwork artwork = (SdlArtwork) super.clone(); - if (artwork != null) { - artwork.imageRPC = artwork.createImageRPC(); - } + SdlArtwork artwork = (SdlArtwork) super.clone(); + if (artwork != null) { + artwork.imageRPC = artwork.createImageRPC(); return artwork; - } catch (CloneNotSupportedException e) { - if (DebugTool.isDebugEnabled()) { - throw new RuntimeException("Clone not supported by super class"); - } } return null; } diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java index 6fd91b8a0..5e8a2cf94 100644 --- a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java +++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java @@ -35,6 +35,7 @@ import androidx.annotation.NonNull; import com.smartdevicelink.proxy.rpc.enums.FileType; import com.smartdevicelink.proxy.rpc.enums.StaticIconName; +import com.smartdevicelink.util.DebugTool; import java.net.URI; import java.security.MessageDigest; @@ -366,4 +367,22 @@ public class SdlFile { // 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 SdlFile clone() { + try { + SdlFile fileClone = (SdlFile) super.clone(); + return fileClone; + } catch (CloneNotSupportedException e) { + if (DebugTool.isDebugEnabled()) { + throw new RuntimeException("Clone not supported by super class"); + } + } + return null; + } } -- cgit v1.2.1 From c511de25b8ee744c9ee8ac33a8392d99cda077f3 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Fri, 24 Sep 2021 13:24:57 -0400 Subject: Removed whitespace line --- .../com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java index c03a6b00b..5282456e1 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/test/streaming/video/SdlRemoteDisplayTest.java @@ -17,7 +17,6 @@ import com.smartdevicelink.streaming.video.VideoStreamingParameters; import junit.framework.TestCase; - import java.nio.ByteBuffer; import java.util.concurrent.FutureTask; -- cgit v1.2.1 From 1f005dd08952a206e2c85cd1fb736dc21b8401f6 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Fri, 24 Sep 2021 15:15:38 -0400 Subject: add unit test to FileManager --- .../managers/file/FileManagerTests.java | 37 ++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java index 90bedd434..298cb26d7 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java @@ -850,6 +850,43 @@ public class FileManagerTests { }); } + /** + * Tests to make sure files are not being uploaded to head unit multiple times in a row + */ + @Test + public void testFileNotOnHmi() { + final ISdl internalInterface = createISdlMock(); + + doAnswer(onListFilesSuccess).when(internalInterface).sendRPC(any(ListFiles.class)); + doAnswer(onPutFileSuccess).when(internalInterface).sendRPC(any(PutFile.class)); + + final SdlArtwork validFile2 = new SdlArtwork(TestValues.GENERAL_STRING + "2", FileType.GRAPHIC_JPEG, TestValues.GENERAL_STRING.getBytes(), false); + + final List list = Arrays.asList(validFile2, validFile2); + + FileManagerConfig fileManagerConfig = new FileManagerConfig(); + + final FileManager fileManager = new FileManager(internalInterface, mTestContext, fileManagerConfig); + fileManager.start(new CompletionListener() { + @Override + public void onComplete(boolean success) { + fileManager.uploadArtworks(list, new MultipleFileCompletionListener() { + @Override + public void onComplete(final Map errors) { + assertOnMainThread(new Runnable() { + @Override + public void run() { + assertTrue(errors != null); + assertTrue(errors.containsValue(fileManager.fileManagerCannotOverwriteError)); + } + }); + } + }); + } + }); + } + + /** * Test custom overridden SdlFile equals method */ -- cgit v1.2.1 From 196d96e9ddf9575777df736139ce397d1c8d8607 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Mon, 27 Sep 2021 11:26:56 -0400 Subject: Add unit test --- .../lifecycle/SystemCapabilityManagerTests.java | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) 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 65f9c7bb4..03854d3ee 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 @@ -32,6 +32,7 @@ import com.smartdevicelink.proxy.rpc.SdlMsgVersion; import com.smartdevicelink.proxy.rpc.SetDisplayLayoutResponse; import com.smartdevicelink.proxy.rpc.SoftButtonCapabilities; import com.smartdevicelink.proxy.rpc.SystemCapability; +import com.smartdevicelink.proxy.rpc.TextField; import com.smartdevicelink.proxy.rpc.VideoStreamingCapability; import com.smartdevicelink.proxy.rpc.WindowCapability; import com.smartdevicelink.proxy.rpc.WindowTypeCapabilities; @@ -52,6 +53,7 @@ import com.smartdevicelink.proxy.rpc.enums.ServiceUpdateReason; import com.smartdevicelink.proxy.rpc.enums.SpeechCapabilities; import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType; import com.smartdevicelink.proxy.rpc.enums.SystemContext; +import com.smartdevicelink.proxy.rpc.enums.TextFieldName; import com.smartdevicelink.proxy.rpc.enums.WindowType; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; import com.smartdevicelink.proxy.rpc.listeners.OnRPCListener; @@ -689,6 +691,35 @@ public class SystemCapabilityManagerTests { verify(internalInterface, times(0)).sendRPC(any(GetSystemCapability.class)); } + /** + * Test to verify that we can get null for templatesAvailable without hitting an NPE and + * test media field conversion for NON_MEDIA to NON-MEDIA for Sync bug. + */ + @Test + public void testMediaFieldConversion() { + SystemCapabilityManager systemCapabilityManager = new SystemCapabilityManager(new InternalSDLInterface()); + + RegisterAppInterfaceResponse raiResponse = new RegisterAppInterfaceResponse(); + DisplayCapabilities displayCapabilities = new DisplayCapabilities(); + displayCapabilities.setGraphicSupported(false); + TextField textField = new TextField(); + textField.setName(TextFieldName.mainField1); + displayCapabilities.setTextFields(Collections.singletonList(textField)); + raiResponse.setDisplayCapabilities(displayCapabilities); + raiResponse.setSuccess(true); + systemCapabilityManager.parseRAIResponse(raiResponse); + + WindowCapability windowCapability = systemCapabilityManager.getDefaultMainWindowCapability(); + assertNull(windowCapability.getTemplatesAvailable()); + + List templates = new ArrayList<>(); + templates.add("NON_MEDIA"); + displayCapabilities.setTemplatesAvailable(templates); + systemCapabilityManager.parseRAIResponse(raiResponse); + windowCapability = systemCapabilityManager.getDefaultMainWindowCapability(); + assertTrue(windowCapability.getTemplatesAvailable().contains("NON-MEDIA")); + } + @Test public void testListConversion() { SystemCapabilityManager systemCapabilityManager = createSampleManager(); -- cgit v1.2.1 From ecd6beaaec53e504e753cb454f416d5f1bc9c7ee Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Mon, 27 Sep 2021 13:33:53 -0400 Subject: Remove unused Imports in SdlArtwork and add implement Cloneable in SdlFile --- .../java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java | 1 - .../main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java | 2 +- .../java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java | 1 - .../main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java index 0634cb237..b46745917 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java @@ -40,7 +40,6 @@ import com.smartdevicelink.proxy.rpc.Image; import com.smartdevicelink.proxy.rpc.enums.FileType; import com.smartdevicelink.proxy.rpc.enums.ImageType; import com.smartdevicelink.proxy.rpc.enums.StaticIconName; -import com.smartdevicelink.util.DebugTool; /** * A class that extends SdlFile, representing artwork (JPEG, PNG, or BMP) to be uploaded to core diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java index ef2e259a1..a0c0d9369 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java @@ -47,7 +47,7 @@ import java.util.Arrays; /** * A class representing data to be uploaded to core */ -public class SdlFile { +public class SdlFile implements Cloneable { private String fileName; private int id = -1; private Uri uri; diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java index 60d2f396f..2c31c848a 100644 --- a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java +++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlArtwork.java @@ -37,7 +37,6 @@ import com.smartdevicelink.proxy.rpc.Image; import com.smartdevicelink.proxy.rpc.enums.FileType; import com.smartdevicelink.proxy.rpc.enums.ImageType; import com.smartdevicelink.proxy.rpc.enums.StaticIconName; -import com.smartdevicelink.util.DebugTool; import java.net.URI; diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java index 5e8a2cf94..727413bf2 100644 --- a/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java +++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/managers/file/filetypes/SdlFile.java @@ -45,7 +45,7 @@ import java.util.Arrays; /** * A class representing data to be uploaded to core */ -public class SdlFile { +public class SdlFile implements Cloneable { private String fileName; private String filePath; private URI uri; -- cgit v1.2.1 From 0f9e1ce10e86d8ecf28398833dde5bd45522e8a9 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Mon, 27 Sep 2021 16:01:01 -0400 Subject: Reverted breaking change to make sure when the overwrite error is hit, we return true --- .../java/com/smartdevicelink/managers/file/FileManagerTests.java | 3 +-- .../main/java/com/smartdevicelink/managers/file/BaseFileManager.java | 2 +- .../java/com/smartdevicelink/managers/file/UploadFileOperation.java | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java index 298cb26d7..b3d489eaa 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/file/FileManagerTests.java @@ -876,8 +876,7 @@ public class FileManagerTests { assertOnMainThread(new Runnable() { @Override public void run() { - assertTrue(errors != null); - assertTrue(errors.containsValue(fileManager.fileManagerCannotOverwriteError)); + verify(internalInterface, times(1)).sendRPC(any(PutFile.class)); } }); } diff --git a/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java b/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java index 9f6f1a60b..da6bae4e6 100644 --- a/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/file/BaseFileManager.java @@ -417,7 +417,7 @@ abstract class BaseFileManager extends BaseSubManager { if (!file.isPersistent()) { BaseFileManager.this.uploadedEphemeralFileNames.add(fileClone.getName()); } - } else if (!fileManagerCannotOverwriteError.equals(errorMessage)) { + } else { incrementFailedUploadCountForFileName(fileClone.getName(), BaseFileManager.this.failedFileUploadsCount); int maxUploadCount = fileClone instanceof SdlArtwork ? maxArtworkUploadAttempts : maxFileUploadAttempts; diff --git a/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java b/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java index b05c6ff40..aab50a280 100644 --- a/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/file/UploadFileOperation.java @@ -96,7 +96,7 @@ class UploadFileOperation extends Task { if (!file.getOverwrite() && fileManager.get().mutableRemoteFileNames.contains(file.getName())) { DebugTool.logWarning(TAG, fileManager.get().fileManagerCannotOverwriteError); if (this.fileWrapper.getCompletionListener() != null) { - this.fileWrapper.getCompletionListener().onComplete(false, bytesAvailable, null, fileManager.get().fileManagerCannotOverwriteError); + this.fileWrapper.getCompletionListener().onComplete(true, bytesAvailable, null, fileManager.get().fileManagerCannotOverwriteError); } onFinished(); return; -- cgit v1.2.1 From d1c2a7990a6307788ca788b0eb2cd138d4117af5 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Mon, 27 Sep 2021 16:43:59 -0400 Subject: Fixed descriptions per RPC spec --- .../java/com/smartdevicelink/proxy/rpc/RadioControlCapabilities.java | 2 +- .../java/com/smartdevicelink/proxy/rpc/SeatControlCapabilities.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/RadioControlCapabilities.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/RadioControlCapabilities.java index 05dccbc88..c9681645c 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/RadioControlCapabilities.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/RadioControlCapabilities.java @@ -79,7 +79,7 @@ public class RadioControlCapabilities extends RPCStruct { /** * Sets the moduleName portion of the RadioControlCapabilities class * - * @param moduleName The short friendly name of the climate control module. + * @param moduleName The short friendly name of the radio control module. * It should not be used to identify a module by mobile application. */ public RadioControlCapabilities setModuleName(@NonNull String moduleName) { diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatControlCapabilities.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatControlCapabilities.java index ef82a3686..75ab39d9f 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatControlCapabilities.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatControlCapabilities.java @@ -94,7 +94,7 @@ public class SeatControlCapabilities extends RPCStruct { /** * Sets the moduleName portion of the SeatControlCapabilities class * - * @param moduleName - The short friendly name of the light control module. It should not be used to identify a module by mobile application. + * @param moduleName - The short friendly name of the seat control module. It should not be used to identify a module by mobile application. */ public SeatControlCapabilities setModuleName(@NonNull String moduleName) { setValue(KEY_MODULE_NAME, moduleName); -- cgit v1.2.1 From 61c929297a15c9f88197add752cce1e2eab9396b Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Mon, 27 Sep 2021 17:42:34 -0400 Subject: fix base on review feedback --- .../com/smartdevicelink/proxy/rpc/AudioControlCapabilities.java | 6 +++--- .../com/smartdevicelink/proxy/rpc/RadioControlCapabilities.java | 4 ++-- .../java/com/smartdevicelink/proxy/rpc/SeatControlCapabilities.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/AudioControlCapabilities.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/AudioControlCapabilities.java index 7ead85fef..edff66488 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/AudioControlCapabilities.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/AudioControlCapabilities.java @@ -64,7 +64,7 @@ public class AudioControlCapabilities extends RPCStruct { /** * Constructs a newly allocated AudioControlCapabilities object * - * @param moduleName short friendly name of the light control module. + * @param moduleName short friendly name of the audio control module. */ public AudioControlCapabilities(@NonNull String moduleName) { this(); @@ -74,7 +74,7 @@ public class AudioControlCapabilities extends RPCStruct { /** * Sets the moduleName portion of the AudioControlCapabilities class * - * @param moduleName The short friendly name of the light control module. It should not be used to identify a module by mobile application. + * @param moduleName The short friendly name of the audio control module. It should not be used to identify a module by mobile application. */ public AudioControlCapabilities setModuleName(@NonNull String moduleName) { setValue(KEY_MODULE_NAME, moduleName); @@ -84,7 +84,7 @@ public class AudioControlCapabilities extends RPCStruct { /** * Gets the moduleName portion of the AudioControlCapabilities class * - * @return String - The short friendly name of the light control module. It should not be used to identify a module by mobile application. + * @return String - The short friendly name of the audio control module. It should not be used to identify a module by mobile application. */ public String getModuleName() { return getString(KEY_MODULE_NAME); diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/RadioControlCapabilities.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/RadioControlCapabilities.java index c9681645c..a120e66bc 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/RadioControlCapabilities.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/RadioControlCapabilities.java @@ -68,7 +68,7 @@ public class RadioControlCapabilities extends RPCStruct { /** * Constructs a new RadioControlCapabilities object * - * @param moduleName The short friendly name of the climate control module. + * @param moduleName The short friendly name of the radio control module. * It should not be used to identify a module by mobile application. */ public RadioControlCapabilities(@NonNull String moduleName) { @@ -90,7 +90,7 @@ public class RadioControlCapabilities extends RPCStruct { /** * Gets the moduleName portion of the RadioControlCapabilities class * - * @return String - Short friendly name of the climate control module. + * @return String - Short friendly name of the radio control module. */ public String getModuleName() { return getString(KEY_MODULE_NAME); diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatControlCapabilities.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatControlCapabilities.java index 75ab39d9f..cbb44fcdd 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatControlCapabilities.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/SeatControlCapabilities.java @@ -75,7 +75,7 @@ public class SeatControlCapabilities extends RPCStruct { /** * Constructs a newly allocated SeatControlCapabilities object * - * @param moduleName short friendly name of the light control module. + * @param moduleName short friendly name of the seat control module. */ public SeatControlCapabilities(@NonNull String moduleName) { this(); -- cgit v1.2.1 From b29b0635fef4e638cda39a1df8ca683fdba47767 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Thu, 30 Sep 2021 09:58:36 -0400 Subject: Fix IndexOutOfBoundsException in SdlRouterService --- .../src/main/java/com/smartdevicelink/transport/SdlRouterService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java index 7f6eb2b40..bcabc9546 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java @@ -3319,7 +3319,7 @@ public class SdlRouterService extends Service { try { List transportTypes = (List) obj; if (transportTypes != null) { - if (transportTypes.get(0) != null) { + if (transportTypes.size() > 0 && transportTypes.get(0) != null) { return transportTypes.get(0); } } -- cgit v1.2.1 From 967b538bf5b482404ac6b03569ad5336e3e98d18 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi <599206+bilal-alsharifi@users.noreply.github.com> Date: Thu, 30 Sep 2021 11:18:22 -0400 Subject: Update AlertManager queue id (#1744) --- .../main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java b/base/src/main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java index 56aec5c19..10db9c54c 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/BaseAlertManager.java @@ -182,7 +182,7 @@ abstract class BaseAlertManager extends BaseSubManager { private Queue newTransactionQueue() { - Queue queue = internalInterface.getTaskmaster().createQueue("AlertManager", 6, false); + Queue queue = internalInterface.getTaskmaster().createQueue("AlertManager", 8, false); queue.pause(); return queue; } -- cgit v1.2.1 From ed0591873c155e44f18cf60915447a010065f6d6 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Thu, 30 Sep 2021 14:02:46 -0400 Subject: code review suggestions --- .../com/smartdevicelink/managers/file/DeleteFileOperation.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/file/DeleteFileOperation.java b/base/src/main/java/com/smartdevicelink/managers/file/DeleteFileOperation.java index b732a525f..522c5c7e9 100644 --- a/base/src/main/java/com/smartdevicelink/managers/file/DeleteFileOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/file/DeleteFileOperation.java @@ -70,10 +70,12 @@ class DeleteFileOperation extends Task { if (getState() == Task.CANCELED) { return; } - if (!mutableRemoteFileNames.contains(fileName) && completionListener != null) { - String errorMessage = "File to delete is no longer on the head unit, aborting operation"; - // Returning BaseFileManager.SPACE_AVAILABLE_MAX_VALUE for bytesAvaialble as a placeHolder, it will not get updated in BaseFileManager as long as success returned is false. - completionListener.onComplete(false, BaseFileManager.SPACE_AVAILABLE_MAX_VALUE, mutableRemoteFileNames, errorMessage); + if (!mutableRemoteFileNames.contains(fileName)) { + if (completionListener != null) { + String errorMessage = "File to delete is no longer on the head unit, aborting operation"; + // Returning BaseFileManager.SPACE_AVAILABLE_MAX_VALUE for bytesAvaialble as a placeHolder, it will not get updated in BaseFileManager as long as success returned is false. + completionListener.onComplete(false, BaseFileManager.SPACE_AVAILABLE_MAX_VALUE, mutableRemoteFileNames, errorMessage); + } onFinished(); return; } -- cgit v1.2.1 From e55f735c735d96b53ee8fa098f6c8cca0d13c38d Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Thu, 30 Sep 2021 15:33:43 -0400 Subject: Update unit test to fix instances of random fails --- .../com/smartdevicelink/managers/screen/TextAndGraphicManagerTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicManagerTests.java b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicManagerTests.java index 9b948f808..3999e7d2c 100644 --- a/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicManagerTests.java +++ b/android/sdl_android/src/androidTest/java/com/smartdevicelink/managers/screen/TextAndGraphicManagerTests.java @@ -275,6 +275,7 @@ public class TextAndGraphicManagerTests { @Test public void testOperationManagement() { + textAndGraphicManager.transactionQueue.pause(); textAndGraphicManager.isDirty = true; textAndGraphicManager.updateOperation = null; textAndGraphicManager.update(null); -- cgit v1.2.1 From c97f550df3ace3d85926633952f725964f9614b8 Mon Sep 17 00:00:00 2001 From: Julian Kast Date: Thu, 30 Sep 2021 16:08:51 -0400 Subject: Add new TaskMaster release to Java Suite Library --- android/sdl_android/libs/TaskMaster-0.5.jar | Bin 17590 -> 0 bytes android/sdl_android/libs/Taskmaster-0.6.jar | Bin 0 -> 17636 bytes javaEE/javaEE/libs/TaskMaster-0.5.jar | Bin 17590 -> 0 bytes javaEE/javaEE/libs/Taskmaster-0.6.jar | Bin 0 -> 17636 bytes javaSE/javaSE/libs/TaskMaster-0.5.jar | Bin 17590 -> 0 bytes javaSE/javaSE/libs/Taskmaster-0.6.jar | Bin 0 -> 17636 bytes 6 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 android/sdl_android/libs/TaskMaster-0.5.jar create mode 100644 android/sdl_android/libs/Taskmaster-0.6.jar delete mode 100644 javaEE/javaEE/libs/TaskMaster-0.5.jar create mode 100644 javaEE/javaEE/libs/Taskmaster-0.6.jar delete mode 100644 javaSE/javaSE/libs/TaskMaster-0.5.jar create mode 100644 javaSE/javaSE/libs/Taskmaster-0.6.jar diff --git a/android/sdl_android/libs/TaskMaster-0.5.jar b/android/sdl_android/libs/TaskMaster-0.5.jar deleted file mode 100644 index a7881051b..000000000 Binary files a/android/sdl_android/libs/TaskMaster-0.5.jar and /dev/null differ diff --git a/android/sdl_android/libs/Taskmaster-0.6.jar b/android/sdl_android/libs/Taskmaster-0.6.jar new file mode 100644 index 000000000..8f249d0f7 Binary files /dev/null and b/android/sdl_android/libs/Taskmaster-0.6.jar differ diff --git a/javaEE/javaEE/libs/TaskMaster-0.5.jar b/javaEE/javaEE/libs/TaskMaster-0.5.jar deleted file mode 100644 index a7881051b..000000000 Binary files a/javaEE/javaEE/libs/TaskMaster-0.5.jar and /dev/null differ diff --git a/javaEE/javaEE/libs/Taskmaster-0.6.jar b/javaEE/javaEE/libs/Taskmaster-0.6.jar new file mode 100644 index 000000000..8f249d0f7 Binary files /dev/null and b/javaEE/javaEE/libs/Taskmaster-0.6.jar differ diff --git a/javaSE/javaSE/libs/TaskMaster-0.5.jar b/javaSE/javaSE/libs/TaskMaster-0.5.jar deleted file mode 100644 index a7881051b..000000000 Binary files a/javaSE/javaSE/libs/TaskMaster-0.5.jar and /dev/null differ diff --git a/javaSE/javaSE/libs/Taskmaster-0.6.jar b/javaSE/javaSE/libs/Taskmaster-0.6.jar new file mode 100644 index 000000000..8f249d0f7 Binary files /dev/null and b/javaSE/javaSE/libs/Taskmaster-0.6.jar differ -- cgit v1.2.1 From 00be8c4276e8f4a82a69f4990fae5561765f155b Mon Sep 17 00:00:00 2001 From: Henigan Date: Mon, 4 Oct 2021 11:18:29 -0400 Subject: Release Candidate Changes --- .gitmodules | 2 +- CHANGELOG.md | 47 ++++++++++------------ VERSION | 2 +- android/sdl_android/build.gradle | 4 +- .../smartdevicelink/protocol/SdlProtocolBase.java | 2 +- generator/rpc_spec | 2 +- javaSE/javaSE/build.gradle | 2 +- 7 files changed, 29 insertions(+), 32 deletions(-) diff --git a/.gitmodules b/.gitmodules index cd0213164..d2f68e804 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "generator/rpc_spec"] path = generator/rpc_spec url = https://github.com/smartdevicelink/rpc_spec.git - branch = master + branch = version/8.0.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index 23e2aa55c..b7692ed29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,50 +1,47 @@ -# 5.2.0 Release Notes +# 5.3.0 Release Notes ## Summary: ||Version| |--|--| -| **Protocol** | 5.4.0 -| **RPC** | 7.1.0 +| **Protocol** | 5.4.1 +| **RPC** | 8.0.0 | **Tested Targeting** | Android 30 -## Bug Fixes / Enhancements: - -- [MenuManager sending secondary image with menuCells when menuCommandSecondaryImage is not supported.](https://github.com/smartdevicelink/sdl_java_suite/issues/1688) - -- [IllegalArgumentException when starting video stream with custom RPC MTU](https://github.com/smartdevicelink/sdl_java_suite/issues/1667) +## Features: -- [Send voiceCommand with duplicate strings](https://github.com/smartdevicelink/sdl_java_suite/issues/1664) +- [[SDL 0293] Enable OEM exclusive apps support](https://github.com/smartdevicelink/sdl_java_suite/issues/1588) -- [Two voiceCommands contains the same string](https://github.com/smartdevicelink/sdl_java_suite/issues/1677) - -- [java.lang.NegativeArraySizeException Crash at SdlPsm.java line 241 com.smartdevicelink.transport.SdlPsm.transitionOnInput](https://github.com/smartdevicelink/sdl_java_suite/issues/1678) - -- [Exception handling variances](https://github.com/smartdevicelink/sdl_java_suite/issues/1687) +## Bug Fixes / Enhancements: -- [Allow SdlDeviceListener to start after BT connection](https://github.com/smartdevicelink/sdl_java_suite/pull/1685) +- [New TaskMaster release needs to be added to Java Suite Library ](https://github.com/smartdevicelink/sdl_java_suite/issues/1745) -- [voiceCommand that contains no string should be removed](https://github.com/smartdevicelink/sdl_java_suite/issues/1675) +- [File Manager will upload the same file multiple times](https://github.com/smartdevicelink/sdl_java_suite/issues/1736) -- [Image returned as "not uploaded" in certain circumstances when it's already uploaded, leading to the image being unusable](https://github.com/smartdevicelink/sdl_java_suite/issues/1692) +- [Fix description for `SeatControlCapabilities` and `RadioControlCapabilities`](https://github.com/smartdevicelink/sdl_java_suite/issues/1739) -- [Primary Graphic not sent to SDL Core for Media Template ](https://github.com/smartdevicelink/sdl_java_suite/issues/1690) +- [AudioStreamManager and AlertManager have the same queue ID](https://github.com/smartdevicelink/sdl_java_suite/issues/1742) -- [Race condition leads to NPE in TransportManager](https://github.com/smartdevicelink/sdl_java_suite/issues/1703) +- [java.lang.IndexOutOfBoundsException Crash: SdlRouterService.java line 3275 ](https://github.com/smartdevicelink/sdl_java_suite/issues/1741) -- [Avoid deleting and setting identical voice commands](https://github.com/smartdevicelink/sdl_java_suite/issues/1676) +- [Old devices with SDL 2.0 can't display text fields and SoftButtons](https://github.com/smartdevicelink/sdl_java_suite/issues/1729) -- [Sdl disconnection is not notified to the app](https://github.com/smartdevicelink/sdl_java_suite/issues/1697) +- [Swapping de-duplicated menu items causes incorrect unique titles](https://github.com/smartdevicelink/sdl_java_suite/issues/1723) -- [PredefinedLayout.NON_MEDIA not found in templatesAvailable](https://github.com/smartdevicelink/sdl_java_suite/issues/1705) +- [MenuCell title update issue](https://github.com/smartdevicelink/sdl_java_suite/issues/1651) -- [Lockscreen should show again after dismissal if a DD notification is received where DismissalEnabled is false](https://github.com/smartdevicelink/sdl_java_suite/issues/1695) +- [Refactor the Menu Manager to Use Queues ](https://github.com/smartdevicelink/sdl_java_suite/issues/1605) -- [Choice Cells and Menu Cells do not take which properties are available into account for uniqueness](https://github.com/smartdevicelink/sdl_java_suite/issues/1682) +- [Security queries are not implemented to spec](https://github.com/smartdevicelink/sdl_java_suite/issues/1720) -- [BSON library should be updated to the latest version (1.2.5)](https://github.com/smartdevicelink/sdl_java_suite/issues/1712) +- [Choice Set Present followed directly by a Delete can have undefined behavior](https://github.com/smartdevicelink/sdl_java_suite/issues/1718) +- [Back-to-back choice sets can fail](https://github.com/smartdevicelink/sdl_java_suite/issues/1717) +- [Github CI fails even though tests pass locally ](https://github.com/smartdevicelink/sdl_java_suite/issues/1731) +- [Handler instances using deprecated constructors ](https://github.com/smartdevicelink/sdl_java_suite/issues/1696) +- [[SDL 0236] Update mismatch in TireStatus structure](https://github.com/smartdevicelink/sdl_java_suite/issues/1089) +- [Choices not saved as preloaded if some choices fail](https://github.com/smartdevicelink/sdl_java_suite/issues/1715) \ No newline at end of file diff --git a/VERSION b/VERSION index 7cbea073b..03f488b07 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.2.0 \ No newline at end of file +5.3.0 diff --git a/android/sdl_android/build.gradle b/android/sdl_android/build.gradle index 73665a4b7..489da844e 100644 --- a/android/sdl_android/build.gradle +++ b/android/sdl_android/build.gradle @@ -5,7 +5,7 @@ android { defaultConfig { minSdkVersion 16 targetSdkVersion 30 - versionCode 20 + versionCode 21 versionName new File(projectDir.path, ('/../../VERSION')).text.trim() buildConfigField "String", "VERSION_NAME", '\"' + versionName + '\"' resValue "string", "SDL_LIB_VERSION", '\"' + versionName + '\"' @@ -42,7 +42,7 @@ android { dependencies { api fileTree(dir: 'libs', include: ['*.jar']) - //api 'com.livio.taskmaster:taskmaster:0.4.0' + //api 'com.livio.taskmaster:taskmaster:0.6.0' api 'com.smartdevicelink:bson_java_port:1.2.5' api 'androidx.lifecycle:lifecycle-extensions:2.2.0' api 'androidx.annotation:annotation:1.1.0' diff --git a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java index e80255d1b..f7dd797d0 100644 --- a/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java +++ b/base/src/main/java/com/smartdevicelink/protocol/SdlProtocolBase.java @@ -89,7 +89,7 @@ public class SdlProtocolBase { public static final int V2_HEADER_SIZE = 12; //If increasing MAX PROTOCOL VERSION major version, make sure to alter it in SdlPsm - private static final Version MAX_PROTOCOL_VERSION = new Version(5, 4, 0); + private static final Version MAX_PROTOCOL_VERSION = new Version(5, 4, 1); public static final int V1_V2_MTU_SIZE = 1500; public static final int V3_V4_MTU_SIZE = 131072; diff --git a/generator/rpc_spec b/generator/rpc_spec index 72632f946..20d64e68e 160000 --- a/generator/rpc_spec +++ b/generator/rpc_spec @@ -1 +1 @@ -Subproject commit 72632f946941d63a57ee5e99896e3eae3627f7dd +Subproject commit 20d64e68e1a2af630ac9f33888c971573bcebdd9 diff --git a/javaSE/javaSE/build.gradle b/javaSE/javaSE/build.gradle index 94b7a54d1..aa97ea53a 100644 --- a/javaSE/javaSE/build.gradle +++ b/javaSE/javaSE/build.gradle @@ -32,7 +32,7 @@ dependencies { extraLibs 'org.mongodb:bson:4.0.5' extraLibs 'androidx.annotation:annotation:1.1.0' extraLibs 'org.java-websocket:Java-WebSocket:1.3.9' - //extraLibs 'com.livio.taskmaster:taskmaster:0.4.0' + //extraLibs 'com.livio.taskmaster:taskmaster:0.6.0' configurations.api.extendsFrom(configurations.extraLibs) } -- cgit v1.2.1 From 0aa09102dd5e8b751df06069703ff881677fee1a Mon Sep 17 00:00:00 2001 From: Henigan Date: Mon, 4 Oct 2021 11:36:18 -0400 Subject: Change BC and gradle properties to 5.3 --- android/sdl_android/gradle.properties | 2 +- javaEE/javaEE/gradle.properties | 2 +- javaSE/javaSE/gradle.properties | 2 +- javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/android/sdl_android/gradle.properties b/android/sdl_android/gradle.properties index 3136c040b..d93b695d2 100644 --- a/android/sdl_android/gradle.properties +++ b/android/sdl_android/gradle.properties @@ -1,6 +1,6 @@ GROUP=com.smartdevicelink POM_ARTIFACT_ID=sdl_android -VERSION_NAME=5.2.0 +VERSION_NAME=5.3.0 POM_NAME=sdl_android POM_PACKAGING=aar diff --git a/javaEE/javaEE/gradle.properties b/javaEE/javaEE/gradle.properties index 9dbf6adf2..6f84e23f1 100644 --- a/javaEE/javaEE/gradle.properties +++ b/javaEE/javaEE/gradle.properties @@ -1,6 +1,6 @@ GROUP=com.smartdevicelink POM_ARTIFACT_ID=sdl_java_ee -VERSION_NAME=5.2.0 +VERSION_NAME=5.3.0 POM_NAME=sdl_java_ee POM_PACKAGING=jar diff --git a/javaSE/javaSE/gradle.properties b/javaSE/javaSE/gradle.properties index b8f580351..a9e29ea87 100644 --- a/javaSE/javaSE/gradle.properties +++ b/javaSE/javaSE/gradle.properties @@ -1,6 +1,6 @@ GROUP=com.smartdevicelink POM_ARTIFACT_ID=sdl_java_se -VERSION_NAME=5.2.0 +VERSION_NAME=5.3.0 POM_NAME=sdl_java_se POM_PACKAGING=jar diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java index 30aaeaed4..51add4ed2 100644 --- a/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java +++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java @@ -32,5 +32,5 @@ package com.smartdevicelink; // THIS FILE IS AUTO GENERATED, DO NOT MODIFY!! public final class BuildConfig { - public static final String VERSION_NAME = "5.2.0"; + public static final String VERSION_NAME = "5.3.0"; } \ No newline at end of file -- cgit v1.2.1 From 60230823b341eb9e2e95f2c0649a322c7d5b9db1 Mon Sep 17 00:00:00 2001 From: Henigan Date: Mon, 4 Oct 2021 11:56:02 -0400 Subject: Change to RC_5.3.0 --- VERSION | 2 +- android/sdl_android/gradle.properties | 2 +- javaEE/javaEE/gradle.properties | 2 +- javaSE/javaSE/gradle.properties | 2 +- javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index 03f488b07..5a2442574 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.3.0 +RC_5.3.0 diff --git a/android/sdl_android/gradle.properties b/android/sdl_android/gradle.properties index d93b695d2..44ca7bd1b 100644 --- a/android/sdl_android/gradle.properties +++ b/android/sdl_android/gradle.properties @@ -1,6 +1,6 @@ GROUP=com.smartdevicelink POM_ARTIFACT_ID=sdl_android -VERSION_NAME=5.3.0 +VERSION_NAME=RC_5.3.0 POM_NAME=sdl_android POM_PACKAGING=aar diff --git a/javaEE/javaEE/gradle.properties b/javaEE/javaEE/gradle.properties index 6f84e23f1..70cd4e63f 100644 --- a/javaEE/javaEE/gradle.properties +++ b/javaEE/javaEE/gradle.properties @@ -1,6 +1,6 @@ GROUP=com.smartdevicelink POM_ARTIFACT_ID=sdl_java_ee -VERSION_NAME=5.3.0 +VERSION_NAME=RC_5.3.0 POM_NAME=sdl_java_ee POM_PACKAGING=jar diff --git a/javaSE/javaSE/gradle.properties b/javaSE/javaSE/gradle.properties index a9e29ea87..9c0a0ca96 100644 --- a/javaSE/javaSE/gradle.properties +++ b/javaSE/javaSE/gradle.properties @@ -1,6 +1,6 @@ GROUP=com.smartdevicelink POM_ARTIFACT_ID=sdl_java_se -VERSION_NAME=5.3.0 +VERSION_NAME=RC_5.3.0 POM_NAME=sdl_java_se POM_PACKAGING=jar diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java index 51add4ed2..206310779 100644 --- a/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java +++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java @@ -32,5 +32,5 @@ package com.smartdevicelink; // THIS FILE IS AUTO GENERATED, DO NOT MODIFY!! public final class BuildConfig { - public static final String VERSION_NAME = "5.3.0"; + public static final String VERSION_NAME = "RC_5.3.0"; } \ No newline at end of file -- cgit v1.2.1 From cd4131baaa96aae98a1d4bcb623113e07f5d2a07 Mon Sep 17 00:00:00 2001 From: Henigan Date: Wed, 6 Oct 2021 10:57:03 -0400 Subject: Align to JavaScript Behavior --- .../managers/screen/choiceset/PreloadPresentChoicesOperation.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java index 4c9d5a3b5..8c6ebca6f 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/choiceset/PreloadPresentChoicesOperation.java @@ -629,9 +629,13 @@ class PreloadPresentChoicesOperation extends Task { private void updateChoiceSet(ChoiceSet choiceSet, HashSet loadedCells, HashSet cellsToUpload) { ArrayList choiceSetCells = new ArrayList<>(); + ArrayList loadedCellsList = new ArrayList<>(loadedCells); + ArrayList CellsToUploadList = new ArrayList<>(cellsToUpload); for (ChoiceCell cell : choiceSet.getChoices()) { - if (loadedCells.contains(cell) || cellsToUpload.contains(cell)) { - choiceSetCells.add(cell); + if (loadedCells.contains(cell)) { + choiceSetCells.add(loadedCellsList.get(loadedCellsList.indexOf(cell))); + } else if (cellsToUpload.contains(cell)) { + choiceSetCells.add(CellsToUploadList.get(CellsToUploadList.indexOf(cell))); } } this.choiceSet.setChoices((List) choiceSetCells.clone()); -- cgit v1.2.1 From b25de3ae487d8161d56c89b1a6e19953a29c1210 Mon Sep 17 00:00:00 2001 From: Jordyn Mackool Date: Tue, 19 Oct 2021 15:59:13 -0400 Subject: Fixes issue #1754 --- .../src/main/java/com/smartdevicelink/transport/SdlRouterService.java | 2 +- .../src/main/java/com/smartdevicelink/proxy/rpc/SetMediaClockTimer.java | 2 +- base/src/main/java/com/smartdevicelink/proxy/rpc/Show.java | 2 +- base/src/main/java/com/smartdevicelink/proxy/rpc/SystemCapability.java | 2 +- base/src/main/java/com/smartdevicelink/util/BitConverter.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java index af63c9219..c5e7cb174 100644 --- a/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java +++ b/android/sdl_android/src/main/java/com/smartdevicelink/transport/SdlRouterService.java @@ -2416,7 +2416,7 @@ public class SdlRouterService extends Service { /** * Set the connection establishment status of the particular device * - * @param address address of the device in quesiton + * @param address address of the device in question * @param hasSDLConnected true if a connection has been established, false if not */ protected void setSDLConnectedStatus(String address, boolean hasSDLConnected) { diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/SetMediaClockTimer.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/SetMediaClockTimer.java index 60b9c524f..099224638 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/SetMediaClockTimer.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/SetMediaClockTimer.java @@ -72,7 +72,7 @@ import java.util.Hashtable; * endTime * StartTime * EndTime can be provided for "COUNTUP" and "COUNTDOWN"; to be used to calculate any visual progress bar (if not provided, this feature is ignored) - * If endTime is greater then startTime for COUNTDOWN or less than startTime for COUNTUP, then the request will return an INVALID_DATA. + * If endTime is greater than startTime for COUNTDOWN or less than startTime for COUNTUP, then the request will return an INVALID_DATA. * endTime will be ignored for "RESUME", and "CLEAR" * endTime can be sent for "PAUSE", in which case it will update the paused endTime * N diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/Show.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/Show.java index 5962a04fd..e9bdcd905 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/Show.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/Show.java @@ -534,7 +534,7 @@ public class Show extends RPCRequest { } /** - * Sets the the Soft buttons defined by the App + * Sets the Soft buttons defined by the App * * @param softButtons a List value representing the Soft buttons defined by the * App diff --git a/base/src/main/java/com/smartdevicelink/proxy/rpc/SystemCapability.java b/base/src/main/java/com/smartdevicelink/proxy/rpc/SystemCapability.java index e930bf533..c39ef4703 100644 --- a/base/src/main/java/com/smartdevicelink/proxy/rpc/SystemCapability.java +++ b/base/src/main/java/com/smartdevicelink/proxy/rpc/SystemCapability.java @@ -39,7 +39,7 @@ import com.smartdevicelink.proxy.rpc.enums.SystemCapabilityType; import java.util.Hashtable; /** - * Struct that indicates the a SystemCapabilityType and houses different structs to describe particular capabilities + * Struct that indicates the SystemCapabilityType and houses different structs to describe particular capabilities */ public class SystemCapability extends RPCStruct { diff --git a/base/src/main/java/com/smartdevicelink/util/BitConverter.java b/base/src/main/java/com/smartdevicelink/util/BitConverter.java index 94695802f..df507d6fe 100644 --- a/base/src/main/java/com/smartdevicelink/util/BitConverter.java +++ b/base/src/main/java/com/smartdevicelink/util/BitConverter.java @@ -163,7 +163,7 @@ public class BitConverter { * Converts the byte array into a string of hex values. * * @param bytes byte array that will be converted to hex - * @param end EXCLUSIVE so if it it receives 10 it will print 0-9 + * @param end EXCLUSIVE so if it receives 10 it will print 0-9 * @return the String containing converted hex values or null if byte array is null */ public static String bytesToHex(byte[] bytes, int end) { -- cgit v1.2.1 From 92b8ccbd73029a3ef8d540b839ee28b726d0093f Mon Sep 17 00:00:00 2001 From: Robert Henigan Date: Mon, 25 Oct 2021 15:28:00 -0400 Subject: Fix NPE for processControlService (#1757) --- base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java index d68e9cd20..5fcc8d8ca 100644 --- a/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java +++ b/base/src/main/java/com/smartdevicelink/session/BaseSdlSession.java @@ -240,6 +240,7 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ // Assemble a security query payload header for our response SecurityQueryPayload responseHeader = new SecurityQueryPayload(); + byte[] returnBytes; if (iNumBytes == null || iNumBytes <= 0) { DebugTool.logError(TAG, "Internal Error processing control service"); @@ -247,16 +248,18 @@ public abstract class BaseSdlSession implements ISdlProtocol, ISecurityInitializ responseHeader.setQueryType(SecurityQueryType.NOTIFICATION); responseHeader.setCorrelationID(msg.getCorrID()); responseHeader.setJsonSize(0); + returnBytes = new byte[12]; } else { responseHeader.setQueryID(SecurityQueryID.SEND_HANDSHAKE_DATA); responseHeader.setQueryType(SecurityQueryType.RESPONSE); responseHeader.setCorrelationID(msg.getCorrID()); responseHeader.setJsonSize(0); + returnBytes = new byte[iNumBytes + 12]; + System.arraycopy(dataToRead, 0, returnBytes, 12, iNumBytes); } - byte[] returnBytes = new byte[iNumBytes + 12]; + System.arraycopy(responseHeader.assembleHeaderBytes(), 0, returnBytes, 0, 12); - System.arraycopy(dataToRead, 0, returnBytes, 12, iNumBytes); ProtocolMessage protocolMessage = new ProtocolMessage(); protocolMessage.setSessionType(SessionType.CONTROL); -- cgit v1.2.1 From 1588fb9341891046c416b7db7d557e33fb33b80e Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi <599206+bilal-alsharifi@users.noreply.github.com> Date: Tue, 26 Oct 2021 10:29:39 -0400 Subject: Log warning message if we set voice sommands to sub menus (#1761) --- .../smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java index f274130d3..210b24ba9 100644 --- a/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java +++ b/base/src/main/java/com/smartdevicelink/managers/screen/menu/MenuReplaceUtilities.java @@ -52,6 +52,7 @@ import com.smartdevicelink.proxy.rpc.enums.ImageFieldName; import com.smartdevicelink.proxy.rpc.enums.MenuLayout; import com.smartdevicelink.proxy.rpc.enums.TextFieldName; import com.smartdevicelink.proxy.rpc.listeners.OnMultipleRequestListener; +import com.smartdevicelink.util.DebugTool; import java.util.ArrayList; import java.util.HashMap; @@ -64,6 +65,7 @@ import java.util.Set; * Created by Bilal Alsharifi on 1/25/21. */ class MenuReplaceUtilities { + private static final String TAG = "MenuReplaceUtilities"; private static int menuId = 0; static int getNextMenuId() { @@ -271,6 +273,10 @@ class MenuReplaceUtilities { boolean shouldCellIncludeSecondaryImage = cell.getSecondaryArtwork() != null && cell.getSecondaryArtwork().getImageRPC() != null && shouldCellIncludeSecondaryImageFromCell(cell, fileManager, windowCapability); Image secondaryIcon = (shouldCellIncludeSecondaryImage ? cell.getSecondaryArtwork().getImageRPC() : null); + if (cell.getVoiceCommands() != null && !cell.getVoiceCommands().isEmpty()) { + DebugTool.logWarning(TAG, "Setting voice commands for submenu cells is not supported. The voice commands will not be set."); + } + MenuLayout submenuLayout; List availableMenuLayouts = windowCapability != null ? windowCapability.getMenuLayoutsAvailable() : null; if (cell.getSubMenuLayout() != null && availableMenuLayouts != null && availableMenuLayouts.contains(cell.getSubMenuLayout())) { -- cgit v1.2.1 From 5b6bbf518ae2df9d0e192f809d699391c5ae4ce8 Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 26 Oct 2021 10:59:36 -0400 Subject: Update CHANGELOG.md --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7692ed29..9d760b0d1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,4 +44,8 @@ - [[SDL 0236] Update mismatch in TireStatus structure](https://github.com/smartdevicelink/sdl_java_suite/issues/1089) -- [Choices not saved as preloaded if some choices fail](https://github.com/smartdevicelink/sdl_java_suite/issues/1715) \ No newline at end of file +- [Choices not saved as preloaded if some choices fail](https://github.com/smartdevicelink/sdl_java_suite/issues/1715) + +- [MenuManager should print warning when trying to send AddSubMenu with voice commands](https://github.com/smartdevicelink/sdl_java_suite/issues/1760) + +- [Spelling/ Grammar Updates](https://github.com/smartdevicelink/sdl_java_suite/issues/1754) \ No newline at end of file -- cgit v1.2.1 From 25ce7decce0d64f1003197b591e78bded179a34f Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Tue, 26 Oct 2021 11:19:59 -0400 Subject: Update version to 5.3.0 --- VERSION | 2 +- android/sdl_android/gradle.properties | 2 +- javaEE/javaEE/gradle.properties | 2 +- javaSE/javaSE/gradle.properties | 2 +- javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/VERSION b/VERSION index 5a2442574..03f488b07 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -RC_5.3.0 +5.3.0 diff --git a/android/sdl_android/gradle.properties b/android/sdl_android/gradle.properties index 44ca7bd1b..d93b695d2 100644 --- a/android/sdl_android/gradle.properties +++ b/android/sdl_android/gradle.properties @@ -1,6 +1,6 @@ GROUP=com.smartdevicelink POM_ARTIFACT_ID=sdl_android -VERSION_NAME=RC_5.3.0 +VERSION_NAME=5.3.0 POM_NAME=sdl_android POM_PACKAGING=aar diff --git a/javaEE/javaEE/gradle.properties b/javaEE/javaEE/gradle.properties index 70cd4e63f..6f84e23f1 100644 --- a/javaEE/javaEE/gradle.properties +++ b/javaEE/javaEE/gradle.properties @@ -1,6 +1,6 @@ GROUP=com.smartdevicelink POM_ARTIFACT_ID=sdl_java_ee -VERSION_NAME=RC_5.3.0 +VERSION_NAME=5.3.0 POM_NAME=sdl_java_ee POM_PACKAGING=jar diff --git a/javaSE/javaSE/gradle.properties b/javaSE/javaSE/gradle.properties index 9c0a0ca96..a9e29ea87 100644 --- a/javaSE/javaSE/gradle.properties +++ b/javaSE/javaSE/gradle.properties @@ -1,6 +1,6 @@ GROUP=com.smartdevicelink POM_ARTIFACT_ID=sdl_java_se -VERSION_NAME=RC_5.3.0 +VERSION_NAME=5.3.0 POM_NAME=sdl_java_se POM_PACKAGING=jar diff --git a/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java b/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java index 206310779..51add4ed2 100644 --- a/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java +++ b/javaSE/javaSE/src/main/java/com/smartdevicelink/BuildConfig.java @@ -32,5 +32,5 @@ package com.smartdevicelink; // THIS FILE IS AUTO GENERATED, DO NOT MODIFY!! public final class BuildConfig { - public static final String VERSION_NAME = "RC_5.3.0"; + public static final String VERSION_NAME = "5.3.0"; } \ No newline at end of file -- cgit v1.2.1 From 7ccf3c2a1dcbd2a466eb49e70e68511d58071fbb Mon Sep 17 00:00:00 2001 From: Bilal Alsharifi Date: Wed, 27 Oct 2021 09:59:26 -0400 Subject: Update rpc_spec submodule to use master --- .gitmodules | 2 +- generator/rpc_spec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index d2f68e804..cd0213164 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "generator/rpc_spec"] path = generator/rpc_spec url = https://github.com/smartdevicelink/rpc_spec.git - branch = version/8.0.0 + branch = master diff --git a/generator/rpc_spec b/generator/rpc_spec index 20d64e68e..6537500b4 160000 --- a/generator/rpc_spec +++ b/generator/rpc_spec @@ -1 +1 @@ -Subproject commit 20d64e68e1a2af630ac9f33888c971573bcebdd9 +Subproject commit 6537500b45f65e02d884da9d73d6820ba7b0b1f5 -- cgit v1.2.1