Skip to content

Commit b1be2a7

Browse files
committed
Merge branch 'develop' into feature/alert-manager
2 parents 0b9ff50 + ec4bf9a commit b1be2a7

9 files changed

Lines changed: 190 additions & 42 deletions

File tree

lib/js/src/manager/SystemCapabilityManager.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -726,8 +726,12 @@ class SystemCapabilityManager extends _SubManagerBase {
726726
const convertedCapabilities = new DisplayCapabilities();
727727
convertedCapabilities.setDisplayType(DisplayType.SDL_GENERIC); // deprecated but it is mandatory...
728728
convertedCapabilities.setDisplayName(displayName);
729-
convertedCapabilities.setTextFields(defaultMainWindow.getTextFields());
730-
convertedCapabilities.setImageFields(defaultMainWindow.getImageFields());
729+
if (defaultMainWindow.getTextFields() !== null && defaultMainWindow.getTextFields() !== undefined) {
730+
convertedCapabilities.setTextFields(defaultMainWindow.getTextFields());
731+
}
732+
if (defaultMainWindow.getImageFields() !== null && defaultMainWindow.getImageFields() !== undefined) {
733+
convertedCapabilities.setImageFields(defaultMainWindow.getImageFields());
734+
}
731735
convertedCapabilities.setTemplatesAvailable(defaultMainWindow.getTemplatesAvailable());
732736
convertedCapabilities.setNumCustomPresetsAvailable(defaultMainWindow.getNumCustomPresetsAvailable());
733737
convertedCapabilities.setMediaClockFormats([]); // mandatory field but allows empty array

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

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3030
* POSSIBILITY OF SUCH DAMAGE.
3131
*/
32+
import { SdlArtwork } from '../../file/filetypes/SdlArtwork.js';
3233

3334
class ChoiceCell {
3435
/**
@@ -40,6 +41,7 @@ class ChoiceCell {
4041
this._text = null;
4142
this._secondaryText = null;
4243
this._tertiaryText = null;
44+
this._uniqueText = null;
4345
this._voiceCommands = null;
4446
this._artwork = null;
4547
this._secondaryArtwork = null;
@@ -51,6 +53,7 @@ class ChoiceCell {
5153
}
5254

5355
this._text = text;
56+
this._uniqueText = text;
5457
}
5558

5659
/**
@@ -112,6 +115,29 @@ class ChoiceCell {
112115
return this;
113116
}
114117

118+
/**
119+
* Get the state unique text. USED INTERNALLY
120+
* @private
121+
* @returns {String} - The uniqueText to be used in place of primaryText when core does not support identical names for ChoiceSets
122+
*/
123+
_getUniqueText () {
124+
return this._uniqueText;
125+
}
126+
127+
/**
128+
* Primary text of the cell to be displayed on the module. Used to distinguish cells with the
129+
* same `text` but other fields are different. This is autogenerated by the screen manager.
130+
* Attempting to use cells that are exactly the same (all text and artwork fields are the same)
131+
* will not cause this to be used. This will not be used when connected to modules supporting RPC 7.1+.
132+
* @private
133+
* @param {String} uniqueText - The uniqueText to be used in place of primaryText when core does not support identical names for ChoiceSets
134+
* @returns {ChoiceCell} - A reference to this instance to support method chaining
135+
*/
136+
_setUniqueText (uniqueText) {
137+
this._uniqueText = uniqueText;
138+
return this;
139+
}
140+
115141
/**
116142
* Get the state voiceCommands
117143
* @returns {String[]|null} - The list of voice command strings
@@ -251,6 +277,27 @@ class ChoiceCell {
251277
}
252278
return true;
253279
}
280+
281+
/**
282+
* Creates a deep copy of the object
283+
* @returns {ChoiceCell} - A deep copy of the object
284+
*/
285+
clone () {
286+
const clonedParams = Object.assign({}, this); // shallow copy. copy all objects afterwards
287+
288+
if (clonedParams._artwork !== null) {
289+
clonedParams._artwork = Object.assign(new SdlArtwork(), clonedParams._artwork);
290+
}
291+
if (clonedParams._secondaryArtwork !== null) {
292+
clonedParams._secondaryArtwork = Object.assign(new SdlArtwork(), clonedParams._secondaryArtwork);
293+
}
294+
295+
if (Array.isArray(this.getVoiceCommands())) {
296+
clonedParams._voiceCommands = this.getVoiceCommands().map(vc => vc);
297+
}
298+
299+
return Object.assign(new ChoiceCell(this.getText()), clonedParams);
300+
}
254301
}
255302

256303
// MAX ID for cells - Reasoning is from Java library: cannot use Integer.MAX_INT as the value is too high.

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ import { VrHelpItem } from '../../../rpc/structs/VrHelpItem.js';
3636
class ChoiceSet {
3737
/**
3838
* Create a new instance of ChoiceSet
39+
* Initialize with a title, choices, and listener. It will use the default timeout and layout, all other properties (such as prompts) will be null.
40+
* WARNING: If you display multiple cells with the same title with the only uniquing property between cells being different `vrCommands` or a feature
41+
* that is not displayed on the head unit (e.g. if the head unit doesn't display `secondaryArtwork` and that's the only uniquing property between two cells)
42+
* then the cells may appear to be the same to the user in `Manual` mode. This only applies to RPC connections >= 7.1.0.
43+
* WARNING: On < 7.1.0 connections, the title cell will be automatically modified among cells that have the same title when they are preloaded, so they will
44+
* always appear differently on-screen when they are displayed. Unique text will be created by appending " (2)", " (3)", etc.
3945
* @class
4046
* @param {String} title - The choice set's title
4147
* @param {ChoiceCell[]} choices - The choices to be displayed to the user for interaction

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

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,14 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
130130
* @param {ChoiceCell[]} choices - A list of ChoiceCell objects that will be part of a choice set later
131131
* @returns {Promise} - A promise that resolves to a Boolean of whether the operation is a success
132132
*/
133-
async preloadChoices (choices) {
133+
async preloadChoices (choices = null) {
134134
if (this._getState() === _SubManagerBase.ERROR) {
135135
console.warn('ChoiceSetManager: Choice Manager In Error State');
136136
return false;
137137
}
138-
const choicesToUpload = choices.map(choice => choice); // shallow copy
138+
139+
const choicesToUpload = this._getChoicesToBeUploadedWithArray(choices);
140+
139141
this._removeChoicesFromChoices(this._preloadedChoices, choicesToUpload);
140142
this._removeChoicesFromChoices(this._pendingPreloadChoices, choicesToUpload);
141143

@@ -167,6 +169,25 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
167169
});
168170
}
169171

172+
/**
173+
* 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)".
174+
* 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)".
175+
* @param {ChoiceCell[]} choices - A list of ChoiceCell objects to be uploaded
176+
*/
177+
_addUniqueNamesToCells (choices) {
178+
const dictCounter = {}; // create a string to number hash for counting similar primary texts
179+
180+
choices.forEach(choice => {
181+
const choiceName = choice.getText();
182+
if (dictCounter[choiceName] === undefined) {
183+
dictCounter[choiceName] = 1; // unique text
184+
} else { // found a duplicate
185+
dictCounter[choiceName] += 1;
186+
choice._setUniqueText(`${choiceName} (${dictCounter[choiceName]})`);
187+
}
188+
});
189+
}
190+
170191
/**
171192
* Deletes choices that were sent previously
172193
* @param {ChoiceCell[]} choices - A list of ChoiceCell objects
@@ -276,15 +297,16 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
276297
}
277298
}
278299

279-
const uniqueChoiceTexts = {};
300+
const uniqueChoiceCells = [];
280301
const uniqueVoiceCommands = {};
281302
let choiceCellWithVoiceCommandCount = 0;
282303
let allVoiceCommandsCount = 0;
283304

284305
for (let index = 0; index < choices.length; index++) {
285-
const choiceText = choices[index].getText();
286306
const choiceVoiceCommands = choices[index].getVoiceCommands();
287-
uniqueChoiceTexts[choiceText] = true;
307+
if (uniqueChoiceCells.findIndex(choice => choice.equals(choices[index])) === -1) {
308+
uniqueChoiceCells.push(choices[index]);
309+
}
288310

289311
if (choiceVoiceCommands !== null) {
290312
choiceCellWithVoiceCommandCount++;
@@ -297,9 +319,8 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
297319
}
298320
}
299321

300-
// Cell text MUST be unique
301-
if (Object.keys(uniqueChoiceTexts).length < choices.length) {
302-
console.error('ChoiceSetManager: Attempted to create a choice set with duplicate cell text. Cell text must be unique. The choice set will not be set.');
322+
if (uniqueChoiceCells.length !== choices.length) {
323+
console.error('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.');
303324
return false;
304325
}
305326

@@ -579,6 +600,22 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
579600
.setKeypressMode(KeypressMode.RESEND_CURRENT_ENTRY);
580601
}
581602

603+
/**
604+
* Modifies the choices names depending on SDL version
605+
* @param {ChoiceCell[]} choices - The first list of choices
606+
* @returns {ChoiceCell[]} - A deep copy of the name modified choices
607+
*/
608+
_getChoicesToBeUploadedWithArray (choices) {
609+
// 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.
610+
if (choices !== null && this._lifecycleManager.getSdlMsgVersion() !== null
611+
&& (this._lifecycleManager.getSdlMsgVersion().getMajorVersion() < 7
612+
|| (this._lifecycleManager.getSdlMsgVersion().getMajorVersion() === 7 && this._lifecycleManager.getSdlMsgVersion().getMinorVersion() === 0))) {
613+
// version if 7.0.0 or lower
614+
this._addUniqueNamesToCells(choices);
615+
}
616+
return choices.map(choice => choice.clone()); // deep copy
617+
}
618+
582619
/**
583620
* Listen for DISPLAYS capability updates
584621
* @private

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ class _PreloadChoicesOperation extends _Task {
166166
vrCommands = this._isVrOptional ? null : [`${cell._getChoiceId()}`]; // stringified choice id
167167
}
168168

169-
const menuName = this._shouldSendChoiceText() ? cell.getText() : null;
169+
const menuName = this._shouldSendChoiceText() ? cell._getUniqueText() : null;
170170
if (menuName === null) {
171171
console.error('PreloadChoicesOperation: Could not convert Choice Cell to CreateInteractionChoiceSet. It will not be shown');
172172
return null;
@@ -247,4 +247,4 @@ class _PreloadChoicesOperation extends _Task {
247247
}
248248
}
249249

250-
export { _PreloadChoicesOperation };
250+
export { _PreloadChoicesOperation };

lib/js/src/protocol/_SdlProtocolBase.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,27 +216,27 @@ class _SdlProtocolBase {
216216
*/
217217
_setVersion (version) {
218218
if (version > 5) {
219-
this._protocolVersion = new Version('5.1.0'); // protect for future, proxy only supports v5 or lower
219+
this._protocolVersion = new Version(5, 0, 0); // protect for future, proxy only supports v5 or lower
220220
this.headerSize = this.constructor.V2_HEADER_SIZE;
221221
this._mtus[_ServiceType.RPC] = this.constructor.V3_V4_MTU_SIZE;
222222
} else if (version === 5) {
223-
this._protocolVersion = new Version('5.0.0');
223+
this._protocolVersion = new Version(5, 0, 0);
224224
this.headerSize = this.constructor.V2_HEADER_SIZE;
225225
this._mtus[_ServiceType.RPC] = this.constructor.V3_V4_MTU_SIZE;
226226
} else if (version === 4) {
227-
this._protocolVersion = new Version('4.0.0');
227+
this._protocolVersion = new Version(4, 0, 0);
228228
this.headerSize = this.constructor.V2_HEADER_SIZE;
229229
this._mtus[_ServiceType.RPC] = this.constructor.V3_V4_MTU_SIZE; // versions 4 supports 128k MTU
230230
} else if (version === 3) {
231-
this._protocolVersion = new Version('3.0.0');
231+
this._protocolVersion = new Version(3, 0, 0);
232232
this.headerSize = this.constructor.V2_HEADER_SIZE;
233233
this._mtus[_ServiceType.RPC] = this.constructor.V3_V4_MTU_SIZE; // versions 3 supports 128k MTU
234234
} else if (version === 2) {
235-
this._protocolVersion = new Version('2.0.0');
235+
this._protocolVersion = new Version(2, 0, 0);
236236
this.headerSize = this.constructor.V2_HEADER_SIZE;
237237
this._mtus[_ServiceType.RPC] = this.constructor.V1_V2_MTU_SIZE - this.headerSize;
238238
} else if (version === 1) {
239-
this._protocolVersion = new Version('1.0.0');
239+
this._protocolVersion = new Version(1, 0, 0);
240240
this.headerSize = this.constructor.V1_HEADER_SIZE;
241241
this._mtus[_ServiceType.RPC] = this.constructor.V1_V2_MTU_SIZE - this.headerSize;
242242
}

lib/js/src/rpc/RpcStruct.js

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -130,19 +130,21 @@ class RpcStruct {
130130
* @param {Boolean} isArray - Whether or not the given variable is an array to be iterated through.
131131
*/
132132
_validateType (tClass, obj, isArray = false) {
133-
if (isArray) {
134-
if (!Array.isArray(obj)) {
135-
throw new Error(`${obj.name} must be an array containing items of type ${tClass.name}`);
136-
} else {
137-
for (const item of obj) {
138-
this._validateType(tClass, item, false);
133+
if (obj !== null) {
134+
if (isArray) {
135+
if (!Array.isArray(obj)) {
136+
throw new Error(`${obj.name} must be an array containing items of type ${tClass.name}`);
137+
} else {
138+
for (const item of obj) {
139+
this._validateType(tClass, item, false);
140+
}
139141
}
142+
} else if (
143+
(tClass.prototype instanceof Enum && tClass.keyForValue(obj) === null)
144+
|| (tClass.prototype instanceof RpcStruct && obj.constructor !== tClass)
145+
) {
146+
throw new Error(`${obj.name} must be of type ${tClass.name}`);
140147
}
141-
} else if (
142-
(tClass.prototype instanceof Enum && tClass.keyForValue(obj) === null)
143-
|| (tClass.prototype instanceof RpcStruct && obj !== null && obj.constructor !== tClass)
144-
) {
145-
throw new Error(`${obj.name} must be of type ${tClass.name}`);
146148
}
147149
}
148150
}

tests/managers/screen/choiceset/ChoiceCellTests.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ module.exports = function (appClient) {
1717
Validator.assertNull(choiceCell.getTertiaryText());
1818
Validator.assertNull(choiceCell.getArtwork());
1919
Validator.assertNull(choiceCell.getSecondaryArtwork());
20+
Validator.assertNull(choiceCell._getUniqueText());
2021
});
2122

2223
it('testSettersAndGetters', function () {
@@ -37,6 +38,11 @@ module.exports = function (appClient) {
3738
Validator.assertEquals(choiceCell.getSecondaryArtwork(), artwork);
3839
Validator.assertEquals(choiceCell._getChoiceId(), MAX_ID);
3940

41+
Validator.assertEquals(choiceCell._getUniqueText(), choiceCell.getText());
42+
43+
choiceCell._setUniqueText('hi');
44+
Validator.assertEquals(choiceCell._getUniqueText(), 'hi');
45+
4046
choiceCell.setText('hello');
4147
Validator.assertEquals(choiceCell.getText(), 'hello');
4248
});
@@ -62,8 +68,13 @@ module.exports = function (appClient) {
6268
.setSecondaryText(Test.GENERAL_STRING)
6369
.setTertiaryText(Test.GENERAL_STRING);
6470

71+
// UniqueText should not be taken into consideration when checking equality
72+
choiceCell._setUniqueText('1');
73+
choiceCell2._setUniqueText('2');
74+
choiceCell3._setUniqueText('3');
75+
6576
Validator.assertTrue(choiceCell.equals(choiceCell2));
6677
Validator.assertTrue(!choiceCell.equals(choiceCell3));
6778
});
6879
});
69-
};
80+
};

0 commit comments

Comments
 (0)