fix(android): memory leak with EditableTextBase (#10052)

This commit is contained in:
farfromrefuge
2022-11-24 04:54:34 +00:00
committed by GitHub
parent 75eefa669d
commit 501d310431

View File

@ -27,12 +27,11 @@ function clearDismissTimer(): void {
} }
} }
function dismissSoftInput(_owner: WeakRef<EditableTextBase>): void { function dismissSoftInput(view: EditableTextBase): void {
clearDismissTimer(); clearDismissTimer();
if (!dismissKeyboardTimeoutId) { if (!dismissKeyboardTimeoutId) {
dismissKeyboardTimeoutId = setTimeout(() => { dismissKeyboardTimeoutId = setTimeout(() => {
const owner = _owner && _owner.get(); const activity = view._context as androidx.appcompat.app.AppCompatActivity;
const activity = owner?._context as androidx.appcompat.app.AppCompatActivity;
dismissKeyboardTimeoutId = null; dismissKeyboardTimeoutId = null;
const focused = activity && activity.getCurrentFocus(); const focused = activity && activity.getCurrentFocus();
if (focused && !(focused instanceof android.widget.EditText)) { if (focused && !(focused instanceof android.widget.EditText)) {
@ -61,81 +60,23 @@ function initializeEditTextListeners(): void {
} }
public beforeTextChanged(text: string, start: number, count: number, after: number): void { public beforeTextChanged(text: string, start: number, count: number, after: number): void {
// this.owner?.get()?.beforeTextChanged(text, start, count, after);
} }
public onTextChanged(text: string, start: number, before: number, count: number): void { public onTextChanged(text: string, start: number, before: number, count: number): void {
// const owner = this.owner; this.owner?.get()?.onTextChanged(text, start, before, count);
// let selectionStart = owner.android.getSelectionStart();
// owner.android.removeTextChangedListener(owner._editTextListeners);
// owner.android.addTextChangedListener(owner._editTextListeners);
// owner.android.setSelection(selectionStart);
} }
public afterTextChanged(editable: android.text.Editable): void { public afterTextChanged(editable: android.text.Editable): void {
const owner = this.owner && this.owner.get(); this.owner?.get()?.afterTextChanged(editable);
if (!owner || owner._changeFromCode) {
return;
}
switch (owner.updateTextTrigger) {
case 'focusLost':
owner._dirtyTextAccumulator = editable.toString();
break;
case 'textChanged':
textProperty.nativeValueChange(owner, editable.toString());
break;
default:
throw new Error('Invalid updateTextTrigger: ' + owner.updateTextTrigger);
}
} }
public onFocusChange(view: android.view.View, hasFocus: boolean): void { public onFocusChange(view: android.view.View, hasFocus: boolean): void {
const owner = this.owner && this.owner.get(); this.owner?.get()?.onFocusChange(view, hasFocus);
if (!owner) {
return;
}
if (hasFocus) {
clearDismissTimer();
owner.notify({
eventName: EditableTextBase.focusEvent,
object: owner,
});
} else {
if (owner._dirtyTextAccumulator || owner._dirtyTextAccumulator === '') {
textProperty.nativeValueChange(owner, owner._dirtyTextAccumulator);
owner._dirtyTextAccumulator = undefined;
}
owner.notify({
eventName: EditableTextBase.blurEvent,
object: owner,
});
dismissSoftInput(this.owner);
}
} }
public onEditorAction(textView: android.widget.TextView, actionId: number, event: android.view.KeyEvent): boolean { public onEditorAction(textView: android.widget.TextView, actionId: number, event: android.view.KeyEvent): boolean {
const owner = this.owner && this.owner.get(); return this.owner?.get()?.onEditorAction(textView, actionId, event) || false;
if (!owner) {
return false;
}
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) {
owner.dismissSoftInput();
}
owner._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
owner._onReturnPress();
}
return false;
} }
} }
@ -178,7 +119,12 @@ export abstract class EditableTextBase extends EditableTextBaseCommon {
} }
public disposeNativeView(): void { public disposeNativeView(): void {
(<any>this.nativeTextViewProtected).listener.owner = null; const editText = this.nativeTextViewProtected;
editText.removeTextChangedListener((<any>editText).listener);
editText.setOnFocusChangeListener(null);
editText.setOnEditorActionListener(null);
(<any>editText).listener.owner = null;
(<any>editText).listener = null;
this._keyListenerCache = null; this._keyListenerCache = null;
super.disposeNativeView(); super.disposeNativeView();
} }
@ -531,4 +477,72 @@ export abstract class EditableTextBase extends EditableTextBaseCommon {
} }
} }
} }
public beforeTextChanged(text: string, start: number, count: number, after: number): void {
// called by android.text.TextWatcher
}
public onTextChanged(text: string, start: number, before: number, count: number): void {
// called by android.text.TextWatcher
// const owner = this.owner;
// let selectionStart = owner.android.getSelectionStart();
// owner.android.removeTextChangedListener(owner._editTextListeners);
// owner.android.addTextChangedListener(owner._editTextListeners);
// owner.android.setSelection(selectionStart);
}
public afterTextChanged(editable: android.text.Editable): void {
// 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);
}
}
public onFocusChange(view: android.view.View, hasFocus: boolean): void {
if (hasFocus) {
clearDismissTimer();
this.notify({
eventName: EditableTextBase.focusEvent,
object: this,
});
} else {
if (this._dirtyTextAccumulator || this._dirtyTextAccumulator === '') {
textProperty.nativeValueChange(this, this._dirtyTextAccumulator);
this._dirtyTextAccumulator = undefined;
}
this.notify({
eventName: EditableTextBase.blurEvent,
});
dismissSoftInput(this);
}
}
public onEditorAction(textView: android.widget.TextView, actionId: number, event: android.view.KeyEvent): boolean {
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;
}
} }