feat(videocall): complete signaling integration, group call support, and UI polish
- Wire VIDEO_CALL_REQUEST/CANCEL/REFUSE/ROOM_COUNT commands via CommandsRepository - Add VideocallChatType enum (single/multi) with chatType stored in state - Implement auto-login to Juphoon SDK using sanitized email + user UUID - Add runtime camera/microphone permissions before call start - Add RetryInterceptor for transient TLS/socket errors in Dio - Migrate VideocallItem to Freezed with isTalking extension - Implement startGroupCall/leaveGroupCall using ChannelService with participant grid - Add PopScope to intercept back navigation during active calls - Redesign idle screen with device option cards and group call button - Redesign active call UI with video overlay, PiP local view, and new controls layout - Clean up SDK wrapper: remove unused streams, merge destroy+dispose into shutdown - Add i18n keys for videocall UI across 6 locales
This commit is contained in:
@@ -213,3 +213,70 @@ Nota: `@` y `.` se reemplazan por `_` en room numbers y userIDs.
|
|||||||
- Schematics: `~/Downloads/schematics _2025.03.26 (2)/`
|
- Schematics: `~/Downloads/schematics _2025.03.26 (2)/`
|
||||||
- pub.dev: https://pub.dev/packages/jc_sdk
|
- pub.dev: https://pub.dev/packages/jc_sdk
|
||||||
- Consola: https://developer.juphoon.com
|
- Consola: https://developer.juphoon.com
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Flujos de llamada (protocolo TCP + Juphoon SDK)
|
||||||
|
|
||||||
|
### APP → Reloj (outgoing)
|
||||||
|
|
||||||
|
1. App envía `VIDEO_CALL_REQUEST` al backend con `chatType`, `appAccount`, `roomNumber`, `sessionId`
|
||||||
|
2. Backend reenvía la notificación al reloj via TCP
|
||||||
|
3. App inicia audio/cámara y llama al watch account via SDK (`startCall(userId: "w_<IMEI>")`)
|
||||||
|
4. Reloj contesta → SDK notifica via `callItemUpdateStream` (estado `isTalking`)
|
||||||
|
5. App envía `VIDEO_CALL_ROOM_COUNT_REQUEST` con `type` (0/1), `count: 2`, `room_num`
|
||||||
|
|
||||||
|
### Reloj → APP (incoming)
|
||||||
|
|
||||||
|
1. Reloj envía notificación de llamada al backend
|
||||||
|
2. Backend notifica a la app (requiere app abierta con SDK inicializado, ver Fase 9)
|
||||||
|
3. SDK detecta llamada entrante via `callItemAddStream` con `CallDirection.incoming`
|
||||||
|
4. Usuario acepta → `answerCall()` → SDK conecta
|
||||||
|
5. Reloj reporta participantes al backend
|
||||||
|
|
||||||
|
### Colgar / Rechazar
|
||||||
|
|
||||||
|
- **Colgar (en llamada):** `hangUp()` en SDK + `VIDEO_CALL_CANCEL` al backend
|
||||||
|
- **Rechazar (incoming):** `hangUp()` en SDK + `VIDEO_CALL_REFUSE` al backend con `appAccount`, `roomNumber`
|
||||||
|
|
||||||
|
### Convenciones de nombres
|
||||||
|
|
||||||
|
| Campo | Formato | Ejemplo |
|
||||||
|
|---|---|---|
|
||||||
|
| Watch userID | `w_` + IMEI | `w_000078932675810` |
|
||||||
|
| Mobile userID | `p_` + email sanitizado | `p_user_example_com` |
|
||||||
|
| Room (single) | `deviceId` + `_` + appAccount | `0245423235_p_user_example_com` |
|
||||||
|
| Room (group) | `deviceId` + `_group` | `0245423235_group` |
|
||||||
|
| Session ID | `deviceId` + `_` + epoch en segundos | `0245423235_1714150800` |
|
||||||
|
|
||||||
|
Sanitización: `@` y `.` se reemplazan por `_` en userIDs y roomNumbers.
|
||||||
|
|
||||||
|
### Configuración del SDK por tipo de dispositivo
|
||||||
|
|
||||||
|
- RTOS watches: `MediaConfig.MODE_RTOS`
|
||||||
|
- Android watches: `MediaConfig.MODE_INTELLIGENT_HARDWARE`
|
||||||
|
- Se determina con `device.capabilities.system` (`isRtos` / `isAndroid`)
|
||||||
|
|
||||||
|
### Auto-login
|
||||||
|
|
||||||
|
- userId: `p_` + email sanitizado (ej: `p_julian_test_com`)
|
||||||
|
- password: `user.id` (UUID del usuario padre)
|
||||||
|
- En dev/testing `autoCreateAccount = true` permite login con cualquier password
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Limitaciones actuales
|
||||||
|
|
||||||
|
### Recepción de llamadas requiere app abierta
|
||||||
|
|
||||||
|
La app debe estar en primer plano con el SDK inicializado y el client logueado para recibir llamadas entrantes. Si el app está en background o cerrada, las llamadas no llegan. Esto se resuelve en Fase 9 (Push/Background).
|
||||||
|
|
||||||
|
### Sin timeout de llamada
|
||||||
|
|
||||||
|
El protocolo menciona un límite de 5 min por llamada, pero no está implementado en la app. El reloj podría manejar el corte por su lado.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pendientes por verificar
|
||||||
|
|
||||||
|
- **chatType**: El protocolo TCP usa `0` (single) y `1` (multi) como enteros. Nuestra app envía `"single"`/`"multi"` como strings en el JSON del comando. Verificar que el backend hace la conversión correctamente antes de enviar al reloj via TCP.
|
||||||
|
|||||||
@@ -128,6 +128,15 @@ class DeviceManagementScreen extends ConsumerWidget {
|
|||||||
text: context.translate(I18n.callWatch),
|
text: context.translate(I18n.callWatch),
|
||||||
),
|
),
|
||||||
gap,
|
gap,
|
||||||
|
AppMenuButton(
|
||||||
|
color: primaryColor,
|
||||||
|
onPressed: () =>
|
||||||
|
navigationContract.pushTo(AppRoutes.videocall),
|
||||||
|
icon: Icons.videocam_outlined,
|
||||||
|
iconSize: SizeUtils.getByScreen(small: 42, big: 40),
|
||||||
|
text: context.translate(I18n.videocallTitle),
|
||||||
|
),
|
||||||
|
gap,
|
||||||
AppMenuButton(
|
AppMenuButton(
|
||||||
color: primaryColor,
|
color: primaryColor,
|
||||||
onPressed: () =>
|
onPressed: () =>
|
||||||
|
|||||||
@@ -1,22 +1,30 @@
|
|||||||
|
import '../../domain/entities/videocall_chat_type.dart';
|
||||||
|
|
||||||
abstract class VideocallSignalingDatasource {
|
abstract class VideocallSignalingDatasource {
|
||||||
Future<void> initiateCall({
|
Future<void> initiateCall({
|
||||||
required String deviceId,
|
required String deviceIdentificator,
|
||||||
|
required VideocallChatType chatType,
|
||||||
|
required String appAccount,
|
||||||
|
required String roomNumber,
|
||||||
|
required String sessionId,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> cancelCall({
|
||||||
|
required String deviceIdentificator,
|
||||||
|
required VideocallChatType chatType,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> refuseCall({
|
||||||
|
required String deviceIdentificator,
|
||||||
|
required VideocallChatType chatType,
|
||||||
required String appAccount,
|
required String appAccount,
|
||||||
required String roomNumber,
|
required String roomNumber,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> cancelCall({required String deviceId});
|
Future<void> reportRoomCount({
|
||||||
|
required String deviceIdentificator,
|
||||||
Future<void> refuseCall({
|
required VideocallChatType chatType,
|
||||||
required String deviceId,
|
|
||||||
required String roomNumber,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<int> getRoomParticipantCount({required String roomNumber});
|
|
||||||
|
|
||||||
Future<void> reportParticipantCount({
|
|
||||||
required String roomNumber,
|
|
||||||
required int count,
|
required int count,
|
||||||
required int type,
|
required String roomNumber,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,50 +1,86 @@
|
|||||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
import 'package:legacy_device_state/legacy_device_state.dart';
|
||||||
|
|
||||||
|
import '../../domain/entities/videocall_chat_type.dart';
|
||||||
import 'videocall_signaling_datasource.dart';
|
import 'videocall_signaling_datasource.dart';
|
||||||
|
|
||||||
class VideocallSignalingDatasourceImpl implements VideocallSignalingDatasource {
|
class VideocallSignalingDatasourceImpl implements VideocallSignalingDatasource {
|
||||||
VideocallSignalingDatasourceImpl(this._repository);
|
VideocallSignalingDatasourceImpl(this._commandsRepository);
|
||||||
|
|
||||||
final SaveFamilyRepository _repository;
|
final CommandsRepository _commandsRepository;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> initiateCall({
|
Future<void> initiateCall({
|
||||||
required String deviceId,
|
required String deviceIdentificator,
|
||||||
|
required VideocallChatType chatType,
|
||||||
required String appAccount,
|
required String appAccount,
|
||||||
required String roomNumber,
|
required String roomNumber,
|
||||||
|
required String sessionId,
|
||||||
}) async {
|
}) async {
|
||||||
throw UnimplementedError(
|
await _commandsRepository.send(
|
||||||
'Backend signaling API not yet available. Waiting for endpoint spec.');
|
request: SendCommandRequestModel(
|
||||||
|
device: deviceIdentificator,
|
||||||
|
command: DeviceCommand.videoCallRequest,
|
||||||
|
data: {
|
||||||
|
'chatType': chatType.value,
|
||||||
|
'appAccount': appAccount,
|
||||||
|
'roomNumber': roomNumber,
|
||||||
|
'sessionId': sessionId,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> cancelCall({required String deviceId}) async {
|
Future<void> cancelCall({
|
||||||
throw UnimplementedError(
|
required String deviceIdentificator,
|
||||||
'Backend signaling API not yet available. Waiting for endpoint spec.');
|
required VideocallChatType chatType,
|
||||||
|
}) async {
|
||||||
|
await _commandsRepository.send(
|
||||||
|
request: SendCommandRequestModel(
|
||||||
|
device: deviceIdentificator,
|
||||||
|
command: DeviceCommand.videoCallCancel,
|
||||||
|
data: {'chatType': chatType.value},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> refuseCall({
|
Future<void> refuseCall({
|
||||||
required String deviceId,
|
required String deviceIdentificator,
|
||||||
|
required VideocallChatType chatType,
|
||||||
|
required String appAccount,
|
||||||
required String roomNumber,
|
required String roomNumber,
|
||||||
}) async {
|
}) async {
|
||||||
throw UnimplementedError(
|
await _commandsRepository.send(
|
||||||
'Backend signaling API not yet available. Waiting for endpoint spec.');
|
request: SendCommandRequestModel(
|
||||||
|
device: deviceIdentificator,
|
||||||
|
command: DeviceCommand.videoCallRefuse,
|
||||||
|
data: {
|
||||||
|
'chatType': chatType.value,
|
||||||
|
'appAccount': appAccount,
|
||||||
|
'roomNumber': roomNumber,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> getRoomParticipantCount({required String roomNumber}) async {
|
Future<void> reportRoomCount({
|
||||||
throw UnimplementedError(
|
required String deviceIdentificator,
|
||||||
'Backend signaling API not yet available. Waiting for endpoint spec.');
|
required VideocallChatType chatType,
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> reportParticipantCount({
|
|
||||||
required String roomNumber,
|
|
||||||
required int count,
|
required int count,
|
||||||
required int type,
|
required String roomNumber,
|
||||||
}) async {
|
}) async {
|
||||||
throw UnimplementedError(
|
await _commandsRepository.send(
|
||||||
'Backend signaling API not yet available. Waiting for endpoint spec.');
|
request: SendCommandRequestModel(
|
||||||
|
device: deviceIdentificator,
|
||||||
|
command: DeviceCommand.videoCallRoomCountRequest,
|
||||||
|
data: {
|
||||||
|
'type': chatType == VideocallChatType.single ? 0 : 1,
|
||||||
|
'count': count,
|
||||||
|
'room_num': roomNumber,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import '../../domain/entities/videocall_chat_type.dart';
|
||||||
import '../../domain/repositories/videocall_signaling_repository.dart';
|
import '../../domain/repositories/videocall_signaling_repository.dart';
|
||||||
import '../datasources/videocall_signaling_datasource.dart';
|
import '../datasources/videocall_signaling_datasource.dart';
|
||||||
|
|
||||||
@@ -8,45 +9,59 @@ class VideocallSignalingRepositoryImpl implements VideocallSignalingRepository {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> initiateCall({
|
Future<void> initiateCall({
|
||||||
required String deviceId,
|
required String deviceIdentificator,
|
||||||
|
required VideocallChatType chatType,
|
||||||
|
required String appAccount,
|
||||||
|
required String roomNumber,
|
||||||
|
required String sessionId,
|
||||||
|
}) {
|
||||||
|
return _datasource.initiateCall(
|
||||||
|
deviceIdentificator: deviceIdentificator,
|
||||||
|
chatType: chatType,
|
||||||
|
appAccount: appAccount,
|
||||||
|
roomNumber: roomNumber,
|
||||||
|
sessionId: sessionId,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> cancelCall({
|
||||||
|
required String deviceIdentificator,
|
||||||
|
required VideocallChatType chatType,
|
||||||
|
}) {
|
||||||
|
return _datasource.cancelCall(
|
||||||
|
deviceIdentificator: deviceIdentificator,
|
||||||
|
chatType: chatType,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> refuseCall({
|
||||||
|
required String deviceIdentificator,
|
||||||
|
required VideocallChatType chatType,
|
||||||
required String appAccount,
|
required String appAccount,
|
||||||
required String roomNumber,
|
required String roomNumber,
|
||||||
}) {
|
}) {
|
||||||
return _datasource.initiateCall(
|
return _datasource.refuseCall(
|
||||||
deviceId: deviceId,
|
deviceIdentificator: deviceIdentificator,
|
||||||
|
chatType: chatType,
|
||||||
appAccount: appAccount,
|
appAccount: appAccount,
|
||||||
roomNumber: roomNumber,
|
roomNumber: roomNumber,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> cancelCall({required String deviceId}) {
|
Future<void> reportRoomCount({
|
||||||
return _datasource.cancelCall(deviceId: deviceId);
|
required String deviceIdentificator,
|
||||||
}
|
required VideocallChatType chatType,
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> refuseCall({
|
|
||||||
required String deviceId,
|
|
||||||
required String roomNumber,
|
|
||||||
}) {
|
|
||||||
return _datasource.refuseCall(deviceId: deviceId, roomNumber: roomNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<int> getRoomParticipantCount({required String roomNumber}) {
|
|
||||||
return _datasource.getRoomParticipantCount(roomNumber: roomNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> reportParticipantCount({
|
|
||||||
required String roomNumber,
|
|
||||||
required int count,
|
required int count,
|
||||||
required int type,
|
required String roomNumber,
|
||||||
}) {
|
}) {
|
||||||
return _datasource.reportParticipantCount(
|
return _datasource.reportRoomCount(
|
||||||
roomNumber: roomNumber,
|
deviceIdentificator: deviceIdentificator,
|
||||||
|
chatType: chatType,
|
||||||
count: count,
|
count: count,
|
||||||
type: type,
|
roomNumber: roomNumber,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
enum VideocallChatType {
|
||||||
|
single,
|
||||||
|
multi;
|
||||||
|
|
||||||
|
String get value => name;
|
||||||
|
}
|
||||||
@@ -20,4 +20,5 @@ enum VideocallScreenMode {
|
|||||||
outgoing,
|
outgoing,
|
||||||
incoming,
|
incoming,
|
||||||
inCall,
|
inCall,
|
||||||
|
groupCall,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,30 @@
|
|||||||
|
import '../entities/videocall_chat_type.dart';
|
||||||
|
|
||||||
abstract class VideocallSignalingRepository {
|
abstract class VideocallSignalingRepository {
|
||||||
Future<void> initiateCall({
|
Future<void> initiateCall({
|
||||||
required String deviceId,
|
required String deviceIdentificator,
|
||||||
|
required VideocallChatType chatType,
|
||||||
|
required String appAccount,
|
||||||
|
required String roomNumber,
|
||||||
|
required String sessionId,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> cancelCall({
|
||||||
|
required String deviceIdentificator,
|
||||||
|
required VideocallChatType chatType,
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<void> refuseCall({
|
||||||
|
required String deviceIdentificator,
|
||||||
|
required VideocallChatType chatType,
|
||||||
required String appAccount,
|
required String appAccount,
|
||||||
required String roomNumber,
|
required String roomNumber,
|
||||||
});
|
});
|
||||||
|
|
||||||
Future<void> cancelCall({required String deviceId});
|
Future<void> reportRoomCount({
|
||||||
|
required String deviceIdentificator,
|
||||||
Future<void> refuseCall({
|
required VideocallChatType chatType,
|
||||||
required String deviceId,
|
|
||||||
required String roomNumber,
|
|
||||||
});
|
|
||||||
|
|
||||||
Future<int> getRoomParticipantCount({required String roomNumber});
|
|
||||||
|
|
||||||
Future<void> reportParticipantCount({
|
|
||||||
required String roomNumber,
|
|
||||||
required int count,
|
required int count,
|
||||||
required int type,
|
required String roomNumber,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ import 'group_call_state.dart';
|
|||||||
|
|
||||||
part 'group_call_controller.g.dart';
|
part 'group_call_controller.g.dart';
|
||||||
|
|
||||||
|
const _channelCapacity = 6;
|
||||||
|
const _channelHeartbeatTime = 20;
|
||||||
|
const _channelHeartbeatTimeout = 60;
|
||||||
|
const _channelFramerate = 24;
|
||||||
|
const _channelVideoRatio = 1.78;
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
class GroupCallController extends _$GroupCallController {
|
class GroupCallController extends _$GroupCallController {
|
||||||
late VideocallChannelService _channelService;
|
late VideocallChannelService _channelService;
|
||||||
@@ -51,7 +57,16 @@ class GroupCallController extends _$GroupCallController {
|
|||||||
await _channelService.enableUploadAudioStream(true);
|
await _channelService.enableUploadAudioStream(true);
|
||||||
await _channelService.enableUploadVideoStream(true);
|
await _channelService.enableUploadVideoStream(true);
|
||||||
|
|
||||||
final ok = await _channelService.join(channelId);
|
final joinParam = JoinParam();
|
||||||
|
joinParam.capacity = _channelCapacity;
|
||||||
|
joinParam.heartbeatTime = _channelHeartbeatTime;
|
||||||
|
joinParam.heartbeatTimeout = _channelHeartbeatTimeout;
|
||||||
|
joinParam.framerate = _channelFramerate;
|
||||||
|
joinParam.videoRatio = _channelVideoRatio;
|
||||||
|
joinParam.smooth = true;
|
||||||
|
joinParam.maxResolution = 0;
|
||||||
|
|
||||||
|
final ok = await _channelService.join(channelId, joinParam: joinParam);
|
||||||
if (!ref.mounted) return;
|
if (!ref.mounted) return;
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ final class GroupCallControllerProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$groupCallControllerHash() =>
|
String _$groupCallControllerHash() =>
|
||||||
r'0d3c5bd234ef3ed76b0b0f7666ddb73c8b98be55';
|
r'e0c417dc7b6669fb801b17d8b04ed6dc814b42f3';
|
||||||
|
|
||||||
abstract class _$GroupCallController extends $Notifier<GroupCallState> {
|
abstract class _$GroupCallController extends $Notifier<GroupCallState> {
|
||||||
GroupCallState build();
|
GroupCallState build();
|
||||||
|
|||||||
@@ -1,26 +1,40 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
import 'package:sf_shared/sf_shared.dart';
|
import 'package:sf_shared/sf_shared.dart';
|
||||||
import 'package:videocall_sdk/videocall_sdk.dart' hide VideocallState;
|
import 'package:videocall_sdk/videocall_sdk.dart' hide VideocallState;
|
||||||
|
|
||||||
|
import '../../domain/entities/videocall_chat_type.dart';
|
||||||
import '../../domain/entities/videocall_error.dart';
|
import '../../domain/entities/videocall_error.dart';
|
||||||
|
import '../../domain/repositories/videocall_signaling_repository.dart';
|
||||||
|
import '../../providers/videocall_signaling_repository_provider.dart';
|
||||||
|
import 'group_call_controller.dart';
|
||||||
import 'videocall_state.dart';
|
import 'videocall_state.dart';
|
||||||
|
|
||||||
part 'videocall_controller.g.dart';
|
part 'videocall_controller.g.dart';
|
||||||
|
|
||||||
|
const _cameraWidth = 640;
|
||||||
|
const _cameraHeight = 360;
|
||||||
|
const _cameraFps = 30;
|
||||||
|
const _videoAngle = 0;
|
||||||
|
const _maxCalls = 1;
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
class VideocallController extends _$VideocallController {
|
class VideocallController extends _$VideocallController {
|
||||||
late VideocallSdkManager _manager;
|
late VideocallSdkManager _manager;
|
||||||
late VideocallCallService _callService;
|
late VideocallCallService _callService;
|
||||||
late VideocallDeviceService _deviceService;
|
late VideocallDeviceService _deviceService;
|
||||||
late VideocallClient _client;
|
late VideocallClient _client;
|
||||||
|
late VideocallSignalingRepository _signaling;
|
||||||
|
|
||||||
StreamSubscription<VideocallItem>? _callAddSub;
|
StreamSubscription<VideocallItem>? _callAddSub;
|
||||||
StreamSubscription<VideocallItem>? _callUpdateSub;
|
StreamSubscription<VideocallItem>? _callUpdateSub;
|
||||||
StreamSubscription<({int reason, String description})>? _callRemoveSub;
|
StreamSubscription<({int reason, String description})>? _callRemoveSub;
|
||||||
StreamSubscription<VideocallItem>? _missedCallSub;
|
StreamSubscription<VideocallItem>? _missedCallSub;
|
||||||
StreamSubscription<VideocallClientState>? _clientStateSub;
|
StreamSubscription<VideocallClientState>? _clientStateSub;
|
||||||
|
bool _wasInCall = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
VideocallState build() {
|
VideocallState build() {
|
||||||
@@ -28,44 +42,72 @@ class VideocallController extends _$VideocallController {
|
|||||||
_callService = ref.read(videocallCallServiceProvider);
|
_callService = ref.read(videocallCallServiceProvider);
|
||||||
_deviceService = ref.read(videocallDeviceServiceProvider);
|
_deviceService = ref.read(videocallDeviceServiceProvider);
|
||||||
_client = ref.read(videocallClientProvider);
|
_client = ref.read(videocallClientProvider);
|
||||||
|
_signaling = ref.read(videocallSignalingRepositoryProvider);
|
||||||
|
|
||||||
final device = ref.read(selectedDeviceProvider).value;
|
final device = ref.read(selectedDeviceProvider).value;
|
||||||
final deviceId = device?.identificator ?? '';
|
final deviceId = device?.identificator ?? '';
|
||||||
|
|
||||||
ref.onDispose(_disposeSubscriptions);
|
ref.onDispose(() {
|
||||||
|
_callAddSub?.cancel();
|
||||||
|
_callUpdateSub?.cancel();
|
||||||
|
_callRemoveSub?.cancel();
|
||||||
|
_missedCallSub?.cancel();
|
||||||
|
_clientStateSub?.cancel();
|
||||||
|
if (_wasInCall) {
|
||||||
|
_callService.hangUp();
|
||||||
|
_deviceService.stopAudio();
|
||||||
|
_deviceService.stopCamera();
|
||||||
|
}
|
||||||
|
});
|
||||||
Future.microtask(_initSdk);
|
Future.microtask(_initSdk);
|
||||||
|
|
||||||
return VideocallState(deviceId: deviceId);
|
return VideocallState(deviceId: deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _initSdk() async {
|
Future<void> _initSdk() async {
|
||||||
if (_manager.isInitialized) {
|
debugPrint('[Videocall] _initSdk: isInitialized=${_manager.isInitialized}');
|
||||||
_subscribeToStreams();
|
if (!_manager.isInitialized) {
|
||||||
await _configureDevice();
|
final ok = await _manager.initialize();
|
||||||
|
debugPrint('[Videocall] _initSdk: initialize result=$ok');
|
||||||
if (!ref.mounted) return;
|
if (!ref.mounted) return;
|
||||||
state = state.copyWith(isSdkReady: true);
|
if (!ok) {
|
||||||
return;
|
state = state.copyWith(
|
||||||
}
|
errorEvent: VideocallErrorEvent.sdkInitialization,
|
||||||
|
);
|
||||||
final ok = await _manager.initialize();
|
return;
|
||||||
if (!ref.mounted) return;
|
}
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
state = state.copyWith(errorEvent: VideocallErrorEvent.sdkInitialization);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_subscribeToStreams();
|
_subscribeToStreams();
|
||||||
await _configureDevice();
|
await _configureDevice();
|
||||||
if (!ref.mounted) return;
|
if (!ref.mounted) return;
|
||||||
state = state.copyWith(isSdkReady: true);
|
await _autoLogin();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _autoLogin() async {
|
||||||
|
final user = await ref.read(userInfoProvider.future);
|
||||||
|
final sanitizedEmail = user.email.replaceAll('@', '_').replaceAll('.', '_');
|
||||||
|
final userId = 'p_$sanitizedEmail';
|
||||||
|
debugPrint('[Videocall] _autoLogin: userId=$userId');
|
||||||
|
final ok = await _client.login(userId: userId, password: user.id);
|
||||||
|
debugPrint(
|
||||||
|
'[Videocall] _autoLogin: login result=$ok, mounted=${ref.mounted}',
|
||||||
|
);
|
||||||
|
if (!ref.mounted) return;
|
||||||
|
if (!ok) {
|
||||||
|
debugPrint('[Videocall] _autoLogin: login FAILED');
|
||||||
|
state = state.copyWith(errorEvent: VideocallErrorEvent.authentication);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
debugPrint('[Videocall] _autoLogin: setting isSdkReady=true');
|
||||||
|
state = state.copyWith(localUserId: userId, isSdkReady: true);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _configureDevice() async {
|
Future<void> _configureDevice() async {
|
||||||
_deviceService.setDefaultSpeakerOn(true);
|
_deviceService.setDefaultSpeakerOn(true);
|
||||||
_deviceService.setCameraProperty(640, 360, 30);
|
_deviceService.setCameraProperty(_cameraWidth, _cameraHeight, _cameraFps);
|
||||||
_deviceService.setVideoAngle(0);
|
_deviceService.setVideoAngle(_videoAngle);
|
||||||
_callService.setMaxCallNum(1);
|
_callService.setMaxCallNum(_maxCalls);
|
||||||
_callService.setTermWhenNetDisconnected(true);
|
_callService.setTermWhenNetDisconnected(true);
|
||||||
|
|
||||||
final device = ref.read(selectedDeviceProvider).value;
|
final device = ref.read(selectedDeviceProvider).value;
|
||||||
@@ -88,29 +130,81 @@ class VideocallController extends _$VideocallController {
|
|||||||
_clientStateSub = _client.stateStream.listen(_onClientStateChange);
|
_clientStateSub = _client.stateStream.listen(_onClientStateChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> login({required String userId, required String password}) async {
|
String _sanitize(String value) =>
|
||||||
final ok = await _client.login(userId: userId, password: password);
|
value.replaceAll('@', '_').replaceAll('.', '_');
|
||||||
if (!ref.mounted) return false;
|
|
||||||
if (!ok) {
|
String _buildRoomNumber(
|
||||||
state = state.copyWith(errorEvent: VideocallErrorEvent.authentication);
|
String deviceId,
|
||||||
|
String appAccount,
|
||||||
|
VideocallChatType chatType,
|
||||||
|
) {
|
||||||
|
final sanitizedAccount = _sanitize(appAccount);
|
||||||
|
return chatType == VideocallChatType.multi
|
||||||
|
? '${deviceId}_group'
|
||||||
|
: '${deviceId}_$sanitizedAccount';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _buildSessionId(String deviceId) {
|
||||||
|
return '${deviceId}_${DateTime.now().millisecondsSinceEpoch ~/ 1000}';
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<bool> _requestMediaPermissions() async {
|
||||||
|
final statuses = await [Permission.camera, Permission.microphone].request();
|
||||||
|
final cameraGranted = statuses[Permission.camera]?.isGranted ?? false;
|
||||||
|
final micGranted = statuses[Permission.microphone]?.isGranted ?? false;
|
||||||
|
if (!cameraGranted) {
|
||||||
|
state = state.copyWith(errorEvent: VideocallErrorEvent.cameraPermission);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!micGranted) {
|
||||||
|
state = state.copyWith(
|
||||||
|
errorEvent: VideocallErrorEvent.microphonePermission,
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
state = state.copyWith(localUserId: userId);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> startCall(String remoteUserId) async {
|
Future<void> startCall(String remoteUserId) async {
|
||||||
|
final permissionsGranted = await _requestMediaPermissions();
|
||||||
|
if (!permissionsGranted || !ref.mounted) return;
|
||||||
|
_wasInCall = true;
|
||||||
|
|
||||||
final device = ref.read(selectedDeviceProvider).value;
|
final device = ref.read(selectedDeviceProvider).value;
|
||||||
|
final deviceId = device?.identificator ?? '';
|
||||||
final targetUserId = remoteUserId.isNotEmpty
|
final targetUserId = remoteUserId.isNotEmpty
|
||||||
? remoteUserId
|
? remoteUserId
|
||||||
: 'w_${device?.imei ?? ''}';
|
: 'w_${device?.imei ?? ''}';
|
||||||
|
|
||||||
|
final chatType = state.chatType;
|
||||||
|
final roomNumber = _buildRoomNumber(deviceId, state.localUserId, chatType);
|
||||||
|
final sessionId = _buildSessionId(deviceId);
|
||||||
|
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
remoteUserId: targetUserId,
|
remoteUserId: targetUserId,
|
||||||
screenMode: VideocallScreenMode.outgoing,
|
screenMode: VideocallScreenMode.outgoing,
|
||||||
errorEvent: null,
|
errorEvent: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _signaling.initiateCall(
|
||||||
|
deviceIdentificator: deviceId,
|
||||||
|
chatType: chatType,
|
||||||
|
appAccount: state.localUserId,
|
||||||
|
roomNumber: roomNumber,
|
||||||
|
sessionId: sessionId,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('[Videocall] initiateCall signaling failed: $error');
|
||||||
|
if (!ref.mounted) return;
|
||||||
|
state = state.copyWith(
|
||||||
|
screenMode: VideocallScreenMode.idle,
|
||||||
|
errorEvent: VideocallErrorEvent.callStart,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ref.mounted) return;
|
||||||
|
|
||||||
await _deviceService.startAudio();
|
await _deviceService.startAudio();
|
||||||
await _deviceService.startCamera();
|
await _deviceService.startCamera();
|
||||||
|
|
||||||
@@ -125,6 +219,10 @@ class VideocallController extends _$VideocallController {
|
|||||||
if (!ok) {
|
if (!ok) {
|
||||||
await _deviceService.stopAudio();
|
await _deviceService.stopAudio();
|
||||||
await _deviceService.stopCamera();
|
await _deviceService.stopCamera();
|
||||||
|
await _signaling.cancelCall(
|
||||||
|
deviceIdentificator: deviceId,
|
||||||
|
chatType: chatType,
|
||||||
|
);
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
screenMode: VideocallScreenMode.idle,
|
screenMode: VideocallScreenMode.idle,
|
||||||
errorEvent: VideocallErrorEvent.callStart,
|
errorEvent: VideocallErrorEvent.callStart,
|
||||||
@@ -132,7 +230,70 @@ class VideocallController extends _$VideocallController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> startGroupCall() async {
|
||||||
|
final permissionsGranted = await _requestMediaPermissions();
|
||||||
|
if (!permissionsGranted || !ref.mounted) return;
|
||||||
|
|
||||||
|
final device = ref.read(selectedDeviceProvider).value;
|
||||||
|
final deviceId = device?.identificator ?? '';
|
||||||
|
final chatType = VideocallChatType.multi;
|
||||||
|
final roomNumber = _buildRoomNumber(deviceId, state.localUserId, chatType);
|
||||||
|
final sessionId = _buildSessionId(deviceId);
|
||||||
|
|
||||||
|
state = state.copyWith(
|
||||||
|
chatType: chatType,
|
||||||
|
screenMode: VideocallScreenMode.groupCall,
|
||||||
|
errorEvent: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await _signaling.initiateCall(
|
||||||
|
deviceIdentificator: deviceId,
|
||||||
|
chatType: chatType,
|
||||||
|
appAccount: state.localUserId,
|
||||||
|
roomNumber: roomNumber,
|
||||||
|
sessionId: sessionId,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('[Videocall] initiateCall group signaling failed: $error');
|
||||||
|
if (!ref.mounted) return;
|
||||||
|
state = state.copyWith(
|
||||||
|
chatType: VideocallChatType.single,
|
||||||
|
screenMode: VideocallScreenMode.idle,
|
||||||
|
errorEvent: VideocallErrorEvent.callStart,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!ref.mounted) return;
|
||||||
|
|
||||||
|
ref.read(groupCallControllerProvider.notifier).joinChannel(roomNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> leaveGroupCall() async {
|
||||||
|
ref.read(groupCallControllerProvider.notifier).leaveChannel();
|
||||||
|
final deviceId = state.deviceId;
|
||||||
|
final chatType = state.chatType;
|
||||||
|
try {
|
||||||
|
await _signaling.cancelCall(
|
||||||
|
deviceIdentificator: deviceId,
|
||||||
|
chatType: chatType,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('[Videocall] cancelCall group signaling failed: $error');
|
||||||
|
}
|
||||||
|
if (!ref.mounted) return;
|
||||||
|
state = state.copyWith(
|
||||||
|
chatType: VideocallChatType.single,
|
||||||
|
screenMode: VideocallScreenMode.idle,
|
||||||
|
successEvent: VideocallSuccessEvent.callEnded,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> answerCall() async {
|
Future<void> answerCall() async {
|
||||||
|
final permissionsGranted = await _requestMediaPermissions();
|
||||||
|
if (!permissionsGranted || !ref.mounted) return;
|
||||||
|
_wasInCall = true;
|
||||||
|
|
||||||
await _deviceService.startAudio();
|
await _deviceService.startAudio();
|
||||||
await _deviceService.startCamera();
|
await _deviceService.startCamera();
|
||||||
|
|
||||||
@@ -155,6 +316,15 @@ class VideocallController extends _$VideocallController {
|
|||||||
await _stopVideoViews();
|
await _stopVideoViews();
|
||||||
await _deviceService.stopAudio();
|
await _deviceService.stopAudio();
|
||||||
await _deviceService.stopCamera();
|
await _deviceService.stopCamera();
|
||||||
|
try {
|
||||||
|
await _signaling.cancelCall(
|
||||||
|
deviceIdentificator: state.deviceId,
|
||||||
|
chatType: state.chatType,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('[Videocall] cancelCall signaling failed: $error');
|
||||||
|
}
|
||||||
|
if (!ref.mounted) return;
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
screenMode: VideocallScreenMode.idle,
|
screenMode: VideocallScreenMode.idle,
|
||||||
currentCall: null,
|
currentCall: null,
|
||||||
@@ -163,8 +333,22 @@ class VideocallController extends _$VideocallController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> rejectCall() async {
|
Future<void> rejectCall() async {
|
||||||
|
final deviceId = state.deviceId;
|
||||||
|
final chatType = state.chatType;
|
||||||
|
final roomNumber = _buildRoomNumber(deviceId, state.localUserId, chatType);
|
||||||
await _callService.hangUp();
|
await _callService.hangUp();
|
||||||
if (!ref.mounted) return;
|
if (!ref.mounted) return;
|
||||||
|
try {
|
||||||
|
await _signaling.refuseCall(
|
||||||
|
deviceIdentificator: deviceId,
|
||||||
|
chatType: chatType,
|
||||||
|
appAccount: state.localUserId,
|
||||||
|
roomNumber: roomNumber,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('[Videocall] refuseCall signaling failed: $error');
|
||||||
|
}
|
||||||
|
if (!ref.mounted) return;
|
||||||
state = state.copyWith(
|
state = state.copyWith(
|
||||||
screenMode: VideocallScreenMode.idle,
|
screenMode: VideocallScreenMode.idle,
|
||||||
currentCall: null,
|
currentCall: null,
|
||||||
@@ -231,6 +415,22 @@ class VideocallController extends _$VideocallController {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _reportRoomCount() async {
|
||||||
|
final deviceId = state.deviceId;
|
||||||
|
final chatType = state.chatType;
|
||||||
|
final roomNumber = _buildRoomNumber(deviceId, state.localUserId, chatType);
|
||||||
|
try {
|
||||||
|
await _signaling.reportRoomCount(
|
||||||
|
deviceIdentificator: deviceId,
|
||||||
|
chatType: chatType,
|
||||||
|
count: 2,
|
||||||
|
roomNumber: roomNumber,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('[Videocall] reportRoomCount failed: $error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void _onCallItemAdd(VideocallItem item) {
|
void _onCallItemAdd(VideocallItem item) {
|
||||||
if (!ref.mounted) return;
|
if (!ref.mounted) return;
|
||||||
if (item.direction == CallDirection.incoming) {
|
if (item.direction == CallDirection.incoming) {
|
||||||
@@ -249,6 +449,9 @@ class VideocallController extends _$VideocallController {
|
|||||||
|
|
||||||
void _onCallItemUpdate(VideocallItem item) {
|
void _onCallItemUpdate(VideocallItem item) {
|
||||||
if (!ref.mounted) return;
|
if (!ref.mounted) return;
|
||||||
|
debugPrint(
|
||||||
|
'[Videocall] _onCallItemUpdate: state=${item.state}, isTalking=${item.isTalking}, uploadSelf=${item.uploadVideoStreamSelf}, uploadOther=${item.uploadVideoStreamOther}',
|
||||||
|
);
|
||||||
state = state.copyWith(currentCall: item);
|
state = state.copyWith(currentCall: item);
|
||||||
|
|
||||||
final isTalking = item.isTalking;
|
final isTalking = item.isTalking;
|
||||||
@@ -258,6 +461,7 @@ class VideocallController extends _$VideocallController {
|
|||||||
screenMode: VideocallScreenMode.inCall,
|
screenMode: VideocallScreenMode.inCall,
|
||||||
successEvent: VideocallSuccessEvent.connected,
|
successEvent: VideocallSuccessEvent.connected,
|
||||||
);
|
);
|
||||||
|
_reportRoomCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.uploadVideoStreamSelf && state.localCanvas == null) {
|
if (item.uploadVideoStreamSelf && state.localCanvas == null) {
|
||||||
@@ -309,17 +513,4 @@ class VideocallController extends _$VideocallController {
|
|||||||
void clearSuccess() {
|
void clearSuccess() {
|
||||||
state = state.copyWith(successEvent: null);
|
state = state.copyWith(successEvent: null);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _disposeSubscriptions() {
|
|
||||||
_callAddSub?.cancel();
|
|
||||||
_callUpdateSub?.cancel();
|
|
||||||
_callRemoveSub?.cancel();
|
|
||||||
_missedCallSub?.cancel();
|
|
||||||
_clientStateSub?.cancel();
|
|
||||||
if (state.screenMode != VideocallScreenMode.idle) {
|
|
||||||
_callService.hangUp();
|
|
||||||
_deviceService.stopAudio();
|
|
||||||
_deviceService.stopCamera();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ final class VideocallControllerProvider
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _$videocallControllerHash() =>
|
String _$videocallControllerHash() =>
|
||||||
r'910674a27ecef98e5df917d193f97fca1c60ac02';
|
r'46fef79b569fea891291d092f187d30f191719ae';
|
||||||
|
|
||||||
abstract class _$VideocallController extends $Notifier<VideocallState> {
|
abstract class _$VideocallController extends $Notifier<VideocallState> {
|
||||||
VideocallState build();
|
VideocallState build();
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:videocall_sdk/videocall_sdk.dart';
|
import 'package:videocall_sdk/videocall_sdk.dart';
|
||||||
|
|
||||||
|
import '../../domain/entities/videocall_chat_type.dart';
|
||||||
import '../../domain/entities/videocall_error.dart';
|
import '../../domain/entities/videocall_error.dart';
|
||||||
|
|
||||||
part 'videocall_state.freezed.dart';
|
part 'videocall_state.freezed.dart';
|
||||||
@@ -12,6 +13,7 @@ abstract class VideocallState with _$VideocallState {
|
|||||||
@Default('') String localUserId,
|
@Default('') String localUserId,
|
||||||
@Default('') String remoteUserId,
|
@Default('') String remoteUserId,
|
||||||
@Default(VideocallScreenMode.idle) VideocallScreenMode screenMode,
|
@Default(VideocallScreenMode.idle) VideocallScreenMode screenMode,
|
||||||
|
@Default(VideocallChatType.single) VideocallChatType chatType,
|
||||||
@Default(false) bool isSdkReady,
|
@Default(false) bool isSdkReady,
|
||||||
@Default(true) bool isMicEnabled,
|
@Default(true) bool isMicEnabled,
|
||||||
@Default(true) bool isSpeakerEnabled,
|
@Default(true) bool isSpeakerEnabled,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$VideocallState {
|
mixin _$VideocallState {
|
||||||
|
|
||||||
String get deviceId; String get localUserId; String get remoteUserId; VideocallScreenMode get screenMode; bool get isSdkReady; bool get isMicEnabled; bool get isSpeakerEnabled; bool get isFrontCamera; bool get isRemoteVideoAvailable; VideocallItem? get currentCall; JCMediaDeviceVideoCanvas? get localCanvas; JCMediaDeviceVideoCanvas? get remoteCanvas; VideocallErrorEvent? get errorEvent; VideocallSuccessEvent? get successEvent;
|
String get deviceId; String get localUserId; String get remoteUserId; VideocallScreenMode get screenMode; VideocallChatType get chatType; bool get isSdkReady; bool get isMicEnabled; bool get isSpeakerEnabled; bool get isFrontCamera; bool get isRemoteVideoAvailable; VideocallItem? get currentCall; JCMediaDeviceVideoCanvas? get localCanvas; JCMediaDeviceVideoCanvas? get remoteCanvas; VideocallErrorEvent? get errorEvent; VideocallSuccessEvent? get successEvent;
|
||||||
/// Create a copy of VideocallState
|
/// Create a copy of VideocallState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -25,16 +25,16 @@ $VideocallStateCopyWith<VideocallState> get copyWith => _$VideocallStateCopyWith
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is VideocallState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.localUserId, localUserId) || other.localUserId == localUserId)&&(identical(other.remoteUserId, remoteUserId) || other.remoteUserId == remoteUserId)&&(identical(other.screenMode, screenMode) || other.screenMode == screenMode)&&(identical(other.isSdkReady, isSdkReady) || other.isSdkReady == isSdkReady)&&(identical(other.isMicEnabled, isMicEnabled) || other.isMicEnabled == isMicEnabled)&&(identical(other.isSpeakerEnabled, isSpeakerEnabled) || other.isSpeakerEnabled == isSpeakerEnabled)&&(identical(other.isFrontCamera, isFrontCamera) || other.isFrontCamera == isFrontCamera)&&(identical(other.isRemoteVideoAvailable, isRemoteVideoAvailable) || other.isRemoteVideoAvailable == isRemoteVideoAvailable)&&(identical(other.currentCall, currentCall) || other.currentCall == currentCall)&&(identical(other.localCanvas, localCanvas) || other.localCanvas == localCanvas)&&(identical(other.remoteCanvas, remoteCanvas) || other.remoteCanvas == remoteCanvas)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.successEvent, successEvent) || other.successEvent == successEvent));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is VideocallState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.localUserId, localUserId) || other.localUserId == localUserId)&&(identical(other.remoteUserId, remoteUserId) || other.remoteUserId == remoteUserId)&&(identical(other.screenMode, screenMode) || other.screenMode == screenMode)&&(identical(other.chatType, chatType) || other.chatType == chatType)&&(identical(other.isSdkReady, isSdkReady) || other.isSdkReady == isSdkReady)&&(identical(other.isMicEnabled, isMicEnabled) || other.isMicEnabled == isMicEnabled)&&(identical(other.isSpeakerEnabled, isSpeakerEnabled) || other.isSpeakerEnabled == isSpeakerEnabled)&&(identical(other.isFrontCamera, isFrontCamera) || other.isFrontCamera == isFrontCamera)&&(identical(other.isRemoteVideoAvailable, isRemoteVideoAvailable) || other.isRemoteVideoAvailable == isRemoteVideoAvailable)&&(identical(other.currentCall, currentCall) || other.currentCall == currentCall)&&(identical(other.localCanvas, localCanvas) || other.localCanvas == localCanvas)&&(identical(other.remoteCanvas, remoteCanvas) || other.remoteCanvas == remoteCanvas)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.successEvent, successEvent) || other.successEvent == successEvent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,deviceId,localUserId,remoteUserId,screenMode,isSdkReady,isMicEnabled,isSpeakerEnabled,isFrontCamera,isRemoteVideoAvailable,currentCall,localCanvas,remoteCanvas,errorEvent,successEvent);
|
int get hashCode => Object.hash(runtimeType,deviceId,localUserId,remoteUserId,screenMode,chatType,isSdkReady,isMicEnabled,isSpeakerEnabled,isFrontCamera,isRemoteVideoAvailable,currentCall,localCanvas,remoteCanvas,errorEvent,successEvent);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'VideocallState(deviceId: $deviceId, localUserId: $localUserId, remoteUserId: $remoteUserId, screenMode: $screenMode, isSdkReady: $isSdkReady, isMicEnabled: $isMicEnabled, isSpeakerEnabled: $isSpeakerEnabled, isFrontCamera: $isFrontCamera, isRemoteVideoAvailable: $isRemoteVideoAvailable, currentCall: $currentCall, localCanvas: $localCanvas, remoteCanvas: $remoteCanvas, errorEvent: $errorEvent, successEvent: $successEvent)';
|
return 'VideocallState(deviceId: $deviceId, localUserId: $localUserId, remoteUserId: $remoteUserId, screenMode: $screenMode, chatType: $chatType, isSdkReady: $isSdkReady, isMicEnabled: $isMicEnabled, isSpeakerEnabled: $isSpeakerEnabled, isFrontCamera: $isFrontCamera, isRemoteVideoAvailable: $isRemoteVideoAvailable, currentCall: $currentCall, localCanvas: $localCanvas, remoteCanvas: $remoteCanvas, errorEvent: $errorEvent, successEvent: $successEvent)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -45,11 +45,11 @@ abstract mixin class $VideocallStateCopyWith<$Res> {
|
|||||||
factory $VideocallStateCopyWith(VideocallState value, $Res Function(VideocallState) _then) = _$VideocallStateCopyWithImpl;
|
factory $VideocallStateCopyWith(VideocallState value, $Res Function(VideocallState) _then) = _$VideocallStateCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String deviceId, String localUserId, String remoteUserId, VideocallScreenMode screenMode, bool isSdkReady, bool isMicEnabled, bool isSpeakerEnabled, bool isFrontCamera, bool isRemoteVideoAvailable, VideocallItem? currentCall, JCMediaDeviceVideoCanvas? localCanvas, JCMediaDeviceVideoCanvas? remoteCanvas, VideocallErrorEvent? errorEvent, VideocallSuccessEvent? successEvent
|
String deviceId, String localUserId, String remoteUserId, VideocallScreenMode screenMode, VideocallChatType chatType, bool isSdkReady, bool isMicEnabled, bool isSpeakerEnabled, bool isFrontCamera, bool isRemoteVideoAvailable, VideocallItem? currentCall, JCMediaDeviceVideoCanvas? localCanvas, JCMediaDeviceVideoCanvas? remoteCanvas, VideocallErrorEvent? errorEvent, VideocallSuccessEvent? successEvent
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
$VideocallItemCopyWith<$Res>? get currentCall;
|
||||||
|
|
||||||
}
|
}
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -62,13 +62,14 @@ class _$VideocallStateCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of VideocallState
|
/// Create a copy of VideocallState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? deviceId = null,Object? localUserId = null,Object? remoteUserId = null,Object? screenMode = null,Object? isSdkReady = null,Object? isMicEnabled = null,Object? isSpeakerEnabled = null,Object? isFrontCamera = null,Object? isRemoteVideoAvailable = null,Object? currentCall = freezed,Object? localCanvas = freezed,Object? remoteCanvas = freezed,Object? errorEvent = freezed,Object? successEvent = freezed,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? deviceId = null,Object? localUserId = null,Object? remoteUserId = null,Object? screenMode = null,Object? chatType = null,Object? isSdkReady = null,Object? isMicEnabled = null,Object? isSpeakerEnabled = null,Object? isFrontCamera = null,Object? isRemoteVideoAvailable = null,Object? currentCall = freezed,Object? localCanvas = freezed,Object? remoteCanvas = freezed,Object? errorEvent = freezed,Object? successEvent = freezed,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||||
as String,localUserId: null == localUserId ? _self.localUserId : localUserId // ignore: cast_nullable_to_non_nullable
|
as String,localUserId: null == localUserId ? _self.localUserId : localUserId // ignore: cast_nullable_to_non_nullable
|
||||||
as String,remoteUserId: null == remoteUserId ? _self.remoteUserId : remoteUserId // ignore: cast_nullable_to_non_nullable
|
as String,remoteUserId: null == remoteUserId ? _self.remoteUserId : remoteUserId // ignore: cast_nullable_to_non_nullable
|
||||||
as String,screenMode: null == screenMode ? _self.screenMode : screenMode // ignore: cast_nullable_to_non_nullable
|
as String,screenMode: null == screenMode ? _self.screenMode : screenMode // ignore: cast_nullable_to_non_nullable
|
||||||
as VideocallScreenMode,isSdkReady: null == isSdkReady ? _self.isSdkReady : isSdkReady // ignore: cast_nullable_to_non_nullable
|
as VideocallScreenMode,chatType: null == chatType ? _self.chatType : chatType // ignore: cast_nullable_to_non_nullable
|
||||||
|
as VideocallChatType,isSdkReady: null == isSdkReady ? _self.isSdkReady : isSdkReady // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,isMicEnabled: null == isMicEnabled ? _self.isMicEnabled : isMicEnabled // ignore: cast_nullable_to_non_nullable
|
as bool,isMicEnabled: null == isMicEnabled ? _self.isMicEnabled : isMicEnabled // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,isSpeakerEnabled: null == isSpeakerEnabled ? _self.isSpeakerEnabled : isSpeakerEnabled // ignore: cast_nullable_to_non_nullable
|
as bool,isSpeakerEnabled: null == isSpeakerEnabled ? _self.isSpeakerEnabled : isSpeakerEnabled // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,isFrontCamera: null == isFrontCamera ? _self.isFrontCamera : isFrontCamera // ignore: cast_nullable_to_non_nullable
|
as bool,isFrontCamera: null == isFrontCamera ? _self.isFrontCamera : isFrontCamera // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -81,7 +82,19 @@ as VideocallErrorEvent?,successEvent: freezed == successEvent ? _self.successEve
|
|||||||
as VideocallSuccessEvent?,
|
as VideocallSuccessEvent?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
/// Create a copy of VideocallState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$VideocallItemCopyWith<$Res>? get currentCall {
|
||||||
|
if (_self.currentCall == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $VideocallItemCopyWith<$Res>(_self.currentCall!, (value) {
|
||||||
|
return _then(_self.copyWith(currentCall: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -163,10 +176,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String deviceId, String localUserId, String remoteUserId, VideocallScreenMode screenMode, bool isSdkReady, bool isMicEnabled, bool isSpeakerEnabled, bool isFrontCamera, bool isRemoteVideoAvailable, VideocallItem? currentCall, JCMediaDeviceVideoCanvas? localCanvas, JCMediaDeviceVideoCanvas? remoteCanvas, VideocallErrorEvent? errorEvent, VideocallSuccessEvent? successEvent)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String deviceId, String localUserId, String remoteUserId, VideocallScreenMode screenMode, VideocallChatType chatType, bool isSdkReady, bool isMicEnabled, bool isSpeakerEnabled, bool isFrontCamera, bool isRemoteVideoAvailable, VideocallItem? currentCall, JCMediaDeviceVideoCanvas? localCanvas, JCMediaDeviceVideoCanvas? remoteCanvas, VideocallErrorEvent? errorEvent, VideocallSuccessEvent? successEvent)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _VideocallState() when $default != null:
|
case _VideocallState() when $default != null:
|
||||||
return $default(_that.deviceId,_that.localUserId,_that.remoteUserId,_that.screenMode,_that.isSdkReady,_that.isMicEnabled,_that.isSpeakerEnabled,_that.isFrontCamera,_that.isRemoteVideoAvailable,_that.currentCall,_that.localCanvas,_that.remoteCanvas,_that.errorEvent,_that.successEvent);case _:
|
return $default(_that.deviceId,_that.localUserId,_that.remoteUserId,_that.screenMode,_that.chatType,_that.isSdkReady,_that.isMicEnabled,_that.isSpeakerEnabled,_that.isFrontCamera,_that.isRemoteVideoAvailable,_that.currentCall,_that.localCanvas,_that.remoteCanvas,_that.errorEvent,_that.successEvent);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -184,10 +197,10 @@ return $default(_that.deviceId,_that.localUserId,_that.remoteUserId,_that.screen
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceId, String localUserId, String remoteUserId, VideocallScreenMode screenMode, bool isSdkReady, bool isMicEnabled, bool isSpeakerEnabled, bool isFrontCamera, bool isRemoteVideoAvailable, VideocallItem? currentCall, JCMediaDeviceVideoCanvas? localCanvas, JCMediaDeviceVideoCanvas? remoteCanvas, VideocallErrorEvent? errorEvent, VideocallSuccessEvent? successEvent) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String deviceId, String localUserId, String remoteUserId, VideocallScreenMode screenMode, VideocallChatType chatType, bool isSdkReady, bool isMicEnabled, bool isSpeakerEnabled, bool isFrontCamera, bool isRemoteVideoAvailable, VideocallItem? currentCall, JCMediaDeviceVideoCanvas? localCanvas, JCMediaDeviceVideoCanvas? remoteCanvas, VideocallErrorEvent? errorEvent, VideocallSuccessEvent? successEvent) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _VideocallState():
|
case _VideocallState():
|
||||||
return $default(_that.deviceId,_that.localUserId,_that.remoteUserId,_that.screenMode,_that.isSdkReady,_that.isMicEnabled,_that.isSpeakerEnabled,_that.isFrontCamera,_that.isRemoteVideoAvailable,_that.currentCall,_that.localCanvas,_that.remoteCanvas,_that.errorEvent,_that.successEvent);case _:
|
return $default(_that.deviceId,_that.localUserId,_that.remoteUserId,_that.screenMode,_that.chatType,_that.isSdkReady,_that.isMicEnabled,_that.isSpeakerEnabled,_that.isFrontCamera,_that.isRemoteVideoAvailable,_that.currentCall,_that.localCanvas,_that.remoteCanvas,_that.errorEvent,_that.successEvent);case _:
|
||||||
throw StateError('Unexpected subclass');
|
throw StateError('Unexpected subclass');
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -204,10 +217,10 @@ return $default(_that.deviceId,_that.localUserId,_that.remoteUserId,_that.screen
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceId, String localUserId, String remoteUserId, VideocallScreenMode screenMode, bool isSdkReady, bool isMicEnabled, bool isSpeakerEnabled, bool isFrontCamera, bool isRemoteVideoAvailable, VideocallItem? currentCall, JCMediaDeviceVideoCanvas? localCanvas, JCMediaDeviceVideoCanvas? remoteCanvas, VideocallErrorEvent? errorEvent, VideocallSuccessEvent? successEvent)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String deviceId, String localUserId, String remoteUserId, VideocallScreenMode screenMode, VideocallChatType chatType, bool isSdkReady, bool isMicEnabled, bool isSpeakerEnabled, bool isFrontCamera, bool isRemoteVideoAvailable, VideocallItem? currentCall, JCMediaDeviceVideoCanvas? localCanvas, JCMediaDeviceVideoCanvas? remoteCanvas, VideocallErrorEvent? errorEvent, VideocallSuccessEvent? successEvent)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _VideocallState() when $default != null:
|
case _VideocallState() when $default != null:
|
||||||
return $default(_that.deviceId,_that.localUserId,_that.remoteUserId,_that.screenMode,_that.isSdkReady,_that.isMicEnabled,_that.isSpeakerEnabled,_that.isFrontCamera,_that.isRemoteVideoAvailable,_that.currentCall,_that.localCanvas,_that.remoteCanvas,_that.errorEvent,_that.successEvent);case _:
|
return $default(_that.deviceId,_that.localUserId,_that.remoteUserId,_that.screenMode,_that.chatType,_that.isSdkReady,_that.isMicEnabled,_that.isSpeakerEnabled,_that.isFrontCamera,_that.isRemoteVideoAvailable,_that.currentCall,_that.localCanvas,_that.remoteCanvas,_that.errorEvent,_that.successEvent);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -219,13 +232,14 @@ return $default(_that.deviceId,_that.localUserId,_that.remoteUserId,_that.screen
|
|||||||
|
|
||||||
|
|
||||||
class _VideocallState implements VideocallState {
|
class _VideocallState implements VideocallState {
|
||||||
const _VideocallState({this.deviceId = '', this.localUserId = '', this.remoteUserId = '', this.screenMode = VideocallScreenMode.idle, this.isSdkReady = false, this.isMicEnabled = true, this.isSpeakerEnabled = true, this.isFrontCamera = true, this.isRemoteVideoAvailable = false, this.currentCall, this.localCanvas, this.remoteCanvas, this.errorEvent, this.successEvent});
|
const _VideocallState({this.deviceId = '', this.localUserId = '', this.remoteUserId = '', this.screenMode = VideocallScreenMode.idle, this.chatType = VideocallChatType.single, this.isSdkReady = false, this.isMicEnabled = true, this.isSpeakerEnabled = true, this.isFrontCamera = true, this.isRemoteVideoAvailable = false, this.currentCall, this.localCanvas, this.remoteCanvas, this.errorEvent, this.successEvent});
|
||||||
|
|
||||||
|
|
||||||
@override@JsonKey() final String deviceId;
|
@override@JsonKey() final String deviceId;
|
||||||
@override@JsonKey() final String localUserId;
|
@override@JsonKey() final String localUserId;
|
||||||
@override@JsonKey() final String remoteUserId;
|
@override@JsonKey() final String remoteUserId;
|
||||||
@override@JsonKey() final VideocallScreenMode screenMode;
|
@override@JsonKey() final VideocallScreenMode screenMode;
|
||||||
|
@override@JsonKey() final VideocallChatType chatType;
|
||||||
@override@JsonKey() final bool isSdkReady;
|
@override@JsonKey() final bool isSdkReady;
|
||||||
@override@JsonKey() final bool isMicEnabled;
|
@override@JsonKey() final bool isMicEnabled;
|
||||||
@override@JsonKey() final bool isSpeakerEnabled;
|
@override@JsonKey() final bool isSpeakerEnabled;
|
||||||
@@ -247,16 +261,16 @@ _$VideocallStateCopyWith<_VideocallState> get copyWith => __$VideocallStateCopyW
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _VideocallState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.localUserId, localUserId) || other.localUserId == localUserId)&&(identical(other.remoteUserId, remoteUserId) || other.remoteUserId == remoteUserId)&&(identical(other.screenMode, screenMode) || other.screenMode == screenMode)&&(identical(other.isSdkReady, isSdkReady) || other.isSdkReady == isSdkReady)&&(identical(other.isMicEnabled, isMicEnabled) || other.isMicEnabled == isMicEnabled)&&(identical(other.isSpeakerEnabled, isSpeakerEnabled) || other.isSpeakerEnabled == isSpeakerEnabled)&&(identical(other.isFrontCamera, isFrontCamera) || other.isFrontCamera == isFrontCamera)&&(identical(other.isRemoteVideoAvailable, isRemoteVideoAvailable) || other.isRemoteVideoAvailable == isRemoteVideoAvailable)&&(identical(other.currentCall, currentCall) || other.currentCall == currentCall)&&(identical(other.localCanvas, localCanvas) || other.localCanvas == localCanvas)&&(identical(other.remoteCanvas, remoteCanvas) || other.remoteCanvas == remoteCanvas)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.successEvent, successEvent) || other.successEvent == successEvent));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _VideocallState&&(identical(other.deviceId, deviceId) || other.deviceId == deviceId)&&(identical(other.localUserId, localUserId) || other.localUserId == localUserId)&&(identical(other.remoteUserId, remoteUserId) || other.remoteUserId == remoteUserId)&&(identical(other.screenMode, screenMode) || other.screenMode == screenMode)&&(identical(other.chatType, chatType) || other.chatType == chatType)&&(identical(other.isSdkReady, isSdkReady) || other.isSdkReady == isSdkReady)&&(identical(other.isMicEnabled, isMicEnabled) || other.isMicEnabled == isMicEnabled)&&(identical(other.isSpeakerEnabled, isSpeakerEnabled) || other.isSpeakerEnabled == isSpeakerEnabled)&&(identical(other.isFrontCamera, isFrontCamera) || other.isFrontCamera == isFrontCamera)&&(identical(other.isRemoteVideoAvailable, isRemoteVideoAvailable) || other.isRemoteVideoAvailable == isRemoteVideoAvailable)&&(identical(other.currentCall, currentCall) || other.currentCall == currentCall)&&(identical(other.localCanvas, localCanvas) || other.localCanvas == localCanvas)&&(identical(other.remoteCanvas, remoteCanvas) || other.remoteCanvas == remoteCanvas)&&(identical(other.errorEvent, errorEvent) || other.errorEvent == errorEvent)&&(identical(other.successEvent, successEvent) || other.successEvent == successEvent));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,deviceId,localUserId,remoteUserId,screenMode,isSdkReady,isMicEnabled,isSpeakerEnabled,isFrontCamera,isRemoteVideoAvailable,currentCall,localCanvas,remoteCanvas,errorEvent,successEvent);
|
int get hashCode => Object.hash(runtimeType,deviceId,localUserId,remoteUserId,screenMode,chatType,isSdkReady,isMicEnabled,isSpeakerEnabled,isFrontCamera,isRemoteVideoAvailable,currentCall,localCanvas,remoteCanvas,errorEvent,successEvent);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'VideocallState(deviceId: $deviceId, localUserId: $localUserId, remoteUserId: $remoteUserId, screenMode: $screenMode, isSdkReady: $isSdkReady, isMicEnabled: $isMicEnabled, isSpeakerEnabled: $isSpeakerEnabled, isFrontCamera: $isFrontCamera, isRemoteVideoAvailable: $isRemoteVideoAvailable, currentCall: $currentCall, localCanvas: $localCanvas, remoteCanvas: $remoteCanvas, errorEvent: $errorEvent, successEvent: $successEvent)';
|
return 'VideocallState(deviceId: $deviceId, localUserId: $localUserId, remoteUserId: $remoteUserId, screenMode: $screenMode, chatType: $chatType, isSdkReady: $isSdkReady, isMicEnabled: $isMicEnabled, isSpeakerEnabled: $isSpeakerEnabled, isFrontCamera: $isFrontCamera, isRemoteVideoAvailable: $isRemoteVideoAvailable, currentCall: $currentCall, localCanvas: $localCanvas, remoteCanvas: $remoteCanvas, errorEvent: $errorEvent, successEvent: $successEvent)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -267,11 +281,11 @@ abstract mixin class _$VideocallStateCopyWith<$Res> implements $VideocallStateCo
|
|||||||
factory _$VideocallStateCopyWith(_VideocallState value, $Res Function(_VideocallState) _then) = __$VideocallStateCopyWithImpl;
|
factory _$VideocallStateCopyWith(_VideocallState value, $Res Function(_VideocallState) _then) = __$VideocallStateCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String deviceId, String localUserId, String remoteUserId, VideocallScreenMode screenMode, bool isSdkReady, bool isMicEnabled, bool isSpeakerEnabled, bool isFrontCamera, bool isRemoteVideoAvailable, VideocallItem? currentCall, JCMediaDeviceVideoCanvas? localCanvas, JCMediaDeviceVideoCanvas? remoteCanvas, VideocallErrorEvent? errorEvent, VideocallSuccessEvent? successEvent
|
String deviceId, String localUserId, String remoteUserId, VideocallScreenMode screenMode, VideocallChatType chatType, bool isSdkReady, bool isMicEnabled, bool isSpeakerEnabled, bool isFrontCamera, bool isRemoteVideoAvailable, VideocallItem? currentCall, JCMediaDeviceVideoCanvas? localCanvas, JCMediaDeviceVideoCanvas? remoteCanvas, VideocallErrorEvent? errorEvent, VideocallSuccessEvent? successEvent
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@override $VideocallItemCopyWith<$Res>? get currentCall;
|
||||||
|
|
||||||
}
|
}
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
@@ -284,13 +298,14 @@ class __$VideocallStateCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of VideocallState
|
/// Create a copy of VideocallState
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? deviceId = null,Object? localUserId = null,Object? remoteUserId = null,Object? screenMode = null,Object? isSdkReady = null,Object? isMicEnabled = null,Object? isSpeakerEnabled = null,Object? isFrontCamera = null,Object? isRemoteVideoAvailable = null,Object? currentCall = freezed,Object? localCanvas = freezed,Object? remoteCanvas = freezed,Object? errorEvent = freezed,Object? successEvent = freezed,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? deviceId = null,Object? localUserId = null,Object? remoteUserId = null,Object? screenMode = null,Object? chatType = null,Object? isSdkReady = null,Object? isMicEnabled = null,Object? isSpeakerEnabled = null,Object? isFrontCamera = null,Object? isRemoteVideoAvailable = null,Object? currentCall = freezed,Object? localCanvas = freezed,Object? remoteCanvas = freezed,Object? errorEvent = freezed,Object? successEvent = freezed,}) {
|
||||||
return _then(_VideocallState(
|
return _then(_VideocallState(
|
||||||
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
deviceId: null == deviceId ? _self.deviceId : deviceId // ignore: cast_nullable_to_non_nullable
|
||||||
as String,localUserId: null == localUserId ? _self.localUserId : localUserId // ignore: cast_nullable_to_non_nullable
|
as String,localUserId: null == localUserId ? _self.localUserId : localUserId // ignore: cast_nullable_to_non_nullable
|
||||||
as String,remoteUserId: null == remoteUserId ? _self.remoteUserId : remoteUserId // ignore: cast_nullable_to_non_nullable
|
as String,remoteUserId: null == remoteUserId ? _self.remoteUserId : remoteUserId // ignore: cast_nullable_to_non_nullable
|
||||||
as String,screenMode: null == screenMode ? _self.screenMode : screenMode // ignore: cast_nullable_to_non_nullable
|
as String,screenMode: null == screenMode ? _self.screenMode : screenMode // ignore: cast_nullable_to_non_nullable
|
||||||
as VideocallScreenMode,isSdkReady: null == isSdkReady ? _self.isSdkReady : isSdkReady // ignore: cast_nullable_to_non_nullable
|
as VideocallScreenMode,chatType: null == chatType ? _self.chatType : chatType // ignore: cast_nullable_to_non_nullable
|
||||||
|
as VideocallChatType,isSdkReady: null == isSdkReady ? _self.isSdkReady : isSdkReady // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,isMicEnabled: null == isMicEnabled ? _self.isMicEnabled : isMicEnabled // ignore: cast_nullable_to_non_nullable
|
as bool,isMicEnabled: null == isMicEnabled ? _self.isMicEnabled : isMicEnabled // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,isSpeakerEnabled: null == isSpeakerEnabled ? _self.isSpeakerEnabled : isSpeakerEnabled // ignore: cast_nullable_to_non_nullable
|
as bool,isSpeakerEnabled: null == isSpeakerEnabled ? _self.isSpeakerEnabled : isSpeakerEnabled // ignore: cast_nullable_to_non_nullable
|
||||||
as bool,isFrontCamera: null == isFrontCamera ? _self.isFrontCamera : isFrontCamera // ignore: cast_nullable_to_non_nullable
|
as bool,isFrontCamera: null == isFrontCamera ? _self.isFrontCamera : isFrontCamera // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -304,7 +319,19 @@ as VideocallSuccessEvent?,
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a copy of VideocallState
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$VideocallItemCopyWith<$Res>? get currentCall {
|
||||||
|
if (_self.currentCall == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $VideocallItemCopyWith<$Res>(_self.currentCall!, (value) {
|
||||||
|
return _then(_self.copyWith(currentCall: value));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dart format on
|
// dart format on
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:design_system/design_system.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:legacy_theme/legacy_theme.dart';
|
import 'package:legacy_theme/legacy_theme.dart';
|
||||||
|
import 'package:legacy_ui/legacy_ui.dart';
|
||||||
import 'package:navigation/navigation.dart';
|
import 'package:navigation/navigation.dart';
|
||||||
import 'package:sf_localizations/sf_localizations.dart';
|
import 'package:sf_localizations/sf_localizations.dart';
|
||||||
import 'package:sf_shared/sf_shared.dart';
|
import 'package:sf_shared/sf_shared.dart';
|
||||||
|
import 'package:utils/utils.dart';
|
||||||
|
|
||||||
import '../domain/entities/videocall_error.dart';
|
import '../domain/entities/videocall_error.dart';
|
||||||
|
import 'providers/group_call_controller.dart';
|
||||||
import 'providers/videocall_controller.dart';
|
import 'providers/videocall_controller.dart';
|
||||||
import 'providers/videocall_state.dart';
|
import 'providers/videocall_state.dart';
|
||||||
import 'widgets/call_controls_widget.dart';
|
import 'widgets/call_controls_widget.dart';
|
||||||
import 'widgets/call_status_indicator.dart';
|
|
||||||
import 'widgets/incoming_call_overlay.dart';
|
import 'widgets/incoming_call_overlay.dart';
|
||||||
|
import 'widgets/participant_grid_widget.dart';
|
||||||
import 'widgets/video_view_widget.dart';
|
import 'widgets/video_view_widget.dart';
|
||||||
|
|
||||||
class VideocallScreen extends ConsumerStatefulWidget {
|
class VideocallScreen extends ConsumerStatefulWidget {
|
||||||
@@ -24,14 +27,6 @@ class VideocallScreen extends ConsumerStatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _VideocallScreenState extends ConsumerState<VideocallScreen> {
|
class _VideocallScreenState extends ConsumerState<VideocallScreen> {
|
||||||
final _userIdController = TextEditingController();
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_userIdController.dispose();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final state = ref.watch(videocallControllerProvider);
|
final state = ref.watch(videocallControllerProvider);
|
||||||
@@ -61,156 +56,264 @@ class _VideocallScreenState extends ConsumerState<VideocallScreen> {
|
|||||||
(_, next) {
|
(_, next) {
|
||||||
if (next == null) return;
|
if (next == null) return;
|
||||||
if (next == VideocallSuccessEvent.callEnded) {
|
if (next == VideocallSuccessEvent.callEnded) {
|
||||||
showInfoDialog(context, I18n.videoCall);
|
showInfoDialog(context, I18n.videocallCallEnded);
|
||||||
}
|
}
|
||||||
vm.clearSuccess();
|
vm.clearSuccess();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return Scaffold(
|
final device = ref.watch(selectedDeviceProvider).value;
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
final deviceName = device?.carrierName ?? '';
|
||||||
body: SafeArea(
|
|
||||||
child: switch (state.screenMode) {
|
final isInActiveCall = state.screenMode != VideocallScreenMode.idle;
|
||||||
VideocallScreenMode.idle => _IdleView(
|
|
||||||
state: state,
|
final body = switch (state.screenMode) {
|
||||||
controller: _userIdController,
|
VideocallScreenMode.idle => !state.isSdkReady
|
||||||
vm: vm,
|
? const LegacyLoadingIndicator()
|
||||||
),
|
: _IdleBody(deviceName: deviceName, vm: vm),
|
||||||
VideocallScreenMode.outgoing => _OutgoingView(state: state, vm: vm),
|
VideocallScreenMode.outgoing || VideocallScreenMode.inCall =>
|
||||||
VideocallScreenMode.incoming => _IncomingView(state: state, vm: vm),
|
_ActiveCallView(state: state, vm: vm, deviceName: deviceName),
|
||||||
VideocallScreenMode.inCall => _InCallView(state: state, vm: vm),
|
VideocallScreenMode.incoming => _IncomingView(state: state, vm: vm),
|
||||||
},
|
VideocallScreenMode.groupCall => _GroupCallView(vm: vm),
|
||||||
|
};
|
||||||
|
|
||||||
|
return PopScope(
|
||||||
|
canPop: !isInActiveCall,
|
||||||
|
onPopInvokedWithResult: (didPop, _) {
|
||||||
|
if (!didPop) {
|
||||||
|
if (state.screenMode == VideocallScreenMode.groupCall) {
|
||||||
|
vm.leaveGroupCall();
|
||||||
|
} else {
|
||||||
|
vm.hangUp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: LegacyPageLayout(
|
||||||
|
title: context.translate(I18n.videocallTitle),
|
||||||
|
body: body,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _IdleView extends StatelessWidget {
|
class _IdleBody extends StatelessWidget {
|
||||||
const _IdleView({
|
const _IdleBody({required this.deviceName, required this.vm});
|
||||||
required this.state,
|
|
||||||
required this.controller,
|
|
||||||
required this.vm,
|
|
||||||
});
|
|
||||||
|
|
||||||
final VideocallState state;
|
final String deviceName;
|
||||||
final TextEditingController controller;
|
|
||||||
final VideocallController vm;
|
final VideocallController vm;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
|
final horizontalPadding = SizeUtils.getByScreen<double>(big: 22, small: 21);
|
||||||
if (!state.isSdkReady) {
|
|
||||||
return Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
CircularProgressIndicator(color: colorScheme.onSurface),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
context.translate(I18n.videocallInitializingSdk),
|
|
||||||
style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.7)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.all(24),
|
padding: EdgeInsets.symmetric(horizontal: horizontalPadding),
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.videocam, color: colorScheme.onSurface, size: 64),
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
deviceName,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: SizeUtils.getByScreen(small: 14, big: 13),
|
||||||
|
color: colorScheme.onSurface.withValues(alpha: 0.6),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
_VideocallOptionCard(
|
||||||
|
image: Image.asset(
|
||||||
|
'assets/shared/images/iso_sf.png',
|
||||||
|
width: SizeUtils.getByScreen<double>(small: 48, big: 44),
|
||||||
|
height: SizeUtils.getByScreen<double>(small: 48, big: 44),
|
||||||
|
),
|
||||||
|
text: deviceName,
|
||||||
|
onTap: () => vm.startCall(''),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
_VideocallOptionCard(
|
||||||
|
icon: Icon(
|
||||||
|
Icons.groups_outlined,
|
||||||
|
size: SizeUtils.getByScreen<double>(small: 36, big: 32),
|
||||||
|
color: context.sfColors.legacyPrimary,
|
||||||
|
),
|
||||||
|
text: context.translate(I18n.videocallGroupCall),
|
||||||
|
onTap: () => vm.startGroupCall(),
|
||||||
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
Text(
|
Text(
|
||||||
context.translate(I18n.videocallTitle),
|
context.translate(I18n.videocallDisclaimer),
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: colorScheme.onSurface,
|
fontSize: SizeUtils.getByScreen(small: 12, big: 11),
|
||||||
fontSize: 24,
|
color: colorScheme.onSurface.withValues(alpha: 0.5),
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 32),
|
|
||||||
TextField(
|
|
||||||
controller: controller,
|
|
||||||
style: TextStyle(color: colorScheme.onSurface),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
hintText: context.translate(I18n.videocallRecipientUserId),
|
|
||||||
hintStyle: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.38)),
|
|
||||||
enabledBorder: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(color: colorScheme.onSurface.withValues(alpha: 0.24)),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
focusedBorder: OutlineInputBorder(
|
|
||||||
borderSide: BorderSide(
|
|
||||||
color: context.sfColors.legacyPrimary,
|
|
||||||
),
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 24),
|
|
||||||
SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
height: 56,
|
|
||||||
child: ElevatedButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
final userId = controller.text.trim();
|
|
||||||
if (userId.isEmpty) return;
|
|
||||||
vm.startCall(userId);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.videocam),
|
|
||||||
label: Text(context.translate(I18n.videocallStart)),
|
|
||||||
style: ElevatedButton.styleFrom(
|
|
||||||
backgroundColor: context.sfColors.legacyPrimary,
|
|
||||||
foregroundColor: colorScheme.onPrimary,
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(12),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
if (kDebugMode && state.localUserId.isEmpty)
|
|
||||||
TextButton(
|
|
||||||
onPressed: () async {
|
|
||||||
await vm.login(userId: 'p_test1', password: 'test123');
|
|
||||||
},
|
|
||||||
child: Text(
|
|
||||||
'Login como p_test1 (testing)',
|
|
||||||
style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.38)),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (state.localUserId.isNotEmpty)
|
|
||||||
Text(
|
|
||||||
context.translate(I18n.videocallLoggedInAs, args: {'userId': state.localUserId}),
|
|
||||||
style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.38), fontSize: 12),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _OutgoingView extends StatelessWidget {
|
class _VideocallOptionCard extends StatelessWidget {
|
||||||
const _OutgoingView({required this.state, required this.vm});
|
const _VideocallOptionCard({
|
||||||
|
this.image,
|
||||||
|
this.icon,
|
||||||
|
required this.text,
|
||||||
|
required this.onTap,
|
||||||
|
});
|
||||||
|
|
||||||
final VideocallState state;
|
final Widget? image;
|
||||||
final VideocallController vm;
|
final Widget? icon;
|
||||||
|
final String text;
|
||||||
|
final VoidCallback onTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Center(
|
final colorScheme = Theme.of(context).colorScheme;
|
||||||
child: CallStatusIndicator(
|
|
||||||
screenMode: state.screenMode,
|
return Material(
|
||||||
remoteUserId: state.remoteUserId,
|
color: colorScheme.surfaceContainerLowest,
|
||||||
onCancel: vm.hangUp,
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
elevation: 0.5,
|
||||||
|
shadowColor: colorScheme.shadow.withValues(alpha: 0.3),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onTap,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: Padding(
|
||||||
|
padding: EdgeInsets.symmetric(
|
||||||
|
horizontal: SizeUtils.getByScreen<double>(small: 16, big: 15),
|
||||||
|
vertical: SizeUtils.getByScreen<double>(small: 14, big: 13),
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: SizeUtils.getByScreen<double>(small: 48, big: 44),
|
||||||
|
height: SizeUtils.getByScreen<double>(small: 48, big: 44),
|
||||||
|
child: Center(child: image ?? icon),
|
||||||
|
),
|
||||||
|
SizedBox(width: SizeUtils.getByScreen<double>(small: 16, big: 15)),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
text,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: SizeUtils.getByScreen(small: 16, big: 15),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _ActiveCallView extends StatelessWidget {
|
||||||
|
const _ActiveCallView({
|
||||||
|
required this.state,
|
||||||
|
required this.vm,
|
||||||
|
required this.deviceName,
|
||||||
|
});
|
||||||
|
|
||||||
|
final VideocallState state;
|
||||||
|
final VideocallController vm;
|
||||||
|
final String deviceName;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final isOutgoing = state.screenMode == VideocallScreenMode.outgoing;
|
||||||
|
final hasRemoteVideo = state.remoteCanvas != null;
|
||||||
|
|
||||||
|
return Stack(
|
||||||
|
fit: StackFit.expand,
|
||||||
|
children: [
|
||||||
|
if (hasRemoteVideo)
|
||||||
|
VideoViewWidget(canvas: state.remoteCanvas!)
|
||||||
|
else if (state.localCanvas != null)
|
||||||
|
VideoViewWidget(canvas: state.localCanvas!)
|
||||||
|
else
|
||||||
|
const ColoredBox(color: Colors.black),
|
||||||
|
|
||||||
|
if (hasRemoteVideo && state.localCanvas != null)
|
||||||
|
Positioned(
|
||||||
|
top: 8,
|
||||||
|
right: 16,
|
||||||
|
child: Container(
|
||||||
|
width: 100,
|
||||||
|
height: 140,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
border: Border.all(color: Colors.white.withValues(alpha: 0.3), width: 1),
|
||||||
|
),
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: VideoViewWidget(
|
||||||
|
canvas: state.localCanvas!,
|
||||||
|
width: 100,
|
||||||
|
height: 140,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Positioned(
|
||||||
|
top: 8,
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Image.asset(
|
||||||
|
'assets/shared/images/iso_sf.png',
|
||||||
|
width: 36,
|
||||||
|
height: 36,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
deviceName,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.white,
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (isOutgoing)
|
||||||
|
Text(
|
||||||
|
context.translate(I18n.videocallConnecting),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Colors.white.withValues(alpha: 0.7),
|
||||||
|
fontSize: 12,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
|
Positioned(
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
child: CallControlsWidget(
|
||||||
|
isMicEnabled: state.isMicEnabled,
|
||||||
|
isSpeakerEnabled: state.isSpeakerEnabled,
|
||||||
|
isFrontCamera: state.isFrontCamera,
|
||||||
|
onToggleMic: vm.toggleMic,
|
||||||
|
onToggleSpeaker: vm.toggleSpeaker,
|
||||||
|
onSwitchCamera: vm.switchCamera,
|
||||||
|
onHangUp: vm.hangUp,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class _IncomingView extends StatelessWidget {
|
class _IncomingView extends StatelessWidget {
|
||||||
const _IncomingView({required this.state, required this.vm});
|
const _IncomingView({required this.state, required this.vm});
|
||||||
|
|
||||||
@@ -228,83 +331,59 @@ class _IncomingView extends StatelessWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InCallView extends StatelessWidget {
|
class _GroupCallView extends ConsumerWidget {
|
||||||
const _InCallView({required this.state, required this.vm});
|
const _GroupCallView({required this.vm});
|
||||||
|
|
||||||
final VideocallState state;
|
|
||||||
final VideocallController vm;
|
final VideocallController vm;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final groupState = ref.watch(groupCallControllerProvider);
|
||||||
|
final groupVm = ref.read(groupCallControllerProvider.notifier);
|
||||||
|
|
||||||
return Stack(
|
return Stack(
|
||||||
fit: StackFit.expand,
|
fit: StackFit.expand,
|
||||||
children: [
|
children: [
|
||||||
if (state.remoteCanvas != null)
|
const ColoredBox(color: Colors.black),
|
||||||
VideoViewWidget(canvas: state.remoteCanvas!)
|
ParticipantGridWidget(participants: groupState.participants),
|
||||||
else
|
if (groupState.localCanvas != null)
|
||||||
Center(
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(Icons.person, color: colorScheme.onSurface.withValues(alpha: 0.24), size: 96),
|
|
||||||
const SizedBox(height: 8),
|
|
||||||
Text(
|
|
||||||
context.translate(I18n.videocallWaitingRemoteVideo),
|
|
||||||
style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.38)),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
if (state.localCanvas != null)
|
|
||||||
Positioned(
|
Positioned(
|
||||||
top: 16,
|
top: 8,
|
||||||
right: 16,
|
right: 16,
|
||||||
child: Container(
|
child: Container(
|
||||||
width: 120,
|
width: 100,
|
||||||
height: 160,
|
height: 140,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
border: Border.all(color: colorScheme.onSurface.withValues(alpha: 0.24), width: 1),
|
border: Border.all(
|
||||||
|
color: Colors.white.withValues(alpha: 0.3),
|
||||||
|
width: 1,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
clipBehavior: Clip.antiAlias,
|
clipBehavior: Clip.antiAlias,
|
||||||
child: VideoViewWidget(
|
child: VideoViewWidget(
|
||||||
canvas: state.localCanvas!,
|
canvas: groupState.localCanvas!,
|
||||||
width: 120,
|
width: 100,
|
||||||
height: 160,
|
height: 140,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
Positioned(
|
Positioned(
|
||||||
left: 0,
|
left: 0,
|
||||||
right: 0,
|
right: 0,
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
child: Container(
|
child: CallControlsWidget(
|
||||||
decoration: BoxDecoration(
|
isMicEnabled: groupState.isMicEnabled,
|
||||||
gradient: LinearGradient(
|
isSpeakerEnabled: true,
|
||||||
begin: Alignment.bottomCenter,
|
isFrontCamera: true,
|
||||||
end: Alignment.topCenter,
|
onToggleMic: groupVm.toggleMic,
|
||||||
colors: [
|
onToggleSpeaker: () {},
|
||||||
colorScheme.surface.withValues(alpha: 0.8),
|
onSwitchCamera: groupVm.switchCamera,
|
||||||
Colors.transparent,
|
onHangUp: vm.leaveGroupCall,
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: CallControlsWidget(
|
|
||||||
isMicEnabled: state.isMicEnabled,
|
|
||||||
isSpeakerEnabled: state.isSpeakerEnabled,
|
|
||||||
isFrontCamera: state.isFrontCamera,
|
|
||||||
onToggleMic: vm.toggleMic,
|
|
||||||
onToggleSpeaker: vm.toggleSpeaker,
|
|
||||||
onSwitchCamera: vm.switchCamera,
|
|
||||||
onHangUp: vm.hangUp,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,39 +23,52 @@ class CallControlsWidget extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Padding(
|
return SafeArea(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
|
top: false,
|
||||||
child: Row(
|
child: Padding(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
|
||||||
children: [
|
child: Column(
|
||||||
_ControlButton(
|
mainAxisSize: MainAxisSize.min,
|
||||||
icon: isMicEnabled ? Icons.mic : Icons.mic_off,
|
children: [
|
||||||
label: context.translate(isMicEnabled ? I18n.videocallMicOn : I18n.videocallMicOff),
|
Row(
|
||||||
isActive: isMicEnabled,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
onPressed: onToggleMic,
|
children: [
|
||||||
),
|
_ControlButton(
|
||||||
_ControlButton(
|
icon: isMicEnabled ? Icons.mic_none : Icons.mic_off,
|
||||||
icon: isSpeakerEnabled
|
label: context.translate(I18n.videocallMute),
|
||||||
? Icons.volume_up
|
isActive: !isMicEnabled,
|
||||||
: Icons.volume_off,
|
onPressed: onToggleMic,
|
||||||
label: context.translate(isSpeakerEnabled ? I18n.videocallSpeaker : I18n.videocallEarpiece),
|
),
|
||||||
isActive: isSpeakerEnabled,
|
_ControlButton(
|
||||||
onPressed: onToggleSpeaker,
|
icon: isSpeakerEnabled ? Icons.volume_up : Icons.hearing,
|
||||||
),
|
label: context.translate(I18n.videocallSpeakerphone),
|
||||||
_ControlButton(
|
isActive: isSpeakerEnabled,
|
||||||
icon: Icons.cameraswitch,
|
onPressed: onToggleSpeaker,
|
||||||
label: context.translate(isFrontCamera ? I18n.videocallCameraFront : I18n.videocallCameraBack),
|
),
|
||||||
isActive: true,
|
_ControlButton(
|
||||||
onPressed: onSwitchCamera,
|
icon: Icons.videocam,
|
||||||
),
|
label: context.translate(I18n.videocallCameraOff),
|
||||||
_ControlButton(
|
isActive: true,
|
||||||
icon: Icons.call_end,
|
onPressed: () {},
|
||||||
label: context.translate(I18n.videocallHangUp),
|
),
|
||||||
isActive: false,
|
],
|
||||||
isDestructive: true,
|
),
|
||||||
onPressed: onHangUp,
|
const SizedBox(height: 24),
|
||||||
),
|
Row(
|
||||||
],
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Spacer(),
|
||||||
|
_HangUpButton(onPressed: onHangUp),
|
||||||
|
Expanded(
|
||||||
|
child: Align(
|
||||||
|
alignment: Alignment.centerRight,
|
||||||
|
child: _SwitchCameraButton(onPressed: onSwitchCamera),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -66,31 +79,19 @@ class _ControlButton extends StatelessWidget {
|
|||||||
required this.icon,
|
required this.icon,
|
||||||
required this.label,
|
required this.label,
|
||||||
required this.isActive,
|
required this.isActive,
|
||||||
this.isDestructive = false,
|
|
||||||
required this.onPressed,
|
required this.onPressed,
|
||||||
});
|
});
|
||||||
|
|
||||||
final IconData icon;
|
final IconData icon;
|
||||||
final String label;
|
final String label;
|
||||||
final bool isActive;
|
final bool isActive;
|
||||||
final bool isDestructive;
|
|
||||||
final VoidCallback onPressed;
|
final VoidCallback onPressed;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final colorScheme = Theme.of(context).colorScheme;
|
final backgroundColor = isActive
|
||||||
|
? Colors.white.withValues(alpha: 0.3)
|
||||||
final backgroundColor = isDestructive
|
: Colors.white.withValues(alpha: 0.15);
|
||||||
? colorScheme.error
|
|
||||||
: isActive
|
|
||||||
? colorScheme.onSurface.withValues(alpha: 0.2)
|
|
||||||
: colorScheme.onSurface.withValues(alpha: 0.4);
|
|
||||||
|
|
||||||
final iconColor = isDestructive
|
|
||||||
? Colors.white
|
|
||||||
: isActive
|
|
||||||
? colorScheme.onSurface
|
|
||||||
: colorScheme.onSurface.withValues(alpha: 0.7);
|
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -103,16 +104,60 @@ class _ControlButton extends StatelessWidget {
|
|||||||
customBorder: const CircleBorder(),
|
customBorder: const CircleBorder(),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: Icon(icon, color: iconColor, size: 28),
|
child: Icon(icon, color: Colors.white, size: 28),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 6),
|
||||||
Text(
|
Text(
|
||||||
label,
|
label,
|
||||||
style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.7), fontSize: 11),
|
style: TextStyle(color: Colors.white.withValues(alpha: 0.9), fontSize: 11),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _HangUpButton extends StatelessWidget {
|
||||||
|
const _HangUpButton({required this.onPressed});
|
||||||
|
|
||||||
|
final VoidCallback onPressed;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
color: Theme.of(context).colorScheme.error,
|
||||||
|
shape: const CircleBorder(),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onPressed,
|
||||||
|
customBorder: const CircleBorder(),
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.all(18),
|
||||||
|
child: Icon(Icons.call_end, color: Colors.white, size: 32),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SwitchCameraButton extends StatelessWidget {
|
||||||
|
const _SwitchCameraButton({required this.onPressed});
|
||||||
|
|
||||||
|
final VoidCallback onPressed;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Material(
|
||||||
|
color: Colors.white.withValues(alpha: 0.2),
|
||||||
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||||
|
child: InkWell(
|
||||||
|
onTap: onPressed,
|
||||||
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.all(12),
|
||||||
|
child: Icon(Icons.cameraswitch_outlined, color: Colors.white, size: 24),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:legacy_device_state/legacy_device_state.dart';
|
||||||
import 'package:sf_infrastructure/sf_infrastructure.dart';
|
|
||||||
|
|
||||||
import '../data/datasources/videocall_signaling_datasource.dart';
|
import '../data/datasources/videocall_signaling_datasource.dart';
|
||||||
import '../data/datasources/videocall_signaling_datasource_impl.dart';
|
import '../data/datasources/videocall_signaling_datasource_impl.dart';
|
||||||
|
|
||||||
final videocallSignalingDatasourceProvider =
|
final videocallSignalingDatasourceProvider =
|
||||||
Provider<VideocallSignalingDatasource>((ref) {
|
Provider<VideocallSignalingDatasource>((ref) {
|
||||||
final repository = GetIt.I<SaveFamilyRepository>();
|
return VideocallSignalingDatasourceImpl(
|
||||||
return VideocallSignalingDatasourceImpl(repository);
|
ref.read(commandsRepositoryProvider),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ dependencies:
|
|||||||
image_picker: ^1.2.1
|
image_picker: ^1.2.1
|
||||||
share_plus: ^10.1.4
|
share_plus: ^10.1.4
|
||||||
path_provider: ^2.1.5
|
path_provider: ^2.1.5
|
||||||
|
permission_handler: ^12.0.1
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
|
|||||||
@@ -32,6 +32,14 @@ enum DeviceCommand {
|
|||||||
setWifi,
|
setWifi,
|
||||||
@JsonValue('WIFI_DELETE')
|
@JsonValue('WIFI_DELETE')
|
||||||
wifiDelete,
|
wifiDelete,
|
||||||
|
@JsonValue('VIDEO_CALL_REQUEST')
|
||||||
|
videoCallRequest,
|
||||||
|
@JsonValue('VIDEO_CALL_CANCEL')
|
||||||
|
videoCallCancel,
|
||||||
|
@JsonValue('VIDEO_CALL_REFUSE')
|
||||||
|
videoCallRefuse,
|
||||||
|
@JsonValue('VIDEO_CALL_ROOM_COUNT_REQUEST')
|
||||||
|
videoCallRoomCountRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
|||||||
@@ -37,4 +37,8 @@ const _$DeviceCommandEnumMap = {
|
|||||||
DeviceCommand.wifiSearch: 'WIFI_SEARCH',
|
DeviceCommand.wifiSearch: 'WIFI_SEARCH',
|
||||||
DeviceCommand.setWifi: 'SET_WIFI',
|
DeviceCommand.setWifi: 'SET_WIFI',
|
||||||
DeviceCommand.wifiDelete: 'WIFI_DELETE',
|
DeviceCommand.wifiDelete: 'WIFI_DELETE',
|
||||||
|
DeviceCommand.videoCallRequest: 'VIDEO_CALL_REQUEST',
|
||||||
|
DeviceCommand.videoCallCancel: 'VIDEO_CALL_CANCEL',
|
||||||
|
DeviceCommand.videoCallRefuse: 'VIDEO_CALL_REFUSE',
|
||||||
|
DeviceCommand.videoCallRoomCountRequest: 'VIDEO_CALL_ROOM_COUNT_REQUEST',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import 'package:dio/dio.dart';
|
|||||||
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
import 'package:dio_cookie_manager/dio_cookie_manager.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
|
import 'retry_interceptor.dart';
|
||||||
|
|
||||||
Future<Dio> buildDioClient({
|
Future<Dio> buildDioClient({
|
||||||
required String baseUrl,
|
required String baseUrl,
|
||||||
required String origin,
|
required String origin,
|
||||||
@@ -25,6 +27,7 @@ Future<Dio> buildDioClient({
|
|||||||
|
|
||||||
final jar = cookieJar ?? await buildPersistCookieJar();
|
final jar = cookieJar ?? await buildPersistCookieJar();
|
||||||
dio.interceptors.add(CookieManager(jar));
|
dio.interceptors.add(CookieManager(jar));
|
||||||
|
dio.interceptors.add(RetryInterceptor(dio));
|
||||||
|
|
||||||
if (log) {
|
if (log) {
|
||||||
dio.interceptors.add(
|
dio.interceptors.add(
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
|
||||||
|
class RetryInterceptor extends Interceptor {
|
||||||
|
RetryInterceptor(this._dio, {this.maxRetries = 2, this.retryDelay = const Duration(seconds: 1)});
|
||||||
|
|
||||||
|
final Dio _dio;
|
||||||
|
final int maxRetries;
|
||||||
|
final Duration retryDelay;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onError(DioException err, ErrorInterceptorHandler handler) {
|
||||||
|
if (!_isTransientError(err)) {
|
||||||
|
return handler.next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
final attempt = _getAttempt(err.requestOptions);
|
||||||
|
if (attempt >= maxRetries) {
|
||||||
|
return handler.next(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
_retry(err, handler, attempt + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isTransientError(DioException err) {
|
||||||
|
final error = err.error;
|
||||||
|
if (error is HandshakeException) return true;
|
||||||
|
if (error is SocketException) return true;
|
||||||
|
if (err.type == DioExceptionType.connectionError) return true;
|
||||||
|
if (err.type == DioExceptionType.connectionTimeout) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int _getAttempt(RequestOptions options) {
|
||||||
|
return options.extra['_retryAttempt'] as int? ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _retry(DioException err, ErrorInterceptorHandler handler, int attempt) async {
|
||||||
|
await Future<void>.delayed(retryDelay * attempt);
|
||||||
|
|
||||||
|
final options = err.requestOptions;
|
||||||
|
options.extra['_retryAttempt'] = attempt;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final response = await _dio.fetch(options);
|
||||||
|
handler.resolve(response);
|
||||||
|
} on DioException catch (retryError) {
|
||||||
|
handler.next(retryError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -711,6 +711,14 @@
|
|||||||
"videocallErrorCamera": "Kameraberechtigung erforderlich",
|
"videocallErrorCamera": "Kameraberechtigung erforderlich",
|
||||||
"videocallErrorMic": "Mikrofonberechtigung erforderlich",
|
"videocallErrorMic": "Mikrofonberechtigung erforderlich",
|
||||||
"videocallErrorMissedCall": "Verpasster Anruf",
|
"videocallErrorMissedCall": "Verpasster Anruf",
|
||||||
|
"videocallCallEnded": "Anruf beendet",
|
||||||
|
"videocallGroupCall": "Gruppen-Videoanruf",
|
||||||
|
"videocallDisclaimer": "*Für einen optimalen Videoanruf stellen Sie sicher, dass Ihre Uhr über mobile Daten mit Internetverbindung, guter Abdeckung oder WLAN-Verbindung verfügt.",
|
||||||
|
"videocallConnecting": "Warten auf Verbindungsaufbau...",
|
||||||
|
"videocallMute": "stummschalten",
|
||||||
|
"videocallSpeakerphone": "Freisprechen",
|
||||||
|
"videocallCameraOff": "Kamera ausschalten",
|
||||||
|
"videocallCameraOn": "Kamera einschalten",
|
||||||
"positionUpdated": "Letzte verfügbare Position aktualisiert",
|
"positionUpdated": "Letzte verfügbare Position aktualisiert",
|
||||||
"locationMapStyleLight": "Hell",
|
"locationMapStyleLight": "Hell",
|
||||||
"locationMapStyleDark": "Dunkel",
|
"locationMapStyleDark": "Dunkel",
|
||||||
|
|||||||
@@ -891,6 +891,14 @@
|
|||||||
"videocallErrorCamera": "Camera permission required",
|
"videocallErrorCamera": "Camera permission required",
|
||||||
"videocallErrorMic": "Microphone permission required",
|
"videocallErrorMic": "Microphone permission required",
|
||||||
"videocallErrorMissedCall": "Missed call",
|
"videocallErrorMissedCall": "Missed call",
|
||||||
|
"videocallCallEnded": "Call ended",
|
||||||
|
"videocallGroupCall": "Group video call",
|
||||||
|
"videocallDisclaimer": "*For an optimal video call, make sure your watch has mobile data with Internet connection, good coverage or WiFi connection.",
|
||||||
|
"videocallConnecting": "Waiting for the connection to be established...",
|
||||||
|
"videocallMute": "mute",
|
||||||
|
"videocallSpeakerphone": "speakerphone",
|
||||||
|
"videocallCameraOff": "turn off camera",
|
||||||
|
"videocallCameraOn": "turn on camera",
|
||||||
"positionUpdated": "Updated to latest available position",
|
"positionUpdated": "Updated to latest available position",
|
||||||
"locationMapStyleLight": "Light",
|
"locationMapStyleLight": "Light",
|
||||||
"locationMapStyleDark": "Dark",
|
"locationMapStyleDark": "Dark",
|
||||||
|
|||||||
@@ -892,6 +892,14 @@
|
|||||||
"videocallErrorCamera": "Se requiere permiso de cámara",
|
"videocallErrorCamera": "Se requiere permiso de cámara",
|
||||||
"videocallErrorMic": "Se requiere permiso de micrófono",
|
"videocallErrorMic": "Se requiere permiso de micrófono",
|
||||||
"videocallErrorMissedCall": "Llamada perdida",
|
"videocallErrorMissedCall": "Llamada perdida",
|
||||||
|
"videocallCallEnded": "Llamada finalizada",
|
||||||
|
"videocallGroupCall": "Videollamada grupal",
|
||||||
|
"videocallDisclaimer": "*Para una llamada de video óptima, asegúrate de que tu reloj tenga datos móviles con conexión a Internet, buena cobertura o conexión wifi.",
|
||||||
|
"videocallConnecting": "Esperando a que se establezca la conexión...",
|
||||||
|
"videocallMute": "silenciar",
|
||||||
|
"videocallSpeakerphone": "manos libres",
|
||||||
|
"videocallCameraOff": "apagar la cámara",
|
||||||
|
"videocallCameraOn": "encender la cámara",
|
||||||
"positionUpdated": "Última posición disponible actualizada",
|
"positionUpdated": "Última posición disponible actualizada",
|
||||||
"locationMapStyleLight": "Claro",
|
"locationMapStyleLight": "Claro",
|
||||||
"locationMapStyleDark": "Oscuro",
|
"locationMapStyleDark": "Oscuro",
|
||||||
|
|||||||
@@ -711,6 +711,14 @@
|
|||||||
"videocallErrorCamera": "Autorisation de la caméra requise",
|
"videocallErrorCamera": "Autorisation de la caméra requise",
|
||||||
"videocallErrorMic": "Autorisation du microphone requise",
|
"videocallErrorMic": "Autorisation du microphone requise",
|
||||||
"videocallErrorMissedCall": "Appel manqué",
|
"videocallErrorMissedCall": "Appel manqué",
|
||||||
|
"videocallCallEnded": "Appel terminé",
|
||||||
|
"videocallGroupCall": "Appel vidéo de groupe",
|
||||||
|
"videocallDisclaimer": "*Pour un appel vidéo optimal, assurez-vous que votre montre dispose de données mobiles avec connexion Internet, bonne couverture ou connexion WiFi.",
|
||||||
|
"videocallConnecting": "En attente de la connexion...",
|
||||||
|
"videocallMute": "couper le son",
|
||||||
|
"videocallSpeakerphone": "haut-parleur",
|
||||||
|
"videocallCameraOff": "éteindre la caméra",
|
||||||
|
"videocallCameraOn": "allumer la caméra",
|
||||||
"positionUpdated": "Dernière position disponible mise à jour",
|
"positionUpdated": "Dernière position disponible mise à jour",
|
||||||
"locationMapStyleLight": "Clair",
|
"locationMapStyleLight": "Clair",
|
||||||
"locationMapStyleDark": "Sombre",
|
"locationMapStyleDark": "Sombre",
|
||||||
|
|||||||
@@ -711,6 +711,14 @@
|
|||||||
"videocallErrorCamera": "Permesso fotocamera richiesto",
|
"videocallErrorCamera": "Permesso fotocamera richiesto",
|
||||||
"videocallErrorMic": "Permesso microfono richiesto",
|
"videocallErrorMic": "Permesso microfono richiesto",
|
||||||
"videocallErrorMissedCall": "Chiamata persa",
|
"videocallErrorMissedCall": "Chiamata persa",
|
||||||
|
"videocallCallEnded": "Chiamata terminata",
|
||||||
|
"videocallGroupCall": "Videochiamata di gruppo",
|
||||||
|
"videocallDisclaimer": "*Per una videochiamata ottimale, assicurati che il tuo orologio abbia dati mobili con connessione Internet, buona copertura o connessione WiFi.",
|
||||||
|
"videocallConnecting": "In attesa della connessione...",
|
||||||
|
"videocallMute": "silenzia",
|
||||||
|
"videocallSpeakerphone": "vivavoce",
|
||||||
|
"videocallCameraOff": "spegni fotocamera",
|
||||||
|
"videocallCameraOn": "accendi fotocamera",
|
||||||
"positionUpdated": "Ultima posizione disponibile aggiornata",
|
"positionUpdated": "Ultima posizione disponibile aggiornata",
|
||||||
"locationMapStyleLight": "Chiaro",
|
"locationMapStyleLight": "Chiaro",
|
||||||
"locationMapStyleDark": "Scuro",
|
"locationMapStyleDark": "Scuro",
|
||||||
|
|||||||
@@ -711,6 +711,14 @@
|
|||||||
"videocallErrorCamera": "Permissão de câmara necessária",
|
"videocallErrorCamera": "Permissão de câmara necessária",
|
||||||
"videocallErrorMic": "Permissão de microfone necessária",
|
"videocallErrorMic": "Permissão de microfone necessária",
|
||||||
"videocallErrorMissedCall": "Chamada perdida",
|
"videocallErrorMissedCall": "Chamada perdida",
|
||||||
|
"videocallCallEnded": "Chamada terminada",
|
||||||
|
"videocallGroupCall": "Videochamada em grupo",
|
||||||
|
"videocallDisclaimer": "*Para uma videochamada ideal, certifique-se de que o seu relógio tenha dados móveis com conexão à Internet, boa cobertura ou conexão WiFi.",
|
||||||
|
"videocallConnecting": "Aguardando conexão...",
|
||||||
|
"videocallMute": "silenciar",
|
||||||
|
"videocallSpeakerphone": "viva-voz",
|
||||||
|
"videocallCameraOff": "desligar câmara",
|
||||||
|
"videocallCameraOn": "ligar câmara",
|
||||||
"positionUpdated": "Última posição disponível atualizada",
|
"positionUpdated": "Última posição disponível atualizada",
|
||||||
"locationMapStyleLight": "Claro",
|
"locationMapStyleLight": "Claro",
|
||||||
"locationMapStyleDark": "Escuro",
|
"locationMapStyleDark": "Escuro",
|
||||||
|
|||||||
@@ -1007,6 +1007,14 @@ class I18n {
|
|||||||
static const String vibrationOnly = 'vibrationOnly';
|
static const String vibrationOnly = 'vibrationOnly';
|
||||||
static const String videoCall = 'videoCall';
|
static const String videoCall = 'videoCall';
|
||||||
static const String videocallAccept = 'videocallAccept';
|
static const String videocallAccept = 'videocallAccept';
|
||||||
|
static const String videocallCallEnded = 'videocallCallEnded';
|
||||||
|
static const String videocallGroupCall = 'videocallGroupCall';
|
||||||
|
static const String videocallDisclaimer = 'videocallDisclaimer';
|
||||||
|
static const String videocallConnecting = 'videocallConnecting';
|
||||||
|
static const String videocallMute = 'videocallMute';
|
||||||
|
static const String videocallSpeakerphone = 'videocallSpeakerphone';
|
||||||
|
static const String videocallCameraOff = 'videocallCameraOff';
|
||||||
|
static const String videocallCameraOn = 'videocallCameraOn';
|
||||||
static const String videocallCalling = 'videocallCalling';
|
static const String videocallCalling = 'videocallCalling';
|
||||||
static const String videocallCameraBack = 'videocallCameraBack';
|
static const String videocallCameraBack = 'videocallCameraBack';
|
||||||
static const String videocallCameraFront = 'videocallCameraFront';
|
static const String videocallCameraFront = 'videocallCameraFront';
|
||||||
|
|||||||
@@ -49,10 +49,9 @@ class VideocallSdkManager {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> destroy() async {
|
Future<void> shutdown() async {
|
||||||
if (!_initialized) return;
|
if (!_initialized) return;
|
||||||
|
|
||||||
// Reverse order
|
|
||||||
await pushService.destroy();
|
await pushService.destroy();
|
||||||
await channelService.destroy();
|
await channelService.destroy();
|
||||||
await callService.destroy();
|
await callService.destroy();
|
||||||
@@ -60,16 +59,13 @@ class VideocallSdkManager {
|
|||||||
await deviceService.destroy();
|
await deviceService.destroy();
|
||||||
await client.destroy();
|
await client.destroy();
|
||||||
|
|
||||||
_initialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void dispose() {
|
|
||||||
callService.dispose();
|
callService.dispose();
|
||||||
channelService.dispose();
|
channelService.dispose();
|
||||||
pushService.dispose();
|
pushService.dispose();
|
||||||
netService.dispose();
|
netService.dispose();
|
||||||
deviceService.dispose();
|
deviceService.dispose();
|
||||||
client.dispose();
|
client.dispose();
|
||||||
|
|
||||||
_initialized = false;
|
_initialized = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +1,22 @@
|
|||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
import 'call_direction.dart';
|
import 'call_direction.dart';
|
||||||
import 'call_state.dart';
|
import 'call_state.dart';
|
||||||
|
|
||||||
class VideocallItem {
|
part 'videocall_item.freezed.dart';
|
||||||
const VideocallItem({
|
|
||||||
required this.userId,
|
|
||||||
required this.isVideo,
|
|
||||||
required this.direction,
|
|
||||||
required this.state,
|
|
||||||
this.uploadVideoStreamSelf = false,
|
|
||||||
this.uploadVideoStreamOther = false,
|
|
||||||
});
|
|
||||||
|
|
||||||
final String userId;
|
@freezed
|
||||||
final bool isVideo;
|
abstract class VideocallItem with _$VideocallItem {
|
||||||
final CallDirection direction;
|
const factory VideocallItem({
|
||||||
final VideocallState state;
|
required String userId,
|
||||||
final bool uploadVideoStreamSelf;
|
required bool isVideo,
|
||||||
final bool uploadVideoStreamOther;
|
required CallDirection direction,
|
||||||
|
required VideocallState state,
|
||||||
bool get isTalking => state == VideocallState.talking;
|
@Default(false) bool uploadVideoStreamSelf,
|
||||||
|
@Default(false) bool uploadVideoStreamOther,
|
||||||
VideocallItem copyWith({
|
}) = _VideocallItem;
|
||||||
String? userId,
|
}
|
||||||
bool? isVideo,
|
|
||||||
CallDirection? direction,
|
extension VideocallItemX on VideocallItem {
|
||||||
VideocallState? state,
|
bool get isTalking => state == VideocallState.talking;
|
||||||
bool? uploadVideoStreamSelf,
|
|
||||||
bool? uploadVideoStreamOther,
|
|
||||||
}) {
|
|
||||||
return VideocallItem(
|
|
||||||
userId: userId ?? this.userId,
|
|
||||||
isVideo: isVideo ?? this.isVideo,
|
|
||||||
direction: direction ?? this.direction,
|
|
||||||
state: state ?? this.state,
|
|
||||||
uploadVideoStreamSelf:
|
|
||||||
uploadVideoStreamSelf ?? this.uploadVideoStreamSelf,
|
|
||||||
uploadVideoStreamOther:
|
|
||||||
uploadVideoStreamOther ?? this.uploadVideoStreamOther,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
// coverage:ignore-file
|
||||||
|
// ignore_for_file: type=lint
|
||||||
|
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark
|
||||||
|
|
||||||
|
part of 'videocall_item.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// FreezedGenerator
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// dart format off
|
||||||
|
T _$identity<T>(T value) => value;
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$VideocallItem {
|
||||||
|
|
||||||
|
String get userId; bool get isVideo; CallDirection get direction; VideocallState get state; bool get uploadVideoStreamSelf; bool get uploadVideoStreamOther;
|
||||||
|
/// Create a copy of VideocallItem
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$VideocallItemCopyWith<VideocallItem> get copyWith => _$VideocallItemCopyWithImpl<VideocallItem>(this as VideocallItem, _$identity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is VideocallItem&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.isVideo, isVideo) || other.isVideo == isVideo)&&(identical(other.direction, direction) || other.direction == direction)&&(identical(other.state, state) || other.state == state)&&(identical(other.uploadVideoStreamSelf, uploadVideoStreamSelf) || other.uploadVideoStreamSelf == uploadVideoStreamSelf)&&(identical(other.uploadVideoStreamOther, uploadVideoStreamOther) || other.uploadVideoStreamOther == uploadVideoStreamOther));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,userId,isVideo,direction,state,uploadVideoStreamSelf,uploadVideoStreamOther);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'VideocallItem(userId: $userId, isVideo: $isVideo, direction: $direction, state: $state, uploadVideoStreamSelf: $uploadVideoStreamSelf, uploadVideoStreamOther: $uploadVideoStreamOther)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $VideocallItemCopyWith<$Res> {
|
||||||
|
factory $VideocallItemCopyWith(VideocallItem value, $Res Function(VideocallItem) _then) = _$VideocallItemCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
String userId, bool isVideo, CallDirection direction, VideocallState state, bool uploadVideoStreamSelf, bool uploadVideoStreamOther
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$VideocallItemCopyWithImpl<$Res>
|
||||||
|
implements $VideocallItemCopyWith<$Res> {
|
||||||
|
_$VideocallItemCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final VideocallItem _self;
|
||||||
|
final $Res Function(VideocallItem) _then;
|
||||||
|
|
||||||
|
/// Create a copy of VideocallItem
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? userId = null,Object? isVideo = null,Object? direction = null,Object? state = null,Object? uploadVideoStreamSelf = null,Object? uploadVideoStreamOther = null,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,isVideo: null == isVideo ? _self.isVideo : isVideo // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,direction: null == direction ? _self.direction : direction // ignore: cast_nullable_to_non_nullable
|
||||||
|
as CallDirection,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
|
||||||
|
as VideocallState,uploadVideoStreamSelf: null == uploadVideoStreamSelf ? _self.uploadVideoStreamSelf : uploadVideoStreamSelf // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,uploadVideoStreamOther: null == uploadVideoStreamOther ? _self.uploadVideoStreamOther : uploadVideoStreamOther // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [VideocallItem].
|
||||||
|
extension VideocallItemPatterns on VideocallItem {
|
||||||
|
/// A variant of `map` that fallback to returning `orElse`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _VideocallItem value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _VideocallItem() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// Callbacks receives the raw object, upcasted.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case final Subclass2 value:
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _VideocallItem value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _VideocallItem():
|
||||||
|
return $default(_that);case _:
|
||||||
|
throw StateError('Unexpected subclass');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `map` that fallback to returning `null`.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case final Subclass value:
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _VideocallItem value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _VideocallItem() when $default != null:
|
||||||
|
return $default(_that);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to an `orElse` callback.
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return orElse();
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String userId, bool isVideo, CallDirection direction, VideocallState state, bool uploadVideoStreamSelf, bool uploadVideoStreamOther)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _VideocallItem() when $default != null:
|
||||||
|
return $default(_that.userId,_that.isVideo,_that.direction,_that.state,_that.uploadVideoStreamSelf,_that.uploadVideoStreamOther);case _:
|
||||||
|
return orElse();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A `switch`-like method, using callbacks.
|
||||||
|
///
|
||||||
|
/// As opposed to `map`, this offers destructuring.
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case Subclass2(:final field2):
|
||||||
|
/// return ...;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String userId, bool isVideo, CallDirection direction, VideocallState state, bool uploadVideoStreamSelf, bool uploadVideoStreamOther) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _VideocallItem():
|
||||||
|
return $default(_that.userId,_that.isVideo,_that.direction,_that.state,_that.uploadVideoStreamSelf,_that.uploadVideoStreamOther);case _:
|
||||||
|
throw StateError('Unexpected subclass');
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// A variant of `when` that fallback to returning `null`
|
||||||
|
///
|
||||||
|
/// It is equivalent to doing:
|
||||||
|
/// ```dart
|
||||||
|
/// switch (sealedClass) {
|
||||||
|
/// case Subclass(:final field):
|
||||||
|
/// return ...;
|
||||||
|
/// case _:
|
||||||
|
/// return null;
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
|
||||||
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String userId, bool isVideo, CallDirection direction, VideocallState state, bool uploadVideoStreamSelf, bool uploadVideoStreamOther)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _VideocallItem() when $default != null:
|
||||||
|
return $default(_that.userId,_that.isVideo,_that.direction,_that.state,_that.uploadVideoStreamSelf,_that.uploadVideoStreamOther);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
|
||||||
|
|
||||||
|
class _VideocallItem implements VideocallItem {
|
||||||
|
const _VideocallItem({required this.userId, required this.isVideo, required this.direction, required this.state, this.uploadVideoStreamSelf = false, this.uploadVideoStreamOther = false});
|
||||||
|
|
||||||
|
|
||||||
|
@override final String userId;
|
||||||
|
@override final bool isVideo;
|
||||||
|
@override final CallDirection direction;
|
||||||
|
@override final VideocallState state;
|
||||||
|
@override@JsonKey() final bool uploadVideoStreamSelf;
|
||||||
|
@override@JsonKey() final bool uploadVideoStreamOther;
|
||||||
|
|
||||||
|
/// Create a copy of VideocallItem
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$VideocallItemCopyWith<_VideocallItem> get copyWith => __$VideocallItemCopyWithImpl<_VideocallItem>(this, _$identity);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _VideocallItem&&(identical(other.userId, userId) || other.userId == userId)&&(identical(other.isVideo, isVideo) || other.isVideo == isVideo)&&(identical(other.direction, direction) || other.direction == direction)&&(identical(other.state, state) || other.state == state)&&(identical(other.uploadVideoStreamSelf, uploadVideoStreamSelf) || other.uploadVideoStreamSelf == uploadVideoStreamSelf)&&(identical(other.uploadVideoStreamOther, uploadVideoStreamOther) || other.uploadVideoStreamOther == uploadVideoStreamOther));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,userId,isVideo,direction,state,uploadVideoStreamSelf,uploadVideoStreamOther);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'VideocallItem(userId: $userId, isVideo: $isVideo, direction: $direction, state: $state, uploadVideoStreamSelf: $uploadVideoStreamSelf, uploadVideoStreamOther: $uploadVideoStreamOther)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$VideocallItemCopyWith<$Res> implements $VideocallItemCopyWith<$Res> {
|
||||||
|
factory _$VideocallItemCopyWith(_VideocallItem value, $Res Function(_VideocallItem) _then) = __$VideocallItemCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
String userId, bool isVideo, CallDirection direction, VideocallState state, bool uploadVideoStreamSelf, bool uploadVideoStreamOther
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$VideocallItemCopyWithImpl<$Res>
|
||||||
|
implements _$VideocallItemCopyWith<$Res> {
|
||||||
|
__$VideocallItemCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _VideocallItem _self;
|
||||||
|
final $Res Function(_VideocallItem) _then;
|
||||||
|
|
||||||
|
/// Create a copy of VideocallItem
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? userId = null,Object? isVideo = null,Object? direction = null,Object? state = null,Object? uploadVideoStreamSelf = null,Object? uploadVideoStreamOther = null,}) {
|
||||||
|
return _then(_VideocallItem(
|
||||||
|
userId: null == userId ? _self.userId : userId // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,isVideo: null == isVideo ? _self.isVideo : isVideo // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,direction: null == direction ? _self.direction : direction // ignore: cast_nullable_to_non_nullable
|
||||||
|
as CallDirection,state: null == state ? _self.state : state // ignore: cast_nullable_to_non_nullable
|
||||||
|
as VideocallState,uploadVideoStreamSelf: null == uploadVideoStreamSelf ? _self.uploadVideoStreamSelf : uploadVideoStreamSelf // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,uploadVideoStreamOther: null == uploadVideoStreamOther ? _self.uploadVideoStreamOther : uploadVideoStreamOther // ignore: cast_nullable_to_non_nullable
|
||||||
|
as bool,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// dart format on
|
||||||
@@ -11,8 +11,6 @@ import '../services/videocall_device_service.dart';
|
|||||||
import '../services/videocall_net_service.dart';
|
import '../services/videocall_net_service.dart';
|
||||||
import '../services/videocall_push_service.dart';
|
import '../services/videocall_push_service.dart';
|
||||||
|
|
||||||
// -- Service providers (thin wrappers over GetIt) --
|
|
||||||
|
|
||||||
final videocallManagerProvider = Provider<VideocallSdkManager>((ref) {
|
final videocallManagerProvider = Provider<VideocallSdkManager>((ref) {
|
||||||
return GetIt.I<VideocallSdkManager>();
|
return GetIt.I<VideocallSdkManager>();
|
||||||
});
|
});
|
||||||
@@ -42,8 +40,6 @@ final videocallNetServiceProvider = Provider<VideocallNetService>((ref) {
|
|||||||
return GetIt.I<VideocallNetService>();
|
return GetIt.I<VideocallNetService>();
|
||||||
});
|
});
|
||||||
|
|
||||||
// -- Stream providers (for reactive UI consumption) --
|
|
||||||
|
|
||||||
final videocallClientStateProvider =
|
final videocallClientStateProvider =
|
||||||
StreamProvider<VideocallClientState>((ref) {
|
StreamProvider<VideocallClientState>((ref) {
|
||||||
return ref.watch(videocallClientProvider).stateStream;
|
return ref.watch(videocallClientProvider).stateStream;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
import 'package:jc_sdk/jc_sdk.dart';
|
import 'package:jc_sdk/jc_sdk.dart';
|
||||||
|
|
||||||
import '../models/call_direction.dart';
|
import '../models/call_direction.dart';
|
||||||
@@ -12,16 +13,14 @@ class VideocallCallService with JCCallCallback {
|
|||||||
VideocallCallService({
|
VideocallCallService({
|
||||||
required VideocallClient client,
|
required VideocallClient client,
|
||||||
required VideocallDeviceService deviceService,
|
required VideocallDeviceService deviceService,
|
||||||
}) : _clientRef = client,
|
}) : _clientRef = client,
|
||||||
_deviceRef = deviceService;
|
_deviceRef = deviceService;
|
||||||
|
|
||||||
final VideocallClient _clientRef;
|
final VideocallClient _clientRef;
|
||||||
final VideocallDeviceService _deviceRef;
|
final VideocallDeviceService _deviceRef;
|
||||||
JCCall? _call;
|
JCCall? _call;
|
||||||
JCCall? get call => _call;
|
JCCall? get call => _call;
|
||||||
|
|
||||||
// -- Streams --
|
|
||||||
|
|
||||||
final _callItemAddController = StreamController<VideocallItem>.broadcast();
|
final _callItemAddController = StreamController<VideocallItem>.broadcast();
|
||||||
Stream<VideocallItem> get callItemAddStream => _callItemAddController.stream;
|
Stream<VideocallItem> get callItemAddStream => _callItemAddController.stream;
|
||||||
|
|
||||||
@@ -37,25 +36,9 @@ class VideocallCallService with JCCallCallback {
|
|||||||
final _missedCallController = StreamController<VideocallItem>.broadcast();
|
final _missedCallController = StreamController<VideocallItem>.broadcast();
|
||||||
Stream<VideocallItem> get missedCallStream => _missedCallController.stream;
|
Stream<VideocallItem> get missedCallStream => _missedCallController.stream;
|
||||||
|
|
||||||
final _messageReceivedController =
|
|
||||||
StreamController<({String type, String content, JCCallItem item})>
|
|
||||||
.broadcast();
|
|
||||||
Stream<({String type, String content, JCCallItem item})>
|
|
||||||
get messageReceivedStream => _messageReceivedController.stream;
|
|
||||||
|
|
||||||
final _dtmfReceivedController =
|
|
||||||
StreamController<({JCCallItem item, int value})>.broadcast();
|
|
||||||
Stream<({JCCallItem item, int value})> get dtmfReceivedStream =>
|
|
||||||
_dtmfReceivedController.stream;
|
|
||||||
|
|
||||||
final _earlyMediaController = StreamController<JCCallItem>.broadcast();
|
|
||||||
Stream<JCCallItem> get earlyMediaStream => _earlyMediaController.stream;
|
|
||||||
|
|
||||||
VideocallItem? _currentItem;
|
VideocallItem? _currentItem;
|
||||||
VideocallItem? get currentItem => _currentItem;
|
VideocallItem? get currentItem => _currentItem;
|
||||||
|
|
||||||
// -- Lifecycle --
|
|
||||||
|
|
||||||
Future<bool> initialize() async {
|
Future<bool> initialize() async {
|
||||||
final client = _clientRef.client;
|
final client = _clientRef.client;
|
||||||
final mediaDevice = _deviceRef.mediaDevice;
|
final mediaDevice = _deviceRef.mediaDevice;
|
||||||
@@ -66,8 +49,6 @@ class VideocallCallService with JCCallCallback {
|
|||||||
|
|
||||||
Future<bool> destroy() async => JCCall.destroy();
|
Future<bool> destroy() async => JCCall.destroy();
|
||||||
|
|
||||||
// -- Call actions --
|
|
||||||
|
|
||||||
Future<bool> startCall({
|
Future<bool> startCall({
|
||||||
required String userId,
|
required String userId,
|
||||||
required bool isVideo,
|
required bool isVideo,
|
||||||
@@ -89,14 +70,11 @@ class VideocallCallService with JCCallCallback {
|
|||||||
return _call!.term(item, reason ?? JCCall.REASON_NONE, description ?? '');
|
return _call!.term(item, reason ?? JCCall.REASON_NONE, description ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> termCall(
|
Future<bool> termCall(JCCallItem item, int reason, String description) async {
|
||||||
JCCallItem item, int reason, String description) async {
|
|
||||||
if (_call == null) return false;
|
if (_call == null) return false;
|
||||||
return _call!.term(item, reason, description);
|
return _call!.term(item, reason, description);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Mute/Audio --
|
|
||||||
|
|
||||||
Future<bool> mute(JCCallItem item) async {
|
Future<bool> mute(JCCallItem item) async {
|
||||||
if (_call == null) return false;
|
if (_call == null) return false;
|
||||||
return _call!.mute(item);
|
return _call!.mute(item);
|
||||||
@@ -117,8 +95,6 @@ class VideocallCallService with JCCallCallback {
|
|||||||
return _call!.setMicScale(item, scale);
|
return _call!.setMicScale(item, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Hold --
|
|
||||||
|
|
||||||
Future<bool> hold(JCCallItem item) async {
|
Future<bool> hold(JCCallItem item) async {
|
||||||
if (_call == null) return false;
|
if (_call == null) return false;
|
||||||
return _call!.hold(item);
|
return _call!.hold(item);
|
||||||
@@ -129,15 +105,14 @@ class VideocallCallService with JCCallCallback {
|
|||||||
return _call!.becomeActive(item);
|
return _call!.becomeActive(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Video --
|
|
||||||
|
|
||||||
Future<bool> enableUploadVideoStream(JCCallItem item) async {
|
Future<bool> enableUploadVideoStream(JCCallItem item) async {
|
||||||
if (_call == null) return false;
|
if (_call == null) return false;
|
||||||
return _call!.enableUploadVideoStream(item);
|
return _call!.enableUploadVideoStream(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<JCMediaDeviceVideoCanvas?> startLocalVideo(
|
Future<JCMediaDeviceVideoCanvas?> startLocalVideo({
|
||||||
{int renderType = JCMediaDevice.RENDER_FULL_AUTO}) async {
|
int renderType = JCMediaDevice.RENDER_FULL_AUTO,
|
||||||
|
}) async {
|
||||||
final item = await _call?.getActiveCallItem();
|
final item = await _call?.getActiveCallItem();
|
||||||
if (item == null) return null;
|
if (item == null) return null;
|
||||||
return item.startSelfVideo(renderType);
|
return item.startSelfVideo(renderType);
|
||||||
@@ -149,8 +124,9 @@ class VideocallCallService with JCCallCallback {
|
|||||||
return item.stopSelfVideo();
|
return item.stopSelfVideo();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<JCMediaDeviceVideoCanvas?> startRemoteVideo(
|
Future<JCMediaDeviceVideoCanvas?> startRemoteVideo({
|
||||||
{int renderType = JCMediaDevice.RENDER_FULL_CONTENT}) async {
|
int renderType = JCMediaDevice.RENDER_FULL_CONTENT,
|
||||||
|
}) async {
|
||||||
final item = await _call?.getActiveCallItem();
|
final item = await _call?.getActiveCallItem();
|
||||||
if (item == null) return null;
|
if (item == null) return null;
|
||||||
return item.startOtherVideo(renderType);
|
return item.startOtherVideo(renderType);
|
||||||
@@ -162,10 +138,11 @@ class VideocallCallService with JCCallCallback {
|
|||||||
return item.stopOtherVideo();
|
return item.stopOtherVideo();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Recording --
|
|
||||||
|
|
||||||
Future<bool> audioRecord(
|
Future<bool> audioRecord(
|
||||||
JCCallItem item, bool enable, String filePath) async {
|
JCCallItem item,
|
||||||
|
bool enable,
|
||||||
|
String filePath,
|
||||||
|
) async {
|
||||||
if (_call == null) return false;
|
if (_call == null) return false;
|
||||||
return _call!.audioRecord(item, enable, filePath);
|
return _call!.audioRecord(item, enable, filePath);
|
||||||
}
|
}
|
||||||
@@ -182,13 +159,18 @@ class VideocallCallService with JCCallCallback {
|
|||||||
}) async {
|
}) async {
|
||||||
if (_call == null) return false;
|
if (_call == null) return false;
|
||||||
return _call!.videoRecord(
|
return _call!.videoRecord(
|
||||||
item, enable, remote, width, height, filePath, bothAudio, keyframe);
|
item,
|
||||||
|
enable,
|
||||||
|
remote,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
filePath,
|
||||||
|
bothAudio,
|
||||||
|
keyframe,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Messaging --
|
Future<bool> sendMessage(JCCallItem item, String type, String content) async {
|
||||||
|
|
||||||
Future<bool> sendMessage(
|
|
||||||
JCCallItem item, String type, String content) async {
|
|
||||||
if (_call == null) return false;
|
if (_call == null) return false;
|
||||||
return _call!.sendMessage(item, type, content);
|
return _call!.sendMessage(item, type, content);
|
||||||
}
|
}
|
||||||
@@ -198,14 +180,9 @@ class VideocallCallService with JCCallCallback {
|
|||||||
return _call!.sendDtmf(item, value);
|
return _call!.sendDtmf(item, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Items --
|
|
||||||
|
|
||||||
Future<List<JCCallItem>?> getCallItems() async => _call?.getCallItems();
|
Future<List<JCCallItem>?> getCallItems() async => _call?.getCallItems();
|
||||||
|
|
||||||
Future<JCCallItem?> getActiveCallItem() async =>
|
Future<JCCallItem?> getActiveCallItem() async => _call?.getActiveCallItem();
|
||||||
_call?.getActiveCallItem();
|
|
||||||
|
|
||||||
// -- Config --
|
|
||||||
|
|
||||||
Future<String> getStatistics() async {
|
Future<String> getStatistics() async {
|
||||||
if (_call == null) return '';
|
if (_call == null) return '';
|
||||||
@@ -232,20 +209,13 @@ class VideocallCallService with JCCallCallback {
|
|||||||
static Future<MediaConfig> generateMediaConfigByMode(int mode) =>
|
static Future<MediaConfig> generateMediaConfigByMode(int mode) =>
|
||||||
JCCall.generateByMode(mode);
|
JCCall.generateByMode(mode);
|
||||||
|
|
||||||
// -- Dispose --
|
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_callItemAddController.close();
|
_callItemAddController.close();
|
||||||
_callItemUpdateController.close();
|
_callItemUpdateController.close();
|
||||||
_callItemRemoveController.close();
|
_callItemRemoveController.close();
|
||||||
_missedCallController.close();
|
_missedCallController.close();
|
||||||
_messageReceivedController.close();
|
|
||||||
_dtmfReceivedController.close();
|
|
||||||
_earlyMediaController.close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Internal --
|
|
||||||
|
|
||||||
VideocallState _mapCallState(int state) {
|
VideocallState _mapCallState(int state) {
|
||||||
if (state == JCCall.STATE_PENDING) return VideocallState.pending;
|
if (state == JCCall.STATE_PENDING) return VideocallState.pending;
|
||||||
if (state == JCCall.STATE_CONNECTING) return VideocallState.connecting;
|
if (state == JCCall.STATE_CONNECTING) return VideocallState.connecting;
|
||||||
@@ -270,30 +240,36 @@ class VideocallCallService with JCCallCallback {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- JCCallCallback --
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onCallItemAdd(JCCallItem item) {
|
void onCallItemAdd(JCCallItem item) {
|
||||||
|
debugPrint(
|
||||||
|
'[VideocallSDK] onCallItemAdd: userId=${item.getUserId()}, video=${item.getVideo()}, direction=${item.getDirection()}',
|
||||||
|
);
|
||||||
_currentItem = _buildItem(item, VideocallState.pending);
|
_currentItem = _buildItem(item, VideocallState.pending);
|
||||||
_callItemAddController.add(_currentItem!);
|
_callItemAddController.add(_currentItem!);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onCallItemUpdate(JCCallItem item, ChangeParam changeParam) {
|
void onCallItemUpdate(JCCallItem item, ChangeParam changeParam) {
|
||||||
_currentItem = _buildItem(item, _mapCallState(item.getState()));
|
final mappedState = _mapCallState(item.getState());
|
||||||
|
debugPrint(
|
||||||
|
'[VideocallSDK] onCallItemUpdate: rawState=${item.getState()}, mappedState=$mappedState, uploadSelf=${item.uploadVideoStreamSelf}, uploadOther=${item.uploadVideoStreamOther}',
|
||||||
|
);
|
||||||
|
_currentItem = _buildItem(item, mappedState);
|
||||||
_callItemUpdateController.add(_currentItem!);
|
_callItemUpdateController.add(_currentItem!);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onCallItemRemove(JCCallItem item, int reason, String description) {
|
void onCallItemRemove(JCCallItem item, int reason, String description) {
|
||||||
|
debugPrint(
|
||||||
|
'[VideocallSDK] onCallItemRemove: reason=$reason, description=$description',
|
||||||
|
);
|
||||||
_currentItem = null;
|
_currentItem = null;
|
||||||
_callItemRemoveController.add((reason: reason, description: description));
|
_callItemRemoveController.add((reason: reason, description: description));
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onMessageReceive(String type, String content, JCCallItem item) {
|
void onMessageReceive(String type, String content, JCCallItem item) {}
|
||||||
_messageReceivedController.add((type: type, content: content, item: item));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onMissedCallItem(JCCallItem item) {
|
void onMissedCallItem(JCCallItem item) {
|
||||||
@@ -301,14 +277,10 @@ class VideocallCallService with JCCallCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onDtmfReceived(JCCallItem item, int value) {
|
void onDtmfReceived(JCCallItem item, int value) {}
|
||||||
_dtmfReceivedController.add((item: item, value: value));
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onEarlyMediaReceived(JCCallItem item) {
|
void onEarlyMediaReceived(JCCallItem item) {}
|
||||||
_earlyMediaController.add(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onSipRingInfoReceived(JCCallItem item, String callSipType) {}
|
void onSipRingInfoReceived(JCCallItem item, String callSipType) {}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
JCMediaChannel? _channel;
|
JCMediaChannel? _channel;
|
||||||
JCMediaChannel? get channel => _channel;
|
JCMediaChannel? get channel => _channel;
|
||||||
|
|
||||||
// -- Streams --
|
|
||||||
|
|
||||||
final _stateChangeController =
|
final _stateChangeController =
|
||||||
StreamController<({int state, int oldState})>.broadcast();
|
StreamController<({int state, int oldState})>.broadcast();
|
||||||
@@ -97,7 +96,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
Stream<({int operationId, bool result, int reason})>
|
Stream<({int operationId, bool result, int reason})>
|
||||||
get inviteSipUserResultStream => _inviteSipUserResultController.stream;
|
get inviteSipUserResultStream => _inviteSipUserResultController.stream;
|
||||||
|
|
||||||
// -- Lifecycle --
|
|
||||||
|
|
||||||
Future<bool> initialize() async {
|
Future<bool> initialize() async {
|
||||||
final client = _clientRef.client;
|
final client = _clientRef.client;
|
||||||
@@ -109,7 +107,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
|
|
||||||
Future<bool> destroy() async => JCMediaChannel.destroy();
|
Future<bool> destroy() async => JCMediaChannel.destroy();
|
||||||
|
|
||||||
// -- Channel actions --
|
|
||||||
|
|
||||||
Future<bool> join(String channelId, {JoinParam? joinParam}) async {
|
Future<bool> join(String channelId, {JoinParam? joinParam}) async {
|
||||||
if (_channel == null) return false;
|
if (_channel == null) return false;
|
||||||
@@ -131,7 +128,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
return _channel!.query(channelId);
|
return _channel!.query(channelId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Audio/Video streams --
|
|
||||||
|
|
||||||
Future<bool> enableUploadAudioStream(bool enable) async {
|
Future<bool> enableUploadAudioStream(bool enable) async {
|
||||||
if (_channel == null) return false;
|
if (_channel == null) return false;
|
||||||
@@ -159,7 +155,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
return _channel!.requestScreenVideo(screenUri, pictureSize);
|
return _channel!.requestScreenVideo(screenUri, pictureSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Screen share / CDN / Recording --
|
|
||||||
|
|
||||||
Future<bool> enableScreenShare(
|
Future<bool> enableScreenShare(
|
||||||
bool enable, ScreenShareParam? screenShareParam) async {
|
bool enable, ScreenShareParam? screenShareParam) async {
|
||||||
@@ -177,7 +172,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
return _channel!.enableRecord(enable, recordParam);
|
return _channel!.enableRecord(enable, recordParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Participants --
|
|
||||||
|
|
||||||
Future<List<JCMediaChannelParticipant>?> getParticipants() async =>
|
Future<List<JCMediaChannelParticipant>?> getParticipants() async =>
|
||||||
_channel?.getParticipants();
|
_channel?.getParticipants();
|
||||||
@@ -195,7 +189,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
return _channel!.inviteSipUser(userId, sipParam);
|
return _channel!.inviteSipUser(userId, sipParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Custom roles/state --
|
|
||||||
|
|
||||||
Future<bool> setCustomRole(int customRole,
|
Future<bool> setCustomRole(int customRole,
|
||||||
{JCMediaChannelParticipant? participant}) async {
|
{JCMediaChannelParticipant? participant}) async {
|
||||||
@@ -219,7 +212,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
return _channel!.getCustomState();
|
return _channel!.getCustomState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Channel properties --
|
|
||||||
|
|
||||||
Future<int> setCustomProperty(String property) async {
|
Future<int> setCustomProperty(String property) async {
|
||||||
if (_channel == null) return -1;
|
if (_channel == null) return -1;
|
||||||
@@ -231,7 +223,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
return _channel!.getCustomProperty();
|
return _channel!.getCustomProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Channel info --
|
|
||||||
|
|
||||||
Future<String> getChannelUri() async {
|
Future<String> getChannelUri() async {
|
||||||
if (_channel == null) return '';
|
if (_channel == null) return '';
|
||||||
@@ -313,7 +304,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
return _channel!.getScreenUserId();
|
return _channel!.getScreenUserId();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Self participant --
|
|
||||||
|
|
||||||
Future<JCMediaChannelParticipant?> getSelfParticipant() async {
|
Future<JCMediaChannelParticipant?> getSelfParticipant() async {
|
||||||
return _channel?.getSelfParticipant();
|
return _channel?.getSelfParticipant();
|
||||||
@@ -325,7 +315,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
return _channel!.subscribeParticipantAudio(participant, subscribe);
|
return _channel!.subscribeParticipantAudio(participant, subscribe);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Screen share video --
|
|
||||||
|
|
||||||
Future<JCMediaDeviceVideoCanvas?> startScreenShareVideo(
|
Future<JCMediaDeviceVideoCanvas?> startScreenShareVideo(
|
||||||
int renderType, int pictureSize) async {
|
int renderType, int pictureSize) async {
|
||||||
@@ -337,7 +326,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
return _channel!.stopScreenShareVideo();
|
return _channel!.stopScreenShareVideo();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Video ratio / resolution --
|
|
||||||
|
|
||||||
Future<bool> enableSelfVideoRatio(bool enable, double ratio) async {
|
Future<bool> enableSelfVideoRatio(bool enable, double ratio) async {
|
||||||
if (_channel == null) return false;
|
if (_channel == null) return false;
|
||||||
@@ -349,7 +337,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
return _channel!.getMaxResolution();
|
return _channel!.getMaxResolution();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Messaging --
|
|
||||||
|
|
||||||
Future<bool> sendMessage(
|
Future<bool> sendMessage(
|
||||||
String type, String content, String toUserId) async {
|
String type, String content, String toUserId) async {
|
||||||
@@ -367,14 +354,12 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
return _channel!.sendCommandToDelivery(command);
|
return _channel!.sendCommandToDelivery(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Statistics --
|
|
||||||
|
|
||||||
Future<String> getStatistics() async {
|
Future<String> getStatistics() async {
|
||||||
if (_channel == null) return '';
|
if (_channel == null) return '';
|
||||||
return _channel!.getStatistics();
|
return _channel!.getStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Volume change notify --
|
|
||||||
|
|
||||||
Future<bool> enableVolumeChangeNotify(bool value) async {
|
Future<bool> enableVolumeChangeNotify(bool value) async {
|
||||||
if (_channel == null) return false;
|
if (_channel == null) return false;
|
||||||
@@ -386,7 +371,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
return _channel!.getVolumeChangeNotify();
|
return _channel!.getVolumeChangeNotify();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Dispose --
|
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_stateChangeController.close();
|
_stateChangeController.close();
|
||||||
@@ -403,7 +387,6 @@ class VideocallChannelService with JCMediaChannelCallback {
|
|||||||
_inviteSipUserResultController.close();
|
_inviteSipUserResultController.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- JCMediaChannelCallback --
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onMediaChannelStateChange(int state, int oldState) =>
|
void onMediaChannelStateChange(int state, int oldState) =>
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ class VideocallClient with JCClientCallback {
|
|||||||
JCClient? _client;
|
JCClient? _client;
|
||||||
JCClient? get client => _client;
|
JCClient? get client => _client;
|
||||||
|
|
||||||
// -- Streams --
|
|
||||||
|
|
||||||
final _stateController = StreamController<VideocallClientState>.broadcast();
|
final _stateController = StreamController<VideocallClientState>.broadcast();
|
||||||
Stream<VideocallClientState> get stateStream => _stateController.stream;
|
Stream<VideocallClientState> get stateStream => _stateController.stream;
|
||||||
@@ -46,7 +45,6 @@ class VideocallClient with JCClientCallback {
|
|||||||
VideocallClientState _state = VideocallClientState.notInitialized;
|
VideocallClientState _state = VideocallClientState.notInitialized;
|
||||||
VideocallClientState get state => _state;
|
VideocallClientState get state => _state;
|
||||||
|
|
||||||
// -- Lifecycle --
|
|
||||||
|
|
||||||
Future<bool> initialize() async {
|
Future<bool> initialize() async {
|
||||||
try {
|
try {
|
||||||
@@ -75,7 +73,6 @@ class VideocallClient with JCClientCallback {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Auth --
|
|
||||||
|
|
||||||
Future<bool> login({
|
Future<bool> login({
|
||||||
required String userId,
|
required String userId,
|
||||||
@@ -100,7 +97,6 @@ class VideocallClient with JCClientCallback {
|
|||||||
return _client!.logout();
|
return _client!.logout();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- User info --
|
|
||||||
|
|
||||||
Future<String?> getUserId() async => _client?.getUserId();
|
Future<String?> getUserId() async => _client?.getUserId();
|
||||||
|
|
||||||
@@ -118,7 +114,6 @@ class VideocallClient with JCClientCallback {
|
|||||||
return _client!.getServerUid();
|
return _client!.getServerUid();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Server config --
|
|
||||||
|
|
||||||
Future<bool> setServerAddress(String serverAddress) async {
|
Future<bool> setServerAddress(String serverAddress) async {
|
||||||
if (_client == null) return false;
|
if (_client == null) return false;
|
||||||
@@ -130,14 +125,12 @@ class VideocallClient with JCClientCallback {
|
|||||||
return _client!.getServerAddress();
|
return _client!.getServerAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Foreground/background --
|
|
||||||
|
|
||||||
Future<bool> setForeground(bool foreground) async {
|
Future<bool> setForeground(bool foreground) async {
|
||||||
if (_client == null) return false;
|
if (_client == null) return false;
|
||||||
return _client!.setForeground(foreground);
|
return _client!.setForeground(foreground);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Online messaging --
|
|
||||||
|
|
||||||
Future<int> sendOnlineMessage({
|
Future<int> sendOnlineMessage({
|
||||||
required String userId,
|
required String userId,
|
||||||
@@ -147,7 +140,6 @@ class VideocallClient with JCClientCallback {
|
|||||||
return _client!.sendOnlineMessage(userId, content);
|
return _client!.sendOnlineMessage(userId, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Params --
|
|
||||||
|
|
||||||
Future<CreateParam?> getCreateParam() async => _client?.getCreateParam();
|
Future<CreateParam?> getCreateParam() async => _client?.getCreateParam();
|
||||||
|
|
||||||
@@ -158,7 +150,6 @@ class VideocallClient with JCClientCallback {
|
|||||||
return _client!.getState();
|
return _client!.getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Dispose --
|
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_stateController.close();
|
_stateController.close();
|
||||||
@@ -169,7 +160,6 @@ class VideocallClient with JCClientCallback {
|
|||||||
_serverMessageController.close();
|
_serverMessageController.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Internal --
|
|
||||||
|
|
||||||
void _updateState(VideocallClientState newState) {
|
void _updateState(VideocallClientState newState) {
|
||||||
_state = newState;
|
_state = newState;
|
||||||
@@ -207,7 +197,6 @@ class VideocallClient with JCClientCallback {
|
|||||||
return LoginFailureReason.unknown;
|
return LoginFailureReason.unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- JCClientCallback --
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClientStateChange(int state, int oldState) {
|
void onClientStateChange(int state, int oldState) {
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
JCMediaDevice? _mediaDevice;
|
JCMediaDevice? _mediaDevice;
|
||||||
JCMediaDevice? get mediaDevice => _mediaDevice;
|
JCMediaDevice? get mediaDevice => _mediaDevice;
|
||||||
|
|
||||||
// -- Streams --
|
|
||||||
|
|
||||||
final _cameraUpdateController = StreamController<void>.broadcast();
|
final _cameraUpdateController = StreamController<void>.broadcast();
|
||||||
Stream<void> get cameraUpdateStream => _cameraUpdateController.stream;
|
Stream<void> get cameraUpdateStream => _cameraUpdateController.stream;
|
||||||
@@ -41,7 +40,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
final _audioResumeController = StreamController<void>.broadcast();
|
final _audioResumeController = StreamController<void>.broadcast();
|
||||||
Stream<void> get audioResumeStream => _audioResumeController.stream;
|
Stream<void> get audioResumeStream => _audioResumeController.stream;
|
||||||
|
|
||||||
// -- Lifecycle --
|
|
||||||
|
|
||||||
Future<bool> initialize() async {
|
Future<bool> initialize() async {
|
||||||
final client = _clientRef.client;
|
final client = _clientRef.client;
|
||||||
@@ -52,7 +50,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
|
|
||||||
Future<bool> destroy() async => JCMediaDevice.destroy();
|
Future<bool> destroy() async => JCMediaDevice.destroy();
|
||||||
|
|
||||||
// -- Camera --
|
|
||||||
|
|
||||||
Future<bool> isCameraOpen() async {
|
Future<bool> isCameraOpen() async {
|
||||||
if (_mediaDevice == null) return false;
|
if (_mediaDevice == null) return false;
|
||||||
@@ -106,7 +103,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
return _mediaDevice!.getCameraType(cameraIndex);
|
return _mediaDevice!.getCameraType(cameraIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Exposure --
|
|
||||||
|
|
||||||
Future<int> getMinExposureCompensation() async {
|
Future<int> getMinExposureCompensation() async {
|
||||||
if (_mediaDevice == null) return 0;
|
if (_mediaDevice == null) return 0;
|
||||||
@@ -133,7 +129,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
return _mediaDevice!.setExposureCompensation(level);
|
return _mediaDevice!.setExposureCompensation(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Flash --
|
|
||||||
|
|
||||||
Future<bool> isCameraFlashSupported() async {
|
Future<bool> isCameraFlashSupported() async {
|
||||||
if (_mediaDevice == null) return false;
|
if (_mediaDevice == null) return false;
|
||||||
@@ -145,7 +140,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
return _mediaDevice!.enableFlash(enable);
|
return _mediaDevice!.enableFlash(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Focus/Zoom --
|
|
||||||
|
|
||||||
Future<bool> handleFocusMetering(
|
Future<bool> handleFocusMetering(
|
||||||
JCMediaDeviceVideoCanvas canvas, double xPercent, double yPercent) async {
|
JCMediaDeviceVideoCanvas canvas, double xPercent, double yPercent) async {
|
||||||
@@ -168,7 +162,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
return _mediaDevice!.getCameraCurrentZoom();
|
return _mediaDevice!.getCameraCurrentZoom();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Speaker --
|
|
||||||
|
|
||||||
Future<bool> isSpeakerOn() async {
|
Future<bool> isSpeakerOn() async {
|
||||||
if (_mediaDevice == null) return false;
|
if (_mediaDevice == null) return false;
|
||||||
@@ -195,7 +188,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
return _mediaDevice!.getAudioRouteType();
|
return _mediaDevice!.getAudioRouteType();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Audio --
|
|
||||||
|
|
||||||
Future<bool> isAudioStart() async {
|
Future<bool> isAudioStart() async {
|
||||||
if (_mediaDevice == null) return false;
|
if (_mediaDevice == null) return false;
|
||||||
@@ -245,7 +237,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
Future<JCMediaDeviceAudioParam?> getAudioParam() async =>
|
Future<JCMediaDeviceAudioParam?> getAudioParam() async =>
|
||||||
_mediaDevice?.getAudioParam();
|
_mediaDevice?.getAudioParam();
|
||||||
|
|
||||||
// -- Volume --
|
|
||||||
|
|
||||||
Future<int> getOutputVolume() async {
|
Future<int> getOutputVolume() async {
|
||||||
if (_mediaDevice == null) return 0;
|
if (_mediaDevice == null) return 0;
|
||||||
@@ -267,7 +258,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
return _mediaDevice!.removeVolumeCallback(callback);
|
return _mediaDevice!.removeVolumeCallback(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Video rendering --
|
|
||||||
|
|
||||||
Future<JCMediaDeviceVideoCanvas?> startCameraVideo(
|
Future<JCMediaDeviceVideoCanvas?> startCameraVideo(
|
||||||
{int renderType = JCMediaDevice.RENDER_FULL_AUTO}) async {
|
{int renderType = JCMediaDevice.RENDER_FULL_AUTO}) async {
|
||||||
@@ -284,7 +274,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
return _mediaDevice!.stopVideo(canvas);
|
return _mediaDevice!.stopVideo(canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Video file (custom capture) --
|
|
||||||
|
|
||||||
Future<bool> isVideoFileOpen() async {
|
Future<bool> isVideoFileOpen() async {
|
||||||
if (_mediaDevice == null) return false;
|
if (_mediaDevice == null) return false;
|
||||||
@@ -313,7 +302,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
return _mediaDevice!.stopVideoFile();
|
return _mediaDevice!.stopVideoFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Video angle --
|
|
||||||
|
|
||||||
Future<bool> setVideoAngle(int angle) async {
|
Future<bool> setVideoAngle(int angle) async {
|
||||||
if (_mediaDevice == null) return false;
|
if (_mediaDevice == null) return false;
|
||||||
@@ -325,7 +313,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
return _mediaDevice!.getVideoAngle();
|
return _mediaDevice!.getVideoAngle();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Frame callbacks --
|
|
||||||
|
|
||||||
Future<bool> setAudioFrameCallback(JCAudioFrameCallback? callback) async {
|
Future<bool> setAudioFrameCallback(JCAudioFrameCallback? callback) async {
|
||||||
if (_mediaDevice == null) return false;
|
if (_mediaDevice == null) return false;
|
||||||
@@ -337,7 +324,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
return _mediaDevice!.setVideoFrameCallback(callback);
|
return _mediaDevice!.setVideoFrameCallback(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Custom audio --
|
|
||||||
|
|
||||||
Future<bool> inputCustomAudioData(int sampleRateHz, int channels,
|
Future<bool> inputCustomAudioData(int sampleRateHz, int channels,
|
||||||
Uint8List byteBuffer, int playDelayMS, int recDelayMS, int clockDrift) async {
|
Uint8List byteBuffer, int playDelayMS, int recDelayMS, int clockDrift) async {
|
||||||
@@ -350,7 +336,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
return _mediaDevice?.getAudioOutputData(sampleRateHz, channels);
|
return _mediaDevice?.getAudioOutputData(sampleRateHz, channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Dispose --
|
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_cameraUpdateController.close();
|
_cameraUpdateController.close();
|
||||||
@@ -362,7 +347,6 @@ class VideocallDeviceService with JCMediaDeviceCallback {
|
|||||||
_audioResumeController.close();
|
_audioResumeController.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- JCMediaDeviceCallback --
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onCameraUpdate() => _cameraUpdateController.add(null);
|
void onCameraUpdate() => _cameraUpdateController.add(null);
|
||||||
|
|||||||
@@ -3,14 +3,12 @@ import 'dart:async';
|
|||||||
import 'package:jc_sdk/jc_sdk.dart';
|
import 'package:jc_sdk/jc_sdk.dart';
|
||||||
|
|
||||||
class VideocallNetService with JCNetCallback {
|
class VideocallNetService with JCNetCallback {
|
||||||
// -- Streams --
|
|
||||||
|
|
||||||
final _netChangeController =
|
final _netChangeController =
|
||||||
StreamController<({int newNetType, int oldNetType})>.broadcast();
|
StreamController<({int newNetType, int oldNetType})>.broadcast();
|
||||||
Stream<({int newNetType, int oldNetType})> get netChangeStream =>
|
Stream<({int newNetType, int oldNetType})> get netChangeStream =>
|
||||||
_netChangeController.stream;
|
_netChangeController.stream;
|
||||||
|
|
||||||
// -- Lifecycle --
|
|
||||||
|
|
||||||
void initialize() {
|
void initialize() {
|
||||||
JCNet.getInstance().addCallback(this);
|
JCNet.getInstance().addCallback(this);
|
||||||
@@ -20,20 +18,17 @@ class VideocallNetService with JCNetCallback {
|
|||||||
JCNet.getInstance().removeCallback(this);
|
JCNet.getInstance().removeCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Network info --
|
|
||||||
|
|
||||||
Future<int> getNetType() async => JCNet.getInstance().getNetType();
|
Future<int> getNetType() async => JCNet.getInstance().getNetType();
|
||||||
|
|
||||||
Future<bool> hasNet() async => JCNet.getInstance().hasNet();
|
Future<bool> hasNet() async => JCNet.getInstance().hasNet();
|
||||||
|
|
||||||
// -- Dispose --
|
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
uninitialize();
|
uninitialize();
|
||||||
_netChangeController.close();
|
_netChangeController.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- JCNetCallback --
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onNetChange(int newNetType, int oldNetType) {
|
void onNetChange(int newNetType, int oldNetType) {
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ class VideocallPushService {
|
|||||||
JCPush? _push;
|
JCPush? _push;
|
||||||
JCPush? get push => _push;
|
JCPush? get push => _push;
|
||||||
|
|
||||||
// -- Lifecycle --
|
|
||||||
|
|
||||||
Future<bool> initialize() async {
|
Future<bool> initialize() async {
|
||||||
final client = _clientRef.client;
|
final client = _clientRef.client;
|
||||||
@@ -25,7 +24,6 @@ class VideocallPushService {
|
|||||||
_push = null;
|
_push = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Push --
|
|
||||||
|
|
||||||
Future<bool> addPushInfo(JCPushTemplate info) async {
|
Future<bool> addPushInfo(JCPushTemplate info) async {
|
||||||
if (_push == null) return false;
|
if (_push == null) return false;
|
||||||
@@ -37,7 +35,6 @@ class VideocallPushService {
|
|||||||
return _push!.addPushTemplate(data);
|
return _push!.addPushTemplate(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -- Dispose --
|
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_push = null;
|
_push = null;
|
||||||
|
|||||||
@@ -14,8 +14,11 @@ dependencies:
|
|||||||
jc_sdk: ^2.16.5
|
jc_sdk: ^2.16.5
|
||||||
flutter_riverpod: ^3.0.3
|
flutter_riverpod: ^3.0.3
|
||||||
get_it: ^9.0.5
|
get_it: ^9.0.5
|
||||||
|
freezed_annotation: ^3.0.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
flutter_lints: ^5.0.0
|
flutter_lints: ^5.0.0
|
||||||
|
freezed: ^3.0.6
|
||||||
|
build_runner: ^2.4.15
|
||||||
|
|||||||
Reference in New Issue
Block a user