Skip to content

Commit

Permalink
Merge pull request #456 from smartdevicelink/bugfix/choice-set-unique…
Browse files Browse the repository at this point in the history
…ness

Choice Cells and Menu Cells do not take which properties are available into account for uniqueness
  • Loading branch information
renonick87 authored Jun 4, 2021
2 parents 2cb7105 + 9a07a64 commit 0b123ab
Show file tree
Hide file tree
Showing 2 changed files with 187 additions and 3 deletions.
104 changes: 101 additions & 3 deletions lib/js/src/manager/screen/choiceset/_ChoiceSetManagerBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ import { KeypressMode } from '../../../rpc/enums/KeypressMode.js';
import { InteractionMode } from '../../../rpc/enums/InteractionMode.js';
import { PredefinedWindows } from '../../../rpc/enums/PredefinedWindows.js';
import { SystemCapabilityType } from '../../../rpc/enums/SystemCapabilityType.js';
import { _ManagerUtility } from '../../_ManagerUtility.js';
import { TextFieldName } from '../../../rpc/enums/TextFieldName.js';
import { ImageFieldName } from '../../../rpc/enums/ImageFieldName.js';

// operations and listeners
import { _CheckChoiceVrOptionalInterface } from './_CheckChoiceVrOptionalInterface.js';
Expand Down Expand Up @@ -301,7 +304,6 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
if (uniqueChoiceCells.findIndex(choice => choice.equals(choices[index])) === -1) {
uniqueChoiceCells.push(choices[index]);
}

if (choiceVoiceCommands !== null) {
choiceCellWithVoiceCommandCount++;
allVoiceCommandsCount += choiceVoiceCommands.length;
Expand Down Expand Up @@ -505,6 +507,32 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
return modifiedKeyboardConfiguration;
}

/**
* Finds non-unique choice cells and updates their unique text accordingly
* @param {ChoiceCell[]} strippedCells - Choice cells with their unsupported properties removed
* @param {ChoiceCell[]} unstrippedCells - The original choice cells
*/
_addUniqueNamesBasedOnStrippedCells (strippedCells, unstrippedCells) {
if (!Array.isArray(strippedCells) || !Array.isArray(unstrippedCells) || strippedCells.length !== unstrippedCells.length) {
return;
}
// array of unique choice cells
const cells = [];
// array of the count of how many times each cell has been found
const cellsCounter = [];
strippedCells.forEach((strippedCell, index) => {
// find if a previous cell was a duplicate and update unique text of the current cell if so
const duplicateIndex = cells.map(cell => cell.equals(strippedCell)).indexOf(true);
if (duplicateIndex >= 0) {
cellsCounter[duplicateIndex]++;
unstrippedCells[index]._setUniqueText(`${unstrippedCells[index].getText()} (${cellsCounter[duplicateIndex]})`);
} else {
cells.push(strippedCell);
cellsCounter.push(1);
}
});
}


/**
* Return an array of choice cells that have been preloaded to the head unit
Expand Down Expand Up @@ -594,20 +622,90 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
.setKeypressMode(KeypressMode.RESEND_CURRENT_ENTRY);
}

/**
* Clones a list of choice cells
* @param {ChoiceCell[]} originalList - A list of choice cells to be cloned
* @returns {ChoiceCell[]|null} - The cloned cell list
*/
_cloneChoiceCellList (originalList) {
if (!Array.isArray(originalList)) {
return null;
}
return originalList.map((choiceCell) => choiceCell.clone());
}

/**
* Modifies the choices names depending on SDL version
* @param {ChoiceCell[]} choices - The first list of choices
* @returns {ChoiceCell[]} - A deep copy of the name modified choices
*/
_getChoicesToBeUploadedWithArray (choices) {
const choicesClone = this._cloneChoiceCellList(choices);
// 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 (choices !== null && this._lifecycleManager.getSdlMsgVersion() !== null
&& (this._lifecycleManager.getSdlMsgVersion().getMajorVersion() < 7
|| (this._lifecycleManager.getSdlMsgVersion().getMajorVersion() === 7 && this._lifecycleManager.getSdlMsgVersion().getMinorVersion() === 0))) {
// version if 7.0.0 or lower
this._addUniqueNamesToCells(choices);
this._addUniqueNamesToCells(choicesClone);
} else {
const strippedCellsClone = this._removeUnusedProperties(choicesClone);
this._addUniqueNamesBasedOnStrippedCells(strippedCellsClone, choicesClone);
}
return choices.map(choice => choice.clone()); // deep copy

return choicesClone.filter((clonedChoice) => {
// returns false if the cloned choice appears in the list of preloaded choices
return !this._preloadedChoices.map((preloadedChoice) => {
// the unique text is important for this comparison but it isn't checked by .equals()
return clonedChoice.equals(preloadedChoice) && (clonedChoice._getUniqueText() === preloadedChoice._getUniqueText());
}).includes(true);
});
}

/**
* Remove properties from ChoiceCells if they are not supported on the head unit
* @param {ChoiceCell[]} choiceCells - The array of ChoiceCells to have its unused properties removed
* @returns {ChoiceCell[]} - An array of ChoiceCells that has had its unsupported properties removed
*/
_removeUnusedProperties (choiceCells) {
const strippedCellsClone = this._cloneChoiceCellList(choiceCells);
for (const cell of strippedCellsClone) {
// Strip cell parameters that are not supported on head unit to support uniqueness.
cell.setVoiceCommands(null);

if (!this._hasTextFieldOfName(TextFieldName.secondaryText)) {
cell.setSecondaryText(null);
}
if (!this._hasTextFieldOfName(TextFieldName.tertiaryText)) {
cell.setTertiaryText(null);
}
if (!this._hasImageFieldOfName(ImageFieldName.choiceImage)) {
cell.setArtwork(null);
}
if (!this._hasImageFieldOfName(ImageFieldName.choiceSecondaryImage)) {
cell.setSecondaryArtwork(null);
}
}
return strippedCellsClone;
}

/**
* Check to see if WindowCapability has an ImageFieldName of a given name.
* @private
* @param {ImageFieldName} imageFieldName - Representing a name of a given Image field that would be stored in WindowCapability
* @returns {Boolean} - True if the name exists in WindowCapability, otherwise false
*/
_hasImageFieldOfName (imageFieldName) {
return this._defaultMainWindowCapability === null || _ManagerUtility.hasImageFieldOfName(this._defaultMainWindowCapability, imageFieldName);
}

/**
* Check to see if WindowCapability has a textField of a given name.
* @private
* @param {TextFieldName} textFieldName - Representing a name of a given text field that would be stored in WindowCapability
* @returns {Boolean} - True if the name exists in WindowCapability, otherwise false
*/
_hasTextFieldOfName (textFieldName) {
return this._defaultMainWindowCapability === null || _ManagerUtility.hasTextFieldOfName(this._defaultMainWindowCapability, textFieldName);
}

/**
Expand Down
86 changes: 86 additions & 0 deletions tests/managers/screen/choiceset/ChoiceSetManagerTests.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const SDL = require('../../../config.js').node;

const Validator = require('../../../Validator');
const sinon = require('sinon');
const Test = require('../../../Test.js');

module.exports = function (appClient) {
describe('ChoiceSetManagerTests', function () {
Expand Down Expand Up @@ -314,5 +315,90 @@ module.exports = function (appClient) {
Validator.assertEquals(cell5._getUniqueText(), 'Starbucks (2)');
Validator.assertEquals(cell6._getUniqueText(), 'Meijer');
});

it('testUniquenessForAvailableFields', function () {
const windowCapability = new SDL.rpc.structs.WindowCapability();
const secondaryText = new SDL.rpc.structs.TextField()
.setNameParam(SDL.rpc.enums.TextFieldName.secondaryText);
const tertiaryText = new SDL.rpc.structs.TextField()
.setNameParam(SDL.rpc.enums.TextFieldName.tertiaryText);

const textFields = [
secondaryText,
tertiaryText,
];
windowCapability.setTextFields(textFields);

const choiceImage = new SDL.rpc.structs.ImageField()
.setNameParam(SDL.rpc.enums.ImageFieldName.choiceImage);
const choiceSecondaryImage = new SDL.rpc.structs.ImageField()
.setNameParam(SDL.rpc.enums.ImageFieldName.choiceSecondaryImage);

const imageFieldList = [
choiceImage,
choiceSecondaryImage,
];
windowCapability.setImageFields(imageFieldList);

csm._defaultMainWindowCapability = windowCapability;

const cell1 = new SDL.manager.screen.choiceset.ChoiceCell('Item 1')
.setSecondaryText('null')
.setTertiaryText('tertiaryText')
.setVoiceCommands(null)
.setArtwork(Test.GENERAL_ARTWORK)
.setSecondaryArtwork(Test.GENERAL_ARTWORK);
const cell2 = new SDL.manager.screen.choiceset.ChoiceCell('Item 1')
.setSecondaryText('null2')
.setTertiaryText('tertiaryText2')
.setVoiceCommands(null)
.setArtwork(null)
.setSecondaryArtwork(null);

const choiceCellList = [
cell1,
cell2,
];

let removedProperties = csm._removeUnusedProperties(choiceCellList);
Validator.assertNotNullUndefined(removedProperties[0].getSecondaryText());

csm._defaultMainWindowCapability.setTextFields([]);
csm._defaultMainWindowCapability.setImageFields([]);

removedProperties = csm._removeUnusedProperties(choiceCellList);
csm._addUniqueNamesBasedOnStrippedCells(removedProperties, choiceCellList);
Validator.assertEquals(choiceCellList[1]._getUniqueText(), 'Item 1 (2)');
});

it('testChoicesToBeUploaded', function () {
const cell1 = new SDL.manager.screen.choiceset.ChoiceCell('Item 1')
.setSecondaryText('null')
.setTertiaryText('tertiaryText')
.setVoiceCommands(null)
.setArtwork(Test.GENERAL_ARTWORK)
.setSecondaryArtwork(Test.GENERAL_ARTWORK);
const cell2 = new SDL.manager.screen.choiceset.ChoiceCell('Item 2')
.setSecondaryText('null2')
.setTertiaryText('tertiaryText2')
.setVoiceCommands(null)
.setArtwork(null)
.setSecondaryArtwork(null);

const choiceCellList = [
cell1,
cell2,
];

csm._preloadedChoices = choiceCellList;
Validator.assertEquals(csm._getChoicesToBeUploadedWithArray(choiceCellList), []);
const cell3 = new SDL.manager.screen.choiceset.ChoiceCell('Item 3')
.setSecondaryText('null3')
.setTertiaryText('tertiaryText3')
.setVoiceCommands(null)
.setArtwork(null)
.setSecondaryArtwork(null);
Validator.assertEquals(csm._getChoicesToBeUploadedWithArray([cell1, cell2, cell3]), [cell3]);
});
});
};

0 comments on commit 0b123ab

Please sign in to comment.