diff --git a/tns-core-modules/application/application-common.ts b/tns-core-modules/application/application-common.ts index 7a79d34cc..970f9287a 100644 --- a/tns-core-modules/application/application-common.ts +++ b/tns-core-modules/application/application-common.ts @@ -21,6 +21,7 @@ import { UnhandledErrorEventData, iOSApplication, AndroidApplication, CssChanged export const launchEvent = "launch"; export const suspendEvent = "suspend"; +export const displayedEvent = "displayed"; export const resumeEvent = "resume"; export const exitEvent = "exit"; export const lowMemoryEvent = "lowMemory"; @@ -45,6 +46,7 @@ export let ios = undefined; export const on: typeof events.on = events.on.bind(events); export const off: typeof events.off = events.off.bind(events); export const notify: typeof events.notify = events.notify.bind(events); +export const hasListeners: typeof events.hasListeners = events.hasListeners.bind(events); let app: iOSApplication | AndroidApplication; export function setApplication(instance: iOSApplication | AndroidApplication): void { diff --git a/tns-core-modules/application/application.android.ts b/tns-core-modules/application/application.android.ts index 621115fe6..d36319e1c 100644 --- a/tns-core-modules/application/application.android.ts +++ b/tns-core-modules/application/application.android.ts @@ -4,7 +4,7 @@ } from "."; import { - notify, lowMemoryEvent, orientationChangedEvent, suspendEvent, resumeEvent, + notify, hasListeners, lowMemoryEvent, orientationChangedEvent, suspendEvent, resumeEvent, displayedEvent, setApplication, livesync, Observable } from "./application-common"; @@ -181,7 +181,19 @@ function initLifecycleCallbacks() { androidApp.startActivity = activity; } - androidApp.notify({ eventName: ActivityCreated, object: androidApp, activity: activity, bundle: savedInstanceState }); + androidApp.notify({ eventName: ActivityCreated, object: androidApp, activity, bundle: savedInstanceState }); + + if (hasListeners(displayedEvent)) { + let rootView = activity.findViewById((android).R.id.content); + let onGlobalLayoutListener = new android.view.ViewTreeObserver.OnGlobalLayoutListener({ + onGlobalLayout() { + notify({ eventName: displayedEvent, object: androidApp, activity }); + let viewTreeObserver = rootView.getViewTreeObserver(); + viewTreeObserver.removeOnGlobalLayoutListener(onGlobalLayoutListener); + } + }); + rootView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener); + } }, onActivityDestroyed: function (activity: android.app.Activity) { diff --git a/tns-core-modules/application/application.d.ts b/tns-core-modules/application/application.d.ts index 6ef5a70a5..3fc9d7c71 100644 --- a/tns-core-modules/application/application.d.ts +++ b/tns-core-modules/application/application.d.ts @@ -11,6 +11,11 @@ import { NavigationEntry, View, Observable, EventData } from "../ui/frame"; */ export var launchEvent: string; +/** + * String value used when hooking to displayed event. + */ +export var displayedEvent: string; + /** * String value used when hooking to uncaughtError event. */ @@ -189,6 +194,13 @@ export function hasListeners(eventName: string): boolean; */ export function on(event: "launch", callback: (args: LaunchEventData) => void, thisArg?: any); +/** + * This event is raised after the application has performed most of its startup actions. + * Its intent is to be suitable for measuring app startup times. + * @experimental + */ +export function on(event: "displayed", callback: (args: EventData) => void, thisArg?: any); + /** * This event is raised when the Application is suspended. */ diff --git a/tns-core-modules/application/application.ios.ts b/tns-core-modules/application/application.ios.ts index eb8924945..74ef34e32 100644 --- a/tns-core-modules/application/application.ios.ts +++ b/tns-core-modules/application/application.ios.ts @@ -2,7 +2,7 @@ import { notify, launchEvent, resumeEvent, suspendEvent, exitEvent, lowMemoryEvent, - orientationChangedEvent, setApplication, livesync + orientationChangedEvent, setApplication, livesync, displayedEvent } from "./application-common"; // First reexport so that app module is initialized. @@ -16,6 +16,8 @@ class Responder extends UIResponder { // } +let displayedOnce = false; + class Window extends UIWindow { public content; @@ -138,7 +140,13 @@ class IOSApplication implements IOSApplicationDefinition { } private didBecomeActive(notification: NSNotification) { - notify({ eventName: resumeEvent, object: this, ios: utils.ios.getter(UIApplication, UIApplication.sharedApplication) }); + let ios = utils.ios.getter(UIApplication, UIApplication.sharedApplication); + let object = this; + notify({ eventName: resumeEvent, object, ios }); + if (!displayedOnce) { + notify({ eventName: displayedEvent, object, ios }); + displayedOnce = true; + } } private didEnterBackground(notification: NSNotification) {