@stream-io/video-react-native-sdk 1.45.0 → 1.45.2

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.
Files changed (55) hide show
  1. package/CHANGELOG.md +31 -7
  2. package/android/build.gradle +7 -1
  3. package/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt +110 -58
  4. package/android/src/main/java/com/streamvideo/reactnative/audio/CommunicationModeKeepAlive.kt +247 -0
  5. package/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt +16 -5
  6. package/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt +112 -26
  7. package/android/src/main/java/com/streamvideo/reactnative/util/CallAlivePermissionsHelper.kt +68 -37
  8. package/dist/commonjs/hooks/push/useCallingExpWithCallingStateEffect.js +3 -1
  9. package/dist/commonjs/hooks/push/useCallingExpWithCallingStateEffect.js.map +1 -1
  10. package/dist/commonjs/hooks/useAndroidKeepCallAliveEffect.js +38 -11
  11. package/dist/commonjs/hooks/useAndroidKeepCallAliveEffect.js.map +1 -1
  12. package/dist/commonjs/utils/StreamVideoRN/index.js +23 -0
  13. package/dist/commonjs/utils/StreamVideoRN/index.js.map +1 -1
  14. package/dist/commonjs/utils/internal/callingx/callingx.js +37 -55
  15. package/dist/commonjs/utils/internal/callingx/callingx.js.map +1 -1
  16. package/dist/commonjs/utils/keepCallAliveHeadlessTask.js +43 -4
  17. package/dist/commonjs/utils/keepCallAliveHeadlessTask.js.map +1 -1
  18. package/dist/commonjs/utils/push/internal/android.js +28 -10
  19. package/dist/commonjs/utils/push/internal/android.js.map +1 -1
  20. package/dist/commonjs/version.js +1 -1
  21. package/dist/module/hooks/push/useCallingExpWithCallingStateEffect.js +3 -1
  22. package/dist/module/hooks/push/useCallingExpWithCallingStateEffect.js.map +1 -1
  23. package/dist/module/hooks/useAndroidKeepCallAliveEffect.js +39 -12
  24. package/dist/module/hooks/useAndroidKeepCallAliveEffect.js.map +1 -1
  25. package/dist/module/utils/StreamVideoRN/index.js +23 -0
  26. package/dist/module/utils/StreamVideoRN/index.js.map +1 -1
  27. package/dist/module/utils/internal/callingx/callingx.js +37 -55
  28. package/dist/module/utils/internal/callingx/callingx.js.map +1 -1
  29. package/dist/module/utils/keepCallAliveHeadlessTask.js +41 -3
  30. package/dist/module/utils/keepCallAliveHeadlessTask.js.map +1 -1
  31. package/dist/module/utils/push/internal/android.js +28 -10
  32. package/dist/module/utils/push/internal/android.js.map +1 -1
  33. package/dist/module/version.js +1 -1
  34. package/dist/typescript/hooks/push/useCallingExpWithCallingStateEffect.d.ts.map +1 -1
  35. package/dist/typescript/hooks/useAndroidKeepCallAliveEffect.d.ts.map +1 -1
  36. package/dist/typescript/utils/StreamVideoRN/index.d.ts +13 -0
  37. package/dist/typescript/utils/StreamVideoRN/index.d.ts.map +1 -1
  38. package/dist/typescript/utils/StreamVideoRN/types.d.ts +1 -2
  39. package/dist/typescript/utils/StreamVideoRN/types.d.ts.map +1 -1
  40. package/dist/typescript/utils/internal/callingx/callingx.d.ts.map +1 -1
  41. package/dist/typescript/utils/keepCallAliveHeadlessTask.d.ts +9 -0
  42. package/dist/typescript/utils/keepCallAliveHeadlessTask.d.ts.map +1 -1
  43. package/dist/typescript/utils/push/internal/android.d.ts.map +1 -1
  44. package/dist/typescript/version.d.ts +1 -1
  45. package/expo-config-plugin/dist/withAndroidPermissions.js +1 -1
  46. package/package.json +6 -6
  47. package/src/hooks/push/useCallingExpWithCallingStateEffect.ts +13 -6
  48. package/src/hooks/useAndroidKeepCallAliveEffect.ts +59 -21
  49. package/src/modules/call-manager/native-module.d.ts +14 -0
  50. package/src/utils/StreamVideoRN/index.ts +27 -0
  51. package/src/utils/StreamVideoRN/types.ts +1 -2
  52. package/src/utils/internal/callingx/callingx.ts +47 -66
  53. package/src/utils/keepCallAliveHeadlessTask.ts +45 -3
  54. package/src/utils/push/internal/android.ts +31 -10
  55. package/src/version.ts +1 -1
@@ -9,7 +9,10 @@ import {
9
9
  Platform,
10
10
  } from 'react-native';
11
11
  import { CallingState, videoLoggerSystem } from '@stream-io/video-client';
12
- import { keepCallAliveCallRef } from '../utils/keepCallAliveHeadlessTask';
12
+ import {
13
+ endKeepCallAliveHeadlessTask,
14
+ keepCallAliveCallRef,
15
+ } from '../utils/keepCallAliveHeadlessTask';
13
16
  import { getCallingxLibIfAvailable } from '../utils/push/libs';
14
17
 
15
18
  async function stopForegroundServiceNoThrow() {
@@ -21,7 +24,10 @@ async function stopForegroundServiceNoThrow() {
21
24
  }
22
25
  }
23
26
 
24
- async function startForegroundService(call_cid: string) {
27
+ /**
28
+ * @returns true when the native foreground service was asked to start.
29
+ */
30
+ async function startForegroundService(call_cid: string): Promise<boolean> {
25
31
  const logger = videoLoggerSystem.getLogger('startForegroundService');
26
32
  const isCallAliveConfigured = await (async () => {
27
33
  try {
@@ -32,10 +38,14 @@ async function startForegroundService(call_cid: string) {
32
38
  }
33
39
  })();
34
40
  if (!isCallAliveConfigured) {
35
- logger.info(
36
- 'KeepCallAlive is not configured. Skipping foreground service setup.',
41
+ logger.warn(
42
+ 'KeepCallAlive is not configured, the call will not survive the app going to the ' +
43
+ 'background: audio publishing stops and the user is eventually dropped from the call. ' +
44
+ 'Declare android.permission.FOREGROUND_SERVICE and ' +
45
+ 'android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK in the app manifest (see logcat, ' +
46
+ 'tag StreamVideoReactNative, for the exact list of missing permissions).',
37
47
  );
38
- return;
48
+ return false;
39
49
  }
40
50
  // Check for notification permission (Android 13+) before starting the service.
41
51
  const hasPostNotificationsPermission =
@@ -47,7 +57,7 @@ async function startForegroundService(call_cid: string) {
47
57
  logger.info(
48
58
  'Notification permission not granted, can not start foreground service to keep the call alive',
49
59
  );
50
- return;
60
+ return false;
51
61
  }
52
62
  const videoConfig = StreamVideoRN.getConfig();
53
63
  const foregroundServiceConfig = videoConfig.foregroundService;
@@ -58,19 +68,34 @@ async function startForegroundService(call_cid: string) {
58
68
  // NOTE: we use requestAnimationFrame to ensure that the foreground service is started after all the current UI operations are done
59
69
  // this is a workaround for the crash - android.app.RemoteServiceException$ForegroundServiceDidNotStartInTimeException: Context.startForegroundService() did not then call Service.startForeground()
60
70
  // this crash was reproducible only in some android devices
61
- requestAnimationFrame(async () => {
62
- try {
63
- await NativeModules.StreamVideoReactNative.startKeepCallAliveService(
64
- call_cid,
65
- channel.id,
66
- channel.name,
67
- notificationTexts.title,
68
- notificationTexts.body,
69
- smallIconName ?? null,
70
- );
71
- } catch (e) {
72
- logger.warn('Failed to start keep-call-alive foreground service', e);
73
- }
71
+ return new Promise<boolean>((resolve) => {
72
+ requestAnimationFrame(async () => {
73
+ // The service declares the camera/microphone foreground service types, which are
74
+ // while-in-use permissions: Android requires startForegroundService() to be called while the
75
+ // app has a visible activity, otherwise the start is rejected. The frame we deferred to can
76
+ // land after the app left the foreground, so re-check before calling native.
77
+ if (AppState.currentState !== 'active') {
78
+ logger.info(
79
+ 'App is no longer in the foreground, not starting the keep-call-alive foreground service',
80
+ );
81
+ resolve(false);
82
+ return;
83
+ }
84
+ try {
85
+ await NativeModules.StreamVideoReactNative.startKeepCallAliveService(
86
+ call_cid,
87
+ channel.id,
88
+ channel.name,
89
+ notificationTexts.title,
90
+ notificationTexts.body,
91
+ smallIconName ?? null,
92
+ );
93
+ resolve(true);
94
+ } catch (e) {
95
+ logger.warn('Failed to start keep-call-alive foreground service', e);
96
+ resolve(false);
97
+ }
98
+ });
74
99
  });
75
100
  }
76
101
 
@@ -147,8 +172,18 @@ export const useAndroidKeepCallAliveEffect = () => {
147
172
  return;
148
173
  }
149
174
 
150
- await startForegroundService(activeCallCid);
151
- foregroundServiceStartedRef.current = true;
175
+ try {
176
+ // only mark as started when native actually accepted the start, so that a later render
177
+ // while still joined can retry.
178
+ foregroundServiceStartedRef.current =
179
+ await startForegroundService(activeCallCid);
180
+ } catch (e) {
181
+ // run() is intentionally not awaited, so anything thrown here would surface as an
182
+ // unhandled rejection. Leave the ref false so a later render can retry.
183
+ videoLoggerSystem
184
+ .getLogger('useAndroidKeepCallAliveEffect')
185
+ .warn('Failed to start the keep-call-alive foreground service', e);
186
+ }
152
187
  };
153
188
 
154
189
  // ensure that app is active before running the function
@@ -170,6 +205,8 @@ export const useAndroidKeepCallAliveEffect = () => {
170
205
  } else if (isCallEnded) {
171
206
  if (foregroundServiceStartedRef.current) {
172
207
  keepCallAliveCallRef.current = undefined;
208
+ // let the headless task resolve so RN unwinds it, then stop the foreground service
209
+ endKeepCallAliveHeadlessTask();
173
210
  // stop foreground service when the call is not active
174
211
  stopForegroundServiceNoThrow();
175
212
  foregroundServiceStartedRef.current = false;
@@ -188,6 +225,7 @@ export const useAndroidKeepCallAliveEffect = () => {
188
225
  // stop foreground service when this effect is unmounted
189
226
  if (foregroundServiceStartedRef.current) {
190
227
  keepCallAliveCallRef.current = undefined;
228
+ endKeepCallAliveHeadlessTask();
191
229
  stopForegroundServiceNoThrow();
192
230
  foregroundServiceStartedRef.current = false;
193
231
  }
@@ -32,6 +32,20 @@ export interface CallManager extends NativeModule {
32
32
  */
33
33
  setTelecomManagedMode: (enabled: boolean) => void;
34
34
 
35
+ /**
36
+ * Opt out of the Android 11+ communication-mode keep-alive workaround.
37
+ *
38
+ * On Android 11+ (API 30) the OS resets `MODE_IN_COMMUNICATION` ~6s after it is
39
+ * set when there is no active voice-communication playback/recording, which
40
+ * breaks audio routing and AEC. By default the SDK plays a silent
41
+ * voice-communication track for the duration of a communicator-role call to
42
+ * prevent this. Pass `true` to disable it. Sticky for the process lifetime.
43
+ *
44
+ * No-op on iOS and on Android below API 30. See
45
+ * https://issuetracker.google.com/issues/209493718
46
+ */
47
+ setDisableCommunicationModeWorkaround: (disabled: boolean) => void;
48
+
35
49
  /**
36
50
  * Choose an audio device endpoint by its stable id.
37
51
  * @param deviceId - The id of the audio device to choose (`AudioDevice.id`).
@@ -158,6 +158,33 @@ export class StreamVideoRN {
158
158
  pushLogoutCallbacks.current = [];
159
159
  }
160
160
 
161
+ /**
162
+ * Android only. Opt out of the Android 11+ communication-mode keep-alive.
163
+ *
164
+ * By default the SDK plays a silent voice-communication track for the duration of a
165
+ * communicator-role call, so Android does not reset `MODE_IN_COMMUNICATION` ~6s after it is
166
+ * set (which breaks audio routing and echo cancellation).
167
+ * See {@link https://issuetracker.google.com/issues/209493718}
168
+ *
169
+ * Call this at app start, alongside {@link setPushConfig} — the native audio manager
170
+ * rejects the change once it has been activated for a call. No-op on iOS and on
171
+ * Android below API 30.
172
+ */
173
+ static setDisableCommunicationModeWorkaround(disabled: boolean) {
174
+ if (Platform.OS !== 'android') {
175
+ return;
176
+ }
177
+ try {
178
+ NativeModules.StreamInCallManager?.setDisableCommunicationModeWorkaround(
179
+ disabled,
180
+ );
181
+ } catch (error) {
182
+ videoLoggerSystem
183
+ .getLogger('StreamVideoRN')
184
+ .warn('setDisableCommunicationModeWorkaround failed', error);
185
+ }
186
+ }
187
+
161
188
  /**
162
189
  * Play native busy tone for call rejection
163
190
  */
@@ -176,8 +176,7 @@ export type StreamVideoConfig = {
176
176
  */
177
177
  notificationTexts: KeepAliveAndroidNotificationTexts;
178
178
  /**
179
- * The task to run in the foreground service
180
- * The task must resolve a promise once complete
179
+ * The task to run in the foreground service.
181
180
  */
182
181
  taskToRun: (call: Call) => Promise<void>;
183
182
  };
@@ -55,15 +55,24 @@ export function getCallDisplayName(
55
55
  return names.sort().join(', ');
56
56
  }
57
57
 
58
- function getCallDisplayNameFromCall(call: Call): string {
59
- return (
58
+ /**
59
+ * Args shared by the callingx call registration APIs:
60
+ * `(callId, phoneNumber, callerName, hasVideo)`.
61
+ */
62
+ function getCallingxCallArgs(call: Call): [string, string, string, boolean] {
63
+ const callDisplayName =
60
64
  call.state.custom?.display_name ||
61
65
  getCallDisplayName(
62
66
  call.state.members,
63
67
  call.state.participants,
64
68
  call.currentUserId,
65
- )
66
- );
69
+ );
70
+ return [
71
+ call.cid, // unique id for call
72
+ call.state.createdBy?.id ?? callDisplayName, // handle for native call UI (prefer createdBy user id, fallback to call display name)
73
+ callDisplayName, // display name for display in call screen
74
+ call.state.settings?.video?.enabled ?? false, // is video call?
75
+ ];
67
76
  }
68
77
 
69
78
  export async function registerOutgoingCall(call: Call) {
@@ -84,13 +93,7 @@ export async function registerOutgoingCall(call: Call) {
84
93
 
85
94
  try {
86
95
  logger.debug(`registerOutgoingCall: Registering outgoing call ${call.cid}`);
87
- const callDisplayName = getCallDisplayNameFromCall(call);
88
- await CallingxModule.startCall(
89
- call.cid, // unique id for call
90
- call.state.createdBy?.id ?? callDisplayName, // handle for native call UI (prefer createdBy user id, fallback to call display name)
91
- callDisplayName, // display name for display in call screen
92
- call.state.settings?.video?.enabled ?? false, // is video call?
93
- );
96
+ await CallingxModule.startCall(...getCallingxCallArgs(call));
94
97
  } catch (error) {
95
98
  logger.error(
96
99
  `registerOutgoingCall: Error registering outgoing call in callingx: ${call.cid}`,
@@ -119,67 +122,45 @@ export async function joinCallingxCall(call: Call, activeCalls: Call[]) {
119
122
  const logger = videoLoggerSystem.getLogger('callingx');
120
123
  const isOutcomingCall = call.ringing && call.isCreatedByMe;
121
124
  const isIncomingCall = call.ringing && !call.isCreatedByMe;
125
+ const isOngoingCall = (c: Call) =>
126
+ !c.ringing &&
127
+ CallingxModule.isOngoingCallsEnabled &&
128
+ !c.isOwnTracksLoopbackAllowed;
122
129
 
123
- const startCallInCallingx = async () => {
124
- logger.debug(`joinCallingxCall: Joining call ${call.cid}`);
125
- const callDisplayName = getCallDisplayNameFromCall(call);
126
- await CallingxModule.startCall(
127
- call.cid, // unique id for call
128
- call.state.createdBy?.id ?? callDisplayName, // handle for native call UI (prefer createdBy user id, fallback to call display name)
129
- callDisplayName, // display name for display in call screen
130
- call.state.settings?.video?.enabled ?? false, // is video call?
131
- );
132
- };
130
+ if (!isIncomingCall && !isOutcomingCall && !isOngoingCall(call)) {
131
+ return;
132
+ }
133
133
 
134
- if (
135
- isOutcomingCall ||
136
- (!call.ringing && CallingxModule.isOngoingCallsEnabled)
137
- ) {
138
- try {
139
- await startCallInCallingx();
140
- } catch (error) {
141
- logger.error(
142
- `startCallingxCall: Error starting call in callingx: ${call.cid}`,
143
- error,
134
+ try {
135
+ const activeCallsToLeave = activeCalls.filter(
136
+ (c) =>
137
+ c.cid !== call.cid &&
138
+ (c.ringing || isOngoingCall(c)) &&
139
+ c.state.callingState !== CallingState.LEFT,
140
+ );
141
+ for (const activeCall of activeCallsToLeave) {
142
+ logger.debug(
143
+ `leaving currently-active-call:${activeCall.cid} before joining the call:${call.cid}`,
144
144
  );
145
+ await activeCall.leave({ reason: 'cancel' }).catch((e) => {
146
+ logger.error(`failed to leave active call ${activeCall.cid}`, e);
147
+ });
145
148
  }
146
- } else if (isIncomingCall) {
147
- logger.debug(`joinCallingxCall: Joining incoming call ${call.cid}`);
148
-
149
- try {
150
- // Leave any existing active ringing calls before joining a new ringing call
151
- const activeCallsToLeave = activeCalls.filter(
152
- (c) =>
153
- c.cid !== call.cid &&
154
- c.ringing &&
155
- c.state.callingState !== CallingState.LEFT,
156
- );
157
- for (const activeCall of activeCallsToLeave) {
158
- logger.debug(
159
- `leaving active call ${activeCall.cid} before joining ${call.cid}`,
160
- );
161
- await activeCall.leave({ reason: 'cancel' }).catch((e) => {
162
- logger.error(`failed to leave active call ${activeCall.cid}`, e);
163
- });
164
- }
165
- // Awaits native CallKit/Telecom registration before answering.
166
- // Safe to call even if the call is already registered (e.g. from VoIP push) --
167
- // iOS early-returns with no error, Android sends the registered broadcast.
168
- const callDisplayName = getCallDisplayNameFromCall(call);
169
- await CallingxModule.displayIncomingCall(
170
- call.cid, // unique id for call
171
- call.state.createdBy?.id ?? callDisplayName, // handle for native call UI (prefer createdBy user id, fallback to call display name)
172
- callDisplayName, // display name for display in call screen
173
- call.state.settings?.video?.enabled ?? false, // is video call?
174
- );
175
-
149
+ logger.debug(
150
+ `joinCallingxCall: Joining call ${call.cid} isIncoming: ${isIncomingCall} isOutgoing: ${isOutcomingCall}`,
151
+ );
152
+ const callArgs = getCallingxCallArgs(call);
153
+ if (isIncomingCall) {
154
+ await CallingxModule.displayIncomingCall(...callArgs);
176
155
  await CallingxModule.answerIncomingCall(call.cid);
177
- } catch (error) {
178
- logger.error(
179
- `Error joining incoming call in callingx: ${call.cid}`,
180
- error,
181
- );
156
+ } else {
157
+ await CallingxModule.startCall(...callArgs);
182
158
  }
159
+ } catch (error) {
160
+ logger.error(
161
+ `startCallingxCall: Error starting call in callingx: ${call.cid} isIncoming: ${isIncomingCall} isOutgoing: ${isOutcomingCall}`,
162
+ error,
163
+ );
183
164
  }
184
165
  }
185
166
 
@@ -13,6 +13,21 @@ export const keepCallAliveCallRef: { current: Call | undefined } = {
13
13
  current: undefined,
14
14
  };
15
15
 
16
+ let resolveRunningTask: (() => void) | undefined;
17
+
18
+ /**
19
+ * Ends the currently running keep-alive headless task.
20
+ *
21
+ * The task's promise is app-provided (`foregroundService.android.taskToRun`) and by default never
22
+ * resolves, so stopping the native service alone would leave the task registered in
23
+ * `HeadlessJsTaskContext` forever - which keeps RN's timer choreographer callback posted for the
24
+ * rest of the process lifetime. Resolving it here lets React Native unwind the task properly.
25
+ */
26
+ export const endKeepCallAliveHeadlessTask = () => {
27
+ resolveRunningTask?.();
28
+ resolveRunningTask = undefined;
29
+ };
30
+
16
31
  function registerKeepCallAliveHeadlessTaskOnce() {
17
32
  if (Platform.OS !== 'android') return;
18
33
 
@@ -42,10 +57,37 @@ function registerKeepCallAliveHeadlessTaskOnce() {
42
57
 
43
58
  const config = StreamVideoRN.getConfig();
44
59
  const taskToRun = config.foregroundService.android.taskToRun;
60
+ // A previous task (if any) must not keep hanging around.
61
+ endKeepCallAliveHeadlessTask();
62
+ const taskEnded = new Promise<void>((resolve) => {
63
+ resolveRunningTask = resolve;
64
+ });
65
+ const ownResolver = resolveRunningTask;
66
+
67
+ // React Native stops the foreground service as soon as the promise returned from here
68
+ // settles. `taskToRun` is app-provided and may resolve early - or immediately - so its
69
+ // completion must not end the keep-alive: that would drop the call's background protection
70
+ // while the call is still ongoing. The task's lifetime is owned by the SDK instead, and ends
71
+ // via endKeepCallAliveHeadlessTask() when the call is left.
72
+ (async () => {
73
+ try {
74
+ await taskToRun(call);
75
+ logger.debug(
76
+ 'Keep-alive taskToRun completed, keeping the call alive until the call ends',
77
+ );
78
+ } catch (e) {
79
+ logger.error('Keep-alive headless task failed', e);
80
+ }
81
+ })();
82
+
45
83
  try {
46
- await taskToRun(call);
47
- } catch (e) {
48
- logger.error('Keep-alive headless task failed', e);
84
+ await taskEnded;
85
+ } finally {
86
+ // a newer task may already have installed its own resolver - clearing it here would leave
87
+ // that task with no way to be ended.
88
+ if (resolveRunningTask === ownResolver) {
89
+ resolveRunningTask = undefined;
90
+ }
49
91
  }
50
92
  },
51
93
  );
@@ -79,6 +79,35 @@ export const onRingNotificationReceived = async (
79
79
  callingx.releaseBackgroundTask(backgroundTaskOwner);
80
80
  };
81
81
 
82
+ /**
83
+ * Gives up on this push: dismisses the call the native push path already displayed, then asks
84
+ * the call service to stop. `stopService` is only a request — it no-ops while any other call is
85
+ * registered or being registered — so this call must be ended explicitly rather than relying on
86
+ * the service teardown to wipe it.
87
+ */
88
+ const abandonPush = async () => {
89
+ if (asForegroundService) {
90
+ finishBackgroundTask();
91
+ }
92
+ // Each step is contained on its own: failing to dismiss the call must not skip the stop
93
+ // request, and neither may skip the unsubscription cleanup — a stale entry makes every later
94
+ // push for this cid look like a duplicate and get discarded.
95
+ try {
96
+ await callingx.endCallWithReason(call_cid, 'error');
97
+ } catch (error) {
98
+ nativeLog(`Failed to end call ${call_cid}: ${error}`, 'error');
99
+ }
100
+ try {
101
+ await callingx.stopService();
102
+ } catch (error) {
103
+ nativeLog(
104
+ `Failed to stop the call service for ${call_cid}: ${error}`,
105
+ 'error',
106
+ );
107
+ }
108
+ pushUnsubscriptionCallbacks.delete(call_cid);
109
+ };
110
+
82
111
  if (asForegroundService) {
83
112
  // initialize the callback array immediately to avoid race condition
84
113
  pushUnsubscriptionCallbacks.set(call_cid, []);
@@ -98,21 +127,13 @@ export const onRingNotificationReceived = async (
98
127
  client = await pushConfig.createStreamVideoClient();
99
128
  if (!client) {
100
129
  nativeLog(`video client not found, skipping the call.ring notification`);
101
- if (asForegroundService) {
102
- finishBackgroundTask();
103
- }
104
- await callingx.stopService();
105
- pushUnsubscriptionCallbacks.delete(call_cid);
130
+ await abandonPush();
106
131
  return;
107
132
  }
108
133
  } catch (error) {
109
134
  //we need to release the background task and stop the service to avoid stale owner
110
135
  nativeLog(`Failed to create video client: ${error}`, 'error');
111
- if (asForegroundService) {
112
- finishBackgroundTask();
113
- }
114
- await callingx.stopService();
115
- pushUnsubscriptionCallbacks.delete(call_cid);
136
+ await abandonPush();
116
137
  return;
117
138
  }
118
139
 
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '1.45.0';
1
+ export const version = '1.45.2';