Skip to content

Commit 956abe4

Browse files
authored
feat(llc): ring individual call member (#1111)
* individual call ringing * tweak
1 parent e884fb7 commit 956abe4

28 files changed

+773
-24
lines changed

packages/stream_video/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## Unreleased
22

3+
✅ Added
4+
- Introduced a new method in the `Call` class that allows ringing individual members of an existing call. Use `call.ring(userId: ['userId'])` to have the backend send a VoIP/ringing push notification to the user's devices. Note: the user must first be a member of the call (use `call.addMembers()` if needed).
5+
36
🐞 Fixed
47
- Resolved an issue that could cause the StreamVideo instance to be disposed prematurely before ringing events were fully processed when handling ringing notifications in the terminated state.
58

packages/stream_video/lib/globals.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import 'package:stream_webrtc_flutter/stream_webrtc_flutter.dart' as webrtc;
33

44
const String streamSdkName = 'stream-flutter';
55
const String streamVideoVersion = '0.11.2';
6-
const String openapiModelsVersion = '190.7.0';
6+
const String openapiModelsVersion = '202.0.0';
77
const String protocolModelsVersion = '1.40.1';
88
const String androidWebRTCVersion = webrtc.androidWebRTCVersion;
99
const String iosWebRTCVersion = webrtc.iosWebRTCVersion;

packages/stream_video/lib/open_api/video/coordinator/api.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ part 'model/permission_request_event.dart';
189189
part 'model/pin_request.dart';
190190
part 'model/pin_response.dart';
191191
part 'model/privacy_settings.dart';
192+
part 'model/published_track_flags.dart';
192193
part 'model/publisher_stats_response.dart';
193194
part 'model/push_preferences.dart';
194195
part 'model/quality_score_report.dart';
@@ -218,6 +219,8 @@ part 'model/report_response.dart';
218219
part 'model/request_permission_request.dart';
219220
part 'model/request_permission_response.dart';
220221
part 'model/response.dart';
222+
part 'model/ring_call_request.dart';
223+
part 'model/ring_call_response.dart';
221224
part 'model/ring_settings_request.dart';
222225
part 'model/ring_settings_response.dart';
223226
part 'model/sdk_usage_report.dart';

packages/stream_video/lib/open_api/video/coordinator/api/productvideo_api.dart

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2192,6 +2192,88 @@ class ProductvideoApi {
21922192
return null;
21932193
}
21942194

2195+
/// Ring Call Users
2196+
///
2197+
/// Sends a ring notification to the provided users who are not already in the call. All users should be members of the call Sends events: - call.ring
2198+
///
2199+
/// Note: This method returns the HTTP [Response].
2200+
///
2201+
/// Parameters:
2202+
///
2203+
/// * [String] type (required):
2204+
///
2205+
/// * [String] id (required):
2206+
///
2207+
/// * [RingCallRequest] ringCallRequest (required):
2208+
/// RingCallRequest
2209+
Future<Response> ringCallWithHttpInfo(
2210+
String type,
2211+
String id,
2212+
RingCallRequest ringCallRequest,
2213+
) async {
2214+
// ignore: prefer_const_declarations
2215+
final path = r'/video/call/{type}/{id}/ring'
2216+
.replaceAll('{type}', type)
2217+
.replaceAll('{id}', id);
2218+
2219+
// ignore: prefer_final_locals
2220+
Object? postBody = ringCallRequest;
2221+
2222+
final queryParams = <QueryParam>[];
2223+
final headerParams = <String, String>{};
2224+
final formParams = <String, String>{};
2225+
2226+
const contentTypes = <String>['application/json'];
2227+
2228+
return apiClient.invokeAPI(
2229+
path,
2230+
'POST',
2231+
queryParams,
2232+
postBody,
2233+
headerParams,
2234+
formParams,
2235+
contentTypes.isEmpty ? null : contentTypes.first,
2236+
);
2237+
}
2238+
2239+
/// Ring Call Users
2240+
///
2241+
/// Sends a ring notification to the provided users who are not already in the call. All users should be members of the call Sends events: - call.ring
2242+
///
2243+
/// Parameters:
2244+
///
2245+
/// * [String] type (required):
2246+
///
2247+
/// * [String] id (required):
2248+
///
2249+
/// * [RingCallRequest] ringCallRequest (required):
2250+
/// RingCallRequest
2251+
Future<RingCallResponse?> ringCall(
2252+
String type,
2253+
String id,
2254+
RingCallRequest ringCallRequest,
2255+
) async {
2256+
final response = await ringCallWithHttpInfo(
2257+
type,
2258+
id,
2259+
ringCallRequest,
2260+
);
2261+
if (response.statusCode >= HttpStatus.badRequest) {
2262+
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
2263+
}
2264+
// When a remote server returns no body with a status of 204, we shall not decode it.
2265+
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
2266+
// FormatException when trying to decode an empty string.
2267+
if (response.body.isNotEmpty &&
2268+
response.statusCode != HttpStatus.noContent) {
2269+
return await apiClient.deserializeAsync(
2270+
await _decodeBodyBytes(response),
2271+
'RingCallResponse',
2272+
) as RingCallResponse;
2273+
}
2274+
return null;
2275+
}
2276+
21952277
/// Send custom event
21962278
///
21972279
/// Sends custom event to the call Sends events: - custom

packages/stream_video/lib/open_api/video/coordinator/api_client.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,8 @@ class ApiClient {
548548
return PinResponse.fromJson(value);
549549
case 'PrivacySettings':
550550
return PrivacySettings.fromJson(value);
551+
case 'PublishedTrackFlags':
552+
return PublishedTrackFlags.fromJson(value);
551553
case 'PublisherStatsResponse':
552554
return PublisherStatsResponse.fromJson(value);
553555
case 'PushPreferences':
@@ -606,6 +608,10 @@ class ApiClient {
606608
return RequestPermissionResponse.fromJson(value);
607609
case 'DurationResponse':
608610
return DurationResponse.fromJson(value);
611+
case 'RingCallRequest':
612+
return RingCallRequest.fromJson(value);
613+
case 'RingCallResponse':
614+
return RingCallResponse.fromJson(value);
609615
case 'RingSettingsRequest':
610616
return RingSettingsRequest.fromJson(value);
611617
case 'RingSettingsResponse':

packages/stream_video/lib/open_api/video/coordinator/model/audio_settings_request.dart

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class AudioSettingsRequest {
1515
AudioSettingsRequest({
1616
this.accessRequestEnabled,
1717
required this.defaultDevice,
18+
this.hifiAudioEnabled,
1819
this.micDefaultOn,
1920
this.noiseCancellation,
2021
this.opusDtxEnabled,
@@ -32,6 +33,14 @@ class AudioSettingsRequest {
3233

3334
AudioSettingsRequestDefaultDeviceEnum defaultDevice;
3435

36+
///
37+
/// Please note: This property should have been non-nullable! Since the specification file
38+
/// does not include a default value (using the "default:" property), however, the generated
39+
/// source code must fall back to having a nullable type.
40+
/// Consider adding a "default:" property in the specification file to hide this note.
41+
///
42+
bool? hifiAudioEnabled;
43+
3544
///
3645
/// Please note: This property should have been non-nullable! Since the specification file
3746
/// does not include a default value (using the "default:" property), however, the generated
@@ -78,6 +87,7 @@ class AudioSettingsRequest {
7887
other is AudioSettingsRequest &&
7988
other.accessRequestEnabled == accessRequestEnabled &&
8089
other.defaultDevice == defaultDevice &&
90+
other.hifiAudioEnabled == hifiAudioEnabled &&
8191
other.micDefaultOn == micDefaultOn &&
8292
other.noiseCancellation == noiseCancellation &&
8393
other.opusDtxEnabled == opusDtxEnabled &&
@@ -89,6 +99,7 @@ class AudioSettingsRequest {
8999
// ignore: unnecessary_parenthesis
90100
(accessRequestEnabled == null ? 0 : accessRequestEnabled!.hashCode) +
91101
(defaultDevice.hashCode) +
102+
(hifiAudioEnabled == null ? 0 : hifiAudioEnabled!.hashCode) +
92103
(micDefaultOn == null ? 0 : micDefaultOn!.hashCode) +
93104
(noiseCancellation == null ? 0 : noiseCancellation!.hashCode) +
94105
(opusDtxEnabled == null ? 0 : opusDtxEnabled!.hashCode) +
@@ -97,7 +108,7 @@ class AudioSettingsRequest {
97108

98109
@override
99110
String toString() =>
100-
'AudioSettingsRequest[accessRequestEnabled=$accessRequestEnabled, defaultDevice=$defaultDevice, micDefaultOn=$micDefaultOn, noiseCancellation=$noiseCancellation, opusDtxEnabled=$opusDtxEnabled, redundantCodingEnabled=$redundantCodingEnabled, speakerDefaultOn=$speakerDefaultOn]';
111+
'AudioSettingsRequest[accessRequestEnabled=$accessRequestEnabled, defaultDevice=$defaultDevice, hifiAudioEnabled=$hifiAudioEnabled, micDefaultOn=$micDefaultOn, noiseCancellation=$noiseCancellation, opusDtxEnabled=$opusDtxEnabled, redundantCodingEnabled=$redundantCodingEnabled, speakerDefaultOn=$speakerDefaultOn]';
101112

102113
Map<String, dynamic> toJson() {
103114
final json = <String, dynamic>{};
@@ -107,6 +118,11 @@ class AudioSettingsRequest {
107118
json[r'access_request_enabled'] = null;
108119
}
109120
json[r'default_device'] = this.defaultDevice;
121+
if (this.hifiAudioEnabled != null) {
122+
json[r'hifi_audio_enabled'] = this.hifiAudioEnabled;
123+
} else {
124+
json[r'hifi_audio_enabled'] = null;
125+
}
110126
if (this.micDefaultOn != null) {
111127
json[r'mic_default_on'] = this.micDefaultOn;
112128
} else {
@@ -160,6 +176,7 @@ class AudioSettingsRequest {
160176
mapValueOfType<bool>(json, r'access_request_enabled'),
161177
defaultDevice: AudioSettingsRequestDefaultDeviceEnum.fromJson(
162178
json[r'default_device'])!,
179+
hifiAudioEnabled: mapValueOfType<bool>(json, r'hifi_audio_enabled'),
163180
micDefaultOn: mapValueOfType<bool>(json, r'mic_default_on'),
164181
noiseCancellation:
165182
NoiseCancellationSettings.fromJson(json[r'noise_cancellation']),

packages/stream_video/lib/open_api/video/coordinator/model/audio_settings_response.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class AudioSettingsResponse {
1515
AudioSettingsResponse({
1616
required this.accessRequestEnabled,
1717
required this.defaultDevice,
18+
required this.hifiAudioEnabled,
1819
required this.micDefaultOn,
1920
this.noiseCancellation,
2021
required this.opusDtxEnabled,
@@ -26,6 +27,8 @@ class AudioSettingsResponse {
2627

2728
AudioSettingsResponseDefaultDeviceEnum defaultDevice;
2829

30+
bool hifiAudioEnabled;
31+
2932
bool micDefaultOn;
3033

3134
///
@@ -48,6 +51,7 @@ class AudioSettingsResponse {
4851
other is AudioSettingsResponse &&
4952
other.accessRequestEnabled == accessRequestEnabled &&
5053
other.defaultDevice == defaultDevice &&
54+
other.hifiAudioEnabled == hifiAudioEnabled &&
5155
other.micDefaultOn == micDefaultOn &&
5256
other.noiseCancellation == noiseCancellation &&
5357
other.opusDtxEnabled == opusDtxEnabled &&
@@ -59,6 +63,7 @@ class AudioSettingsResponse {
5963
// ignore: unnecessary_parenthesis
6064
(accessRequestEnabled.hashCode) +
6165
(defaultDevice.hashCode) +
66+
(hifiAudioEnabled.hashCode) +
6267
(micDefaultOn.hashCode) +
6368
(noiseCancellation == null ? 0 : noiseCancellation!.hashCode) +
6469
(opusDtxEnabled.hashCode) +
@@ -67,12 +72,13 @@ class AudioSettingsResponse {
6772

6873
@override
6974
String toString() =>
70-
'AudioSettingsResponse[accessRequestEnabled=$accessRequestEnabled, defaultDevice=$defaultDevice, micDefaultOn=$micDefaultOn, noiseCancellation=$noiseCancellation, opusDtxEnabled=$opusDtxEnabled, redundantCodingEnabled=$redundantCodingEnabled, speakerDefaultOn=$speakerDefaultOn]';
75+
'AudioSettingsResponse[accessRequestEnabled=$accessRequestEnabled, defaultDevice=$defaultDevice, hifiAudioEnabled=$hifiAudioEnabled, micDefaultOn=$micDefaultOn, noiseCancellation=$noiseCancellation, opusDtxEnabled=$opusDtxEnabled, redundantCodingEnabled=$redundantCodingEnabled, speakerDefaultOn=$speakerDefaultOn]';
7176

7277
Map<String, dynamic> toJson() {
7378
final json = <String, dynamic>{};
7479
json[r'access_request_enabled'] = this.accessRequestEnabled;
7580
json[r'default_device'] = this.defaultDevice;
81+
json[r'hifi_audio_enabled'] = this.hifiAudioEnabled;
7682
json[r'mic_default_on'] = this.micDefaultOn;
7783
if (this.noiseCancellation != null) {
7884
json[r'noise_cancellation'] = this.noiseCancellation;
@@ -110,6 +116,7 @@ class AudioSettingsResponse {
110116
mapValueOfType<bool>(json, r'access_request_enabled')!,
111117
defaultDevice: AudioSettingsResponseDefaultDeviceEnum.fromJson(
112118
json[r'default_device'])!,
119+
hifiAudioEnabled: mapValueOfType<bool>(json, r'hifi_audio_enabled')!,
113120
micDefaultOn: mapValueOfType<bool>(json, r'mic_default_on')!,
114121
noiseCancellation:
115122
NoiseCancellationSettings.fromJson(json[r'noise_cancellation']),
@@ -175,6 +182,7 @@ class AudioSettingsResponse {
175182
static const requiredKeys = <String>{
176183
'access_request_enabled',
177184
'default_device',
185+
'hifi_audio_enabled',
178186
'mic_default_on',
179187
'opus_dtx_enabled',
180188
'redundant_coding_enabled',

packages/stream_video/lib/open_api/video/coordinator/model/call_closed_caption.dart

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,51 +14,85 @@ class CallClosedCaption {
1414
/// Returns a new [CallClosedCaption] instance.
1515
CallClosedCaption({
1616
required this.endTime,
17+
required this.id,
18+
required this.language,
19+
this.service,
1720
required this.speakerId,
1821
required this.startTime,
1922
required this.text,
23+
required this.translated,
2024
required this.user,
2125
});
2226

2327
DateTime endTime;
2428

29+
String id;
30+
31+
String language;
32+
33+
///
34+
/// Please note: This property should have been non-nullable! Since the specification file
35+
/// does not include a default value (using the "default:" property), however, the generated
36+
/// source code must fall back to having a nullable type.
37+
/// Consider adding a "default:" property in the specification file to hide this note.
38+
///
39+
String? service;
40+
2541
String speakerId;
2642

2743
DateTime startTime;
2844

2945
String text;
3046

47+
bool translated;
48+
3149
UserResponse user;
3250

3351
@override
3452
bool operator ==(Object other) =>
3553
identical(this, other) ||
3654
other is CallClosedCaption &&
3755
other.endTime == endTime &&
56+
other.id == id &&
57+
other.language == language &&
58+
other.service == service &&
3859
other.speakerId == speakerId &&
3960
other.startTime == startTime &&
4061
other.text == text &&
62+
other.translated == translated &&
4163
other.user == user;
4264

4365
@override
4466
int get hashCode =>
4567
// ignore: unnecessary_parenthesis
4668
(endTime.hashCode) +
69+
(id.hashCode) +
70+
(language.hashCode) +
71+
(service == null ? 0 : service!.hashCode) +
4772
(speakerId.hashCode) +
4873
(startTime.hashCode) +
4974
(text.hashCode) +
75+
(translated.hashCode) +
5076
(user.hashCode);
5177

5278
@override
5379
String toString() =>
54-
'CallClosedCaption[endTime=$endTime, speakerId=$speakerId, startTime=$startTime, text=$text, user=$user]';
80+
'CallClosedCaption[endTime=$endTime, id=$id, language=$language, service=$service, speakerId=$speakerId, startTime=$startTime, text=$text, translated=$translated, user=$user]';
5581

5682
Map<String, dynamic> toJson() {
5783
final json = <String, dynamic>{};
5884
json[r'end_time'] = this.endTime.toUtc().toIso8601String();
85+
json[r'id'] = this.id;
86+
json[r'language'] = this.language;
87+
if (this.service != null) {
88+
json[r'service'] = this.service;
89+
} else {
90+
json[r'service'] = null;
91+
}
5992
json[r'speaker_id'] = this.speakerId;
6093
json[r'start_time'] = this.startTime.toUtc().toIso8601String();
6194
json[r'text'] = this.text;
95+
json[r'translated'] = this.translated;
6296
json[r'user'] = this.user;
6397
return json;
6498
}
@@ -85,9 +119,13 @@ class CallClosedCaption {
85119

86120
return CallClosedCaption(
87121
endTime: mapDateTime(json, r'end_time', r'')!,
122+
id: mapValueOfType<String>(json, r'id')!,
123+
language: mapValueOfType<String>(json, r'language')!,
124+
service: mapValueOfType<String>(json, r'service'),
88125
speakerId: mapValueOfType<String>(json, r'speaker_id')!,
89126
startTime: mapDateTime(json, r'start_time', r'')!,
90127
text: mapValueOfType<String>(json, r'text')!,
128+
translated: mapValueOfType<bool>(json, r'translated')!,
91129
user: UserResponse.fromJson(json[r'user'])!,
92130
);
93131
}
@@ -146,9 +184,12 @@ class CallClosedCaption {
146184
/// The list of required keys that must be present in a JSON.
147185
static const requiredKeys = <String>{
148186
'end_time',
187+
'id',
188+
'language',
149189
'speaker_id',
150190
'start_time',
151191
'text',
192+
'translated',
152193
'user',
153194
};
154195
}

0 commit comments

Comments
 (0)