Skip to content

Commit 0b123ab

Browse files
authored
Merge pull request #456 from smartdevicelink/bugfix/choice-set-uniqueness
Choice Cells and Menu Cells do not take which properties are available into account for uniqueness
2 parents 2cb7105 + 9a07a64 commit 0b123ab

2 files changed

Lines changed: 187 additions & 3 deletions

File tree

lib/js/src/manager/screen/choiceset/_ChoiceSetManagerBase.js

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ import { KeypressMode } from '../../../rpc/enums/KeypressMode.js';
3939
import { InteractionMode } from '../../../rpc/enums/InteractionMode.js';
4040
import { PredefinedWindows } from '../../../rpc/enums/PredefinedWindows.js';
4141
import { SystemCapabilityType } from '../../../rpc/enums/SystemCapabilityType.js';
42+
import { _ManagerUtility } from '../../_ManagerUtility.js';
43+
import { TextFieldName } from '../../../rpc/enums/TextFieldName.js';
44+
import { ImageFieldName } from '../../../rpc/enums/ImageFieldName.js';
4245

4346
// operations and listeners
4447
import { _CheckChoiceVrOptionalInterface } from './_CheckChoiceVrOptionalInterface.js';
@@ -301,7 +304,6 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
301304
if (uniqueChoiceCells.findIndex(choice => choice.equals(choices[index])) === -1) {
302305
uniqueChoiceCells.push(choices[index]);
303306
}
304-
305307
if (choiceVoiceCommands !== null) {
306308
choiceCellWithVoiceCommandCount++;
307309
allVoiceCommandsCount += choiceVoiceCommands.length;
@@ -505,6 +507,32 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
505507
return modifiedKeyboardConfiguration;
506508
}
507509

510+
/**
511+
* Finds non-unique choice cells and updates their unique text accordingly
512+
* @param {ChoiceCell[]} strippedCells - Choice cells with their unsupported properties removed
513+
* @param {ChoiceCell[]} unstrippedCells - The original choice cells
514+
*/
515+
_addUniqueNamesBasedOnStrippedCells (strippedCells, unstrippedCells) {
516+
if (!Array.isArray(strippedCells) || !Array.isArray(unstrippedCells) || strippedCells.length !== unstrippedCells.length) {
517+
return;
518+
}
519+
// array of unique choice cells
520+
const cells = [];
521+
// array of the count of how many times each cell has been found
522+
const cellsCounter = [];
523+
strippedCells.forEach((strippedCell, index) => {
524+
// find if a previous cell was a duplicate and update unique text of the current cell if so
525+
const duplicateIndex = cells.map(cell => cell.equals(strippedCell)).indexOf(true);
526+
if (duplicateIndex >= 0) {
527+
cellsCounter[duplicateIndex]++;
528+
unstrippedCells[index]._setUniqueText(`${unstrippedCells[index].getText()} (${cellsCounter[duplicateIndex]})`);
529+
} else {
530+
cells.push(strippedCell);
531+
cellsCounter.push(1);
532+
}
533+
});
534+
}
535+
508536

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

625+
/**
626+
* Clones a list of choice cells
627+
* @param {ChoiceCell[]} originalList - A list of choice cells to be cloned
628+
* @returns {ChoiceCell[]|null} - The cloned cell list
629+
*/
630+
_cloneChoiceCellList (originalList) {
631+
if (!Array.isArray(originalList)) {
632+
return null;
633+
}
634+
return originalList.map((choiceCell) => choiceCell.clone());
635+
}
636+
597637
/**
598638
* Modifies the choices names depending on SDL version
599639
* @param {ChoiceCell[]} choices - The first list of choices
600640
* @returns {ChoiceCell[]} - A deep copy of the name modified choices
601641
*/
602642
_getChoicesToBeUploadedWithArray (choices) {
643+
const choicesClone = this._cloneChoiceCellList(choices);
603644
// 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.
604645
if (choices !== null && this._lifecycleManager.getSdlMsgVersion() !== null
605646
&& (this._lifecycleManager.getSdlMsgVersion().getMajorVersion() < 7
606647
|| (this._lifecycleManager.getSdlMsgVersion().getMajorVersion() === 7 && this._lifecycleManager.getSdlMsgVersion().getMinorVersion() === 0))) {
607648
// version if 7.0.0 or lower
608-
this._addUniqueNamesToCells(choices);
649+
this._addUniqueNamesToCells(choicesClone);
650+
} else {
651+
const strippedCellsClone = this._removeUnusedProperties(choicesClone);
652+
this._addUniqueNamesBasedOnStrippedCells(strippedCellsClone, choicesClone);
609653
}
610-
return choices.map(choice => choice.clone()); // deep copy
654+
655+
return choicesClone.filter((clonedChoice) => {
656+
// returns false if the cloned choice appears in the list of preloaded choices
657+
return !this._preloadedChoices.map((preloadedChoice) => {
658+
// the unique text is important for this comparison but it isn't checked by .equals()
659+
return clonedChoice.equals(preloadedChoice) && (clonedChoice._getUniqueText() === preloadedChoice._getUniqueText());
660+
}).includes(true);
661+
});
662+
}
663+
664+
/**
665+
* Remove properties from ChoiceCells if they are not supported on the head unit
666+
* @param {ChoiceCell[]} choiceCells - The array of ChoiceCells to have its unused properties removed
667+
* @returns {ChoiceCell[]} - An array of ChoiceCells that has had its unsupported properties removed
668+
*/
669+
_removeUnusedProperties (choiceCells) {
670+
const strippedCellsClone = this._cloneChoiceCellList(choiceCells);
671+
for (const cell of strippedCellsClone) {
672+
// Strip cell parameters that are not supported on head unit to support uniqueness.
673+
cell.setVoiceCommands(null);
674+
675+
if (!this._hasTextFieldOfName(TextFieldName.secondaryText)) {
676+
cell.setSecondaryText(null);
677+
}
678+
if (!this._hasTextFieldOfName(TextFieldName.tertiaryText)) {
679+
cell.setTertiaryText(null);
680+
}
681+
if (!this._hasImageFieldOfName(ImageFieldName.choiceImage)) {
682+
cell.setArtwork(null);
683+
}
684+
if (!this._hasImageFieldOfName(ImageFieldName.choiceSecondaryImage)) {
685+
cell.setSecondaryArtwork(null);
686+
}
687+
}
688+
return strippedCellsClone;
689+
}
690+
691+
/**
692+
* Check to see if WindowCapability has an ImageFieldName of a given name.
693+
* @private
694+
* @param {ImageFieldName} imageFieldName - Representing a name of a given Image field that would be stored in WindowCapability
695+
* @returns {Boolean} - True if the name exists in WindowCapability, otherwise false
696+
*/
697+
_hasImageFieldOfName (imageFieldName) {
698+
return this._defaultMainWindowCapability === null || _ManagerUtility.hasImageFieldOfName(this._defaultMainWindowCapability, imageFieldName);
699+
}
700+
701+
/**
702+
* Check to see if WindowCapability has a textField of a given name.
703+
* @private
704+
* @param {TextFieldName} textFieldName - Representing a name of a given text field that would be stored in WindowCapability
705+
* @returns {Boolean} - True if the name exists in WindowCapability, otherwise false
706+
*/
707+
_hasTextFieldOfName (textFieldName) {
708+
return this._defaultMainWindowCapability === null || _ManagerUtility.hasTextFieldOfName(this._defaultMainWindowCapability, textFieldName);
611709
}
612710

613711
/**

tests/managers/screen/choiceset/ChoiceSetManagerTests.js

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ const SDL = require('../../../config.js').node;
22

33
const Validator = require('../../../Validator');
44
const sinon = require('sinon');
5+
const Test = require('../../../Test.js');
56

67
module.exports = function (appClient) {
78
describe('ChoiceSetManagerTests', function () {
@@ -314,5 +315,90 @@ module.exports = function (appClient) {
314315
Validator.assertEquals(cell5._getUniqueText(), 'Starbucks (2)');
315316
Validator.assertEquals(cell6._getUniqueText(), 'Meijer');
316317
});
318+
319+
it('testUniquenessForAvailableFields', function () {
320+
const windowCapability = new SDL.rpc.structs.WindowCapability();
321+
const secondaryText = new SDL.rpc.structs.TextField()
322+
.setNameParam(SDL.rpc.enums.TextFieldName.secondaryText);
323+
const tertiaryText = new SDL.rpc.structs.TextField()
324+
.setNameParam(SDL.rpc.enums.TextFieldName.tertiaryText);
325+
326+
const textFields = [
327+
secondaryText,
328+
tertiaryText,
329+
];
330+
windowCapability.setTextFields(textFields);
331+
332+
const choiceImage = new SDL.rpc.structs.ImageField()
333+
.setNameParam(SDL.rpc.enums.ImageFieldName.choiceImage);
334+
const choiceSecondaryImage = new SDL.rpc.structs.ImageField()
335+
.setNameParam(SDL.rpc.enums.ImageFieldName.choiceSecondaryImage);
336+
337+
const imageFieldList = [
338+
choiceImage,
339+
choiceSecondaryImage,
340+
];
341+
windowCapability.setImageFields(imageFieldList);
342+
343+
csm._defaultMainWindowCapability = windowCapability;
344+
345+
const cell1 = new SDL.manager.screen.choiceset.ChoiceCell('Item 1')
346+
.setSecondaryText('null')
347+
.setTertiaryText('tertiaryText')
348+
.setVoiceCommands(null)
349+
.setArtwork(Test.GENERAL_ARTWORK)
350+
.setSecondaryArtwork(Test.GENERAL_ARTWORK);
351+
const cell2 = new SDL.manager.screen.choiceset.ChoiceCell('Item 1')
352+
.setSecondaryText('null2')
353+
.setTertiaryText('tertiaryText2')
354+
.setVoiceCommands(null)
355+
.setArtwork(null)
356+
.setSecondaryArtwork(null);
357+
358+
const choiceCellList = [
359+
cell1,
360+
cell2,
361+
];
362+
363+
let removedProperties = csm._removeUnusedProperties(choiceCellList);
364+
Validator.assertNotNullUndefined(removedProperties[0].getSecondaryText());
365+
366+
csm._defaultMainWindowCapability.setTextFields([]);
367+
csm._defaultMainWindowCapability.setImageFields([]);
368+
369+
removedProperties = csm._removeUnusedProperties(choiceCellList);
370+
csm._addUniqueNamesBasedOnStrippedCells(removedProperties, choiceCellList);
371+
Validator.assertEquals(choiceCellList[1]._getUniqueText(), 'Item 1 (2)');
372+
});
373+
374+
it('testChoicesToBeUploaded', function () {
375+
const cell1 = new SDL.manager.screen.choiceset.ChoiceCell('Item 1')
376+
.setSecondaryText('null')
377+
.setTertiaryText('tertiaryText')
378+
.setVoiceCommands(null)
379+
.setArtwork(Test.GENERAL_ARTWORK)
380+
.setSecondaryArtwork(Test.GENERAL_ARTWORK);
381+
const cell2 = new SDL.manager.screen.choiceset.ChoiceCell('Item 2')
382+
.setSecondaryText('null2')
383+
.setTertiaryText('tertiaryText2')
384+
.setVoiceCommands(null)
385+
.setArtwork(null)
386+
.setSecondaryArtwork(null);
387+
388+
const choiceCellList = [
389+
cell1,
390+
cell2,
391+
];
392+
393+
csm._preloadedChoices = choiceCellList;
394+
Validator.assertEquals(csm._getChoicesToBeUploadedWithArray(choiceCellList), []);
395+
const cell3 = new SDL.manager.screen.choiceset.ChoiceCell('Item 3')
396+
.setSecondaryText('null3')
397+
.setTertiaryText('tertiaryText3')
398+
.setVoiceCommands(null)
399+
.setArtwork(null)
400+
.setSecondaryArtwork(null);
401+
Validator.assertEquals(csm._getChoicesToBeUploadedWithArray([cell1, cell2, cell3]), [cell3]);
402+
});
317403
});
318404
};

0 commit comments

Comments
 (0)