From 44f0a96bb40eeee4ef03908e38e94a432b6a1e3b Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Fri, 4 Nov 2022 18:12:43 +0545 Subject: [PATCH 01/30] chore : test flutter 3 support --- .../ParticipantViewFactory.kt | 2 +- .../method_channel_programmable_video.dart | 221 ++++++++---- .../lib/src/models/connect_options_model.dart | 54 ++- ...programmable_video_platform_interface.dart | 25 +- ...ethod_channel_programmable_video_test.dart | 334 +++++++++++++----- .../models/connect_options_model_test.dart | 25 +- .../test/models/room/room_model_test.dart | 1 - 7 files changed, 474 insertions(+), 188 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/ParticipantViewFactory.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/ParticipantViewFactory.kt index 8a2f731..f5c6aa0 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/ParticipantViewFactory.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/ParticipantViewFactory.kt @@ -10,7 +10,7 @@ import io.flutter.plugin.platform.PlatformViewFactory class ParticipantViewFactory(createArgsCodec: MessageCodec, private val plugin: PluginHandler) : PlatformViewFactory(createArgsCodec) { private val TAG = "RoomListener" - override fun create(context: Context, viewId: Int, args: Any?): PlatformView? { + override fun create(p0: Context?, p1: Int, p2: Any? PlatformView? { var videoTrack: VideoTrack? = null if (args != null) { diff --git a/programmable_video_platform_interface/lib/src/method_channel_programmable_video.dart b/programmable_video_platform_interface/lib/src/method_channel_programmable_video.dart index cb76904..06ab596 100644 --- a/programmable_video_platform_interface/lib/src/method_channel_programmable_video.dart +++ b/programmable_video_platform_interface/lib/src/method_channel_programmable_video.dart @@ -4,12 +4,8 @@ import 'dart:typed_data'; import 'package:enum_to_string/enum_to_string.dart'; import 'package:flutter/services.dart'; import 'package:meta/meta.dart'; -import 'package:twilio_programmable_video_platform_interface/src/models/capturers/camera_event.dart'; import 'package:twilio_programmable_video_platform_interface/src/camera_source.dart'; -import 'enums/enum_exports.dart'; -import 'models/local_participant/local_participant_event.dart'; -import 'models/model_exports.dart'; import 'programmable_video_platform_interface.dart'; /// An implementation of [ProgrammableVideoPlatform] that uses method channels. @@ -19,10 +15,14 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { : _methodChannel = MethodChannel('twilio_programmable_video'), _cameraChannel = EventChannel('twilio_programmable_video/camera'), _roomChannel = EventChannel('twilio_programmable_video/room'), - _remoteParticipantChannel = EventChannel('twilio_programmable_video/remote'), - _localParticipantChannel = EventChannel('twilio_programmable_video/local'), - _remoteDataTrackChannel = EventChannel('twilio_programmable_video/remote_data_track'), - _audioNotificationChannel = EventChannel('twilio_programmable_video/audio_notification'), + _remoteParticipantChannel = + EventChannel('twilio_programmable_video/remote'), + _localParticipantChannel = + EventChannel('twilio_programmable_video/local'), + _remoteDataTrackChannel = + EventChannel('twilio_programmable_video/remote_data_track'), + _audioNotificationChannel = + EventChannel('twilio_programmable_video/audio_notification'), _loggingChannel = EventChannel('twilio_programmable_video/logging'), super(); @@ -225,7 +225,10 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { 'CameraSource#getSources', null, ); - return methodData.map((dynamic source) => CameraSource.fromMap(Map.from(source))).toList(); + return methodData + .map((dynamic source) => + CameraSource.fromMap(Map.from(source))) + .toList(); } /// Calls native code to switch the camera. @@ -233,17 +236,20 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { /// Returns the new camera source. @override Future switchCamera(CameraSource source) async { - final methodData = await _methodChannel.invokeMethod('CameraCapturer#switchCamera', { + final methodData = await _methodChannel + .invokeMethod('CameraCapturer#switchCamera', { 'cameraId': source.cameraId, }); - return CameraSource.fromMap(Map.from(methodData['source'])); + return CameraSource.fromMap( + Map.from(methodData['source'])); } /// Calls native code to change the torch state. @override Future setTorch(bool enable) async { - await _methodChannel.invokeMethod('CameraCapturer#setTorch', { + await _methodChannel + .invokeMethod('CameraCapturer#setTorch', { 'enable': enable, }); } @@ -261,7 +267,8 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { /// This stream is used to update the Room in a plugin implementation. @override Stream cameraStream() { - _cameraStream ??= _cameraChannel.receiveBroadcastStream().map(_parseCameraEvent); + _cameraStream ??= + _cameraChannel.receiveBroadcastStream().map(_parseCameraEvent); return _cameraStream!; } @@ -283,8 +290,10 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { case 'cameraError': late TwilioExceptionModel twilioException; if (event['error'] != null) { - final errorMap = Map.from(event['error'] as Map); - twilioException = TwilioExceptionModel(errorMap['code'] as int, errorMap['message']); + final errorMap = Map.from( + event['error'] as Map); + twilioException = TwilioExceptionModel( + errorMap['code'] as int, errorMap['message']); } return CameraError(model, twilioException); default: @@ -307,7 +316,8 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { /// This stream is used to update the Room in a plugin implementation. @override Stream roomStream(int internalId) { - _roomStream ??= _roomChannel.receiveBroadcastStream(internalId).map(_parseRoomEvent); + _roomStream ??= + _roomChannel.receiveBroadcastStream(internalId).map(_parseRoomEvent); return _roomStream!; } @@ -324,39 +334,51 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { LocalParticipantModel? localParticipant; if (roomMap['localParticipant'] != null) { - localParticipant = LocalParticipantModel.fromEventChannelMap(Map.from(roomMap['localParticipant'])); + localParticipant = LocalParticipantModel.fromEventChannelMap( + Map.from(roomMap['localParticipant'])); } final remoteParticipants = []; if (roomMap['remoteParticipants'] != null) { - final List> remoteParticipantsList = roomMap['remoteParticipants'].map>((r) => Map.from(r)).toList(); + final List> remoteParticipantsList = + roomMap['remoteParticipants'] + .map>((r) => Map.from(r)) + .toList(); for (final remoteParticipantMap in remoteParticipantsList) { - remoteParticipants.add(RemoteParticipantModel.fromEventChannelMap(remoteParticipantMap)); + remoteParticipants.add( + RemoteParticipantModel.fromEventChannelMap(remoteParticipantMap)); } } RemoteParticipantModel? dominantSpeaker; if (roomMap['dominantSpeaker'] != null) { - final dominantSpeakerMap = Map.from(roomMap['dominantSpeaker']); - dominantSpeaker = RemoteParticipantModel.fromEventChannelMap(dominantSpeakerMap); + final dominantSpeakerMap = + Map.from(roomMap['dominantSpeaker']); + dominantSpeaker = + RemoteParticipantModel.fromEventChannelMap(dominantSpeakerMap); } late RemoteParticipantModel remoteParticipant; if (data['remoteParticipant'] != null) { - final remoteParticipantMap = Map.from(data['remoteParticipant']); - remoteParticipant = RemoteParticipantModel.fromEventChannelMap(remoteParticipantMap); + final remoteParticipantMap = + Map.from(data['remoteParticipant']); + remoteParticipant = + RemoteParticipantModel.fromEventChannelMap(remoteParticipantMap); } TwilioExceptionModel? twilioException; if (event['error'] != null) { - final errorMap = Map.from(event['error'] as Map); - twilioException = TwilioExceptionModel(errorMap['code'] as int, errorMap['message']); + final errorMap = + Map.from(event['error'] as Map); + twilioException = + TwilioExceptionModel(errorMap['code'] as int, errorMap['message']); } Region? mediaRegion; if (roomMap['mediaRegion'] != null) { - mediaRegion = EnumToString.fromString(Region.values, roomMap['mediaRegion']); + mediaRegion = + EnumToString.fromString(Region.values, roomMap['mediaRegion']); } final roomModel = RoomModel( @@ -408,7 +430,9 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { /// This stream is used to update the RemoteParticipants in a plugin implementation. @override Stream remoteParticipantStream(int internalId) { - _remoteParticipantStream ??= _remoteParticipantChannel.receiveBroadcastStream(internalId).map(_parseRemoteParticipantEvent); + _remoteParticipantStream ??= _remoteParticipantChannel + .receiveBroadcastStream(internalId) + .map(_parseRemoteParticipantEvent); return _remoteParticipantStream!; } @@ -421,58 +445,87 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { if (data['remoteParticipant'] == null) { return SkippableRemoteParticipantEvent(); } - final remoteParticipantMap = Map.from(data['remoteParticipant']); - final remoteParticipantModel = RemoteParticipantModel.fromEventChannelMap(remoteParticipantMap); + final remoteParticipantMap = + Map.from(data['remoteParticipant']); + final remoteParticipantModel = + RemoteParticipantModel.fromEventChannelMap(remoteParticipantMap); late RemoteAudioTrackPublicationModel remoteAudioTrackPublicationModel; if (data['remoteAudioTrackPublication'] != null) { - final remoteAudioTrackPublicationMap = Map.from(data['remoteAudioTrackPublication']); - remoteAudioTrackPublicationModel = RemoteAudioTrackPublicationModel.fromEventChannelMap(remoteAudioTrackPublicationMap); + final remoteAudioTrackPublicationMap = + Map.from(data['remoteAudioTrackPublication']); + remoteAudioTrackPublicationModel = + RemoteAudioTrackPublicationModel.fromEventChannelMap( + remoteAudioTrackPublicationMap); } RemoteAudioTrackModel? remoteAudioTrackModel; - if (['audioTrackSubscribed', 'audioTrackUnsubscribed', 'audioTrackEnabled', 'audioTrackDisabled'].contains(eventName)) { + if ([ + 'audioTrackSubscribed', + 'audioTrackUnsubscribed', + 'audioTrackEnabled', + 'audioTrackDisabled' + ].contains(eventName)) { remoteAudioTrackModel = remoteAudioTrackPublicationModel.remoteAudioTrack; if (remoteAudioTrackModel == null) { - final remoteAudioTrackMap = Map.from(data['remoteAudioTrack']); - remoteAudioTrackModel = RemoteAudioTrackModel.fromEventChannelMap(remoteAudioTrackMap); + final remoteAudioTrackMap = + Map.from(data['remoteAudioTrack']); + remoteAudioTrackModel = + RemoteAudioTrackModel.fromEventChannelMap(remoteAudioTrackMap); } } late RemoteDataTrackPublicationModel remoteDataTrackPublicationModel; if (data['remoteDataTrackPublication'] != null) { - final remoteDataTrackPublicationMap = Map.from(data['remoteDataTrackPublication']); - remoteDataTrackPublicationModel = RemoteDataTrackPublicationModel.fromEventChannelMap(remoteDataTrackPublicationMap); + final remoteDataTrackPublicationMap = + Map.from(data['remoteDataTrackPublication']); + remoteDataTrackPublicationModel = + RemoteDataTrackPublicationModel.fromEventChannelMap( + remoteDataTrackPublicationMap); } RemoteDataTrackModel? remoteDataTrackModel; if (['dataTrackSubscribed', 'dataTrackUnsubscribed'].contains(eventName)) { remoteDataTrackModel = remoteDataTrackPublicationModel.remoteDataTrack; if (remoteDataTrackModel == null) { - final remoteDataTrackMap = Map.from(data['remoteDataTrack']); - remoteDataTrackModel = RemoteDataTrackModel.fromEventChannelMap(remoteDataTrackMap); + final remoteDataTrackMap = + Map.from(data['remoteDataTrack']); + remoteDataTrackModel = + RemoteDataTrackModel.fromEventChannelMap(remoteDataTrackMap); } } late RemoteVideoTrackPublicationModel remoteVideoTrackPublicationModel; if (data['remoteVideoTrackPublication'] != null) { - final remoteVideoTrackPublicationMap = Map.from(data['remoteVideoTrackPublication']); - remoteVideoTrackPublicationModel = RemoteVideoTrackPublicationModel.fromEventChannelMap(remoteVideoTrackPublicationMap); + final remoteVideoTrackPublicationMap = + Map.from(data['remoteVideoTrackPublication']); + remoteVideoTrackPublicationModel = + RemoteVideoTrackPublicationModel.fromEventChannelMap( + remoteVideoTrackPublicationMap); } RemoteVideoTrackModel? remoteVideoTrackModel; - if (['videoTrackSubscribed', 'videoTrackUnsubscribed', 'videoTrackEnabled', 'videoTrackDisabled'].contains(eventName)) { + if ([ + 'videoTrackSubscribed', + 'videoTrackUnsubscribed', + 'videoTrackEnabled', + 'videoTrackDisabled' + ].contains(eventName)) { remoteVideoTrackModel = remoteVideoTrackPublicationModel.remoteVideoTrack; if (remoteVideoTrackModel == null) { - final remoteVideoTrackMap = Map.from(data['remoteVideoTrack']); - remoteVideoTrackModel = RemoteVideoTrackModel.fromEventChannelMap(remoteVideoTrackMap); + final remoteVideoTrackMap = + Map.from(data['remoteVideoTrack']); + remoteVideoTrackModel = + RemoteVideoTrackModel.fromEventChannelMap(remoteVideoTrackMap); } } TwilioExceptionModel? twilioException; if (event['error'] != null) { - final errorMap = Map.from(event['error'] as Map); - twilioException = TwilioExceptionModel(errorMap['code'] as int, errorMap['message']); + final errorMap = + Map.from(event['error'] as Map); + twilioException = + TwilioExceptionModel(errorMap['code'] as int, errorMap['message']); } switch (eventName) { @@ -495,7 +548,8 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { return (remoteAudioTrackModel != null) ? RemoteAudioTrackSubscribed( remoteParticipantModel: remoteParticipantModel, - remoteAudioTrackPublicationModel: remoteAudioTrackPublicationModel, + remoteAudioTrackPublicationModel: + remoteAudioTrackPublicationModel, remoteAudioTrackModel: remoteAudioTrackModel, ) : SkippableRemoteParticipantEvent(); @@ -503,7 +557,8 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { return (twilioException != null) ? RemoteAudioTrackSubscriptionFailed( remoteParticipantModel: remoteParticipantModel, - remoteAudioTrackPublicationModel: remoteAudioTrackPublicationModel, + remoteAudioTrackPublicationModel: + remoteAudioTrackPublicationModel, exception: twilioException, ) : SkippableRemoteParticipantEvent(); @@ -516,7 +571,8 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { return (remoteAudioTrackModel != null) ? RemoteAudioTrackUnsubscribed( remoteParticipantModel: remoteParticipantModel, - remoteAudioTrackPublicationModel: remoteAudioTrackPublicationModel, + remoteAudioTrackPublicationModel: + remoteAudioTrackPublicationModel, remoteAudioTrackModel: remoteAudioTrackModel, ) : SkippableRemoteParticipantEvent(); @@ -529,7 +585,8 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { return (remoteDataTrackModel != null) ? RemoteDataTrackSubscribed( remoteParticipantModel: remoteParticipantModel, - remoteDataTrackPublicationModel: remoteDataTrackPublicationModel, + remoteDataTrackPublicationModel: + remoteDataTrackPublicationModel, remoteDataTrackModel: remoteDataTrackModel, ) : SkippableRemoteParticipantEvent(); @@ -549,7 +606,8 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { return (remoteDataTrackModel != null) ? RemoteDataTrackUnsubscribed( remoteParticipantModel: remoteParticipantModel, - remoteDataTrackPublicationModel: remoteDataTrackPublicationModel, + remoteDataTrackPublicationModel: + remoteDataTrackPublicationModel, remoteDataTrackModel: remoteDataTrackModel, ) : SkippableRemoteParticipantEvent(); @@ -572,7 +630,8 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { return (remoteVideoTrackModel != null) ? RemoteVideoTrackSubscribed( remoteParticipantModel: remoteParticipantModel, - remoteVideoTrackPublicationModel: remoteVideoTrackPublicationModel, + remoteVideoTrackPublicationModel: + remoteVideoTrackPublicationModel, remoteVideoTrackModel: remoteVideoTrackModel, ) : SkippableRemoteParticipantEvent(); @@ -580,7 +639,8 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { return (twilioException != null) ? RemoteVideoTrackSubscriptionFailed( remoteParticipantModel: remoteParticipantModel, - remoteVideoTrackPublicationModel: remoteVideoTrackPublicationModel, + remoteVideoTrackPublicationModel: + remoteVideoTrackPublicationModel, exception: twilioException, ) : SkippableRemoteParticipantEvent(); @@ -593,14 +653,17 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { return (remoteVideoTrackModel != null) ? RemoteVideoTrackUnsubscribed( remoteParticipantModel: remoteParticipantModel, - remoteVideoTrackPublicationModel: remoteVideoTrackPublicationModel, + remoteVideoTrackPublicationModel: + remoteVideoTrackPublicationModel, remoteVideoTrackModel: remoteVideoTrackModel, ) : SkippableRemoteParticipantEvent(); case 'networkQualityLevelChanged': NetworkQualityLevel? networkQualityLevel; if (data['networkQualityLevel'] != null) { - networkQualityLevel = EnumToString.fromString(NetworkQualityLevel.values, data['networkQualityLevel']) ?? NetworkQualityLevel.NETWORK_QUALITY_LEVEL_UNKNOWN; + networkQualityLevel = EnumToString.fromString( + NetworkQualityLevel.values, data['networkQualityLevel']) ?? + NetworkQualityLevel.NETWORK_QUALITY_LEVEL_UNKNOWN; } final networkQualityLevelEnum = networkQualityLevel; @@ -625,7 +688,9 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { /// This stream is used to update the LocalParticipant in a plugin implementation. @override Stream localParticipantStream(int internalId) { - _localParticipantStream ??= _localParticipantChannel.receiveBroadcastStream(internalId).map(_parseLocalParticipantEvent); + _localParticipantStream ??= _localParticipantChannel + .receiveBroadcastStream(internalId) + .map(_parseLocalParticipantEvent); return _localParticipantStream!; } @@ -637,14 +702,17 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { return SkippableLocalParticipantEvent(); } - final localParticipantModel = LocalParticipantModel.fromEventChannelMap(Map.from(data['localParticipant'])); + final localParticipantModel = LocalParticipantModel.fromEventChannelMap( + Map.from(data['localParticipant'])); final String? eventName = event['name']; TwilioExceptionModel? twilioException; if (event['error'] != null) { - final errorMap = Map.from(event['error'] as Map); - twilioException = TwilioExceptionModel(errorMap['code'] as int, errorMap['message']); + final errorMap = + Map.from(event['error'] as Map); + twilioException = + TwilioExceptionModel(errorMap['code'] as int, errorMap['message']); } final exception = twilioException; @@ -653,8 +721,11 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { case 'audioTrackPublished': LocalAudioTrackPublicationModel? localAudioTrackPublication; if (data['localAudioTrackPublication'] != null) { - final localAudioTrackPublicationMap = Map.from(data['localAudioTrackPublication']); - localAudioTrackPublication = LocalAudioTrackPublicationModel.fromEventChannelMap(localAudioTrackPublicationMap); + final localAudioTrackPublicationMap = + Map.from(data['localAudioTrackPublication']); + localAudioTrackPublication = + LocalAudioTrackPublicationModel.fromEventChannelMap( + localAudioTrackPublicationMap); } final localAudioTrackPublicationModel = localAudioTrackPublication; @@ -682,8 +753,10 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { case 'dataTrackPublished': LocalDataTrackPublicationModel? localDataTrackPublication; if (data['localDataTrackPublication'] != null) { - final map = Map.from(data['localDataTrackPublication']); - localDataTrackPublication = LocalDataTrackPublicationModel.fromEventChannelMap(map); + final map = + Map.from(data['localDataTrackPublication']); + localDataTrackPublication = + LocalDataTrackPublicationModel.fromEventChannelMap(map); } final localDataTrackPublicationModel = localDataTrackPublication; @@ -711,8 +784,10 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { case 'videoTrackPublished': LocalVideoTrackPublicationModel? localVideoTrackPublication; if (data['localVideoTrackPublication'] != null) { - final map = Map.from(data['localVideoTrackPublication']); - localVideoTrackPublication = LocalVideoTrackPublicationModel.fromEventChannelMap(map); + final map = + Map.from(data['localVideoTrackPublication']); + localVideoTrackPublication = + LocalVideoTrackPublicationModel.fromEventChannelMap(map); } final localVideoTrackPublicationModel = localVideoTrackPublication; @@ -740,7 +815,9 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { case 'networkQualityLevelChanged': NetworkQualityLevel? networkQualityLevel; if (data['networkQualityLevel'] != null) { - networkQualityLevel = EnumToString.fromString(NetworkQualityLevel.values, data['networkQualityLevel']) ?? NetworkQualityLevel.NETWORK_QUALITY_LEVEL_UNKNOWN; + networkQualityLevel = EnumToString.fromString( + NetworkQualityLevel.values, data['networkQualityLevel']) ?? + NetworkQualityLevel.NETWORK_QUALITY_LEVEL_UNKNOWN; } final networkQualityLevelEnum = networkQualityLevel; @@ -769,7 +846,9 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { /// This stream is used to update the RemoteDataTrack in a plugin implementation. @override Stream remoteDataTrackStream(int internalId) { - _remoteDataTrackStream ??= _remoteDataTrackChannel.receiveBroadcastStream(internalId).map(_parseRemoteDataTrackEvent); + _remoteDataTrackStream ??= _remoteDataTrackChannel + .receiveBroadcastStream(internalId) + .map(_parseRemoteDataTrackEvent); return _remoteDataTrackStream!; } @@ -781,7 +860,8 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { if (data['remoteDataTrack'] == null) { return SkippableRemoteDataTrackEvent(); } - final remoteDataTrackModel = RemoteDataTrackModel.fromEventChannelMap(Map.from(data['remoteDataTrack'])); + final remoteDataTrackModel = RemoteDataTrackModel.fromEventChannelMap( + Map.from(data['remoteDataTrack'])); switch (eventName) { case 'stringMessage': return StringMessage(remoteDataTrackModel, data['message'] as String?); @@ -795,7 +875,8 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { // - data['message'].buffer // - (data['message'] as Uint8List).buffer // - return BufferMessage(remoteDataTrackModel, Uint8List.fromList(data['message']).buffer); + return BufferMessage( + remoteDataTrackModel, Uint8List.fromList(data['message']).buffer); default: return UnknownEvent(remoteDataTrackModel, eventName); } @@ -815,7 +896,9 @@ class MethodChannelProgrammableVideo extends ProgrammableVideoPlatform { /// This stream is used to update the RemoteDataTrack in a plugin implementation. @override Stream audioNotificationStream() { - _audioNotificationStream ??= _audioNotificationChannel.receiveBroadcastStream().map(_parseAudioNotificationEvent); + _audioNotificationStream ??= _audioNotificationChannel + .receiveBroadcastStream() + .map(_parseAudioNotificationEvent); return _audioNotificationStream!; } diff --git a/programmable_video_platform_interface/lib/src/models/connect_options_model.dart b/programmable_video_platform_interface/lib/src/models/connect_options_model.dart index 0aa4608..634dda9 100644 --- a/programmable_video_platform_interface/lib/src/models/connect_options_model.dart +++ b/programmable_video_platform_interface/lib/src/models/connect_options_model.dart @@ -4,8 +4,6 @@ import 'package:twilio_programmable_video_platform_interface/src/enums/enum_expo import 'package:twilio_programmable_video_platform_interface/src/models/model_exports.dart'; import 'package:twilio_programmable_video_platform_interface/src/video_codecs/video_codec.dart'; -import 'model_exports.dart'; - class ConnectOptionsModel { /// This Access Token is the credential you must use to identify and authenticate your request. /// More information about Access Tokens can be found here: https://www.twilio.com/docs/video/tutorials/user-identity-access-tokens @@ -60,13 +58,21 @@ class ConnectOptionsModel { this.enableNetworkQuality, this.networkQualityConfiguration, }) : assert(accessToken.isNotEmpty), - assert((audioTracks != null && audioTracks.isNotEmpty) || audioTracks == null), - assert((dataTracks != null && dataTracks.isNotEmpty) || dataTracks == null), - assert((preferredAudioCodecs != null && preferredAudioCodecs.isNotEmpty) || preferredAudioCodecs == null), - assert((preferredVideoCodecs != null && preferredVideoCodecs.isNotEmpty) || preferredVideoCodecs == null), - assert((region != null && region is Region) || region == null), - assert((videoTracks != null && videoTracks.isNotEmpty) || videoTracks == null), - assert((networkQualityConfiguration != null && networkQualityConfiguration is NetworkQualityConfigurationModel) || networkQualityConfiguration == null); + assert((audioTracks != null && audioTracks.isNotEmpty) || + audioTracks == null), + assert((dataTracks != null && dataTracks.isNotEmpty) || + dataTracks == null), + assert( + (preferredAudioCodecs != null && preferredAudioCodecs.isNotEmpty) || + preferredAudioCodecs == null), + assert( + (preferredVideoCodecs != null && preferredVideoCodecs.isNotEmpty) || + preferredVideoCodecs == null), + assert((region != null) || region == null), + assert((videoTracks != null && videoTracks.isNotEmpty) || + videoTracks == null), + assert((networkQualityConfiguration != null) || + networkQualityConfiguration == null); /// Create map from properties. Map toMap() { @@ -75,15 +81,33 @@ class ConnectOptionsModel { 'accessToken': accessToken, 'roomName': roomName, 'region': region != null ? EnumToString.convertToString(region) : null, - 'preferredAudioCodecs': preferredAudioCodecs != null ? Map.fromIterable(preferredAudioCodecs!.map((AudioCodec a) => a.name)) : null, - 'preferredVideoCodecs': preferredVideoCodecs != null ? Map.fromIterable(preferredVideoCodecs!.map((VideoCodec v) => v.name)) : null, - 'audioTracks': audioTracks != null ? Map.fromIterable(audioTracks!.map>((TrackModel a) => a.toMap())) : null, - 'dataTracks': dataTracks != null ? Map.fromIterable(dataTracks!.map>((LocalDataTrackModel d) => d.toMap())) : null, - 'videoTracks': videoTracks != null ? Map.fromIterable(videoTracks!.map>((LocalVideoTrackModel v) => v.toMap())) : null, + 'preferredAudioCodecs': preferredAudioCodecs != null + ? Map.fromIterable( + preferredAudioCodecs!.map((AudioCodec a) => a.name)) + : null, + 'preferredVideoCodecs': preferredVideoCodecs != null + ? Map.fromIterable( + preferredVideoCodecs!.map((VideoCodec v) => v.name)) + : null, + 'audioTracks': audioTracks != null + ? Map.fromIterable(audioTracks! + .map>((TrackModel a) => a.toMap())) + : null, + 'dataTracks': dataTracks != null + ? Map.fromIterable(dataTracks! + .map>((LocalDataTrackModel d) => d.toMap())) + : null, + 'videoTracks': videoTracks != null + ? Map.fromIterable(videoTracks! + .map>( + (LocalVideoTrackModel v) => v.toMap())) + : null, 'enableDominantSpeaker': enableDominantSpeaker, 'enableAutomaticSubscription': enableAutomaticSubscription, 'enableNetworkQuality': enableNetworkQuality, - 'networkQualityConfiguration': networkQualityConfiguration != null ? networkQualityConfiguration!.toMap() : null + 'networkQualityConfiguration': networkQualityConfiguration != null + ? networkQualityConfiguration!.toMap() + : null }, }; } diff --git a/programmable_video_platform_interface/lib/src/programmable_video_platform_interface.dart b/programmable_video_platform_interface/lib/src/programmable_video_platform_interface.dart index b30fe42..c0bb064 100644 --- a/programmable_video_platform_interface/lib/src/programmable_video_platform_interface.dart +++ b/programmable_video_platform_interface/lib/src/programmable_video_platform_interface.dart @@ -3,7 +3,6 @@ import 'dart:typed_data'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:twilio_programmable_video_platform_interface/src/camera_source.dart'; -import 'package:twilio_programmable_video_platform_interface/src/models/capturers/camera_event.dart'; import 'method_channel_programmable_video.dart'; import 'models/model_exports.dart'; @@ -73,7 +72,8 @@ abstract class ProgrammableVideoPlatform extends PlatformInterface { /// Calls native code to reset the speaker and bluetooth settings to their default values. /// The native layer will stop observing and managing changes to audio state. Future disableAudioSettings() { - throw UnimplementedError('disableAudioSettings() has not been implemented.'); + throw UnimplementedError( + 'disableAudioSettings() has not been implemented.'); } /// Calls native code to check if speaker mode is enabled. @@ -122,12 +122,14 @@ abstract class ProgrammableVideoPlatform extends PlatformInterface { /// Calls native code to enable playback of the RemoteAudioTrack. Future enableRemoteAudioTrack(bool enable, String sid) { - throw UnimplementedError('enableRemoteAudioTrack() has not been implemented.'); + throw UnimplementedError( + 'enableRemoteAudioTrack() has not been implemented.'); } /// Calls native code to check if playback is enabled for the RemoteAudioTrack. Future isRemoteAudioTrackPlaybackEnabled(String sid) { - throw UnimplementedError('isRemoteAudioTrackPlaybackEnabled() has not been implemented.'); + throw UnimplementedError( + 'isRemoteAudioTrackPlaybackEnabled() has not been implemented.'); } /// Calls native code for retrieving the different camera sources available. @@ -142,7 +144,8 @@ abstract class ProgrammableVideoPlatform extends PlatformInterface { /// Calls native code to change the torch state. Future setTorch(bool enabled) { - throw UnimplementedError('setTorch(bool enabled) has not been implemented.'); + throw UnimplementedError( + 'setTorch(bool enabled) has not been implemented.'); } //#endregion @@ -167,28 +170,32 @@ abstract class ProgrammableVideoPlatform extends PlatformInterface { /// /// This stream is used to update the RemoteParticipants in a plugin implementation. Stream? remoteParticipantStream(int internalId) { - throw UnimplementedError('remoteParticipantStream() has not been implemented'); + throw UnimplementedError( + 'remoteParticipantStream() has not been implemented'); } /// Stream of the BaseLocalParticipantEvent model. /// /// This stream is used to update the LocalParticipant in a plugin implementation. Stream? localParticipantStream(int internalId) { - throw UnimplementedError('localParticipantStream() has not been implemented'); + throw UnimplementedError( + 'localParticipantStream() has not been implemented'); } /// Stream of the BaseRemoteDataTrackEvent model. /// /// This stream is used to update the RemoteDataTrack in a plugin implementation. Stream? remoteDataTrackStream(int internalId) { - throw UnimplementedError('remoteDataTrackStream() has not been implemented'); + throw UnimplementedError( + 'remoteDataTrackStream() has not been implemented'); } /// Stream of the BaseRemoteDataTrackEvent model. /// /// This stream is used to update the RemoteDataTrack in a plugin implementation. Stream audioNotificationStream() { - throw UnimplementedError('audioNotificationStream() has not been implemented'); + throw UnimplementedError( + 'audioNotificationStream() has not been implemented'); } /// Stream of dynamic that contains all the native logging output. diff --git a/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart b/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart index 65b294e..c3de8d5 100644 --- a/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart +++ b/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart @@ -1,12 +1,10 @@ import 'dart:async'; -import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/mockito.dart'; import 'package:twilio_programmable_video_platform_interface/src/camera_source.dart'; import 'package:twilio_programmable_video_platform_interface/src/method_channel_programmable_video.dart'; -import 'package:twilio_programmable_video_platform_interface/src/models/model_exports.dart'; import 'package:twilio_programmable_video_platform_interface/src/programmable_video_platform_interface.dart'; import 'event_channel_maps.dart'; @@ -46,31 +44,41 @@ void main() { setUpAll(() { cameraController = StreamController.broadcast(); final cameraChannel = MockEventChannel(); - when(cameraChannel.receiveBroadcastStream(0)).thenAnswer((Invocation invoke) => cameraController.stream); + when(cameraChannel.receiveBroadcastStream(0)) + .thenAnswer((Invocation invoke) => cameraController.stream); roomController = StreamController.broadcast(sync: true); final roomChannel = MockEventChannel(); - when(roomChannel.receiveBroadcastStream(0)).thenAnswer((Invocation invoke) => roomController.stream); + when(roomChannel.receiveBroadcastStream(0)) + .thenAnswer((Invocation invoke) => roomController.stream); - remoteParticipantController = StreamController.broadcast(sync: true); + remoteParticipantController = + StreamController.broadcast(sync: true); final remoteParticipantChannel = MockEventChannel(); - when(remoteParticipantChannel.receiveBroadcastStream(0)).thenAnswer((Invocation invoke) => remoteParticipantController.stream); + when(remoteParticipantChannel.receiveBroadcastStream(0)) + .thenAnswer((Invocation invoke) => remoteParticipantController.stream); - localParticipantController = StreamController.broadcast(sync: true); + localParticipantController = + StreamController.broadcast(sync: true); final localParticipantChannel = MockEventChannel(); - when(localParticipantChannel.receiveBroadcastStream(0)).thenAnswer((Invocation invoke) => localParticipantController.stream); + when(localParticipantChannel.receiveBroadcastStream(0)) + .thenAnswer((Invocation invoke) => localParticipantController.stream); remoteDataTrackController = StreamController.broadcast(sync: true); final remoteDataTrackChannel = MockEventChannel(); - when(remoteDataTrackChannel.receiveBroadcastStream(0)).thenAnswer((Invocation invoke) => remoteDataTrackController.stream); + when(remoteDataTrackChannel.receiveBroadcastStream(0)) + .thenAnswer((Invocation invoke) => remoteDataTrackController.stream); - audioNotificationController = StreamController.broadcast(sync: true); + audioNotificationController = + StreamController.broadcast(sync: true); final audioNotificationChannel = MockEventChannel(); - when(audioNotificationChannel.receiveBroadcastStream()).thenAnswer((Invocation invoke) => audioNotificationController.stream); + when(audioNotificationChannel.receiveBroadcastStream()) + .thenAnswer((Invocation invoke) => audioNotificationController.stream); loggingController = StreamController.broadcast(sync: true); final loggingChannel = MockEventChannel(); - when(loggingChannel.receiveBroadcastStream()).thenAnswer((Invocation invoke) => loggingController.stream); + when(loggingChannel.receiveBroadcastStream()) + .thenAnswer((Invocation invoke) => loggingController.stream); instance = MethodChannelProgrammableVideo.private( MethodChannel('twilio_programmable_video'), @@ -83,7 +91,8 @@ void main() { loggingChannel, ); - MethodChannel('twilio_programmable_video').setMockMethodCallHandler((MethodCall methodCall) async { + MethodChannel('twilio_programmable_video') + .setMockMethodCallHandler((MethodCall methodCall) async { methodCalls.add(methodCall); switch (methodCall.method) { case 'debug': @@ -213,8 +222,11 @@ void main() { }); group('.sendBuffer()', () { - test('should call native code to send a ByteBuffer message in dart', () async { - final list = 'This data has been sent over the ByteBuffer channel of the DataTrack API'.codeUnits; + test('should call native code to send a ByteBuffer message in dart', + () async { + final list = + 'This data has been sent over the ByteBuffer channel of the DataTrack API' + .codeUnits; final bytes = Uint8List.fromList(list); final testMessage = bytes.buffer; final testName = 'testName'; @@ -231,7 +243,8 @@ void main() { }); group('.enableRemoteAudioTrack()', () { - test('should call native code to enable playback of a remote audiotrack', () async { + test('should call native code to enable playback of a remote audiotrack', + () async { final testEnable = true; final testSid = 'testSid'; @@ -247,7 +260,9 @@ void main() { }); group('.isRemoteAudioTrackPlaybackEnabled()', () { - test('should call native code to check if playback of a remote audiotrack is enabled', () async { + test( + 'should call native code to check if playback of a remote audiotrack is enabled', + () async { final testSid = 'testSid'; await instance.isRemoteAudioTrackPlaybackEnabled(testSid); @@ -335,7 +350,9 @@ void main() { ]); }); - test('should call native getSpeakerPhone code in dart and get same bool as previously set', () async { + test( + 'should call native getSpeakerPhone code in dart and get same bool as previously set', + () async { final result = await instance.getAudioSettings(); expect(result['speakerphoneEnabled'], speakerphoneOn); expect(result['bluetoothPreferred'], bluetoothOn); @@ -408,7 +425,8 @@ void main() { expect(lastEvent, isA()); }); - test('participantConnected event map should result in ParticipantConnected', () async { + test('participantConnected event map should result in ParticipantConnected', + () async { roomController.add({ 'name': 'participantConnected', 'data': { @@ -420,7 +438,9 @@ void main() { expect(lastEvent, isA()); }); - test('participantDisconnected event map should result in ParticipantDisconnected', () async { + test( + 'participantDisconnected event map should result in ParticipantDisconnected', + () async { roomController.add({ 'name': 'participantDisconnected', 'data': { @@ -450,7 +470,8 @@ void main() { expect(lastEvent, isA()); }); - test('recordingStarted event map should result in RecordingStarted', () async { + test('recordingStarted event map should result in RecordingStarted', + () async { roomController.add({ 'name': 'recordingStarted', 'data': {'room': EventChannelMaps.roomMap}, @@ -459,7 +480,8 @@ void main() { expect(lastEvent, isA()); }); - test('recordingStopped event map should result in RecordingStopped', () async { + test('recordingStopped event map should result in RecordingStopped', + () async { roomController.add({ 'name': 'recordingStopped', 'data': {'room': EventChannelMaps.roomMap}, @@ -468,7 +490,9 @@ void main() { expect(lastEvent, isA()); }); - test('dominantSpeakerChanged event map should result in DominantSpeakerChanged', () async { + test( + 'dominantSpeakerChanged event map should result in DominantSpeakerChanged', + () async { roomController.add({ 'name': 'dominantSpeakerChanged', 'data': { @@ -488,48 +512,59 @@ void main() { group('.remoteParticipantStream()', () { test('should return a Stream of BaseRemoteParticipantEvent', () { - expect(instance.remoteParticipantStream(0), isA>()); + expect(instance.remoteParticipantStream(0), + isA>()); }); BaseRemoteParticipantEvent? lastEvent; late StreamSubscription subscription; setUp(() { - subscription = instance.remoteParticipantStream(0).listen((data) => lastEvent = data); + subscription = instance + .remoteParticipantStream(0) + .listen((data) => lastEvent = data); }); tearDown(() async { await subscription.cancel(); }); - test('audioTrackDisabled event map should result in RemoteAudioTrackDisabled', () async { + test( + 'audioTrackDisabled event map should result in RemoteAudioTrackDisabled', + () async { remoteParticipantController.add({ 'name': 'audioTrackDisabled', 'data': { 'remoteParticipant': EventChannelMaps.remoteParticipantMap, - 'remoteAudioTrackPublication': EventChannelMaps.remoteAudioTrackPublicationMap, + 'remoteAudioTrackPublication': + EventChannelMaps.remoteAudioTrackPublicationMap, }, 'error': null }); expect(lastEvent, isA()); }); - test('audioTrackEnabled event map should result in RemoteAudioTrackEnabled', () async { + test('audioTrackEnabled event map should result in RemoteAudioTrackEnabled', + () async { remoteParticipantController.add({ 'name': 'audioTrackEnabled', 'data': { 'remoteParticipant': EventChannelMaps.remoteParticipantMap, - 'remoteAudioTrackPublication': EventChannelMaps.remoteAudioTrackPublicationMap, + 'remoteAudioTrackPublication': + EventChannelMaps.remoteAudioTrackPublicationMap, }, 'error': null }); expect(lastEvent, isA()); }); - test('audioTrackSubscribed event map should result in RemoteAudioTrackSubscribed', () async { + test( + 'audioTrackSubscribed event map should result in RemoteAudioTrackSubscribed', + () async { remoteParticipantController.add({ 'name': 'audioTrackSubscribed', 'data': { 'remoteParticipant': EventChannelMaps.remoteParticipantMap, - 'remoteAudioTrackPublication': EventChannelMaps.remoteAudioTrackPublicationMap, + 'remoteAudioTrackPublication': + EventChannelMaps.remoteAudioTrackPublicationMap, 'remoteAudioTrack': EventChannelMaps.remoteAudioTrackMap, }, 'error': null @@ -537,142 +572,236 @@ void main() { expect(lastEvent, isA()); }); - test('audioTrackSubscriptionFailed event map should result in RemoteAudioTrackSubscriptionFailed', () async { + test( + 'audioTrackSubscriptionFailed event map should result in RemoteAudioTrackSubscriptionFailed', + () async { remoteParticipantController.add({ 'name': 'audioTrackSubscriptionFailed', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteAudioTrackPublication': EventChannelMaps.remoteAudioTrackPublicationMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteAudioTrackPublication': + EventChannelMaps.remoteAudioTrackPublicationMap + }, 'error': EventChannelMaps.errorMap }); expect(lastEvent, isA()); }); - test('audioTrackUnpublished event map should result in RemoteAudioTrackUnpublished', () async { + test( + 'audioTrackUnpublished event map should result in RemoteAudioTrackUnpublished', + () async { remoteParticipantController.add({ 'name': 'audioTrackUnpublished', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteAudioTrackPublication': EventChannelMaps.remoteAudioTrackPublicationMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteAudioTrackPublication': + EventChannelMaps.remoteAudioTrackPublicationMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('audioTrackUnsubscribed event map should result in RemoteAudioTrackUnsubscribed', () async { + test( + 'audioTrackUnsubscribed event map should result in RemoteAudioTrackUnsubscribed', + () async { remoteParticipantController.add({ 'name': 'audioTrackUnsubscribed', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteAudioTrackPublication': EventChannelMaps.remoteAudioTrackPublicationMap, 'remoteAudioTrack': EventChannelMaps.remoteAudioTrackMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteAudioTrackPublication': + EventChannelMaps.remoteAudioTrackPublicationMap, + 'remoteAudioTrack': EventChannelMaps.remoteAudioTrackMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('dataTrackPublished event map should result in RemoteDataTrackPublished', () async { + test( + 'dataTrackPublished event map should result in RemoteDataTrackPublished', + () async { remoteParticipantController.add({ 'name': 'dataTrackPublished', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteDataTrackPublication': EventChannelMaps.remoteDataTrackPublicationMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteDataTrackPublication': + EventChannelMaps.remoteDataTrackPublicationMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('dataTrackSubscribed event map should result in RemoteDataTrackSubscribed', () async { + test( + 'dataTrackSubscribed event map should result in RemoteDataTrackSubscribed', + () async { remoteParticipantController.add({ 'name': 'dataTrackSubscribed', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteDataTrackPublication': EventChannelMaps.remoteDataTrackPublicationMap, 'remoteDataTrack': EventChannelMaps.remoteDataTrackMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteDataTrackPublication': + EventChannelMaps.remoteDataTrackPublicationMap, + 'remoteDataTrack': EventChannelMaps.remoteDataTrackMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('dataTrackSubscriptionFailed event map should result in RemoteDataTrackSubscriptionFailed', () async { + test( + 'dataTrackSubscriptionFailed event map should result in RemoteDataTrackSubscriptionFailed', + () async { remoteParticipantController.add({ 'name': 'dataTrackSubscriptionFailed', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteDataTrackPublication': EventChannelMaps.remoteDataTrackPublicationMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteDataTrackPublication': + EventChannelMaps.remoteDataTrackPublicationMap + }, 'error': EventChannelMaps.errorMap }); expect(lastEvent, isA()); }); - test('dataTrackUnpublished event map should result in RemoteDataTrackUnpublished', () async { + test( + 'dataTrackUnpublished event map should result in RemoteDataTrackUnpublished', + () async { remoteParticipantController.add({ 'name': 'dataTrackUnpublished', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteDataTrackPublication': EventChannelMaps.remoteDataTrackPublicationMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteDataTrackPublication': + EventChannelMaps.remoteDataTrackPublicationMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('dataTrackUnsubscribed event map should result in RemoteDataTrackUnsubscribed', () async { + test( + 'dataTrackUnsubscribed event map should result in RemoteDataTrackUnsubscribed', + () async { remoteParticipantController.add({ 'name': 'dataTrackUnsubscribed', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteDataTrackPublication': EventChannelMaps.remoteDataTrackPublicationMap, 'remoteDataTrack': EventChannelMaps.remoteDataTrackMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteDataTrackPublication': + EventChannelMaps.remoteDataTrackPublicationMap, + 'remoteDataTrack': EventChannelMaps.remoteDataTrackMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('videoTrackDisabled event map should result in RemoteVideoTrackDisabled', () async { + test( + 'videoTrackDisabled event map should result in RemoteVideoTrackDisabled', + () async { remoteParticipantController.add({ 'name': 'videoTrackDisabled', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteVideoTrackPublication': EventChannelMaps.remoteVideoTrackPublicationMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteVideoTrackPublication': + EventChannelMaps.remoteVideoTrackPublicationMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('videoTrackEnabled event map should result in RemoteVideoTrackEnabled', () async { + test('videoTrackEnabled event map should result in RemoteVideoTrackEnabled', + () async { remoteParticipantController.add({ 'name': 'videoTrackEnabled', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteVideoTrackPublication': EventChannelMaps.remoteVideoTrackPublicationMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteVideoTrackPublication': + EventChannelMaps.remoteVideoTrackPublicationMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('videoTrackPublished event map should result in RemoteVideoTrackPublished', () async { + test( + 'videoTrackPublished event map should result in RemoteVideoTrackPublished', + () async { remoteParticipantController.add({ 'name': 'videoTrackPublished', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteVideoTrackPublication': EventChannelMaps.remoteVideoTrackPublicationMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteVideoTrackPublication': + EventChannelMaps.remoteVideoTrackPublicationMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('videoTrackSubscribed event map should result in RemoteVideoTrackSubscribed', () async { + test( + 'videoTrackSubscribed event map should result in RemoteVideoTrackSubscribed', + () async { remoteParticipantController.add({ 'name': 'videoTrackSubscribed', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteVideoTrackPublication': EventChannelMaps.remoteVideoTrackPublicationMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteVideoTrackPublication': + EventChannelMaps.remoteVideoTrackPublicationMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('videoTrackSubscriptionFailed event map should result in RemoteVideoTrackSubscriptionFailed', () async { + test( + 'videoTrackSubscriptionFailed event map should result in RemoteVideoTrackSubscriptionFailed', + () async { remoteParticipantController.add({ 'name': 'videoTrackSubscriptionFailed', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteVideoTrackPublication': EventChannelMaps.remoteVideoTrackPublicationMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteVideoTrackPublication': + EventChannelMaps.remoteVideoTrackPublicationMap + }, 'error': EventChannelMaps.errorMap }); expect(lastEvent, isA()); }); - test('videoTrackUnpublished event map should result in RemoteVideoTrackUnpublished', () async { + test( + 'videoTrackUnpublished event map should result in RemoteVideoTrackUnpublished', + () async { remoteParticipantController.add({ 'name': 'videoTrackUnpublished', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteVideoTrackPublication': EventChannelMaps.remoteVideoTrackPublicationMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteVideoTrackPublication': + EventChannelMaps.remoteVideoTrackPublicationMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('videoTrackUnsubscribed event map should result in RemoteVideoTrackUnsubscribed', () async { + test( + 'videoTrackUnsubscribed event map should result in RemoteVideoTrackUnsubscribed', + () async { remoteParticipantController.add({ 'name': 'videoTrackUnsubscribed', - 'data': {'remoteParticipant': EventChannelMaps.remoteParticipantMap, 'remoteVideoTrackPublication': EventChannelMaps.remoteVideoTrackPublicationMap, 'remoteVideoTrack': EventChannelMaps.remoteVideoTrackMap}, + 'data': { + 'remoteParticipant': EventChannelMaps.remoteParticipantMap, + 'remoteVideoTrackPublication': + EventChannelMaps.remoteVideoTrackPublicationMap, + 'remoteVideoTrack': EventChannelMaps.remoteVideoTrackMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('invalid map should result in SkippableRemoteParticipantEvent', () async { + test('invalid map should result in SkippableRemoteParticipantEvent', + () async { remoteParticipantController.add({'data': {}}); expect(lastEvent, isA()); }); @@ -680,73 +809,109 @@ void main() { group('.localParticipantStream()', () { test('should return a Stream of LocalDataTrackPublished', () { - expect(instance.localParticipantStream(0), isA>()); + expect(instance.localParticipantStream(0), + isA>()); }); BaseLocalParticipantEvent? lastEvent; late StreamSubscription subscription; setUp(() { - subscription = instance.localParticipantStream(0).listen((data) => lastEvent = data); + subscription = + instance.localParticipantStream(0).listen((data) => lastEvent = data); }); tearDown(() async { await subscription.cancel(); }); - test('audioTrackPublished event map should result in LocalAudioTrackPublished', () async { + test( + 'audioTrackPublished event map should result in LocalAudioTrackPublished', + () async { localParticipantController.add({ 'name': 'audioTrackPublished', - 'data': {'localParticipant': EventChannelMaps.localParticipantMap, 'localAudioTrackPublication': EventChannelMaps.localAudioTrackPublicationMap}, + 'data': { + 'localParticipant': EventChannelMaps.localParticipantMap, + 'localAudioTrackPublication': + EventChannelMaps.localAudioTrackPublicationMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('audioTrackPublicationFailed event map should result in LocalAudioTrackPublicationFailed', () async { + test( + 'audioTrackPublicationFailed event map should result in LocalAudioTrackPublicationFailed', + () async { localParticipantController.add({ 'name': 'audioTrackPublicationFailed', - 'data': {'localParticipant': EventChannelMaps.localParticipantMap, 'localAudioTrack': EventChannelMaps.localAudioTrackMap}, + 'data': { + 'localParticipant': EventChannelMaps.localParticipantMap, + 'localAudioTrack': EventChannelMaps.localAudioTrackMap + }, 'error': EventChannelMaps.errorMap }); expect(lastEvent, isA()); }); - test('dataTrackPublished event map should result in LocalDataTrackPublished', () async { + test( + 'dataTrackPublished event map should result in LocalDataTrackPublished', + () async { localParticipantController.add({ 'name': 'dataTrackPublished', - 'data': {'localParticipant': EventChannelMaps.localParticipantMap, 'localDataTrackPublication': EventChannelMaps.localDataTrackPublicationMap}, + 'data': { + 'localParticipant': EventChannelMaps.localParticipantMap, + 'localDataTrackPublication': + EventChannelMaps.localDataTrackPublicationMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('dataTrackPublicationFailed event map should result in LocalDataTrackPublicationFailed', () async { + test( + 'dataTrackPublicationFailed event map should result in LocalDataTrackPublicationFailed', + () async { localParticipantController.add({ 'name': 'dataTrackPublicationFailed', - 'data': {'localParticipant': EventChannelMaps.localParticipantMap, 'localDataTrack': EventChannelMaps.localDataTrackMap}, + 'data': { + 'localParticipant': EventChannelMaps.localParticipantMap, + 'localDataTrack': EventChannelMaps.localDataTrackMap + }, 'error': EventChannelMaps.errorMap }); expect(lastEvent, isA()); }); - test('videoTrackPublished event map should result in LocalVideoTrackPublished', () async { + test( + 'videoTrackPublished event map should result in LocalVideoTrackPublished', + () async { localParticipantController.add({ 'name': 'videoTrackPublished', - 'data': {'localParticipant': EventChannelMaps.localParticipantMap, 'localVideoTrackPublication': EventChannelMaps.localVideoTrackPublicationMap}, + 'data': { + 'localParticipant': EventChannelMaps.localParticipantMap, + 'localVideoTrackPublication': + EventChannelMaps.localVideoTrackPublicationMap + }, 'error': null }); expect(lastEvent, isA()); }); - test('videoTrackPublicationFailed event map should result in LocalVideoTrackPublicationFailed', () async { + test( + 'videoTrackPublicationFailed event map should result in LocalVideoTrackPublicationFailed', + () async { localParticipantController.add({ 'name': 'videoTrackPublicationFailed', - 'data': {'localParticipant': EventChannelMaps.localParticipantMap, 'localVideoTrack': EventChannelMaps.localVideoTrackMap}, + 'data': { + 'localParticipant': EventChannelMaps.localParticipantMap, + 'localVideoTrack': EventChannelMaps.localVideoTrackMap + }, 'error': EventChannelMaps.errorMap }); expect(lastEvent, isA()); }); - test('invalid map should result in SkippableLocalParticipantEvent', () async { + test('invalid map should result in SkippableLocalParticipantEvent', + () async { localParticipantController.add({'data': {}}); expect(lastEvent, isA()); }); @@ -754,25 +919,29 @@ void main() { group('.remoteDataTrackStream()', () { test('should return a Stream of BaseRemoteDataTrackEvent', () { - expect(instance.remoteDataTrackStream(0), isA>()); + expect(instance.remoteDataTrackStream(0), + isA>()); }); BaseRemoteDataTrackEvent? lastEvent; late StreamSubscription subscription; setUp(() { - subscription = instance.remoteDataTrackStream(0).listen((data) => lastEvent = data); + subscription = + instance.remoteDataTrackStream(0).listen((data) => lastEvent = data); }); tearDown(() async { await subscription.cancel(); }); final remoteDataTrackMap = EventChannelMaps.remoteDataTrackMap; - test('invalid map should result in SkippableRemoteDataTrackEvent', () async { + test('invalid map should result in SkippableRemoteDataTrackEvent', + () async { remoteDataTrackController.add({'data': {}}); expect(lastEvent, isA()); }); - test('valid map with unknown event name should result in UnknownEvent', () async { + test('valid map with unknown event name should result in UnknownEvent', + () async { remoteDataTrackController.add({ 'name': 'unimplemented', 'data': {'remoteDataTrack': remoteDataTrackMap} @@ -815,7 +984,8 @@ void main() { class MockEventChannel extends Mock implements EventChannel { @override - Stream receiveBroadcastStream([dynamic arguments]) => super.noSuchMethod( + Stream receiveBroadcastStream([dynamic arguments]) => + super.noSuchMethod( Invocation.method(#receiveBroadcastStream, [arguments]), returnValue: StreamController().stream, ); diff --git a/programmable_video_platform_interface/test/models/connect_options_model_test.dart b/programmable_video_platform_interface/test/models/connect_options_model_test.dart index 219408c..bda1853 100644 --- a/programmable_video_platform_interface/test/models/connect_options_model_test.dart +++ b/programmable_video_platform_interface/test/models/connect_options_model_test.dart @@ -1,11 +1,6 @@ import 'package:collection/collection.dart'; import 'package:enum_to_string/enum_to_string.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:twilio_programmable_video_platform_interface/src/audio_codecs/audio_codec.dart'; -import 'package:twilio_programmable_video_platform_interface/src/camera_source.dart'; -import 'package:twilio_programmable_video_platform_interface/src/enums/region.dart'; -import 'package:twilio_programmable_video_platform_interface/src/models/model_exports.dart'; -import 'package:twilio_programmable_video_platform_interface/src/video_codecs/video_codec.dart'; import 'package:twilio_programmable_video_platform_interface/twilio_programmable_video_platform_interface.dart'; void main() { @@ -30,7 +25,8 @@ void main() { LocalVideoTrackModel( name: 'video', enabled: true, - cameraCapturer: CameraCapturerModel(CameraSource('FRONT_CAMERA', false, false, false), 'type'), + cameraCapturer: CameraCapturerModel( + CameraSource('FRONT_CAMERA', false, false, false), 'type'), ), ]; final enableAutomaticSubscription = true; @@ -69,11 +65,18 @@ void main() { 'accessToken': accessToken, 'roomName': roomName, 'region': EnumToString.convertToString(region), - 'preferredAudioCodecs': Map.fromIterable(preferredAudioCodecs.map((AudioCodec a) => a.name)), - 'preferredVideoCodecs': Map.fromIterable(preferredVideoCodecs.map((VideoCodec v) => v.name)), - 'audioTracks': Map.fromIterable(audioTracks.map>((TrackModel a) => a.toMap())), - 'dataTracks': Map.fromIterable(dataTracks.map>((LocalDataTrackModel d) => d.toMap())), - 'videoTracks': Map.fromIterable(videoTracks.map>((LocalVideoTrackModel v) => v.toMap())), + 'preferredAudioCodecs': Map.fromIterable( + preferredAudioCodecs.map((AudioCodec a) => a.name)), + 'preferredVideoCodecs': Map.fromIterable( + preferredVideoCodecs.map((VideoCodec v) => v.name)), + 'audioTracks': Map.fromIterable(audioTracks + .map>((TrackModel a) => a.toMap())), + 'dataTracks': Map.fromIterable( + dataTracks.map>( + (LocalDataTrackModel d) => d.toMap())), + 'videoTracks': Map.fromIterable( + videoTracks.map>( + (LocalVideoTrackModel v) => v.toMap())), 'enableDominantSpeaker': enableDominantSpeaker, 'enableAutomaticSubscription': enableAutomaticSubscription, 'enableNetworkQuality': enableNetworkQuality, diff --git a/programmable_video_platform_interface/test/models/room/room_model_test.dart b/programmable_video_platform_interface/test/models/room/room_model_test.dart index 695801d..9076f4a 100644 --- a/programmable_video_platform_interface/test/models/room/room_model_test.dart +++ b/programmable_video_platform_interface/test/models/room/room_model_test.dart @@ -1,6 +1,5 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:twilio_programmable_video_platform_interface/src/enums/enum_exports.dart'; -import 'package:twilio_programmable_video_platform_interface/src/enums/region.dart'; import 'package:twilio_programmable_video_platform_interface/src/models/model_exports.dart'; import '../model_instances.dart'; From 4865ca035fd477d81a76fd564379e4e85643270c Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Mon, 7 Nov 2022 10:12:00 +0545 Subject: [PATCH 02/30] fix : typo --- .../flutter/twilio_programmable_video/ParticipantViewFactory.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/ParticipantViewFactory.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/ParticipantViewFactory.kt index f5c6aa0..b64a2a0 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/ParticipantViewFactory.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/ParticipantViewFactory.kt @@ -10,7 +10,7 @@ import io.flutter.plugin.platform.PlatformViewFactory class ParticipantViewFactory(createArgsCodec: MessageCodec, private val plugin: PluginHandler) : PlatformViewFactory(createArgsCodec) { private val TAG = "RoomListener" - override fun create(p0: Context?, p1: Int, p2: Any? PlatformView? { + override fun create(context: Context?, viewId: Int, args: Any?): PlatformView { var videoTrack: VideoTrack? = null if (args != null) { From 1fac68909bc1e22c9f044958102386844ed50008 Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Mon, 7 Nov 2022 12:02:19 +0545 Subject: [PATCH 03/30] chore : update code --- programmable_video/android/build.gradle | 6 +- .../ParticipantViewFactory.kt | 44 ++--- .../PluginHandler.kt | 161 ++++++++++++++---- .../TwilioProgrammableVideoPlugin.kt | 3 + ...ethod_channel_programmable_video_test.dart | 1 + 5 files changed, 158 insertions(+), 57 deletions(-) diff --git a/programmable_video/android/build.gradle b/programmable_video/android/build.gradle index f795e95..6bb37c0 100644 --- a/programmable_video/android/build.gradle +++ b/programmable_video/android/build.gradle @@ -2,7 +2,7 @@ group 'twilio.flutter.twilio_programmable_video' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.4.0' + ext.kotlin_version = '1.6.10' ext.twilio_video_version = '6.4.+' repositories { google() @@ -10,7 +10,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.6.1' + classpath 'com.android.tools.build:gradle:7.0.4' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } @@ -27,7 +27,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' android { - compileSdkVersion 28 + compileSdkVersion 31 sourceSets { main.java.srcDirs += 'src/main/kotlin' diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/ParticipantViewFactory.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/ParticipantViewFactory.kt index b64a2a0..3a42377 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/ParticipantViewFactory.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/ParticipantViewFactory.kt @@ -12,34 +12,36 @@ class ParticipantViewFactory(createArgsCodec: MessageCodec, private val plu override fun create(context: Context?, viewId: Int, args: Any?): PlatformView { var videoTrack: VideoTrack? = null + val params = args as? Map<*, *> ?: throw IllegalStateException("args cannot be null") - if (args != null) { - val params = args as Map - if (params.containsKey("isLocal")) { - debug("create => constructing local view") - val localParticipant = plugin.getLocalParticipant() - if (localParticipant != null && localParticipant.localVideoTracks != null && localParticipant.localVideoTracks?.size != 0) { - videoTrack = localParticipant.localVideoTracks!![0].localVideoTrack - } + if (params["isLocal"] == true) { + debug("create => constructing local view with params: '${params.values.joinToString(", ")}'") + val localVideoTrackName = params["name"] as? String ?: "" + if (localVideoTrackName != "" && localVideoTrackName in TwilioProgrammableVideoPlugin.localVideoTracks) { + videoTrack = TwilioProgrammableVideoPlugin.localVideoTracks[localVideoTrackName] } else { - debug("create => constructing view with params: '${params.values.joinToString(", ")}'") - if (params.containsKey("remoteParticipantSid") && params.containsKey("remoteVideoTrackSid")) { - val remoteParticipant = plugin.getRemoteParticipant(params["remoteParticipantSid"] as String) - val remoteVideoTrack = remoteParticipant?.remoteVideoTracks?.find { it.trackSid == params["remoteVideoTrackSid"] } - if (remoteParticipant != null && remoteVideoTrack != null) { - videoTrack = remoteVideoTrack.remoteVideoTrack - } + val localParticipant = plugin.getLocalParticipant() + if (localParticipant?.localVideoTracks?.isNotEmpty() == true) { + videoTrack = localParticipant.localVideoTracks.firstOrNull()?.localVideoTrack } } - - if (videoTrack != null) { - val videoView = VideoView(context) - videoView.mirror = params["mirror"] as Boolean - return ParticipantView(videoView, videoTrack) + } else { + debug("create => constructing view with params: '${params.values.joinToString(", ")}'") + if ("remoteParticipantSid" in params && "remoteVideoTrackSid" in params) { + val remoteParticipant = plugin.getRemoteParticipant(params["remoteParticipantSid"] as String) + val remoteVideoTrack = remoteParticipant?.remoteVideoTracks?.find { it.trackSid == params["remoteVideoTrackSid"] } + if (remoteParticipant != null && remoteVideoTrack != null) { + videoTrack = remoteVideoTrack.remoteVideoTrack + } } } - return null + if (videoTrack == null) { + throw IllegalStateException("Could not create VideoTrack") + } + val videoView = VideoView(context as Context) + videoView.mirror = params["mirror"] as Boolean + return ParticipantView(videoView, videoTrack) } internal fun debug(msg: String) { diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index 6c05094..76d2c79 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -40,6 +40,7 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import java.nio.ByteBuffer +import java.util.ArrayList import tvi.webrtc.voiceengine.WebRtcAudioUtils class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { @@ -115,7 +116,11 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { "LocalAudioTrack#enable" -> localAudioTrackEnable(call, result) "LocalDataTrack#sendString" -> localDataTrackSendString(call, result) "LocalDataTrack#sendByteBuffer" -> localDataTrackSendByteBuffer(call, result) + "LocalVideoTrack#create" -> localVideoTrackCreate(call, result) "LocalVideoTrack#enable" -> localVideoTrackEnable(call, result) + "LocalVideoTrack#publish" -> localVideoTrackPublish(call, result) + "LocalVideoTrack#unpublish" -> localVideoTrackUnpublish(call, result) + "LocalVideoTrack#release" -> localVideoTrackRelease(call, result) "RemoteAudioTrack#enablePlayback" -> remoteAudioTrackEnable(call, result) "RemoteAudioTrack#isPlaybackEnabled" -> isRemoteAudioTrackPlaybackEnabled(call, result) "CameraCapturer#switchCamera" -> switchCamera(call, result) @@ -150,11 +155,43 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { VideoCapturerHandler.setTorch(call, result) } + private fun localVideoTrackCreate(call: MethodCall, result: MethodChannel.Result) { + val name = call.argument("name") + ?: return result.error("MISSING_PARAMS", missingParameterMessage("name"), null) + val enabled = call.argument("enable") + ?: return result.error("MISSING_PARAMS", missingParameterMessage("enabled"), null) + val videoCapturer = call.argument>("videoCapturer") + ?: return result.error("MISSING_PARAMS", missingParameterMessage("videoCapturer"), null) + + debug("localVideoTrackCreate => called for $name, enable=$enabled, videoCapturer=$videoCapturer") + + if (TwilioProgrammableVideoPlugin.cameraCapturer == null) { + VideoCapturerHandler.initializeCapturer(videoCapturer, result) + } + + if (TwilioProgrammableVideoPlugin.localVideoTracks[name] == null) { + val localVideoTrack = LocalVideoTrack.create( + this.applicationContext, + enabled, + TwilioProgrammableVideoPlugin.cameraCapturer!!, + name + ) ?: return result.error( + "INIT_ERROR", + "Unable to create local video track with name $name", + null + ) + + TwilioProgrammableVideoPlugin.localVideoTracks[name]?.release() + TwilioProgrammableVideoPlugin.localVideoTracks[name] = localVideoTrack + } + result.success(null) + } + private fun localVideoTrackEnable(call: MethodCall, result: MethodChannel.Result) { val localVideoTrackName = call.argument("name") - ?: return result.error("MISSING_PARAMS", missingParameterMessage("name"), null) + ?: return result.error("MISSING_PARAMS", missingParameterMessage("name"), null) val localVideoTrackEnable = call.argument("enable") - ?: return result.error("MISSING_PARAMS", missingParameterMessage("enable"), null) + ?: return result.error("MISSING_PARAMS", missingParameterMessage("enable"), null) debug("localVideoTrackEnable => called for $localVideoTrackName, enable=$localVideoTrackEnable") @@ -166,11 +203,62 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { return result.error("NOT_FOUND", "No LocalVideoTrack found with the name '$localVideoTrackName'", null) } + private fun localVideoTrackPublish(call: MethodCall, result: MethodChannel.Result) { + val localVideoTrackName = call.argument("name") + ?: return result.error("MISSING_PARAMS", missingParameterMessage("name"), null) + + debug("localVideoTrackPublish => called for $localVideoTrackName") + + val localVideoTrack = TwilioProgrammableVideoPlugin.localVideoTracks[localVideoTrackName] + ?: return result.error("NOT_FOUND", "No LocalVideoTrack found with the name '$localVideoTrackName'", null) + + getLocalParticipant()?.publishTrack(localVideoTrack) + + TwilioProgrammableVideoPlugin.localVideoTracks -= localVideoTrackName + + return result.success(null) + } + + private fun localVideoTrackUnpublish(call: MethodCall, result: MethodChannel.Result) { + val localVideoTrackName = call.argument("name") + ?: return result.error("MISSING_PARAMS", missingParameterMessage("name"), null) + + debug("localVideoTrackUnpublish => called for $localVideoTrackName") + + val localVideoTrack = getLocalParticipant() + ?.localVideoTracks + ?.firstOrNull { it.trackName == localVideoTrackName } + ?.localVideoTrack + ?: return result.error("NOT_FOUND", "No LocalVideoTrack found with the name '$localVideoTrackName'", null) + + getLocalParticipant()?.unpublishTrack(localVideoTrack) + + TwilioProgrammableVideoPlugin.localVideoTracks[localVideoTrackName] = localVideoTrack + + return result.success(null) + } + + private fun localVideoTrackRelease(call: MethodCall, result: MethodChannel.Result) { + val localVideoTrackName = call.argument("name") + ?: return result.error("MISSING_PARAMS", missingParameterMessage("name"), null) + + debug("localVideoTrackRelease => called for $localVideoTrackName") + + val preview = TwilioProgrammableVideoPlugin.localVideoTracks[localVideoTrackName] + if (preview != null) { + preview.release() + TwilioProgrammableVideoPlugin.localVideoTracks -= localVideoTrackName + return result.success(null) + } + + return result.error("NOT_FOUND", "No LocalVideoTrack found with the name '$localVideoTrackName'", null) + } + private fun localAudioTrackEnable(call: MethodCall, result: MethodChannel.Result) { val localAudioTrackName = call.argument("name") - ?: return result.error("MISSING_PARAMS", missingParameterMessage("name"), null) + ?: return result.error("MISSING_PARAMS", missingParameterMessage("name"), null) val localAudioTrackEnable = call.argument("enable") - ?: return result.error("MISSING_PARAMS", missingParameterMessage("enable"), null) + ?: return result.error("MISSING_PARAMS", missingParameterMessage("enable"), null) debug("localAudioTrackEnable => called for $localAudioTrackName, enable=$localAudioTrackEnable") @@ -184,14 +272,14 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { private fun localDataTrackSendString(call: MethodCall, result: MethodChannel.Result) { val localDataTrackName = call.argument("name") - ?: return result.error("MISSING_PARAMS", missingParameterMessage("name"), null) + ?: return result.error("MISSING_PARAMS", missingParameterMessage("name"), null) val localDataTrackMessage = call.argument("message") - ?: return result.error("MISSING_PARAMS", missingParameterMessage("message"), null) + ?: return result.error("MISSING_PARAMS", missingParameterMessage("message"), null) debug("localDataTrackSendString => called for $localDataTrackName") val localDataTrack = TwilioProgrammableVideoPlugin.roomListener.room?.localParticipant?.localDataTracks?.firstOrNull { it.trackName == localDataTrackName } - ?: return result.error("NOT_FOUND", "No LocalDataTrack found with the name '$localDataTrackName'", null) + ?: return result.error("NOT_FOUND", "No LocalDataTrack found with the name '$localDataTrackName'", null) localDataTrack.localDataTrack.send(localDataTrackMessage) return result.success(null) @@ -199,14 +287,14 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { private fun localDataTrackSendByteBuffer(call: MethodCall, result: MethodChannel.Result) { val localDataTrackName = call.argument("name") - ?: return result.error("MISSING_PARAMS", missingParameterMessage("name"), null) + ?: return result.error("MISSING_PARAMS", missingParameterMessage("name"), null) val localDataTrackMessage = call.argument("message") - ?: return result.error("MISSING_PARAMS", missingParameterMessage("message"), null) + ?: return result.error("MISSING_PARAMS", missingParameterMessage("message"), null) debug("localDataTrackSendByteBuffer => called for $localDataTrackName") val localDataTrack = TwilioProgrammableVideoPlugin.roomListener.room?.localParticipant?.localDataTracks?.firstOrNull { it.trackName == localDataTrackName } - ?: return result.error("NOT_FOUND", "No LocalDataTrack found with the name '$localDataTrackName'", null) + ?: return result.error("NOT_FOUND", "No LocalDataTrack found with the name '$localDataTrackName'", null) localDataTrack.localDataTrack.send(ByteBuffer.wrap(localDataTrackMessage)) return result.success(null) @@ -214,12 +302,12 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { private fun remoteAudioTrackEnable(call: MethodCall, result: MethodChannel.Result) { val remoteAudioTrackSid = call.argument("sid") - ?: return result.error("MISSING_PARAMS", missingParameterMessage("sid"), null) + ?: return result.error("MISSING_PARAMS", missingParameterMessage("sid"), null) val enable = call.argument("enable") - ?: return result.error("MISSING_PARAMS", missingParameterMessage("enable"), null) + ?: return result.error("MISSING_PARAMS", missingParameterMessage("enable"), null) debug("remoteAudioTrackEnable => sid: $remoteAudioTrackSid enable: $enable") val remoteAudioTrack = getRemoteAudioTrack(remoteAudioTrackSid) - ?: return result.error("NOT_FOUND", "No RemoteAudioTrack found with sid $remoteAudioTrackSid", null) + ?: return result.error("NOT_FOUND", "No RemoteAudioTrack found with sid $remoteAudioTrackSid", null) remoteAudioTrack.remoteAudioTrack?.enablePlayback(enable) return result.success(null) @@ -227,17 +315,17 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { private fun isRemoteAudioTrackPlaybackEnabled(call: MethodCall, result: MethodChannel.Result) { val remoteAudioTrackSid = call.argument("sid") - ?: return result.error("MISSING_PARAMS", missingParameterMessage("sid"), null) + ?: return result.error("MISSING_PARAMS", missingParameterMessage("sid"), null) debug("isRemoteAudioTrackPlaybackEnabled => sid: $remoteAudioTrackSid") val remoteAudioTrack = getRemoteAudioTrack(remoteAudioTrackSid) - ?: return result.error("NOT_FOUND", "No RemoteAudioTrack found with sid $remoteAudioTrackSid", null) + ?: return result.error("NOT_FOUND", "No RemoteAudioTrack found with sid $remoteAudioTrackSid", null) return result.success(remoteAudioTrack.remoteAudioTrack?.isPlaybackEnabled) } private fun getRemoteAudioTrack(sid: String): RemoteAudioTrackPublication? { val remoteParticipants = TwilioProgrammableVideoPlugin.roomListener.room?.remoteParticipants - ?: return null + ?: return null var remoteAudioTrack: RemoteAudioTrackPublication? for (remoteParticipant in remoteParticipants) { @@ -265,10 +353,10 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { private fun getAudioSettings(call: MethodCall, result: MethodChannel.Result) { val audioSettingsMap = - mapOf( - "speakerphoneEnabled" to audioSettings.speakerEnabled, - "bluetoothPreferred" to audioSettings.bluetoothPreferred - ) + mapOf( + "speakerphoneEnabled" to audioSettings.speakerEnabled, + "bluetoothPreferred" to audioSettings.bluetoothPreferred + ) result.success(audioSettingsMap) } @@ -296,10 +384,10 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { val isConnected = TwilioProgrammableVideoPlugin.isConnected() val anyPlaying = TwilioProgrammableVideoPlugin.audioNotificationListener.anyAudioPlayersActive() debug("applyBluetoothSettings BEGIN =>\n" + - "\ton: ${audioSettings.bluetoothPreferred}\n" + - "\tscoOn: ${audioManager.isBluetoothScoOn}\n" + - "\tconnected: $isConnected\n" + - "\tanyPlaying: $anyPlaying") + "\ton: ${audioSettings.bluetoothPreferred}\n" + + "\tscoOn: ${audioManager.isBluetoothScoOn}\n" + + "\tconnected: $isConnected\n" + + "\tanyPlaying: $anyPlaying") if (isConnected || anyPlaying) { Handler(Looper.getMainLooper()).postDelayed({ setBluetoothSco(audioSettings.bluetoothPreferred) @@ -323,7 +411,7 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { private fun setSpeakerphoneOn(call: MethodCall, result: MethodChannel.Result) { val on = call.argument("on") - ?: return result.error("MISSING_PARAMS", missingParameterMessage("on"), null) + ?: return result.error("MISSING_PARAMS", missingParameterMessage("on"), null) audioSettings.speakerEnabled = on setSpeakerPhoneOnInternal() @@ -335,7 +423,7 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { } private fun setSpeakerPhoneOnInternal() { - val bluetoothProfileConnectionState = BluetoothAdapter.getDefaultAdapter()?.getProfileConnectionState(BluetoothProfile.HEADSET) + val bluetoothProfileConnectionState = BluetoothAdapter.getDefaultAdapter().getProfileConnectionState(BluetoothProfile.HEADSET) debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") // Even if already enabled, setting `audioManager.isSpeakerphoneOn` to true @@ -404,7 +492,7 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true) } val optionsObj = call.argument>("connectOptions") - ?: return result.error("MISSING_PARAMS", "Missing 'connectOptions' parameter", null) + ?: return result.error("MISSING_PARAMS", "Missing 'connectOptions' parameter", null) val obtainedFocus = setAudioFocus(true) if (!obtainedFocus) { @@ -526,6 +614,7 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { for ((videoTrack) in videoTrackOptions) { videoTrack as Map<*, *> // Ensure right type. val videoCapturerMap = videoTrack["videoCapturer"] as Map<*, *> + val name = videoTrack["name"] as? String ?: "" if ((videoCapturerMap["type"] as String) == "CameraCapturer") { VideoCapturerHandler.initializeCapturer(videoCapturerMap, result) @@ -534,10 +623,16 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { } if (TwilioProgrammableVideoPlugin.cameraCapturer != null) { - videoTracks.add(LocalVideoTrack.create(this.applicationContext, videoTrack["enable"] as Boolean, TwilioProgrammableVideoPlugin.cameraCapturer!!, videoTrack["name"] as String)) + if (name in TwilioProgrammableVideoPlugin.localVideoTracks) { + videoTracks.add(TwilioProgrammableVideoPlugin.localVideoTracks[name]) + TwilioProgrammableVideoPlugin.localVideoTracks -= name + } else { + videoTracks.add(LocalVideoTrack.create(this.applicationContext, videoTrack["enable"] as Boolean, TwilioProgrammableVideoPlugin.cameraCapturer!!, videoTrack["name"] as String)) + } } } debug("connect => setting videoTracks to '${videoTracks.joinToString(", ")}'") + optionsBuilder.videoTracks(videoTracks) } @@ -564,10 +659,10 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { private fun debug(call: MethodCall, result: MethodChannel.Result) { val enableNative = call.argument("native") - ?: return result.error("MISSING_PARAMS", "Missing 'native' parameter", null) + ?: return result.error("MISSING_PARAMS", "Missing 'native' parameter", null) val enableAudio = call.argument("audio") - ?: return result.error("MISSING_PARAMS", "Missing 'audio' parameter", null) + ?: return result.error("MISSING_PARAMS", "Missing 'audio' parameter", null) TwilioProgrammableVideoPlugin.nativeDebug = enableNative TwilioProgrammableVideoPlugin.audioDebug = enableAudio @@ -589,9 +684,9 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { // Request audio focus if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val playbackAttributes = AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) - .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) - .build() + .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) + .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH) + .build() audioFocusRequest = AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN_TRANSIENT) .setAudioAttributes(playbackAttributes) .setAcceptsDelayedFocusGain(false) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/TwilioProgrammableVideoPlugin.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/TwilioProgrammableVideoPlugin.kt index 4495334..0282211 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/TwilioProgrammableVideoPlugin.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/TwilioProgrammableVideoPlugin.kt @@ -5,6 +5,7 @@ import android.os.Handler import android.os.Looper import android.util.Log import androidx.annotation.NonNull +import com.twilio.video.LocalVideoTrack import com.twilio.video.Video import com.twilio.video.VideoCapturer import io.flutter.embedding.engine.plugins.FlutterPlugin @@ -73,6 +74,8 @@ class TwilioProgrammableVideoPlugin : FlutterPlugin { "MI 5" ) + val localVideoTracks = mutableMapOf() + lateinit var pluginHandler: PluginHandler lateinit var cameraEnumerator: CameraEnumerator diff --git a/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart b/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart index c3de8d5..872975e 100644 --- a/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart +++ b/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; From e1201f9c169777b3a679afda71d055d67019500f Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Mon, 7 Nov 2022 16:27:42 +0545 Subject: [PATCH 04/30] Test : crashing of app in android 12 --- .../AudioNotificationListener.kt | 14 +++++++------- .../twilio_programmable_video/PluginHandler.kt | 8 +++++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt index b4998a0..6b03bad 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt @@ -26,13 +26,13 @@ class AudioNotificationListener() : BaseListener() { override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) { debug("onServiceConnected => profile: $profile, proxy: $proxy") -// if (profile == BluetoothProfile.HEADSET) { -// bluetoothProfile = proxy -// if (bluetoothProfile!!.connectedDevices.size > 0 && -// TwilioProgrammableVideoPlugin.pluginHandler.audioSettings.bluetoothPreferred) { -// TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() -// } -// } + if (profile == BluetoothProfile.HEADSET) { + bluetoothProfile = proxy + if (bluetoothProfile!!.connectedDevices.size > 0 && + TwilioProgrammableVideoPlugin.pluginHandler.audioSettings.bluetoothPreferred) { + TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() + } + } } } diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index 76d2c79..ceaadc6 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -422,8 +422,10 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { return result.success(audioSettings.speakerEnabled) } - private fun setSpeakerPhoneOnInternal() { - val bluetoothProfileConnectionState = BluetoothAdapter.getDefaultAdapter().getProfileConnectionState(BluetoothProfile.HEADSET) + private fun setSpeakerPhoneOnInternal() { + val bluetoothProfileConnectionState = BluetoothAdapter.getDefaultAdapter()?.getProfileConnectionState(BluetoothProfile.HEADSET) + ?: BluetoothProfile.STATE_DISCONNECTED + debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") // Even if already enabled, setting `audioManager.isSpeakerphoneOn` to true @@ -437,7 +439,7 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { if (!audioSettings.bluetoothPreferred || bluetoothProfileConnectionState != BluetoothProfile.STATE_CONNECTED) { applySpeakerPhoneSettings() - } + } } internal fun applySpeakerPhoneSettings() { From 4eea4960c5640fa9a84263fcda5512b36c35a056 Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Mon, 7 Nov 2022 17:59:20 +0545 Subject: [PATCH 05/30] chore : update permission package test --- programmable_video/pubspec.yaml | 2 +- .../test/method_channel_programmable_video_test.dart | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/programmable_video/pubspec.yaml b/programmable_video/pubspec.yaml index 50fb4ed..d57cdf2 100644 --- a/programmable_video/pubspec.yaml +++ b/programmable_video/pubspec.yaml @@ -13,7 +13,7 @@ environment: dependencies: flutter: sdk: flutter - permission_handler: ^8.3.0 + permission_handler: ^10.2.0 enum_to_string: ^2.0.1 twilio_programmable_video_platform_interface: git: diff --git a/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart b/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart index 872975e..c3de8d5 100644 --- a/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart +++ b/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; From d498cbae5855a60abb13d023003c1fb478361a79 Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Tue, 8 Nov 2022 11:22:34 +0545 Subject: [PATCH 06/30] chore : crashing of android test --- .../twilio/flutter/twilio_programmable_video/PluginHandler.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index ceaadc6..78ca6df 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -423,8 +423,7 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { } private fun setSpeakerPhoneOnInternal() { - val bluetoothProfileConnectionState = BluetoothAdapter.getDefaultAdapter()?.getProfileConnectionState(BluetoothProfile.HEADSET) - ?: BluetoothProfile.STATE_DISCONNECTED + val bluetoothProfileConnectionState = BluetoothAdapter.getDefaultAdapter().getProfileConnectionState(BluetoothProfile.HEADSET) debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") From 1c473d37ca20419a038cd7c01d42cf6e39645087 Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Tue, 8 Nov 2022 11:45:51 +0545 Subject: [PATCH 07/30] Revert "chore : crashing of android test" This reverts commit d498cbae5855a60abb13d023003c1fb478361a79. --- .../twilio/flutter/twilio_programmable_video/PluginHandler.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index 78ca6df..ceaadc6 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -423,7 +423,8 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { } private fun setSpeakerPhoneOnInternal() { - val bluetoothProfileConnectionState = BluetoothAdapter.getDefaultAdapter().getProfileConnectionState(BluetoothProfile.HEADSET) + val bluetoothProfileConnectionState = BluetoothAdapter.getDefaultAdapter()?.getProfileConnectionState(BluetoothProfile.HEADSET) + ?: BluetoothProfile.STATE_DISCONNECTED debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") From 344a610d26f771684904d49130e88352f9a3ce07 Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Fri, 24 Mar 2023 17:47:32 +0545 Subject: [PATCH 08/30] fix : bluetooth crash in flutter 3 --- .../AudioNotificationListener.kt | 40 +++++++++---------- .../PluginHandler.kt | 3 +- 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt index 6b03bad..660efff 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt @@ -15,26 +15,26 @@ class AudioNotificationListener() : BaseListener() { private val intentFilter: IntentFilter = IntentFilter() private val activeAudioPlayers: MutableSet = mutableSetOf() - private var bluetoothProfileProxy: BluetoothProfile.ServiceListener = object : BluetoothProfile.ServiceListener { - override fun onServiceDisconnected(profile: Int) { - debug("onServiceDisconnected => profile: $profile") - if (profile == BluetoothProfile.HEADSET) { - bluetoothProfile = null - TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() - } - } - - override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) { - debug("onServiceConnected => profile: $profile, proxy: $proxy") - if (profile == BluetoothProfile.HEADSET) { - bluetoothProfile = proxy - if (bluetoothProfile!!.connectedDevices.size > 0 && - TwilioProgrammableVideoPlugin.pluginHandler.audioSettings.bluetoothPreferred) { - TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() - } - } - } - } + // private var bluetoothProfileProxy: BluetoothProfile.ServiceListener = object : BluetoothProfile.ServiceListener { + // override fun onServiceDisconnected(profile: Int) { + // debug("onServiceDisconnected => profile: $profile") + // if (profile == BluetoothProfile.HEADSET) { + // bluetoothProfile = null + // TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() + // } + // } + + // override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) { + // debug("onServiceConnected => profile: $profile, proxy: $proxy") + // if (profile == BluetoothProfile.HEADSET) { + // bluetoothProfile = proxy + // if (bluetoothProfile!!.connectedDevices.size > 0 && + // TwilioProgrammableVideoPlugin.pluginHandler.audioSettings.bluetoothPreferred) { + // TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() + // } + // } + // } + // } var bluetoothProfile: BluetoothProfile? = null diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index ceaadc6..c720ef0 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -423,8 +423,7 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { } private fun setSpeakerPhoneOnInternal() { - val bluetoothProfileConnectionState = BluetoothAdapter.getDefaultAdapter()?.getProfileConnectionState(BluetoothProfile.HEADSET) - ?: BluetoothProfile.STATE_DISCONNECTED + val bluetoothProfileConnectionState = BluetoothAdapter.getDefaultAdapter().getProfileConnectionState(BluetoothProfile.HEADSET) debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") From 8f72cff54e08ebafc7eb7a9bbdb5041c9e945df9 Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Mon, 27 Mar 2023 10:27:09 +0545 Subject: [PATCH 09/30] chore : uncomment crashing code --- .../AudioNotificationListener.kt | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt index 660efff..6b03bad 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt @@ -15,26 +15,26 @@ class AudioNotificationListener() : BaseListener() { private val intentFilter: IntentFilter = IntentFilter() private val activeAudioPlayers: MutableSet = mutableSetOf() - // private var bluetoothProfileProxy: BluetoothProfile.ServiceListener = object : BluetoothProfile.ServiceListener { - // override fun onServiceDisconnected(profile: Int) { - // debug("onServiceDisconnected => profile: $profile") - // if (profile == BluetoothProfile.HEADSET) { - // bluetoothProfile = null - // TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() - // } - // } - - // override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) { - // debug("onServiceConnected => profile: $profile, proxy: $proxy") - // if (profile == BluetoothProfile.HEADSET) { - // bluetoothProfile = proxy - // if (bluetoothProfile!!.connectedDevices.size > 0 && - // TwilioProgrammableVideoPlugin.pluginHandler.audioSettings.bluetoothPreferred) { - // TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() - // } - // } - // } - // } + private var bluetoothProfileProxy: BluetoothProfile.ServiceListener = object : BluetoothProfile.ServiceListener { + override fun onServiceDisconnected(profile: Int) { + debug("onServiceDisconnected => profile: $profile") + if (profile == BluetoothProfile.HEADSET) { + bluetoothProfile = null + TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() + } + } + + override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) { + debug("onServiceConnected => profile: $profile, proxy: $proxy") + if (profile == BluetoothProfile.HEADSET) { + bluetoothProfile = proxy + if (bluetoothProfile!!.connectedDevices.size > 0 && + TwilioProgrammableVideoPlugin.pluginHandler.audioSettings.bluetoothPreferred) { + TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() + } + } + } + } var bluetoothProfile: BluetoothProfile? = null From 8e02ff7b61b8230c8609439111076de906d59d5e Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Mon, 27 Mar 2023 11:18:55 +0545 Subject: [PATCH 10/30] test : asking bluetooth permission --- .../lib/src/programmable_video.dart | 35 +++++++++++++------ 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/programmable_video/lib/src/programmable_video.dart b/programmable_video/lib/src/programmable_video.dart index 722baad..b42ae2e 100644 --- a/programmable_video/lib/src/programmable_video.dart +++ b/programmable_video/lib/src/programmable_video.dart @@ -5,15 +5,19 @@ class TwilioProgrammableVideo { // ignore: cancel_subscriptions static StreamSubscription? _loggingStream; - static final StreamController _onAudioNotification = StreamController.broadcast(onListen: () { - _audioNotificationStream = ProgrammableVideoPlatform.instance.audioNotificationStream().listen(_parseAudioNotificationEvents); + static final StreamController _onAudioNotification = + StreamController.broadcast(onListen: () { + _audioNotificationStream = ProgrammableVideoPlatform.instance + .audioNotificationStream() + .listen(_parseAudioNotificationEvents); }, onCancel: () { _audioNotificationStream?.cancel(); }); static Stream onAudioNotification = _onAudioNotification.stream; - static StreamSubscription? _audioNotificationStream; + static StreamSubscription? + _audioNotificationStream; static var _dartDebug = false; @@ -78,11 +82,14 @@ class TwilioProgrammableVideo { /// Enable debug logging. /// /// For native logging set [native] to `true` and for dart set [dart] to `true`. - static Future debug({bool dart = false, bool native = false, bool audio = false}) async { + static Future debug( + {bool dart = false, bool native = false, bool audio = false}) async { _dartDebug = dart; await ProgrammableVideoPlatform.instance.setNativeDebug(native, audio); if ((native || audio) && _loggingStream == null) { - _loggingStream = ProgrammableVideoPlatform.instance.loggingStream().listen((dynamic event) { + _loggingStream = ProgrammableVideoPlatform.instance + .loggingStream() + .listen((dynamic event) { if (native || audio) { print('[ NATIVE ] $event'); } @@ -162,20 +169,27 @@ class TwilioProgrammableVideo { /// /// Uses the PermissionHandler plugin. Returns the granted result. static Future requestPermissionForCameraAndMicrophone() async { - await [Permission.camera, Permission.microphone].request(); + await [ + Permission.camera, + Permission.microphone, + Permission.bluetoothConnect + ].request(); final micPermission = await Permission.microphone.status; final camPermission = await Permission.camera.status; _log('Permissions => Microphone: $micPermission, Camera: $camPermission'); - if (micPermission == PermissionStatus.granted && camPermission == PermissionStatus.granted) { + if (micPermission == PermissionStatus.granted && + camPermission == PermissionStatus.granted) { return true; } - if (micPermission == PermissionStatus.denied || camPermission == PermissionStatus.denied) { + if (micPermission == PermissionStatus.denied || + camPermission == PermissionStatus.denied) { return requestPermissionForCameraAndMicrophone(); } - if (micPermission == PermissionStatus.permanentlyDenied || camPermission == PermissionStatus.permanentlyDenied) { + if (micPermission == PermissionStatus.permanentlyDenied || + camPermission == PermissionStatus.permanentlyDenied) { _log('Permissions => Opening App Settings'); await openAppSettings(); } @@ -193,7 +207,8 @@ class TwilioProgrammableVideo { static Future connect(ConnectOptions connectOptions) async { if (await requestPermissionForCameraAndMicrophone()) { try { - final roomId = await ProgrammableVideoPlatform.instance.connectToRoom(connectOptions.toModel()); + final roomId = await ProgrammableVideoPlatform.instance + .connectToRoom(connectOptions.toModel()); if (roomId == null) { throw Exception('RoomId is null'); } From 59117283acd3e78683cce4122db5a1dd4316d4d7 Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Mon, 27 Mar 2023 11:28:12 +0545 Subject: [PATCH 11/30] testing --- .../AudioNotificationListener.kt | 24 +++++++++---------- .../lib/src/programmable_video.dart | 1 - 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt index 6b03bad..a8b0bc4 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt @@ -17,22 +17,22 @@ class AudioNotificationListener() : BaseListener() { private var bluetoothProfileProxy: BluetoothProfile.ServiceListener = object : BluetoothProfile.ServiceListener { override fun onServiceDisconnected(profile: Int) { - debug("onServiceDisconnected => profile: $profile") - if (profile == BluetoothProfile.HEADSET) { - bluetoothProfile = null - TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() - } + // debug("onServiceDisconnected => profile: $profile") + // if (profile == BluetoothProfile.HEADSET) { + // bluetoothProfile = null + // TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() + // } } override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) { debug("onServiceConnected => profile: $profile, proxy: $proxy") - if (profile == BluetoothProfile.HEADSET) { - bluetoothProfile = proxy - if (bluetoothProfile!!.connectedDevices.size > 0 && - TwilioProgrammableVideoPlugin.pluginHandler.audioSettings.bluetoothPreferred) { - TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() - } - } + // if (profile == BluetoothProfile.HEADSET) { + // bluetoothProfile = proxy + // if (bluetoothProfile!!.connectedDevices.size > 0 && + // TwilioProgrammableVideoPlugin.pluginHandler.audioSettings.bluetoothPreferred) { + // TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() + // } + // } } } diff --git a/programmable_video/lib/src/programmable_video.dart b/programmable_video/lib/src/programmable_video.dart index b42ae2e..728eca9 100644 --- a/programmable_video/lib/src/programmable_video.dart +++ b/programmable_video/lib/src/programmable_video.dart @@ -172,7 +172,6 @@ class TwilioProgrammableVideo { await [ Permission.camera, Permission.microphone, - Permission.bluetoothConnect ].request(); final micPermission = await Permission.microphone.status; final camPermission = await Permission.camera.status; From 184aba65b861d9c150c1302069dd25d1e6cefb05 Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Mon, 27 Mar 2023 11:37:34 +0545 Subject: [PATCH 12/30] chore : fix --- .../AudioNotificationListener.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt index a8b0bc4..7a91e28 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt @@ -17,11 +17,11 @@ class AudioNotificationListener() : BaseListener() { private var bluetoothProfileProxy: BluetoothProfile.ServiceListener = object : BluetoothProfile.ServiceListener { override fun onServiceDisconnected(profile: Int) { - // debug("onServiceDisconnected => profile: $profile") - // if (profile == BluetoothProfile.HEADSET) { - // bluetoothProfile = null - // TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() - // } + debug("onServiceDisconnected => profile: $profile") + if (profile == BluetoothProfile.HEADSET) { + bluetoothProfile = null + TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() + } } override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) { From 39f49e8a7a3e47bb8289d9d0c8210d38b881d207 Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Mon, 27 Mar 2023 14:43:03 +0545 Subject: [PATCH 13/30] chore : minor change --- .../twilio_programmable_video/AudioNotificationListener.kt | 2 +- .../test/method_channel_programmable_video_test.dart | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt index 7a91e28..4c98024 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt @@ -25,7 +25,7 @@ class AudioNotificationListener() : BaseListener() { } override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) { - debug("onServiceConnected => profile: $profile, proxy: $proxy") + // debug("onServiceConnected => profile: $profile, proxy: $proxy") // if (profile == BluetoothProfile.HEADSET) { // bluetoothProfile = proxy // if (bluetoothProfile!!.connectedDevices.size > 0 && diff --git a/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart b/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart index c3de8d5..872975e 100644 --- a/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart +++ b/programmable_video_platform_interface/test/method_channel_programmable_video_test.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; From a821daea8a55890134d8a3f1af962ce186673d5d Mon Sep 17 00:00:00 2001 From: CrestNiraj12 Date: Mon, 27 Mar 2023 17:19:34 +0545 Subject: [PATCH 14/30] handled bluetooth adapter as null --- .../PluginHandler.kt | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index c720ef0..6fde94d 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -423,22 +423,27 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { } private fun setSpeakerPhoneOnInternal() { - val bluetoothProfileConnectionState = BluetoothAdapter.getDefaultAdapter().getProfileConnectionState(BluetoothProfile.HEADSET) - - debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") - - // Even if already enabled, setting `audioManager.isSpeakerphoneOn` to true - // will reroute audio to the speaker. If using a Bluetooth headset, this will cause audio to - // momentarily be routed to the device bottom speaker. - // - // It has been observed when disconnecting a bluetooth headset that sometimes - // the bluetoothProfileConnectionState will still be BluetoothProfile.STATE_CONNECTED - // resulting in an edge case where audio will be routed via the receiver rather than the - // bottom speaker. - if (!audioSettings.bluetoothPreferred || - bluetoothProfileConnectionState != BluetoothProfile.STATE_CONNECTED) { - applySpeakerPhoneSettings() - } + val adapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter() + var bluetoothProfileConnectionState: Int? = null + if (adapter != null) { + bluetoothProfileConnectionState = adapter?.getProfileConnectionState(BluetoothProfile.HEADSET) + } + + debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") + + // Even if already enabled, setting `audioManager.isSpeakerphoneOn` to true + // will reroute audio to the speaker. If using a Bluetooth headset, this will cause audio to + // momentarily be routed to the device bottom speaker. + // + // It has been observed when disconnecting a bluetooth headset that sometimes + // the bluetoothProfileConnectionState will still be BluetoothProfile.STATE_CONNECTED + // resulting in an edge case where audio will be routed via the receiver rather than the + // bottom speaker. + + if (bluetoothProfileConnectionState == null || !audioSettings.bluetoothPreferred || + bluetoothProfileConnectionState != BluetoothProfile.STATE_CONNECTED) { + applySpeakerPhoneSettings() + } } internal fun applySpeakerPhoneSettings() { From 9212e800e22892546738d6955e56756f8678f40c Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Mon, 27 Mar 2023 17:27:43 +0545 Subject: [PATCH 15/30] chore : uncomment code --- .../AudioNotificationListener.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt index 4c98024..481a78b 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt @@ -25,14 +25,14 @@ class AudioNotificationListener() : BaseListener() { } override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) { - // debug("onServiceConnected => profile: $profile, proxy: $proxy") - // if (profile == BluetoothProfile.HEADSET) { - // bluetoothProfile = proxy - // if (bluetoothProfile!!.connectedDevices.size > 0 && - // TwilioProgrammableVideoPlugin.pluginHandler.audioSettings.bluetoothPreferred) { - // TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() - // } - // } + debug("onServiceConnected => profile: $profile, proxy: $proxy") + if (profile == BluetoothProfile.HEADSET) { + bluetoothProfile = proxy + if (bluetoothProfile!!.connectedDevices.size > 0 && + TwilioProgrammableVideoPlugin.pluginHandler.audioSettings.bluetoothPreferred) { + TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() + } + } } } From 03a3803960ecbbbd3b7324bf258e1b46a887bd4b Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Tue, 28 Mar 2023 17:36:26 +0545 Subject: [PATCH 16/30] Revert "chore : uncomment code" This reverts commit 9212e800e22892546738d6955e56756f8678f40c. --- .../AudioNotificationListener.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt index 481a78b..4c98024 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioNotificationListener.kt @@ -25,14 +25,14 @@ class AudioNotificationListener() : BaseListener() { } override fun onServiceConnected(profile: Int, proxy: BluetoothProfile?) { - debug("onServiceConnected => profile: $profile, proxy: $proxy") - if (profile == BluetoothProfile.HEADSET) { - bluetoothProfile = proxy - if (bluetoothProfile!!.connectedDevices.size > 0 && - TwilioProgrammableVideoPlugin.pluginHandler.audioSettings.bluetoothPreferred) { - TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() - } - } + // debug("onServiceConnected => profile: $profile, proxy: $proxy") + // if (profile == BluetoothProfile.HEADSET) { + // bluetoothProfile = proxy + // if (bluetoothProfile!!.connectedDevices.size > 0 && + // TwilioProgrammableVideoPlugin.pluginHandler.audioSettings.bluetoothPreferred) { + // TwilioProgrammableVideoPlugin.pluginHandler.applyAudioSettings() + // } + // } } } From 67e76e31227a1f1e25fdd9bdbd1148ba3884856d Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Tue, 28 Mar 2023 17:52:06 +0545 Subject: [PATCH 17/30] chore: disbale bluetoothpreferred --- .../twilio/flutter/twilio_programmable_video/AudioSettings.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioSettings.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioSettings.kt index 79fbf37..89a2c94 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioSettings.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/AudioSettings.kt @@ -11,6 +11,6 @@ class AudioSettings(speakerEnabled: Boolean = true, bluetoothPreferred: Boolean fun reset() { speakerEnabled = true - bluetoothPreferred = true + bluetoothPreferred = false } } From 329661a90595429f88a4027d7785acbde1dea672 Mon Sep 17 00:00:00 2001 From: Saurav Date: Tue, 28 Mar 2023 18:14:49 +0545 Subject: [PATCH 18/30] test : disabe applyBluetoothSettings() --- .../flutter/twilio_programmable_video/PluginHandler.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index 6fde94d..9887a26 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -370,9 +370,9 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { debug("applyAudioSettings") setSpeakerPhoneOnInternal() - if (!audioSettings.speakerEnabled) { - applyBluetoothSettings() - } + // if (!audioSettings.speakerEnabled) { + // applyBluetoothSettings() + // } } // BluetoothSco being enabled functions similarly to holding Audio Focus when it comes From 6575609fe4cfde9eb0024b93131286eaa55a7c76 Mon Sep 17 00:00:00 2001 From: Kohsaku Onozawa Date: Sat, 4 Nov 2023 13:21:46 +0900 Subject: [PATCH 19/30] =?UTF-8?q?twilio=20video=20=E3=83=90=E3=83=BC?= =?UTF-8?q?=E3=82=B8=E3=83=A7=E3=83=B3=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- programmable_video/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programmable_video/android/build.gradle b/programmable_video/android/build.gradle index 6bb37c0..6ce0306 100644 --- a/programmable_video/android/build.gradle +++ b/programmable_video/android/build.gradle @@ -3,7 +3,7 @@ version '1.0-SNAPSHOT' buildscript { ext.kotlin_version = '1.6.10' - ext.twilio_video_version = '6.4.+' + ext.twilio_video_version = '7.6.+' repositories { google() mavenCentral() From 246dc5885368d379b5b02c9e893b2c5573866f57 Mon Sep 17 00:00:00 2001 From: Kohsaku Onozawa Date: Sat, 2 Dec 2023 11:20:40 +0900 Subject: [PATCH 20/30] =?UTF-8?q?=E3=83=90=E3=83=BC=E3=82=B8=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=82=92=E5=9B=BA=E5=AE=9A=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- programmable_video/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programmable_video/android/build.gradle b/programmable_video/android/build.gradle index 6ce0306..05ba674 100644 --- a/programmable_video/android/build.gradle +++ b/programmable_video/android/build.gradle @@ -3,7 +3,7 @@ version '1.0-SNAPSHOT' buildscript { ext.kotlin_version = '1.6.10' - ext.twilio_video_version = '7.6.+' + ext.twilio_video_version = '7.6.4' repositories { google() mavenCentral() From 18d28d987dc282795c4bf42a4e768fdec356f692 Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Wed, 6 Dec 2023 08:53:45 +0545 Subject: [PATCH 21/30] [test]chore: update kotlin version --- programmable_video/android/build.gradle | 2 +- programmable_video/example/android/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/programmable_video/android/build.gradle b/programmable_video/android/build.gradle index 6ce0306..e71dbd4 100644 --- a/programmable_video/android/build.gradle +++ b/programmable_video/android/build.gradle @@ -2,7 +2,7 @@ group 'twilio.flutter.twilio_programmable_video' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.8.0' ext.twilio_video_version = '7.6.+' repositories { google() diff --git a/programmable_video/example/android/build.gradle b/programmable_video/example/android/build.gradle index bf7da8f..a30b4ad 100644 --- a/programmable_video/example/android/build.gradle +++ b/programmable_video/example/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.8.0' repositories { google() mavenCentral() From 4a6d601e161877644fac99b5a3ef158d0760d980 Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Wed, 6 Dec 2023 09:04:03 +0545 Subject: [PATCH 22/30] chore: replace android-extensions with parcelize --- programmable_video/android/build.gradle | 6 +++++- programmable_video/example/android/build.gradle | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/programmable_video/android/build.gradle b/programmable_video/android/build.gradle index e71dbd4..26ed5e4 100644 --- a/programmable_video/android/build.gradle +++ b/programmable_video/android/build.gradle @@ -24,7 +24,7 @@ rootProject.allprojects { apply plugin: 'com.android.library' apply plugin: 'kotlin-android' -apply plugin: 'kotlin-android-extensions' +apply plugin: 'kotlin-parcelize' android { compileSdkVersion 31 @@ -44,6 +44,10 @@ android { sourceCompatibility 1.8 targetCompatibility 1.8 } + buildFeatures { + viewBinding true + } + } dependencies { diff --git a/programmable_video/example/android/build.gradle b/programmable_video/example/android/build.gradle index a30b4ad..42fac5d 100644 --- a/programmable_video/example/android/build.gradle +++ b/programmable_video/example/android/build.gradle @@ -1,5 +1,6 @@ buildscript { - ext.kotlin_version = '1.8.0' + ext.kotlin_version = '1.8.+' + repositories { google() mavenCentral() From 60470a4cbfb0eca68af362c96d7a30d1e74fe7a1 Mon Sep 17 00:00:00 2001 From: Bibek Magar Date: Thu, 28 Dec 2023 12:33:53 +0545 Subject: [PATCH 23/30] fix: fix kotlin version --- programmable_video/android/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programmable_video/android/build.gradle b/programmable_video/android/build.gradle index 9dd34ef..356ec24 100644 --- a/programmable_video/android/build.gradle +++ b/programmable_video/android/build.gradle @@ -2,7 +2,7 @@ group 'twilio.flutter.twilio_programmable_video' version '1.0-SNAPSHOT' buildscript { - ext.kotlin_version = '1.6.10' + ext.kotlin_version = '1.8.0' ext.twilio_video_version = '7.6.4' repositories { google() From 8ffd3600a1580fef34dc3e31f76431b7730f8687 Mon Sep 17 00:00:00 2001 From: Bibek Magar Date: Thu, 28 Dec 2023 12:41:46 +0545 Subject: [PATCH 24/30] fix: remove unused bluetooth code --- .../PluginHandler.kt | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index 9887a26..bc92838 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -416,18 +416,18 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { audioSettings.speakerEnabled = on setSpeakerPhoneOnInternal() - if (!audioSettings.speakerEnabled && audioSettings.bluetoothPreferred) { - applyBluetoothSettings() - } + // if (!audioSettings.speakerEnabled && audioSettings.bluetoothPreferred) { + // applyBluetoothSettings() + // } return result.success(audioSettings.speakerEnabled) } private fun setSpeakerPhoneOnInternal() { - val adapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter() - var bluetoothProfileConnectionState: Int? = null - if (adapter != null) { - bluetoothProfileConnectionState = adapter?.getProfileConnectionState(BluetoothProfile.HEADSET) - } + // val adapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter() + // var bluetoothProfileConnectionState: Int? = null + // if (adapter != null) { + // bluetoothProfileConnectionState = adapter?.getProfileConnectionState(BluetoothProfile.HEADSET) + // } debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") @@ -440,10 +440,10 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { // resulting in an edge case where audio will be routed via the receiver rather than the // bottom speaker. - if (bluetoothProfileConnectionState == null || !audioSettings.bluetoothPreferred || - bluetoothProfileConnectionState != BluetoothProfile.STATE_CONNECTED) { - applySpeakerPhoneSettings() - } + // if (bluetoothProfileConnectionState == null || !audioSettings.bluetoothPreferred || + // bluetoothProfileConnectionState != BluetoothProfile.STATE_CONNECTED) { + // applySpeakerPhoneSettings() + // } } internal fun applySpeakerPhoneSettings() { From 5d2608bb2b4f7c6abe14afcbcb9db05b5f2629dd Mon Sep 17 00:00:00 2001 From: Bibek Magar Date: Thu, 28 Dec 2023 12:49:48 +0545 Subject: [PATCH 25/30] fix: comment remove unused code --- .../twilio/flutter/twilio_programmable_video/PluginHandler.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index bc92838..e17cb41 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -414,7 +414,7 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { ?: return result.error("MISSING_PARAMS", missingParameterMessage("on"), null) audioSettings.speakerEnabled = on - setSpeakerPhoneOnInternal() + // setSpeakerPhoneOnInternal() // if (!audioSettings.speakerEnabled && audioSettings.bluetoothPreferred) { // applyBluetoothSettings() @@ -429,7 +429,7 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { // bluetoothProfileConnectionState = adapter?.getProfileConnectionState(BluetoothProfile.HEADSET) // } - debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") + // debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") // Even if already enabled, setting `audioManager.isSpeakerphoneOn` to true // will reroute audio to the speaker. If using a Bluetooth headset, this will cause audio to From 3d6894e37a5e01e9cb0c36ec3357d18dc68d7c13 Mon Sep 17 00:00:00 2001 From: Bibek Magar Date: Thu, 28 Dec 2023 15:00:46 +0545 Subject: [PATCH 26/30] fix: revert changes for testing --- .../PluginHandler.kt | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index e17cb41..9dd9d6e 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -414,22 +414,22 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { ?: return result.error("MISSING_PARAMS", missingParameterMessage("on"), null) audioSettings.speakerEnabled = on - // setSpeakerPhoneOnInternal() + setSpeakerPhoneOnInternal() - // if (!audioSettings.speakerEnabled && audioSettings.bluetoothPreferred) { - // applyBluetoothSettings() - // } + if (!audioSettings.speakerEnabled && audioSettings.bluetoothPreferred) { + applyBluetoothSettings() + } return result.success(audioSettings.speakerEnabled) } private fun setSpeakerPhoneOnInternal() { - // val adapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter() - // var bluetoothProfileConnectionState: Int? = null - // if (adapter != null) { - // bluetoothProfileConnectionState = adapter?.getProfileConnectionState(BluetoothProfile.HEADSET) - // } + val adapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter() + var bluetoothProfileConnectionState: Int? = null + if (adapter != null) { + bluetoothProfileConnectionState = adapter?.getProfileConnectionState(BluetoothProfile.HEADSET) + } - // debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") + debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") // Even if already enabled, setting `audioManager.isSpeakerphoneOn` to true // will reroute audio to the speaker. If using a Bluetooth headset, this will cause audio to @@ -440,10 +440,10 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { // resulting in an edge case where audio will be routed via the receiver rather than the // bottom speaker. - // if (bluetoothProfileConnectionState == null || !audioSettings.bluetoothPreferred || - // bluetoothProfileConnectionState != BluetoothProfile.STATE_CONNECTED) { - // applySpeakerPhoneSettings() - // } + if (bluetoothProfileConnectionState == null || !audioSettings.bluetoothPreferred || + bluetoothProfileConnectionState != BluetoothProfile.STATE_CONNECTED) { + applySpeakerPhoneSettings() + } } internal fun applySpeakerPhoneSettings() { From 8472e46e0255f29fb01b19695b2285670e57fa5e Mon Sep 17 00:00:00 2001 From: Bibek Magar Date: Thu, 28 Dec 2023 15:16:06 +0545 Subject: [PATCH 27/30] fix: revert changes --- .../flutter/twilio_programmable_video/PluginHandler.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index 9dd9d6e..15f62e9 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -429,7 +429,7 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { bluetoothProfileConnectionState = adapter?.getProfileConnectionState(BluetoothProfile.HEADSET) } - debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") + debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") // Even if already enabled, setting `audioManager.isSpeakerphoneOn` to true // will reroute audio to the speaker. If using a Bluetooth headset, this will cause audio to @@ -441,8 +441,8 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { // bottom speaker. if (bluetoothProfileConnectionState == null || !audioSettings.bluetoothPreferred || - bluetoothProfileConnectionState != BluetoothProfile.STATE_CONNECTED) { - applySpeakerPhoneSettings() + bluetoothProfileConnectionState != BluetoothProfile.STATE_CONNECTED) { + applySpeakerPhoneSettings() } } From e4b979660a39cf38419989437ea6be1e57ac4481 Mon Sep 17 00:00:00 2001 From: Bibek Magar Date: Thu, 28 Dec 2023 15:35:57 +0545 Subject: [PATCH 28/30] fix: remove bluetooth preffered --- .../twilio/flutter/twilio_programmable_video/PluginHandler.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index 15f62e9..259dac8 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -338,11 +338,11 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { private fun setAudioSettings(call: MethodCall, result: MethodChannel.Result) { val speakerphoneEnabled = call.argument("speakerphoneEnabled") ?: return result.error("MISSING_PARAMS", missingParameterMessage("speakerphoneEnabled"), null) - val bluetoothPreferred = call.argument("bluetoothPreferred") + // val bluetoothPreferred = call.argument("bluetoothPreferred") ?: return result.error("MISSING_PARAMS", missingParameterMessage("bluetoothPreferred"), null) audioSettings.speakerEnabled = speakerphoneEnabled - audioSettings.bluetoothPreferred = bluetoothPreferred + // audioSettings.bluetoothPreferred = bluetoothPreferred TwilioProgrammableVideoPlugin.audioNotificationListener.listenForRouteChanges(applicationContext) From 55763009279410f29a1592768d6d33727597cccf Mon Sep 17 00:00:00 2001 From: Bibek Magar Date: Thu, 28 Dec 2023 15:39:19 +0545 Subject: [PATCH 29/30] fix: remove bluetooth preffered --- .../flutter/twilio_programmable_video/PluginHandler.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt index 259dac8..75287ef 100644 --- a/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt +++ b/programmable_video/android/src/main/kotlin/twilio/flutter/twilio_programmable_video/PluginHandler.kt @@ -338,11 +338,11 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { private fun setAudioSettings(call: MethodCall, result: MethodChannel.Result) { val speakerphoneEnabled = call.argument("speakerphoneEnabled") ?: return result.error("MISSING_PARAMS", missingParameterMessage("speakerphoneEnabled"), null) - // val bluetoothPreferred = call.argument("bluetoothPreferred") + val bluetoothPreferred = call.argument("bluetoothPreferred") ?: return result.error("MISSING_PARAMS", missingParameterMessage("bluetoothPreferred"), null) audioSettings.speakerEnabled = speakerphoneEnabled - // audioSettings.bluetoothPreferred = bluetoothPreferred + audioSettings.bluetoothPreferred = bluetoothPreferred TwilioProgrammableVideoPlugin.audioNotificationListener.listenForRouteChanges(applicationContext) @@ -426,7 +426,7 @@ class PluginHandler : MethodCallHandler, ActivityAware, BaseListener { val adapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter() var bluetoothProfileConnectionState: Int? = null if (adapter != null) { - bluetoothProfileConnectionState = adapter?.getProfileConnectionState(BluetoothProfile.HEADSET) + // bluetoothProfileConnectionState = adapter?.getProfileConnectionState(BluetoothProfile.HEADSET) } debug("setSpeakerPhoneOnInternal => on: ${audioSettings.speakerEnabled}\n bluetoothEnable: ${audioSettings.bluetoothPreferred}\n bluetoothScoOn: ${audioManager.isBluetoothScoOn}\n bluetoothProfileConnectionState: $bluetoothProfileConnectionState") From b10cc28d56025e30f32725541826b4d84421ea03 Mon Sep 17 00:00:00 2001 From: pranavo72bex Date: Fri, 3 May 2024 16:41:04 +0545 Subject: [PATCH 30/30] feat: update packages --- programmable_video/pubspec.yaml | 4 ++-- programmable_video_platform_interface/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/programmable_video/pubspec.yaml b/programmable_video/pubspec.yaml index d57cdf2..5c326ec 100644 --- a/programmable_video/pubspec.yaml +++ b/programmable_video/pubspec.yaml @@ -13,7 +13,7 @@ environment: dependencies: flutter: sdk: flutter - permission_handler: ^10.2.0 + permission_handler: ^11.3.1 enum_to_string: ^2.0.1 twilio_programmable_video_platform_interface: git: @@ -29,7 +29,7 @@ dev_dependencies: # https://github.com/flutter/flutter/issues/48246 pedantic: ^1.11.0 mockito: ^5.0.6 - build_runner: ^2.0.1 + build_runner: ^2.4.9 dependency_overrides: pedantic: ^1.9.0 diff --git a/programmable_video_platform_interface/pubspec.yaml b/programmable_video_platform_interface/pubspec.yaml index f8dbeed..a31a81a 100644 --- a/programmable_video_platform_interface/pubspec.yaml +++ b/programmable_video_platform_interface/pubspec.yaml @@ -22,7 +22,7 @@ dev_dependencies: sdk: flutter pedantic: ^1.11.0 mockito: ^5.0.6 - build_runner: ^1.10.0 + build_runner: ^2.4.9 dependency_overrides: pedantic: ^1.9.0