Skip to content

feat(llc)!: move ringing into RingingFlowCoordinator as StreamVideo.ringing - #1437

Open
renefloor wants to merge 9 commits into
fix/flu-857-coordinator-connectionfrom
feat/flu-856-ringing-call-coordinator
Open

renefloor wants to merge 9 commits into
fix/flu-857-coordinator-connectionfrom
feat/flu-856-ringing-call-coordinator

Conversation

@renefloor

@renefloor renefloor commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Fixes FLU-856

Part of FLU-859 · 22/26, stacked on #1436

What changed

RingingFlowCoordinator (lib/src/ringing/) holds the ringing and push handling that was about 900 lines of StreamVideo. stream_video.dart drops from 1,900 to 965 lines.

  • Public API (v2 breaking):
    • The coordinator is reached through streamVideo.ringing. The ringing methods keep their names on ringing, so migrating is mechanical: streamVideo.observeCoreRingingEvents(...) becomes streamVideo.ringing.observeCoreRingingEvents(...).
    • The old methods on StreamVideo stay as deprecated forwarders to ringing.
    • The deprecated observeCoreCallKitEvents, observe*CallKitEvent and handleVoipPushNotification are removed.
    • RingingFlowCoordinator is an exported interface with only the public API. The implementation, RingingFlowCoordinatorImpl, holds the constructor and the hooks StreamVideo uses (clear, dispose, ringingCalls, handleCoordinatorEvent); it is @internal and not exported.
    • The interface is @sealed (from package:meta): implementing it outside stream_video gives an analyzer warning.
  • Moved into the coordinator:
    • the coordinator's ringing and rejected-by-caller handling (_onEvent's two branches);
    • the native accept, incoming, decline and ended handlers;
    • the displayed-calls check;
    • the incoming verify and consume path;
    • handleRingingFlowNotifications, getCallRingingState, consumeIncomingCall;
    • the auto-reject timers and the dedupe sets.
  • One accept path. _acceptIncomingCall and consumeAndAcceptActiveCall ran the same steps: cancel the timer, guard, consume, accept, and end the native call if it can't be set up. They are now one _acceptRingingCall with two flags:
    • connectFirst, for consumeAndAcceptActiveCall on a cold start;
    • joinAfter, for the native accept event.
  • The ringing and accepted call maps live in MutableClientState. ClientState gains three @internal hooks, markCallAcceptedOnThisDevice, clearCallAcceptedOnThisDevice and releaseRingingCall, so Call talks to the client state alone on accept and on leave. The maps themselves are @internal.
  • Dependencies: the client state, the coordinator client, the push manager (read lazily, so a push manager that reaches for streamVideo.ringing while it is built works), the options, a ringing-call factory, and connect as the ensureConnected callback (C3).
  • Updated call sites: the push package's background handler and StreamVideoPushNotificationManager, dogfooding, and the stream_video_flutter example.

Decisions to check

  • The name is RingingFlowCoordinator, to keep it apart from the per-call CallRingingController in B3. It handles ringing flows across calls and the push and native layers; the controller handles one call's ring.
  • The method names stay as they were. ringing.observeCoreRingingEvents repeats "ringing", but keeping the names makes the migration a search and replace, and dart fix cannot move a method behind a getter. For the same reason the deprecated forwarders have no dart fix rule, and the old CallKit rename rules that pointed at StreamVideo are deleted.
  • No forwarders for markCallAcceptedOnThisDevice, clearCallAcceptedOnThisDevice and releaseRingingCall. They were already @internal on StreamVideo.
  • handleCoordinatorEvent takes every CoordinatorCallRingingEvent, including the caller's own and non-ringing ones. Before, those fell through to the connection, which ignores them, so nothing changes in behaviour.
  • @sealed from package:meta, not a class modifier. sealed, final and base are enforced per library, so the implementation would have to be a part file, and no test anywhere, ours or an app's, could mock ringing. @sealed is per package and only a warning, so a mock can still opt out with an // ignore.
  • The deprecated CallKitEvent typedef stays. It is a type, not a StreamVideo method, and has its own dart fix rule.
  • getCallRingingState reads the call through the coordinator client. It no longer builds a Call and calls get. That Call was never disposed, and its get resolved the call settings, which enumerates the media devices: media work in the background push isolate. The request is the same getCall with ringing, notify and video false.
  • disposeAfterResolvingRinging stays on StreamVideo. It is deprecated already and disposes the client, so it uses ringing.onRingingEvent. It goes away with the deprecation.
  • The _mutedCameraByStateChange and _mutedAudioByStateChange maps stay for C4, as the ticket says.
  • The hooks on ClientState are abstract. Every ClientState implementation now has to provide them; the SDK's own is the only one, and test mocks pick them up from Mock.
  • The coordinator owns the ringing logic, MutableClientState owns the maps. This lets Call write through its client state alone, which B6's CallHost builds on. The alternative is private maps in the coordinator, with Call calling @internal hooks on streamVideo.ringing.

Tests

  • New: test/src/ringing/ringing_flow_coordinator_test.dart (28), on the C2 fixture with a push manager whose native events the test emits:
    • a native decline rejects;
    • a native end rejects on iOS, and on Android only when the system ended it;
    • the auto-reject timer fires, and is cancelled by a decline, by dispose, and by an accept while the user is still connecting (the dispose and accept tests on fakeAsync);
    • a ring the native call screen shows is ended silently when the caller cancelled or the call ended, and kept when this device or the native call screen already accepted it;
    • two incoming events at once verify the call once;
    • handleRingingFlowNotifications for a ringing push, a cancelled one, a missed one and a foreign one;
    • getCallRingingState, also when the call can't be read, and without ringing the call again;
    • consumeAndAcceptActiveCall connects before it consumes, ends the native call when connecting fails, and leaves joining to the app;
    • an accept event accepts without connecting, and joins the call once accepted;
    • two accepts at once fetch and accept the call once;
    • an Android accept while the app is detached is ignored;
    • the deprecated StreamVideo.handleRingingFlowNotifications still forwards;
    • a call accepted elsewhere on the client is handed over.
  • Mutation checks: each of these fails a test: removing the timer start, connectFirst, the accepted-elsewhere check, or the timer cancel on dispose or on accept; flipping joinAfter; dropping either dedupe set or the answered-on-this-device check; not ending the native call on a failed connect; and getCallRingingState ringing the call.
  • Existing tests now call through streamVideo.ringing:
    • verify_displayed_incoming_calls_test, call_accepted_on_this_device_test, ringing_cancelled_by_caller_test;
    • the push package's background_push_handler_test, whose MockStreamVideo now carries a MockRingingFlowCoordinator, with an // ignore: subtype_of_sealed_class.
  • Results:
    • stream_video: 1324 passed;
    • stream_video_flutter without goldens: 729;
    • stream_video_push_notification: 67.

Note

The external docs (incoming-calls guide, ringing tutorial) need a docs-content follow-up for streamVideo.ringing.*.

The first commit picked up the untracked packages/stream_video_flutter/example/macos/Podfile by mistake. The next commit removes it again, so the squash leaves it out.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

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.

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e02711d5-e61f-45ac-be43-16d2506f0455

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

@codecov

codecov Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 73.71134% with 102 lines in your changes missing coverage. Please review.
✅ Project coverage is 44.43%. Comparing base (3be2946) to head (00995b8).

Files with missing lines Patch % Lines
...lib/src/ringing/ringing_call_coordinator_impl.dart 78.06% 68 Missing ⚠️
packages/stream_video/lib/src/stream_video.dart 31.25% 33 Missing ⚠️
...cation/lib/src/stream_video_push_notification.dart 0.00% 1 Missing ⚠️
Additional details and impacted files
@@                          Coverage Diff                           @@
##           fix/flu-857-coordinator-connection    #1437      +/-   ##
======================================================================
+ Coverage                               43.86%   44.43%   +0.57%     
======================================================================
  Files                                     420      421       +1     
  Lines                                   31525    31532       +7     
======================================================================
+ Hits                                    13827    14011     +184     
+ Misses                                  17698    17521     -177     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@renefloor
renefloor added this pull request to stack #1442 October 9, 2026 06:12
@renefloor
renefloor force-pushed the feat/flu-856-ringing-call-coordinator branch from f04dd8f to bcc30a7 Compare October 9, 2026 12:27
@renefloor
renefloor force-pushed the feat/flu-856-ringing-call-coordinator branch 2 times, most recently from 0a0c630 to e959863 Compare October 9, 2026 12:58
@renefloor
renefloor force-pushed the feat/flu-856-ringing-call-coordinator branch 2 times, most recently from 78c2fbf to d31b923 Compare October 9, 2026 13:17
@renefloor
renefloor force-pushed the feat/flu-856-ringing-call-coordinator branch from d31b923 to a8cd297 Compare October 9, 2026 13:37
renefloor and others added 3 commits October 9, 2026 16:28
…inging

The ringing and push handling moves out of StreamVideo into a
RingingCallCoordinator, reached through streamVideo.ringing. The ringing
delegates on StreamVideo and the deprecated CallKit aliases are removed.
The ringing call and accepted call maps move into the client state, which
Call clears on leave. Accepting a call answered on the native call screen
runs through one method for both entry points.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@renefloor
renefloor force-pushed the feat/flu-856-ringing-call-coordinator branch from a8cd297 to 5355808 Compare October 9, 2026 14:35
renefloor and others added 5 commits October 9, 2026 17:16
- Keep the moved ringing methods on StreamVideo as deprecated forwarders.
- Drop the dart fix renames that pointed at StreamVideo methods that no
  longer exist.
- Mark the ringing maps on MutableClientState @internal and restore the
  accept markers' verbose logs.
- Read the push notification manager lazily, so a manager that reaches
  for StreamVideo.ringing while it is built does not hit a late field.
- Test joining after an accept, a failed cold-start connect, the stale
  ring checks, the accept and verification dedupe, and the auto-reject
  cancel on accept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
StreamVideo.ringing is now typed as an exported RingingCallCoordinator
interface carrying only the public ringing API. The implementation,
with the hooks the client drives it through, is an internal
RingingCallCoordinatorImpl that is not exported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The analyzer now warns when a class outside stream_video implements the
interface; the push package's test mock opts out of the warning.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The accept now starts from a disconnected client, so connectUser is
actually awaited while the ring timer runs. The two real-time timer
tests wait longer, and a redundant argument is dropped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@renefloor
renefloor marked this pull request as ready for review October 9, 2026 15:32
@renefloor
renefloor requested a review from a team as a code owner October 9, 2026 15:32
The client-level ringing handler is now RingingFlowCoordinator, with
RingingFlowCoordinatorImpl behind it, so it is no longer confused with
the per-call CallRingingController.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@renefloor renefloor changed the title feat(llc)!: move ringing into RingingCallCoordinator as StreamVideo.ringing feat(llc)!: move ringing into RingingFlowCoordinator as StreamVideo.ringing Oct 9, 2026
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

SDK Size

title v2 branch diff status
android_stream_video 15.1 MB 15.1 MB 0 KB 🟢
android_stream_video_flutter 17.36 MB 17.36 MB 0 KB 🟢
ios_stream_video 15.53 MB 15.56 MB +32 KB 🟢
ios_stream_video_flutter 18.31 MB 18.35 MB +32 KB 🟢
pub_stream_video 1.0 MB 1.0 MB 0 KB 🟢
pub_stream_video_filters 0.02 MB 0.02 MB 0 KB 🟢
pub_stream_video_flutter 1.0 MB 1.0 MB 0 KB 🟢
pub_stream_video_noise_cancellation 0.05 MB 0.05 MB 0 KB 🟢
pub_stream_video_push_notification 0.36 MB 0.36 MB 0 KB 🟢
pub_stream_video_screen_sharing 0.01 MB 0.01 MB 0 KB 🟢

Measured on 3b993e4.

@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

Size Breakdown

What each SDK adds to an empty release app, per Dart package and native library. Dart sizes are uncompressed symbol sizes. Open the sdk-size-analysis artifacts of this run in the DevTools App Size tool to drill down further.

Measured on 3b993e4.

android · stream_video: 15.04 MB (+47.2 KB)
Component Size Δ vs v2
native:libjingle_peerconnection_so.so 11.96 MB 0
package:stream_video 1.07 MB +38.0 KB
classes.dex 256.7 KB 0
dart:@unknown 254.0 KB +4.2 KB
dart:_http 153.9 KB -0.0 KB
package:protobuf 145.0 KB +0.0 KB
dart:mixin_deduplication 137.0 KB +3.7 KB
native:libdartjni.so 121.7 KB 0
dart:io 99.3 KB -0.0 KB
dart:async 77.1 KB -0.0 KB
package:stream_core 65.8 KB +0.0 KB
package:stream_webrtc_flutter 56.6 KB -0.0 KB
package:rxdart 54.3 KB +0.0 KB
dart:core 52.2 KB -0.0 KB
package:dio 52.0 KB -0.0 KB
other (71) 535.6 KB +1.3 KB
android · stream_video_flutter: 17.28 MB (+16.2 KB)
Component Size Δ vs v2
native:libjingle_peerconnection_so.so 11.96 MB 0
package:stream_video 1.12 MB +27.5 KB
package:flutter 692.3 KB -0.1 KB
classes.dex 447.0 KB 0
dart:@unknown 423.6 KB +2.0 KB
dart:mixin_deduplication 326.3 KB -15.0 KB
package:stream_video_flutter 194.1 KB +0.2 KB
dart:_http 154.1 KB -0.0 KB
package:protobuf 147.0 KB +0.0 KB
dart:io 136.4 KB -0.0 KB
native:libdartjni.so 121.7 KB 0
assets 118.5 KB 0
package:stream_core_flutter 114.3 KB -0.0 KB
package:hive_ce 99.2 KB +0.0 KB
dart:@shared 81.4 KB +1.0 KB
other (98) 1.22 MB +0.5 KB
ios · stream_video: 15.55 MB (+53.9 KB)
Component Size Δ vs v2
native:StreamWebRTC.framework 11.82 MB 0
package:stream_video 1.19 MB +43.4 KB
Runner 625.3 KB 0
dart:@unknown 251.0 KB +4.6 KB
dart:_http 166.0 KB 0
native:objective_c.framework 164.2 KB 0
package:protobuf 155.2 KB -0.0 KB
dart:mixin_deduplication 145.0 KB +3.9 KB
dart:io 95.5 KB -0.0 KB
dart:async 84.5 KB -0.0 KB
package:stream_core 74.3 KB +0.0 KB
package:stream_webrtc_flutter 64.6 KB +0.3 KB
dart:@shared 62.8 KB +1.4 KB
package:rxdart 59.0 KB -0.0 KB
package:dio 57.1 KB +0.0 KB
other (71) 586.2 KB +0.3 KB
ios · stream_video_flutter: 18.3 MB (+24.4 KB)
Component Size Δ vs v2
native:StreamWebRTC.framework 11.82 MB 0
package:stream_video 1.25 MB +32.8 KB
Runner 1.16 MB 0
package:flutter 788.7 KB -0.0 KB
dart:@unknown 424.5 KB +2.1 KB
dart:mixin_deduplication 337.3 KB -12.5 KB
package:stream_video_flutter 209.1 KB +0.2 KB
Frameworks 171.3 KB 0
dart:_http 166.2 KB -0.0 KB
native:objective_c.framework 164.2 KB 0
package:protobuf 157.1 KB +0.0 KB
dart:io 136.5 KB +0.0 KB
package:stream_core_flutter 123.8 KB +0.0 KB
dart:@shared 113.2 KB +1.5 KB
package:hive_ce 107.1 KB -0.0 KB
other (98) 1.23 MB +0.5 KB

This branch has not been deployed

No deployments
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.

1 participant