// // JuPhoonCallViewController.m // watch // // Created by Apple on 2019/4/29. // Copyright © 2019年 xTT. All rights reserved. // //ios 4~10 #import #import //ios > 10 remove by lsz 2020-12-22 //#import //#import #import "JuPhoonCallViewController.h" #import "User.h" #import "Device.h" #import "JCManager.h" #import #import "AppDelegate.h" /*CXCallObserverDelegate*/ @interface JuPhoonCallViewController () { AVAudioPlayer *audioPlayer; int wait_duration; NSInteger tempElapsed ; BOOL hangUping; CTCallCenter *callCenter_old; //CXCallObserver *callObserver; } /** 控制 挂断 和 接听的 容器 */ @property (weak, nonatomic) IBOutlet UIStackView *buttonStackView; /** 主动挂断按钮 View */ @property (weak, nonatomic) IBOutlet UIView *activeHangupView; /** 右侧按钮的 容器 View */ @property (weak, nonatomic) IBOutlet UIView *rightBtnView; /// 转换摄像头(视频) 免提 (语音) @property (weak, nonatomic) IBOutlet UILabel *rightBtnDesLabel; /// 转换摄像头的图像按钮(视频) 免提的图像按钮 (语音) @property (weak, nonatomic) IBOutlet UIButton *rightBtn; /** 静音按钮 容器View */ @property (weak, nonatomic) IBOutlet UIView *muteBtnView; /** 被动挂断按钮 View */ @property (weak, nonatomic) IBOutlet UIView *hangupView; /** 接听按钮 View */ @property (weak, nonatomic) IBOutlet UIView *acceptView; @property (strong, nonatomic) NSTimer *callDurationTimer; @property (weak, nonatomic) IBOutlet UILabel *timeLabel; /** 是否是接通状态 */ @property (nonatomic) BOOL isCall; @property (weak, nonatomic) IBOutlet UIImageView *headImageView; @property (weak, nonatomic) IBOutlet UILabel *nameLabel; @property (weak, nonatomic) IBOutlet UIView *backView; /// 是否未打开麦克风 @property (nonatomic) BOOL isUNAuthorizationTypeAudio; /// 是否未打开相机 @property (nonatomic) BOOL isUNAuthorizationTypeCamera; @end @implementation JuPhoonCallViewController -(BOOL)shouldAutorotate{ return NO; } - (void)judgeAuthorization{ if(self.isUNAuthorizationTypeCamera == YES){ [self showAlertAuthorizationTypeCamera]; if(self.isReceiveCall){ [self activeHangUpBtnAction:nil]; }else{ [self hangUpButton:nil]; } }else if(self.isUNAuthorizationTypeAudio == YES){ [self showAlertAuthorizationTypeVideo]; if(self.isReceiveCall){ [self activeHangUpBtnAction:nil]; }else{ [self hangUpButton:nil]; } } } - (void)viewDidLoad { [super viewDidLoad]; WEAKSELF if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive){ //当处于非前台时 [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidBecomeActiveNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) { //当从后台变为前台时收到的通知 [weakSelf judgeAuthorization]; }]; } JLAuthorizationManager *jlManager = [JLAuthorizationManager defaultManager]; [jlManager JL_requestAuthorizationWithAuthorizationType:JLAuthorizationTypeAudio authorizedHandler:nil unAuthorizedHandler:^{ weakSelf.isUNAuthorizationTypeAudio = YES; }]; [jlManager JL_requestAuthorizationWithAuthorizationType:JLAuthorizationTypeVideo authorizedHandler:nil unAuthorizedHandler:^{ weakSelf.isUNAuthorizationTypeCamera = YES; }]; // MARK: 处理来电接听挂断 if([UIDevice currentDevice].systemVersion.floatValue > 10.0){ //callObserver= [CXCallObserver new]; //[callObserver setDelegate:self queue:dispatch_get_main_queue()]; }else{ callCenter_old= [CTCallCenter new]; WEAKSELF callCenter_old.callEventHandler = ^(CTCall *call) { dispatch_async(dispatch_get_main_queue(), ^{ if ([call.callState isEqualToString:CTCallStateDialing]) { xLog(@"电话主动拨打电话"); } else if ([call.callState isEqualToString:CTCallStateConnected]) { xLog(@"电话接通"); [weakSelf activeHangUpBtnAction:nil]; } else if ([call.callState isEqualToString:CTCallStateDisconnected]) { xLog(@"电话挂断"); } else if ([call.callState isEqualToString:CTCallStateIncoming]) { xLog(@"电话被叫"); } else { xLog(@"电话其他状态"); } }); }; } [self initUI]; //订阅状态接收的通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateUI:) name:kCallNotification object:nil]; } -(void)showAlertAuthorizationTypeVideo{ LGAlertView *alertView = [[LGAlertView alloc] initWithTitle:[NSString stringWithFormat:@"请在iPhone的\"设置-隐私-麦克风\"选项中,允许%@访问你的麦克风",APPName] message:nil style:LGAlertViewStyleAlert buttonTitles:@[@"确定"] cancelButtonTitle:@"" destructiveButtonTitle:nil actionHandler:^(LGAlertView *alertView, NSString *title, NSUInteger index) { //[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString]]; } cancelHandler:^(LGAlertView *alertView) { } destructiveHandler:nil]; [alertView showAnimated:YES completionHandler:nil]; } -(void)showAlertAuthorizationTypeCamera{ LGAlertView *alertView = [[LGAlertView alloc] initWithTitle:[NSString stringWithFormat:@"请在iPhone的\"设置-隐私-相机\"选项中,允许%@访问你的相机",APPName] message:nil style:LGAlertViewStyleAlert buttonTitles:@[@"确定"] cancelButtonTitle:@"" destructiveButtonTitle:nil actionHandler:^(LGAlertView *alertView, NSString *title, NSUInteger index) { } cancelHandler:^(LGAlertView *alertView) { } destructiveHandler:nil]; [alertView showAnimated:YES completionHandler:nil]; } #pragma mark ---- CXCallObserverDelegate---- //- (void)callObserver:(CXCallObserver *)callObserver callChanged:(CXCall *)call{ // NSLog(@"outgoing :%d onHold :%d hasConnected :%d hasEnded :%d",call.outgoing,call.onHold,call.hasConnected,call.hasEnded); /* 拨打: outgoing :1 onHold :0 hasConnected :0 hasEnded :0 拒绝: outgoing :1 onHold :0 hasConnected :0 hasEnded :1 链接: outgoing :1 onHold :0 hasConnected :1 hasEnded :0 挂断: outgoing :1 onHold :0 hasConnected :1 hasEnded :1 对方未接听时挂断: outgoing :1 onHold :0 hasConnected :0 hasEnded :1 新来电话: outgoing :0 onHold :0 hasConnected :0 hasEnded :0 保留并接听: outgoing :1 onHold :1 hasConnected :1 hasEnded :0 另一个挂掉: outgoing :0 onHold :0 hasConnected :1 hasEnded :0 保持链接: outgoing :1 onHold :0 hasConnected :1 hasEnded :1 对方挂掉: outgoing :0 onHold :0 hasConnected :1 hasEnded :1 */ //接通 另一个挂掉 //if (!call.outgoing && !call.onHold && call.hasConnected && !call.hasEnded) { // [self activeHangUpBtnAction:nil]; //} //} - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; //强制亮屏 [[UIApplication sharedApplication] setIdleTimerDisabled:YES]; if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive){ [self judgeAuthorization]; } } - (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; //取消强制亮屏 [[UIApplication sharedApplication] setIdleTimerDisabled:NO]; [self stopRing]; [self _stopCallDurationTimer]; self.nameLabel.text = nil; self.timeLabel.text = nil; //if(callObserver){ // [callObserver setDelegate:nil queue:dispatch_get_main_queue()]; // callObserver = nil; //} if(callCenter_old){ [callCenter_old setCallEventHandler:nil]; callCenter_old = nil; } // [[JCManager shared].client logout]; } -(void)initUI{ //等待时的图片 //判断性别 NSString *sexImageName ; if([cUser.cDevice.sex isEqualToString:@"girl"]){ //女 sexImageName = @"icon_girl_head_portrait"; }else{ //男 sexImageName = @"icon_boy_head_portrait"; } [self.headImageView sd_setImageWithURL:[NSURL URLWithString:self.device.avator] placeholderImage:[myHelper getImageWithName:sexImageName] options:SDWebImageRetryFailed]; self.nameLabel.text = self.device.name; self.timeLabel.hidden = YES; self.statusLabel.hidden = YES; NSString * music = @"ring"; if(self.isReceiveCall){ //呼入 music = @"ring"; //隐藏 主动挂断按钮 self.activeHangupView.hidden = YES; self.hangupView.hidden = NO; self.acceptView.hidden = NO; }else{ //呼出 music = @"tones"; //显示 主动挂断按钮 self.activeHangupView.hidden = NO; self.hangupView.hidden = YES; self.acceptView.hidden = YES; } //如果是在后台的情况,不响铃 if([UIApplication sharedApplication].applicationState != UIApplicationStateBackground){ dispatch_async(dispatch_get_main_queue(), ^{ [self playRing:music]; }); } self.muteBtnView.hidden = YES; self.rightBtnView.hidden = YES; } -(void)updateUI:(NSNotification *)noti{ //振铃 self.timeLabel.hidden = YES; JCCallItem *activeCall = [self getActiveCall]; if (activeCall.state == JCCallStatePending) { } else if (activeCall.state == JCCallStateTalking) { //通话中 [self stopRing]; [JCManager.shared.mediaDevice startAudio]; // xLog(@"--------------------音频输出是否是扬声器 :JCManager.shared.mediaDevice.speakerOn : %d",JCManager.shared.mediaDevice.speakerOn) if(!self.callDurationTimer){ [self _startCallDurationTimer]; } self.timeLabel.hidden = NO; self.statusLabel.hidden = YES; //显示主动挂断 self.activeHangupView.hidden = NO; //隐藏 接听和被动挂断 self.hangupView.hidden = YES; self.acceptView.hidden = YES; //显示 静音 self.muteBtnView.hidden = NO; if (activeCall.video) { self.nameLabel.hidden = YES; self.headImageView.hidden = YES; if (_localCanvas == nil && activeCall.uploadVideoStreamSelf) { [self openLocationCarmeraViewWithFullSScreen:NO]; //小屏 //本地 显示View 添加手势 [self addGestureRecognizer:_localCanvas.videoView]; //为远程View添加手势 [self addGestureRecognizer:_remoteCanvas.videoView]; } else if (_localCanvas != nil && !activeCall.uploadVideoStreamSelf) { [JCManager.shared.mediaDevice stopVideo:_localCanvas]; [_localCanvas.videoView removeFromSuperview]; _localCanvas = nil; } if (_remoteCanvas == nil && activeCall.uploadVideoStreamOther) { [self openRemoteCarmeraViewWithRenderId:activeCall.renderId isFull:YES]; //为远程View添加手势 [self addGestureRecognizer:_remoteCanvas.videoView]; //本地 显示View 添加手势 [self addGestureRecognizer:_localCanvas.videoView]; } else if (_remoteCanvas != nil && !activeCall.uploadVideoStreamOther) { [JCManager.shared.mediaDevice stopVideo:_remoteCanvas]; [_remoteCanvas.videoView removeFromSuperview]; _remoteCanvas = nil; } //视频默认 启用扬声器 if(!JCManager.shared.mediaDevice.isSpeakerOn){ [JCManager.shared.mediaDevice enableSpeaker:YES]; } [self.rightBtn setImage:[UIImage imageNamed:@"Button_Camera"] forState:0]; [self.rightBtn setImage:[UIImage imageNamed:@"Button_Camera_active"] forState:UIControlStateSelected]; self.rightBtnDesLabel.text = @"转换摄像头"; }else{ self.nameLabel.hidden = NO; self.headImageView.hidden = NO; //音频默认 不启用扬声器 [self.rightBtn setImage:[UIImage imageNamed:@"sound"] forState:0]; [self.rightBtn setImage:[UIImage imageNamed:@"sound_selected"] forState:UIControlStateSelected]; self.rightBtnDesLabel.text = @"免提"; } //摄像头转换(视频) 语音(免提) self.rightBtnView.hidden = NO; } else if(activeCall.state == JCCallStateOk || activeCall.state == JCCallStateCancel || activeCall.state == JCCallStateCanceled){ xLog(@"状态改变: %ld",(long)activeCall.state); [UICommon MessageSuccessText:@"通话已结束" isImg:NO]; [JCManager.shared.mediaDevice stopAudio]; [JCManager.shared.mediaDevice stopCamera]; //通话正常结束 [self removeCanvas]; } } -(void)openLocationCarmeraViewWithFullSScreen:(BOOL)isFull{ if(!_localCanvas){ xLog(@"JCManager.shared.mediaDevice.camera : %@",JCManager.shared.mediaDevice.camera) _localCanvas = [JCManager.shared.mediaDevice startCameraVideo:JCMediaDeviceRenderFullScreen]; if(JCManager.shared.mediaDevice.camera.cameraType != JCMediaDeviceCameraTypeFront){ [JCManager.shared.mediaDevice switchCamera]; } [self.view insertSubview:_localCanvas.videoView aboveSubview:self.backView]; } if(isFull){ _localCanvas.videoView.frame = CGRectMake(0,0,ScreenWidth ,ScreenHeight); }else{ _localCanvas.videoView.frame = CGRectMake(ScreenWidth*(1-0.25)-25, 50, ScreenWidth * 0.25, ScreenHeight*0.25); } } -(void)openRemoteCarmeraViewWithRenderId:(NSString*)renderId isFull:(BOOL)isFull{ if(!_remoteCanvas){ _remoteCanvas = [JCManager.shared.mediaDevice startVideo:renderId renderType:JCMediaDeviceRenderFullContent]; // [_remoteCanvas rotate:0]; [self.view insertSubview:_remoteCanvas.videoView aboveSubview:self.backView]; } if(isFull){ _remoteCanvas.videoView.frame = CGRectMake(0,0,ScreenWidth ,ScreenHeight); //需要把自己的换成小屏的 _localCanvas.videoView.frame = CGRectMake(ScreenWidth*(1-0.25)-25, 50, ScreenWidth * 0.25, ScreenHeight*0.25); }else{ _remoteCanvas.videoView.frame = CGRectMake(ScreenWidth*(1-0.25)-25, 50, ScreenWidth * 0.25, ScreenHeight*0.25); _localCanvas.videoView.frame = CGRectMake(0,0,ScreenWidth ,ScreenHeight); } } -(void)removeCanvas { if (_localCanvas) { [JCManager.shared.mediaDevice stopVideo:_localCanvas]; [_localCanvas.videoView removeFromSuperview]; _localCanvas = nil; } if (_remoteCanvas) { [JCManager.shared.mediaDevice stopVideo:_remoteCanvas]; [_remoteCanvas.videoView removeFromSuperview]; _remoteCanvas = nil; } } //MARK: 为View 添加拖动和双击手势 -(void)addGestureRecognizer:(UIView*)view{ //添加拖动手势 UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePan:)]; [view addGestureRecognizer:panGestureRecognizer]; //双击手势 UITapGestureRecognizer *doubleTapGesture = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleDoubleTap:)]; doubleTapGesture.numberOfTapsRequired =2; doubleTapGesture.numberOfTouchesRequired =1; [view addGestureRecognizer:doubleTapGesture]; } //MARK: --- 拖动手势 ---- - (void)handlePan:(UIPanGestureRecognizer*)recognizer{ UIView *selectView = recognizer.view; if(selectView.bounds.size.width != ScreenWidth){ //不是全屏状态 // Figure out where the user is trying to drag the view. CGPoint translation = [recognizer translationInView:self.view]; CGPoint newCenter = CGPointMake(recognizer.view.center.x+ translation.x, recognizer.view.center.y + translation.y);// 限制屏幕范围: newCenter.y = MAX(recognizer.view.frame.size.height/2, newCenter.y); newCenter.y = MIN(self.view.frame.size.height - recognizer.view.frame.size.height/2, newCenter.y); newCenter.x = MAX(recognizer.view.frame.size.width/2, newCenter.x); newCenter.x = MIN(self.view.frame.size.width - recognizer.view.frame.size.width/2,newCenter.x); recognizer.view.center = newCenter; [recognizer setTranslation:CGPointZero inView:self.view]; } } //MARK: --- 双击放大---- -(void)handleDoubleTap:(UIGestureRecognizer *)recognizer{ UIView *selectView = recognizer.view; CGRect tempRect = selectView.frame; if(selectView.bounds.size.width != ScreenWidth){ if([_localCanvas.videoView isEqual:selectView]){ _localCanvas.videoView.frame = CGRectMake(0, 0, ScreenWidth, ScreenHeight); _remoteCanvas.videoView.frame = tempRect; [self.view exchangeSubviewAtIndex:_remoteCanvas.videoView.getInSuperViewIndex withSubviewAtIndex:_localCanvas.videoView.getInSuperViewIndex]; }else{ _remoteCanvas.videoView.frame = CGRectMake(0, 0, ScreenWidth, ScreenHeight); _localCanvas.videoView.frame = tempRect; [self.view exchangeSubviewAtIndex:_localCanvas.videoView.getInSuperViewIndex withSubviewAtIndex:_remoteCanvas.videoView.getInSuperViewIndex]; } } } /** MARK: APP 主动挂断按钮 */ - (IBAction)activeHangUpBtnAction:(id)sender { [self stopRing]; [JCManager.shared.call term:[self getActiveCall] reason:JCCallReasonNone description:@"APP挂断"]; [JCManager.shared _stopCallTimeoutTimer]; [JCManager.shared _stopVideoLimitTimeoutTimer]; [JCManager.shared.mediaDevice stopVideo:_localCanvas]; [JCManager.shared.mediaDevice stopVideo:_remoteCanvas]; JCManager.shared.callViewController = nil; [self dismissViewControllerAnimated:YES completion:nil]; } /** MARK: APP 被动挂断按钮 */ - (IBAction)hangUpButton:(UIButton *)sender { [self stopRing]; [JCManager.shared.call term:[self getActiveCall] reason:JCCallReasonNone description:@"APP挂断"]; [JCManager.shared _stopCallTimeoutTimer]; [JCManager.shared _stopVideoLimitTimeoutTimer]; [JCManager.shared.mediaDevice stopVideo:_localCanvas]; JCManager.shared.callViewController = nil; [self dismissViewControllerAnimated:YES completion:nil]; } /** MARK: APP 接听按钮 */ - (IBAction)acceptBtnAction:(id)sender { [self stopRing]; [[JCManager shared].call answer:[self getActiveCall] video:true]; } //MARK: 切换摄像头 / 切换免提 - (IBAction)didClickSwitchCameraButton:(UIButton *)sender { sender.selected = !sender.selected; if ([self.rightBtnDesLabel.text isEqualToString:@"免提"]) { // 切换免提 [JCManager.shared.mediaDevice enableSpeaker:sender.selected]; }else{ // 切换摄像头 [JCManager.shared.mediaDevice switchCamera]; } } //MARK: 静音 - (IBAction)didClickMuteButton:(UIButton *)sender { sender.selected = !sender.selected; //[JCManager.shared.call mute:[self getActiveCall]]; [JCManager.shared.call muteMicrophone:[self getActiveCall] mute:sender.selected]; } #pragma mark - 工具函数 -(JCCallItem*)getActiveCall { for (JCCallItem* item in JCManager.shared.call.callItems) { if (item.active) { return item; } } return nil; } - (NSString *)genCallInfo:(JCCallItem*)item { switch (item.state) { case JCCallStateInit: return @"呼叫中"; case JCCallStatePending: return @"振铃中"; case JCCallStateConnecting: return @"连接中"; case JCCallStateTalking: if (item.hold) { return @"挂起"; } else if (item.held) { return @"被挂起"; } else { return [self formatTalkingTime:((long)[[NSDate date] timeIntervalSince1970] - item.talkingBeginTime)]; } case JCCallStateOk: return @"通话结束"; case JCCallStateCancel: return @"通话结束"; case JCCallStateCanceled: return @"挂断"; case JCCallStateMissed: return @"未接"; default: return @"异常"; } } //- (NSString *)genNetStatus:(JCCallItem *)item { // if (item.state != JCCallStateTalking) { // return @""; // } // switch (item.netStatus) { // case JCCallNetWorkDisconnected: // return @"无网络"; // case JCCallNetWorkVeryBad: // return @"很差"; // case JCCallNetWorkBad: // return @"差"; // case JCCallNetWorkNormal: // return @"一般"; // case JCCallNetWorkGood: // return @"好"; // case JCCallNetWorkVeryGood: // return @"非常好"; // default: // return @""; // } //} //MARK: 响铃的 MP3 - (void)playRing:(NSString *)name { [self stopRing]; AVAudioSession *audioSession = [AVAudioSession sharedInstance]; [audioSession setCategory:AVAudioSessionCategoryPlayback error:nil]; [audioSession setActive:YES error:nil]; NSURL *path = [[NSBundle mainBundle] URLForResource:name withExtension:@"mp3"]; audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:path error:nil]; audioPlayer.numberOfLoops = -1; [audioPlayer play]; } - (void)stopRing { if (audioPlayer) { [audioPlayer stop]; audioPlayer = nil; } } #pragma mark - Timer - (void)_updateCallDuration { JCCallItem *item = [self getActiveCall]; if(item){ self.callDuration = [[NSDate date] timeIntervalSince1970] - item.talkingBeginTime; self.timeLabel.text = [self formatTalkingTime:((long)[[NSDate date] timeIntervalSince1970] - item.talkingBeginTime)]; } } - (NSString *)formatTalkingTime:(long)time { return [NSString stringWithFormat:@"%02ld:%02ld", time/60, time%60]; } - (void)_startCallDurationTimer { [self _stopCallDurationTimer]; self.callDuration = 0; self.callDurationTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(_updateCallDuration) userInfo:nil repeats:YES]; } - (void)_stopCallDurationTimer { if (self.callDurationTimer) { [self.callDurationTimer invalidate]; self.callDurationTimer = nil; } } @end