@stream-io/video-react-native-sdk 1.29.0-beta.1 → 1.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3114 -0
- package/android/src/main/AndroidManifest.xml +1 -8
- package/android/src/main/AndroidManifestNew.xml +0 -11
- package/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt +5 -42
- package/android/src/main/java/com/streamvideo/reactnative/audio/utils/WebRtcAudioUtils.kt +6 -70
- package/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt +4 -6
- package/android/src/main/java/com/streamvideo/reactnative/util/CallAliveServiceChecker.kt +95 -0
- package/dist/commonjs/hooks/push/useIosCallkeepWithCallingStateEffect.js +160 -0
- package/dist/commonjs/hooks/push/useIosCallkeepWithCallingStateEffect.js.map +1 -0
- package/dist/commonjs/hooks/push/useIosVoipPushEventsSetupEffect.js +31 -18
- package/dist/commonjs/hooks/push/useIosVoipPushEventsSetupEffect.js.map +1 -1
- package/dist/commonjs/hooks/useAndroidKeepCallAliveEffect.js +97 -64
- package/dist/commonjs/hooks/useAndroidKeepCallAliveEffect.js.map +1 -1
- package/dist/commonjs/index.js +0 -1
- package/dist/commonjs/index.js.map +1 -1
- package/dist/commonjs/modules/call-manager/CallManager.js +0 -26
- package/dist/commonjs/modules/call-manager/CallManager.js.map +1 -1
- package/dist/commonjs/providers/StreamCall/index.js +6 -6
- package/dist/commonjs/providers/StreamCall/index.js.map +1 -1
- package/dist/commonjs/utils/StreamVideoRN/index.js +21 -33
- package/dist/commonjs/utils/StreamVideoRN/index.js.map +1 -1
- package/dist/commonjs/utils/internal/registerSDKGlobals.js +3 -52
- package/dist/commonjs/utils/internal/registerSDKGlobals.js.map +1 -1
- package/dist/commonjs/utils/push/android.js +200 -145
- package/dist/commonjs/utils/push/android.js.map +1 -1
- package/dist/commonjs/utils/push/internal/ios.js +34 -16
- package/dist/commonjs/utils/push/internal/ios.js.map +1 -1
- package/dist/commonjs/utils/push/internal/rxSubjects.js +20 -1
- package/dist/commonjs/utils/push/internal/rxSubjects.js.map +1 -1
- package/dist/commonjs/utils/push/internal/utils.js +1 -17
- package/dist/commonjs/utils/push/internal/utils.js.map +1 -1
- package/dist/commonjs/utils/push/ios.js.map +1 -1
- package/dist/commonjs/utils/push/libs/callkeep.js +17 -0
- package/dist/commonjs/utils/push/libs/callkeep.js.map +1 -0
- package/dist/commonjs/utils/push/libs/index.js +19 -8
- package/dist/commonjs/utils/push/libs/index.js.map +1 -1
- package/dist/commonjs/utils/push/libs/notifee/index.js +19 -0
- package/dist/commonjs/utils/push/libs/notifee/index.js.map +1 -1
- package/dist/commonjs/utils/push/libs/voipPushNotification.js +17 -0
- package/dist/commonjs/utils/push/libs/voipPushNotification.js.map +1 -0
- package/dist/commonjs/utils/push/setupIosCallKeepEvents.js +205 -0
- package/dist/commonjs/utils/push/setupIosCallKeepEvents.js.map +1 -0
- package/dist/commonjs/utils/push/setupIosVoipPushEvents.js +6 -7
- package/dist/commonjs/utils/push/setupIosVoipPushEvents.js.map +1 -1
- package/dist/commonjs/version.js +1 -1
- package/dist/commonjs/version.js.map +1 -1
- package/dist/module/hooks/push/useIosCallkeepWithCallingStateEffect.js +153 -0
- package/dist/module/hooks/push/useIosCallkeepWithCallingStateEffect.js.map +1 -0
- package/dist/module/hooks/push/useIosVoipPushEventsSetupEffect.js +31 -18
- package/dist/module/hooks/push/useIosVoipPushEventsSetupEffect.js.map +1 -1
- package/dist/module/hooks/useAndroidKeepCallAliveEffect.js +99 -66
- package/dist/module/hooks/useAndroidKeepCallAliveEffect.js.map +1 -1
- package/dist/module/index.js +0 -1
- package/dist/module/index.js.map +1 -1
- package/dist/module/modules/call-manager/CallManager.js +0 -26
- package/dist/module/modules/call-manager/CallManager.js.map +1 -1
- package/dist/module/providers/StreamCall/index.js +6 -6
- package/dist/module/providers/StreamCall/index.js.map +1 -1
- package/dist/module/utils/StreamVideoRN/index.js +21 -33
- package/dist/module/utils/StreamVideoRN/index.js.map +1 -1
- package/dist/module/utils/internal/registerSDKGlobals.js +4 -53
- package/dist/module/utils/internal/registerSDKGlobals.js.map +1 -1
- package/dist/module/utils/push/android.js +202 -147
- package/dist/module/utils/push/android.js.map +1 -1
- package/dist/module/utils/push/internal/ios.js +34 -16
- package/dist/module/utils/push/internal/ios.js.map +1 -1
- package/dist/module/utils/push/internal/rxSubjects.js +19 -0
- package/dist/module/utils/push/internal/rxSubjects.js.map +1 -1
- package/dist/module/utils/push/internal/utils.js +0 -14
- package/dist/module/utils/push/internal/utils.js.map +1 -1
- package/dist/module/utils/push/ios.js.map +1 -1
- package/dist/module/utils/push/libs/callkeep.js +11 -0
- package/dist/module/utils/push/libs/callkeep.js.map +1 -0
- package/dist/module/utils/push/libs/index.js +2 -1
- package/dist/module/utils/push/libs/index.js.map +1 -1
- package/dist/module/utils/push/libs/notifee/index.js +18 -0
- package/dist/module/utils/push/libs/notifee/index.js.map +1 -1
- package/dist/module/utils/push/libs/voipPushNotification.js +11 -0
- package/dist/module/utils/push/libs/voipPushNotification.js.map +1 -0
- package/dist/module/utils/push/setupIosCallKeepEvents.js +199 -0
- package/dist/module/utils/push/setupIosCallKeepEvents.js.map +1 -0
- package/dist/module/utils/push/setupIosVoipPushEvents.js +6 -7
- package/dist/module/utils/push/setupIosVoipPushEvents.js.map +1 -1
- package/dist/module/version.js +1 -1
- package/dist/module/version.js.map +1 -1
- package/dist/typescript/hooks/push/useIosCallkeepWithCallingStateEffect.d.ts +5 -0
- package/dist/typescript/hooks/push/useIosCallkeepWithCallingStateEffect.d.ts.map +1 -0
- package/dist/typescript/hooks/push/useIosVoipPushEventsSetupEffect.d.ts.map +1 -1
- package/dist/typescript/hooks/useAndroidKeepCallAliveEffect.d.ts.map +1 -1
- package/dist/typescript/index.d.ts +0 -1
- package/dist/typescript/index.d.ts.map +1 -1
- package/dist/typescript/modules/call-manager/CallManager.d.ts +0 -5
- package/dist/typescript/modules/call-manager/CallManager.d.ts.map +1 -1
- package/dist/typescript/utils/StreamVideoRN/index.d.ts +2 -20
- package/dist/typescript/utils/StreamVideoRN/index.d.ts.map +1 -1
- package/dist/typescript/utils/StreamVideoRN/types.d.ts +29 -54
- package/dist/typescript/utils/StreamVideoRN/types.d.ts.map +1 -1
- package/dist/typescript/utils/internal/registerSDKGlobals.d.ts.map +1 -1
- package/dist/typescript/utils/push/android.d.ts +2 -1
- package/dist/typescript/utils/push/android.d.ts.map +1 -1
- package/dist/typescript/utils/push/internal/ios.d.ts.map +1 -1
- package/dist/typescript/utils/push/internal/rxSubjects.d.ts +12 -0
- package/dist/typescript/utils/push/internal/rxSubjects.d.ts.map +1 -1
- package/dist/typescript/utils/push/internal/utils.d.ts +0 -4
- package/dist/typescript/utils/push/internal/utils.d.ts.map +1 -1
- package/dist/typescript/utils/push/ios.d.ts +2 -1
- package/dist/typescript/utils/push/ios.d.ts.map +1 -1
- package/dist/typescript/utils/push/libs/callkeep.d.ts +3 -0
- package/dist/typescript/utils/push/libs/callkeep.d.ts.map +1 -0
- package/dist/typescript/utils/push/libs/firebaseMessaging/index.d.ts +2 -16
- package/dist/typescript/utils/push/libs/firebaseMessaging/index.d.ts.map +1 -1
- package/dist/typescript/utils/push/libs/index.d.ts +2 -1
- package/dist/typescript/utils/push/libs/index.d.ts.map +1 -1
- package/dist/typescript/utils/push/libs/notifee/index.d.ts +1 -0
- package/dist/typescript/utils/push/libs/notifee/index.d.ts.map +1 -1
- package/dist/typescript/utils/push/libs/voipPushNotification.d.ts +3 -0
- package/dist/typescript/utils/push/libs/voipPushNotification.d.ts.map +1 -0
- package/dist/typescript/utils/push/setupIosCallKeepEvents.d.ts +6 -0
- package/dist/typescript/utils/push/setupIosCallKeepEvents.d.ts.map +1 -0
- package/dist/typescript/utils/push/setupIosVoipPushEvents.d.ts.map +1 -1
- package/dist/typescript/version.d.ts +1 -1
- package/dist/typescript/version.d.ts.map +1 -1
- package/expo-config-plugin/dist/withAndroidManifest.js +33 -1
- package/expo-config-plugin/dist/withAndroidPermissions.js +7 -2
- package/expo-config-plugin/dist/withAppDelegate.js +197 -19
- package/expo-config-plugin/dist/withMainActivity.js +1 -1
- package/expo-config-plugin/dist/withiOSInfoPlist.js +3 -2
- package/ios/StreamInCallManager.m +0 -2
- package/ios/StreamInCallManager.swift +5 -20
- package/ios/StreamVideoReactNative.h +4 -7
- package/ios/StreamVideoReactNative.m +82 -191
- package/package.json +17 -12
- package/src/hooks/push/useIosCallkeepWithCallingStateEffect.ts +235 -0
- package/src/hooks/push/useIosVoipPushEventsSetupEffect.ts +34 -21
- package/src/hooks/useAndroidKeepCallAliveEffect.ts +120 -94
- package/src/index.ts +0 -1
- package/src/modules/call-manager/CallManager.ts +0 -36
- package/src/modules/call-manager/native-module.d.ts +0 -7
- package/src/providers/StreamCall/index.tsx +6 -6
- package/src/utils/StreamVideoRN/index.ts +30 -40
- package/src/utils/StreamVideoRN/types.ts +29 -56
- package/src/utils/internal/registerSDKGlobals.ts +4 -47
- package/src/utils/push/android.ts +308 -223
- package/src/utils/push/internal/ios.ts +46 -25
- package/src/utils/push/internal/rxSubjects.ts +29 -0
- package/src/utils/push/internal/utils.ts +0 -25
- package/src/utils/push/ios.ts +6 -1
- package/src/utils/push/libs/callkeep.ts +16 -0
- package/src/utils/push/libs/index.ts +2 -1
- package/src/utils/push/libs/notifee/index.ts +27 -0
- package/src/utils/push/libs/voipPushNotification.ts +17 -0
- package/src/utils/push/setupIosCallKeepEvents.ts +252 -0
- package/src/utils/push/setupIosVoipPushEvents.ts +7 -11
- package/src/version.ts +1 -1
- package/android/src/main/java/com/streamvideo/reactnative/keepalive/KeepAliveNotification.kt +0 -83
- package/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt +0 -134
- package/dist/commonjs/hooks/push/useCallingExpWithCallingStateEffect.js +0 -216
- package/dist/commonjs/hooks/push/useCallingExpWithCallingStateEffect.js.map +0 -1
- package/dist/commonjs/utils/internal/audioSessionPromise.js +0 -46
- package/dist/commonjs/utils/internal/audioSessionPromise.js.map +0 -1
- package/dist/commonjs/utils/internal/callingx.js +0 -84
- package/dist/commonjs/utils/internal/callingx.js.map +0 -1
- package/dist/commonjs/utils/keepCallAliveHeadlessTask.js +0 -48
- package/dist/commonjs/utils/keepCallAliveHeadlessTask.js.map +0 -1
- package/dist/commonjs/utils/push/libs/callingx.js +0 -75
- package/dist/commonjs/utils/push/libs/callingx.js.map +0 -1
- package/dist/commonjs/utils/push/setupCallingExpEvents.js +0 -97
- package/dist/commonjs/utils/push/setupCallingExpEvents.js.map +0 -1
- package/dist/module/hooks/push/useCallingExpWithCallingStateEffect.js +0 -209
- package/dist/module/hooks/push/useCallingExpWithCallingStateEffect.js.map +0 -1
- package/dist/module/utils/internal/audioSessionPromise.js +0 -39
- package/dist/module/utils/internal/audioSessionPromise.js.map +0 -1
- package/dist/module/utils/internal/callingx.js +0 -75
- package/dist/module/utils/internal/callingx.js.map +0 -1
- package/dist/module/utils/keepCallAliveHeadlessTask.js +0 -42
- package/dist/module/utils/keepCallAliveHeadlessTask.js.map +0 -1
- package/dist/module/utils/push/libs/callingx.js +0 -67
- package/dist/module/utils/push/libs/callingx.js.map +0 -1
- package/dist/module/utils/push/setupCallingExpEvents.js +0 -91
- package/dist/module/utils/push/setupCallingExpEvents.js.map +0 -1
- package/dist/typescript/hooks/push/useCallingExpWithCallingStateEffect.d.ts +0 -5
- package/dist/typescript/hooks/push/useCallingExpWithCallingStateEffect.d.ts.map +0 -1
- package/dist/typescript/utils/internal/audioSessionPromise.d.ts +0 -16
- package/dist/typescript/utils/internal/audioSessionPromise.d.ts.map +0 -1
- package/dist/typescript/utils/internal/callingx.d.ts +0 -13
- package/dist/typescript/utils/internal/callingx.d.ts.map +0 -1
- package/dist/typescript/utils/keepCallAliveHeadlessTask.d.ts +0 -10
- package/dist/typescript/utils/keepCallAliveHeadlessTask.d.ts.map +0 -1
- package/dist/typescript/utils/push/libs/callingx.d.ts +0 -9
- package/dist/typescript/utils/push/libs/callingx.d.ts.map +0 -1
- package/dist/typescript/utils/push/setupCallingExpEvents.d.ts +0 -8
- package/dist/typescript/utils/push/setupCallingExpEvents.d.ts.map +0 -1
- package/src/hooks/push/useCallingExpWithCallingStateEffect.ts +0 -307
- package/src/utils/internal/audioSessionPromise.ts +0 -39
- package/src/utils/internal/callingx.ts +0 -108
- package/src/utils/keepCallAliveHeadlessTask.ts +0 -54
- package/src/utils/push/libs/callingx.ts +0 -90
- package/src/utils/push/setupCallingExpEvents.ts +0 -117
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { Platform } from 'react-native';
|
|
1
|
+
import { AppState, NativeModules, Platform } from 'react-native';
|
|
2
|
+
import { getCallKeepLib, getVoipPushNotificationLib } from '../libs';
|
|
3
|
+
import { voipPushNotificationCallCId$ } from './rxSubjects';
|
|
2
4
|
import { pushUnsubscriptionCallbacks } from './constants';
|
|
3
|
-
import {
|
|
5
|
+
import { canAddPushWSSubscriptionsRef, shouldCallBeEnded } from './utils';
|
|
4
6
|
import { StreamVideoConfig } from '../../StreamVideoRN/types';
|
|
5
7
|
import { videoLoggerSystem } from '@stream-io/video-client';
|
|
6
|
-
import { getCallingxLib } from '../libs/callingx';
|
|
7
8
|
|
|
8
9
|
export const onVoipNotificationReceived = async (
|
|
9
10
|
notification: any,
|
|
@@ -31,51 +32,71 @@ export const onVoipNotificationReceived = async (
|
|
|
31
32
|
"version": "v2"
|
|
32
33
|
}
|
|
33
34
|
} */
|
|
34
|
-
const logger = videoLoggerSystem.getLogger('setupIosVoipPushEvents');
|
|
35
|
-
|
|
36
35
|
const sender = notification?.stream?.sender;
|
|
37
36
|
const type = notification?.stream?.type;
|
|
38
37
|
// do not process any other notifications other than stream.video or ringing
|
|
39
38
|
if (sender !== 'stream.video' && type !== 'call.ring') {
|
|
40
39
|
return;
|
|
41
40
|
}
|
|
42
|
-
|
|
43
41
|
const call_cid = notification?.stream?.call_cid;
|
|
44
42
|
if (!call_cid || Platform.OS !== 'ios' || !pushConfig.ios.pushProviderName) {
|
|
45
43
|
return;
|
|
46
44
|
}
|
|
47
|
-
|
|
48
|
-
const callingx = getCallingxLib();
|
|
49
|
-
if (callingx.isCallRegistered(call_cid)) {
|
|
50
|
-
//same call_cid is registered, so we skipping the notification
|
|
51
|
-
logger.debug(
|
|
52
|
-
`the same call_cid ${call_cid} is registered, skipping the call.ring notification`,
|
|
53
|
-
);
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
45
|
+
const logger = videoLoggerSystem.getLogger('setupIosVoipPushEvents');
|
|
57
46
|
const client = await pushConfig.createStreamVideoClient();
|
|
47
|
+
|
|
58
48
|
if (!client) {
|
|
59
49
|
logger.debug(
|
|
60
50
|
'client not found, not processing call.ring voip push notification',
|
|
61
51
|
);
|
|
62
52
|
return;
|
|
63
53
|
}
|
|
64
|
-
|
|
54
|
+
const shouldRejectCallWhenBusy = client['rejectCallWhenBusy'] ?? false;
|
|
55
|
+
if (shouldRejectCallWhenBusy) {
|
|
56
|
+
// inform the iOS native module that we should reject call when busy
|
|
57
|
+
NativeModules.StreamVideoReactNative.setShouldRejectCallWhenBusy(
|
|
58
|
+
shouldRejectCallWhenBusy,
|
|
59
|
+
);
|
|
60
|
+
}
|
|
65
61
|
const callFromPush = await client.onRingingCall(call_cid);
|
|
66
|
-
|
|
62
|
+
let uuid = '';
|
|
63
|
+
try {
|
|
64
|
+
uuid =
|
|
65
|
+
await NativeModules?.StreamVideoReactNative?.getIncomingCallUUid(
|
|
66
|
+
call_cid,
|
|
67
|
+
);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
logger.error('Error in getting call uuid from native module', error);
|
|
70
|
+
}
|
|
71
|
+
if (!uuid) {
|
|
72
|
+
logger.error(
|
|
73
|
+
`Not processing call.ring push notification, as no uuid found for call_cid: ${call_cid}`,
|
|
74
|
+
);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const created_by_id = notification?.stream?.created_by_id;
|
|
78
|
+
const receiver_id = notification?.stream?.receiver_id;
|
|
67
79
|
function closeCallIfNecessary() {
|
|
68
|
-
const mustEndCall =
|
|
80
|
+
const { mustEndCall, callkeepReason } = shouldCallBeEnded(
|
|
81
|
+
callFromPush,
|
|
82
|
+
created_by_id,
|
|
83
|
+
receiver_id,
|
|
84
|
+
);
|
|
69
85
|
if (mustEndCall) {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
86
|
+
const callkeep = getCallKeepLib();
|
|
87
|
+
logger.debug(
|
|
88
|
+
`callkeep.reportEndCallWithUUID for uuid: ${uuid}, call_cid: ${call_cid}, reason: ${callkeepReason}`,
|
|
89
|
+
);
|
|
90
|
+
callkeep.reportEndCallWithUUID(uuid, callkeepReason);
|
|
91
|
+
const voipPushNotification = getVoipPushNotificationLib();
|
|
92
|
+
voipPushNotification.onVoipNotificationCompleted(uuid);
|
|
73
93
|
return true;
|
|
74
94
|
}
|
|
75
95
|
return false;
|
|
76
96
|
}
|
|
77
|
-
|
|
78
97
|
const closed = closeCallIfNecessary();
|
|
98
|
+
const canListenToWS = () =>
|
|
99
|
+
canAddPushWSSubscriptionsRef.current && AppState.currentState !== 'active';
|
|
79
100
|
if (!closed && canListenToWS()) {
|
|
80
101
|
const unsubscribe = callFromPush.on('all', (event) => {
|
|
81
102
|
const _canListenToWS = canListenToWS();
|
|
@@ -100,10 +121,10 @@ export const onVoipNotificationReceived = async (
|
|
|
100
121
|
pushUnsubscriptionCallbacks.get(call_cid)?.forEach((cb) => cb());
|
|
101
122
|
pushUnsubscriptionCallbacks.set(call_cid, [unsubscribe]);
|
|
102
123
|
}
|
|
103
|
-
|
|
104
124
|
// send the info to this subject, it is listened by callkeep events
|
|
105
125
|
// callkeep events will then accept/reject the call
|
|
106
126
|
logger.debug(
|
|
107
|
-
`call_cid:${call_cid} received and processed from call.ring push notification`,
|
|
127
|
+
`call_cid:${call_cid} uuid:${uuid} received and processed from call.ring push notification`,
|
|
108
128
|
);
|
|
129
|
+
voipPushNotificationCallCId$.next(call_cid);
|
|
109
130
|
};
|
|
@@ -40,3 +40,32 @@ export const pushAndroidBackgroundDeliveredIncomingCallCId$ =
|
|
|
40
40
|
export const pushRejectedIncomingCallCId$ = new BehaviorSubject<
|
|
41
41
|
string | undefined
|
|
42
42
|
>(undefined);
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* This rxjs subject is used to store the call cid of the incoming call from ios voip pushkit notification
|
|
46
|
+
*/
|
|
47
|
+
export const voipPushNotificationCallCId$ = new BehaviorSubject<
|
|
48
|
+
string | undefined
|
|
49
|
+
>(undefined);
|
|
50
|
+
|
|
51
|
+
/** The pair of cid of a call and its corresponding uuid created in the native side */
|
|
52
|
+
type CallkeepMap = {
|
|
53
|
+
uuid: string;
|
|
54
|
+
cid: string;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* This rxjs subject should only used to store the CallkeepMap
|
|
59
|
+
* for the incoming call when on foreground
|
|
60
|
+
* or in other words, when we get didDisplayIncomingCall from callkeep lib
|
|
61
|
+
*/
|
|
62
|
+
export const voipCallkeepCallOnForegroundMap$ = new BehaviorSubject<
|
|
63
|
+
CallkeepMap | undefined
|
|
64
|
+
>(undefined);
|
|
65
|
+
|
|
66
|
+
/*
|
|
67
|
+
* This rxjs subject should only used to store the CallkeepMap when it was accepted in the native dialer
|
|
68
|
+
*/
|
|
69
|
+
export const voipCallkeepAcceptedCallOnNativeDialerMap$ = new BehaviorSubject<
|
|
70
|
+
CallkeepMap | undefined
|
|
71
|
+
>(undefined);
|
|
@@ -10,7 +10,6 @@ import type {
|
|
|
10
10
|
} from '../../StreamVideoRN/types';
|
|
11
11
|
import { onNewCallNotification } from '../../internal/newNotificationCallbacks';
|
|
12
12
|
import { pushUnsubscriptionCallbacks } from './constants';
|
|
13
|
-
import { AppState } from 'react-native';
|
|
14
13
|
|
|
15
14
|
type PushConfig = NonNullable<StreamVideoConfig['push']>;
|
|
16
15
|
|
|
@@ -123,16 +122,6 @@ export const processCallFromPush = async (
|
|
|
123
122
|
.debug(
|
|
124
123
|
`joining call from push notification with callCid: ${callFromPush.cid}`,
|
|
125
124
|
);
|
|
126
|
-
|
|
127
|
-
if (callFromPush.state.callingState === CallingState.JOINED) {
|
|
128
|
-
videoLoggerSystem
|
|
129
|
-
.getLogger('processCallFromPush')
|
|
130
|
-
.debug(
|
|
131
|
-
`call already joined from push notification with callCid: ${callFromPush.cid}`,
|
|
132
|
-
);
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
125
|
await callFromPush.join();
|
|
137
126
|
} else if (action === 'decline') {
|
|
138
127
|
const canReject =
|
|
@@ -202,17 +191,3 @@ export const clearPushWSEventSubscriptions = (call_cid: string) => {
|
|
|
202
191
|
export const canAddPushWSSubscriptionsRef: CanAddPushWSSubscriptionsRef = {
|
|
203
192
|
current: true,
|
|
204
193
|
};
|
|
205
|
-
|
|
206
|
-
export const canListenToWS = () =>
|
|
207
|
-
canAddPushWSSubscriptionsRef.current && AppState.currentState !== 'active';
|
|
208
|
-
|
|
209
|
-
export const shouldCallBeClosed = (
|
|
210
|
-
call: Call,
|
|
211
|
-
pushData: { [key: string]: string | object },
|
|
212
|
-
) => {
|
|
213
|
-
const created_by_id = pushData?.created_by_id as string;
|
|
214
|
-
const receiver_id = pushData?.receiver_id as string;
|
|
215
|
-
|
|
216
|
-
const { mustEndCall } = shouldCallBeEnded(call, created_by_id, receiver_id);
|
|
217
|
-
return mustEndCall;
|
|
218
|
-
};
|
package/src/utils/push/ios.ts
CHANGED
|
@@ -62,7 +62,12 @@ export const oniOSExpoNotificationEvent = (event: ExpoNotification) => {
|
|
|
62
62
|
}
|
|
63
63
|
};
|
|
64
64
|
|
|
65
|
-
export const oniOSNotifeeEvent = ({
|
|
65
|
+
export const oniOSNotifeeEvent = ({
|
|
66
|
+
event,
|
|
67
|
+
}: {
|
|
68
|
+
event: Event;
|
|
69
|
+
isBackground: boolean;
|
|
70
|
+
}) => {
|
|
66
71
|
if (Platform.OS !== 'ios') return;
|
|
67
72
|
const pushConfig = StreamVideoRN.getConfig().push;
|
|
68
73
|
const { type, detail } = event;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type RNCallKeepType = typeof import('react-native-callkeep').default;
|
|
2
|
+
|
|
3
|
+
let callkeep: RNCallKeepType | undefined;
|
|
4
|
+
|
|
5
|
+
try {
|
|
6
|
+
callkeep = require('react-native-callkeep').default;
|
|
7
|
+
} catch {}
|
|
8
|
+
|
|
9
|
+
export function getCallKeepLib() {
|
|
10
|
+
if (!callkeep) {
|
|
11
|
+
throw Error(
|
|
12
|
+
'react-native-callkeep library is not installed. Please see https://github.com/react-native-webrtc/react-native-callkeep#Installation for installation instructions',
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
return callkeep;
|
|
16
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export * from './expoNotifications';
|
|
2
2
|
export * from './firebaseMessaging';
|
|
3
3
|
export * from './iosPushNotification';
|
|
4
|
+
export * from './voipPushNotification';
|
|
5
|
+
export * from './callkeep';
|
|
4
6
|
export * from './notifee';
|
|
5
|
-
export * from './callingx';
|
|
6
7
|
|
|
7
8
|
/*
|
|
8
9
|
NOTE: must keep each libs in different files
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { PermissionsAndroid } from 'react-native';
|
|
1
2
|
import { lib, type Type } from './lib';
|
|
3
|
+
import { videoLoggerSystem } from '@stream-io/video-client';
|
|
2
4
|
|
|
3
5
|
export type NotifeeLib = Type;
|
|
4
6
|
|
|
@@ -34,9 +36,34 @@ export function getNotifeeLibThrowIfNotInstalledForPush() {
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export function getNotifeeLibNoThrowForKeepCallAlive() {
|
|
39
|
+
if (!lib) {
|
|
40
|
+
const logger = videoLoggerSystem.getLogger('getNotifeeLibNoThrow');
|
|
41
|
+
logger.info(
|
|
42
|
+
`${'@notifee/react-native library not installed. It is required to keep call alive in the background for Android. '}${INSTALLATION_INSTRUCTION}`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
37
45
|
return lib;
|
|
38
46
|
}
|
|
39
47
|
|
|
48
|
+
export async function getKeepCallAliveForegroundServiceTypes() {
|
|
49
|
+
const types: AndroidForegroundServiceType[] = [
|
|
50
|
+
AndroidForegroundServiceType.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK,
|
|
51
|
+
];
|
|
52
|
+
const hasCameraPermission = await PermissionsAndroid.check(
|
|
53
|
+
PermissionsAndroid.PERMISSIONS.CAMERA!,
|
|
54
|
+
);
|
|
55
|
+
if (hasCameraPermission) {
|
|
56
|
+
types.push(AndroidForegroundServiceType.FOREGROUND_SERVICE_TYPE_CAMERA);
|
|
57
|
+
}
|
|
58
|
+
const hasMicrophonePermission = await PermissionsAndroid.check(
|
|
59
|
+
PermissionsAndroid.PERMISSIONS.RECORD_AUDIO!,
|
|
60
|
+
);
|
|
61
|
+
if (hasMicrophonePermission) {
|
|
62
|
+
types.push(AndroidForegroundServiceType.FOREGROUND_SERVICE_TYPE_MICROPHONE);
|
|
63
|
+
}
|
|
64
|
+
return types;
|
|
65
|
+
}
|
|
66
|
+
|
|
40
67
|
export function getIncomingCallForegroundServiceTypes() {
|
|
41
68
|
const types: AndroidForegroundServiceType[] = [
|
|
42
69
|
AndroidForegroundServiceType.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type VoipPushNotificationType =
|
|
2
|
+
typeof import('react-native-voip-push-notification').default;
|
|
3
|
+
|
|
4
|
+
let voipPushNotification: VoipPushNotificationType | undefined;
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
voipPushNotification = require('react-native-voip-push-notification').default;
|
|
8
|
+
} catch {}
|
|
9
|
+
|
|
10
|
+
export function getVoipPushNotificationLib() {
|
|
11
|
+
if (!voipPushNotification) {
|
|
12
|
+
throw Error(
|
|
13
|
+
"react-native-voip-push-notification library is not installed. Please install it using 'yarn add react-native-voip-push-notification' or 'npm i react-native-voip-push-notification --save'",
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
return voipPushNotification;
|
|
17
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import {
|
|
2
|
+
pushAcceptedIncomingCallCId$,
|
|
3
|
+
voipCallkeepAcceptedCallOnNativeDialerMap$,
|
|
4
|
+
voipCallkeepCallOnForegroundMap$,
|
|
5
|
+
voipPushNotificationCallCId$,
|
|
6
|
+
} from './internal/rxSubjects';
|
|
7
|
+
import { RxUtils, videoLoggerSystem } from '@stream-io/video-client';
|
|
8
|
+
import { getCallKeepLib, getVoipPushNotificationLib } from './libs';
|
|
9
|
+
import type { StreamVideoConfig } from '../StreamVideoRN/types';
|
|
10
|
+
import {
|
|
11
|
+
clearPushWSEventSubscriptions,
|
|
12
|
+
processCallFromPushInBackground,
|
|
13
|
+
} from './internal/utils';
|
|
14
|
+
import { AppState, NativeModules, Platform } from 'react-native';
|
|
15
|
+
import { RTCAudioSession } from '@stream-io/react-native-webrtc';
|
|
16
|
+
import { setPushLogoutCallback } from '../internal/pushLogoutCallback';
|
|
17
|
+
|
|
18
|
+
type PushConfig = NonNullable<StreamVideoConfig['push']>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* This hook is used to listen to callkeep events and do the necessary actions
|
|
22
|
+
*/
|
|
23
|
+
export function setupIosCallKeepEvents(
|
|
24
|
+
pushConfig: NonNullable<StreamVideoConfig['push']>,
|
|
25
|
+
) {
|
|
26
|
+
if (Platform.OS !== 'ios' || !pushConfig.ios.pushProviderName) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (!pushConfig.android.incomingCallChannel) {
|
|
30
|
+
// TODO: remove this check and find a better way once we have telecom integration for android
|
|
31
|
+
videoLoggerSystem
|
|
32
|
+
.getLogger('setupIosCallKeepEvents')
|
|
33
|
+
.debug(
|
|
34
|
+
'android incomingCallChannel is not defined, so skipping the setupIosCallKeepEvents',
|
|
35
|
+
);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const logger = videoLoggerSystem.getLogger('setupIosCallKeepEvents');
|
|
39
|
+
const callkeep = getCallKeepLib();
|
|
40
|
+
|
|
41
|
+
async function getCallCid(callUUID: string): Promise<string | undefined> {
|
|
42
|
+
try {
|
|
43
|
+
const call_cid =
|
|
44
|
+
await NativeModules.StreamVideoReactNative.getIncomingCallCid(callUUID);
|
|
45
|
+
// in a case that voipPushNotificationCallCId$ is empty (this should not happen as voipPushNotificationCallCId$ is updated in push reception)]
|
|
46
|
+
// update it with this call_cid
|
|
47
|
+
const voipPushNotificationCallCId = RxUtils.getCurrentValue(
|
|
48
|
+
voipPushNotificationCallCId$,
|
|
49
|
+
);
|
|
50
|
+
if (!voipPushNotificationCallCId) {
|
|
51
|
+
logger.debug(
|
|
52
|
+
`voipPushNotificationCallCId$ is empty, updating it with the call_cid: ${call_cid} for callUUID: ${callUUID}`,
|
|
53
|
+
);
|
|
54
|
+
voipPushNotificationCallCId$.next(call_cid);
|
|
55
|
+
}
|
|
56
|
+
return call_cid;
|
|
57
|
+
} catch {
|
|
58
|
+
logger.debug(
|
|
59
|
+
`Error in getting call cid from native module for callUUID: ${callUUID} - probably the call was already processed, so ignoring this callkeep event`,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function answerCall(callUUID: string) {
|
|
66
|
+
getCallCid(callUUID).then((call_cid) => {
|
|
67
|
+
logger.debug(`answerCall event with call_cid: ${call_cid}`);
|
|
68
|
+
iosCallkeepAcceptCall(call_cid, callUUID);
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function endCall(callUUID: string) {
|
|
73
|
+
getCallCid(callUUID).then((call_cid) => {
|
|
74
|
+
logger.debug(`endCall event with call_cid: ${call_cid}`);
|
|
75
|
+
iosCallkeepRejectCall(call_cid, callUUID, pushConfig!);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* CallKeep / CallKit audio-session events -> WebRTC (iOS)
|
|
81
|
+
*
|
|
82
|
+
* iOS CallKit is the authority that *activates* and *deactivates* the underlying `AVAudioSession`
|
|
83
|
+
* when a call is answered/ended from the system UI (lock screen, Call UI, Bluetooth, etc).
|
|
84
|
+
*
|
|
85
|
+
* WebRTC on iOS wraps `AVAudioSession` with `RTCAudioSession` and its AudioDeviceModule relies on
|
|
86
|
+
* being notified of those lifecycle transitions to correctly start/stop audio I/O and keep its
|
|
87
|
+
* internal activation state consistent (e.g. activation count, playout/recording start).
|
|
88
|
+
*
|
|
89
|
+
* If these callbacks don’t reach WebRTC, answering via the native dialer UI can result in:
|
|
90
|
+
* - no microphone capture / one-way audio
|
|
91
|
+
* - silent playout until the app forces an audio reconfiguration
|
|
92
|
+
* - flaky audio routing (speaker/earpiece/Bluetooth) across subsequent calls
|
|
93
|
+
*
|
|
94
|
+
* We forward CallKeep’s `didActivateAudioSession` / `didDeactivateAudioSession` events to WebRTC’s
|
|
95
|
+
* `RTCAudioSession.audioSessionDidActivate()` / `audioSessionDidDeactivate()` methods.
|
|
96
|
+
*/
|
|
97
|
+
function didActivateAudioSession() {
|
|
98
|
+
logger.debug('didActivateAudioSession');
|
|
99
|
+
RTCAudioSession.audioSessionDidActivate();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function didDeactivateAudioSession() {
|
|
103
|
+
logger.debug('didDeactivateAudioSession');
|
|
104
|
+
RTCAudioSession.audioSessionDidDeactivate();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function didDisplayIncomingCall(callUUID: string, payload: object) {
|
|
108
|
+
const voipPushNotification = getVoipPushNotificationLib();
|
|
109
|
+
// @ts-expect-error - call_cid is not part of RNCallKeepEventPayload
|
|
110
|
+
const call_cid = payload?.call_cid as string | undefined;
|
|
111
|
+
logger.debug(
|
|
112
|
+
`didDisplayIncomingCall event with callUUID: ${callUUID} call_cid: ${call_cid}`,
|
|
113
|
+
);
|
|
114
|
+
if (call_cid) {
|
|
115
|
+
if (AppState.currentState === 'background') {
|
|
116
|
+
processCallFromPushInBackground(
|
|
117
|
+
pushConfig!,
|
|
118
|
+
call_cid,
|
|
119
|
+
'backgroundDelivered',
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
voipCallkeepCallOnForegroundMap$.next({
|
|
123
|
+
uuid: callUUID,
|
|
124
|
+
cid: call_cid,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
voipPushNotification.onVoipNotificationCompleted(callUUID);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const { remove: removeAnswerCall } = callkeep.addEventListener(
|
|
131
|
+
'answerCall',
|
|
132
|
+
({ callUUID }) => {
|
|
133
|
+
answerCall(callUUID);
|
|
134
|
+
},
|
|
135
|
+
);
|
|
136
|
+
const { remove: removeEndCall } = callkeep.addEventListener(
|
|
137
|
+
'endCall',
|
|
138
|
+
({ callUUID }) => {
|
|
139
|
+
endCall(callUUID);
|
|
140
|
+
},
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const { remove: removeDisplayIncomingCall } = callkeep.addEventListener(
|
|
144
|
+
'didDisplayIncomingCall',
|
|
145
|
+
({ callUUID, payload }) => {
|
|
146
|
+
didDisplayIncomingCall(callUUID, payload);
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
const { remove: removeDidActivateAudioSession } = callkeep.addEventListener(
|
|
151
|
+
'didActivateAudioSession',
|
|
152
|
+
() => {
|
|
153
|
+
didActivateAudioSession();
|
|
154
|
+
},
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const { remove: removeDidDeactivateAudioSession } = callkeep.addEventListener(
|
|
158
|
+
'didDeactivateAudioSession',
|
|
159
|
+
() => {
|
|
160
|
+
didDeactivateAudioSession();
|
|
161
|
+
},
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const { remove: removeDidLoadWithEvents } = callkeep.addEventListener(
|
|
165
|
+
'didLoadWithEvents',
|
|
166
|
+
(events) => {
|
|
167
|
+
if (!events || !Array.isArray(events) || events.length < 1) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
events.forEach((event) => {
|
|
172
|
+
const { name, data } = event;
|
|
173
|
+
if (name === 'RNCallKeepDidDisplayIncomingCall') {
|
|
174
|
+
didDisplayIncomingCall(data.callUUID, data.payload);
|
|
175
|
+
} else if (name === 'RNCallKeepPerformAnswerCallAction') {
|
|
176
|
+
answerCall(data.callUUID);
|
|
177
|
+
} else if (name === 'RNCallKeepPerformEndCallAction') {
|
|
178
|
+
endCall(data.callUUID);
|
|
179
|
+
} else if (name === 'RNCallKeepDidActivateAudioSession') {
|
|
180
|
+
didActivateAudioSession();
|
|
181
|
+
} else if (name === 'RNCallKeepDidDeactivateAudioSession') {
|
|
182
|
+
didDeactivateAudioSession();
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
setPushLogoutCallback(async () => {
|
|
189
|
+
removeAnswerCall();
|
|
190
|
+
removeEndCall();
|
|
191
|
+
removeDisplayIncomingCall();
|
|
192
|
+
removeDidActivateAudioSession();
|
|
193
|
+
removeDidDeactivateAudioSession();
|
|
194
|
+
removeDidLoadWithEvents();
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const iosCallkeepAcceptCall = (
|
|
199
|
+
call_cid: string | undefined,
|
|
200
|
+
callUUIDFromCallkeep: string,
|
|
201
|
+
) => {
|
|
202
|
+
if (!shouldProcessCallFromCallkeep(call_cid, callUUIDFromCallkeep)) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
clearPushWSEventSubscriptions(call_cid);
|
|
206
|
+
// to call end callkeep later if ended in app and not through callkeep
|
|
207
|
+
voipCallkeepAcceptedCallOnNativeDialerMap$.next({
|
|
208
|
+
uuid: callUUIDFromCallkeep,
|
|
209
|
+
cid: call_cid,
|
|
210
|
+
});
|
|
211
|
+
// to process the call in the app
|
|
212
|
+
pushAcceptedIncomingCallCId$.next(call_cid);
|
|
213
|
+
// no need to keep these references anymore
|
|
214
|
+
voipCallkeepCallOnForegroundMap$.next(undefined);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const iosCallkeepRejectCall = async (
|
|
218
|
+
call_cid: string | undefined,
|
|
219
|
+
callUUIDFromCallkeep: string,
|
|
220
|
+
pushConfig: PushConfig,
|
|
221
|
+
) => {
|
|
222
|
+
if (!shouldProcessCallFromCallkeep(call_cid, callUUIDFromCallkeep)) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
clearPushWSEventSubscriptions(call_cid);
|
|
226
|
+
// remove the references if the call_cid matches
|
|
227
|
+
const voipPushNotificationCallCId = RxUtils.getCurrentValue(
|
|
228
|
+
voipPushNotificationCallCId$,
|
|
229
|
+
);
|
|
230
|
+
if (voipPushNotificationCallCId === call_cid) {
|
|
231
|
+
voipCallkeepAcceptedCallOnNativeDialerMap$.next(undefined);
|
|
232
|
+
voipCallkeepCallOnForegroundMap$.next(undefined);
|
|
233
|
+
voipPushNotificationCallCId$.next(undefined);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
await processCallFromPushInBackground(pushConfig, call_cid, 'decline');
|
|
237
|
+
await NativeModules.StreamVideoReactNative?.removeIncomingCall(call_cid);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Helper function to determine if the answer/end call event from callkeep must be processed
|
|
242
|
+
* Just checks if we have a valid call_cid and acts as a type guard for call_cid
|
|
243
|
+
*/
|
|
244
|
+
const shouldProcessCallFromCallkeep = (
|
|
245
|
+
call_cid: string | undefined,
|
|
246
|
+
callUUIDFromCallkeep: string,
|
|
247
|
+
): call_cid is string => {
|
|
248
|
+
if (!call_cid || !callUUIDFromCallkeep) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
return true;
|
|
252
|
+
};
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import { getVoipPushNotificationLib } from './libs';
|
|
2
2
|
|
|
3
3
|
import { Platform } from 'react-native';
|
|
4
4
|
import { onVoipNotificationReceived } from './internal/ios';
|
|
5
5
|
import { setPushLogoutCallback } from '../internal/pushLogoutCallback';
|
|
6
6
|
import { StreamVideoConfig } from '../StreamVideoRN/types';
|
|
7
7
|
import { videoLoggerSystem } from '@stream-io/video-client';
|
|
8
|
-
import { getCallingxLib } from './libs';
|
|
9
8
|
|
|
10
9
|
export function setupIosVoipPushEvents(
|
|
11
10
|
pushConfig: NonNullable<StreamVideoConfig['push']>,
|
|
@@ -21,19 +20,16 @@ export function setupIosVoipPushEvents(
|
|
|
21
20
|
);
|
|
22
21
|
return;
|
|
23
22
|
}
|
|
23
|
+
const voipPushNotification = getVoipPushNotificationLib();
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
onVoipNotificationReceived(params, pushConfig);
|
|
30
|
-
},
|
|
31
|
-
);
|
|
32
|
-
|
|
25
|
+
logger.debug('notification event listener added');
|
|
26
|
+
voipPushNotification.addEventListener('notification', (notification) => {
|
|
27
|
+
onVoipNotificationReceived(notification, pushConfig);
|
|
28
|
+
});
|
|
33
29
|
setPushLogoutCallback(async () => {
|
|
34
30
|
videoLoggerSystem
|
|
35
31
|
.getLogger('setPushLogoutCallback')
|
|
36
32
|
.debug('notification event listener removed');
|
|
37
|
-
|
|
33
|
+
voipPushNotification.removeEventListener('notification');
|
|
38
34
|
});
|
|
39
35
|
}
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = '1.29.0
|
|
1
|
+
export const version = '1.29.0';
|
package/android/src/main/java/com/streamvideo/reactnative/keepalive/KeepAliveNotification.kt
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
package com.streamvideo.reactnative.keepalive
|
|
2
|
-
|
|
3
|
-
import android.app.Notification
|
|
4
|
-
import android.app.NotificationChannel
|
|
5
|
-
import android.app.NotificationManager
|
|
6
|
-
import android.app.PendingIntent
|
|
7
|
-
import android.content.Context
|
|
8
|
-
import android.content.Intent
|
|
9
|
-
import android.content.pm.PackageManager
|
|
10
|
-
import android.os.Build
|
|
11
|
-
import androidx.core.app.NotificationCompat
|
|
12
|
-
|
|
13
|
-
internal object KeepAliveNotification {
|
|
14
|
-
private const val DEFAULT_CHANNEL_DESCRIPTION = "Stream call keep-alive"
|
|
15
|
-
|
|
16
|
-
fun ensureChannel(
|
|
17
|
-
context: Context,
|
|
18
|
-
channelId: String,
|
|
19
|
-
channelName: String
|
|
20
|
-
) {
|
|
21
|
-
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return
|
|
22
|
-
val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
23
|
-
val existing = manager.getNotificationChannel(channelId)
|
|
24
|
-
if (existing != null) return
|
|
25
|
-
|
|
26
|
-
val channel = NotificationChannel(
|
|
27
|
-
channelId,
|
|
28
|
-
channelName,
|
|
29
|
-
NotificationManager.IMPORTANCE_LOW
|
|
30
|
-
).apply {
|
|
31
|
-
description = DEFAULT_CHANNEL_DESCRIPTION
|
|
32
|
-
setShowBadge(false)
|
|
33
|
-
}
|
|
34
|
-
manager.createNotificationChannel(channel)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
fun buildOngoingNotification(
|
|
38
|
-
context: Context,
|
|
39
|
-
channelId: String,
|
|
40
|
-
title: String,
|
|
41
|
-
body: String,
|
|
42
|
-
smallIconName: String?
|
|
43
|
-
): Notification {
|
|
44
|
-
val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)
|
|
45
|
-
val pendingIntentFlags =
|
|
46
|
-
PendingIntent.FLAG_UPDATE_CURRENT or
|
|
47
|
-
PendingIntent.FLAG_IMMUTABLE
|
|
48
|
-
val contentIntent = if (launchIntent != null) {
|
|
49
|
-
PendingIntent.getActivity(context, 0, launchIntent, pendingIntentFlags)
|
|
50
|
-
} else {
|
|
51
|
-
// Fallback: empty intent to avoid crash if launch activity is missing for some reason
|
|
52
|
-
PendingIntent.getActivity(context, 0, Intent(), pendingIntentFlags)
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
val iconResId = resolveSmallIconResId(context, smallIconName)
|
|
56
|
-
return NotificationCompat.Builder(context, channelId)
|
|
57
|
-
.setContentTitle(title)
|
|
58
|
-
.setContentText(body)
|
|
59
|
-
.setOngoing(true)
|
|
60
|
-
.setOnlyAlertOnce(true)
|
|
61
|
-
.setCategory(NotificationCompat.CATEGORY_CALL)
|
|
62
|
-
.setContentIntent(contentIntent)
|
|
63
|
-
.setSmallIcon(iconResId)
|
|
64
|
-
.build()
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
private fun resolveSmallIconResId(context: Context, smallIconName: String?): Int {
|
|
68
|
-
val resources = context.resources
|
|
69
|
-
val packageName = context.packageName
|
|
70
|
-
if (!smallIconName.isNullOrBlank()) {
|
|
71
|
-
val id = resources.getIdentifier(smallIconName, "drawable", packageName)
|
|
72
|
-
if (id != 0) return id
|
|
73
|
-
}
|
|
74
|
-
// Default to the app icon
|
|
75
|
-
return try {
|
|
76
|
-
val appInfo = context.packageManager.getApplicationInfo(packageName, 0)
|
|
77
|
-
appInfo.icon
|
|
78
|
-
} catch (_: PackageManager.NameNotFoundException) {
|
|
79
|
-
android.R.drawable.ic_dialog_info
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|