import 'dart:async'; import 'dart:ui'; import 'package:agora_rtc_engine/agora_rtc_engine.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:chat/chat/gift_page.dart'; import 'package:chat/data/UserData.dart'; import 'package:chat/data/WebData.dart'; import 'package:chat/data/constants.dart'; import 'package:chat/generated/i18n.dart'; import 'package:chat/models/UserInfo.dart'; import 'package:chat/models/gift_item_model.dart'; import 'package:chat/models/gift_select_provider.dart'; import 'package:chat/models/money_change.dart'; import 'package:chat/models/ref_name_provider.dart'; import 'package:chat/r.dart'; import 'package:chat/utils/ChargeMoney.dart'; import 'package:chat/utils/CustomUI.dart'; import 'package:chat/utils/HttpUtil.dart'; import 'package:chat/utils/MessageMgr.dart'; import 'package:chat/utils/TokenMgr.dart'; import 'package:chat/utils/anim_effect_overlay.dart'; import 'package:chat/utils/counter_overlay.dart'; import 'package:chat/utils/msgHandler.dart'; import 'package:chat/utils/screen.dart'; import 'package:chat/utils/sound_util.dart'; import 'package:chat/utils/sp_utils.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:oktoast/oktoast.dart'; import 'package:provider/provider.dart'; class AudioChatPage extends StatefulWidget { final UserInfo userInfo; final bool isReplay; final bool isTranslateButler; final Function translateButlerCloseCallBack; AudioChatPage({@required this.userInfo, this.isReplay = false, this.isTranslateButler,this.translateButlerCloseCallBack}); @override _AudioChatPageState createState() => _AudioChatPageState(); } class _AudioChatPageState extends State { //是否启用扬声器 bool speakPhone = false; int friendId; bool isReply; //是否应答模式 bool isChating = false; //是否通话中 //超时挂断 Timer offTimer; Timer callingTimer; String channelName; //打赏礼物信息 List giftList; final _controller = new PageController(); //礼物数量 int curGiftValue = 1; bool isQuit = false; @override void initState() { super.initState(); friendId = widget.userInfo.userId; isReply = widget.isReplay; MsgHandler.isAudioConnect = true; getGiftList(); initAgoreSdk(); getDefaultSetting(); //事件监听回调 setAgoreEventListener(); //礼物打赏 MessageMgr().on('Receive Gift', receiveGift); MessageMgr().on('AudioChat Failed', closeChat); MessageMgr().on('AudioChat State', refuseAnswer); } void getDefaultSetting() async { bool soundPlayMode = (await SPUtils.getBool(Constants.SOUND_PLAY_MODE)) ?? false; setState(() { speakPhone = soundPlayMode; }); AgoraRtcEngine.setEnableSpeakerphone(speakPhone); } closeChat(args) { showToast(I18n.of(context).not_online); _onExit(context); } @override void didChangeDependencies() { super.didChangeDependencies(); print('test didChangeDependencies'); } refuseAnswer(args) { var fdId = args['fdId']; var isAnswer = args['isAnswer']; if (fdId == friendId && !isAnswer) { // showToast('对方暂时无法接听'); _onExit(context); } } receiveGift(gift) { int giftId = gift.giftId; int amount = gift.giftAmount; int total = gift.money; print(DateTime.now()); var giftModel = giftList[giftId - 1]; print('收到礼物金额:$total'); UserData().incomeMoney += total; //Provider.of(context, listen: false).addMoney(total); AnimEffect.showEffect(context, giftId); AnimEffect.showDashangEffect(context, false, amount, giftModel.name); } //本页面即将销毁 @override void dispose() { _controller.dispose(); MessageMgr().off('AudioChat State', refuseAnswer); MessageMgr().off('AudioChat Failed', closeChat); MessageMgr().off('Receive Gift', receiveGift); MsgHandler.isAudioConnect = false; offTimer?.cancel(); callingTimer?.cancel(); SoundUtils().stop(); AgoraRtcEngine.leaveChannel(); //sdk资源释放 AgoraRtcEngine.destroy(); super.dispose(); } void initAgoreSdk() { //初始化引擎 AgoraRtcEngine.create(Constants.Agore_appId); //设置视频为可用 启用音频模块 AgoraRtcEngine.enableAudio(); //每次需要原生视频都要调用_createRendererView if (!isReply) { int myId = UserData().basicInfo.userId; _createRendererView(myId); } } void getGiftList() async { Map data = { "userId": UserData().basicInfo.userId, }; data['sign'] = TokenMgr().getSign(data); Response res = await HttpUtil().post('prop/get/list', data: data); if (res == null) { return; } print(res); Map resData = res.data; if (resData['code'] == 0) { //领取成功 giftList = resData['data'] .map((v) => GiftItemModel.fromJson(v)) .toList(); print('giftList length : ${giftList.length}'); } } @override Widget build(BuildContext context) { return Scaffold( body: SafeArea( child: Stack( children: [ ConstrainedBox( constraints: BoxConstraints.expand(), child: CachedNetworkImage( imageUrl: widget.userInfo.headimgurl, fit: BoxFit.fill, placeholder: CustomUI.buildImgLoding)), BackdropFilter( filter: new ImageFilter.blur( sigmaX: Screen.width / 8, sigmaY: Screen.width / 8), child: Container( color: Colors.black.withOpacity(0.5), ), ), SafeArea( child: Padding( padding: EdgeInsets.symmetric(vertical: 40), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _viewAudio(), isReply ? _replayToolBar() : _bottomToolBar(), ], ))), widget.isTranslateButler?Material(child: InkWell(child: Container(color: Colors.yellow,width: 60,height: 60,),onTap: (){ widget.translateButlerCloseCallBack(1); print('内页内页'); },),):Container() ], ))); } _viewAudio() { var url = widget.userInfo.headimgurl; print('url : $url'); print(url.length); var userName = Provider.of(context) .getRefName(widget.userInfo.userId, widget.userInfo.nickName); var local = WebData().getCity(widget.userInfo.city); var profession = WebData().getProffesionName(widget.userInfo.occupation); var birth = widget.userInfo.birthday; print('birth : $birth'); var birthDay = DateTime.parse(birth); print(birthDay); var today = DateTime.now(); var testAge = today.difference(birthDay); print(testAge.inDays); var age = testAge.inDays ~/ 365; var connectTip; if (isChating) { connectTip = I18n.of(context).chatting; } else { if (isReply) { connectTip = I18n.of(context).voice_msg.replaceFirst('/s1', userName); } else { connectTip = I18n.of(context).waitting_answer; } } return Container( child: Column( children: [ ClipRRect( borderRadius: BorderRadius.circular(0), child: url.length == 0 ? SizedBox( height: 100, width: 100, child: Image.asset(R.assetsImagesDefaultNorAvatar)) : CachedNetworkImage( width: 100, height: 100, imageUrl: url, placeholder: (context, url) => Padding( padding: EdgeInsets.all(5), child: CircularProgressIndicator( valueColor: AlwaysStoppedAnimation(Constants.BlueTextColor)), ), fit: BoxFit.cover, ), ), SizedBox(height: 5), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( constraints: BoxConstraints(maxWidth: Screen.width - 50), child: Text( userName, style: TextStyle(fontSize: 20, color: Colors.white70), textScaleFactor: 1.0, overflow: TextOverflow.ellipsis, textAlign: TextAlign.center, )), fixedText(' $age', fontSize: 20, color: Colors.white70) ], ), SizedBox(height: 5), Text( '$local', style: TextStyle(fontSize: 16, color: Colors.white70), textScaleFactor: 1.0, textAlign: TextAlign.center, ), SizedBox(height: 5), Text( '$profession', style: TextStyle(fontSize: 16, color: Colors.white70), textScaleFactor: 1.0, textAlign: TextAlign.center, ), SizedBox(height: 20), Container( width: Screen.width - 50, child: Text( connectTip, //maxLines: 2, style: TextStyle(fontSize: 20, color: Colors.white70), textScaleFactor: 1.0, textAlign: TextAlign.center, )), ], ), ); } _replayToolBar() { return Container( child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ //接听按钮 RawMaterialButton( onPressed: () { MsgHandler.stopAudioRing(); int myId = UserData().basicInfo.userId; MsgHandler.sendReplyAudioChatReq(friendId, true); _createRendererView(myId); }, child: Icon( Icons.call, color: Colors.white, size: 35.0, ), shape: CircleBorder(), elevation: 2.0, fillColor: Colors.greenAccent, padding: const EdgeInsets.all(15.0), ), //挂断按钮 RawMaterialButton( onPressed: () { _onExit(context); MsgHandler.stopAudioRing(); MsgHandler.sendReplyAudioChatReq(friendId, false); }, child: Icon( Icons.call_end, color: Colors.white, size: 35.0, ), shape: CircleBorder(), elevation: 2.0, fillColor: Colors.redAccent, padding: const EdgeInsets.all(15.0), ) ])); } _bottomToolBar() { List showWidgets = [ Text(I18n.of(context).voicing, textScaleFactor: 1.0, style: TextStyle(fontSize: 11, color: Colors.white24), textAlign: TextAlign.center), SizedBox(height: 10), Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Offstage( offstage: !isChating, child: UserData().giftSwitch > 0 ? RawMaterialButton( onPressed: _showGiftSheet, child: Icon( IconData(0xe64e, fontFamily: Constants.IconFontFamily), color: Colors.blueAccent, size: 20.0, ), shape: CircleBorder(), elevation: 2.0, fillColor: Colors.white, padding: const EdgeInsets.all(12.0), ) : SizedBox(width: 36, height: 36)), //挂断按钮 RawMaterialButton( onPressed: () { MsgHandler.sendReplyAudioChatReq(friendId, false); _onExit(context); }, child: Icon( Icons.call_end, color: Colors.white, size: 35.0, ), shape: CircleBorder(), elevation: 2.0, fillColor: Colors.redAccent, padding: const EdgeInsets.all(15.0), ), //是否外放 Offstage( offstage: !isChating, child: RawMaterialButton( onPressed: _isSpeakPhone, child: new Icon( speakPhone ? Icons.volume_up : Icons.volume_down, color: Colors.blueAccent, size: 20.0, ), shape: new CircleBorder(), elevation: 2.0, fillColor: Colors.white, padding: const EdgeInsets.all(12.0), )) ]) ]; if (isChating) { showWidgets.insert( 0, Container( alignment: Alignment.center, height: 80, width: 200, child: CounterOverlay(), )); } return Container( child: Column(children: showWidgets), ); } //创建渲染视图 void _createRendererView(int uId) { //增加音频会话对象 为了音频布局需要(通过uid和容器信息) //加入频道 第一个参数是 token 第二个是频道id 第三个参数 频道信息 一般为空 第四个 用户id int myId = UserData().basicInfo.userId; setState(() { print('加入聊天房间'); var channelName = UserData().getSessionId(friendId).toString(); AgoraRtcEngine.joinChannel(null, channelName, null, myId); }); } //设置事件监听 void setAgoreEventListener() { //成功加入房间 AgoraRtcEngine.onJoinChannelSuccess = (String channel, int uid, int elapsed) { print("加入成功id为:$uid elapsed:$elapsed"); if (isReply) { //回应 setState(() { isReply = false; isChating = true; }); } else { MsgHandler.sendAudioChatReq(widget.userInfo.userId); callingTimer = Timer.periodic(Duration(milliseconds: 2200), (timer) { SoundUtils().play( 'http://testcyhd.chengyouhd.com/Upload/Audio/even_wheat_sound.mp3', isLocal: false); }); //30没人接就自动挂断 offTimer = Timer(Duration(seconds: 30), () { MsgHandler.sendReplyAudioChatReq(friendId, false); _onExit(context); }); } }; //监听是否有新用户加入 AgoraRtcEngine.onUserJoined = (int uid, int elapsed) { print("新用户所加入的id为:$uid elapsed:$elapsed"); setState(() { //更新UI布局 SoundUtils().stop(); callingTimer?.cancel(); isChating = true; offTimer?.cancel(); }); }; //监听用户是否离开这个房间 AgoraRtcEngine.onUserOffline = (int uid, int reason) { print("用户离开的id为:$uid"); _onExit(context); }; AgoraRtcEngine.onError = (dynamic errCode) { print('通话异常'); // _onExit(context); }; //监听用户是否离开这个频道 AgoraRtcEngine.onLeaveChannel = () { print("用户离开"); }; } void _showGiftSheet() { showModalBottomSheet( context: context, elevation: 2.0, shape: RoundedRectangleBorder( borderRadius: BorderRadius.only( topLeft: Radius.circular(20), topRight: Radius.circular(20))), backgroundColor: Colors.transparent, builder: (BuildContext context) { return StatefulBuilder( builder: (BuildContext context, setBottomSheetState) { return Container( height: 400, width: Screen.width, decoration: BoxDecoration( color: Colors.white, ), child: giftList == null ? Center( child: Text( I18n.of(context).no_gift, textScaleFactor: 1.0, )) : Column( children: [ Container( // decoration: BoxDecoration( // color: Colors.orangeAccent, // borderRadius: borderRadius // ), padding: EdgeInsets.symmetric( vertical: 10, horizontal: 20), child: Row( children: [ Text(I18n.of(context).sent_gift, textScaleFactor: 1.0, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: Constants.BlackTextColor)), Expanded( child: SizedBox(), ), Image.asset( R.assetsImagesCoin, scale: 2, ), SizedBox( width: 5, ), fixedText( I18n.of(context) .available_balance .replaceFirst('/s1', ''), color: Constants.GreyTextColor), Consumer( builder: (context, MoneyChangeProvider counter, child) => fixedText(counter.money.toString(), color: Color(0xFFDB305D)), ), fixedText(I18n.of(context).mask_coin, color: Color(0xFFDB305D)), ], ), ), Container( alignment: Alignment.topCenter, padding: EdgeInsets.all(10), height: 230, child: GiftPage(giftList, 0), ), Expanded(child: SizedBox()), Padding( padding: EdgeInsets.symmetric( vertical: 5, horizontal: 20), child: Row( children: [ Expanded( child: SizedBox( height: 36, child: RaisedButton( padding: EdgeInsets.symmetric(horizontal: 5), child: Text( I18n.of(context).recharge, textScaleFactor: 1.0, maxLines: 1, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold), ), elevation: 2.0, color: Colors.orangeAccent, textColor: Colors.white, onPressed: () { ChargeMoney.showChargeSheet(context, () { setState(() {}); }); }, shape: RoundedRectangleBorder( borderRadius: BorderRadius.all( Radius.circular(20)), ), ), )), SizedBox(width: 20), Expanded( flex: 2, child: Container( height: 36, decoration: BoxDecoration( boxShadow: [ BoxShadow( blurRadius: 2.0, color: Colors.white24) ], borderRadius: BorderRadius.all( Radius.circular(20)), border: Border.all( color: Colors.red)), child: Row( children: [ Expanded( child: DropdownButton( isExpanded: true, underline: Container(), value: curGiftValue, style: TextStyle(fontSize: 16), iconEnabledColor: Colors.redAccent, onChanged: (newValue) { setBottomSheetState(() { curGiftValue = newValue; }); }, items: [1, 6, 8, 66] .map((v) => DropdownMenuItem( value: v, child: Container( padding: EdgeInsets.only( left: 20), child: fixedText( v.toString(), color: v == curGiftValue ? Colors .redAccent : Colors .black)))) .toList(), )), Expanded( child: InkWell( child: Container( decoration: BoxDecoration( color: Color(0xFFFD6E8F), borderRadius: BorderRadius.only( topRight: Radius .circular(20), bottomRight: Radius.circular( 20)), ), height: 36, alignment: Alignment.center, child: Text( I18n.of(context).give, textScaleFactor: 1.0, style: TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold), ), ), onTap: () { _sendGift(context); }, )) ], ))) ], ), ) ], )); }, ); }); } _sendGift(BuildContext context) async { int curSelectIndex = Provider.of(context).curSelectIndex; var curGift = giftList[curSelectIndex]; int price = curGift.price; int giftId = curGift.id; int total = curGiftValue * price; if (total > Provider.of(context).money) { showToast(I18n.of(context).not_enough); return; } int buyCount = 0; if (curGiftValue > curGift.amount) { buyCount = curGiftValue - curGift.amount; } Map data = { "userId": UserData().basicInfo.userId, "rUserId": friendId, "id": giftId, "price": price, "amount": curGiftValue, "totalPrice": total, "payNum": buyCount }; data['sign'] = TokenMgr().getSign(data); Response res = await HttpUtil().post('reward/gift', data: data); if (res == null) { return; } Map resData = res.data; //showToast(resData['msg']); if (resData['code'] == 0) { //赠送成功 print('赠送金额:${resData['data']}'); int receiveAmount = resData['data']; Navigator.pop(context); Provider.of(context, listen: false).subMoney(total); AnimEffect.showEffect(context, giftId); AnimEffect.showDashangEffect(context, true, curGiftValue, curGift.name); MsgHandler.sendGiftTo(friendId, giftId, curGiftValue, receiveAmount); setState(() {}); } } //是否开启扬声器 void _isSpeakPhone() { setState(() { speakPhone = !speakPhone; }); AgoraRtcEngine.setEnableSpeakerphone(speakPhone); } //退出频道 退出本页面 void _onExit(BuildContext context) { if (!isQuit) { if (Navigator.canPop(context)) { isQuit = true; if(widget.translateButlerCloseCallBack!=null){ widget.translateButlerCloseCallBack(2); }else{ Navigator.of(context).pop(); } SoundUtils().stop(); callingTimer?.cancel(); } } } }