diff --git a/tns-core-modules/ui/action-bar/action-bar.android.ts b/tns-core-modules/ui/action-bar/action-bar.android.ts index 73ff6c0dc..59b397ccf 100644 --- a/tns-core-modules/ui/action-bar/action-bar.android.ts +++ b/tns-core-modules/ui/action-bar/action-bar.android.ts @@ -124,11 +124,15 @@ export class ActionBar extends common.ActionBar { public _createUI() { this._toolbar = new android.support.v7.widget.Toolbar(this._context); - var owner = this; + let ownerRef = new WeakRef(this); this._toolbar.setOnMenuItemClickListener(new android.support.v7.widget.Toolbar.OnMenuItemClickListener({ onMenuItemClick: function (item: android.view.IMenuItem): boolean { - var itemId = item.getItemId(); - return owner._onAndroidItemSelected(itemId); + let ownerValue = ownerRef.get(); + if (!ownerValue) { + return false; + } + let itemId = item.getItemId(); + return ownerValue._onAndroidItemSelected(itemId); } })); } diff --git a/tns-core-modules/ui/transition/transition.android.ts b/tns-core-modules/ui/transition/transition.android.ts index 4d9c07e24..f4eb66e19 100644 --- a/tns-core-modules/ui/transition/transition.android.ts +++ b/tns-core-modules/ui/transition/transition.android.ts @@ -7,6 +7,27 @@ import * as animationModule from "ui/animation"; import lazy from "utils/lazy"; import trace = require("trace"); +let idleGCHandler; +let scheduledGC = false; +function scheduleGCOnIdle() { + if (!idleGCHandler) { + idleGCHandler = new android.os.MessageQueue.IdleHandler({ + queueIdle: function () { + gc(); + scheduledGC = false; + return false; + } + }); + } + + if (!scheduledGC) { + android.os.Looper.myQueue().addIdleHandler(idleGCHandler); + scheduledGC = true; + } + + return idleGCHandler; +} + let slideTransition: any; function ensureSlideTransition() { if (!slideTransition) { @@ -41,7 +62,9 @@ interface CompleteOptions { interface ExpandedFragment { enterPopExitTransition: definitionTransition; + enterPopExitTransitionListener: { remove(); } exitPopEnterTransition: definitionTransition; + exitPopEnterTransitionListener: { remove(); } completePageAdditionWhenTransitionEnds: CompleteOptions; completePageRemovalWhenTransitionEnds: CompleteOptions; exitHack: boolean; @@ -66,6 +89,9 @@ export function _clearBackwardTransitions(fragment: any): void { if (trace.enabled) { trace.write(`Cleared enterPopExitTransition ${expandedFragment.enterPopExitTransition} for ${fragment}`, trace.categories.Transition); } + if (expandedFragment.enterPopExitTransitionListener) { + expandedFragment.enterPopExitTransitionListener.remove(); + } expandedFragment.enterPopExitTransition = undefined; } @@ -75,6 +101,9 @@ export function _clearBackwardTransitions(fragment: any): void { if (trace.enabled) { trace.write(`Cleared Enter ${enterTransition.getClass().getSimpleName() } transition for ${fragment}`, trace.categories.Transition); } + if (enterTransition.transitionListener) { + enterTransition.transitionListener.remove(); + } (fragment).setEnterTransition(null); } let returnTransition = (fragment).getReturnTransition(); @@ -82,6 +111,9 @@ export function _clearBackwardTransitions(fragment: any): void { if (trace.enabled) { trace.write(`Cleared Pop Exit ${returnTransition.getClass().getSimpleName() } transition for ${fragment}`, trace.categories.Transition); } + if (returnTransition.transitionListener) { + returnTransition.transitionListener.remove(); + } (fragment).setReturnTransition(null); } } @@ -93,6 +125,9 @@ export function _clearForwardTransitions(fragment: any): void { if (trace.enabled) { trace.write(`Cleared exitPopEnterTransition ${expandedFragment.exitPopEnterTransition} for ${fragment}`, trace.categories.Transition); } + if (expandedFragment.exitPopEnterTransitionListener) { + expandedFragment.exitPopEnterTransitionListener.remove(); + } expandedFragment.exitPopEnterTransition = undefined; } @@ -102,6 +137,9 @@ export function _clearForwardTransitions(fragment: any): void { if (trace.enabled) { trace.write(`Cleared Exit ${exitTransition.getClass().getSimpleName() } transition for ${fragment}`, trace.categories.Transition); } + if (exitTransition.transitionListener) { + exitTransition.transitionListener.remove(); + } (fragment).setExitTransition(null);//exit } let reenterTransition = (fragment).getReenterTransition(); @@ -109,6 +147,9 @@ export function _clearForwardTransitions(fragment: any): void { if (trace.enabled) { trace.write(`Cleared Pop Enter ${reenterTransition.getClass().getSimpleName() } transition for ${fragment}`, trace.categories.Transition); } + if (reenterTransition.transitionListener) { + reenterTransition.transitionListener.remove(); + } (fragment).setReenterTransition(null);//popEnter } } @@ -402,6 +443,8 @@ function _completePageRemoval(fragment: any, isBack: boolean) { } entry.isNavigation = undefined; + + scheduleGCOnIdle(); } export function _removePageNativeViewFromAndroidParent(page: Page): void { @@ -422,56 +465,81 @@ function _toShortString(nativeTransition: any): string { } function _addNativeTransitionListener(fragment: any, nativeTransition: any/*android.transition.Transition*/) { - let expandedFragmentRef = new WeakRef(fragment); - let nativeTransitionRef = new WeakRef(nativeTransition); let transitionListener = new (android).transition.Transition.TransitionListener({ onTransitionCancel: function (transition: any): void { - let expandedFragment = expandedFragmentRef.get(); + let expandedFragment = this.fragment; if (!expandedFragment) { return; } if (trace.enabled) { - trace.write(`CANCEL ${_toShortString(nativeTransitionRef.get())} transition for ${fragment}`, trace.categories.Transition); + trace.write(`CANCEL ${_toShortString(transition)} transition for ${expandedFragment}`, trace.categories.Transition); } if ( expandedFragment.completePageRemovalWhenTransitionEnds) { - _completePageRemoval(fragment, expandedFragment.completePageRemovalWhenTransitionEnds.isBack); + _completePageRemoval(expandedFragment, expandedFragment.completePageRemovalWhenTransitionEnds.isBack); } if (expandedFragment.completePageAdditionWhenTransitionEnds) { - _completePageAddition(fragment, expandedFragment.completePageAdditionWhenTransitionEnds.isBack); + _completePageAddition(expandedFragment, expandedFragment.completePageAdditionWhenTransitionEnds.isBack); } + this.checkedRemove(); }, onTransitionEnd: function (transition: any): void { - let expandedFragment = expandedFragmentRef.get(); + let expandedFragment = this.fragment; if (!expandedFragment) { return; } if (trace.enabled) { - trace.write(`END ${_toShortString(nativeTransitionRef.get())} transition for ${fragment}`, trace.categories.Transition); + trace.write(`END ${_toShortString(transition)} transition for ${expandedFragment}`, trace.categories.Transition); } if (expandedFragment.completePageRemovalWhenTransitionEnds) { - _completePageRemoval(fragment, expandedFragment.completePageRemovalWhenTransitionEnds.isBack); + _completePageRemoval(expandedFragment, expandedFragment.completePageRemovalWhenTransitionEnds.isBack); } if (expandedFragment.completePageAdditionWhenTransitionEnds) { - _completePageAddition(fragment, expandedFragment.completePageAdditionWhenTransitionEnds.isBack); + _completePageAddition(expandedFragment, expandedFragment.completePageAdditionWhenTransitionEnds.isBack); } + this.checkedRemove(); }, onTransitionPause: function (transition: any): void { + let expandedFragment = this.fragment; if (trace.enabled) { - trace.write(`PAUSE ${_toShortString(nativeTransitionRef.get())} transition for ${fragment}`, trace.categories.Transition); + trace.write(`PAUSE ${_toShortString(transition)} transition for ${expandedFragment}`, trace.categories.Transition); } }, onTransitionResume: function (transition: any): void { + let expandedFragment = this.fragment; if (trace.enabled) { - trace.write(`RESUME ${_toShortString(nativeTransitionRef.get())} transition for ${fragment}`, trace.categories.Transition); + trace.write(`RESUME ${_toShortString(transition)} transition for ${expandedFragment}`, trace.categories.Transition); } }, onTransitionStart: function (transition: any): void { + let expandedFragment = this.fragment; if (trace.enabled) { - trace.write(`START ${_toShortString(nativeTransitionRef.get())} transition for ${fragment}`, trace.categories.Transition); + trace.write(`START ${_toShortString(transition)} transition for ${expandedFragment}`, trace.categories.Transition); } } }); + transitionListener.fragment = fragment; + transitionListener.count = 2; + transitionListener.transition = nativeTransition; + transitionListener.listener = transitionListener; + transitionListener.checkedRemove = function() { + if (--this.count) { + return; + } + this.remove(); + }; + transitionListener.remove = function() { + if (!this.listener) { + return; + } + this.transition.removeListener(this.listener); + this.fragment = null; + this.listener = null; + this.transition.transitionListener = null; + this.transition = null; + }; + nativeTransition.addListener(transitionListener); + nativeTransition.transitionListener = transitionListener; } export function _onFragmentCreateAnimator(fragment: ExpandedFragment, nextAnim: number): android.animation.Animator { @@ -510,58 +578,96 @@ export function _onFragmentCreateAnimator(fragment: ExpandedFragment, nextAnim: animator = transition.createAndroidAnimator(transitionType); trace.write(`${transition}.createAndroidAnimator(${transitionType}): ${animator}`, trace.categories.Transition); - let transitionRef = new WeakRef(transition); - let fragmentRef = new WeakRef(fragment); - - let transitionListener = new android.animation.Animator.AnimatorListener({ + let transitionListener: any = new android.animation.Animator.AnimatorListener({ onAnimationStart: function (animator: android.animation.Animator): void { if (trace.enabled) { - trace.write(`START ${transitionType} ${transitionRef.get()} for ${fragmentRef.get()}`, trace.categories.Transition); + trace.write(`START ${transitionType} ${this.transition} for ${this.fragment}`, trace.categories.Transition); } }, onAnimationRepeat: function (animator: android.animation.Animator): void { if (trace.enabled) { - trace.write(`REPEAT ${transitionType} ${transitionRef.get()} for ${fragmentRef.get()}`, trace.categories.Transition); + trace.write(`REPEAT ${transitionType} ${this.transition} for ${this.fragment}`, trace.categories.Transition); } }, onAnimationEnd: function (animator: android.animation.Animator): void { - let fragmentValue = fragmentRef.get(); - if (!fragmentValue) { - return; - } - if (trace.enabled) { - trace.write(`END ${transitionType} ${transitionRef.get()} for ${fragmentValue}`, trace.categories.Transition); + trace.write(`END ${transitionType} ${this.transition} for ${this.fragment}`, trace.categories.Transition); } - if (fragmentValue.completePageRemovalWhenTransitionEnds) { - _completePageRemoval(fragmentValue, fragmentValue.completePageRemovalWhenTransitionEnds.isBack); + if (this.fragment.completePageRemovalWhenTransitionEnds) { + _completePageRemoval(this.fragment, this.fragment.completePageRemovalWhenTransitionEnds.isBack); } - if (fragmentValue.completePageAdditionWhenTransitionEnds) { - _completePageAddition(fragmentValue, fragmentValue.completePageAdditionWhenTransitionEnds.isBack); + if (this.fragment.completePageAdditionWhenTransitionEnds) { + _completePageAddition(this.fragment, this.fragment.completePageAdditionWhenTransitionEnds.isBack); } + + this.checkedRemove(); }, onAnimationCancel: function (animator: android.animation.Animator): void { - let fragmentValue = fragmentRef.get(); - if (!fragmentValue) { - return; - } - if (trace.enabled) { - trace.write(`CANCEL ${transitionType} ${transitionRef.get()} for ${fragmentValue}`, trace.categories.Transition); + trace.write(`CANCEL ${transitionType} ${this.transition} for ${this.fragment}`, trace.categories.Transition); } - if (fragmentValue.completePageRemovalWhenTransitionEnds) { - _completePageRemoval(fragmentValue, fragmentValue.completePageRemovalWhenTransitionEnds.isBack); + if (this.fragment.completePageRemovalWhenTransitionEnds) { + _completePageRemoval(this.fragment, this.fragment.completePageRemovalWhenTransitionEnds.isBack); } - if (fragmentValue.completePageAdditionWhenTransitionEnds) { - _completePageAddition(fragmentValue, fragmentValue.completePageAdditionWhenTransitionEnds.isBack); + if (this.fragment.completePageAdditionWhenTransitionEnds) { + _completePageAddition(this.fragment, this.fragment.completePageAdditionWhenTransitionEnds.isBack); } + + this.checkedRemove(); } }); + + transitionListener.fragment = fragment; + transitionListener.transitionType = transitionType; + transitionListener.count = 2; + transitionListener.listener = transitionListener; + transitionListener.animator = animator; + transitionListener.checkedRemove = function() { + if (--this.count) { + return; + } + this.remove(); + }; + transitionListener.remove = function() { + if (!this.listener) { + return; + } + this.animator.removeListener(this.listener); + switch(this.transitionType) { + case AndroidTransitionType.enter: + case AndroidTransitionType.popExit: + this.fragment.enterPopExitTransitionListener = null; + break; + case AndroidTransitionType.exit: + case AndroidTransitionType.popEnter: + this.fragment.exitPopEnterTransitionListener = null; + break; + } + this.transitionType = null; + this.fragment = null; + this.listener = null; + this.animator.transitionListener = null; + this.animator = null; + this.transitionType = null; + }; + + (animator).transitionListener = transitionListener; animator.addListener(transitionListener); + + switch (transitionType) { + case AndroidTransitionType.enter: + case AndroidTransitionType.popExit: + fragment.enterPopExitTransitionListener = transitionListener; + break; + case AndroidTransitionType.exit: + case AndroidTransitionType.popEnter: + fragment.exitPopEnterTransitionListener = transitionListener; + break; + } } if (transitionType && !animator) {