feat(android): support independent broadcast listeners (#10936)

This commit is contained in:
Eduardo Speroni
2025-11-04 14:29:09 -03:00
committed by GitHub
parent bbeca526f0
commit fe4c9c0b7d
8 changed files with 137 additions and 135 deletions

View File

@@ -40,9 +40,11 @@ import {
isA11yEnabled,
setA11yEnabled,
} from '../accessibility/accessibility-common';
import { androidGetForegroundActivity, androidGetStartActivity, androidPendingReceiverRegistrations, androidRegisterBroadcastReceiver, androidRegisteredReceivers, androidSetForegroundActivity, androidSetStartActivity, androidUnregisterBroadcastReceiver, applyContentDescription } from './helpers';
import { androidGetForegroundActivity, androidGetStartActivity, androidSetForegroundActivity, androidSetStartActivity, applyContentDescription } from './helpers';
import { getImageFetcher, getNativeApp, getRootView, initImageCache, setA11yUpdatePropertiesCallback, setApplicationPropertiesCallback, setAppMainEntry, setNativeApp, setRootView, setToggleApplicationEventListenersCallback } from './helpers-common';
import { getNativeScriptGlobals } from '../globals/global-utils';
import type { AndroidApplication as IAndroidApplication } from './application';
import lazy from '../utils/lazy';
declare class NativeScriptLifecycleCallbacks extends android.app.Application.ActivityLifecycleCallbacks {}
@@ -276,7 +278,36 @@ function initNativeScriptComponentCallbacks() {
return NativeScriptComponentCallbacks_;
}
export class AndroidApplication extends ApplicationCommon {
interface RegisteredReceiverInfo {
receiver: android.content.BroadcastReceiver;
intent: string;
callback: (context: android.content.Context, intent: android.content.Intent) => void;
id: number;
flags: number;
}
const BroadcastReceiver = lazy(() => {
@NativeClass
class BroadcastReceiverImpl extends android.content.BroadcastReceiver {
private _onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void;
constructor(onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void) {
super();
this._onReceiveCallback = onReceiveCallback;
return global.__native(this);
}
public onReceive(context: android.content.Context, intent: android.content.Intent) {
if (this._onReceiveCallback) {
this._onReceiveCallback(context, intent);
}
}
}
return BroadcastReceiverImpl;
});
export class AndroidApplication extends ApplicationCommon implements IAndroidApplication {
static readonly activityCreatedEvent = 'activityCreated';
static readonly activityDestroyedEvent = 'activityDestroyed';
static readonly activityStartedEvent = 'activityStarted';
@@ -332,10 +363,13 @@ export class AndroidApplication extends ApplicationCommon {
this._registerPendingReceivers();
}
private _registeredReceivers: Record<string, RegisteredReceiverInfo[]> = {};
private _registeredReceiversById: Record<number, RegisteredReceiverInfo> = {};
private _nextReceiverId: number = 1;
private _pendingReceiverRegistrations: Omit<RegisteredReceiverInfo, 'receiver'>[] = [];
private _registerPendingReceivers() {
androidPendingReceiverRegistrations.forEach((func) => func(this.context));
androidPendingReceiverRegistrations.length = 0;
this._pendingReceiverRegistrations.forEach((info) => this._registerReceiver(this.context, info.intent, info.callback, info.flags, info.id));
this._pendingReceiverRegistrations.length = 0;
}
onConfigurationChanged(configuration: android.content.res.Configuration): void {
@@ -414,18 +448,69 @@ export class AndroidApplication extends ApplicationCommon {
// RECEIVER_EXPORTED (2)
// RECEIVER_NOT_EXPORTED (4)
// RECEIVER_VISIBLE_TO_INSTANT_APPS (1)
public registerBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void, flags = 2): void {
androidRegisterBroadcastReceiver(intentFilter, onReceiveCallback, flags);
public registerBroadcastReceiver(intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void, flags = 2): () => void {
const receiverId = this._nextReceiverId++;
if (this.context) {
this._registerReceiver(this.context, intentFilter, onReceiveCallback, flags, receiverId);
} else {
this._pendingReceiverRegistrations.push({
intent: intentFilter,
callback: onReceiveCallback,
id: receiverId,
flags,
});
}
let removed = false;
return () => {
if (removed) {
return;
}
removed = true;
if (this._registeredReceiversById[receiverId]) {
const receiverInfo = this._registeredReceiversById[receiverId];
this.context.unregisterReceiver(receiverInfo.receiver);
this._registeredReceivers[receiverInfo.intent] = this._registeredReceivers[receiverInfo.intent]?.filter((ri) => ri.id !== receiverId);
delete this._registeredReceiversById[receiverId];
} else {
this._pendingReceiverRegistrations = this._pendingReceiverRegistrations.filter((ri) => ri.id !== receiverId);
}
};
}
private _registerReceiver(context: android.content.Context, intentFilter: string, onReceiveCallback: (context: android.content.Context, intent: android.content.Intent) => void, flags: number, id: number): android.content.BroadcastReceiver {
const receiver: android.content.BroadcastReceiver = new (BroadcastReceiver())(onReceiveCallback);
if (SDK_VERSION >= 26) {
context.registerReceiver(receiver, new android.content.IntentFilter(intentFilter), flags);
} else {
context.registerReceiver(receiver, new android.content.IntentFilter(intentFilter));
}
const receiverInfo: RegisteredReceiverInfo = { receiver, intent: intentFilter, callback: onReceiveCallback, id: typeof id === 'number' ? id : this._nextReceiverId++, flags };
this._registeredReceivers[intentFilter] ??= [];
this._registeredReceivers[intentFilter].push(receiverInfo);
this._registeredReceiversById[receiverInfo.id] = receiverInfo;
return receiver;
}
public unregisterBroadcastReceiver(intentFilter: string): void {
androidUnregisterBroadcastReceiver(intentFilter);
const receivers = this._registeredReceivers[intentFilter];
if (receivers) {
receivers.forEach((receiver) => {
this.context.unregisterReceiver(receiver.receiver);
});
this._registeredReceivers[intentFilter] = [];
}
}
public getRegisteredBroadcastReceiver(intentFilter: string): android.content.BroadcastReceiver | undefined {
return androidRegisteredReceivers[intentFilter];
return this._registeredReceivers[intentFilter]?.[0].receiver;
}
public getRegisteredBroadcastReceivers(intentFilter: string): android.content.BroadcastReceiver[] {
const receiversInfo = this._registeredReceivers[intentFilter];
if (receiversInfo) {
return receiversInfo.map((info) => info.receiver);
}
return [];
}
getRootView(): View {
const activity = this.foregroundActivity || this.startActivity;
if (!activity) {