Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SDL-0238] Keyboard Enhancements #366

2 changes: 1 addition & 1 deletion lib/js/src/manager/screen/_ScreenManagerBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -457,7 +457,7 @@ class _ScreenManagerBase extends _SubManagerBase {

/**
* Preload choices to improve performance while presenting a choice set at a later time
* @param {ChoiceCell[]} choices - a list of ChoiceCell objects that will be part of a choice set later
* @param {ChoiceCell[]} choices - a list of ChoiceCell objects that will be part of a choice set later
* @returns {Promise} - A promise.
*/
async preloadChoices (choices) {
Expand Down
22 changes: 22 additions & 0 deletions lib/js/src/manager/screen/choiceset/KeyboardListener.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class KeyboardListener {
this._updateAutocompleteWithInput = null;
this._updateCharacterSetWithInput = null;
this._onKeyboardDidSendEvent = null;
this._onKeyboardInputMaskHasChanged = null;
}

/**
Expand Down Expand Up @@ -98,6 +99,16 @@ class KeyboardListener {
return this;
}

/**
* Set the onKeyboardInputMaskHasChanged function.
* @param {function} listener - A function to be invoked when the event occurs.
* @returns {KeyboardListener} - A reference to this instance to allow method chaining.
*/
setOnKeyboardInputMaskHasChanged (listener) {
this._onKeyboardInputMaskHasChanged = listener;
return this;
}

/**
* Safely attempts to invoke the onUserDidSubmitInput event.
* The keyboard session completed with some input.
Expand Down Expand Up @@ -162,6 +173,17 @@ class KeyboardListener {
this._onKeyboardDidSendEvent(event, currentInputText);
}
}

/**
* Safely attempts to invoke the onKeyboardInputMaskHasChanged event.
* Implement this to be notified of all events occurring on the keyboard
* @param {KeyboardEvent} event - The event that occurred
*/
onKeyboardInputMaskHasChanged (event) {
if (typeof this._onKeyboardInputMaskHasChanged === 'function') {
this._onKeyboardInputMaskHasChanged(event);
}
}
}

export { KeyboardListener };
70 changes: 62 additions & 8 deletions lib/js/src/manager/screen/choiceset/_ChoiceSetManagerBase.js
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,8 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
console.warn('ChoiceSetManager: There is a current or pending choice set, cancelling and continuing.');
}

customKeyboardConfig = this._createValidKeyboardConfigurationBasedOnKeyboardCapabilitiesFromConfiguration(customKeyboardConfig);

if (customKeyboardConfig === null) {
customKeyboardConfig = this._keyboardConfiguration !== null ? this._keyboardConfiguration : this._defaultKeyboardConfiguration();
}
Expand Down Expand Up @@ -424,16 +426,68 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
* @param {KeyboardProperties} keyboardConfiguration - The custom keyboard configuration to be used when the keyboard is displayed
*/
setKeyboardConfiguration (keyboardConfiguration = null) {
if (keyboardConfiguration === null) {
const properties = this._createValidKeyboardConfigurationBasedOnKeyboardCapabilitiesFromConfiguration(keyboardConfiguration);
if (properties === null) {
this._keyboardConfiguration = this._defaultKeyboardConfiguration();
} else {
this._keyboardConfiguration = new KeyboardProperties()
.setLanguage(keyboardConfiguration.getLanguage() === null ? Language.EN_US : keyboardConfiguration.getLanguage())
.setKeyboardLayout(keyboardConfiguration.getKeyboardLayout() === null ? KeyboardLayout.QWERTZ : keyboardConfiguration.getKeyboardLayout())
.setKeypressMode(keyboardConfiguration.getKeypressMode() === null ? KeypressMode.RESEND_CURRENT_ENTRY : keyboardConfiguration.getKeypressMode())
.setLimitedCharacterList(keyboardConfiguration.getLimitedCharacterList())
.setAutoCompleteText(keyboardConfiguration.getAutoCompleteText());
this._keyboardConfiguration = properties;
}
}

/**
* Takes a keyboard configuration and creates a valid version of it, if possible, based on keyboardCapabilities
* @param {KeyboardProperties} keyboardConfiguration - The custom keyboard configuration to be used when the keyboard is displayed
* @returns {KeyboardProperties|null} Returns KeyboardProperties or null if the keyboard layout is not supported
*/
_createValidKeyboardConfigurationBasedOnKeyboardCapabilitiesFromConfiguration (keyboardConfiguration = null) {
/**
* @type {KeyboardCapabilities}
*/
const keyboardCapabilities = (this._defaultMainWindowCapability && this._defaultMainWindowCapability.getKeyboardCapabilities()) || null;

// 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
if (keyboardCapabilities === null || keyboardConfiguration === null || keyboardConfiguration.getKeyboardLayout() === null) {
return keyboardConfiguration;
}

/**
* @type {KeyboardLayoutCapability}
*/
let selectedLayoutCapability = null;

for (const layoutCapability of keyboardCapabilities.getSupportedKeyboards()) {
if (layoutCapability.getKeyboardLayout() === keyboardConfiguration.getKeyboardLayout()) {
selectedLayoutCapability = layoutCapability;
break;
}
}

if (selectedLayoutCapability === null) {
console.error(`Configured keyboard layout is not supported: ${keyboardConfiguration.getKeyboardLayout()}`);
return null;
}

const modifiedKeyboardConfiguration = new KeyboardProperties(keyboardConfiguration.getParameters());

const customKeys = keyboardConfiguration.getCustomKeys();
if (!customKeys || !Array.isArray(customKeys) || customKeys.length === 0) {
modifiedKeyboardConfiguration.setCustomKeys(null);
} else {
// 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.
const numConfigurableKeys = selectedLayoutCapability.getNumConfigurableKeys();
if (customKeys.length > numConfigurableKeys) {
modifiedKeyboardConfiguration.setCustomKeys(customKeys.slice(0, numConfigurableKeys));
console.warn(`${customKeys.length} custom keys set, but the selected layout: ${keyboardConfiguration.getKeyboardLayout()} only supports ${numConfigurableKeys}. Dropping the rest.`);
}
}

// If the keyboard does not support masking input characters, we will remove it from the keyboard configuration
if (!keyboardCapabilities.getMaskInputCharactersSupported()) {
modifiedKeyboardConfiguration.setMaskInputCharacters(null);
console.warn('Mask input characters is not supported');
}

return modifiedKeyboardConfiguration;
}


Expand Down Expand Up @@ -515,7 +569,7 @@ class _ChoiceSetManagerBase extends _SubManagerBase {
}

/**
* Defauly keyboard properties object
* Default keyboard properties object
* @returns {KeyboardProperties} - A KeyboardProperties RPC
*/
_defaultKeyboardConfiguration () {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@ class _PresentChoiceSetOperation extends _Task {
} else if (onKeyboardInput.getEvent() === KeyboardEvent.ENTRY_ABORTED || onKeyboardInput.getEvent() === KeyboardEvent.ENTRY_CANCELLED) {
// notify of abort / cancelation
this._keyboardListener.onKeyboardDidAbortWithReason(onKeyboardInput.getEvent());
} else if (onKeyboardInput.getEvent() === KeyboardEvent.INPUT_KEY_MASK_ENABLED || onKeyboardInput.getEvent() === KeyboardEvent.INPUT_KEY_MASK_DISABLED) {
this._keyboardListener.onKeyboardInputMaskHasChanged(onKeyboardInput.getEvent());
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,8 @@ class _PresentKeyboardOperation extends _Task {
} else if (onKeyboardInput.getEvent() === KeyboardEvent.ENTRY_ABORTED || onKeyboardInput.getEvent() === KeyboardEvent.ENTRY_CANCELLED) {
// notify of abort / cancelation
this._keyboardListener.onKeyboardDidAbortWithReason(onKeyboardInput.getEvent());
} else if (onKeyboardInput.getEvent() === KeyboardEvent.INPUT_KEY_MASK_ENABLED || onKeyboardInput.getEvent() === KeyboardEvent.INPUT_KEY_MASK_DISABLED) {
this._keyboardListener.onKeyboardInputMaskHasChanged(onKeyboardInput.getEvent());
}
};

Expand Down
22 changes: 21 additions & 1 deletion lib/js/src/rpc/enums/KeyboardEvent.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable camelcase */
/*
* Copyright (c) 2020, SmartDeviceLink Consortium, Inc.
* Copyright (c) 2021, SmartDeviceLink Consortium, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -88,6 +88,24 @@ class KeyboardEvent extends Enum {
return KeyboardEvent._MAP.ENTRY_ABORTED;
}

/**
* Get the enum value for INPUT_KEY_MASK_ENABLED.
* @since SmartDeviceLink 7.1.0
* @returns {String} - The enum value.
*/
static get INPUT_KEY_MASK_ENABLED () {
return KeyboardEvent._MAP.INPUT_KEY_MASK_ENABLED;
}

/**
* Get the enum value for INPUT_KEY_MASK_DISABLED.
* @since SmartDeviceLink 7.1.0
* @returns {String} - The enum value.
*/
static get INPUT_KEY_MASK_DISABLED () {
return KeyboardEvent._MAP.INPUT_KEY_MASK_DISABLED;
}

/**
* Get the value for the given enum key
* @param {*} key - A key to find in the map of the subclass
Expand Down Expand Up @@ -121,6 +139,8 @@ KeyboardEvent._MAP = Object.freeze({
'ENTRY_VOICE': 'ENTRY_VOICE',
'ENTRY_CANCELLED': 'ENTRY_CANCELLED',
'ENTRY_ABORTED': 'ENTRY_ABORTED',
'INPUT_KEY_MASK_ENABLED': 'INPUT_KEY_MASK_ENABLED',
'INPUT_KEY_MASK_DISABLED': 'INPUT_KEY_MASK_DISABLED',
});

export { KeyboardEvent };
108 changes: 108 additions & 0 deletions lib/js/src/rpc/enums/KeyboardInputMask.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/* eslint-disable camelcase */
/*
* Copyright (c) 2021, SmartDeviceLink Consortium, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the
* distribution.
*
* Neither the name of the SmartDeviceLink Consortium Inc. nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/

import { Enum } from '../../util/Enum.js';

/**
* Enumeration listing possible input character masking.
* @typedef {Enum} KeyboardInputMask
* @property {Object} _MAP
*/
class KeyboardInputMask extends Enum {
/**
* Constructor for KeyboardInputMask.
* @class
* @since SmartDeviceLink 7.1.0
*/
constructor () {
super();
}

/**
* Get the enum value for ENABLE_INPUT_KEY_MASK.
* @returns {String} - The enum value.
*/
static get ENABLE_INPUT_KEY_MASK () {
return KeyboardInputMask._MAP.ENABLE_INPUT_KEY_MASK;
}

/**
* Get the enum value for DISABLE_INPUT_KEY_MASK.
* @returns {String} - The enum value.
*/
static get DISABLE_INPUT_KEY_MASK () {
return KeyboardInputMask._MAP.DISABLE_INPUT_KEY_MASK;
}

/**
* Get the enum value for USER_CHOICE_INPUT_KEY_MASK.
* @returns {String} - The enum value.
*/
static get USER_CHOICE_INPUT_KEY_MASK () {
return KeyboardInputMask._MAP.USER_CHOICE_INPUT_KEY_MASK;
}

/**
* Get the value for the given enum key
* @param {*} key - A key to find in the map of the subclass
* @returns {*} - Returns a value if found, or null if not found
*/
static valueForKey (key) {
return KeyboardInputMask._valueForKey(key, KeyboardInputMask._MAP);
}

/**
* Get the key for the given enum value
* @param {*} value - A primitive value to find the matching key for in the map of the subclass
* @returns {*} - Returns a key if found, or null if not found
*/
static keyForValue (value) {
return KeyboardInputMask._keyForValue(value, KeyboardInputMask._MAP);
}

/**
* Retrieve all enumerated values for this class
* @returns {*} - Returns an array of values
*/
static values () {
return Object.values(KeyboardInputMask._MAP);
}
}

KeyboardInputMask._MAP = Object.freeze({
'ENABLE_INPUT_KEY_MASK': 'ENABLE_INPUT_KEY_MASK',
'DISABLE_INPUT_KEY_MASK': 'DISABLE_INPUT_KEY_MASK',
'USER_CHOICE_INPUT_KEY_MASK': 'USER_CHOICE_INPUT_KEY_MASK',
});

export { KeyboardInputMask };
12 changes: 11 additions & 1 deletion lib/js/src/rpc/enums/KeyboardLayout.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable camelcase */
/*
* Copyright (c) 2020, SmartDeviceLink Consortium, Inc.
* Copyright (c) 2021, SmartDeviceLink Consortium, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -72,6 +72,15 @@ class KeyboardLayout extends Enum {
return KeyboardLayout._MAP.AZERTY;
}

/**
* Get the enum value for NUMERIC.
* @since SmartDeviceLink 7.1.0
* @returns {String} - The enum value.
*/
static get NUMERIC () {
return KeyboardLayout._MAP.NUMERIC;
}

/**
* Get the value for the given enum key
* @param {*} key - A key to find in the map of the subclass
Expand Down Expand Up @@ -103,6 +112,7 @@ KeyboardLayout._MAP = Object.freeze({
'QWERTY': 'QWERTY',
'QWERTZ': 'QWERTZ',
'AZERTY': 'AZERTY',
'NUMERIC': 'NUMERIC',
});

export { KeyboardLayout };
Loading