Skip to content

Conversation

@santhoshvai
Copy link
Member

@santhoshvai santhoshvai commented Jan 14, 2026

💡 Overview

Previously we used Notifee for the foreground service to keep the call alive when app goes to background. Now this foreground service is added to our SDK.

image

Summary by CodeRabbit

  • New Features

    • Added an SDK-managed keep-alive service for Android calls, enabling better background call persistence with system notifications.
    • Added support for Android 13+ notification permissions, ensuring proper permission requests before displaying call notifications.
  • Improvements

    • Enhanced Android notification channel configuration for better call state management.

✏️ Tip: You can customize this high-level summary in your review settings.

greenfrvr and others added 29 commits December 29, 2025 16:35
### 💡 Overview
In case when caller is in active call, they can be interrupted by
incoming call even when `rejectCallWhenBusy` is set to `true`.
 
### 📝 Implementation notes

🎫 Ticket:
https://linear.app/stream/issue/RN-326/rejectcallwhenbusy-is-not-working-for-caller
Introduces new `useModeration` hook that handles the `call.moderation_blur` event.
Follow-up of: #1822

🎫 Ticket: https://linear.app/stream/issue/RN-329/moderation-video-blurring
📑 Docs: GetStream/docs-content#373
### 💡 Overview
Implement collapsible accordion for QR code section in participants
panel.
### 💡 Overview

Adds PipLayout.Grid, a grid layout for PiP mode with built-in pagination
and automatic column adjustment based on the current participant count.

### 📝 Implementation notes

🎫 Ticket:
https://linear.app/stream/issue/REACT-702/improve-pip-while-presenting-add-grid-view-during-screenshare
…o re-mount (#2077)

### 💡 Overview
This change removes the use of a randomly generated portal ID and
instead uses a ref backed DOM element as the FloatingPortal root. Using
Math.random() during render is not recommended and causes issues with
some versions of React Compiler since the compiler may strip or reorder
memoization.
Adds dev page + API for the new `GetCallStatsMap` endpoint.

Ref: GetStream/chat#10910
…ra (#2080)

This code used to be buggy, and when combined with`usePersistedDevicePreferences`, it was causing a race condition that caused devices to be enabled/disabled randomly, depending on who reached the critical block first.
Besides that, `CameraManager` was incorrectly checking for `SEND_AUDIO` instead of `SEND_VIDEO` capability. This is fixed now, too.

### 📝 Implementation notes

- The `apply` method is now refactored and made easier to understand.
- `usePersistedDevicePreferences` now sets the `deferServerDefaults` flag that prevents applying server-side defaults, as the user preferences should always take precedence. This hook handles the initial device setup, too, so we aren't losing the default settings.

🎫 Ticket:
https://linear.app/stream/issue/REACT-737/improve-applying-of-default-device-preferences
📑 Docs: GetStream/docs-content#896
This update introduces a new service, StreamCallKeepAliveHeadlessService, to the AndroidManifest.xml, enabling foreground service capabilities for media playback, camera, and microphone. This enhancement aims to improve the app's performance and reliability during call handling.
…video

This update addresses a race condition in the setup of server-side preferences for microphone and camera, ensuring consistent device state. The `apply` method has been refactored for clarity, and the `usePersistedDevicePreferences` hook now correctly prioritizes user preferences over server defaults. This enhancement improves the reliability of device management during calls.
@changeset-bot
Copy link

changeset-bot bot commented Jan 14, 2026

⚠️ No Changeset found

Latest commit: 7b4c647

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

Adds a new SDK-owned Android foreground service for maintaining call state while the app runs in the background. Includes native service implementation, React Native bridge methods, notification management, and TypeScript headless task registration replacing prior Notifee-based approach.

Changes

Cohort / File(s) Summary
Android Manifest Configuration
packages/react-native-sdk/android/src/main/AndroidManifest.xml, packages/react-native-sdk/android/src/main/AndroidManifestNew.xml
Added StreamCallKeepAliveHeadlessService declaration with exported="false", stopWithTask="true", and foregroundServiceType="mediaPlayback|camera|microphone". New permissions added: INTERNET, DEVICE_POWER, WAKE_LOCK.
Android Service Implementation
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt, packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/KeepAliveNotification.kt
Introduces foreground service that extends HeadlessJsTaskService; manages notification creation, channel setup, and dynamic foreground service type resolution based on granted permissions. Includes intent builders for start/stop operations.
React Native Bridge
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
Exposes startKeepCallAliveService and stopKeepCallAliveService ReactMethods to invoke native foreground service from JavaScript with metadata (call CID, channel, title, body, icon).
React Native Hook & Headless Task
packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts, packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
Replaces Notifee with native service invocation; adds Android 13+ notification permission check. Registers headless task that retrieves active call and executes configured task.
Configuration & Type Definitions
packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts, packages/react-native-sdk/src/utils/StreamVideoRN/types.ts, packages/react-native-sdk/src/utils/StreamVideoRN/index.ts
Updates manifest plugin to conditionally add keep-alive service when androidKeepCallAlive is enabled. Introduces KeepAliveAndroidChannelConfig and KeepAliveAndroidNotificationTexts types; removes notification channel lights/vibration defaults.
Utilities & Checks
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/util/CallAliveServiceChecker.kt, packages/react-native-sdk/src/index.ts
Updates service class target from Notifee to StreamCallKeepAliveHeadlessService; adjusts expected foreground service types. Imports headless task module for side-effect registration.

Sequence Diagram

sequenceDiagram
    participant RN as React Native App
    participant Hook as useAndroidKeepCallAliveEffect
    participant Bridge as StreamVideoReactNativeModule
    participant Service as StreamCallKeepAliveHeadlessService
    participant Notif as KeepAliveNotification
    participant Headless as HeadlessJsTaskRunner

    RN->>Hook: useAndroidKeepCallAliveEffect triggered
    Hook->>Hook: Check Android 13+ notification permission
    Hook->>Bridge: startKeepCallAliveService(callCid, channel, title, body, icon)
    Bridge->>Service: buildStartIntent() + startForeground()
    Service->>Notif: ensureChannel(context, channelId, channelName)
    Notif-->>Service: channel created/verified
    Service->>Notif: buildOngoingNotification(context, channelId, title, body, icon)
    Notif-->>Service: notification object
    Service->>Service: startForeground(NOTIFICATION_ID, notification)
    Service-->>Bridge: service started
    Bridge-->>Hook: promise resolved

    rect rgb(200, 220, 255)
    note over Headless: Headless Task Execution
    Service->>Headless: getTaskConfig(callCid)
    Headless->>Headless: retrieve keepCallAliveCallRef
    Headless->>Headless: validate callCid matches
    Headless->>Headless: execute taskToRun(call)
    end

    RN->>Hook: cleanup on unload
    Hook->>Bridge: stopKeepCallAliveService()
    Bridge->>Service: buildStopIntent() + stopService()
    Service-->>Bridge: service stopped
    Bridge-->>Hook: promise resolved
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A service hops to life so keen,
Keeping calls alive, unseen,
With notifications standing guard,
No background task is now too hard!
The headless task runs swift and true,
SDK-owned through and through! 🚀

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The PR description provides an overview but lacks implementation details, documentation link, and ticket reference required by the template. Add missing sections: 📝 Implementation notes, 🎫 Ticket link, and 📑 Docs link. Include details on how the new foreground service works and any migration steps needed.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: replacing Notifee with a custom keep-alive foreground service implementation in the SDK.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@santhoshvai
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In `@packages/react-native-sdk/android/src/main/AndroidManifestNew.xml`:
- Around line 3-5: Remove the unnecessary protected permission by deleting the
<uses-permission android:name="android.permission.DEVICE_POWER" /> entry from
AndroidManifestNew.xml; keep the existing INTERNET and WAKE_LOCK permissions,
and ensure no code paths call PowerManager methods that require DEVICE_POWER
(e.g., goToSleep(), reboot())—thermal status monitoring and proximity wake locks
via PowerManager are fine without that permission.

In
`@packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt`:
- Around line 58-61: Replace the deprecated stopForeground(true) call in
StreamCallKeepAliveHeadlessService.onDestroy with the modern constant: call
stopForeground(Service.STOP_FOREGROUND_REMOVE) (or fully qualify
android.app.Service.STOP_FOREGROUND_REMOVE) so the service removes the
notification without using the deprecated boolean API; update imports if needed.

In
`@packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/util/CallAliveServiceChecker.kt`:
- Around line 18-30: CallAliveServiceChecker currently compares
serviceInfo.foregroundServiceType for exact equality with
expectedForegroundServiceTypes
(ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK | CAMERA | MICROPHONE),
which fails when StreamCallKeepAliveHeadlessService omits CAMERA or MICROPHONE
bits at runtime; change the check in CallAliveServiceChecker (the code around
packageManager.getServiceInfo and expectedForegroundServiceTypes) to use bitwise
checks instead of equality—e.g., verify required bits (at minimum
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK) are present via & checks or
ensure serviceInfo.foregroundServiceType has the expected bits set with
(serviceInfo.foregroundServiceType and expectedForegroundServiceTypes) ==
serviceInfo.foregroundServiceType / or at least check the MEDIA_PLAYBACK bit—so
the validation succeeds when optional CAMERA/MICROPHONE bits are absent.

In `@packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts`:
- Around line 49-58: The call to
NativeModules.StreamVideoReactNative.startKeepCallAliveService inside
useAndroidKeepCallAliveEffect is a promise but currently has no rejection
handling; update the requestAnimationFrame callback to handle rejections from
startKeepCallAliveService (either by awaiting it in an async function with
try/catch or by chaining .catch) and log/report the error (e.g., using
console.error or the existing logger) so unhandled promise rejections are
prevented; reference startKeepCallAliveService, requestAnimationFrame, and
useAndroidKeepCallAliveEffect when making the change.
- Around line 144-147: The call to
NativeModules.StreamVideoReactNative.stopKeepCallAliveService() is missing
promise error handling; update the block where keepCallAliveCallRef.current is
cleared (and foregroundServiceStartedRef.current is set to false) to call
stopKeepCallAliveService().catch(...) and log or handle the error (mirroring the
pattern used for startKeepCallAliveService()), so failures won't be swallowed —
reference keepCallAliveCallRef,
NativeModules.StreamVideoReactNative.stopKeepCallAliveService, and
foregroundServiceStartedRef when applying the change.

In `@packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts`:
- Around line 50-56: The code accesses StreamVideoRN.getConfig() and
config.foregroundService.android.taskToRun outside the try-catch which can throw
if the SDK isn't configured; guard this by validating getConfig() and the nested
properties (e.g., config.foregroundService.android and taskToRun) before calling
taskToRun(call), and if any are missing log a clear error via logger.error and
return early; alternatively move the getConfig() and property access into the
try block so exceptions are caught, then call taskToRun(call) inside the same
try/catch and log the caught error using the existing logger.error call.
🧹 Nitpick comments (4)
packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts (1)

19-63: Move hasRegistered declaration before the function for clarity.

The current ordering is technically correct but confusing: hasRegistered is declared on line 61, after the function definition but before it's called. Moving the declaration before the function makes the intent clearer and follows the conventional pattern of declaring variables before use.

♻️ Suggested reordering
+let hasRegistered = false;
+
 function registerKeepCallAliveHeadlessTaskOnce() {
   if (Platform.OS !== 'android') return;

   // Registering multiple times can throw in RN; guard with a module-level flag.

   if (hasRegistered) return;

   AppRegistry.registerHeadlessTask(
     KEEP_CALL_ALIVE_HEADLESS_TASK_NAME,
     () => async (data: { callCid?: string } | undefined) => {
       // ... task implementation
     },
   );
+  hasRegistered = true;
 }

-let hasRegistered = false;
 registerKeepCallAliveHeadlessTaskOnce();
-hasRegistered = true;
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/KeepAliveNotification.kt (2)

48-53: Empty fallback Intent may not provide useful behavior.

When the launch activity is unavailable, creating a PendingIntent with an empty Intent() will do nothing when tapped. Consider either:

  1. Not setting a contentIntent at all (notification still works, just not tappable)
  2. Logging a warning so developers are aware of the misconfiguration
💡 Suggested improvement
         val contentIntent = if (launchIntent != null) {
             PendingIntent.getActivity(context, 0, launchIntent, pendingIntentFlags)
         } else {
-            // Fallback: empty intent to avoid crash if launch activity is missing for some reason
-            PendingIntent.getActivity(context, 0, Intent(), pendingIntentFlags)
+            // Fallback: no-op intent if launch activity is missing
+            android.util.Log.w("KeepAliveNotification", "Launch activity not found; notification tap will have no effect")
+            PendingIntent.getActivity(context, 0, Intent(), pendingIntentFlags)
         }

67-81: Icon fallback may fail if app icon resource ID is invalid.

appInfo.icon can return 0 if no icon is set, which would cause the notification to fail. Consider adding a check:

♻️ Proposed fix
         // Default to the app icon
         return try {
             val appInfo = context.packageManager.getApplicationInfo(packageName, 0)
-            appInfo.icon
+            if (appInfo.icon != 0) appInfo.icon else android.R.drawable.ic_dialog_info
         } catch (_: PackageManager.NameNotFoundException) {
             android.R.drawable.ic_dialog_info
         }
packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts (1)

70-71: Setting ref during render may cause issues.

keepCallAliveCallRef.current = call is set during render, which can cause the ref to be updated even when the component doesn't commit. Consider moving this assignment into a useEffect to ensure it only updates when the component actually mounts/updates.

♻️ Proposed fix
   const call = useCall();
-  keepCallAliveCallRef.current = call;
   const activeCallCid = call?.cid;
+
+  useEffect(() => {
+    keepCallAliveCallRef.current = call;
+    return () => {
+      // Only clear if this hook's call is still the current one
+      if (keepCallAliveCallRef.current === call) {
+        keepCallAliveCallRef.current = undefined;
+      }
+    };
+  }, [call]);
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 4867e14 and 12042cc.

📒 Files selected for processing (12)
  • packages/react-native-sdk/android/src/main/AndroidManifest.xml
  • packages/react-native-sdk/android/src/main/AndroidManifestNew.xml
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/KeepAliveNotification.kt
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/util/CallAliveServiceChecker.kt
  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/src/utils/StreamVideoRN/index.ts
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
💤 Files with no reviewable changes (1)
  • packages/react-native-sdk/src/utils/StreamVideoRN/index.ts
🧰 Additional context used
📓 Path-based instructions (10)
packages/react-native-sdk/src/index.ts

📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)

packages/react-native-sdk/src/index.ts: Always call registerGlobals() from @stream-io/react-native-webrtc before using WebRTC on non-web platforms in the React Native SDK entry point
Export all public SDK APIs (components, hooks, providers, types) from the main src/index.ts file, with re-exports from client and bindings packages and SDK-specific implementations

Files:

  • packages/react-native-sdk/src/index.ts
packages/react-native-sdk/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)

packages/react-native-sdk/src/**/*.{ts,tsx}: Use React hooks from @stream-io/video-react-bindings (via useCall() and useCallStateHooks()) instead of directly accessing RxJS observables from @stream-io/video-client
Use Platform.OS checks and conditional imports to gate platform-specific code (iOS, Android, web) and prevent importing platform-specific libraries on unsupported platforms

Files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x)

📄 CodeRabbit inference engine (AGENTS.md)

packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x): Use TypeScript for all source code in packages/client, packages/react-sdk, and packages/react-native-sdk
Mark deprecated APIs with @deprecated JSDoc, including rationale and replacement guidance
Throw descriptive errors or return typed error results consistently with existing patterns in public APIs
Gate internal debug logging behind an environment flag; no console noise in production builds
Never leak credentials or user data in error messages or logs
Check instance IDs and timestamps before state updates to avoid race conditions in async operations
Make public API surfaces explicit with TypeScript types and interfaces

Files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Use camelCase for function and property names
Narrowly scope eslint-disable comments with inline explanatory comments and rationale

Files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use PascalCase for component and type names

Files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
**/*.{sh,js,ts}

📄 CodeRabbit inference engine (AGENTS.md)

Make scripts error on missing critical environment variables

Files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
packages/react-native-sdk/expo-config-plugin/src/**/*.ts

📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)

Configure Expo config plugins in expo-config-plugin/ with platform-specific modifiers for Android manifest, iOS Info.plist, and app delegate modifications

Files:

  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/**/*.kt

📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)

When adding Android native functionality, create Kotlin file in android/src/main/java/com/streamvideo/reactnative/, use @ReactMethod annotation, register in StreamVideoReactNativePackage.kt, and call from TypeScript via NativeModules.StreamVideoReactNative

Files:

  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/util/CallAliveServiceChecker.kt
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/KeepAliveNotification.kt
packages/react-native-sdk/src/utils/**/*.ts

📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)

Implement React Native native modules using NativeModules API and always wrap calls in try-catch blocks to handle promise rejection from native code

Files:

  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
packages/react-native-sdk/src/hooks/**/*.ts

📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)

packages/react-native-sdk/src/hooks/**/*.ts: Handle iOS backgrounding by disabling video tracks to prevent battery drain when AppState changes to 'background', while keeping audio enabled
Implement Android foreground service via useAndroidKeepCallAliveEffect() hook to prevent call termination when app is backgrounded, with automatic permission handling for POST_NOTIFICATIONS (Android 13+)
When adding new React Native hooks, use useCall() and useCallStateHooks() from bindings layer, handle platform-specific logic with Platform.OS, add tests in __tests__/hooks/, and export from src/hooks/index.ts

Files:

  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
🧠 Learnings (28)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : Implement Android foreground service via `useAndroidKeepCallAliveEffect()` hook to prevent call termination when app is backgrounded, with automatic permission handling for POST_NOTIFICATIONS (Android 13+)
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/utils/enterPiPAndroid.ts : Create and register notification channels with proper IDs and names before starting Android foreground service, respecting platform-specific requirements (different behavior for Android 10+)
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : Handle iOS backgrounding by disabling video tracks to prevent battery drain when `AppState` changes to 'background', while keeping audio enabled
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/utils/push/setupIosVoipPushEvents.ts : Implement VoIP push display logic to call `CallKeep.displayIncomingCall()` within 30 seconds of receiving VoIP push notification on iOS, or the system will terminate the app
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/push/**/*.ts : Use RxJS subjects from `src/utils/push/internal/rxSubjects.ts` (like `pushTappedIncomingCallCId$`, `pushAcceptedIncomingCallCId$`, `pushRejectedIncomingCallCId$`, `voipPushNotificationCallCId$`) to bridge native push events to React hooks
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Export all public SDK APIs (components, hooks, providers, types) from the main `src/index.ts` file, with re-exports from client and bindings packages and SDK-specific implementations

Applied to files:

  • packages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : Implement Android foreground service via `useAndroidKeepCallAliveEffect()` hook to prevent call termination when app is backgrounded, with automatic permission handling for POST_NOTIFICATIONS (Android 13+)

Applied to files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/util/CallAliveServiceChecker.kt
  • packages/react-native-sdk/android/src/main/AndroidManifest.xml
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
  • packages/react-native-sdk/android/src/main/AndroidManifestNew.xml
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/KeepAliveNotification.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use Platform.OS checks and conditional imports to gate platform-specific code (iOS, Android, web) and prevent importing platform-specific libraries on unsupported platforms

Applied to files:

  • packages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/version.ts : Store generated version information in `src/version.ts` (auto-generated via `yarn copy-version` script) and export for SDK consumers

Applied to files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/modules/**/*.ts : Structure native module TypeScript interfaces in `src/modules/` to match the exact signatures of their native implementations (Android Kotlin and iOS Swift), including parameter types and return types

Applied to files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
📚 Learning: 2026-01-09T11:08:06.856Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:08:06.856Z
Learning: Applies to packages/react-sdk/**/*.{ts,tsx} : Destructure useCallStateHooks() at component top level, not in dependency arrays, to avoid unnecessary re-renders

Applied to files:

  • packages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/utils/**/*.ts : Implement React Native native modules using `NativeModules` API and always wrap calls in try-catch blocks to handle promise rejection from native code

Applied to files:

  • packages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/client/src/**/*.ts?(x) : Avoid unguarded web-only APIs in shared code between React and React Native

Applied to files:

  • packages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/components/**/*.tsx : Import translation strings from `src/translations/` and use i18n system to provide localized content for UI components

Applied to files:

  • packages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : When adding new React Native hooks, use `useCall()` and `useCallStateHooks()` from bindings layer, handle platform-specific logic with `Platform.OS`, add tests in `__tests__/hooks/`, and export from `src/hooks/index.ts`

Applied to files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Always call `registerGlobals()` from `stream-io/react-native-webrtc` before using WebRTC on non-web platforms in the React Native SDK entry point

Applied to files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes

Applied to files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/util/CallAliveServiceChecker.kt
  • packages/react-native-sdk/android/src/main/AndroidManifest.xml
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
  • packages/react-native-sdk/android/src/main/AndroidManifestNew.xml
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : Handle iOS backgrounding by disabling video tracks to prevent battery drain when `AppState` changes to 'background', while keeping audio enabled

Applied to files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/utils/push/setupIosVoipPushEvents.ts : Implement VoIP push display logic to call `CallKeep.displayIncomingCall()` within 30 seconds of receiving VoIP push notification on iOS, or the system will terminate the app

Applied to files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/KeepAliveNotification.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use React hooks from `stream-io/video-react-bindings` (via `useCall()` and `useCallStateHooks()`) instead of directly accessing RxJS observables from `stream-io/video-client`

Applied to files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/ios/**/*.swift : When adding iOS native functionality, create Swift file in `ios/`, create Objective-C bridge with `RCT_EXTERN_METHOD`, expose via `StreamVideoReactNative.swift`, and call from TypeScript via `NativeModules.StreamVideoReactNative`

Applied to files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider

Applied to files:

  • packages/react-native-sdk/src/index.ts
  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/expo-config-plugin/src/**/*.ts : Configure Expo config plugins in `expo-config-plugin/` with platform-specific modifiers for Android manifest, iOS Info.plist, and app delegate modifications

Applied to files:

  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
  • packages/react-native-sdk/android/src/main/AndroidManifest.xml
  • packages/react-native-sdk/android/src/main/AndroidManifestNew.xml
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/utils/enterPiPAndroid.ts : Create and register notification channels with proper IDs and names before starting Android foreground service, respecting platform-specific requirements (different behavior for Android 10+)

Applied to files:

  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/util/CallAliveServiceChecker.kt
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/KeepAliveNotification.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/utils/push/**/*.ts : Safely detect optional library availability using helper functions like `getFirebaseMessagingLibNoThrow()` and `getExpoNotificationsLib()` before accessing optional peer dependencies

Applied to files:

  • packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/**/*.kt : When adding Android native functionality, create Kotlin file in `android/src/main/java/com/streamvideo/reactnative/`, use `ReactMethod` annotation, register in `StreamVideoReactNativePackage.kt`, and call from TypeScript via `NativeModules.StreamVideoReactNative`

Applied to files:

  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/util/CallAliveServiceChecker.kt
  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/KeepAliveNotification.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.tsx : Always request media permissions via `usePermissionRequest()` hook before calling device enable methods like `call.camera.enable()` or `call.microphone.enable()`

Applied to files:

  • packages/react-native-sdk/android/src/main/AndroidManifest.xml
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
  • packages/react-native-sdk/android/src/main/AndroidManifestNew.xml
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/theme/**/*.ts : Apply deep partial theme overrides using the `DeepPartial<Theme>` type when customizing StreamVideo provider theme to maintain type safety

Applied to files:

  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x) : Make public API surfaces explicit with TypeScript types and interfaces

Applied to files:

  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/push/**/*.ts : Use RxJS subjects from `src/utils/push/internal/rxSubjects.ts` (like `pushTappedIncomingCallCId$`, `pushAcceptedIncomingCallCId$`, `pushRejectedIncomingCallCId$`, `voipPushNotificationCallCId$`) to bridge native push events to React hooks

Applied to files:

  • packages/react-native-sdk/src/utils/StreamVideoRN/types.ts
  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
📚 Learning: 2026-01-12T11:59:18.251Z
Learnt from: santhoshvai
Repo: GetStream/stream-video-js PR: 2038
File: packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.kt:37-48
Timestamp: 2026-01-12T11:59:18.251Z
Learning: In packages/react-native-sdk Android audio utilities (specifically AudioFocusUtil.kt), it's acceptable to use the !! (force unwrap) operator when accessing WebRTCModule via reactContext.getNativeModule(), as a fatal crash is the preferred behavior if the module is not registered.

Applied to files:

  • packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/**/*.tsx : Use the renderless component pattern (component that returns null) for side-effect-only child components within `StreamCall` and `StreamVideo` providers to keep logic separated and testable

Applied to files:

  • packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts
🧬 Code graph analysis (1)
packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts (2)
packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts (1)
  • keepCallAliveCallRef (15-17)
packages/client/src/store/CallState.ts (1)
  • callingState (752-754)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Code Lint, Unit Test and dogfood versioning
  • GitHub Check: test-and-build
🔇 Additional comments (16)
packages/react-native-sdk/src/utils/StreamVideoRN/types.ts (2)

8-16: LGTM! Clean, minimal type definitions for keep-alive configuration.

These new types appropriately capture only the required fields (id, name, title, body) rather than depending on the full AndroidChannel type from Notifee. This decouples the foreground service configuration from the Notifee library.

Note: The AndroidChannel import on line 6 is still needed for callChannel and incomingCallChannel in the push notification config.


155-171: Configuration type update aligns with the new SDK-owned service.

The simplified KeepAliveAndroidChannelConfig and KeepAliveAndroidNotificationTexts types are structurally compatible with existing code that may have passed full AndroidChannel objects, since TypeScript uses structural typing and the new types require only a subset of properties.

packages/react-native-sdk/src/utils/keepCallAliveHeadlessTask.ts (1)

8-17: Well-documented ref pattern for headless task communication.

The module-level ref with explicit documentation about process death limitations is a pragmatic approach. The comment correctly notes that killed-state support would require a different architecture.

packages/react-native-sdk/src/index.ts (1)

11-11: LGTM! Side-effect import for headless task registration.

The import triggers task registration at SDK initialization. The keepCallAliveHeadlessTask module internally guards with Platform.OS !== 'android', so this is safe on all platforms.

packages/react-native-sdk/android/src/main/AndroidManifest.xml (1)

8-14: Service declaration is correct; permissions are properly delegated to the consuming app.

The service attributes are appropriate:

  • exported="false" prevents external access
  • stopWithTask="true" ensures the service stops when the user dismisses the app
  • foregroundServiceType="mediaPlayback|camera|microphone" correctly matches the service capabilities

The manifest correctly omits FOREGROUND_SERVICE_* and POST_NOTIFICATIONS permissions—these are properly handled at the app level. The Expo config plugin automatically adds FOREGROUND_SERVICE_CAMERA, FOREGROUND_SERVICE_MICROPHONE, and POST_NOTIFICATIONS to consuming apps. Runtime permission requests via useAndroidKeepCallAliveEffect() handle the optional FOREGROUND_SERVICE_MEDIA_PLAYBACK. Notification channels are created before service startup in KeepAliveNotification.kt.

packages/react-native-sdk/expo-config-plugin/src/withAndroidManifest.ts (2)

46-63: LGTM! New keep-alive service declaration is well-structured.

The service configuration correctly defines the StreamCallKeepAliveHeadlessService with appropriate foregroundServiceType values for media playback, camera, and microphone. The tools:replace attribute ensures the foreground service type properly overrides any inherited values.


75-94: Clean separation of concerns between Notifee and SDK keep-alive services.

The logic correctly:

  1. Adds Notifee service only for ringing push notifications (with shortService type)
  2. Adds SDK-owned keep-alive service separately when androidKeepCallAlive is enabled

This separation aligns with the PR objective of replacing Notifee for keep-alive while preserving its use for ringing notifications.

packages/react-native-sdk/android/src/main/AndroidManifestNew.xml (1)

8-12: Service declaration looks correct.

The service is properly configured with:

  • exported="false" - prevents external access
  • stopWithTask="true" - ensures cleanup when app is killed
  • Appropriate foregroundServiceType for media/camera/microphone
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/StreamVideoReactNativeModule.kt (2)

134-159: LGTM! Service start method is well-structured.

The implementation correctly:

  • Uses ContextCompat.startForegroundService for backward compatibility
  • Handles exceptions and rejects the promise with descriptive error
  • Follows the coding guidelines by using @ReactMethod annotation

One consideration: the promise resolves immediately before the service actually starts its foreground notification. This is acceptable as the service has a few seconds to call startForeground(), but be aware that if the service fails to do so, the app may ANR without the JS side knowing.


164-173: LGTM! Service stop method is clean and handles errors properly.

packages/react-native-sdk/src/hooks/useAndroidKeepCallAliveEffect.ts (1)

27-39: LGTM! Permission check is correctly implemented.

The logic properly:

  • Bypasses the check for Android versions below 13 (API 33)
  • Uses PermissionsAndroid.check (read-only) rather than requesting
  • Logs appropriately when permission is not granted

This aligns with the coding guidelines for automatic permission handling for POST_NOTIFICATIONS on Android 13+.

packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/keepalive/StreamCallKeepAliveHeadlessService.kt (5)

21-42: Well-structured foreground service initialization.

The implementation correctly calls startForeground before delegating to the superclass, which prevents ANR issues when starting foreground services. Defensive null handling with defaults for all extras is appropriate. As per learnings, creating the notification channel before building the notification is the correct approach.


44-56: Correct HeadlessJS task configuration.

Returning null when callCid is missing prevents invalid task execution. The unbounded timeout (0) with allowedInForeground=true is appropriate for long-running call tasks.


63-81: Correct foreground service type computation with permission checks.

The implementation properly checks runtime permissions before adding CAMERA and MICROPHONE foreground service types, which is required on Android 14+. Using MEDIA_PLAYBACK as the baseline is appropriate for keeping calls alive in the background.


83-89: Proper version-aware foreground service start.

The compatibility wrapper correctly uses the 3-parameter startForeground overload on Android Q+ while falling back to the 2-parameter version for older APIs.


91-133: Well-organized companion object with proper intent builders.

Constants are appropriately scoped (public for API, private for internal defaults). Intent builders are clean and follow standard patterns. Task name "StreamVideoKeepCallAlive" correctly matches the TypeScript headless task registration in keepCallAliveHeadlessTask.ts.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

return NotificationCompat.Builder(context, channelId)
.setContentTitle(title)
.setContentText(body)
.setOngoing(true)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use NotificationCompat.CallStyle.forOngoingCall for creating notification style. this will make notifications for call alive and telecom consistent.

@greenfrvr greenfrvr changed the base branch from main to feat/callkit-telecom-integration January 19, 2026 10:05
@greenfrvr greenfrvr marked this pull request as ready for review January 19, 2026 10:18
@greenfrvr greenfrvr merged commit 6fe17e4 into feat/callkit-telecom-integration Jan 19, 2026
10 checks passed
@greenfrvr greenfrvr deleted the keepCallAliveChange branch January 19, 2026 10:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants