Skip to content

Commit 38969fe

Browse files
authored
Merge pull request #366 from LuxoftSDL/feature/SDL-0238-Keyboard-Enhancements
[SDL-0238] Keyboard Enhancements
2 parents 9f90731 + 21771bb commit 38969fe

21 files changed

+987
-18
lines changed

lib/js/src/manager/screen/_ScreenManagerBase.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,7 @@ class _ScreenManagerBase extends _SubManagerBase {
457457

458458
/**
459459
* Preload choices to improve performance while presenting a choice set at a later time
460-
* @param {ChoiceCell[]} choices - a list of ChoiceCell objects that will be part of a choice set later
460+
* @param {ChoiceCell[]} choices - a list of ChoiceCell objects that will be part of a choice set later
461461
* @returns {Promise} - A promise.
462462
*/
463463
async preloadChoices (choices) {

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

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class KeyboardListener {
4646
this._updateAutocompleteWithInput = null;
4747
this._updateCharacterSetWithInput = null;
4848
this._onKeyboardDidSendEvent = null;
49+
this._onKeyboardInputMaskHasChanged = null;
4950
}
5051

5152
/**
@@ -98,6 +99,16 @@ class KeyboardListener {
9899
return this;
99100
}
100101

102+
/**
103+
* Set the onKeyboardInputMaskHasChanged function.
104+
* @param {function} listener - A function to be invoked when the event occurs.
105+
* @returns {KeyboardListener} - A reference to this instance to allow method chaining.
106+
*/
107+
setOnKeyboardInputMaskHasChanged (listener) {
108+
this._onKeyboardInputMaskHasChanged = listener;
109+
return this;
110+
}
111+
101112
/**
102113
* Safely attempts to invoke the onUserDidSubmitInput event.
103114
* The keyboard session completed with some input.
@@ -162,6 +173,17 @@ class KeyboardListener {
162173
this._onKeyboardDidSendEvent(event, currentInputText);
163174
}
164175
}
176+
177+
/**
178+
* Safely attempts to invoke the onKeyboardInputMaskHasChanged event.
179+
* Implement this to be notified of all events occurring on the keyboard
180+
* @param {KeyboardEvent} event - The event that occurred
181+
*/
182+
onKeyboardInputMaskHasChanged (event) {
183+
if (typeof this._onKeyboardInputMaskHasChanged === 'function') {
184+
this._onKeyboardInputMaskHasChanged(event);
185+
}
186+
}
165187
}
166188

167189
export { KeyboardListener };

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

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,8 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
381381
console.warn('ChoiceSetManager: There is a current or pending choice set, cancelling and continuing.');
382382
}
383383

384+
customKeyboardConfig = this._createValidKeyboardConfigurationBasedOnKeyboardCapabilitiesFromConfiguration(customKeyboardConfig);
385+
384386
if (customKeyboardConfig === null) {
385387
customKeyboardConfig = this._keyboardConfiguration !== null ? this._keyboardConfiguration : this._defaultKeyboardConfiguration();
386388
}
@@ -424,16 +426,68 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
424426
* @param {KeyboardProperties} keyboardConfiguration - The custom keyboard configuration to be used when the keyboard is displayed
425427
*/
426428
setKeyboardConfiguration (keyboardConfiguration = null) {
427-
if (keyboardConfiguration === null) {
429+
const properties = this._createValidKeyboardConfigurationBasedOnKeyboardCapabilitiesFromConfiguration(keyboardConfiguration);
430+
if (properties === null) {
428431
this._keyboardConfiguration = this._defaultKeyboardConfiguration();
429432
} else {
430-
this._keyboardConfiguration = new KeyboardProperties()
431-
.setLanguage(keyboardConfiguration.getLanguage() === null ? Language.EN_US : keyboardConfiguration.getLanguage())
432-
.setKeyboardLayout(keyboardConfiguration.getKeyboardLayout() === null ? KeyboardLayout.QWERTZ : keyboardConfiguration.getKeyboardLayout())
433-
.setKeypressMode(keyboardConfiguration.getKeypressMode() === null ? KeypressMode.RESEND_CURRENT_ENTRY : keyboardConfiguration.getKeypressMode())
434-
.setLimitedCharacterList(keyboardConfiguration.getLimitedCharacterList())
435-
.setAutoCompleteText(keyboardConfiguration.getAutoCompleteText());
433+
this._keyboardConfiguration = properties;
434+
}
435+
}
436+
437+
/**
438+
* Takes a keyboard configuration and creates a valid version of it, if possible, based on keyboardCapabilities
439+
* @param {KeyboardProperties} keyboardConfiguration - The custom keyboard configuration to be used when the keyboard is displayed
440+
* @returns {KeyboardProperties|null} Returns KeyboardProperties or null if the keyboard layout is not supported
441+
*/
442+
_createValidKeyboardConfigurationBasedOnKeyboardCapabilitiesFromConfiguration (keyboardConfiguration = null) {
443+
/**
444+
* @type {KeyboardCapabilities}
445+
*/
446+
const keyboardCapabilities = (this._defaultMainWindowCapability && this._defaultMainWindowCapability.getKeyboardCapabilities()) || null;
447+
448+
// If there are no keyboard capabilities, if there is no passed keyboard configuration, or if there is no layout to the passed keyboard configuration, just pass back the passed in configuration
449+
if (keyboardCapabilities === null || keyboardConfiguration === null || keyboardConfiguration.getKeyboardLayout() === null) {
450+
return keyboardConfiguration;
451+
}
452+
453+
/**
454+
* @type {KeyboardLayoutCapability}
455+
*/
456+
let selectedLayoutCapability = null;
457+
458+
for (const layoutCapability of keyboardCapabilities.getSupportedKeyboards()) {
459+
if (layoutCapability.getKeyboardLayout() === keyboardConfiguration.getKeyboardLayout()) {
460+
selectedLayoutCapability = layoutCapability;
461+
break;
462+
}
463+
}
464+
465+
if (selectedLayoutCapability === null) {
466+
console.error(`Configured keyboard layout is not supported: ${keyboardConfiguration.getKeyboardLayout()}`);
467+
return null;
468+
}
469+
470+
const modifiedKeyboardConfiguration = new KeyboardProperties(keyboardConfiguration.getParameters());
471+
472+
const customKeys = keyboardConfiguration.getCustomKeys();
473+
if (!customKeys || !Array.isArray(customKeys) || customKeys.length === 0) {
474+
modifiedKeyboardConfiguration.setCustomKeys(null);
475+
} else {
476+
// If there are more custom keys than are allowed for the selected keyboard layout, we need to trim the number of keys to only use the first n number of custom keys, where n is the number of allowed custom keys for that layout.
477+
const numConfigurableKeys = selectedLayoutCapability.getNumConfigurableKeys();
478+
if (customKeys.length > numConfigurableKeys) {
479+
modifiedKeyboardConfiguration.setCustomKeys(customKeys.slice(0, numConfigurableKeys));
480+
console.warn(`${customKeys.length} custom keys set, but the selected layout: ${keyboardConfiguration.getKeyboardLayout()} only supports ${numConfigurableKeys}. Dropping the rest.`);
481+
}
436482
}
483+
484+
// If the keyboard does not support masking input characters, we will remove it from the keyboard configuration
485+
if (!keyboardCapabilities.getMaskInputCharactersSupported()) {
486+
modifiedKeyboardConfiguration.setMaskInputCharacters(null);
487+
console.warn('Mask input characters is not supported');
488+
}
489+
490+
return modifiedKeyboardConfiguration;
437491
}
438492

439493

@@ -515,7 +569,7 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
515569
}
516570

517571
/**
518-
* Defauly keyboard properties object
572+
* Default keyboard properties object
519573
* @returns {KeyboardProperties} - A KeyboardProperties RPC
520574
*/
521575
_defaultKeyboardConfiguration () {

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,8 @@ class _PresentChoiceSetOperation extends _Task {
306306
} else if (onKeyboardInput.getEvent() === KeyboardEvent.ENTRY_ABORTED || onKeyboardInput.getEvent() === KeyboardEvent.ENTRY_CANCELLED) {
307307
// notify of abort / cancelation
308308
this._keyboardListener.onKeyboardDidAbortWithReason(onKeyboardInput.getEvent());
309+
} else if (onKeyboardInput.getEvent() === KeyboardEvent.INPUT_KEY_MASK_ENABLED || onKeyboardInput.getEvent() === KeyboardEvent.INPUT_KEY_MASK_DISABLED) {
310+
this._keyboardListener.onKeyboardInputMaskHasChanged(onKeyboardInput.getEvent());
309311
}
310312
};
311313

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,8 @@ class _PresentKeyboardOperation extends _Task {
249249
} else if (onKeyboardInput.getEvent() === KeyboardEvent.ENTRY_ABORTED || onKeyboardInput.getEvent() === KeyboardEvent.ENTRY_CANCELLED) {
250250
// notify of abort / cancelation
251251
this._keyboardListener.onKeyboardDidAbortWithReason(onKeyboardInput.getEvent());
252+
} else if (onKeyboardInput.getEvent() === KeyboardEvent.INPUT_KEY_MASK_ENABLED || onKeyboardInput.getEvent() === KeyboardEvent.INPUT_KEY_MASK_DISABLED) {
253+
this._keyboardListener.onKeyboardInputMaskHasChanged(onKeyboardInput.getEvent());
252254
}
253255
};
254256

lib/js/src/rpc/enums/KeyboardEvent.js

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable camelcase */
22
/*
3-
* Copyright (c) 2020, SmartDeviceLink Consortium, Inc.
3+
* Copyright (c) 2021, SmartDeviceLink Consortium, Inc.
44
* All rights reserved.
55
*
66
* Redistribution and use in source and binary forms, with or without
@@ -88,6 +88,24 @@ class KeyboardEvent extends Enum {
8888
return KeyboardEvent._MAP.ENTRY_ABORTED;
8989
}
9090

91+
/**
92+
* Get the enum value for INPUT_KEY_MASK_ENABLED.
93+
* @since SmartDeviceLink 7.1.0
94+
* @returns {String} - The enum value.
95+
*/
96+
static get INPUT_KEY_MASK_ENABLED () {
97+
return KeyboardEvent._MAP.INPUT_KEY_MASK_ENABLED;
98+
}
99+
100+
/**
101+
* Get the enum value for INPUT_KEY_MASK_DISABLED.
102+
* @since SmartDeviceLink 7.1.0
103+
* @returns {String} - The enum value.
104+
*/
105+
static get INPUT_KEY_MASK_DISABLED () {
106+
return KeyboardEvent._MAP.INPUT_KEY_MASK_DISABLED;
107+
}
108+
91109
/**
92110
* Get the value for the given enum key
93111
* @param {*} key - A key to find in the map of the subclass
@@ -121,6 +139,8 @@ KeyboardEvent._MAP = Object.freeze({
121139
'ENTRY_VOICE': 'ENTRY_VOICE',
122140
'ENTRY_CANCELLED': 'ENTRY_CANCELLED',
123141
'ENTRY_ABORTED': 'ENTRY_ABORTED',
142+
'INPUT_KEY_MASK_ENABLED': 'INPUT_KEY_MASK_ENABLED',
143+
'INPUT_KEY_MASK_DISABLED': 'INPUT_KEY_MASK_DISABLED',
124144
});
125145

126146
export { KeyboardEvent };
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/* eslint-disable camelcase */
2+
/*
3+
* Copyright (c) 2021, SmartDeviceLink Consortium, Inc.
4+
* All rights reserved.
5+
*
6+
* Redistribution and use in source and binary forms, with or without
7+
* modification, are permitted provided that the following conditions are met:
8+
*
9+
* Redistributions of source code must retain the above copyright notice, this
10+
* list of conditions and the following disclaimer.
11+
*
12+
* Redistributions in binary form must reproduce the above copyright notice,
13+
* this list of conditions and the following
14+
* disclaimer in the documentation and/or other materials provided with the
15+
* distribution.
16+
*
17+
* Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
18+
* its contributors may be used to endorse or promote products derived
19+
* from this software without specific prior written permission.
20+
*
21+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31+
* POSSIBILITY OF SUCH DAMAGE.
32+
*/
33+
34+
import { Enum } from '../../util/Enum.js';
35+
36+
/**
37+
* Enumeration listing possible input character masking.
38+
* @typedef {Enum} KeyboardInputMask
39+
* @property {Object} _MAP
40+
*/
41+
class KeyboardInputMask extends Enum {
42+
/**
43+
* Constructor for KeyboardInputMask.
44+
* @class
45+
* @since SmartDeviceLink 7.1.0
46+
*/
47+
constructor () {
48+
super();
49+
}
50+
51+
/**
52+
* Get the enum value for ENABLE_INPUT_KEY_MASK.
53+
* @returns {String} - The enum value.
54+
*/
55+
static get ENABLE_INPUT_KEY_MASK () {
56+
return KeyboardInputMask._MAP.ENABLE_INPUT_KEY_MASK;
57+
}
58+
59+
/**
60+
* Get the enum value for DISABLE_INPUT_KEY_MASK.
61+
* @returns {String} - The enum value.
62+
*/
63+
static get DISABLE_INPUT_KEY_MASK () {
64+
return KeyboardInputMask._MAP.DISABLE_INPUT_KEY_MASK;
65+
}
66+
67+
/**
68+
* Get the enum value for USER_CHOICE_INPUT_KEY_MASK.
69+
* @returns {String} - The enum value.
70+
*/
71+
static get USER_CHOICE_INPUT_KEY_MASK () {
72+
return KeyboardInputMask._MAP.USER_CHOICE_INPUT_KEY_MASK;
73+
}
74+
75+
/**
76+
* Get the value for the given enum key
77+
* @param {*} key - A key to find in the map of the subclass
78+
* @returns {*} - Returns a value if found, or null if not found
79+
*/
80+
static valueForKey (key) {
81+
return KeyboardInputMask._valueForKey(key, KeyboardInputMask._MAP);
82+
}
83+
84+
/**
85+
* Get the key for the given enum value
86+
* @param {*} value - A primitive value to find the matching key for in the map of the subclass
87+
* @returns {*} - Returns a key if found, or null if not found
88+
*/
89+
static keyForValue (value) {
90+
return KeyboardInputMask._keyForValue(value, KeyboardInputMask._MAP);
91+
}
92+
93+
/**
94+
* Retrieve all enumerated values for this class
95+
* @returns {*} - Returns an array of values
96+
*/
97+
static values () {
98+
return Object.values(KeyboardInputMask._MAP);
99+
}
100+
}
101+
102+
KeyboardInputMask._MAP = Object.freeze({
103+
'ENABLE_INPUT_KEY_MASK': 'ENABLE_INPUT_KEY_MASK',
104+
'DISABLE_INPUT_KEY_MASK': 'DISABLE_INPUT_KEY_MASK',
105+
'USER_CHOICE_INPUT_KEY_MASK': 'USER_CHOICE_INPUT_KEY_MASK',
106+
});
107+
108+
export { KeyboardInputMask };

lib/js/src/rpc/enums/KeyboardLayout.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable camelcase */
22
/*
3-
* Copyright (c) 2020, SmartDeviceLink Consortium, Inc.
3+
* Copyright (c) 2021, SmartDeviceLink Consortium, Inc.
44
* All rights reserved.
55
*
66
* Redistribution and use in source and binary forms, with or without
@@ -72,6 +72,15 @@ class KeyboardLayout extends Enum {
7272
return KeyboardLayout._MAP.AZERTY;
7373
}
7474

75+
/**
76+
* Get the enum value for NUMERIC.
77+
* @since SmartDeviceLink 7.1.0
78+
* @returns {String} - The enum value.
79+
*/
80+
static get NUMERIC () {
81+
return KeyboardLayout._MAP.NUMERIC;
82+
}
83+
7584
/**
7685
* Get the value for the given enum key
7786
* @param {*} key - A key to find in the map of the subclass
@@ -103,6 +112,7 @@ KeyboardLayout._MAP = Object.freeze({
103112
'QWERTY': 'QWERTY',
104113
'QWERTZ': 'QWERTZ',
105114
'AZERTY': 'AZERTY',
115+
'NUMERIC': 'NUMERIC',
106116
});
107117

108118
export { KeyboardLayout };

0 commit comments

Comments
 (0)