feat: new 'background/foreground` events for app

On android `suspend/resume` was not doing what was expected.
in particular it was called while showing dialogs
This commit is contained in:
Martin Guillon
2022-02-01 21:45:22 +01:00
parent 663ab77a7d
commit 16d4338f96
4 changed files with 46 additions and 7 deletions

View File

@ -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';

View File

@ -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(<any>{
onActivityCreated: <any>profile('onActivityCreated', function (activity: androidx.appcompat.app.AppCompatActivity, savedInstanceState: android.os.Bundle) {
if (!androidApp.foregroundActivity) {
@ -374,7 +378,7 @@ function initLifecycleCallbacks() {
if ((<any>activity).isNativeScriptActivity) {
androidApp.paused = true;
appCommon.notify(<ApplicationEventData>{
eventName: appCommon.suspendEvent,
eventName: appCommon.backgroundEvent,
object: androidApp,
android: activity,
});
@ -407,6 +411,15 @@ function initLifecycleCallbacks() {
}),
onActivityStarted: <any>profile('onActivityStarted', function (activity: androidx.appcompat.app.AppCompatActivity) {
activitiesStarted++;
if (activitiesStarted === 1) {
androidApp.backgrounded = true;
appCommon.notify(<ApplicationEventData>{
eventName: appCommon.foregroundEvent,
object: androidApp,
android: activity,
});
}
androidApp.notify(<AndroidActivityEventData>{
eventName: ActivityStarted,
object: androidApp,
@ -415,6 +428,15 @@ function initLifecycleCallbacks() {
}),
onActivityStopped: <any>profile('onActivityStopped', function (activity: androidx.appcompat.app.AppCompatActivity) {
activitiesStarted--;
if (activitiesStarted === 0) {
androidApp.backgrounded = true;
appCommon.notify(<ApplicationEventData>{
eventName: appCommon.backgroundEvent,
object: androidApp,
android: activity,
});
}
androidApp.notify(<AndroidActivityEventData>{
eventName: ActivityStopped,
object: androidApp,

View File

@ -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.

View File

@ -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(<ApplicationEventData>{ eventName: resumeEvent, object, ios });
notify(<ApplicationEventData>{ 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(<ApplicationEventData>{
eventName: suspendEvent,
object: this,
ios: UIApplication.sharedApplication,
});
const ios = UIApplication.sharedApplication;
const object = this;
notify(<ApplicationEventData>{ eventName: suspendEvent, object, ios });
notify(<ApplicationEventData>{ eventName: backgroundEvent, object, ios });
const rootView = this._rootView;
if (rootView && rootView.isLoaded) {
rootView.callUnloaded();