call_cubit.dart 6.6 KB

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