diff --git a/packages/core/application/application-common.ts b/packages/core/application/application-common.ts index 25a7c6837..25284557c 100644 --- a/packages/core/application/application-common.ts +++ b/packages/core/application/application-common.ts @@ -22,6 +22,8 @@ export function hasLaunched(): boolean { export const launchEvent = 'launch'; export const suspendEvent = 'suspend'; export const displayedEvent = 'displayed'; +export const backgroundEvent = 'background'; +export const foregroundEvent = 'foreground'; export const resumeEvent = 'resume'; export const exitEvent = 'exit'; export const lowMemoryEvent = 'lowMemory'; diff --git a/packages/core/application/index.android.ts b/packages/core/application/index.android.ts index 0fc7bd6b4..a7ec8299a 100644 --- a/packages/core/application/index.android.ts +++ b/packages/core/application/index.android.ts @@ -43,6 +43,7 @@ export class AndroidApplication extends Observable implements AndroidApplication private _orientation: 'portrait' | 'landscape' | 'unknown'; private _systemAppearance: 'light' | 'dark'; public paused: boolean; + public backgrounded: boolean; public nativeApp: android.app.Application; public context: android.content.Context; public foregroundActivity: androidx.appcompat.app.AppCompatActivity; @@ -334,6 +335,9 @@ function initLifecycleCallbacks() { rootView.getViewTreeObserver().addOnGlobalLayoutListener(global.onGlobalLayoutListener); }); + + let activitiesStarted = 0; + const lifecycleCallbacks = new android.app.Application.ActivityLifecycleCallbacks({ onActivityCreated: profile('onActivityCreated', function (activity: androidx.appcompat.app.AppCompatActivity, savedInstanceState: android.os.Bundle) { if (!androidApp.foregroundActivity) { @@ -374,7 +378,7 @@ function initLifecycleCallbacks() { if ((activity).isNativeScriptActivity) { androidApp.paused = true; appCommon.notify({ - eventName: appCommon.suspendEvent, + eventName: appCommon.backgroundEvent, object: androidApp, android: activity, }); @@ -407,6 +411,15 @@ function initLifecycleCallbacks() { }), onActivityStarted: profile('onActivityStarted', function (activity: androidx.appcompat.app.AppCompatActivity) { + activitiesStarted++; + if (activitiesStarted === 1) { + androidApp.backgrounded = true; + appCommon.notify({ + eventName: appCommon.foregroundEvent, + object: androidApp, + android: activity, + }); + } androidApp.notify({ eventName: ActivityStarted, object: androidApp, @@ -415,6 +428,15 @@ function initLifecycleCallbacks() { }), onActivityStopped: profile('onActivityStopped', function (activity: androidx.appcompat.app.AppCompatActivity) { + activitiesStarted--; + if (activitiesStarted === 0) { + androidApp.backgrounded = true; + appCommon.notify({ + eventName: appCommon.backgroundEvent, + object: androidApp, + android: activity, + }); + } androidApp.notify({ eventName: ActivityStopped, object: androidApp, diff --git a/packages/core/application/index.d.ts b/packages/core/application/index.d.ts index 02c9b6b89..ef70d1fa9 100644 --- a/packages/core/application/index.d.ts +++ b/packages/core/application/index.d.ts @@ -34,6 +34,16 @@ export const suspendEvent: string; */ export const resumeEvent: string; +/** + * String value used when hooking to foreground event. + */ +export const foregroundEvent: string; + +/** + * String value used when hooking to background event. + */ +export const backgroundEvent: string; + /** * String value used when hooking to exit event. */ @@ -503,6 +513,11 @@ export class AndroidApplication extends Observable { */ paused: boolean; + /** + * True if the main application activity is in background, false otherwise. + */ + backgrounded: boolean; + /** * Initialized the android-specific application object with the native android.app.Application instance. * This is useful when creating custom application types. diff --git a/packages/core/application/index.ios.ts b/packages/core/application/index.ios.ts index 232f3e284..fa1f96aa0 100644 --- a/packages/core/application/index.ios.ts +++ b/packages/core/application/index.ios.ts @@ -4,7 +4,7 @@ import { ApplicationEventData, CssChangedEventData, LaunchEventData, LoadAppCSSE // TODO: explain why we need to this or remov it // Use requires to ensure order of imports is maintained -const { displayedEvent, exitEvent, getCssFileName, launchEvent, livesync, lowMemoryEvent, notify, on, orientationChanged, orientationChangedEvent, resumeEvent, setApplication, suspendEvent, systemAppearanceChanged, systemAppearanceChangedEvent } = require('./application-common'); +const { backgroundEvent, displayedEvent, exitEvent, foregroundEvent, getCssFileName, launchEvent, livesync, lowMemoryEvent, notify, on, orientationChanged, orientationChangedEvent, resumeEvent, setApplication, suspendEvent, systemAppearanceChanged, systemAppearanceChangedEvent } = require('./application-common'); // First reexport so that app module is initialized. export * from './application-common'; @@ -220,6 +220,7 @@ export class iOSApplication implements iOSApplicationDefinition { const ios = UIApplication.sharedApplication; const object = this; notify({ eventName: resumeEvent, object, ios }); + notify({ eventName: foregroundEvent, object, ios }); const rootView = this._rootView; if (rootView && !rootView.isLoaded) { rootView.callLoaded(); @@ -227,11 +228,10 @@ export class iOSApplication implements iOSApplicationDefinition { } private didEnterBackground(notification: NSNotification) { - notify({ - eventName: suspendEvent, - object: this, - ios: UIApplication.sharedApplication, - }); + const ios = UIApplication.sharedApplication; + const object = this; + notify({ eventName: suspendEvent, object, ios }); + notify({ eventName: backgroundEvent, object, ios }); const rootView = this._rootView; if (rootView && rootView.isLoaded) { rootView.callUnloaded();