Skip to content

Commit 88818d9

Browse files
authored
Merge pull request #566 from smartdevicelink/bugfix/missing-scm-tests
Bugfix/missing scm tests
2 parents 009976d + 535204a commit 88818d9

File tree

5 files changed

+1605
-27
lines changed

5 files changed

+1605
-27
lines changed

lib/js/src/manager/SystemCapabilityManager.js

Lines changed: 200 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ import { WindowType } from '../rpc/enums/WindowType.js';
3939
import { WindowTypeCapabilities } from '../rpc/structs/WindowTypeCapabilities.js';
4040
import { DisplayCapability } from '../rpc/structs/DisplayCapability.js';
4141
import { DisplayCapabilities } from '../rpc/structs/DisplayCapabilities.js';
42+
import { TextField } from '../rpc/structs/TextField.js';
43+
import { ImageField } from '../rpc/structs/ImageField.js';
44+
import { SoftButtonCapabilities } from '../rpc/structs/SoftButtonCapabilities.js';
45+
import { ButtonCapabilities } from '../rpc/structs/ButtonCapabilities.js';
46+
import { DynamicUpdateCapabilities } from '../rpc/structs/DynamicUpdateCapabilities.js';
47+
import { KeyboardCapabilities } from '../rpc/structs/KeyboardCapabilities.js';
48+
import { ImageResolution } from '../rpc/structs/ImageResolution.js';
4249
import { ImageType } from '../rpc/enums/ImageType.js';
4350
import { MessageType } from '../rpc/enums/MessageType.js';
4451
import { DisplayType } from '../rpc/enums/DisplayType.js';
@@ -68,6 +75,8 @@ class SystemCapabilityManager extends _SubManagerBase {
6875
this._seatLocationCapability = null;
6976
this._displays = null;
7077
this._driverDistractionCapability = null;
78+
this._systemCapabilitiesSubscriptionStatus = new Set(); // Set<SystemCapabilityType>
79+
this._systemCapabilitiesSubscriptionStatus.add(SystemCapabilityType.DISPLAYS); // subscribe to displays by default
7180

7281
// capabilities not in the spec
7382
this._hmiCapabilities = null;
@@ -196,6 +205,37 @@ class SystemCapabilityManager extends _SubManagerBase {
196205
if (currentWindowId === windowId) {
197206
// Clone WindowCapability to prevent modification of stored WindowCapability in SystemCapabilityManager
198207
const windowCapabilityCopy = new WindowCapability(JSON.parse(JSON.stringify(windowCapability.getParameters())));
208+
if (windowCapability.getTextFields() !== null && windowCapability.getTextFields() !== undefined) {
209+
windowCapabilityCopy.setTextFields(windowCapability.getTextFields().map(textField => {
210+
return new TextField(JSON.parse(JSON.stringify(textField.getParameters())));
211+
}));
212+
}
213+
if (windowCapability.getImageFields() !== null && windowCapability.getImageFields() !== undefined) {
214+
windowCapabilityCopy.setImageFields(windowCapability.getImageFields().map(imageField => {
215+
return new ImageField(JSON.parse(JSON.stringify(imageField.getParameters())))
216+
.setImageResolution(new ImageResolution(JSON.parse(JSON.stringify(imageField.getImageResolution().getParameters()))));
217+
}));
218+
}
219+
if (windowCapability.getSoftButtonCapabilities() !== null && windowCapability.getSoftButtonCapabilities() !== undefined) {
220+
windowCapabilityCopy.setSoftButtonCapabilities(windowCapability.getSoftButtonCapabilities().map(capability => {
221+
return new SoftButtonCapabilities(JSON.parse(JSON.stringify(capability.getParameters())));
222+
}));
223+
}
224+
if (windowCapability.getButtonCapabilities() !== null && windowCapability.getButtonCapabilities() !== undefined) {
225+
windowCapabilityCopy.setButtonCapabilities(windowCapability.getButtonCapabilities().map(capability => {
226+
return new ButtonCapabilities(JSON.parse(JSON.stringify(capability.getParameters())));
227+
}));
228+
}
229+
if (windowCapability.getDynamicUpdateCapabilities() !== null && windowCapability.getDynamicUpdateCapabilities() !== undefined) {
230+
windowCapabilityCopy.setDynamicUpdateCapabilities(new DynamicUpdateCapabilities(
231+
JSON.parse(JSON.stringify(windowCapability.getDynamicUpdateCapabilities().getParameters()))
232+
));
233+
}
234+
if (windowCapability.getKeyboardCapabilities() !== null && windowCapability.getKeyboardCapabilities() !== undefined) {
235+
windowCapabilityCopy.setKeyboardCapabilities(new KeyboardCapabilities(
236+
JSON.parse(JSON.stringify(windowCapability.getKeyboardCapabilities().getParameters()))
237+
));
238+
}
199239
// A null windowID is assumed to be the DefaultWindow according to the spec, but that can be hard for developers to check, so set it explicitly.
200240
windowCapabilityCopy.setWindowID(windowId);
201241
return windowCapabilityCopy;
@@ -258,6 +298,15 @@ class SystemCapabilityManager extends _SubManagerBase {
258298
return [displayCapability];
259299
}
260300

301+
// Ford Sync bug returning incorrect template name for "NON-MEDIA" https://github.com/smartdevicelink/sdl_javascript_suite/issues/450
302+
const templatesAvailable = displayCapabilities.getTemplatesAvailable() !== null ? displayCapabilities.getTemplatesAvailable() : [];
303+
for (let index = 0; index < templatesAvailable.length; index++) {
304+
if (templatesAvailable[index] === 'NON_MEDIA') {
305+
templatesAvailable[index] = 'NON-MEDIA';
306+
break;
307+
}
308+
}
309+
261310
// copy all available display capabilities
262311
defaultWindowCapability.setTemplatesAvailable(displayCapabilities.getTemplatesAvailable())
263312
.setNumCustomPresetsAvailable(displayCapabilities.getNumCustomPresetsAvailable())
@@ -498,15 +547,66 @@ class SystemCapabilityManager extends _SubManagerBase {
498547
* @returns {Promise} - Promise returning either the capability Object or null if not found
499548
*/
500549
async updateCapability (systemCapabilityType) {
550+
return await this._updateCapabilityPrivate(systemCapabilityType, null, true);
551+
}
552+
553+
/**
554+
* Checks if the supplied capability type is currently subscribed for or not
555+
* @param {SystemCapabilityType} systemCapabilityType - type of capability desired
556+
* @returns {Boolean} - true if subscribed and false if not
557+
*/
558+
_isSubscribedToSystemCapability (systemCapabilityType) {
559+
return this._systemCapabilitiesSubscriptionStatus.has(systemCapabilityType);
560+
}
561+
562+
/**
563+
* Sends a request to core for the capability, instead of checking cached capabilities
564+
* @param {SystemCapabilityType} systemCapabilityType - A SystemCapabilityType enum value.
565+
* @param {Boolean} subscribe - Whether to attempt to subscribe to a capability. Null means no change to the subscription status. Defaults to null
566+
* @param {Boolean} forceUpdate - Whether to force a query to the head unit to retrieve a capability anyway
567+
* @param {Function} listener - A newly subscribed listener that may want to receive the updated capability object. Defaults to null
568+
* @returns {Promise} - Promise returning either the capability Object or null if not found
569+
*/
570+
async _updateCapabilityPrivate (systemCapabilityType, subscribe = null, forceUpdate, listener = null) {
571+
const cachedCapability = this.getCapability(systemCapabilityType);
572+
573+
// NOTE: subscription has always been supported within the lifespan of this library (5.2.0+)
574+
const shouldForceUpdate = forceUpdate && !this._isSubscribedToSystemCapability(systemCapabilityType);
575+
const shouldUpdateSystemCapabilitySubscription = (subscribe !== null) && (subscribe !== this._isSubscribedToSystemCapability(systemCapabilityType));
576+
const shouldSendGetCapabilityRequest = shouldForceUpdate || (cachedCapability === null) || shouldUpdateSystemCapabilitySubscription;
577+
const shouldCallListenerWithCachedValue = (cachedCapability !== null) && (listener !== null) && !shouldSendGetCapabilityRequest;
578+
579+
if (shouldCallListenerWithCachedValue) {
580+
listener(cachedCapability);
581+
}
582+
583+
if (!shouldSendGetCapabilityRequest) {
584+
return cachedCapability;
585+
}
586+
587+
// non-cached capability get
588+
501589
// don't bother getting a capability if it isn't queryable
502590
const getCapabilityMethodName = this._getCapabilityMethodForType(systemCapabilityType);
503591
if (getCapabilityMethodName === null) {
504592
console.error(`The systemCapabilityType ${systemCapabilityType} cannot be queried for`);
505593
return null;
506594
}
507595

596+
/*
597+
The subscription flag in the request should be set based on multiple variables:
598+
- if subscribe is null (no change), willSubscribe = current subscription status, or false if the HU does not support subscriptions
599+
- if subscribe is false, then willSubscribe = false
600+
- if subscribe is true and the HU supports subscriptions, then willSubscribe = true
601+
NOTE: subscription has always been supported within the lifespan of this library (5.2.0+)
602+
*/
603+
const shouldSubscribe = (subscribe !== null) ? subscribe : this._isSubscribedToSystemCapability(systemCapabilityType);
604+
508605
const request = new GetSystemCapability()
509606
.setSystemCapabilityType(systemCapabilityType);
607+
if (subscribe !== null) {
608+
request.setSubscribe(shouldSubscribe);
609+
}
510610

511611
if (this._lifecycleManager === null) {
512612
return null;
@@ -526,11 +626,18 @@ class SystemCapabilityManager extends _SubManagerBase {
526626
return null;
527627
}
528628

629+
// RPC get succeeded
630+
if (shouldSubscribe) {
631+
this._systemCapabilitiesSubscriptionStatus.add(systemCapabilityType);
632+
} else {
633+
this._systemCapabilitiesSubscriptionStatus.delete(systemCapabilityType);
634+
}
635+
529636
// invoke the correct get capability method
530637
const retrievedCapability = response.getSystemCapability()[getCapabilityMethodName]();
531638
this._setCapability(systemCapabilityType, retrievedCapability);
532639
// get the capability back through this method, because it may have changed the output
533-
return this.getCapability(systemCapabilityType, retrievedCapability);
640+
return this.getCapability(systemCapabilityType);
534641
}
535642

536643
/**
@@ -547,7 +654,7 @@ class SystemCapabilityManager extends _SubManagerBase {
547654
this._onSystemCapabilityListeners[systemCapabilityType] = [];
548655
}
549656
this._onSystemCapabilityListeners[systemCapabilityType].push(listener);
550-
this.updateCapability(systemCapabilityType); // send out a query, since the caller wants an update for this capability
657+
this._updateCapabilityPrivate(systemCapabilityType, true, false, listener); // send out a query, since the caller wants an update for this capability
551658
}
552659

553660
/**
@@ -573,6 +680,11 @@ class SystemCapabilityManager extends _SubManagerBase {
573680
}
574681
return listener !== listenerCheck;
575682
});
683+
// If the last listener for the supplied capability type is removed, unsubscribe from the capability type
684+
// always stay subscribied to DISPLAYS
685+
if (removed && this._onSystemCapabilityListeners[systemCapabilityType].length === 0 && this._isSubscribedToSystemCapability(systemCapabilityType) && systemCapabilityType !== SystemCapabilityType.DISPLAYS) {
686+
this._updateCapabilityPrivate(systemCapabilityType, false, false);
687+
}
576688
return removed;
577689
}
578690

@@ -618,7 +730,18 @@ class SystemCapabilityManager extends _SubManagerBase {
618730
if (this.getCapability(systemCapabilityType) !== null) { // The capability already exists
619731
switch (systemCapabilityType) {
620732
case SystemCapabilityType.APP_SERVICES: {
621-
// setCapability handles updating the local app service state
733+
// App services only updates what was changed so we need
734+
// to update the capability rather than override it
735+
const appServicesCapabilities = capability;
736+
if (capability !== null) {
737+
const appServicesCapabilitiesList = appServicesCapabilities.getAppServices();
738+
const cachedAppServicesCapabilities = this._appServicesCapabilities;
739+
// Update the cached app services
740+
if (cachedAppServicesCapabilities !== null) {
741+
capability.setAppServices(this._updateAppServices(cachedAppServicesCapabilities, appServicesCapabilitiesList));
742+
}
743+
// Set the new capability object to the updated cached capabilities
744+
}
622745
break;
623746
}
624747
case SystemCapabilityType.DISPLAYS: {
@@ -739,15 +862,7 @@ class SystemCapabilityManager extends _SubManagerBase {
739862
convertedCapabilities.setImageFields(defaultMainWindow.getImageFields());
740863
}
741864

742-
// Ford Sync bug returning incorrect template name for "NON-MEDIA" https://github.com/smartdevicelink/sdl_javascript_suite/issues/450
743-
const templatesAvailable = defaultMainWindow.getTemplatesAvailable() !== null ? defaultMainWindow.getTemplatesAvailable() : [];
744-
for (let index = 0; index < templatesAvailable.length; index++) {
745-
if (templatesAvailable[index] === 'NON_MEDIA') {
746-
templatesAvailable[index] = 'NON-MEDIA';
747-
break;
748-
}
749-
}
750-
convertedCapabilities.setTemplatesAvailable(templatesAvailable);
865+
convertedCapabilities.setTemplatesAvailable(defaultMainWindow.getTemplatesAvailable());
751866
convertedCapabilities.setNumCustomPresetsAvailable(defaultMainWindow.getNumCustomPresetsAvailable());
752867
convertedCapabilities.setMediaClockFormats([]); // mandatory field but allows empty array
753868
// if there are imageTypes in the response, we must assume graphics are supported
@@ -767,6 +882,79 @@ class SystemCapabilityManager extends _SubManagerBase {
767882

768883
return convertedCapabilities;
769884
}
885+
886+
/**
887+
* This method will update the current List<AppServiceCapability> with the updated items. If the
888+
* items don't exist in the original ist they will be added. If the original list is null or
889+
* empty, the new list will simply be set as the list.
890+
*
891+
* @param {AppServicesCapabilities} appServicesCapabilities - the AppServicesCapabilities instance
892+
* @param {AppServiceCapability[]} updatedAppServiceCapabilities - the array that have been updated
893+
* @returns {AppServiceCapability[]} the array of capabilities with old duplicate services removed
894+
*/
895+
_updateAppServices (appServicesCapabilities, updatedAppServiceCapabilities) {
896+
if (updatedAppServiceCapabilities === null) {
897+
return false;
898+
}
899+
900+
let appServiceCapabilities = appServicesCapabilities;
901+
902+
if (appServiceCapabilities === null) {
903+
// If there are currently no app services, create one to iterate over with no entries
904+
appServiceCapabilities = new Map();
905+
}
906+
907+
// Create a shallow copy for us to alter while iterating through the original list
908+
const tempList = new Map(appServiceCapabilities);
909+
910+
for (const updatedAppServiceCapability of updatedAppServiceCapabilities) {
911+
if (updatedAppServiceCapability !== null) {
912+
// First search if the record exists in the current list and remove it if so
913+
for (const appServiceCapability of appServiceCapabilities.values()) {
914+
if (this._matchesAppService(updatedAppServiceCapability, appServiceCapability)) {
915+
tempList.delete(appServiceCapability.getUpdatedAppServiceRecord().getServiceID()); // Remove the old entry
916+
break;
917+
}
918+
}
919+
920+
// Add the app service regardless of whether it was removed or not, the setCapability method will handle removals
921+
tempList.set(updatedAppServiceCapability.getUpdatedAppServiceRecord().getServiceID(), updatedAppServiceCapability);
922+
}
923+
}
924+
925+
return Array.from(tempList.values());
926+
}
927+
928+
/**
929+
* Helper method to compare AppServiceCapability instances
930+
* @param {AppServiceCapability} capability - an AppServiceCapability
931+
* @param {AppServiceCapability} capability2 - the AppServiceCapability to compare to the first
932+
* @returns {Boolean} if both AppServiceCapability objects refer to the same service
933+
*/
934+
_matchesAppService (capability = null, capability2 = null) {
935+
if (capability !== null && capability2 !== null) {
936+
const appServiceRecord = capability.getUpdatedAppServiceRecord();
937+
const otherASR = capability2.getUpdatedAppServiceRecord();
938+
939+
if (appServiceRecord !== null && otherASR !== null) {
940+
// If both service IDs exists we can compare them. If either is null we can't use
941+
// only this check.
942+
if (appServiceRecord.getServiceID() !== null && otherASR.getServiceID() !== null) {
943+
// return whether the app service IDs are equal or not
944+
return appServiceRecord.getServiceID().toLowerCase() === otherASR.getServiceID().toLowerCase();
945+
} else {
946+
const manifest = appServiceRecord.getServiceManifest();
947+
const otherManifest = otherASR.getServiceManifest();
948+
if (manifest !== null && otherManifest !== null) {
949+
// Check the service names, if they are the same it can be assumed they are the same service
950+
return (manifest.getServiceName() !== null && otherManifest.getServiceName() !== null
951+
&& manifest.getServiceName().toLowerCase() === otherManifest.getServiceName().toLowerCase());
952+
}
953+
}
954+
}
955+
}
956+
return false;
957+
}
770958
}
771959

772960
export { SystemCapabilityManager };

0 commit comments

Comments
 (0)