-
Notifications
You must be signed in to change notification settings - Fork 59
Comparing changes
Open a pull request
base repository: GetStream/stream-video-android
base: v1.23.1
head repository: GetStream/stream-video-android
compare: v1.24.0
- 14 commits
- 67 files changed
- 6 contributors
Commits on May 13, 2026
-
Configuration menu - View commit details
-
Copy full SHA for 05eb0e0 - Browse repository at this point
Copy the full SHA 05eb0e0View commit details -
Configuration menu - View commit details
-
Copy full SHA for 2cbf76f - Browse repository at this point
Copy the full SHA 2cbf76fView commit details
Commits on May 14, 2026
-
Add CallJoinInterceptor hook for call activation (#1679)
* feat: add RingingCallJoinInterceptor * feat: remove dead code * feat: update test cases * feat: update logic to run in parallel * feat: remove DefaultRingingCallJoinInterceptor * chore: refactor the interface name * feat: Introduce Call Join Interceptor * chore: improve direct call screen ui * chore: remove logs, remove testing timeouts * chore: minor improvements * chore: add unit tests * chore: refactor * chore: refactor * chore: refactor * fix: disable USE_CALL_JOIN_INTERCEPTOR by default * Clear callJoinInterceptor after RingingState Active transition Co-authored-by: Aleksandar Apostolov <apostolov.alexandar@gmail.com> --------- Co-authored-by: Aleksandar Apostolov <apostolov.alexandar@gmail.com>
Configuration menu - View commit details
-
Copy full SHA for f76343f - Browse repository at this point
Copy the full SHA f76343fView commit details -
Configuration menu - View commit details
-
Copy full SHA for 178d47c - Browse repository at this point
Copy the full SHA 178d47cView commit details -
Remove unneeded direct Dokka dependency (#1680)
Co-authored-by: Aleksandar Apostolov <apostolov.alexandar@gmail.com>
Configuration menu - View commit details
-
Copy full SHA for 09cb9c9 - Browse repository at this point
Copy the full SHA 09cb9c9View commit details
Commits on May 19, 2026
-
Participant sorting presets (SDK parity) (#1684)
* test(core): add failing regression test for sortedParticipants channelFlow bug SortedParticipantsState wraps its sort logic in a channelFlow whose builder block launches two coroutines on the outer scope and returns immediately. The channel closes as soon as the block returns, so all subsequent trySend calls from inside the launches go to a closed channel and are silently dropped. The existing tests in CallStateTest pass because the global DispatcherRule installs UnconfinedTestDispatcher, which runs launched coroutines inline up to first suspension and lets the initial sort squeak through before the channel closes. Under any realistic production dispatcher (Main, Default, IO), the bug surfaces: sortedParticipants never emits, OrientationVideoRenderer always falls back to the unsorted participants map for >6-participant calls. This test isolates SortedParticipantsState with StandardTestDispatcher to surface the production behavior. Expected red on current develop; will go green after the StateFlow migration. * feat(core): add sort comparator primitives and visibility-aware decorators Introduces a React-parity sorting toolkit under stream-video-android-core/sorting: - combineComparators / conditional / ifInvisible / ifInvisibleOrUnknown composable building blocks that return -1, 0, +1 directly (no compareBy(Boolean) inversion bug) - dominantSpeaker, speaking, screenSharing, publishingVideo, publishingAudio, raisedHand, byReactionType, byName, byJoinedAt, byRole, bySourcePriority primitive comparators on ParticipantState - pinned (internal) — local pin > server pin > pinnedAt recency bySourcePriority replaces the previous participantSourceRank, fixing the SIP=RTSP=2 collision by taking a vararg priority list and ranking by argument position. ifInvisible vs ifInvisibleOrUnknown lets presets choose how strict the visibility guard is — UNKNOWN counts as visible for the former, as invisible for the latter. Adds 25 unit tests (15 primitive truth tables, 10 combinator/decorator behaviors) verified against the React equivalents in stream-video-js. Test fixtures support the A-F React fixture shape and 15-participant rolling-scroll scenarios. * feat(core): add SortPreset sealed interface with Default, SpeakerLayout, AudioRoom Three named presets for the participant grid: - Default: screenSharing → pinned → ifInvisibleOrUnknown(dominantSpeaker → speaking → raisedHand → ingressSource → publishingVideo → publishingAudio). The continuous- scroll grid choice — UNKNOWN-as-invisible so off-screen tiles get ordered by activity before they render. - SpeakerLayout: screenSharing → pinned → dominantSpeaker → ifInvisible(...). The dominant speaker is outside the visibility guard so the spotlight always reflects who's talking. - AudioRoom: ifInvisibleOrUnknown(...) → byRole(admin, host, speaker). Role-aware ordering for livestream-style rooms. The pinned comparator is internal (its PinUpdateAtTime parameter type is internal). Presets resolve via an internal `SortPreset.build(pins)` extension invoked by the sorter before each pass, so the latest local/server pin map participates in the ordering. Tests cover the React-parity composed-comparator scenarios, three Default-preset rolling-scroll scenarios (15 participants, visible window P8..P12), the SpeakerLayout visibility-guard difference, and the AudioRoom role priority. 13 tests total, all green. * fix(core): rewrite SortedParticipantsState to back sortedParticipants with StateFlow The previous implementation wrapped sorting in a channelFlow whose builder body launched two coroutines on the outer scope and returned immediately. The channel closed before any collector subscribed, so subsequent trySend calls hit a closed channel and the public sortedParticipants flow never emitted under any non- Unconfined dispatcher. OrientationVideoRenderer's `if (size > 6) sortedParticipants else participants` always took the fallback path; the renderer effectively never applied sorting in production. The rewrite: - Backs the public list with a MutableStateFlow<List<ParticipantState>>. - Drives sorting from a combine over (participants, pinned-detailed, preset, custom-comparator) plus a separate launch for call.events. Both write into the same _sortedParticipants.value via a synchronous resort() that preserves the previous-order stability trick and skips emission when the order is unchanged. - Accepts a SortPreset (default SortPreset.Default) and exposes setPreset(...) for runtime switching. updateComparator(Comparator) stays as an escape hatch for ad-hoc orderings. - CallState now derives an internal _pinnedParticipantsDetailed flow combining _localPins + _serverPins while preserving PinUpdateAtTime (which carries the PinType). Local pins override server pins on key collision. The public pinnedParticipants StateFlow keeps its existing Map<String, OffsetDateTime> shape via mapState — no surface-level change. The earlier regression test now passes (was red on develop). Two existing CallStateTest sorting tests adjusted: switched stateIn target to backgroundScope to avoid runTest leak detection, and updated the expected order to reflect the new Default preset behavior (screen-sharer leads pinned because screenSharing is the first comparator in the chain). * refactor(compose): LivestreamPlayer consumes sortedParticipants; remove duplicated source rank The default livestream picker now reads call.state.sortedParticipants and selects the first participant with videoEnabled. Because the Default SortPreset's bySourcePriority sits inside the visibility guard and viewers in a livestream context typically render as UNKNOWN, the ingress host (RTMP / WHIP / SRT / RTSP) naturally sorts first and the picker resolves to them. This deletes the local participantSourceRank(s: ParticipantSource) helper added in PR #1681, which duplicated the rank table in DefaultSortOrder.kt and carried the same SIP=RTSP=2 collision. The canonical bySourcePriority(vararg) replaces it. DefaultSortOrder.kt is deleted entirely — its behavior is subsumed by SortPresets. * test(core): add SortedParticipantsState flow-behavior tests Six tests covering the reactive surface of the new StateFlow-backed SortedParticipantsState under StandardTestDispatcher: - participants flow change emits an updated sorted list - pinned flow change pushes the pinned participant to the front - preset switch triggers a resort using the new preset - custom comparator overrides the active preset until cleared - repeated identical sorts are coalesced (same list instance returned, no redundant emission) - call event triggers a resort against current participant + pin state, catching state changes that bypass the participants map (e.g. event handlers flipping dominantSpeaker on a ParticipantState directly) * chore(core): apiDump for sorting changes + spotless formatting Updates stream-video-android-core.api to reflect the new public sorting surface: - SortPreset sealed interface with Default, SpeakerLayout, AudioRoom data objects - Top-level comparator primitives (dominantSpeaker, speaking, screenSharing, publishingVideo, publishingAudio, raisedHand, byName, byJoinedAt) - combineComparators, conditional, ifInvisible, ifInvisibleOrUnknown combinators - bySourcePriority, byRole, byReactionType comparator factories - CallState.setSortPreset(...) — runtime preset switching - CallState.getSortedParticipants() return type narrowed from Flow to StateFlow The Flow → StateFlow narrowing is a binary-compat break for Java consumers but source-compatible for Kotlin callers (StateFlow IS-A Flow). Acceptable under the v2 breaking-change allowance per CLAUDE.md. Spotless reflowed ComparatorsTest and SortPresetsTest line breaks; no logic changes. * refactor(core): consolidate participants and sortedParticipants into a single sorted flow CallState.participants now sources from _sortedParticipantsState.sortedParticipants — it always returns the list ordered by the active SortPreset. The old sortedParticipants property is kept as a @deprecated WARNING alias that points at participants, so existing callers compile and run unchanged with a migration nudge. This collapses the dual-flow architecture that was the root cause of a renderer bug discovered during visual smoke testing: OrientationVideoRenderer's val callParticipants by remember(participants) { derivedStateOf { if (sortedParticipants.size > 6) sortedParticipants else participants } } held two separate StateFlow subscriptions for the same conceptual list, with remember keyed only on `participants`. The dual subscription created subtle staleness and lifecycle interactions with the DisposableEffect setVisibility calls that drive SFU track subscriptions — tiles rendered in the correct sort order but their video tracks failed to display. With a single sorted flow, OrientationVideoRenderer collapses to: val callParticipants by call.state.participants.collectAsStateWithLifecycle() remoteParticipants also moves to derive from the sorted participants flow (filtering out the local sessionId), so it's consistently sorted with participants. Lazy-initialized to defer the forward reference to _sortedParticipantsState. LivestreamPlayer's default livestreamFlow and the existing CallStateTest sorting tests switched off the deprecated alias in the same pass. Net effect: - One conceptual list, one StateFlow subscription, no dual-flow staleness - Strictly fewer recompositions in scroll-heavy paths thanks to the sorter's lastSortOrder coalescing (visibility flips that don't change order no longer emit downstream) - React parity restored — both SDKs expose participants as the single sorted list - The previously-dead `if (size > 6) sortedParticipants` branch is gone * test(core): cover visible-block stability end-to-end through the live flow Two reactive-path tests added to SortedParticipantsStateTest: - `visible-block internal order survives off-screen dominant-speaker promotion`: 15 participants with P8..P12 marked VISIBLE, P15 (off-screen) gains dominantSpeaker. The call event drives a resort. Assertions: P15 jumps to index 0, the visible block keeps [P8, P9, P10, P11, P12] internal order. - `visible block stays stable when a visible participant gains a signal`: P10 (inside the viewport) becomes dominantSpeaker. The visible/visible predicate returns 0 for every pair in the visible block, so the list does NOT reshuffle — P10 stays at its original index instead of jumping to the top. These exercise the full reactive path that the existing SortPresetsTest suite skips: direct field mutation on ParticipantState (mirroring the SDK's DominantSpeakerChangedEvent handler), the call.events trigger, the combine collect, the lastSortOrder coalescing, and the final StateFlow emission. Closes the verification gap between the static comparator tests and the visual smoke check. * feat(core): add byUserId tiebreaker to all sort presets, mirroring iOS Appends `ifInvisibleOrUnknown(byUserId)` (or `ifInvisible(byUserId)` for SpeakerLayout) as a trailing decorator on each preset. Matches iOS's pattern of `ifInvisibleBy(userId)` as the last comparator in every preset. Effect: when two participants share every other signal (no dominant-speaker, no pin, no raised hand, no ingress source advantage, equal track state), the sort falls back to lexicographic userId compare instead of stable input order. The visible block keeps its internal relative order (visible/visible pairs return 0 regardless of byUserId), and the invisible tail becomes deterministic across re-sorts and across sessions. In production, userIds are UUIDs so the resulting tail order is meaningless to humans — but it's STABLE. That's the value: no surprise reshuffles when a sort fires with no meaningful state change. Tests: - New `byUserId` primitive truth-table tests in ParticipantComparatorsTest. - Updated `Default preset - two off-screen promotions bubble...` expectation to the new lex-ordered tail, with an explanatory comment. - Updated `custom comparator overrides preset until cleared` to expect [a,b,c] after setPreset(Default) — the tiebreaker actively orders by userId now, replacing the previous "all signals 0 → stable input order" behavior. For AudioRoom, the tiebreaker runs AFTER byRole(...) so role priority still wins on equal participants — the test `AudioRoom - host role beats regular participant` now passes without modification. API additive: `getByUserId()` exposed alongside the other primitive comparators. * fix(core): preserve binary compatibility of sortedParticipants return type The previous commit narrowed CallState.sortedParticipants from Flow to StateFlow, which is binary-incompatible for Java callers compiled against the v1 API. Reverts the public return type of the deprecated `sortedParticipants` alias back to `Flow<List<ParticipantState>>`. The underlying value is still a StateFlow (returned via `get() = participants`), so consumers see identical runtime behavior; only the declared method signature stays on the wider Flow type. apiCheck against develop now shows only additive changes: setSortPreset(...), the SortPreset sealed interface + 3 data objects, and the new comparator primitives/combinators in the io.getstream.video.android.core.sorting package. No removed or modified public symbols. * refactor(compose): keep LivestreamPlayer's source-priority sort self-contained Re-applies bySourcePriority(RTMP, WHIP, SRT, RTSP) inside the LivestreamPlayer's default livestreamFlow before picking the first video-enabled participant. The grid-level preset already orders ingress sources first, but the explicit local sort isolates the picker from any future change to the active grid preset — swapping the grid preset cannot accidentally affect which participant the livestream player picks as the host. Stable sort preserves the upstream grid order among same-source participants, so no additional ordering rules need to be encoded here. Uses the canonical bySourcePriority comparator from the sorting package; no local rank table. * refactor(core): wire CallType.sortPreset and rename AudioRoom preset Adds an open `sortPreset` property on the `CallType` sealed class so each call type can declare its default participant sort preset, matching React's `CallType` → `sortParticipantsBy` association. `CallType.Livestream` overrides to `SortPreset.LivestreamOrAudioRoom`; other types stay on `SortPreset.Default`. `CallState` resolves the preset on construction via `CallType.fromName(call.type)?.sortPreset ?: SortPreset.Default` and passes it as `initialPreset` to `SortedParticipantsState`. Callers can still override at runtime via `setSortPreset(...)` or `updateParticipantSortingOrder(...)`. `SortPreset.AudioRoom` renamed to `SortPreset.LivestreamOrAudioRoom` for cross-platform naming parity (matches iOS `livestreamOrAudioRoomSortPreset` and React `livestreamOrAudioRoomSortPreset`). Rename is internal to this PR — nothing on develop referenced the old name. `CallType.AudioCall` (Android-only 1:1 audio concept) intentionally stays on the default preset; a dedicated `CallType.AudioRoom` for the actual group-audio call type will land in a follow-up PR. `LivestreamPlayer`'s local source-priority sort stays in place as defense in depth — if anything changes the active preset at runtime, the host picker is still correct. 6 new CallTypeSortPresetTest cases verifying the resolution path. apiCheck against develop: still additive only. * fix(core): address CodeRabbit review - mutex + defensive preset fallback - Adds an `else` branch to `SortPreset.build(...)` so unknown SortPreset implementations (Java consumers can implement Kotlin sealed interfaces on JVM targets below 17) fall back to Default rather than throwing NoWhenBranchMatchedException. Sorting must never crash a call. - Wraps SortedParticipantsState.resort() body in a Mutex. The state combine collector and the call.events collector both call resort(), which mutates lastSortOrder and _sortedParticipants. Production CallState scope today is single-threaded (Dispatchers.Main), but the contract should hold regardless of dispatcher choice. Mutex contention is negligible — resort body is microseconds of sync work. Tests: all 55 sorting cases still pass.
Configuration menu - View commit details
-
Copy full SHA for 21ecaa0 - Browse repository at this point
Copy the full SHA 21ecaa0View commit details -
Configuration menu - View commit details
-
Copy full SHA for 3c79cf6 - Browse repository at this point
Copy the full SHA 3c79cf6View commit details
Commits on May 20, 2026
-
feat(core): add CallType.AudioRoom for the audio_room server call type (
#1685) Adds CallType.AudioRoom (name = "audio_room") to mirror the same-named call type used by React and iOS. Overrides sortPreset to LivestreamOrAudioRoom so group audio chats self-sort with role-aware ordering out of the box. Distinct from CallType.AudioCall (Android-specific 1:1 voice call). AudioCall keeps SortPreset.Default — role-based sort doesn't apply to a two-participant call. Updates CallType.fromName to include AudioRoom in the lookup list. Tests: extends CallTypeSortPresetTest with assertions for AudioRoom.sortPreset and the fromName("audio_room") resolution path. apiCheck vs develop: additive only.
Configuration menu - View commit details
-
Copy full SHA for 56e8e46 - Browse repository at this point
Copy the full SHA 56e8e46View commit details -
Improve pinned participant state on rejoin with new session (#1683)
* update sfu models * feat: add pins param in ParticipantJoinedEvent * feat: add logic with logs * feat: remove logs * fix: correct the jetpack compose logic to render pinned icon * fix: remove logs * chore: revert changes * chore: revert changes * chore: remove finally * fix: fix unit tests * feat: update codebase with develop * chore: align with JS logic * fix: refactor pins ownership, move them to PinManager * fix: add unit-tests * fix: remove duplicate code * chore: add logic to updateServer Pins from list of participants with pin information * chore: refactor code * chore: Replace PinUpdateAtTime with PinEntry --------- Co-authored-by: Aleksandar Apostolov <apostolov.alexandar@gmail.com>
Configuration menu - View commit details
-
Copy full SHA for 799baec - Browse repository at this point
Copy the full SHA 799baecView commit details -
Configuration menu - View commit details
-
Copy full SHA for 178892f - Browse repository at this point
Copy the full SHA 178892fView commit details
Commits on May 21, 2026
-
Chore(deps): Bump the stream-conventions group and update dependabot …
…config (#1692) * Chore(deps): Bump GetStream/stream-build-conventions-android/.github/workflows/sdk-size-checks.yml Bumps [GetStream/stream-build-conventions-android/.github/workflows/sdk-size-checks.yml](https://github.com/getstream/stream-build-conventions-android) from 0.12.1 to 0.13.1. - [Release notes](https://github.com/getstream/stream-build-conventions-android/releases) - [Commits](GetStream/stream-build-conventions-android@v0.12.1...v0.13.1) --- updated-dependencies: - dependency-name: GetStream/stream-build-conventions-android/.github/workflows/sdk-size-checks.yml dependency-version: 0.13.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * Bump stream-build-conventions-android to 0.13.1 * Group reusable-workflow updates under stream-conventions * Bump streamConventions plugins to 0.13.1 --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Gian <47775302+gpunto@users.noreply.github.com>
Configuration menu - View commit details
-
Copy full SHA for 21203e5 - Browse repository at this point
Copy the full SHA 21203e5View commit details
Commits on May 26, 2026
-
Upgrade to
m145webrtc and noise-cancellation libv3.0.0(#1678)* upgrade to m145 webrtc and noise-cancellation * Update to webrtc m145 and corresponding noiseCancellation lib * Remove the snapshot repo resolution
Configuration menu - View commit details
-
Copy full SHA for 2e50889 - Browse repository at this point
Copy the full SHA 2e50889View commit details -
Configuration menu - View commit details
-
Copy full SHA for 4fc3437 - Browse repository at this point
Copy the full SHA 4fc3437View commit details -
Configuration menu - View commit details
-
Copy full SHA for d395d7a - Browse repository at this point
Copy the full SHA d395d7aView commit details
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff v1.23.1...v1.24.0