All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.smartdevicelink.managers.screen.menu.MenuReplaceUtilities Maven / Gradle / Ivy

/*
 * 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