Merge remote-tracking branch 'origin/master' into release

This commit is contained in:
SvetoslavTsenov
2018-12-14 14:53:53 +02:00
19 changed files with 382 additions and 165 deletions

View File

@ -225,8 +225,10 @@ git checkout release
``` ```
2. Create a PR to cut the release: 2. Create a PR to cut the release:
``` ```
git checkout -b release-version export RELEASE_VERSION=version
git push --set-upstream origin release-version export BRANCH="release-${RELEASE_VERSION}"
git checkout -b ${BRANCH}
git push --set-upstream origin ${BRANCH}
``` ```
#### Merge master in release branch or cherry-pick commits. If the commits are in release branch **skip this step**. #### Merge master in release branch or cherry-pick commits. If the commits are in release branch **skip this step**.
``` ```
@ -266,9 +268,13 @@ git push
git tag release-version git tag release-version
git push --tags git push --tags
``` ```
9. Merge PR into release branch. 9. Create a pull request. Replace env variables ${RELEASE_VERSION} and ${BRANCH} with their values
```
curl -d '{"title": "release: cut the ${RELEASE_VERSION} release","body": "docs: update changelog","head": "${BRANCH}","base": "release"}' -X POST https://api.github.com/repos/NativeScript/NativeScript/pulls -H "Authorization: token ${GIT_TOKEN}"
```
10. Merge PR into release branch.
10. If all checks has passed publish package. 11. If all checks has passed publish package.
## Merge changes from release into master ## Merge changes from release into master
@ -283,8 +289,9 @@ git pull
``` ```
2. Create PR to merge changes back in master and preserve history: 2. Create PR to merge changes back in master and preserve history:
``` ```
git checkout -b merge-release-in-master export MERGE_BRANCH='merge-release-in-master'
git push --set-upstream origin merge-release-in-master git checkout -b ${MERGE_BRANCH}
git push --set-upstream origin ${MERGE_BRANCH}
git merge origin/master git merge origin/master
``` ```
3. Resolve conflicts. Choose to keep the version of master branch. If it is needed to revert versions of modules, see at the bottom. 3. Resolve conflicts. Choose to keep the version of master branch. If it is needed to revert versions of modules, see at the bottom.
@ -299,6 +306,11 @@ git commit
git push git push
``` ```
6. Create pull request. Replace replace env ${MERGE_BRANCH} with its value
```
curl -d '{"title": "chore: merge release in master","body": "chore: merge release in master","head": "merge-release-in-master","base": "master"}' -X POST https://api.github.com/repos/NativeScript/NativeScript/pulls -H "Authorization: token ${GIT_TOKEN}"
```
**If needed, revert version of modules and platform declarations to take the one from master:** **If needed, revert version of modules and platform declarations to take the one from master:**
``` ```
git checkout origin/master tns-platform-declarations/package.json tns-core-modules/package.json git checkout origin/master tns-platform-declarations/package.json tns-core-modules/package.json

View File

@ -1,46 +1,61 @@
import { Page } from "tns-core-modules/ui/page"; import { Page } from "tns-core-modules/ui/page";
import { Label } from "tns-core-modules/ui/label"; import { Label } from "tns-core-modules/ui/label";
function createCloseCallback(label: Label, context?: string): (username: string, password: string) => void {
return function (username: string, password: string) {
let result = username + "/" + password;
result = context ? context + "/" + result : result;
console.log(result);
label.text = result;
}
}
function openModal(page: Page, label: Label, context: string) {
page.showModal("ui-tests-app/modal-view/login-page", {
context,
closeCallback: createCloseCallback(label, context),
fullscreen: false,
});
}
export function onTap(args) { export function onTap(args) {
const page = <Page>args.object.page; const page = <Page>args.object.page;
const label = page.getViewById<Label>("label"); const label = page.getViewById<Label>("label");
var fullscreen = (<any>args.object).text.indexOf("(full-screen)") !== -1; var fullscreen = (<any>args.object).text.indexOf("(full-screen)") !== -1;
page.showModal("ui-tests-app/modal-view/login-page", "context", function (username: string, password: string) {
console.log(username + "/" + password); page.showModal("ui-tests-app/modal-view/login-page", {
label.text = username + "/" + password; context: "context",
}, fullscreen); closeCallback: createCloseCallback(label),
fullscreen
});
} }
export function onTapStretched(args) { export function onTapStretched(args) {
const page = <Page>args.object.page; const page = <Page>args.object.page;
const label = page.getViewById<Label>("label"); const label = page.getViewById<Label>("label");
var fullscreen = false;
var stretched = true;
page.showModal("ui-tests-app/modal-view/login-page", "context", function (username: string, password: string) { page.showModal("ui-tests-app/modal-view/login-page", {
console.log(username + "/" + password); context: "context",
label.text = username + "/" + password; closeCallback: createCloseCallback(label),
}, fullscreen, false, stretched); fullscreen: false,
} animated: false,
stretched: true
function openModal(page: Page, label: Label, context: string) { });
page.showModal("ui-tests-app/modal-view/login-page", context, function (username: string, password: string) {
const result = context + "/" + username + "/" + password;
console.log(result);
label.text = result;
}, false);
} }
export function onTapSecondModalInCB(args) { export function onTapSecondModalInCB(args) {
const page = <Page>args.object.page; const page = <Page>args.object.page;
const label = page.getViewById<Label>("label"); const label = page.getViewById<Label>("label");
page.showModal("ui-tests-app/modal-view/login-page", "First", function (username: string, password: string) { page.showModal("ui-tests-app/modal-view/login-page", {
context: "First",
closeCallback: (username: string, password: string) => {
const result = "First/" + username + "/" + password; const result = "First/" + username + "/" + password;
console.log(result); console.log(result);
label.text = result; label.text = result;
// Open second modal in the close callback of the first one. // Open second modal in the close callback of the first one.
openModal(page, label, "Second"); openModal(page, label, "Second");
}
}); });
} }

View File

@ -1,4 +1,4 @@
// >> article-require-page-module // >> article-require-page-module
import { Page, ShownModallyData, NavigatedData } from "tns-core-modules/ui/page"; import { Page, ShownModallyData, NavigatedData } from "tns-core-modules/ui/page";
// FrameModule is needed in order to have an option to navigate to the new page. // FrameModule is needed in order to have an option to navigate to the new page.
import { topmost, NavigationEntry } from "tns-core-modules/ui/frame"; import { topmost, NavigationEntry } from "tns-core-modules/ui/frame";
@ -422,8 +422,7 @@ export function test_WhenPageIsNavigatedToFrameCurrentPageIsNowTheSameAsThePage(
} }
export function test_WhenInnerViewCallsCloseModal_WithArguments_ShouldPassResult() { export function test_WhenInnerViewCallsCloseModal_WithArguments_ShouldPassResult() {
_test_WhenInnerViewCallsCloseModal((args: ShownModallyData) => _test_WhenInnerViewCallsCloseModal((args: ShownModallyData) => {
{
const page = <Page>args.object; const page = <Page>args.object;
const button = <Button>page.content; const button = <Button>page.content;
return button.closeModal.bind(button); return button.closeModal.bind(button);
@ -431,8 +430,7 @@ export function test_WhenInnerViewCallsCloseModal_WithArguments_ShouldPassResult
} }
export function test_WhenInnerViewCallsCloseModal_WithoutArguments_ShouldWork() { export function test_WhenInnerViewCallsCloseModal_WithoutArguments_ShouldWork() {
_test_WhenInnerViewCallsCloseModal((args: ShownModallyData) => _test_WhenInnerViewCallsCloseModal((args: ShownModallyData) => {
{
const page = <Page>args.object; const page = <Page>args.object;
const button = <Button>page.content; const button = <Button>page.content;
return button.closeModal.bind(button); return button.closeModal.bind(button);
@ -440,15 +438,13 @@ export function test_WhenInnerViewCallsCloseModal_WithoutArguments_ShouldWork()
} }
export function test_WhenInnerViewCallsCloseCallback_WithArguments_ShouldPassResult() { export function test_WhenInnerViewCallsCloseCallback_WithArguments_ShouldPassResult() {
_test_WhenInnerViewCallsCloseModal((args: ShownModallyData) => _test_WhenInnerViewCallsCloseModal((args: ShownModallyData) => {
{
return args.closeCallback; return args.closeCallback;
}, "return value"); }, "return value");
} }
export function test_WhenInnerViewCallsCloseCallback_WithoutArguments_ShouldWork() { export function test_WhenInnerViewCallsCloseCallback_WithoutArguments_ShouldWork() {
_test_WhenInnerViewCallsCloseModal((args: ShownModallyData) => _test_WhenInnerViewCallsCloseModal((args: ShownModallyData) => {
{
return args.closeCallback; return args.closeCallback;
}); });
} }
@ -460,14 +456,14 @@ function _test_WhenInnerViewCallsCloseModal(closeModalGetter: (ShownModallyData)
modalClosedWithResult = returnValue === result; modalClosedWithResult = returnValue === result;
} }
const modalPageShownModallyEventHandler = function(args: ShownModallyData) { const modalPageShownModallyEventHandler = function (args: ShownModallyData) {
const page = <Page>args.object; const page = <Page>args.object;
page.off(View.shownModallyEvent, modalPageShownModallyEventHandler); page.off(View.shownModallyEvent, modalPageShownModallyEventHandler);
closeModalGetter(args)(result); closeModalGetter(args)(result);
} }
const hostNavigatedToEventHandler = function(args: NavigatedData) { const hostNavigatedToEventHandler = function (args: NavigatedData) {
const page = <Page>args.object; const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler); page.off(Page.navigatedToEvent, hostNavigatedToEventHandler);
@ -482,7 +478,7 @@ function _test_WhenInnerViewCallsCloseModal(closeModalGetter: (ShownModallyData)
(<Button>page.content).showModal(modalPage, {}, modalCloseCallback); (<Button>page.content).showModal(modalPage, {}, modalCloseCallback);
} }
const masterPageFactory = function(): Page { const masterPageFactory = function (): Page {
const masterPage = new Page(); const masterPage = new Page();
masterPage.id = "masterPage_test_WhenInnerViewCallsCloseModal_WithArguments_ShouldPassResult"; masterPage.id = "masterPage_test_WhenInnerViewCallsCloseModal_WithArguments_ShouldPassResult";
masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler) masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler)
@ -511,7 +507,7 @@ export function test_WhenViewBaseCallsShowModal_WithArguments_ShouldOpenModal()
modalClosed = true; modalClosed = true;
} }
const createTabItems = function(count: number) { const createTabItems = function (count: number) {
var items = new Array<TabViewItem>(); var items = new Array<TabViewItem>();
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
@ -527,13 +523,13 @@ export function test_WhenViewBaseCallsShowModal_WithArguments_ShouldOpenModal()
return items; return items;
} }
const modalPageShownModallyEventHandler = function(args: ShownModallyData) { const modalPageShownModallyEventHandler = function (args: ShownModallyData) {
const page = <Page>args.object; const page = <Page>args.object;
page.off(View.shownModallyEvent, modalPageShownModallyEventHandler); page.off(View.shownModallyEvent, modalPageShownModallyEventHandler);
args.closeCallback(); args.closeCallback();
} }
const hostNavigatedToEventHandler = function(args) { const hostNavigatedToEventHandler = function (args) {
const page = <Page>args.object; const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler); page.off(Page.navigatedToEvent, hostNavigatedToEventHandler);
@ -544,7 +540,7 @@ export function test_WhenViewBaseCallsShowModal_WithArguments_ShouldOpenModal()
tabViewItem.showModal(modalPage, {}, modalCloseCallback, false, false); tabViewItem.showModal(modalPage, {}, modalCloseCallback, false, false);
} }
const masterPageFactory = function(): Page { const masterPageFactory = function (): Page {
const masterPage = new Page(); const masterPage = new Page();
masterPage.id = "masterPage_test_WhenViewBaseCallsShowModal_WithArguments_ShouldOpenModal"; masterPage.id = "masterPage_test_WhenViewBaseCallsShowModal_WithArguments_ShouldOpenModal";
masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler) masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler)
@ -566,11 +562,14 @@ export function test_WhenViewBaseCallsShowModal_WithArguments_ShouldOpenModal()
} }
} }
export function test_WhenViewBaseCallsShowModal_WithoutArguments_ShouldThrow() { export function test_WhenViewBaseCallsShowModal_WithShowModalOptionsArguments_ShouldOpenModal() {
let navigatedTo = false; let modalClosed = false;
let modalThrows = false;
const createTabItems = function(count: number) { const modalCloseCallback = function (returnValue: any) {
modalClosed = true;
}
const createTabItems = function (count: number) {
var items = new Array<TabViewItem>(); var items = new Array<TabViewItem>();
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
@ -586,7 +585,66 @@ export function test_WhenViewBaseCallsShowModal_WithoutArguments_ShouldThrow() {
return items; return items;
} }
const hostNavigatedToEventHandler = function(args) { const modalPageShownModallyEventHandler = function (args: ShownModallyData) {
const page = <Page>args.object;
page.off(View.shownModallyEvent, modalPageShownModallyEventHandler);
args.closeCallback();
}
const hostNavigatedToEventHandler = function (args) {
const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler);
const modalPage = new Page();
modalPage.id = "modalPage_test_WhenViewBaseCallsShowModal_WithShowModalOptionsArguments_ShouldOpenModal";
modalPage.on(View.shownModallyEvent, modalPageShownModallyEventHandler);
const tabViewItem = (<TabView>page.content).items[0];
tabViewItem.showModal(modalPage, {
context: {},
closeCallback: modalCloseCallback,
fullscreen: false,
animated: false
});
}
const masterPageFactory = function (): Page {
const masterPage = new Page();
masterPage.id = "masterPage_test_WhenViewBaseCallsShowModal_WithShowModalOptionsArguments_ShouldOpenModal";
masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler)
const tabView = new TabView();
tabView.items = createTabItems(2);
masterPage.content = tabView;
return masterPage;
};
helper.navigate(masterPageFactory);
TKUnit.waitUntilReady(() => modalClosed);
}
export function test_WhenViewBaseCallsShowModal_WithoutArguments_ShouldThrow() {
let navigatedTo = false;
let modalThrows = false;
const createTabItems = function (count: number) {
var items = new Array<TabViewItem>();
for (var i = 0; i < count; i++) {
var label = new Label();
label.text = "Tab " + i;
var tabEntry = new TabViewItem();
tabEntry.title = "Tab " + i;
tabEntry.view = label;
items.push(tabEntry);
}
return items;
}
const hostNavigatedToEventHandler = function (args) {
const page = <Page>args.object; const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler); page.off(Page.navigatedToEvent, hostNavigatedToEventHandler);
@ -601,7 +659,7 @@ export function test_WhenViewBaseCallsShowModal_WithoutArguments_ShouldThrow() {
navigatedTo = true; navigatedTo = true;
} }
const masterPageFactory = function(): Page { const masterPageFactory = function (): Page {
const masterPage = new Page(); const masterPage = new Page();
masterPage.id = "masterPage_test_WhenViewBaseCallsShowModal_WithoutArguments_ShouldThrow"; masterPage.id = "masterPage_test_WhenViewBaseCallsShowModal_WithoutArguments_ShouldThrow";
masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler) masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler)
@ -667,7 +725,7 @@ export function test_WhenRootTabViewShownModallyItCanCloseModal() {
modalClosed = true; modalClosed = true;
} }
const createTabItems = function(count: number) { const createTabItems = function (count: number) {
var items = new Array<TabViewItem>(); var items = new Array<TabViewItem>();
for (var i = 0; i < count; i++) { for (var i = 0; i < count; i++) {
@ -683,11 +741,11 @@ export function test_WhenRootTabViewShownModallyItCanCloseModal() {
return items; return items;
} }
const tabViewShownModallyEventHandler = function(args: ShownModallyData) { const tabViewShownModallyEventHandler = function (args: ShownModallyData) {
args.closeCallback("return value"); args.closeCallback("return value");
} }
const hostNavigatedToEventHandler = function(args) { const hostNavigatedToEventHandler = function (args) {
const page = <Page>args.object; const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler); page.off(Page.navigatedToEvent, hostNavigatedToEventHandler);
@ -698,7 +756,7 @@ export function test_WhenRootTabViewShownModallyItCanCloseModal() {
page.showModal(tabView, {}, modalCloseCallback, false, false); page.showModal(tabView, {}, modalCloseCallback, false, false);
} }
const masterPageFactory = function(): Page { const masterPageFactory = function (): Page {
const masterPage = new Page(); const masterPage = new Page();
masterPage.id = "masterPage_test_WhenRootTabViewShownModallyItCanCloseModal"; masterPage.id = "masterPage_test_WhenRootTabViewShownModallyItCanCloseModal";
masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler); masterPage.on(Page.navigatedToEvent, hostNavigatedToEventHandler);
@ -832,11 +890,11 @@ export function test_WhenModalPageShownHostPageNavigationEventsShouldNotBeRaised
hostNavigatedFromCount++; hostNavigatedFromCount++;
}; };
const modalPageShownModallyEventHandler = function() { const modalPageShownModallyEventHandler = function () {
TKUnit.assertEqual(stack().length, 1, "Single frame should be instantiated at this point!"); TKUnit.assertEqual(stack().length, 1, "Single frame should be instantiated at this point!");
} }
const hostNavigatedToEventHandler2 = function(args: NavigatedData) { const hostNavigatedToEventHandler2 = function (args: NavigatedData) {
const page = <Page>args.object; const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler2); page.off(Page.navigatedToEvent, hostNavigatedToEventHandler2);
@ -915,7 +973,7 @@ export function test_WhenModalPageShownModalNavigationToEventsShouldBeRaised() {
modalNavigatedFromCount++; modalNavigatedFromCount++;
}; };
const modalFrameShownModallyEventHandler = function(args) { const modalFrameShownModallyEventHandler = function (args) {
const basePath = "ui/page/"; const basePath = "ui/page/";
const entry: NavigationEntry = { const entry: NavigationEntry = {
moduleName: basePath + "modal-page" moduleName: basePath + "modal-page"
@ -932,7 +990,7 @@ export function test_WhenModalPageShownModalNavigationToEventsShouldBeRaised() {
let modalFrame; let modalFrame;
const hostNavigatedToEventHandler = function(args) { const hostNavigatedToEventHandler = function (args) {
const page = <Page>args.object; const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler); page.off(Page.navigatedToEvent, hostNavigatedToEventHandler);
@ -981,11 +1039,11 @@ export function test_WhenModalFrameShownModalEventsRaisedOnRootModalFrame() {
ready = true; ready = true;
} }
const modalFrameShowingModallyEventHandler = function(args: ShownModallyData) { const modalFrameShowingModallyEventHandler = function (args: ShownModallyData) {
showingModallyCount++; showingModallyCount++;
} }
const modalFrameShownModallyEventHandler = function(args: ShownModallyData) { const modalFrameShownModallyEventHandler = function (args: ShownModallyData) {
shownModallyCount++; shownModallyCount++;
TKUnit.assertEqual(stack().length, 2, "Host and modal frame should be instantiated at this point!"); TKUnit.assertEqual(stack().length, 2, "Host and modal frame should be instantiated at this point!");
@ -994,7 +1052,7 @@ export function test_WhenModalFrameShownModalEventsRaisedOnRootModalFrame() {
let modalFrame; let modalFrame;
const hostNavigatedToEventHandler = function(args) { const hostNavigatedToEventHandler = function (args) {
const page = <Page>args.object; const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler); page.off(Page.navigatedToEvent, hostNavigatedToEventHandler);
@ -1051,11 +1109,11 @@ export function test_WhenModalPageShownShowModalEventsRaisedOnRootModalPage() {
ready = true; ready = true;
} }
const modalPageShowingModallyEventHandler = function(args: ShownModallyData) { const modalPageShowingModallyEventHandler = function (args: ShownModallyData) {
showingModallyCount++; showingModallyCount++;
} }
const modalPageShownModallyEventHandler = function(args: ShownModallyData) { const modalPageShownModallyEventHandler = function (args: ShownModallyData) {
shownModallyCount++; shownModallyCount++;
setTimeout(() => { setTimeout(() => {
@ -1063,7 +1121,7 @@ export function test_WhenModalPageShownShowModalEventsRaisedOnRootModalPage() {
}, 0); }, 0);
} }
const hostNavigatedToEventHandler = function(args) { const hostNavigatedToEventHandler = function (args) {
const page = <Page>args.object; const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler); page.off(Page.navigatedToEvent, hostNavigatedToEventHandler);
@ -1114,15 +1172,15 @@ export function test_WhenModalPageShownShowModalEventsRaisedOnRootModalTabView()
setTimeout(() => ready = true, 50); setTimeout(() => ready = true, 50);
} }
const modalTabViewShowingModallyEventHandler = function(args: ShownModallyData) { const modalTabViewShowingModallyEventHandler = function (args: ShownModallyData) {
showingModallyCount++; showingModallyCount++;
} }
const modalTabViewShownModallyEventHandler = function(args: ShownModallyData) { const modalTabViewShownModallyEventHandler = function (args: ShownModallyData) {
shownModallyCount++; shownModallyCount++;
} }
const hostNavigatedToEventHandler = function(args) { const hostNavigatedToEventHandler = function (args) {
const page = <Page>args.object; const page = <Page>args.object;
page.off(Page.navigatedToEvent, hostNavigatedToEventHandler); page.off(Page.navigatedToEvent, hostNavigatedToEventHandler);
@ -1139,7 +1197,7 @@ export function test_WhenModalPageShownShowModalEventsRaisedOnRootModalTabView()
TKUnit.assertEqual(stack().length, 2, "Host and tab modal frame should be instantiated at this point!"); TKUnit.assertEqual(stack().length, 2, "Host and tab modal frame should be instantiated at this point!");
page.showModal(modalTabView, { }, modalCloseCallback, false, false); page.showModal(modalTabView, {}, modalCloseCallback, false, false);
} }
const masterPageFactory = function (): Page { const masterPageFactory = function (): Page {

View File

@ -1,10 +1,19 @@
import * as TKUnit from "../../TKUnit"; import * as TKUnit from "../../TKUnit";
import * as helper from "../helper"; import * as helper from "../helper";
import { Page } from "tns-core-modules/ui/page"; import { Page } from "tns-core-modules/ui/page";
import { isAndroid } from "tns-core-modules/platform";
import { Frame, NavigationEntry, stack } from "tns-core-modules/ui/frame"; import { Frame, NavigationEntry, stack } from "tns-core-modules/ui/frame";
import { _resetRootView, getRootView } from "tns-core-modules/application"; import { _resetRootView, getRootView } from "tns-core-modules/application";
import { TabView, TabViewItem } from "tns-core-modules/ui/tab-view"; import { TabView, TabViewItem } from "tns-core-modules/ui/tab-view";
function waitUntilTabViewReady(page: Page, action: Function) {
helper.waitUntilNavigatedTo(page, action);
if (isAndroid) {
TKUnit.waitUntilReady(() => page.frame._currentEntry.fragment.isAdded());
}
}
function createTestFrameRootEntry() { function createTestFrameRootEntry() {
const page = new Page(); const page = new Page();
const frameRoot = new Frame(); const frameRoot = new Frame();
@ -77,7 +86,7 @@ export function test_reset_frame_to_tab() {
const testTabRoot = createTestTabRootEntry(); const testTabRoot = createTestTabRootEntry();
helper.waitUntilNavigatedTo(testTabRoot.page, () => _resetRootView(testTabRoot.entry)); waitUntilTabViewReady(testTabRoot.page, () => _resetRootView(testTabRoot.entry));
const rootView2 = getRootView(); const rootView2 = getRootView();
const frameStack2 = stack(); const frameStack2 = stack();
@ -88,7 +97,7 @@ export function test_reset_frame_to_tab() {
export function test_reset_tab_to_frame() { export function test_reset_tab_to_frame() {
const testTabRoot = createTestTabRootEntry(); const testTabRoot = createTestTabRootEntry();
helper.waitUntilNavigatedTo(testTabRoot.page, () => _resetRootView(testTabRoot.entry)); waitUntilTabViewReady(testTabRoot.page, () => _resetRootView(testTabRoot.entry));
const rootView2 = getRootView(); const rootView2 = getRootView();
const frameStack2 = stack(); const frameStack2 = stack();
@ -108,7 +117,7 @@ export function test_reset_tab_to_frame() {
export function test_reset_tab_to_tab() { export function test_reset_tab_to_tab() {
const testTabRoot1 = createTestTabRootEntry(); const testTabRoot1 = createTestTabRootEntry();
helper.waitUntilNavigatedTo(testTabRoot1.page, () => _resetRootView(testTabRoot1.entry)); waitUntilTabViewReady(testTabRoot1.page, () => _resetRootView(testTabRoot1.entry));
const rootView1 = getRootView(); const rootView1 = getRootView();
const frameStack1 = stack(); const frameStack1 = stack();
@ -117,7 +126,7 @@ export function test_reset_tab_to_tab() {
const testTabRoot2 = createTestTabRootEntry(); const testTabRoot2 = createTestTabRootEntry();
helper.waitUntilNavigatedTo(testTabRoot2.page, () => _resetRootView(testTabRoot2.entry)); waitUntilTabViewReady(testTabRoot2.page, () => _resetRootView(testTabRoot2.entry));
const rootView2 = getRootView(); const rootView2 = getRootView();
const frameStack2 = stack(); const frameStack2 = stack();
@ -128,7 +137,7 @@ export function test_reset_tab_to_tab() {
export function test_reset_during_tab_index_change() { export function test_reset_during_tab_index_change() {
const testTabRoot = createTestTabRootEntry(); const testTabRoot = createTestTabRootEntry();
helper.waitUntilNavigatedTo(testTabRoot.page, () => _resetRootView(testTabRoot.entry)); waitUntilTabViewReady(testTabRoot.page, () => _resetRootView(testTabRoot.entry));
testTabRoot.root.selectedIndex = 1; testTabRoot.root.selectedIndex = 1;

View File

@ -18,6 +18,16 @@ function waitUntilNavigatedToMaxTimeout(pages: Page[], action: Function) {
TKUnit.waitUntilReady(() => completed === pages.length, maxTimeout); TKUnit.waitUntilReady(() => completed === pages.length, maxTimeout);
} }
function waitUntilTabViewReady(page: Page, action: Function) {
action();
if (isAndroid) {
TKUnit.waitUntilReady(() => page.frame._currentEntry.fragment.isAdded());
} else {
TKUnit.waitUntilReady(() => page.isLoaded);
}
}
function createPage(i: number) { function createPage(i: number) {
const page = new Page(); const page = new Page();
page.id = `Tab${i} Frame${i} Page${i}`; page.id = `Tab${i} Frame${i} Page${i}`;
@ -60,25 +70,21 @@ export function test_frame_topmost_matches_selectedIndex() {
const items = createTabItemsWithFrames(3); const items = createTabItemsWithFrames(3);
const tabView = new TabView(); const tabView = new TabView();
tabView.items = items.map(item => item.tabItem); tabView.items = items.map(item => item.tabItem);
tabView.selectedIndex = 0;
// iOS cannot preload tab items
// Android preloads 1 tab item to the sides by default
// set this to 0, so that both platforms behave the same.
tabView.androidOffscreenTabLimit = 0;
const entry: NavigationEntry = { const entry: NavigationEntry = {
create: () => tabView create: () => tabView
}; };
if (isAndroid) {
waitUntilNavigatedToMaxTimeout([items[0].page, items[1].page], () => _resetRootView(entry));
TKUnit.assertEqual(topmost().id, "Tab0 Frame0");
tabView.selectedIndex = 1;
TKUnit.assertEqual(topmost().id, "Tab1 Frame1");
} else {
waitUntilNavigatedToMaxTimeout([items[0].page], () => _resetRootView(entry)); waitUntilNavigatedToMaxTimeout([items[0].page], () => _resetRootView(entry));
TKUnit.assertEqual(topmost().id, "Tab0 Frame0"); TKUnit.assertEqual(topmost().id, "Tab0 Frame0");
waitUntilNavigatedToMaxTimeout([items[1].page], () => tabView.selectedIndex = 1); waitUntilNavigatedToMaxTimeout([items[1].page], () => tabView.selectedIndex = 1);
TKUnit.assertEqual(topmost().id, "Tab1 Frame1"); TKUnit.assertEqual(topmost().id, "Tab1 Frame1");
}
} }
export function test_offset_zero_should_raise_same_events() { export function test_offset_zero_should_raise_same_events() {
@ -159,8 +165,8 @@ export function test_offset_zero_should_raise_same_events() {
TKUnit.assertDeepEqual(actualEventsRaised, expectedEventsRaisedAfterSelectThirdTab); TKUnit.assertDeepEqual(actualEventsRaised, expectedEventsRaisedAfterSelectThirdTab);
resetActualEventsRaised(); resetActualEventsRaised();
tabView.selectedIndex = 0;
TKUnit.waitUntilReady(() => items[0].page.isLoaded); waitUntilTabViewReady(items[0].page, () => tabView.selectedIndex = 0);
const expectedEventsRaisedAfterReturnToFirstTab = [ const expectedEventsRaisedAfterReturnToFirstTab = [
[ [
@ -256,8 +262,8 @@ export function test_android_default_offset_should_preload_1_tab_on_each_side()
TKUnit.assertDeepEqual(actualEventsRaised, expectedEventsRaisedAfterSelectThirdTab); TKUnit.assertDeepEqual(actualEventsRaised, expectedEventsRaisedAfterSelectThirdTab);
resetActualEventsRaised(); resetActualEventsRaised();
tabView.selectedIndex = 0;
TKUnit.waitUntilReady(() => items[0].page.isLoaded); waitUntilTabViewReady(items[0].page, () => tabView.selectedIndex = 0);
const expectedEventsRaisedAfterReturnToFirstTab = [ const expectedEventsRaisedAfterReturnToFirstTab = [
[ [

View File

@ -6,10 +6,10 @@
"nativescript": { "nativescript": {
"id": "org.nativescript.UnitTestApp", "id": "org.nativescript.UnitTestApp",
"tns-ios": { "tns-ios": {
"version": "5.0.0" "version": "5.1.0"
}, },
"tns-android": { "tns-android": {
"version": "5.0.0" "version": "5.1.0"
} }
}, },
"dependencies": { "dependencies": {

View File

@ -21,9 +21,20 @@ import { Frame, NavigationEntry } from "../ui/frame";
import * as utils from "../utils/utils"; import * as utils from "../utils/utils";
import { profile, level as profilingLevel, Level } from "../profiling"; import { profile, level as profilingLevel, Level } from "../profiling";
class Responder extends UIResponder { // NOTE: UIResponder with implementation of window - related to https://github.com/NativeScript/ios-runtime/issues/430
// // TODO: Refactor the UIResponder to use Typescript extends when this issue is resolved:
} // https://github.com/NativeScript/ios-runtime/issues/1012
var Responder = (<any>UIResponder).extend({
get window() {
return iosApp ? iosApp.window : undefined;
},
set window(setWindow) {
// NOOP
}
}, {
protocols: [UIApplicationDelegate]
}
);
class NotificationObserver extends NSObject { class NotificationObserver extends NSObject {
private _onReceiveCallback: (notification: NSNotification) => void; private _onReceiveCallback: (notification: NSNotification) => void;

View File

@ -1,7 +1,7 @@
{ {
"name": "tns-core-modules", "name": "tns-core-modules",
"description": "Telerik NativeScript Core Modules", "description": "Telerik NativeScript Core Modules",
"version": "5.1.0", "version": "5.2.0",
"homepage": "https://www.nativescript.org", "homepage": "https://www.nativescript.org",
"repository": { "repository": {
"type": "git", "type": "git",
@ -26,7 +26,7 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"typings": "tns-core-modules.d.ts", "typings": "tns-core-modules.d.ts",
"dependencies": { "dependencies": {
"tns-core-modules-widgets": "5.1.1", "tns-core-modules-widgets": "next",
"tslib": "^1.9.3" "tslib": "^1.9.3"
}, },
"devDependencies": { "devDependencies": {

View File

@ -46,6 +46,43 @@ export function isEventOrGesture(name: string, view: ViewBase): boolean;
*/ */
export function getViewById(view: ViewBase, id: string): ViewBase; export function getViewById(view: ViewBase, id: string): ViewBase;
export interface ShowModalOptions {
/**
* Any context you want to pass to the modally shown view. This same context will be available in the arguments of the shownModally event handler.
*/
context: any;
/**
* A function that will be called when the view is closed. Any arguments provided when calling ShownModallyData.closeCallback will be available here.
*/
closeCallback: Function;
/**
* An optional parameter specifying whether to show the modal view in full-screen mode.
*/
fullscreen?: boolean;
/**
* An optional parameter specifying whether to show the modal view with animation.
*/
animated?: boolean;
/**
* An optional parameter specifying whether to stretch the modal view when not in full-screen mode.
*/
stretched?: boolean;
/**
* An optional parameter that specify options specific to iOS as an object.
*/
ios?: {
/**
* The UIModalPresentationStyle to be used when showing the dialog in iOS .
*/
presentationStyle: any /* UIModalPresentationStyle */
}
}
export abstract class ViewBase extends Observable { export abstract class ViewBase extends Observable {
// Dynamic properties. // Dynamic properties.
left: Length; left: Length;
@ -110,6 +147,7 @@ export abstract class ViewBase extends Observable {
//@endprivate //@endprivate
/** /**
* @deprecated Use showModal with ShowModalOptions instead.
* Shows the View contained in moduleName as a modal view. * Shows the View contained in moduleName as a modal view.
* @param moduleName - The name of the module to load starting from the application root. * @param moduleName - The name of the module to load starting from the application root.
* @param context - Any context you want to pass to the modally shown view. * @param context - Any context you want to pass to the modally shown view.
@ -123,6 +161,7 @@ export abstract class ViewBase extends Observable {
showModal(moduleName: string, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean): ViewBase; showModal(moduleName: string, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean): ViewBase;
/** /**
* @deprecated Use showModal with ShowModalOptions instead.
* Shows the view passed as parameter as a modal view. * Shows the view passed as parameter as a modal view.
* @param view - View instance to be shown modally. * @param view - View instance to be shown modally.
* @param context - Any context you want to pass to the modally shown view. This same context will be available in the arguments of the shownModally event handler. * @param context - Any context you want to pass to the modally shown view. This same context will be available in the arguments of the shownModally event handler.
@ -133,6 +172,20 @@ export abstract class ViewBase extends Observable {
*/ */
showModal(view: ViewBase, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean): ViewBase; showModal(view: ViewBase, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean): ViewBase;
/**
* Shows the View contained in moduleName as a modal view.
* @param moduleName - The name of the module to load starting from the application root.
* @param modalOptions - A ShowModalOptions instance
*/
showModal(moduleName: string, modalOptions: ShowModalOptions): ViewBase;
/**
* Shows the view passed as parameter as a modal view.
* @param view - View instance to be shown modally.
* @param modalOptions - A ShowModalOptions instance
*/
showModal(view: ViewBase, modalOptions: ShowModalOptions): ViewBase;
/** /**
* Deprecated. Showing view as modal is deprecated. * Deprecated. Showing view as modal is deprecated.
* Use showModal method with arguments. * Use showModal method with arguments.

View File

@ -7,7 +7,8 @@ import {
import { import {
ViewBase, Property, booleanConverter, EventData, layout, ViewBase, Property, booleanConverter, EventData, layout,
getEventOrGestureName, traceEnabled, traceWrite, traceCategories, getEventOrGestureName, traceEnabled, traceWrite, traceCategories,
InheritedProperty InheritedProperty,
ShowModalOptions
} from "../view-base"; } from "../view-base";
import { HorizontalAlignment, VerticalAlignment, Visibility, Length, PercentLength } from "../../styling/style-properties"; import { HorizontalAlignment, VerticalAlignment, Visibility, Length, PercentLength } from "../../styling/style-properties";
@ -215,23 +216,39 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
return undefined; return undefined;
} }
public showModal(): ViewDefinition { private getModalOptions(args: IArguments): { view: ViewCommon, options: ShowModalOptions } {
if (arguments.length === 0) { if (args.length === 0) {
throw new Error("showModal without parameters is deprecated. Please call showModal on a view instance instead."); throw new Error("showModal without parameters is deprecated. Please call showModal on a view instance instead.");
} else { } else {
const firstAgrument = arguments[0]; let options: ShowModalOptions = null;
const context: any = arguments[1];
const closeCallback: Function = arguments[2];
const fullscreen: boolean = arguments[3];
const animated = arguments[4];
const stretched = arguments[5];
const view = firstAgrument instanceof ViewCommon if (args.length === 2) {
? firstAgrument : <ViewCommon>createViewFromEntry({ moduleName: firstAgrument }); options = <ShowModalOptions>args[1];
} else {
view._showNativeModalView(this, context, closeCallback, fullscreen, animated, stretched); // TODO: Add deprecation warning
return view; options = {
context: args[1],
closeCallback: args[2],
fullscreen: args[3],
animated: args[4],
stretched: args[5]
};
} }
const firstArgument = args[0];
const view = firstArgument instanceof ViewCommon
? firstArgument : <ViewCommon>createViewFromEntry({ moduleName: firstArgument });
return { view, options };
}
}
public showModal(): ViewDefinition {
const { view, options } = this.getModalOptions(arguments);
view._showNativeModalView(this, options);
return view;
} }
public closeModal(...args) { public closeModal(...args) {
@ -250,12 +267,12 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
return this._modal; return this._modal;
} }
protected _showNativeModalView(parent: ViewCommon, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean) { protected _showNativeModalView(parent: ViewCommon, options: ShowModalOptions) { //context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean, iosOpts?: any) {
_rootModalViews.push(this); _rootModalViews.push(this);
parent._modal = this; parent._modal = this;
this._modalParent = parent; this._modalParent = parent;
this._modalContext = context; this._modalContext = options.context;
const that = this; const that = this;
this._closeModalCallback = function (...originalArgs) { this._closeModalCallback = function (...originalArgs) {
if (that._closeModalCallback) { if (that._closeModalCallback) {
@ -268,8 +285,8 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
parent._modal = null; parent._modal = null;
const whenClosedCallback = () => { const whenClosedCallback = () => {
if (typeof closeCallback === "function") { if (typeof options.closeCallback === "function") {
closeCallback.apply(undefined, originalArgs); options.closeCallback.apply(undefined, originalArgs);
} }
} }

View File

@ -6,7 +6,7 @@ import {
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty, ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty,
traceEnabled, traceWrite, traceCategories, traceNotifyEvent, traceEnabled, traceWrite, traceCategories, traceNotifyEvent,
paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty, paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty,
Color, EventData Color, EventData, ShowModalOptions
} from "./view-common"; } from "./view-common";
import { import {
@ -574,9 +574,8 @@ export class View extends ViewCommon {
return result | (childMeasuredState & layout.MEASURED_STATE_MASK); return result | (childMeasuredState & layout.MEASURED_STATE_MASK);
} }
protected _showNativeModalView(parent: View, options: ShowModalOptions) { //context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean, iosOpts?: any) {
protected _showNativeModalView(parent: View, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean) { super._showNativeModalView(parent, options);
super._showNativeModalView(parent, context, closeCallback, fullscreen, stretched);
if (!this.backgroundColor) { if (!this.backgroundColor) {
this.backgroundColor = new Color("White"); this.backgroundColor = new Color("White");
} }
@ -590,8 +589,8 @@ export class View extends ViewCommon {
const dialogOptions: DialogOptions = { const dialogOptions: DialogOptions = {
owner: this, owner: this,
fullscreen: !!fullscreen, fullscreen: !!options.fullscreen,
stretched: !!stretched, stretched: !!options.stretched,
shownCallback: () => this._raiseShownModallyEvent(), shownCallback: () => this._raiseShownModallyEvent(),
dismissCallback: () => this.closeModal() dismissCallback: () => this.closeModal()
} }

View File

@ -5,7 +5,7 @@ import { booleanConverter, Property } from "../view";
import { import {
ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty, ViewCommon, layout, isEnabledProperty, originXProperty, originYProperty, automationTextProperty, isUserInteractionEnabledProperty,
traceEnabled, traceWrite, traceCategories, traceError, traceMessageType, getAncestor traceEnabled, traceWrite, traceCategories, traceError, traceMessageType, ShowModalOptions
} from "./view-common"; } from "./view-common";
import { ios as iosBackground, Background } from "../../styling/background"; import { ios as iosBackground, Background } from "../../styling/background";
@ -371,7 +371,7 @@ export class View extends ViewCommon {
return this._suspendCATransaction || this._suspendNativeUpdatesCount; return this._suspendCATransaction || this._suspendNativeUpdatesCount;
} }
protected _showNativeModalView(parent: View, context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean) { protected _showNativeModalView(parent: View, options: ShowModalOptions) { //context: any, closeCallback: Function, fullscreen?: boolean, animated?: boolean, stretched?: boolean, iosOpts?: any) {
const parentWithController = ios.getParentWithViewController(parent); const parentWithController = ios.getParentWithViewController(parent);
if (!parentWithController) { if (!parentWithController) {
traceWrite(`Could not find parent with viewController for ${parent} while showing modal view.`, traceWrite(`Could not find parent with viewController for ${parent} while showing modal view.`,
@ -394,7 +394,7 @@ export class View extends ViewCommon {
this._setupAsRootView({}); this._setupAsRootView({});
super._showNativeModalView(parentWithController, context, closeCallback, fullscreen, stretched); super._showNativeModalView(parentWithController, options);
let controller = this.viewController; let controller = this.viewController;
if (!controller) { if (!controller) {
const nativeView = this.ios || this.nativeViewProtected; const nativeView = this.ios || this.nativeViewProtected;
@ -407,17 +407,31 @@ export class View extends ViewCommon {
this.viewController = controller; this.viewController = controller;
} }
if (fullscreen) { if (options.fullscreen) {
controller.modalPresentationStyle = UIModalPresentationStyle.FullScreen; controller.modalPresentationStyle = UIModalPresentationStyle.FullScreen;
} else { } else {
controller.modalPresentationStyle = UIModalPresentationStyle.FormSheet; controller.modalPresentationStyle = UIModalPresentationStyle.FormSheet;
} }
if (options.ios && options.ios.presentationStyle) {
const presentationStyle = options.ios.presentationStyle;
controller.modalPresentationStyle = presentationStyle;
if (presentationStyle === UIModalPresentationStyle.Popover) {
const popoverPresentationController = controller.popoverPresentationController;
const view = parent.nativeViewProtected;
// Note: sourceView and sourceRect are needed to specify the anchor location for the popover.
// Note: sourceView should be the button triggering the modal. If it the Page the popover might appear "behind" the page content
popoverPresentationController.sourceView = view;
popoverPresentationController.sourceRect = CGRectMake(0, 0, view.frame.size.width, view.frame.size.height);
}
}
this.horizontalAlignment = "stretch"; this.horizontalAlignment = "stretch";
this.verticalAlignment = "stretch"; this.verticalAlignment = "stretch";
this._raiseShowingModallyEvent(); this._raiseShowingModallyEvent();
animated = animated === undefined ? true : !!animated; const animated = options.animated === undefined ? true : !!options.animated;
(<any>controller).animated = animated; (<any>controller).animated = animated;
parentController.presentViewControllerAnimatedCompletion(controller, animated, null); parentController.presentViewControllerAnimatedCompletion(controller, animated, null);
const transitionCoordinator = iosUtils.getter(parentController, parentController.transitionCoordinator); const transitionCoordinator = iosUtils.getter(parentController, parentController.transitionCoordinator);

View File

@ -1,7 +1,7 @@
import { AndroidFragmentCallbacks, setFragmentCallbacks, setFragmentClass } from "./frame"; import { AndroidFragmentCallbacks, setFragmentCallbacks, setFragmentClass } from "./frame";
@JavaProxy("com.tns.FragmentClass") @JavaProxy("com.tns.FragmentClass")
class FragmentClass extends android.support.v4.app.Fragment { class FragmentClass extends org.nativescript.widgets.FragmentBase {
// This field is updated in the frame module upon `new` (although hacky this eases the Fragment->callbacks association a lot) // This field is updated in the frame module upon `new` (although hacky this eases the Fragment->callbacks association a lot)
private _callbacks: AndroidFragmentCallbacks; private _callbacks: AndroidFragmentCallbacks;
@ -15,8 +15,7 @@ class FragmentClass extends android.support.v4.app.Fragment {
} }
public onCreateAnimator(transit: number, enter: boolean, nextAnim: number): android.animation.Animator { public onCreateAnimator(transit: number, enter: boolean, nextAnim: number): android.animation.Animator {
let result = this._callbacks.onCreateAnimator(this, transit, enter, nextAnim, super.onCreateAnimator); return this._callbacks.onCreateAnimator(this, transit, enter, nextAnim, super.onCreateAnimator);
return result;
} }
public onStop(): void { public onStop(): void {

View File

@ -86,7 +86,20 @@ export function _setAndroidFragmentTransitions(
name = navigationTransition.name ? navigationTransition.name.toLowerCase() : ""; name = navigationTransition.name ? navigationTransition.name.toLowerCase() : "";
} }
let useLollipopTransition = name && (name.indexOf("slide") === 0 || name === "fade" || name === "explode") && sdkVersion() >= 21; let useLollipopTransition = !!(name && (name.indexOf("slide") === 0 || name === "fade" || name === "explode") && sdkVersion() >= 21);
// [nested frames / fragments] force disable lollipop transitions in case nested fragments
// are detected as applying dummy animator to the nested fragment with the same duration as
// the exit animator of the removing parent fragment as a workaround for
// https://code.google.com/p/android/issues/detail?id=55228 works only if custom animations are
// used
// NOTE: this effectively means you cannot use Explode transition in nested frames scenarios as
// we have implementations only for slide, fade, and flip
if (currentFragment &&
currentFragment.getChildFragmentManager() &&
currentFragment.getChildFragmentManager().getFragments().toArray().length > 0) {
useLollipopTransition = false;
}
if (!animated) { if (!animated) {
name = "none"; name = "none";
} else if (transition) { } else if (transition) {

View File

@ -457,8 +457,8 @@ export class FrameBase extends CustomLayoutView implements FrameDefinition {
} }
public _onRootViewReset(): void { public _onRootViewReset(): void {
this._removeFromFrameStack();
super._onRootViewReset(); super._onRootViewReset();
this._removeFromFrameStack();
} }
get _childrenCount(): number { get _childrenCount(): number {

View File

@ -209,8 +209,12 @@ export class Frame extends FrameBase {
} }
public _onRootViewReset(): void { public _onRootViewReset(): void {
this.disposeCurrentFragment();
super._onRootViewReset(); super._onRootViewReset();
// call this AFTER the super call to ensure descendants apply their rootview-reset logic first
// i.e. in a scenario with nested frames / frame with tabview let the descendandt cleanup the inner
// fragments first, and then cleanup the parent fragments
this.disposeCurrentFragment();
} }
onUnloaded() { onUnloaded() {
@ -223,11 +227,6 @@ export class Frame extends FrameBase {
} }
private disposeCurrentFragment(): void { private disposeCurrentFragment(): void {
// when interacting with nested fragments it seems Android is smart enough
// to automatically remove child fragments when parent fragment is removed;
// however, we must add a fragment.isAdded() guard as our logic will try to
// explicitly remove the already removed child fragment causing an
// IllegalStateException: Fragment has not been attached yet.
if (!this._currentEntry || if (!this._currentEntry ||
!this._currentEntry.fragment || !this._currentEntry.fragment ||
!this._currentEntry.fragment.isAdded()) { !this._currentEntry.fragment.isAdded()) {
@ -742,7 +741,13 @@ class FragmentCallbacksImplementation implements AndroidFragmentCallbacks {
} }
@profile @profile
public onCreateAnimator(fragment: android.support.v4.app.Fragment, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.animation.Animator { public onCreateAnimator(fragment: org.nativescript.widgets.FragmentBase, transit: number, enter: boolean, nextAnim: number, superFunc: Function): android.animation.Animator {
// HACK: FragmentBase class MUST handle removing nested fragment scenario to workaround
// https://code.google.com/p/android/issues/detail?id=55228
if (!enter && fragment.getRemovingParentFragment()) {
return superFunc.call(fragment, transit, enter, nextAnim);
}
let nextAnimString: string; let nextAnimString: string;
switch (nextAnim) { switch (nextAnim) {
case AnimationType.enterFakeResourceId: nextAnimString = "enter"; break; case AnimationType.enterFakeResourceId: nextAnimString = "enter"; break;

View File

@ -45,7 +45,7 @@ function initializeNativeClasses() {
return; return;
} }
class TabFragmentImplementation extends android.support.v4.app.Fragment { class TabFragmentImplementation extends org.nativescript.widgets.FragmentBase {
private tab: TabView; private tab: TabView;
private index: number; private index: number;
@ -55,7 +55,6 @@ function initializeNativeClasses() {
} }
static newInstance(tabId: number, index: number): TabFragmentImplementation { static newInstance(tabId: number, index: number): TabFragmentImplementation {
const args = new android.os.Bundle(); const args = new android.os.Bundle();
args.putInt(TABID, tabId); args.putInt(TABID, tabId);
args.putInt(INDEX, index); args.putInt(INDEX, index);
@ -79,10 +78,6 @@ function initializeNativeClasses() {
return tabItem.view.nativeViewProtected; return tabItem.view.nativeViewProtected;
} }
public onDestroyView() {
super.onDestroyView();
}
} }
const POSITION_UNCHANGED = -1; const POSITION_UNCHANGED = -1;
@ -560,8 +555,13 @@ export class TabView extends TabViewBase {
} }
public _onRootViewReset(): void { public _onRootViewReset(): void {
this.disposeCurrentFragments();
super._onRootViewReset(); super._onRootViewReset();
// call this AFTER the super call to ensure descendants apply their rootview-reset logic first
// i.e. in a scenario with tab frames let the frames cleanup their fragments first, and then
// cleanup the tab fragments to avoid
// android.content.res.Resources$NotFoundException: Unable to find resource ID #0xfffffff6
this.disposeCurrentFragments();
} }
private disposeCurrentFragments(): void { private disposeCurrentFragments(): void {

View File

@ -164,6 +164,12 @@
public verticalAlignment: VerticalAlignment; public verticalAlignment: VerticalAlignment;
} }
export class FragmentBase extends android.support.v4.app.Fragment {
constructor();
public getRemovingParentFragment(): android.support.v4.app.Fragment;
}
export enum Stretch { export enum Stretch {
none, none,
aspectFill, aspectFill,

View File

@ -1,6 +1,6 @@
{ {
"name": "tns-platform-declarations", "name": "tns-platform-declarations",
"version": "5.1.0", "version": "5.2.0",
"description": "Platform-specific TypeScript declarations for NativeScript for accessing native objects", "description": "Platform-specific TypeScript declarations for NativeScript for accessing native objects",
"main": "", "main": "",
"scripts": { "scripts": {