From 18fe9392d69b02179dbc169b3d845f745fc17155 Mon Sep 17 00:00:00 2001 From: Hristo Hristov Date: Thu, 11 Jan 2018 13:25:03 +0200 Subject: [PATCH] Fix memory leak in edit-text.android Fix fromObjectRecursive to doesn't override source object --- tests/app/data/observable-tests.ts | 7 +++ tests/app/testRunner.ts | 10 +-- .../data/observable/observable.ts | 16 +++-- .../ui/core/view-base/view-base.ts | 2 +- .../editable-text-base.android.ts | 63 ++++++++++++------- 5 files changed, 62 insertions(+), 36 deletions(-) diff --git a/tests/app/data/observable-tests.ts b/tests/app/data/observable-tests.ts index 94227010a..17e56b40b 100644 --- a/tests/app/data/observable-tests.ts +++ b/tests/app/data/observable-tests.ts @@ -558,4 +558,11 @@ export function test_get_set_on_observables_fromObject_with_property_in_json() { const value2 = (vm).p; TKUnit.assertEqual(value1, array); TKUnit.assertEqual(value2, array); +} + +export function test_fromObjectRecursive_does_not_override_source_object_property() { + const myObj = {}; + const source = { name: "a", value: myObj }; + const observable = fromObjectRecursive(source); + TKUnit.assertEqual(source.value, myObj); } \ No newline at end of file diff --git a/tests/app/testRunner.ts b/tests/app/testRunner.ts index f869ee90b..a0947ed32 100644 --- a/tests/app/testRunner.ts +++ b/tests/app/testRunner.ts @@ -348,11 +348,11 @@ function showReportPage(finalMessage: string) { page.content = stack; messageContainer.focus(); page.style.fontSize = 11; - if (page.android) { - setTimeout(() => { - messageContainer.dismissSoftInput(); - (messageContainer.nativeViewProtected).scrollTo(0, 0); - }, 500); + if (platform.isAndroid) { + page.on('navigatedTo', () => { + messageContainer.focus(); + setTimeout(() => messageContainer.dismissSoftInput()); + }); } return page; diff --git a/tns-core-modules/data/observable/observable.ts b/tns-core-modules/data/observable/observable.ts index 87fbc1d29..b18cec7af 100644 --- a/tns-core-modules/data/observable/observable.ts +++ b/tns-core-modules/data/observable/observable.ts @@ -188,7 +188,7 @@ class ObservableFromObject extends Observable { public get(name: string): any { return this._map[name]; } - + public set(name: string, value: any) { const currentValue = this._map[name]; if (currentValue === value) { @@ -218,13 +218,17 @@ function addPropertiesFromObject(observable: ObservableFromObject, source: any, let isRecursive = recursive; for (let prop in source) { if (source.hasOwnProperty(prop)) { - if (isRecursive) { - if (!Array.isArray(source[prop]) && source[prop] && typeof source[prop] === 'object' && !(source[prop] instanceof Observable)) { - source[prop] = fromObjectRecursive(source[prop]); - } + let value = source[prop]; + if (isRecursive + && !Array.isArray(value) + && value + && typeof value === 'object' + && !(value instanceof Observable)) { + value = fromObjectRecursive(value); } + defineNewProperty(observable, prop); - observable.set(prop, source[prop]); + observable.set(prop, value); } } } diff --git a/tns-core-modules/ui/core/view-base/view-base.ts b/tns-core-modules/ui/core/view-base/view-base.ts index 3bae66d75..11e940d3e 100644 --- a/tns-core-modules/ui/core/view-base/view-base.ts +++ b/tns-core-modules/ui/core/view-base/view-base.ts @@ -251,10 +251,10 @@ export abstract class ViewBase extends Observable implements ViewBaseDefinition this._style = new Style(this); } + // Used in Angular. get parentNode() { return this._templateParent || this.parent; } - set parentNode(node: ViewBase) { this._templateParent = node; } diff --git a/tns-core-modules/ui/editable-text-base/editable-text-base.android.ts b/tns-core-modules/ui/editable-text-base/editable-text-base.android.ts index fa86c431e..a3fe358cb 100644 --- a/tns-core-modules/ui/editable-text-base/editable-text-base.android.ts +++ b/tns-core-modules/ui/editable-text-base/editable-text-base.android.ts @@ -10,7 +10,8 @@ import { ad } from "../../utils/utils"; export * from "./editable-text-base-common"; //https://github.com/NativeScript/NativeScript/issues/2942 -let dismissKeyboardTimeoutId: any; +export let dismissKeyboardTimeoutId: NodeJS.Timer; +export let dismissKeyboardOwner: WeakRef; interface EditTextListeners extends android.text.TextWatcher, android.view.View.OnFocusChangeListener, android.widget.TextView.OnEditorActionListener { } @@ -22,6 +23,30 @@ interface EditTextListenersClass { let EditTextListeners: EditTextListenersClass; +function clearDismissTimer(): void { + dismissKeyboardOwner = null; + if (dismissKeyboardTimeoutId) { + clearTimeout(dismissKeyboardTimeoutId); + dismissKeyboardTimeoutId = null; + } +} + +function dismissSoftInput(owner: EditableTextBase): void { + clearDismissTimer(); + if (!dismissKeyboardTimeoutId) { + dismissKeyboardTimeoutId = setTimeout(() => { + const owner = dismissKeyboardOwner && dismissKeyboardOwner.get(); + const activity = (owner && owner._context) as android.app.Activity; + const nativeView = owner && owner.nativeViewProtected; + dismissKeyboardTimeoutId = null; + dismissKeyboardOwner = null; + const focused = activity && activity.getCurrentFocus(); + if (!focused || !(focused instanceof android.widget.EditText)) { + ad.dismissSoftInput(nativeView); + } + }, 10); + } +} function initializeEditTextListeners(): void { if (EditTextListeners) { return; @@ -71,7 +96,7 @@ function initializeEditTextListeners(): void { } if (hasFocus) { - owner.clearDismissTimer(); + clearDismissTimer(); owner.notify({ eventName: EditableTextBase.focusEvent, object: owner }); } else { if (owner._dirtyTextAccumulator || owner._dirtyTextAccumulator === "") { @@ -80,7 +105,7 @@ function initializeEditTextListeners(): void { } owner.notify({ eventName: EditableTextBase.blurEvent, object: owner }); - owner.dismissSoftInput(); + dismissSoftInput(owner); } } @@ -129,7 +154,6 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { private _inputType: number; public _changeFromCode: boolean; - public _dismissId: NodeJS.Timer; public abstract _configureEditText(editText: android.widget.EditText): void; @@ -168,35 +192,26 @@ export abstract class EditableTextBase extends EditableTextBaseCommon { this.nativeViewProtected.setInputType(this._inputType); } + public onUnloaded() { + this.dismissSoftInput(); + super.onUnloaded(); + } + public dismissSoftInput() { const nativeView = this.nativeViewProtected; if (!nativeView) { return; } - const activity = this._context as android.app.Activity; - if (!this._dismissId) { - this._dismissId = setTimeout(() => { - this._dismissId = null; - const focused = activity.getCurrentFocus(); - if (!focused - || focused === nativeView - || !(focused instanceof android.widget.EditText)) { - ad.dismissSoftInput(nativeView); - } - }, 100); - } - } - - public clearDismissTimer(): void { - if (this._dismissId) { - clearTimeout(this._dismissId); - this._dismissId = null; - } + ad.dismissSoftInput(nativeView); } public focus(): boolean { - this.clearDismissTimer(); + const nativeView = this.nativeViewProtected; + if (!nativeView) { + return; + } + const result = super.focus(); if (result) { ad.showSoftInput(this.nativeViewProtected);