Skip to content

Commit c56738a

Browse files
authored
Merge pull request #547 from smartdevicelink/release/1.7.0
JavaScript Suite Release 1.7.0
2 parents 4e78da3 + caae001 commit c56738a

File tree

8 files changed

+137
-32
lines changed

8 files changed

+137
-32
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ The transport related source code for Node.js can use Node specific libraries ne
6060
NodeJS, npm, and Python3 are required:
6161

6262
```js
63+
git submodule init
64+
git submodule update
6365
npm install
6466
npm run build
6567
```

examples/js/hello-sdl/readme.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
## SDL JavaScript App Startup Instructions
22
1) Place the vanilla JS SDL.min.js file into this directory
3-
1) Request a [Manticore instance](https://smartdevicelink.com/resources/manticore/)
4-
1) Open two terminal sessions and `cd` both of them into `./examples/js/hello-sdl`
5-
1) In one terminal session, run `java -jar proxy.jar m.sdl.tools [PORT]`, where `[PORT]` is the one given to you by Manticore
6-
1) In the other terminal session, run `npm install` then `npm start`.
7-
1) Your browser should automatically open and the test app should appear on your Manticore UI.
8-
1) Click on the app in Manticore and observe the series of `Show` RPCs performed. Logs can be seen in your browser's JavaScript console.
3+
2) Request a [Manticore instance](https://smartdevicelink.com/resources/manticore/).
4+
3) Open the file `./examples/js/hello-sdl/index.html` in any text editor and change the transport config's URL and port value to what Manticore's "WS URL" says.
5+
4) Open a terminal session and `cd` into `./examples/js/hello-sdl`
6+
5) In the terminal session, run `npm install` then `npm start`.
7+
6) Your browser should automatically open and the test app should appear on your Manticore UI.
8+
7) Click on the app in Manticore and observe the series of `Show` RPCs performed. Logs can be seen in your browser's JavaScript console.

examples/node/hello-sdl/AppClient.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ class AppClient {
115115
this._managersReady = true;
116116
this._checkReadyState();
117117
const screenManager = this._sdlManager.getScreenManager();
118-
118+
119119
// add menus
120120
const menuListener = new SDL.manager.screen.menu.MenuSelectionListener()
121121
.setOnTriggered(triggerSource => {

lib/js/src/manager/screen/_SoftButtonManagerBase.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ class _SoftButtonManagerBase extends _SubManagerBase {
6060
this._currentMainField1 = null;
6161
this._currentHmiLevel = null;
6262
this._batchUpdates = false; // whether to wait on sending the updates
63+
let displayCapabilities = null;
64+
if (lifecycleManager.getSystemCapabilityManager() !== null) {
65+
displayCapabilities = lifecycleManager.getSystemCapabilityManager().getDisplayCapabilities();
66+
}
67+
this._isDynamicGraphicSupported = (displayCapabilities !== null && displayCapabilities.getGraphicSupported() !== null) ? displayCapabilities.getGraphicSupported() : true;
68+
6369
this._updateListener = () => {
6470
this._transitionSoftButton();
6571
};
@@ -137,7 +143,7 @@ class _SoftButtonManagerBase extends _SubManagerBase {
137143

138144
// Auto-send an updated Show if we have new capabilities
139145
if (this._softButtonObjects.length !== 0 && this._softButtonCapabilities !== null && this._softButtonCapabilities !== undefined && !this._softButtonCapabilitiesEquals(oldSoftButtonCapabilities, this._softButtonCapabilities)) {
140-
const operation = new _SoftButtonReplaceOperation(this._lifecycleManager, this._fileManager, this._softButtonCapabilities, this._softButtonObjects, this.getCurrentMainField1());
146+
const operation = new _SoftButtonReplaceOperation(this._lifecycleManager, this._fileManager, this._softButtonCapabilities, this._softButtonObjects, this.getCurrentMainField1(), this._isDynamicGraphicSupported);
141147
this._addTask(operation);
142148
}
143149
};
@@ -243,7 +249,7 @@ class _SoftButtonManagerBase extends _SubManagerBase {
243249
this._softButtonObjects = softButtonObjects;
244250

245251
// We only need to pass the first softButtonCapabilities in the array due to the fact that all soft button capabilities are the same (i.e. there is no way to assign a softButtonCapabilities to a specific soft button).
246-
const operation = new _SoftButtonReplaceOperation(this._lifecycleManager, this._fileManager, this._softButtonCapabilities, this._softButtonObjects, this.getCurrentMainField1());
252+
const operation = new _SoftButtonReplaceOperation(this._lifecycleManager, this._fileManager, this._softButtonCapabilities, this._softButtonObjects, this.getCurrentMainField1(), this._isDynamicGraphicSupported);
247253

248254
if (this._batchUpdates) {
249255
this._batchQueue.splice(0, this._batchQueue.length); // clear out the array

lib/js/src/manager/screen/_SoftButtonReplaceOperation.js

+97-21
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { _Task } from '../_Task';
3434
import { Show } from '../../rpc/messages/Show.js';
3535
import { SoftButton } from '../../rpc/structs/SoftButton.js';
3636
import { SoftButtonType } from '../../rpc/enums/SoftButtonType.js';
37+
import { ImageType } from '../../rpc/enums/ImageType.js';
3738

3839
class _SoftButtonReplaceOperation extends _Task {
3940
/**
@@ -45,14 +46,16 @@ class _SoftButtonReplaceOperation extends _Task {
4546
* @param {SoftButtonCapabilities} softButtonCapabilities - The soft button capabilities
4647
* @param {SoftButtonObject[]} softButtonObjects - A list of soft button objects
4748
* @param {String} currentMainField1 - The main field value text shown on the head unit
49+
* @param {Boolean} isDynamicGraphicSupported - Whether dynamic graphics are supported
4850
*/
49-
constructor (lifecycleManager, fileManager = null, softButtonCapabilities = null, softButtonObjects = null, currentMainField1 = null) {
50-
super('SoftButtonTransitionOperation');
51+
constructor (lifecycleManager, fileManager = null, softButtonCapabilities = null, softButtonObjects = null, currentMainField1 = null, isDynamicGraphicSupported) {
52+
super('SoftButtonReplaceOperation');
5153
this._lifecycleManager = lifecycleManager;
5254
this._fileManager = fileManager;
5355
this._softButtonCapabilities = softButtonCapabilities;
5456
this._softButtonObjects = softButtonObjects;
5557
this._currentMainField1 = currentMainField1;
58+
this._isDynamicGraphicSupported = isDynamicGraphicSupported;
5659
}
5760

5861
/**
@@ -77,11 +80,17 @@ class _SoftButtonReplaceOperation extends _Task {
7780
// Check the state of our images
7881
if (!this._supportsSoftButtonImages()) {
7982
// We don't support images at all
80-
console.warn('SoftButtonTransitionOperation - Soft button images are not supported. Attempting to send text-only soft buttons. If any button does not contain text, no buttons will be sent.');
83+
console.warn('SoftButtonReplaceOperation - Soft button images are not supported. Attempting to send text-only soft buttons. If any button does not contain text, no buttons will be sent.');
8184
// Send text buttons if all the soft buttons have text
8285
const success = await this._sendCurrentStateTextOnlySoftButtons();
8386
if (!success) {
84-
console.error('SoftButtonTransitionOperation - Head unit does not support images and some of the soft buttons do not have text, so none of the buttons will be sent.');
87+
console.error('SoftButtonReplaceOperation - Head unit does not support images and some of the soft buttons do not have text, so none of the buttons will be sent.');
88+
}
89+
} else if (!this._supportsDynamicSoftButtonImages()) {
90+
console.info('SoftButtonReplaceOperation - Soft button images are not supported. Attempting to send text and static image only soft buttons. If any button does not contain text and/or a static image, no buttons will be sent.');
91+
const success = await this._sendCurrentStateStaticImageOnlySoftButtons();
92+
if (!success) {
93+
console.error('SoftButtonReplaceOperation - Buttons will not be sent because the module does not support dynamic images and some of the buttons do not have text or static images.');
8594
}
8695
} else if (this._currentStateHasImages() && !this._allCurrentStateImagesAreUploaded()) {
8796
// If there are images that aren't uploaded
@@ -116,13 +125,13 @@ class _SoftButtonReplaceOperation extends _Task {
116125
return false;
117126
}
118127

119-
console.log('SoftButtonTransitionOperation - Preparing to send text-only soft buttons');
128+
console.log('SoftButtonReplaceOperation - Preparing to send text-only soft buttons');
120129
const textButtons = [];
121130

122131
for (const softButtonObject of this._softButtonObjects) {
123132
const softButton = softButtonObject.getCurrentStateSoftButton();
124133
if (softButton.getText() === null || softButton.getText() === undefined) {
125-
console.warn('SoftButtonTransitionOperation - Attempted to create text buttons, but some buttons don\'t support text, so no text-only soft buttons will be sent');
134+
console.warn('SoftButtonReplaceOperation - Attempted to create text buttons, but some buttons don\'t support text, so no text-only soft buttons will be sent');
126135
return false;
127136
}
128137
// We should create a new softButtonObject rather than modifying the original one
@@ -137,7 +146,7 @@ class _SoftButtonReplaceOperation extends _Task {
137146
}
138147

139148
if (this._lifecycleManager === null) {
140-
console.error('SoftButtonTransitionOperation: LifecycleManager is null');
149+
console.error('SoftButtonReplaceOperation: LifecycleManager is null');
141150
return false;
142151
}
143152

@@ -147,9 +156,62 @@ class _SoftButtonReplaceOperation extends _Task {
147156

148157
const response = await this._lifecycleManager.sendRpcResolve(show);
149158
if (response.getSuccess()) {
150-
console.log('SoftButtonTransitionOperation - Finished sending text only soft buttons');
159+
console.log('SoftButtonReplaceOperation - Finished sending text only soft buttons');
151160
} else {
152-
console.warn('SoftButtonTransitionOperation - Failed to update soft buttons with text buttons');
161+
console.warn('SoftButtonReplaceOperation - Failed to update soft buttons with text buttons');
162+
}
163+
return response.getSuccess();
164+
}
165+
166+
/**
167+
* Send soft buttons for the current state that only contain text and static images only, if possible.
168+
* @private
169+
* @returns {Promise} - Resolves to whether the operation is successful
170+
*/
171+
async _sendCurrentStateStaticImageOnlySoftButtons () {
172+
if (this.getState() === _Task.CANCELED) {
173+
return false;
174+
}
175+
176+
console.log('SoftButtonReplaceOperation - Preparing to send text and static image only soft buttons.');
177+
const textButtons = [];
178+
179+
for (const softButtonObject of this._softButtonObjects) {
180+
const softButton = softButtonObject.getCurrentStateSoftButton();
181+
if (softButton.getText() === null && softButton.getImage() !== null && softButton.getImage().getImageType() === ImageType.DYNAMIC) {
182+
console.warn('SoftButtonReplaceOperation - Attempted to create text and static image only buttons, but some buttons don\'t support text and have dynamic images, so no soft buttons will be sent.');
183+
return false;
184+
}
185+
186+
if (softButton.getImage() !== null && softButton.getImage().getImageType() === ImageType.DYNAMIC) {
187+
// We should create a new softButtonObject rather than modifying the original one
188+
const textAndStaticImageOnlySoftButton = new SoftButton()
189+
.setType(SoftButtonType.SBT_TEXT)
190+
.setText(softButton.getText())
191+
.setImage(softButton.getImage())
192+
.setIsHighlighted(softButton.getIsHighlighted())
193+
.setSoftButtonID(softButton.getSoftButtonID())
194+
.setSystemAction(softButton.getSystemAction());
195+
textButtons.push(textAndStaticImageOnlySoftButton);
196+
} else {
197+
textButtons.push(softButton);
198+
}
199+
}
200+
201+
if (this._lifecycleManager === null) {
202+
console.error('SoftButtonReplaceOperation: LifecycleManager is null');
203+
return false;
204+
}
205+
206+
const show = new Show()
207+
.setMainField1(this._currentMainField1)
208+
.setSoftButtons(textButtons);
209+
210+
const response = await this._lifecycleManager.sendRpcResolve(show);
211+
if (response.getSuccess()) {
212+
console.log('SoftButtonReplaceOperation - Finished sending text and static image only soft buttons.');
213+
} else {
214+
console.warn('SoftButtonReplaceOperation - Failed to update soft buttons with text and static image only buttons.');
153215
}
154216
return response.getSuccess();
155217
}
@@ -163,15 +225,15 @@ class _SoftButtonReplaceOperation extends _Task {
163225
if (this.getState() === _Task.CANCELED) {
164226
return false;
165227
}
166-
console.log('SoftButtonTransitionOperation - Preparing to send full soft buttons');
228+
console.log('SoftButtonReplaceOperation - Preparing to send full soft buttons');
167229
const softButtons = [];
168230

169231
for (const softButtonObject of this._softButtonObjects) {
170232
softButtons.push(softButtonObject.getCurrentStateSoftButton());
171233
}
172234

173235
if (this._lifecycleManager === null) {
174-
console.error('SoftButtonTransitionOperation: LifecycleManager is null');
236+
console.error('SoftButtonReplaceOperation: LifecycleManager is null');
175237
return false;
176238
}
177239

@@ -181,9 +243,9 @@ class _SoftButtonReplaceOperation extends _Task {
181243

182244
const response = await this._lifecycleManager.sendRpcResolve(show);
183245
if (response.getSuccess()) {
184-
console.log('SoftButtonTransitionOperation - Finished sending full soft buttons');
246+
console.log('SoftButtonReplaceOperation - Finished sending full soft buttons');
185247
} else {
186-
console.warn('SoftButtonTransitionOperation - Failed to update soft buttons');
248+
console.warn('SoftButtonReplaceOperation - Failed to update soft buttons');
187249
}
188250
return response.getSuccess();
189251
}
@@ -207,20 +269,25 @@ class _SoftButtonReplaceOperation extends _Task {
207269
}
208270

209271
if (initialStatesToBeUploaded.length === 0) {
210-
console.log('SoftButtonTransitionOperation: No initial state artworks to upload');
272+
console.log('SoftButtonReplaceOperation: No initial state artworks to upload');
273+
return false;
274+
}
275+
276+
if (!this._supportsDynamicSoftButtonImages()) {
277+
console.log('Head unit does not support dynamic images, skipping upload.');
211278
return false;
212279
}
213280

214-
console.log('SoftButtonTransitionOperation: Uploading soft button initial artworks');
281+
console.log('SoftButtonReplaceOperation: Uploading soft button initial artworks');
215282
if (this._fileManager === null) {
216283
return false;
217284
}
218285

219286
const successes = await this._fileManager.uploadArtworks(initialStatesToBeUploaded);
220287
if (successes.includes(false)) {
221-
console.error('SoftButtonTransitionOperation: Error uploading soft button artworks');
288+
console.error('SoftButtonReplaceOperation: Error uploading soft button artworks');
222289
} else {
223-
console.log('SoftButtonTransitionOperation: Soft button initial state artworks uploaded');
290+
console.log('SoftButtonReplaceOperation: Soft button initial state artworks uploaded');
224291
}
225292

226293
return true;
@@ -246,25 +313,34 @@ class _SoftButtonReplaceOperation extends _Task {
246313
}
247314

248315
if (otherStatesToBeUploaded.length === 0) {
249-
console.log('SoftButtonTransitionOperation: No other state artworks to upload');
316+
console.log('SoftButtonReplaceOperation: No other state artworks to upload');
250317
return false;
251318
}
252319

253-
console.log('SoftButtonTransitionOperation: Uploading soft button other state artworks');
320+
console.log('SoftButtonReplaceOperation: Uploading soft button other state artworks');
254321
if (this._fileManager === null) {
255322
return false;
256323
}
257324

258325
const successes = await this._fileManager.uploadArtworks(otherStatesToBeUploaded);
259326
if (successes.includes(false)) {
260-
console.error('SoftButtonTransitionOperation: Error uploading soft button artworks');
327+
console.error('SoftButtonReplaceOperation: Error uploading soft button artworks');
261328
} else {
262-
console.log('SoftButtonTransitionOperation: Soft button other state artworks uploaded');
329+
console.log('SoftButtonReplaceOperation: Soft button other state artworks uploaded');
263330
}
264331

265332
return true;
266333
}
267334

335+
/**
336+
* Checks if dynamic soft button images are supported
337+
* @private
338+
* @returns {Boolean} - Whether dynamic soft button images are supported
339+
*/
340+
_supportsDynamicSoftButtonImages () {
341+
return this._softButtonCapabilities !== null && this._isDynamicGraphicSupported && this._softButtonCapabilities.getImageSupported();
342+
}
343+
268344
/**
269345
* Checks if soft button images are supported
270346
* @private

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "sdl_javascript_suite",
3-
"version": "1.6.0",
3+
"version": "1.7.0",
44
"description": "The official JavaScript SDK for SmartDeviceLink.",
55
"main": "/lib/js/dist/SDL.js",
66
"engines": {

tests/managers/screen/SoftButtonManagerTests.js

+21
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module.exports = function (appClient) {
1111

1212
const screenManager = appClient._sdlManager.getScreenManager();
1313
const sbm = screenManager._softButtonManager;
14+
sbm._isDynamicGraphicSupported = true;
1415
let fileManagerUploadArtworksListenerCalledCounter = 0;
1516
let internalInterfaceSendRpcListenerCalledCounter = 0;
1617
const softButtonObject1Id = 1000;
@@ -233,6 +234,26 @@ module.exports = function (appClient) {
233234
Validator.assertEquals(softButtonState1, softButtonState2);
234235
});
235236

237+
it('testSoftButtonManagerGraphicNotSupported', function () {
238+
sbm._isDynamicGraphicSupported = false;
239+
fileManagerUploadArtworksListenerCalledCounter = 0;
240+
internalInterfaceSendRpcListenerCalledCounter = 0;
241+
const softButtonObjects = [softButtonObject1, softButtonObject2];
242+
sbm.setSoftButtonObjects(softButtonObjects);
243+
Validator.assertEquals(0, fileManagerUploadArtworksListenerCalledCounter);
244+
});
245+
246+
it('testSoftButtonManagerDynamicImageNotSupportedNoText', function () {
247+
sbm._isDynamicGraphicSupported = false;
248+
fileManagerUploadArtworksListenerCalledCounter = 0;
249+
internalInterfaceSendRpcListenerCalledCounter = 0;
250+
251+
const softButtonState = new SDL.manager.screen.utils.SoftButtonState('testState', null, new SDL.manager.file.filetypes.SdlArtwork('image', SDL.rpc.enums.FileType.GRAPHIC_PNG, '1', true));
252+
const softButtonObject = new SDL.manager.screen.utils.SoftButtonObject('obj1', softButtonState, softButtonState.getName());
253+
sbm.setSoftButtonObjects([softButtonObject]);
254+
Validator.assertEquals(0, fileManagerUploadArtworksListenerCalledCounter);
255+
});
256+
236257
/**
237258
* Pauses execution
238259
* @param {Number} timeout - How long in milliseconds to pause

0 commit comments

Comments
 (0)