com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sdl_java_se Show documentation
Show all versions of sdl_java_se Show documentation
The app library component of SDL is meant to run on the end userâs smart-device from within SDL enabled apps, as an embedded app, or connected to the cloud. App libraries allow the apps to connect to SDL enabled head-units and hardware through bluetooth, USB, and TCP for Android, and cloud and embedded apps can connect through web sockets, Java Beans, and other custom transports. Once the library establishes a connection between the smart device and head-unit through the preferred method of transport, the two components are able to communicate using the SDL defined protocol. The app integrating this library project is then able to expose its functionality to the head-unit through text, media, and other interactive elements.
/*
* 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;
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;
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.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 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.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.
*/
class MenuReplaceUtilities {
private static final String TAG = "MenuReplaceUtilities";
private static int menuId = 0;
static int getNextMenuId() {
return ++menuId;
}
/**
* Assign cell ids on a list of menu cells given a parent id (or no parent id)
*
* @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) {
for (MenuCell cell : menuCells) {
cell.setCellId(getNextMenuId());
if (parentId != parentIdNotFound) {
cell.setParentCellId(parentId);
}
if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) {
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 Set findAllArtworksToBeUploadedFromCells(ISdl internalInterface, List cells, FileManager fileManager, WindowCapability windowCapability) {
// Make sure we can use images in the menus
if (!hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) {
return new HashSet<>();
}
Set artworks = new HashSet<>();
for (MenuCell cell : cells) {
if (fileManager != null) {
if (windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, cell) && fileManager.fileNeedsUpload(cell.getIcon())) {
artworks.add(cell.getIcon());
}
if (windowCapabilitySupportsSecondaryImage(windowCapability, cell) && fileManager.fileNeedsUpload(cell.getSecondaryArtwork())) {
artworks.add(cell.getSecondaryArtwork());
}
}
if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) {
artworks.addAll(findAllArtworksToBeUploadedFromCells(internalInterface, cell.getSubCells(), fileManager, windowCapability));
}
}
return artworks;
}
static boolean windowCapabilitySupportsPrimaryImage(ISdl internalInterface, WindowCapability windowCapability, MenuCell cell) {
boolean supportsImage;
if (cell.isSubMenuCell()) {
if (isRPCVersionBetween5And7(internalInterface) && hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon)) {
supportsImage = true;
} else {
supportsImage = hasImageFieldOfName(windowCapability, ImageFieldName.subMenuIcon);
}
} else {
supportsImage = hasImageFieldOfName(windowCapability, ImageFieldName.cmdIcon);
}
return supportsImage;
}
static boolean isRPCVersionBetween5And7(ISdl internalInterface) {
if (internalInterface != null && internalInterface.getSdlMsgVersion() != null) {
Version headUnitRPCVersion = new Version(internalInterface.getSdlMsgVersion());
Version minRPCVersion = new Version(5, 0, 0);
Version maxRPCVersion = new Version(7, 0, 0);
// If RPC version is >= 5.0 && < 7.0
return (headUnitRPCVersion.isNewerThan(minRPCVersion) == 0 || headUnitRPCVersion.isBetween(minRPCVersion, maxRPCVersion) == 1);
}
return false;
}
static boolean windowCapabilitySupportsSecondaryImage(WindowCapability windowCapability, MenuCell cell) {
return cell.isSubMenuCell() ? hasImageFieldOfName(windowCapability, ImageFieldName.menuSubMenuSecondaryImage) : hasImageFieldOfName(windowCapability, ImageFieldName.menuCommandSecondaryImage);
}
// 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(ISdl internalInterface, MenuCell cell, FileManager fileManager, WindowCapability windowCapability) {
boolean supportsImage = windowCapabilitySupportsPrimaryImage(internalInterface, windowCapability, cell);
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 = windowCapabilitySupportsSecondaryImage(windowCapability, cell);
return cell.getSecondaryArtwork() != null && supportsImage && (fileManager.hasUploadedFile(cell.getSecondaryArtwork()) || cell.getSecondaryArtwork().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 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.isSubMenuCell()) {
DeleteSubMenu delete = new DeleteSubMenu(cell.getCellId());
deletes.add(delete);
} else {
DeleteCommand delete = new DeleteCommand(cell.getCellId());
deletes.add(delete);
}
}
return deletes;
}
static List mainMenuCommandsForCells(ISdl internalInterface, 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
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.isSubMenuCell()) {
commands.add(subMenuCommandForMenuCell(internalInterface, addCell, fileManager, windowCapability, menuInteger, defaultSubmenuLayout));
} else {
commands.add(commandForMenuCell(internalInterface, addCell, fileManager, windowCapability, menuInteger));
}
break;
}
}
}
return commands;
}
static List subMenuCommandsForCells(ISdl internalInterface, List cells, FileManager fileManager, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) {
List commands = new ArrayList<>();
for (MenuCell cell : cells) {
if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) {
commands.addAll(allCommandsForCells(internalInterface, cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout));
}
}
return commands;
}
static List allCommandsForCells(ISdl internalInterface, List cells, FileManager fileManager, WindowCapability windowCapability, MenuLayout defaultSubmenuLayout) {
List commands = new ArrayList<>();
for (int cellIndex = 0; cellIndex < cells.size(); cellIndex++) {
MenuCell cell = cells.get(cellIndex);
if (cell.isSubMenuCell()) {
commands.add(subMenuCommandForMenuCell(internalInterface, cell, fileManager, windowCapability, cellIndex, defaultSubmenuLayout));
// Recursively grab the commands for all the sub cells
if (!cell.getSubCells().isEmpty()) {
commands.addAll(allCommandsForCells(internalInterface, cell.getSubCells(), fileManager, windowCapability, defaultSubmenuLayout));
}
} else {
commands.add(commandForMenuCell(internalInterface, cell, fileManager, windowCapability, cellIndex));
}
}
return commands;
}
static AddCommand commandForMenuCell(ISdl internalInterface, MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position) {
AddCommand command = new AddCommand(cell.getCellId());
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);
params.setPosition(position);
command.setMenuParams(params);
if (cell.getVoiceCommands() != null && !cell.getVoiceCommands().isEmpty()) {
command.setVrCommands(cell.getVoiceCommands());
} else {
command.setVrCommands(null);
}
boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && shouldCellIncludePrimaryImageFromCell(internalInterface, cell, fileManager, windowCapability);
command.setCmdIcon(shouldCellIncludePrimaryImage ? cell.getIcon().getImageRPC() : null);
boolean shouldCellIncludeSecondaryImage = cell.getSecondaryArtwork() != null && shouldCellIncludeSecondaryImageFromCell(cell, fileManager, windowCapability);
command.setSecondaryImage(shouldCellIncludeSecondaryImage ? cell.getSecondaryArtwork().getImageRPC() : null);
return command;
}
static AddSubMenu subMenuCommandForMenuCell(ISdl internalInterface, MenuCell cell, FileManager fileManager, WindowCapability windowCapability, int position, MenuLayout defaultSubmenuLayout) {
boolean shouldCellIncludePrimaryImage = cell.getIcon() != null && cell.getIcon().getImageRPC() != null && shouldCellIncludePrimaryImageFromCell(internalInterface, 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);
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())) {
submenuLayout = cell.getSubMenuLayout();
} else {
submenuLayout = defaultSubmenuLayout;
}
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)
.setPosition(position)
.setMenuLayout(submenuLayout)
.setMenuIcon(icon)
.setSecondaryImage(secondaryIcon);
}
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
menuCellList.remove(menuCell);
return true;
} 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);
if (foundAndRemovedItem) {
menuCell.setSubCells(newList);
return true;
}
}
}
return false;
}
static boolean addCellWithCellId(int cellId, int position, List newMenuList, List mainMenuList) {
MenuCell addedCell = null;
for (MenuCell cell : newMenuList) {
if (cell.getCellId() == cellId) {
addedCell = cell;
break;
} else if (cell.isSubMenuCell() && !cell.getSubCells().isEmpty()) {
boolean success = addCellWithCellId(cellId, position, cell.getSubCells(), mainMenuList);
if (success) {
return true;
}
}
}
if (addedCell != null) {
return addMenuCell(addedCell, mainMenuList, position);
}
return false;
}
private static boolean addMenuCell(MenuCell cell, List menuCellList, int position) {
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 (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);
if (foundAndAddedItem) {
menuCell.setSubCells(newList);
return true;
}
}
}
return false;
}
private static void insertMenuCell(MenuCell cell, List cellList, int position) {
MenuCell cellToInsert = cell;
if (cellToInsert.isSubMenuCell()) {
// We should not add the subCells automatically when adding a parent cell
cellToInsert = cell.clone();
cellToInsert.getSubCells().clear();
}
if (position > cellList.size()) {
cellList.add(cellToInsert);
} else {
cellList.add(position, cellToInsert);
}
}
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()) {
listener.onComplete(true, errors);
return;
}
internalInterface.sendRPCs(requests, new OnMultipleRequestListener() {
@Override
public void onUpdate(int remainingRequests) {
}
@Override
public void onFinished() {
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()) {
errors.put(request, "Failed to send RPC. Result: " + response.getResultCode() + ". Info: " + response.getInfo());
}
listener.onResponse(request, response);
}
});
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy