UNPKG

@nativescript/core

Version:

A JavaScript library providing an easy to use api for interacting with iOS and Android platform APIs.

496 lines • 20.7 kB
import { EditableTextBase as EditableTextBaseCommon, autofillTypeProperty, keyboardTypeProperty, returnKeyTypeProperty, editableProperty, autocapitalizationTypeProperty, autocorrectProperty, hintProperty, placeholderColorProperty, maxLengthProperty } from './editable-text-base-common'; import { textTransformProperty, textProperty, resetSymbol } from '../text-base'; import { Color } from '../../color'; import { ad } from '../../utils'; import { SDK_VERSION } from '../../utils/constants'; import * as timer from '../../timer'; export * from './editable-text-base-common'; //https://github.com/NativeScript/NativeScript/issues/2942 export let dismissKeyboardTimeoutId; let EditTextListeners; function clearDismissTimer() { if (dismissKeyboardTimeoutId) { clearTimeout(dismissKeyboardTimeoutId); dismissKeyboardTimeoutId = null; } } function dismissSoftInput(view) { clearDismissTimer(); if (!dismissKeyboardTimeoutId) { dismissKeyboardTimeoutId = timer.setTimeout(() => { const activity = view._context; dismissKeyboardTimeoutId = null; const focused = activity && activity.getCurrentFocus(); if (focused && !(focused instanceof android.widget.EditText)) { // warning `ad.dismissSoftInput` will actually focus the next view // if we pass a null parameter!!! // => focus and show keyboard again // the fix was for where there were multiple TextField for which it would work! // with this it will still work without breaking for single TextField ad.dismissSoftInput(focused); } }, 10); } } function initializeEditTextListeners() { if (EditTextListeners) { return; } var EditTextListenersImpl = /** @class */ (function (_super) { __extends(EditTextListenersImpl, _super); function EditTextListenersImpl(owner) { var _this = _super.call(this) || this; _this.owner = owner; return global.__native(_this); } EditTextListenersImpl.prototype.beforeTextChanged = function (text, start, count, after) { var _a, _b; (_b = (_a = this.owner) === null || _a === void 0 ? void 0 : _a.get()) === null || _b === void 0 ? void 0 : _b.beforeTextChanged(text, start, count, after); }; EditTextListenersImpl.prototype.onTextChanged = function (text, start, before, count) { var _a, _b; (_b = (_a = this.owner) === null || _a === void 0 ? void 0 : _a.get()) === null || _b === void 0 ? void 0 : _b.onTextChanged(text, start, before, count); }; EditTextListenersImpl.prototype.afterTextChanged = function (editable) { var _a, _b; (_b = (_a = this.owner) === null || _a === void 0 ? void 0 : _a.get()) === null || _b === void 0 ? void 0 : _b.afterTextChanged(editable); }; EditTextListenersImpl.prototype.onFocusChange = function (view, hasFocus) { var _a, _b; (_b = (_a = this.owner) === null || _a === void 0 ? void 0 : _a.get()) === null || _b === void 0 ? void 0 : _b.onFocusChange(view, hasFocus); }; EditTextListenersImpl.prototype.onEditorAction = function (textView, actionId, event) { var _a, _b; return ((_b = (_a = this.owner) === null || _a === void 0 ? void 0 : _a.get()) === null || _b === void 0 ? void 0 : _b.onEditorAction(textView, actionId, event)) || false; }; var _a; EditTextListenersImpl = __decorate([ Interfaces([android.text.TextWatcher, android.view.View.OnFocusChangeListener, android.widget.TextView.OnEditorActionListener]), __metadata("design:paramtypes", [typeof (_a = typeof WeakRef !== "undefined" && WeakRef) === "function" ? _a : Object]) ], EditTextListenersImpl); return EditTextListenersImpl; }(java.lang.Object)); EditTextListeners = EditTextListenersImpl; } export class EditableTextBase extends EditableTextBaseCommon { _onReturnPress() { // } createNativeView() { return new android.widget.EditText(this._context); } initNativeView() { super.initNativeView(); const editText = this.nativeTextViewProtected; this._configureEditText(editText); initializeEditTextListeners(); const listeners = new EditTextListeners(new WeakRef(this)); editText.addTextChangedListener(listeners); editText.setOnFocusChangeListener(listeners); editText.setOnEditorActionListener(listeners); editText.listener = listeners; this._inputType = editText.getInputType(); } disposeNativeView() { const editText = this.nativeTextViewProtected; editText.removeTextChangedListener(editText.listener); editText.setOnFocusChangeListener(null); editText.setOnEditorActionListener(null); editText.listener.owner = null; editText.listener = null; this._keyListenerCache = null; this._dirtyTextAccumulator = undefined; this._inputType = 0; super.disposeNativeView(); } resetNativeView() { super.resetNativeView(); this.nativeTextViewProtected.setInputType(this._inputType); } onUnloaded() { this.dismissSoftInput(); super.onUnloaded(); } dismissSoftInput() { const nativeView = this.nativeTextViewProtected; if (!nativeView) { return; } ad.dismissSoftInput(nativeView); } focus() { const nativeView = this.nativeTextViewProtected; if (!nativeView) { return; } const result = super.focus(); if (result) { ad.showSoftInput(this.nativeTextViewProtected); } return result; } _setInputType(inputType) { const nativeView = this.nativeTextViewProtected; try { this._changeFromCode = true; nativeView.setInputType(parseInt(inputType, 10)); } finally { this._changeFromCode = false; } // setInputType will change the keyListener so we should cache it again const listener = nativeView.getKeyListener(); if (listener) { this._keyListenerCache = listener; } // clear these fields instead of clearing listener. // this allows input Type to be changed even after editable is false. if (!this.editable) { nativeView.setFocusable(false); nativeView.setFocusableInTouchMode(false); nativeView.setLongClickable(false); nativeView.setClickable(false); } } [textProperty.getDefault]() { return resetSymbol; } [textProperty.setNative](value) { try { this._changeFromCode = true; this._setNativeText(value === resetSymbol); } finally { this._changeFromCode = false; } } [keyboardTypeProperty.getDefault]() { return this.nativeTextViewProtected.getInputType(); } [keyboardTypeProperty.setNative](value) { let newInputType; switch (value) { case 'datetime': newInputType = android.text.InputType.TYPE_CLASS_DATETIME | android.text.InputType.TYPE_DATETIME_VARIATION_NORMAL; break; case 'phone': newInputType = android.text.InputType.TYPE_CLASS_PHONE; break; case 'number': newInputType = android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_NORMAL | android.text.InputType.TYPE_NUMBER_FLAG_SIGNED | android.text.InputType.TYPE_NUMBER_FLAG_DECIMAL; break; case 'url': newInputType = android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_URI; break; case 'email': newInputType = android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS; break; case 'integer': newInputType = android.text.InputType.TYPE_CLASS_NUMBER; break; default: { const inputType = +value; if (!isNaN(inputType)) { newInputType = inputType; } else { newInputType = android.text.InputType.TYPE_CLASS_TEXT; } break; } } this._setInputType(newInputType); } [autofillTypeProperty.setNative](value) { if (SDK_VERSION < 26) { return; } let newOptions; switch (value) { case 'phone': newOptions = 'phone'; // android.view.View.AUTOFILL_HINT_PHONE break; case 'postalCode': newOptions = 'postalCode'; // android.view.View.AUTOFILL_HINT_POSTAL_CODE break; case 'creditCardNumber': newOptions = 'creditCardNumber'; // android.view.View.AUTOFILL_HINT_CREDIT_CARD_NUMBER break; case 'email': newOptions = 'emailAddress'; // android.view.View.AUTOFILL_HINT_EMAIL_ADDRESS break; case 'name': newOptions = 'name'; // android.view.View.AUTOFILL_HINT_NAME break; case 'username': newOptions = 'username'; // android.view.View.AUTOFILL_HINT_USERNAME break; case 'password': newOptions = 'password'; // android.view.View.AUTOFILL_HINT_PASSWORD break; case 'newPassword': newOptions = 'newPassword'; // android.view.View.AUTOFILL_HINT_NEW_PASSWORD break; case 'newUsername': newOptions = 'newUsername'; // android.view.View.AUTOFILL_HINT_NEW_USERNAME break; case 'oneTimeCode': newOptions = '2faAppOTPCode'; // android.view.View.AUTOFILL_HINT_2FA_APP_OTP break; case 'none': newOptions = null; break; default: { newOptions = value; break; } } if (newOptions) { const array = Array.create(java.lang.String, 1); array[0] = newOptions; this.nativeTextViewProtected.setAutofillHints(array); } else { this.nativeTextViewProtected.setAutofillHints(null); } } [returnKeyTypeProperty.getDefault]() { const ime = this.nativeTextViewProtected.getImeOptions(); switch (ime) { case android.view.inputmethod.EditorInfo.IME_ACTION_DONE: return 'done'; case android.view.inputmethod.EditorInfo.IME_ACTION_GO: return 'go'; case android.view.inputmethod.EditorInfo.IME_ACTION_NEXT: return 'next'; case android.view.inputmethod.EditorInfo.IME_ACTION_SEARCH: return 'search'; case android.view.inputmethod.EditorInfo.IME_ACTION_SEND: return 'send'; default: return ime.toString(); } } [returnKeyTypeProperty.setNative](value) { let newImeOptions; switch (value) { case 'done': newImeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_DONE; break; case 'go': newImeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_GO; break; case 'next': newImeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_NEXT; break; case 'search': newImeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_SEARCH; break; case 'send': newImeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_SEND; break; default: { const ime = +value; if (!isNaN(ime)) { newImeOptions = ime; } else { newImeOptions = android.view.inputmethod.EditorInfo.IME_ACTION_UNSPECIFIED; } break; } } this.nativeTextViewProtected.setImeOptions(newImeOptions); } [editableProperty.setNative](value) { const nativeView = this.nativeTextViewProtected; if (value) { nativeView.setKeyListener(this._keyListenerCache); } else { if (!this._keyListenerCache) { this._keyListenerCache = nativeView.getKeyListener(); } nativeView.setKeyListener(null); } } [autocapitalizationTypeProperty.getDefault]() { const inputType = this.nativeTextViewProtected.getInputType(); if ((inputType & android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS) === android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS) { return 'words'; } else if ((inputType & android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) === android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES) { return 'sentences'; } else if ((inputType & android.text.InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) === android.text.InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) { return 'allcharacters'; } else { return inputType.toString(); } } [autocapitalizationTypeProperty.setNative](value) { let inputType = this.nativeTextViewProtected.getInputType(); inputType = inputType & ~28672; //28672 (0x00070000) 13,14,15bits (111 0000 0000 0000) switch (value) { case 'none': //Do nothing, we have lowered the three bits above. break; case 'words': inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_CAP_WORDS; //8192 (0x00020000) 14th bit break; case 'sentences': inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES; //16384(0x00040000) 15th bit break; case 'allcharacters': inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS; //4096 (0x00010000) 13th bit break; default: { const number = +value; // We set the default value. if (!isNaN(number)) { inputType = number; } else { inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_CAP_SENTENCES; } break; } } this._setInputType(inputType); } [autocorrectProperty.getDefault]() { const autocorrect = this.nativeTextViewProtected.getInputType(); if ((autocorrect & android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) === android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT) { return true; } return false; } [autocorrectProperty.setNative](value) { let inputType = this.nativeTextViewProtected.getInputType(); switch (value) { case true: inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE; inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; inputType = inputType & ~android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; break; case false: inputType = inputType & ~android.text.InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE; inputType = inputType & ~android.text.InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; inputType = inputType | android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; break; default: // We can't do anything. break; } this._setInputType(inputType); } [hintProperty.getDefault]() { return this.nativeTextViewProtected.getHint(); } [hintProperty.setNative](value) { const text = value === null || value === undefined ? null : value.toString(); this.nativeTextViewProtected.setHint(text); } [placeholderColorProperty.getDefault]() { return this.nativeTextViewProtected.getHintTextColors(); } [placeholderColorProperty.setNative](value) { const color = value instanceof Color ? value.android : value; this.nativeTextViewProtected.setHintTextColor(color); } [textTransformProperty.setNative](value) { // } [maxLengthProperty.setNative](value) { if (value === Number.POSITIVE_INFINITY) { this.nativeTextViewProtected.setFilters([]); } else { const lengthFilter = new android.text.InputFilter.LengthFilter(value); const filters = this.nativeTextViewProtected.getFilters(); const newFilters = []; // retain existing filters for (let i = 0; i < filters.length; i++) { const filter = filters[i]; if (!(filter instanceof android.text.InputFilter.LengthFilter)) { newFilters.push(filter); } } newFilters.push(lengthFilter); this.nativeTextViewProtected.setFilters(newFilters); } } setSelection(start, stop) { const view = this.nativeTextViewProtected; if (view) { if (stop !== undefined) { view.setSelection(start, stop); } else { view.setSelection(start); } } } beforeTextChanged(text, start, count, after) { // called by android.text.TextWatcher } onTextChanged(text, start, before, count) { // called by android.text.TextWatcher if (this.valueFormatter) { this.text = this.valueFormatter(text.toString()); this.android.setSelection((this.text || '').length); } // const owner = this.owner; // let selectionStart = owner.android.getSelectionStart(); // owner.android.removeTextChangedListener(owner._editTextListeners); // owner.android.addTextChangedListener(owner._editTextListeners); // owner.android.setSelection(selectionStart); } afterTextChanged(editable) { // called by android.text.TextWatcher if (this._changeFromCode) { return; } switch (this.updateTextTrigger) { case 'focusLost': this._dirtyTextAccumulator = editable.toString(); break; case 'textChanged': textProperty.nativeValueChange(this, editable.toString()); break; default: throw new Error('Invalid updateTextTrigger: ' + this.updateTextTrigger); } } onFocusChange(view, hasFocus) { if (hasFocus) { clearDismissTimer(); this.notify({ eventName: EditableTextBase.focusEvent }); } else { if (this._dirtyTextAccumulator || this._dirtyTextAccumulator === '') { textProperty.nativeValueChange(this, this._dirtyTextAccumulator); this._dirtyTextAccumulator = undefined; } this.notify({ eventName: EditableTextBase.blurEvent }); dismissSoftInput(this); } } onEditorAction(textView, actionId, event) { if (actionId === android.view.inputmethod.EditorInfo.IME_ACTION_DONE || actionId === android.view.inputmethod.EditorInfo.IME_ACTION_UNSPECIFIED || (event && event.getKeyCode() === android.view.KeyEvent.KEYCODE_ENTER)) { // If it is TextField, close the keyboard. If it is TextView, do not close it since the TextView is multiline // https://github.com/NativeScript/NativeScript/issues/3111 if (textView.getMaxLines() === 1) { this.dismissSoftInput(); } this._onReturnPress(); } else if (actionId === android.view.inputmethod.EditorInfo.IME_ACTION_NEXT || actionId === android.view.inputmethod.EditorInfo.IME_ACTION_PREVIOUS) { // do not close keyboard for ACTION_NEXT or ACTION_PREVIOUS this._onReturnPress(); } return false; } } //# sourceMappingURL=index.android.js.map