diff --git a/CrossPlatformModules.csproj b/CrossPlatformModules.csproj index f3bbc4871..7703aa599 100644 --- a/CrossPlatformModules.csproj +++ b/CrossPlatformModules.csproj @@ -145,6 +145,10 @@ + + + + Designer @@ -2116,6 +2120,7 @@ PreserveNewest + @@ -2213,7 +2218,7 @@ False - + \ No newline at end of file diff --git a/apps/App_Resources/Android/drawable-hdpi/icon.png b/apps/App_Resources/Android/drawable-hdpi/icon.png new file mode 100644 index 000000000..1034356e2 Binary files /dev/null and b/apps/App_Resources/Android/drawable-hdpi/icon.png differ diff --git a/apps/App_Resources/Android/drawable-ldpi/icon.png b/apps/App_Resources/Android/drawable-ldpi/icon.png new file mode 100644 index 000000000..ddfc17a71 Binary files /dev/null and b/apps/App_Resources/Android/drawable-ldpi/icon.png differ diff --git a/apps/App_Resources/Android/drawable-mdpi/icon.png b/apps/App_Resources/Android/drawable-mdpi/icon.png new file mode 100644 index 000000000..486e41091 Binary files /dev/null and b/apps/App_Resources/Android/drawable-mdpi/icon.png differ diff --git a/apps/App_Resources/Android/drawable-nodpi/splashscreen.9.png b/apps/App_Resources/Android/drawable-nodpi/splashscreen.9.png new file mode 100644 index 000000000..bd53be2ec Binary files /dev/null and b/apps/App_Resources/Android/drawable-nodpi/splashscreen.9.png differ diff --git a/apps/App_Resources/package.json b/apps/App_Resources/package.json new file mode 100644 index 000000000..08e44e117 --- /dev/null +++ b/apps/App_Resources/package.json @@ -0,0 +1,2 @@ +{ "name" : "app-resources", + "main" : "dummy.js" } diff --git a/apps/perf-tests/NavigationTest/app.ts b/apps/perf-tests/NavigationTest/app.ts index 80b471c3e..dff5826b6 100644 --- a/apps/perf-tests/NavigationTest/app.ts +++ b/apps/perf-tests/NavigationTest/app.ts @@ -1,11 +1,14 @@ import * as application from "application"; import {NavPage} from "../nav-page"; import * as trace from "trace"; +import { Frame } from "ui/frame"; + +Frame.defaultTransition = { name: "fade" }; + trace.enable(); trace.setCategories(trace.categories.concat( trace.categories.NativeLifecycle, trace.categories.Navigation, - //trace.categories.Animation, trace.categories.Transition )); diff --git a/apps/perf-tests/nav-page.ts b/apps/perf-tests/nav-page.ts index fa421434a..ccaf0fe25 100644 --- a/apps/perf-tests/nav-page.ts +++ b/apps/perf-tests/nav-page.ts @@ -1,7 +1,7 @@ import definition = require("controls-page"); import {View} from "ui/core/view"; import {Page} from "ui/page"; -import {topmost as topmostFrame, NavigationTransition} from "ui/frame"; +import {topmost as topmostFrame, NavigationTransition, Frame} from "ui/frame"; import {Orientation, AnimationCurve} from "ui/enums"; import {StackLayout} from "ui/layouts/stack-layout"; import {Button} from "ui/button"; @@ -10,6 +10,7 @@ import {TextField} from "ui/text-field"; import {Switch} from "ui/switch"; import {Slider} from "ui/slider"; import {Color} from "color"; +import {ScrollView} from "ui/scroll-view"; import * as platform from "platform"; var availableTransitions = ["default", "custom", "flip", "flipRight", "flipLeft", "slide", "slideLeft", "slideRight", "slideTop", "slideBottom", "fade"]; @@ -57,6 +58,10 @@ export class NavPage extends Page implements definition.ControlsPage { }); that.on(Page.navigatedToEvent, (args) => { console.log(`${args.object}.navigatedToEvent`); + (topmostFrame())._printFrameBackStack(); + if (topmostFrame().android) { + (topmostFrame())._printNativeBackStack(); + } }); this.id = "" + context.index; @@ -122,6 +127,14 @@ export class NavPage extends Page implements definition.ControlsPage { animatedSwitch.checked = context.animated; optionsLayout.addChild(animatedSwitch); + var globalTransitionButton = new Button(); + globalTransitionButton.text = "global: " + (Frame.defaultTransition ? Frame.defaultTransition.name : "none"); + globalTransitionButton.on("tap", (args) => { + Frame.defaultTransition = Frame.defaultTransition ? null : { name: "fade" }; + (args.object).text = "global: " + (Frame.defaultTransition ? Frame.defaultTransition.name : "none"); + }); + optionsLayout.addChild(globalTransitionButton); + var transitionButton = new Button(); transitionButton.text = availableTransitions[context.transition]; transitionButton.on("tap", () => { @@ -202,6 +215,8 @@ export class NavPage extends Page implements definition.ControlsPage { }); stackLayout.addChild(forwardButton); - this.content = stackLayout; + var scrollView = new ScrollView(); + scrollView.content = stackLayout; + this.content = scrollView; } } diff --git a/apps/tests/navigation/navigation-tests.ts b/apps/tests/navigation/navigation-tests.ts index 659a3bce2..ee1a7bff1 100644 --- a/apps/tests/navigation/navigation-tests.ts +++ b/apps/tests/navigation/navigation-tests.ts @@ -1,6 +1,6 @@ import * as TKUnit from "../TKUnit"; import {Page} from "ui/page"; -import {topmost as topmostFrame} from "ui/frame"; +import {topmost as topmostFrame, NavigationTransition} from "ui/frame"; import {Color} from "color"; // Creates a random colorful page full of meaningless stuff. @@ -10,32 +10,41 @@ var pageFactory = function(): Page { return page; }; -export var test_backstackVisible = function () { - var mainTestPage = topmostFrame().currentPage; - topmostFrame().navigate({ create: pageFactory }); - TKUnit.waitUntilReady(() => { return topmostFrame().currentPage !== mainTestPage; }); +function _test_backstackVisible(transition?: NavigationTransition) { + let topmost = topmostFrame(); + let mainTestPage = topmost.currentPage; + topmost.navigate({ create: pageFactory, transition: transition }); + TKUnit.waitUntilReady(() => { return topmost.currentPage !== mainTestPage; }); // page1 should not be added to the backstack - var page0 = topmostFrame().currentPage; - topmostFrame().navigate({ create: pageFactory, backstackVisible: false }); - TKUnit.waitUntilReady(() => { return topmostFrame().currentPage !== page0; }); + let page0 = topmost.currentPage; + topmost.navigate({ create: pageFactory, backstackVisible: false, transition: transition }); + TKUnit.waitUntilReady(() => { return topmost.currentPage !== page0; }); - var page1 = topmostFrame().currentPage; - topmostFrame().navigate({ create: pageFactory }); - TKUnit.waitUntilReady(() => { return topmostFrame().currentPage !== page1; }); + let page1 = topmost.currentPage; + topmost.navigate({ create: pageFactory, transition: transition }); + TKUnit.waitUntilReady(() => { return topmost.currentPage !== page1; }); - var page2 = topmostFrame().currentPage; - topmostFrame().goBack(); - TKUnit.waitUntilReady(() => { return topmostFrame().currentPage !== page2; }); + let page2 = topmost.currentPage; + topmost.goBack(); + TKUnit.waitUntilReady(() => { return topmost.currentPage !== page2; }); // From page2 we have to go directly to page0, skipping page1. - TKUnit.assert(topmostFrame().currentPage === page0, "Page 1 should be skipped when going back."); + TKUnit.assert(topmost.currentPage === page0, "Page 1 should be skipped when going back."); - topmostFrame().goBack(); - TKUnit.waitUntilReady(() => { return topmostFrame().currentPage === mainTestPage; }); + topmost.goBack(); + TKUnit.waitUntilReady(() => { return topmost.currentPage === mainTestPage; }); } -export var test_backToEntry = function () { +export var test_backstackVisible = function () { + _test_backstackVisible(); +} + +export var test_backstackVisible_WithTransition = function () { + _test_backstackVisible({name: "fade"}); +} + +function _test_backToEntry(transition?: NavigationTransition) { let page = (tag) => () => { var p = new Page(); p["tag"] = tag; @@ -44,7 +53,7 @@ export var test_backToEntry = function () { let topmost = topmostFrame(); let wait = tag => TKUnit.waitUntilReady(() => topmost.currentPage["tag"] === tag, 1); let navigate = tag => { - topmost.navigate({ create: page(tag) }); + topmost.navigate({ create: page(tag), transition: transition }); wait(tag) } let back = pages => { @@ -75,42 +84,93 @@ export var test_backToEntry = function () { back(1); } -// Clearing the history messes up the tests app. -export var test_ClearHistory = function () { - var mainTestPage = topmostFrame().currentPage; - var mainPageFactory = function (): Page { +export var test_backToEntry = function () { + _test_backToEntry(); +} + +export var test_backToEntry_WithTransition = function () { + _test_backToEntry({name: "flip"}); +} + +function _test_ClearHistory(transition?: NavigationTransition) { + let topmost = topmostFrame(); + let mainTestPage = topmost.currentPage; + let mainPageFactory = function (): Page { return mainTestPage; }; var currentPage: Page; - currentPage = topmostFrame().currentPage; - topmostFrame().navigate({ create: pageFactory, clearHistory: true }); - TKUnit.waitUntilReady(() => { return topmostFrame().currentPage !== currentPage; }); + currentPage = topmost.currentPage; + topmost.navigate({ create: pageFactory, clearHistory: true, transition: transition}); + TKUnit.waitUntilReady(() => { return topmost.currentPage !== currentPage; }); - currentPage = topmostFrame().currentPage; - topmostFrame().navigate({ create: pageFactory }); - TKUnit.waitUntilReady(() => { return topmostFrame().currentPage !== currentPage; }); + currentPage = topmost.currentPage; + topmost.navigate({ create: pageFactory, transition: transition }); + TKUnit.waitUntilReady(() => { return topmost.currentPage !== currentPage; }); - currentPage = topmostFrame().currentPage; - topmostFrame().navigate({ create: pageFactory }); - TKUnit.waitUntilReady(() => { return topmostFrame().currentPage !== currentPage; }); + currentPage = topmost.currentPage; + topmost.navigate({ create: pageFactory, transition: transition }); + TKUnit.waitUntilReady(() => { return topmost.currentPage !== currentPage; }); - currentPage = topmostFrame().currentPage; - topmostFrame().navigate({ create: pageFactory }); - TKUnit.waitUntilReady(() => { return topmostFrame().currentPage !== currentPage; }); + currentPage = topmost.currentPage; + topmost.navigate({ create: pageFactory, transition: transition }); + TKUnit.waitUntilReady(() => { return topmost.currentPage !== currentPage; }); - TKUnit.assert(topmostFrame().canGoBack(), "Frame should be able to go back."); - TKUnit.assert(topmostFrame().backStack.length === 3, "Back stack should have 3 entries."); + TKUnit.assert(topmost.canGoBack(), "Frame should be able to go back."); + TKUnit.assert(topmost.backStack.length === 3, "Back stack should have 3 entries."); // Navigate with clear history. - currentPage = topmostFrame().currentPage; - topmostFrame().navigate({ create: pageFactory, clearHistory: true }); - TKUnit.waitUntilReady(() => { return topmostFrame().currentPage !== currentPage; }); + currentPage = topmost.currentPage; + topmost.navigate({ create: pageFactory, clearHistory: true, transition: transition }); + TKUnit.waitUntilReady(() => { return topmost.currentPage !== currentPage; }); - TKUnit.assert(!topmostFrame().canGoBack(), "Frame should NOT be able to go back."); - TKUnit.assert(topmostFrame().backStack.length === 0, "Back stack should have 0 entries."); + TKUnit.assert(!topmost.canGoBack(), "Frame should NOT be able to go back."); + TKUnit.assert(topmost.backStack.length === 0, "Back stack should have 0 entries."); - topmostFrame().navigate({ create: mainPageFactory }); - TKUnit.waitUntilReady(() => { return topmostFrame().currentPage === mainTestPage; }); + topmost.navigate({ create: mainPageFactory, transition: transition }); + TKUnit.waitUntilReady(() => { return topmost.currentPage === mainTestPage; }); +} + +// Clearing the history messes up the tests app. +export var test_ClearHistory = function () { + _test_ClearHistory(); +} + +export var test_ClearHistory_WithTransition = function () { + _test_ClearHistory({ name: "slide" }); +} + +// Test case for https://github.com/NativeScript/NativeScript/issues/1948 +export var test_ClearHistoryWithTransitionDoesNotBreakNavigation = function () { + let topmost = topmostFrame(); + + let mainTestPage = topmost.currentPage; + let mainPageFactory = function (): Page { + return mainTestPage; + }; + + // Go to details-page + topmost.navigate({ create: pageFactory, clearHistory: false }); + TKUnit.waitUntilReady(() => { return topmost.currentPage !== mainTestPage; }); + + // Go back to main-page with clearHistory + var detailsPage: Page; + detailsPage = topmost.currentPage; + topmost.transition = { name: "fade" }; + topmost.navigate({ create: mainPageFactory, clearHistory: true }); + TKUnit.waitUntilReady(() => { return topmost.currentPage === mainTestPage; }); + + // Go to details-page AGAIN + topmost.navigate({ create: pageFactory, clearHistory: false }); + TKUnit.waitUntilReady(() => { return topmost.currentPage !== mainTestPage; }); + + // Go back to main-page with clearHistory + detailsPage = topmost.currentPage; + topmost.transition = { name: "fade" }; + topmost.navigate({ create: mainPageFactory, clearHistory: true }); + TKUnit.waitUntilReady(() => { return topmost.currentPage === mainTestPage; }); + + // Clean up + topmost.transition = undefined; } diff --git a/ui/frame/frame-common.ts b/ui/frame/frame-common.ts index 0354cab0d..0070b5646 100644 --- a/ui/frame/frame-common.ts +++ b/ui/frame/frame-common.ts @@ -431,6 +431,17 @@ export class Frame extends CustomLayoutView implements definition.Frame { public _removeViewFromNativeVisualTree(child: View): void { child._isAddedToNativeVisualTree = false; } + + public _printFrameBackStack() { + var length = this.backStack.length; + var i = length - 1; + console.log("---------------------------"); + console.log("Frame Back Stack (" + length + ")"); + while (i >= 0) { + var backstackEntry = this.backStack[i--]; + console.log("[ " + backstackEntry.resolvedPage.id + " ]"); + } + } } var _topmost = function (): Frame { diff --git a/ui/frame/frame.android.ts b/ui/frame/frame.android.ts index 5393c585b..88c77fe08 100644 --- a/ui/frame/frame.android.ts +++ b/ui/frame/frame.android.ts @@ -65,8 +65,9 @@ function onFragmentHidden(fragment: FragmentClass) { trace.write(`HIDDEN ${fragment.getTag()}`, trace.categories.NativeLifecycle); if (fragment[CLEARING_HISTORY]) { - trace.write(`${fragment.getTag()} has been hidden, but we are currently clearing history. Returning.`, trace.categories.NativeLifecycle); - return null; + trace.write(`${fragment.getTag()} has been hidden, but we are currently clearing history. Clearing any existing transitions.`, trace.categories.NativeLifecycle); + transitionModule._clearBackwardTransitions(fragment); + transitionModule._clearForwardTransitions(fragment); } var isBack = fragment.entry[IS_BACK]; @@ -188,10 +189,10 @@ export class Frame extends frameCommon.Frame { if (currentFragment) { // There might be transitions left over from previous forward navigations from the current page. transitionModule._clearForwardTransitions(currentFragment); - } - if (animated && navigationTransition) { - transitionModule._setAndroidFragmentTransitions(navigationTransition, currentFragment, newFragment, fragmentTransaction); + if (animated && navigationTransition) { + transitionModule._setAndroidFragmentTransitions(navigationTransition, currentFragment, newFragment, fragmentTransaction); + } } newFragment.frame = this; @@ -328,17 +329,6 @@ export class Frame extends frameCommon.Frame { } } - public _printFrameBackStack() { - var length = this.backStack.length; - var i = length - 1; - console.log("---------------------------"); - console.log("Frame Back Stack (" + length + ")"); - while (i >= 0) { - var backstackEntry = this.backStack[i--]; - console.log("[ " + backstackEntry.resolvedPage.id + " ]"); - } - } - public _getNavBarVisible(page: pages.Page): boolean { if (types.isDefined(page.actionBarHidden)) { return !page.actionBarHidden; diff --git a/ui/transition/transition.android.ts b/ui/transition/transition.android.ts index 20829e2b6..010bb7b48 100644 --- a/ui/transition/transition.android.ts +++ b/ui/transition/transition.android.ts @@ -25,6 +25,26 @@ export module AndroidTransitionType { export var popExit: string = "popExit"; } +export function _clearBackwardTransitions(fragment: any): void { + if (fragment[ENTER_POPEXIT_TRANSITION]) { + trace.write(`Cleared ENTER_POPEXIT_TRANSITION ${fragment[ENTER_POPEXIT_TRANSITION]} for ${fragment.getTag()}`, trace.categories.Transition); + fragment[ENTER_POPEXIT_TRANSITION] = undefined; + } + + if (_sdkVersion >= 21) { + var enterTransition = (fragment).getEnterTransition(); + if (enterTransition) { + trace.write(`Cleared Enter ${enterTransition.getClass().getSimpleName()} transition for ${fragment.getTag()}`, trace.categories.Transition); + (fragment).setEnterTransition(null); + } + var returnTransition = (fragment).getReturnTransition(); + if (returnTransition) { + trace.write(`Cleared Pop Exit ${returnTransition.getClass().getSimpleName()} transition for ${fragment.getTag()}`, trace.categories.Transition); + (fragment).setReturnTransition(null); + } + } +} + export function _clearForwardTransitions(fragment: any): void { if (fragment[EXIT_POPENTER_TRANSITION]) { trace.write(`Cleared EXIT_POPENTER_TRANSITION ${fragment[EXIT_POPENTER_TRANSITION]} for ${fragment.getTag()}`, trace.categories.Transition); @@ -265,9 +285,11 @@ function _completePageRemoval(fragment: android.app.Fragment, force: boolean = f if (page.frame) { frame._removeView(page); page.onNavigatedFrom(isBack); + trace.write(`REMOVAL of ${page} completed`, trace.categories.Transition); + } + else { + trace.write(`REMOVAL of ${page} has already been done`, trace.categories.Transition); } - - trace.write(`REMOVAL of ${page} completed`, trace.categories.Transition); } } diff --git a/ui/transition/transition.d.ts b/ui/transition/transition.d.ts index aba7adb17..1c5f38bde 100644 --- a/ui/transition/transition.d.ts +++ b/ui/transition/transition.d.ts @@ -18,6 +18,7 @@ } //@private + export function _clearBackwardTransitions(fragment: any): void; export function _clearForwardTransitions(fragment: any): void; export function _setAndroidFragmentTransitions(navigationTransition: frame.NavigationTransition, currentFragment: any, newFragment: any, fragmentTransaction: any): void; export function _onFragmentCreateAnimator(fragment: any, nextAnim: number): any;