call_cubit.dart 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import 'dart:async';
  2. import 'dart:typed_data';
  3. import 'package:audioplayers/audioplayers.dart';
  4. import 'package:flutter/material.dart';
  5. import 'package:flutter/services.dart';
  6. import 'package:flutter_bloc/flutter_bloc.dart';
  7. import 'package:agora_rtc_engine/rtc_engine.dart';
  8. import 'package:youtube/data/models/call_model.dart';
  9. import 'package:youtube/presentaion/cubit/home/home_cubit.dart';
  10. import 'package:youtube/shared/constats.dart';
  11. import '../../../data/api/call_api.dart';
  12. import 'call_state.dart';
  13. import 'package:quiver/async.dart';
  14. class CallCubit extends Cubit<CallState> {
  15. CallCubit() : super(CallInitial());
  16. static CallCubit get(context) => BlocProvider.of(context);
  17. //Agora video room
  18. int? remoteUid;
  19. RtcEngine? engine;
  20. Future<void> initAgoraAndJoinChannel({required String channelToken,required String channelName,required bool isCaller}) async {
  21. //create the engine
  22. engine = await RtcEngine.create(agoraAppId);
  23. await engine!.enableVideo();
  24. engine!.setEventHandler(
  25. RtcEngineEventHandler(
  26. joinChannelSuccess: (String channel, int uid, int elapsed) {
  27. debugPrint("local user $uid joined");
  28. },
  29. userJoined: (int uid, int elapsed) {
  30. debugPrint("remote user $uid joined");
  31. remoteUid = uid;
  32. emit(AgoraRemoteUserJoinedEvent());
  33. },
  34. userOffline: (int uid, UserOfflineReason reason) {
  35. debugPrint("remote user $uid left channel");
  36. remoteUid = null;
  37. emit(AgoraUserLeftEvent());
  38. },
  39. ),
  40. );
  41. //join channel
  42. await engine!.joinChannel(agoraTestToken, agoraTestChannelName, null, 0);
  43. if(isCaller){
  44. emit(AgoraInitForSenderSuccessState());
  45. playContactingRing(isCaller: true);
  46. }else{
  47. emit(AgoraInitForReceiverSuccessState());
  48. }
  49. debugPrint('channelTokenIs $channelToken channelNameIs $channelName');
  50. }
  51. //Sender
  52. AudioPlayer assetsAudioPlayer = AudioPlayer();
  53. Future<void> playContactingRing({required bool isCaller}) async {
  54. String audioAsset = "assets/sounds/ringlong.mp3";
  55. ByteData bytes = await rootBundle.load(audioAsset);
  56. Uint8List soundBytes = bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes);
  57. int result = await assetsAudioPlayer.playBytes(soundBytes);
  58. if(result == 1){ //play success
  59. debugPrint("Sound playing successful.");
  60. }else{
  61. debugPrint("Error while playing sound.");
  62. }
  63. if(isCaller){
  64. startCountdownCallTimer();
  65. }
  66. }
  67. int current = 0;
  68. late CountdownTimer countDownTimer;
  69. void startCountdownCallTimer() {
  70. countDownTimer = CountdownTimer(
  71. const Duration(seconds: callDurationInSec),
  72. const Duration(seconds: 1),
  73. );
  74. var sub = countDownTimer.listen(null);
  75. sub.onData((duration) {
  76. current = callDurationInSec - duration.elapsed.inSeconds;
  77. debugPrint("DownCount: $current");
  78. });
  79. sub.onDone(() {
  80. debugPrint("CallTimeDone");
  81. sub.cancel();
  82. emit(DownCountCallTimerFinishState());
  83. });
  84. }
  85. bool muted = false;
  86. Widget muteIcon = const Icon(Icons.keyboard_voice_rounded,color: Colors.black,);
  87. Future<void> toggleMuted() async {
  88. muted = !muted;
  89. muteIcon = muted
  90. ? const Icon(Icons.mic_off_rounded,color: Colors.black,)
  91. : const Icon(Icons.keyboard_voice_rounded,color: Colors.black,);
  92. await engine!.muteLocalAudioStream(muted);
  93. emit(AgoraToggleMutedState());
  94. }
  95. Future<void> switchCamera() async {
  96. await engine!.switchCamera();
  97. emit(AgoraSwitchCameraState());
  98. }
  99. //Update Call Status
  100. final _callApi = CallApi();
  101. void updateCallStatusToUnAnswered({required String callId}){
  102. emit(LoadingUnAnsweredVideoChatState());
  103. _callApi.updateCallStatus(callId: callId, status: CallStatus.unAnswer.name).then((value) {
  104. emit(SuccessUnAnsweredVideoChatState());
  105. }).catchError((onError){
  106. emit(ErrorUnAnsweredVideoChatState(onError.toString()));
  107. });
  108. }
  109. Future<void> updateCallStatusToCancel({required String callId})async {
  110. await _callApi.updateCallStatus(callId: callId, status: CallStatus.cancel.name);
  111. }
  112. Future<void> updateCallStatusToReject({required String callId})async {
  113. await _callApi.updateCallStatus(callId: callId, status: CallStatus.reject.name);
  114. }
  115. Future<void> updateCallStatusToAccept({required CallModel callModel})async {
  116. await _callApi.updateCallStatus(callId: callModel.id, status: CallStatus.accept.name);
  117. initAgoraAndJoinChannel(channelToken: agoraTestChannelName, channelName: agoraTestToken, isCaller: false);
  118. }
  119. Future<void> updateCallStatusToEnd({required String callId})async {
  120. await _callApi.updateCallStatus(callId: callId, status: CallStatus.end.name);
  121. }
  122. Future<void> endCurrentCall({required String callId}) async {
  123. await _callApi.endCurrentCall(callId: callId);
  124. }
  125. Future<void> updateUserBusyStatusFirestore({required CallModel callModel}) async{
  126. await _callApi.updateUserBusyStatusFirestore(callModel: callModel, busy: false);
  127. }
  128. Future<void> performEndCall({required CallModel callModel}) async{
  129. await endCurrentCall(callId: callModel.id);
  130. await updateUserBusyStatusFirestore(callModel: callModel);
  131. }
  132. StreamSubscription? callStatusStreamSubscription;
  133. void listenToCallStatus({required CallModel callModel,required BuildContext context,required bool isReceiver}){
  134. var _homeCubit = HomeCubit.get(context);
  135. callStatusStreamSubscription = _callApi.listenToCallStatus(callId: callModel.id);
  136. callStatusStreamSubscription!.onData((data) {
  137. if(data.exists){
  138. String status = data.data()!['status'];
  139. if(status == CallStatus.accept.name) {
  140. _homeCubit.currentCallStatus = CallStatus.accept;
  141. debugPrint('acceptStatus');
  142. emit(CallAcceptState());
  143. }
  144. if(status == CallStatus.reject.name) {
  145. _homeCubit.currentCallStatus = CallStatus.reject;
  146. debugPrint('rejectStatus');
  147. callStatusStreamSubscription!.cancel();
  148. emit(CallRejectState());
  149. }
  150. if(status == CallStatus.unAnswer.name) {
  151. _homeCubit.currentCallStatus = CallStatus.unAnswer;
  152. debugPrint('unAnswerStatusHere');
  153. callStatusStreamSubscription!.cancel();
  154. emit(CallNoAnswerState());
  155. }
  156. if(status == CallStatus.cancel.name) {
  157. _homeCubit.currentCallStatus = CallStatus.cancel;
  158. debugPrint('cancelStatus');
  159. callStatusStreamSubscription!.cancel();
  160. emit(CallCancelState());
  161. }
  162. if(status == CallStatus.end.name){
  163. _homeCubit.currentCallStatus = CallStatus.end;
  164. debugPrint('endStatus');
  165. callStatusStreamSubscription!.cancel();
  166. emit(CallEndState());
  167. }
  168. }
  169. });
  170. }
  171. }