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 cd6617697..efcbac90d 100644 --- a/packages/core/application/index.android.ts +++ b/packages/core/application/index.android.ts @@ -49,6 +49,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; /** * @deprecated Use Utils.android.getApplicationContext() instead. @@ -364,6 +365,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) { setThemeOnLaunch(activity, undefined, undefined); @@ -434,6 +438,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, @@ -442,6 +455,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 066b0460e..646399a0d 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. */ @@ -525,6 +535,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 1022a004e..b9ed79706 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'; @@ -255,6 +255,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(); @@ -262,11 +263,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();