NewClassRoomViewController.m 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568
  1. //
  2. // NewClassRoomViewController.m
  3. // StudentDaya
  4. //
  5. // Created by Kyle on 2020/6/12.
  6. // Copyright © 2020 DayaMusic. All rights reserved.
  7. //
  8. #import "NewClassRoomViewController.h"
  9. #import "ClassTitleView.h"
  10. #import "ClassroomMainContainer.h"
  11. #import "ClassVideoListView.h"
  12. #import "KSNormalAlertView.h"
  13. // 聊天列表
  14. #import "LoginHelper.h"
  15. #import "ChatAreaView.h"
  16. #import "RTCService.h"
  17. #import "Classroom.h"
  18. #import "ClassroomService.h"
  19. #import <MBProgressHUD/MBProgressHUD.h>
  20. #import "AppDelegate.h"
  21. #import "KSWhiteboardControl.h"
  22. #import "UIDevice+TFDevice.h"
  23. #import "KSRemoteUserManager.h"
  24. #import "LocalRenderManager.h"
  25. #import "NSDate+Transform.h"
  26. #import "UIViewController+zhStatusBarStyle.h"
  27. #import "KSTipsView.h"
  28. #import "ClassroomTimerManager.h"
  29. #define TitleViewHeight (82)
  30. #define KSExamSongFileKey (@"KSDownloadAccompany")
  31. #define TTimeLableWidth (180)
  32. #define TTimeLableHeight (20)
  33. typedef NS_ENUM(NSInteger, ACCOMPANYACTION) {
  34. ACCOMPANYACTION_NONE, // 没有在播放
  35. ACCOMPANYACTION_SONG, // 播放原声
  36. ACCOMPANYACTION_ACCOMPANY, // 播放伴奏
  37. };
  38. @interface NewClassRoomViewController ()<ClassTitleViewDelegate, RCRTCRoomEventDelegate, ClassroomDelegate, ClassroomMainContainerDelegate, UIGestureRecognizerDelegate, RCRTCStatusReportDelegate, RTCServiceDelegate,KSWhiteboardControlDelegate, ClassVideoListViewDelegate,ClassroomTimerManagerDelegate>
  39. @property (nonatomic, strong) ClassTitleView *titleView;
  40. @property (nonatomic, strong) ClassroomMainContainer *containerView;
  41. @property (nonatomic, strong) ClassVideoListView *videoListView;
  42. @property (nonatomic, strong) ChatAreaView *chatAreaView;
  43. @property (nonatomic, strong) KSWhiteboardControl *wBoardCtrl;
  44. @property (nonatomic, strong) MBProgressHUD *hud;
  45. @property (nonatomic, strong) NSMutableArray<NSNumber*>* packetLossStore;
  46. @property (nonatomic, strong) UILabel *networkLabel;
  47. @property (nonatomic, assign) BOOL cancleAlert;
  48. @property (nonatomic, assign) BOOL isQuitRoom; // 是否正在退出房间
  49. @property (nonatomic, assign) BOOL hasShowAlert;
  50. @property (nonatomic, assign) BOOL isSwitchLine; // 是否线路切换
  51. @property (nonatomic, assign) BOOL isChat;
  52. /** 定时器 */
  53. @property (nonatomic, strong) NSTimer *timer;
  54. /** 定时器多少秒循环一次 */
  55. @property (nonatomic, assign) float timerLength;
  56. /** 当前是否播放状态 */
  57. @property (nonatomic, assign) BOOL isPlay;
  58. /** 当前发出的声次数(用来判断播放"嘀"还是"咚"声音) */
  59. @property (nonatomic, assign) int currentTotalNo;
  60. /** 节拍类型 */
  61. @property (nonatomic, assign) KSMetronomeType metronomeStat;
  62. /** 播放速率,范围40~240 */
  63. @property (nonatomic, assign) int rate;
  64. // 播放节拍器音量
  65. @property (nonatomic, assign) NSInteger volume;
  66. /** 记录上一次的频率 */
  67. @property (nonatomic, assign) int lastRate;
  68. @property (nonatomic, strong) NSString *pathDing;
  69. @property (nonatomic, strong) NSString *pathDong;
  70. @property (nonatomic, strong) KSTipsView *tipsView;
  71. @property (nonatomic, strong) ClassroomTimerManager *timeManager;
  72. @property (nonatomic, strong) UILabel *timeLabel;
  73. @property (nonatomic, strong) KSNormalAlertView *alertView;
  74. @end
  75. @implementation NewClassRoomViewController
  76. #pragma mark - Life cycle
  77. - (void)viewWillAppear:(BOOL)animated {
  78. [super viewWillAppear:animated];
  79. self.zh_statusBarHidden = YES;
  80. // 切换到横屏
  81. AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
  82. delegate.allowAutoRotate = YES;
  83. [UIDevice switchNewOrientation:UIInterfaceOrientationLandscapeRight];
  84. [[UIApplication sharedApplication] setIdleTimerDisabled:YES];
  85. [RCRTCEngine sharedInstance].statusReportDelegate = self;
  86. }
  87. - (void)viewWillDisappear:(BOOL)animated {
  88. [super viewWillDisappear:animated];
  89. if (_isPlay) {
  90. [self pause];
  91. [self removeAll];
  92. }
  93. // 竖屏
  94. AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
  95. delegate.allowAutoRotate = NO;
  96. [UIDevice switchNewOrientation:UIInterfaceOrientationPortrait];
  97. [[UIApplication sharedApplication] setIdleTimerDisabled:NO];
  98. self.zh_statusBarHidden = NO;
  99. [self hideAlertView];
  100. [self judgeShowAlert];
  101. }
  102. - (void)hideAlertView {
  103. if (self.alertView && self.alertView.isShow) {
  104. [self.alertView dismissAlertView];
  105. }
  106. }
  107. - (void)judgeShowAlert {
  108. if (self.cancleAlert || self.hasShowAlert) {
  109. return;
  110. }
  111. if (![NSString isEmptyString:self.classEndTime]) {
  112. NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
  113. dateFormatter.timeZone = [NSTimeZone systemTimeZone];
  114. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  115. NSDate *endDate = [dateFormatter dateFromString:self.classEndTime];
  116. NSDate *currentDate = [NSDate date];
  117. NSTimeInterval timeInterval = [currentDate timeIntervalSinceDate:endDate];
  118. if (timeInterval >= 0) { // 课程结束弹出评价
  119. self.hasShowAlert = YES;
  120. [[NSNotificationCenter defaultCenter] postNotificationName:@"showEvaluateAlert" object:self.courseId];
  121. }
  122. }
  123. }
  124. - (void)viewDidLoad {
  125. [super viewDidLoad];
  126. // Do any additional setup after loading the view.
  127. self.view.backgroundColor = HexRGB(0x35343e);
  128. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(leaveRoomAction) name:@"classroomLogout" object:nil];
  129. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(quitRoomAction) name:@"classroomQuit" object:nil];
  130. // 切换到横屏
  131. AppDelegate* delegate = (AppDelegate*)[UIApplication sharedApplication].delegate;
  132. delegate.allowAutoRotate = YES;
  133. [UIDevice switchNewOrientation:UIInterfaceOrientationLandscapeRight];
  134. [self addSubviews];
  135. [self bindDelegates];
  136. [self publishStream];
  137. [self renderMainContainerView];
  138. self.timeManager.isDisplay = YES;
  139. [[RTCService sharedInstance] useSpeaker:YES];
  140. UITapGestureRecognizer *tapGes = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapGesture:)];
  141. [self.view addGestureRecognizer:tapGes];
  142. tapGes.delegate = self;
  143. [self showRoleHud];
  144. self.isQuitRoom = NO;
  145. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(backgroundQuitRoomAction) name:@"backgroundQuit" object:nil];
  146. }
  147. - (void)backgroundQuitRoomAction {
  148. self.isQuitRoom = YES;
  149. self.isSwitchLine = NO;
  150. [[LoginHelper sharedInstance] logout:^{
  151. } error:^(RCRTCCode code) {
  152. }];
  153. self.cancleAlert = YES;
  154. }
  155. #pragma mark ----- 账号异地登陆
  156. - (void)leaveRoomAction {
  157. [self showMessage:@"该账号在其他设备上登录"];
  158. self.isQuitRoom = YES;
  159. self.isSwitchLine = NO;
  160. [[LoginHelper sharedInstance] logout:^{
  161. } error:^(RCRTCCode code) {
  162. }];
  163. self.cancleAlert = YES;
  164. [[NSNotificationCenter defaultCenter] postNotificationName:@"backLoginView" object:nil];
  165. }
  166. - (void)quitRoomAction {
  167. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  168. self.isQuitRoom = YES;
  169. self.isSwitchLine = NO;
  170. [[LoginHelper sharedInstance] logout:^{
  171. } error:^(RCRTCCode code) {
  172. }];
  173. self.cancleAlert = YES;
  174. [[NSNotificationCenter defaultCenter] postNotificationName:@"showOtherLoginAlert" object:nil];
  175. });
  176. }
  177. // 提示信息
  178. - (void)showMessage:(NSString *)message {
  179. MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
  180. [self.view bringSubviewToFront:hud];
  181. hud.removeFromSuperViewOnHide =YES;
  182. hud.mode = MBProgressHUDModeText;
  183. hud.label.text = message;
  184. hud.minSize = CGSizeMake(132.f, 60.0f);
  185. hud.label.textColor = [UIColor whiteColor];
  186. hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
  187. hud.bezelView.backgroundColor = [UIColor colorWithHexString:@"#000000" alpha:0.8];
  188. [hud hideAnimated:YES afterDelay:2];
  189. }
  190. - (void)tapGesture: (UITapGestureRecognizer *)tapGesture {
  191. self.titleView.isDisplay = YES;
  192. self.timeManager.isDisplay = YES;
  193. [self hideChatAreaView];
  194. }
  195. -(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch{
  196. if ([touch.view isDescendantOfView:self.chatAreaView] || [touch.view isDescendantOfView:self.videoListView]) {
  197. return NO;
  198. }
  199. if ([touch.view isDescendantOfView:self.wBoardCtrl.wbView] || [touch.view isDescendantOfView:self.containerView]) {
  200. [self tapGesture:(UITapGestureRecognizer *)gestureRecognizer];
  201. }
  202. return YES;
  203. }
  204. - (void)viewDidAppear:(BOOL)animated {
  205. [super viewDidAppear:animated];
  206. }
  207. - (void)showRoleHud {
  208. Role role = [ClassroomService sharedService].currentRoom.currentMember.role;
  209. if(role == RoleAudience) {
  210. [self.tipsView showTipsMessage:@"你当前身份是旁听人,其他人看/听不见你" inView:self.view];
  211. [self performSelector:@selector(showOnlyYouHUD) withObject:nil afterDelay:5.0f];
  212. }else{
  213. [self showOnlyYouHUD];
  214. }
  215. }
  216. - (void)showOnlyYouHUD {
  217. if ([ClassroomService sharedService].currentRoom.memberList.count == 1) {
  218. [self.tipsView showTipsMessage:@"当前课堂只有你一人,你可以等待或离开" inView:self.view];
  219. }
  220. }
  221. #pragma mark --- ClassTitleViewDelegate
  222. - (void)classTitleView:(UIButton *)button didTapAtTag:(ClassTitleViewActionTag)tag {
  223. [self.chatAreaView.inputBarControl setInputBarStatus:InputBarControlStatusDefault];
  224. switch (tag) {
  225. case ClassTitleViewActionTagMicrophone:
  226. {
  227. if (button.selected == NO) {
  228. if (self.alertView && self.alertView.isShow) {
  229. [self.alertView dismissAlertView];
  230. }
  231. self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"确认关闭麦克风吗?" leftTitle:@"取消" rightTitle:@"确认" cancel:^{
  232. } confirm:^{
  233. button.selected = YES;
  234. [[ClassroomService sharedService] enableDevice:NO withType:DeviceTypeMicrophone];
  235. }];
  236. }
  237. else {
  238. button.selected = NO;
  239. [[ClassroomService sharedService] enableDevice:YES withType:DeviceTypeMicrophone];
  240. }
  241. }
  242. break;
  243. case ClassTitleViewActionTagCamera:
  244. {
  245. if (button.selected == NO) {
  246. if (self.alertView && self.alertView.isShow) {
  247. [self.alertView dismissAlertView];
  248. }
  249. self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"确认关闭摄像头吗?" leftTitle:@"取消" rightTitle:@"确认" cancel:^{
  250. } confirm:^{
  251. button.selected = YES;
  252. [[ClassroomService sharedService] enableDevice:NO withType:DeviceTypeCamera];
  253. }];
  254. }
  255. else {
  256. button.selected = NO;
  257. [[ClassroomService sharedService] enableDevice:YES withType:DeviceTypeCamera];
  258. }
  259. }
  260. break;
  261. case ClassTitleViewActionTagSwitchLine: // 切换线路
  262. {
  263. MJWeakSelf;
  264. if (self.alertView && self.alertView.isShow) {
  265. [self.alertView dismissAlertView];
  266. }
  267. self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"确认切换线路吗?" leftTitle:@"取消" rightTitle:@"确认" cancel:^{
  268. } confirm:^{
  269. [weakSelf restartRoom];
  270. }];
  271. }
  272. break;
  273. case ClassTitleViewActionTagSwitchCamera:
  274. {
  275. [[RTCService sharedInstance] switchCamera];
  276. }
  277. break;
  278. case ClassTitleViewActionTagChat:
  279. {
  280. button.selected = !button.selected;
  281. button.selected ? [self showChatAreaView] : [self hideChatAreaView];
  282. self.isChat = button.selected;
  283. }
  284. break;
  285. case ClassTitleViewActionTagHangup:
  286. {
  287. MJWeakSelf;
  288. if (self.alertView && self.alertView.isShow) {
  289. [self.alertView dismissAlertView];
  290. }
  291. self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"确认退出课堂吗?" leftTitle:@"取消" rightTitle:@"确认" cancel:^{
  292. } confirm:^{
  293. weakSelf.isQuitRoom = YES;
  294. SealClassLog(@"ActionTagHangup");
  295. weakSelf.hud = [self createProgressHUD];
  296. [[LoginHelper sharedInstance] logout:^{
  297. } error:^(RCRTCCode code) {
  298. }];
  299. }];
  300. }
  301. break;
  302. default:
  303. break;
  304. }
  305. }
  306. #pragma mark ---- ClassroomTimerManager delegate
  307. - (void)formatTime:(NSString *)timeString {
  308. self.timeLabel.text = timeString;
  309. }
  310. - (void)timeLabelAddAnimation:(CABasicAnimation *)animation {
  311. self.timeLabel.textColor = HexRGB(0x2DC7AA);
  312. [self.timeLabel.layer addAnimation:animation forKey:nil];
  313. }
  314. - (void)hiddenTitleView {
  315. [self.titleView hiddenView];
  316. }
  317. - (void)quitClassroomNotifer {
  318. // 课程结束后15分钟自动退出房间 连堂课不退出(对内)
  319. if (self.isQuitRoom == NO) {
  320. self.isQuitRoom = YES;
  321. // 退出房间
  322. [self.tipsView showTipsMessage:@"课程已结束" inView:self.view];
  323. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  324. // 课程结束退出房间
  325. SealClassLog(@"ClassEnd!");
  326. self.hud = [self createProgressHUD];
  327. [[LoginHelper sharedInstance] logout:^{
  328. } error:^(RCRTCCode code) {
  329. }];
  330. });
  331. }
  332. }
  333. #pragma mark ------ 退出RTC房间并重新加入
  334. - (void)restartRoom {
  335. self.isSwitchLine = YES;
  336. if (_isPlay) {
  337. [self pause];
  338. [self removeAll];
  339. }
  340. self.hud = [self createProgressHUD];
  341. [self.wBoardCtrl.wbView leaveRoom];
  342. [[LoginHelper sharedInstance] logout:^{
  343. } error:^(RCRTCCode code) {
  344. }];
  345. }
  346. - (void)reJoinRTCRoom {
  347. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  348. NSString *roomId = [LoginHelper sharedInstance].roomId;
  349. NSString *userName = [LoginHelper sharedInstance].userName;
  350. [[ClassroomService sharedService] joinClassroom:roomId userName:userName isAudience:NO success:^(Classroom * _Nonnull classroom) {
  351. self.isSwitchLine = NO;
  352. [self restartRTCConnection];
  353. } error:^(ErrorCode errorCode, NSString * _Nonnull message) {
  354. [self.tipsView showTipsMessage:@"切换线路失败!" inView:self.view];
  355. [self.hud hideAnimated:YES];
  356. self.isSwitchLine = NO;
  357. }];
  358. });
  359. }
  360. - (void)restartRTCConnection {
  361. [[RTCService sharedInstance] joinRongRTCRoom:[ClassroomService sharedService].currentRoom.roomId success:^(RCRTCRoom * _Nonnull room) {
  362. [self.hud hideAnimated:YES];
  363. [self.tipsView showTipsMessage:@"切换线路成功" inView:self.view];
  364. // 重置UI 重新发布音视频流
  365. [LocalRenderManager shareInstance].hadRenderMainView = NO;
  366. self.timeManager.isDisplay = YES;
  367. [self publishStream];
  368. [self bindDelegates];
  369. [self.titleView refreshTitleView];
  370. [self.containerView refreshToolView];
  371. [self.hud hideAnimated:YES];
  372. [RCRTCEngine sharedInstance].statusReportDelegate = self;
  373. [[RTCService sharedInstance] useSpeaker:YES];
  374. [self renderMainContainerView];
  375. [self.videoListView reloadVideoList];
  376. } error:^(RCRTCCode code) {
  377. [self.hud hideAnimated:YES];
  378. [self.tipsView showTipsMessage:@"切换线路失败!" inView:self.view];
  379. }];
  380. }
  381. #pragma mark - ClassroomDelegate
  382. - (void)roomDidLeave {
  383. NSLog(@"roomDidLeave");
  384. if (_isSwitchLine) {
  385. if ([RTCService sharedInstance].rtcRoom) {
  386. [[KSRemoteUserManager shareInstance] removeAllUser];
  387. [[RTCService sharedInstance] leaveRongRTCRoom:[ClassroomService sharedService].currentRoom.roomId success:^{
  388. [self reJoinRTCRoom];
  389. } error:^(RCRTCCode code) {
  390. [self.hud hideAnimated:YES];
  391. [self.tipsView showTipsMessage:@"切换线路失败!" inView:self.view];
  392. self.isSwitchLine = NO;
  393. }];
  394. }
  395. }
  396. else {
  397. [[KSRemoteUserManager shareInstance] removeAllUser];
  398. [[RCIMClient sharedRCIMClient] clearMessagesUnreadStatus:ConversationType_GROUP targetId:[ClassroomService sharedService].currentRoom.roomId];
  399. int unreadMsgCount = [[RCIMClient sharedRCIMClient] getUnreadCount:@[
  400. @(ConversationType_PRIVATE), @(ConversationType_APPSERVICE), @(ConversationType_GROUP),@(ConversationType_SYSTEM)
  401. ]];
  402. if (unreadMsgCount >= 1) {
  403. [(KSTabBarViewController *)self.tabBarController noteNewsWithIndex:2 count:unreadMsgCount];
  404. } else {
  405. [(KSTabBarViewController *)self.tabBarController clearNewsWithIndex:2];
  406. }
  407. // 关闭白板连接
  408. [self.wBoardCtrl.wbView.sdk setCommonCallbackDelegate:nil];
  409. [self.wBoardCtrl.wbView.room disconnect:^{
  410. }];
  411. if ([RTCService sharedInstance].rtcRoom) {
  412. [[RTCService sharedInstance] leaveRongRTCRoom:[ClassroomService sharedService].currentRoom.roomId success:^{
  413. } error:^(RCRTCCode code) {
  414. }];
  415. }
  416. [self dismissClassroom];
  417. }
  418. }
  419. - (void)dismissClassroom {
  420. [self.hud hideAnimated:YES];
  421. [ClassroomService sharedService].currentRoom = nil;
  422. [self dismissViewControllerAnimated:NO completion:^{
  423. [self.timeManager stopDurationTimer];
  424. [self.timeLabel.layer removeAllAnimations];
  425. }];
  426. }
  427. - (void)memberDidJoin:(RoomMember *)member {
  428. NSLog(@"memberDidJoin %@",member);
  429. if(member.role != RoleAudience) {
  430. [self.videoListView reloadVideoList];
  431. }
  432. if (member.role == RoleTeacher) {
  433. [self.tipsView showTipsMessage:[NSString stringWithFormat:@"老师%@已经进入教室", member.name] inView:self.view];
  434. if (_isPlay) {
  435. [self pause];
  436. [self removeAll];
  437. [[RTCService sharedInstance] playAllRemoteUserAudioStream];
  438. }
  439. if ([ClassroomService sharedService].currentRoom.isPlaySong) {
  440. [[RCRTCAudioMixer sharedInstance] stop];
  441. [ClassroomService sharedService].currentRoom.isPlaySong = NO;
  442. [[RTCService sharedInstance] playAllRemoteUserAudioStream];
  443. }
  444. }
  445. }
  446. - (void)memberDidLeave:(RoomMember *)member {
  447. NSLog(@"memberDidLeave %@",member);
  448. // 此处移除
  449. [[KSRemoteUserManager shareInstance] removeUser:member.userId];
  450. if(member.role != RoleAudience) {
  451. [self.videoListView reloadVideoList];
  452. }
  453. if (member.role == RoleTeacher) {
  454. // 老师退出房间后,学生如果在节拍器播放时停止节拍器
  455. if (_isPlay) {
  456. [self pause];
  457. [self removeAll];
  458. [[RTCService sharedInstance] playAllRemoteUserAudioStream];
  459. }
  460. if ([ClassroomService sharedService].currentRoom.isPlaySong) {
  461. [[RCRTCAudioMixer sharedInstance] stop];
  462. [ClassroomService sharedService].currentRoom.isPlaySong = NO;
  463. [[RTCService sharedInstance] playAllRemoteUserAudioStream];
  464. }
  465. }
  466. }
  467. - (void)memberDidKick:(RoomMember *)member {
  468. NSLog(@"memberDidKick %@",member);
  469. [[KSRemoteUserManager shareInstance] removeUser:member.userId];
  470. if ([ClassroomService sharedService].currentRoom.currentMember.role == RoleTeacher) {
  471. [self.tipsView showTipsMessage:[NSString stringWithFormat:@"你已将%@移出课堂", member.name] inView:self.view];
  472. }
  473. [self.videoListView reloadVideoList];
  474. if (self.containerView.member.role == member.role) {
  475. [self.containerView cancelRenderView];
  476. }
  477. }
  478. - (void)errorDidOccur:(ErrorCode)code {
  479. NSLog(@"errorDidOccur %@",@(code));
  480. [self.hud hideAnimated:YES];
  481. if (code != ErrorCodeSuccess) {
  482. if (code == ErrorCodeOverMaxUserCount) {
  483. [self.tipsView showTipsMessage:@"超过人数限制" inView:self.view];
  484. }else {
  485. [self.tipsView showTipsMessage:@"操作失败,请稍后再试" inView:self.view];
  486. }
  487. }
  488. }
  489. - (void)deviceDidEnable:(BOOL)enable type:(DeviceType)type forUser:(RoomMember *)member operator:(nonnull NSString *)operatorId {
  490. NSLog(@"deviceDidEnable devicetype:%@ enable:%@ memeber:%@",@(type),@(enable),member);
  491. RoomMember *curMember = [ClassroomService sharedService].currentRoom.currentMember;
  492. NSString *hudMessage = @"";
  493. //只有老师和自己才有提示
  494. if (curMember.role == RoleTeacher && ![curMember.userId isEqualToString:operatorId]) {
  495. if (type == DeviceTypeCamera) {
  496. hudMessage = !enable ? [NSString stringWithFormat:NSLocalizedStringFromTable(@"SetCameraClose", @"SealClass", nil),member.name] : [NSString stringWithFormat:NSLocalizedStringFromTable(@"SetCameraOpen", @"SealClass", nil),member.name];
  497. } else if (type == DeviceTypeMicrophone) {
  498. hudMessage = !enable ? [NSString stringWithFormat:NSLocalizedStringFromTable(@"SetMicorophoneClose", @"SealClass", nil),member.name] : [NSString stringWithFormat:NSLocalizedStringFromTable(@"SetMicorophoneOpen", @"SealClass", nil),member.name];
  499. }
  500. else if (type == DeviceTypeMusicMode) {
  501. hudMessage = !enable ? [NSString stringWithFormat:@"%@的音乐模式已关闭",member.name] : [NSString stringWithFormat:@"%@的音乐模式已开启",member.name];
  502. }
  503. [self.tipsView showTipsMessage:hudMessage inView:self.view];
  504. } else {
  505. if ([curMember.userId isEqualToString:member.userId]) {
  506. if (type == DeviceTypeCamera) { // 摄像头
  507. // 非自己操作才需要做提示
  508. if(![curMember.userId isEqualToString:operatorId]){
  509. hudMessage = !enable ? @"老师已关闭你的摄像头" : NSLocalizedStringFromTable(@"CameraOpend", @"SealClass", nil);
  510. [self.tipsView showTipsMessage:hudMessage inView:self.view];
  511. }
  512. [self.containerView refreshToolView];
  513. [[RTCService sharedInstance] setCameraDisable:!enable];
  514. } else if (type == DeviceTypeMicrophone) { // 麦克风
  515. // 非自己操作才需要做提示
  516. if(![curMember.userId isEqualToString:operatorId]){
  517. hudMessage = !enable ? @"老师已关闭你的麦克风" : NSLocalizedStringFromTable(@"MicorophoneOpend", @"SealClass", nil);
  518. [self.tipsView showTipsMessage:hudMessage inView:self.view];
  519. }
  520. [self.containerView refreshToolView];
  521. [[RTCService sharedInstance] setMicrophoneDisable:!enable];
  522. }
  523. else if (type == DeviceTypeMusicMode) {
  524. // 非自己操作才需要做提示
  525. if(![curMember.userId isEqualToString:operatorId]) {
  526. // hudMessage = !enable ? NSLocalizedStringFromTable(@"老师关闭了你的音乐模式", @"SealClass", nil) : NSLocalizedStringFromTable(@"音乐模式已打开", @"SealClass", nil);
  527. // [self.tipsView showTipsMessage:hudMessage inView:self.view];
  528. }
  529. [self.containerView refreshToolView];
  530. [self changeModeAction:enable];
  531. }
  532. else if (type == DeviceTypeHandup) {
  533. // if ([curMember.userId isEqualToString:operatorId]) {
  534. // if (enable) {
  535. // [self.tipsView showTipsMessage:@"您已举手" inView:self.view];
  536. // }
  537. // }
  538. }
  539. else if (type == DeviceTypeExamSong) {
  540. if (enable) {
  541. // 播放伴奏
  542. [self playSongAction];
  543. }
  544. else {
  545. // 停止伴奏
  546. [[RCRTCAudioMixer sharedInstance] stop];
  547. [self playSongEnable:NO];
  548. }
  549. }
  550. else if (type == DeviceTypeAccompany || type == DeviceTypeMusicScore) { // 原音或伴奏
  551. if (enable) {
  552. // 检测是否存在该曲目
  553. RoomMember *member = [ClassroomService sharedService].currentRoom.currentMember;
  554. NSString *songId = [NSString stringWithFormat:@"%d", member.musicScoreAccompanimentId];
  555. NSString *songUrl = member.url;
  556. NSString *accompnayUrl = member.mp3Url;
  557. // 播放伴奏
  558. [self checkAccompnayDownloadNotifer:songUrl accompanyUrl:accompnayUrl songId:songId needPlay:YES];
  559. }
  560. else {
  561. // 停止伴奏
  562. [[RCRTCAudioMixer sharedInstance] stop];
  563. [self playSongEnable:NO];
  564. }
  565. }
  566. }
  567. }
  568. if ([ClassroomService sharedService].currentRoom.currentDisplayType != DisplayWhiteboard && type == DeviceTypeCamera) {
  569. // 延迟调用,防止刷新出现灰屏
  570. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  571. [self renderMainContainerView];
  572. });
  573. }
  574. [self.titleView refreshTitleView];
  575. [self.containerView refreshToolView];
  576. }
  577. - (void)playSongAction {
  578. if ([ClassroomService sharedService].currentRoom.isPlaySong || [ClassroomService sharedService].currentRoom.isPlayBeat) {
  579. // 停止伴奏
  580. [[RCRTCAudioMixer sharedInstance] stop];
  581. }
  582. // 获取路径 播放伴奏
  583. RoomMember *member = [ClassroomService sharedService].currentRoom.currentMember;
  584. NSString *songId = [NSString stringWithFormat:@"%d", member.musicScoreAccompanimentId];
  585. NSDictionary *fileDic = UserDefaultObjectForKey(KSExamSongFileKey);
  586. NSDictionary *storeDic = [fileDic dictionaryValueForKey:songId];
  587. NSString *fileName = nil;
  588. NSString *filePath = nil;
  589. if (member.accompanimentPlayStatus) {
  590. fileName = [[storeDic dictionaryValueForKey:@"accompany"] stringValueForKey:@"fileName"];
  591. filePath = [self getFilePathWithName:fileName];
  592. }
  593. else if (member.playStatus) {
  594. fileName = [[storeDic dictionaryValueForKey:@"music"] stringValueForKey:@"fileName"];
  595. filePath = [self getFilePathWithName:fileName];
  596. }
  597. // 判断文件是否存在
  598. NSLog(@"-------songid %@", songId);
  599. NSLog(@"------filePath %@", filePath);
  600. NSFileManager *fileManager = [NSFileManager defaultManager];
  601. if ([fileManager fileExistsAtPath:filePath] && ![NSString isEmptyString:fileName]) {
  602. NSInteger soundVolome = [ClassroomService sharedService].currentRoom.soundVolume;
  603. NSLog(@"soundVolome------%zd",soundVolome);
  604. [[RCRTCAudioMixer sharedInstance] stop];
  605. @try {
  606. // 混音伴奏音量
  607. [RCRTCAudioMixer sharedInstance].mixingVolume = soundVolome;
  608. [RCRTCAudioMixer sharedInstance].playingVolume = soundVolome;
  609. [[RCRTCAudioMixer sharedInstance] startMixingWithURL:[NSURL fileURLWithPath:filePath] playback:YES mixerMode:RCRTCMixerModeMixing loopCount:NSUIntegerMax];
  610. } @catch (NSException *exception) {
  611. NSLog(@"%@", exception);
  612. } @finally {
  613. }
  614. }
  615. [self playSongEnable:YES];
  616. }
  617. - (void)playSongEnable:(BOOL)enable {
  618. NSString *teacherId = [ClassroomService sharedService].currentRoom.teacher.userId;
  619. if (enable) {
  620. if ([ClassroomService sharedService].currentRoom.isPlayBeat) {
  621. [self pause];
  622. [self removeAll];
  623. }
  624. [ClassroomService sharedService].currentRoom.isPlaySong = YES;
  625. [[RTCService sharedInstance] unPlayRemoteUserAudioStreamExcept:teacherId];
  626. }
  627. else {
  628. [ClassroomService sharedService].currentRoom.isPlaySong = NO;
  629. [[RTCService sharedInstance] playAllRemoteUserAudioStream];
  630. }
  631. }
  632. // 请求用户打开设备,助教关闭用户设备没有回调。
  633. - (void)deviceDidInviteEnable:(DeviceType)type ticket:(NSString *)ticket {
  634. NSLog(@"deviceDidInviteEnable devicetype:%@",@(type));
  635. if (type == DeviceTypeCamera) {
  636. if (self.alertView && self.alertView.isShow) {
  637. [self.alertView dismissAlertView];
  638. }
  639. self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"老师邀请你打开摄像头" leftTitle:@"拒绝" rightTitle:@"同意" cancel:^{
  640. [[ClassroomService sharedService] rejectEnableDevice:type];
  641. } confirm:^{
  642. [[ClassroomService sharedService] approveEnableDevice:type];
  643. }];
  644. }else if (type == DeviceTypeMicrophone) {
  645. [self.tipsView showTipsMessage:@"老师开启了你的麦克风" inView:self.view];
  646. [[ClassroomService sharedService] approveEnableDevice:type];
  647. }
  648. else if (type == DeviceTypeExamSong) { // 接口自动处理同意
  649. }
  650. else if (type == DeviceTypeAccompany || type == DeviceTypeMusicScore) { // 伴奏接口自动处理成同意
  651. }
  652. else {
  653. [[ClassroomService sharedService] approveEnableDevice:type];
  654. }
  655. }
  656. //只有助教能收到这个回调,可以不在这里处理文字,因为设备的回调还会走
  657. - (void)deviceInviteEnableDidApprove:(RoomMember *)member type:(DeviceType)type {
  658. NSLog(@"deviceInviteEnableDidApprove devicetype:%@ member:%@ ",@(type),member);
  659. }
  660. //拒绝只有助教能收到这个回调,且只走这个回调
  661. - (void)deviceInviteEnableDidReject:(RoomMember *)member type:(DeviceType)type {
  662. NSLog(@"deviceInviteEnableDidReject devicetype:%@ member:%@ ",@(type),member);
  663. [self.tipsView showTipsMessage:[NSString stringWithFormat:@"%@拒绝了你的邀请",member.name] inView:self.view];
  664. }
  665. #pragma mark ----- 伴奏下载
  666. - (void)accompanyDownloadCallback:(NSString *)songUrl accompanyUrl:(NSString *)accompnayUrl songId:(NSString *)songId {
  667. [self checkAccompnayDownloadNotifer:songUrl accompanyUrl:accompnayUrl songId:songId needPlay:NO];
  668. }
  669. - (void)checkAccompnayDownloadNotifer:(NSString *)songUrl accompanyUrl:(NSString *)accompnayUrl songId:(NSString *)songId needPlay:(BOOL)isNeedPlay {
  670. if ([self checkSongHasSave:songId songUrl:songUrl accompanyUrl:accompnayUrl]) { // 判断文件是否已下载
  671. [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:YES];
  672. if (isNeedPlay) {
  673. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  674. [self playSongAction];
  675. });
  676. }
  677. }
  678. else { // 如果不存在则去下载
  679. if (![NSString isEmptyString:songUrl]) {
  680. [self downloadSongAction:songUrl songId:songId isAccompany:NO success:^{
  681. if (![NSString isEmptyString:accompnayUrl]) {
  682. [self downloadSongAction:accompnayUrl songId:songId isAccompany:YES success:^{
  683. [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:YES];
  684. if (isNeedPlay) {
  685. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  686. [self playSongAction];
  687. });
  688. }
  689. } faliure:^{
  690. [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:NO];
  691. }];
  692. }
  693. else {
  694. [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:YES];
  695. if (isNeedPlay) {
  696. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  697. [self playSongAction];
  698. });
  699. }
  700. }
  701. } faliure:^{
  702. [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:NO];
  703. }];
  704. }
  705. else if (![NSString isEmptyString:accompnayUrl]){
  706. [self downloadSongAction:accompnayUrl songId:songId isAccompany:YES success:^{
  707. [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:YES];
  708. if (isNeedPlay) {
  709. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  710. [self playSongAction];
  711. });
  712. }
  713. } faliure:^{
  714. [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:NO];
  715. }];
  716. }
  717. else { // 原声和伴奏均为空
  718. [[ClassroomService sharedService] adjustAccompanySongNotifer:songId status:NO];
  719. }
  720. }
  721. }
  722. - (void)downloadSongAction:(NSString *)url songId:(NSString *)songId isAccompany:(BOOL)isAccompany success:(void(^)(void))success faliure:(void(^)(void))faliure {
  723. [KSNetworkingManager downloadFileRequestWithFileUrl:url progress:^(int64_t bytesRead, int64_t totalBytes) {
  724. NSLog(@"%.2f", bytesRead *1.0 / totalBytes);
  725. } success:^(NSURL * _Nonnull fileUrl) {
  726. if ([self saveUrlWithFilePath:fileUrl songUrl:url songId:songId isAccompany:isAccompany]) {
  727. success();
  728. }
  729. else {
  730. faliure();
  731. }
  732. } faliure:^(NSError * _Nonnull error) {
  733. faliure();
  734. }];
  735. }
  736. - (BOOL)saveUrlWithFilePath:(NSURL *)fileUrl songUrl:(NSString *)songUrl songId:(NSString *)songId isAccompany:(BOOL)isAccompany {
  737. NSData *sourceData = [NSData dataWithContentsOfURL:fileUrl];
  738. NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  739. NSString *filePath= [cachePath stringByAppendingPathComponent:@"AccompanySong"];
  740. // 先创建子目录
  741. NSFileManager *fileManager = [NSFileManager defaultManager];
  742. if (![fileManager fileExistsAtPath:filePath]) {
  743. [fileManager createDirectoryAtPath:filePath withIntermediateDirectories:YES attributes:nil error:nil];
  744. }else{
  745. NSLog(@"已创建文件夹");
  746. }
  747. NSString *fileName = [[songUrl componentsSeparatedByString:@"/"] lastObject];
  748. NSString *tempPath = [filePath stringByAppendingPathComponent:fileName];
  749. BOOL success = [sourceData writeToFile:tempPath atomically:NO];
  750. if (success) {
  751. NSDictionary *fileDic = UserDefaultObjectForKey(KSExamSongFileKey); // 只能存NSDictionary
  752. NSMutableDictionary *storeDic = [NSMutableDictionary dictionary];
  753. if (!fileDic) {
  754. fileDic = [NSMutableDictionary dictionary];
  755. }
  756. else {
  757. storeDic = [NSMutableDictionary dictionaryWithDictionary:[fileDic dictionaryValueForKey:songId]];
  758. }
  759. NSDictionary *parm = @{@"url" : songUrl,
  760. @"fileName" : fileName};
  761. NSString *keyString = isAccompany ? @"accompany" :@"music";
  762. [storeDic setValue:parm forKey:keyString];
  763. NSMutableDictionary *saveDic = [NSMutableDictionary dictionaryWithDictionary:fileDic];
  764. [saveDic setValue:storeDic forKey:songId];
  765. UserDefaultSetObjectForKey(saveDic, KSExamSongFileKey);
  766. }
  767. return success;
  768. }
  769. - (NSString *)getFilePathWithName:(NSString *)fileName {
  770. NSString *cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
  771. NSString *filePath= [cachePath stringByAppendingPathComponent:@"AccompanySong"];
  772. return [filePath stringByAppendingPathComponent:fileName];;
  773. }
  774. - (BOOL)checkSongHasSave:(NSString *)songId songUrl:(NSString *)songUrl accompanyUrl:(NSString *)accompanyUrl {
  775. BOOL hasSaveFile = NO;
  776. NSMutableDictionary *fileDic = UserDefaultObjectForKey(KSExamSongFileKey);
  777. NSDictionary *storeDic = [fileDic dictionaryValueForKey:songId];
  778. NSFileManager *fileManager = [NSFileManager defaultManager];
  779. if (![NSString isEmptyString:songUrl]) {
  780. NSString *fileName = [[storeDic dictionaryValueForKey:@"music"] stringValueForKey:@"fileName"];
  781. NSString *filePath = [self getFilePathWithName:fileName];
  782. if ([[fileDic allKeys] containsObject:songId] && [fileManager fileExistsAtPath:filePath] && ![NSString isEmptyString:fileName]) {
  783. hasSaveFile = YES;
  784. }
  785. else {
  786. hasSaveFile = NO;
  787. }
  788. }
  789. if (![NSString isEmptyString:accompanyUrl]) {
  790. NSString *fileName = [[storeDic dictionaryValueForKey:@"accompany"] stringValueForKey:@"fileName"];
  791. NSString *filePath = [self getFilePathWithName:fileName];
  792. if ([[fileDic allKeys] containsObject:songId] && [fileManager fileExistsAtPath:filePath] && ![NSString isEmptyString:fileName]) {
  793. hasSaveFile = YES;
  794. }
  795. else {
  796. hasSaveFile = NO;
  797. }
  798. }
  799. return hasSaveFile;
  800. }
  801. #pragma mark - private method
  802. - (void)bindDelegates {
  803. self.titleView.delegate = self;
  804. [[RTCService sharedInstance] setRTCRoomDelegate:self];
  805. [RTCService sharedInstance].delegate = self;
  806. [ClassroomService sharedService].classroomDelegate = self;
  807. }
  808. - (void)addSubviews {
  809. [self.view addSubview:self.containerView];
  810. [self.view addSubview:self.timeLabel];
  811. [self.containerView bringSubviewToFront:self.timeLabel];
  812. [self.view addSubview:self.titleView];
  813. [self wBoardCtrl];
  814. [self chatAreaView];
  815. [self.view addSubview:self.videoListView];
  816. [self.view bringSubviewToFront:self.titleView];
  817. }
  818. - (void)showChatAreaView{
  819. [self.view addSubview:self.chatAreaView];
  820. }
  821. - (void)hideChatAreaView{
  822. [UIView animateWithDuration:0.2 animations:^{
  823. self.titleView.chatBtn.selected = NO;
  824. [self.chatAreaView removeFromSuperview];
  825. }];
  826. }
  827. - (CGRect)mainContainerViewFrame {
  828. CGFloat x = iPhoneXSafeTopMargin;
  829. CGFloat y = 0;
  830. CGFloat width = UIScreenWidth - x - iPhoneXSafeBottomMargin;
  831. CGFloat height = UIScreenHeight - y;
  832. return CGRectMake(x, y, width, height);
  833. }
  834. - (void)displayWhiteboard:(NSString *)boardId {
  835. CGRect frame = [self getWBoardFrame];
  836. [self.wBoardCtrl loadWBoardWithFrame:frame name:boardId];
  837. for (UIView * view in self.view.subviews) {
  838. if ([view isKindOfClass:[ChatAreaView class]]) {
  839. [self.view bringSubviewToFront:view];
  840. }
  841. }
  842. [self.view bringSubviewToFront:self.timeLabel];
  843. }
  844. - (CGRect)getWBoardFrame {
  845. CGRect mainVideoFrame = self.containerView.currentVideoFrame;
  846. mainVideoFrame.origin.x += iPhoneXSafeTopMargin;
  847. mainVideoFrame.origin.y = 10;
  848. return mainVideoFrame;
  849. }
  850. - (void)publishStream {
  851. if ([ClassroomService sharedService].currentRoom.currentMember.role != RoleAudience) {
  852. [[RTCService sharedInstance] publishLocalUserDefaultAVStream];
  853. }
  854. }
  855. - (void)renderMainContainerView {
  856. self.titleView.onShowLabel.hidden = YES;
  857. RoomMember *teacher = [ClassroomService sharedService].currentRoom.teacher;
  858. if ([ClassroomService sharedService].currentRoom.currentDisplayType == DisplayTypeStudent) {
  859. // 将之前订阅的流切换成小流
  860. [self changeLastDisplayToTinyStream];
  861. // 有打开的屏幕
  862. for (RoomMember *displayMember in [ClassroomService sharedService].currentRoom.memberList) {
  863. if ([displayMember.userId isEqualToString:[ClassroomService sharedService].currentRoom.currentDisplayURI]) {
  864. if ([displayMember.userId isEqualToString:[ClassroomService sharedService].currentRoom.currentMember.userId]) {
  865. self.titleView.onShowLabel.hidden = NO;
  866. // 重置举手状态
  867. [ClassroomService sharedService].currentRoom.currentMember.handUpOn = NO;
  868. [self.containerView refreshToolView];
  869. }
  870. [self.wBoardCtrl hideBoard];
  871. [self.containerView containerViewRenderView:displayMember];
  872. [self.videoListView showTeacherPrompt:NO];
  873. break;
  874. }
  875. }
  876. }
  877. else {
  878. // 将之前订阅的流切换成小流
  879. [self changeLastDisplayToTinyStream];
  880. if (([ClassroomService sharedService].currentRoom.currentDisplayType == DisplayTeacher && teacher.cameraEnable)) {
  881. NSLog(@"%@",[ClassroomService sharedService].currentRoom.currentDisplayURI);
  882. [self.wBoardCtrl hideBoard];
  883. [self.containerView containerViewRenderView:[ClassroomService sharedService].currentRoom.teacher];
  884. [self.videoListView showTeacherPrompt:YES];
  885. } else if ([ClassroomService sharedService].currentRoom.currentDisplayType == DisplayWhiteboard) {
  886. [self.containerView cancelRenderView];
  887. [self displayWhiteboard:[ClassroomService sharedService].currentRoom.currentDisplayURI];
  888. [self.videoListView showTeacherPrompt:NO];
  889. } else if (([ClassroomService sharedService].currentRoom.currentDisplayType == DisplaySharedScreen)) {
  890. [self.wBoardCtrl hideBoard];
  891. [[RTCService sharedInstance] renderUserSharedScreenOnView:self.containerView.videoView forUser:[ClassroomService sharedService].currentRoom.currentDisplayURI];
  892. // 分享视频
  893. [self.videoListView showTeacherPrompt:NO];
  894. } else {
  895. [self.wBoardCtrl hideBoard];
  896. [self.containerView cancelRenderView];
  897. }
  898. }
  899. }
  900. - (void)changeLastDisplayToTinyStream {
  901. RoomMember *lastMember = self.containerView.member;
  902. if (![NSString isEmptyString:lastMember.userId] && ![lastMember.userId isEqualToString:[ClassroomService sharedService].currentRoom.currentDisplayURI] && ![lastMember.userId isEqualToString:[ClassroomService sharedService].currentRoom.currentMember.userId]) {
  903. [[RTCService sharedInstance] exchangeRemoteUserAVStreamToTinySteam:lastMember.userId callback:^{
  904. }];
  905. }
  906. }
  907. - (void)whiteboardDidDisplay:(NSString *)boardId {
  908. NSLog(@"whiteboardDidDisplay %@ ",boardId);
  909. [self renderMainContainerView];
  910. }
  911. - (void)sharedScreenDidDisplay:(NSString *)userId {
  912. NSLog(@"sharedScreenDidDisplay %@ ",userId);
  913. [self renderMainContainerView];
  914. }
  915. - (void)studentDidDisplay:(NSString *)studentId {
  916. NSLog(@"studentDidDisplay %@", studentId);
  917. [self renderMainContainerView];
  918. }
  919. - (void)teacherDidDisplay {
  920. NSLog(@"teacherDidDisplay %@ ",[ClassroomService sharedService].currentRoom.teacher.userId);
  921. [self renderMainContainerView];
  922. }
  923. - (void)noneDidDisplay {
  924. NSLog(@"noneDidDisplay");
  925. [self renderMainContainerView];
  926. }
  927. - (void)checkNodePlayAndSongDownload {
  928. RoomMember *currentMember = [ClassroomService sharedService].currentRoom.currentMember;
  929. if (currentMember.enable) {
  930. // 延迟播放
  931. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  932. [self playNode:currentMember.enable rate:currentMember.rate beatType:currentMember.customType volume:currentMember.volume];
  933. });
  934. }
  935. else {
  936. if (currentMember.musicScoreAccompanimentId != 0) { // 如果有记录当前播放曲目
  937. BOOL needPlay = currentMember.accompanimentPlayStatus || currentMember.playStatus;
  938. [self checkAccompnayDownloadNotifer:currentMember.url accompanyUrl:currentMember.mp3Url songId:[NSString stringWithFormat:@"%d", currentMember.musicScoreAccompanimentId] needPlay:needPlay];
  939. }
  940. }
  941. }
  942. // 播放节拍器的回调
  943. - (void)playNodeAction:(BOOL)enable userId:(NSString *)userId rate:(int)rate beatType:(BeatType)type volume:(NSInteger)volume {
  944. NSLog(@"playNodeAction userId:%@ enable:%@ beatType:%@ rate:%@ volume:%@",userId,@(enable),@(type), @(rate), @(volume));
  945. RoomMember *curMember = [ClassroomService sharedService].currentRoom.currentMember;
  946. if (curMember.role == RoleStudent) {
  947. if ([NSString isEmptyString:userId]) { // 操作全部
  948. [self playNode:enable rate:rate beatType:type volume:volume];
  949. }
  950. else {
  951. NSArray *userIdArray = [userId componentsSeparatedByString:@","];
  952. for (NSString *subUserId in userIdArray) {
  953. if ([curMember.userId isEqualToString:subUserId]) { // 操作个人
  954. [self playNode:enable rate:rate beatType:type volume:volume];
  955. break;
  956. }
  957. }
  958. }
  959. }
  960. }
  961. - (void)playNode:(BOOL)enable rate:(int)rate beatType:(BeatType)type volume:(NSInteger)volume {
  962. NSString *teacherId = [ClassroomService sharedService].currentRoom.teacher.userId;
  963. if (enable) {
  964. if ([ClassroomService sharedService].currentRoom.isPlaySong) { // 停止当前伴奏播放
  965. [ClassroomService sharedService].currentRoom.isPlaySong = NO;
  966. [[RCRTCAudioMixer sharedInstance] stop];
  967. }
  968. [self setMetronomeStat:type];
  969. [self setRate:rate];
  970. self.volume = volume;
  971. [self.videoListView updateBeatNode:type beat:rate];
  972. [self play];
  973. [[RTCService sharedInstance] unPlayRemoteUserAudioStreamExcept:teacherId];
  974. }
  975. else {
  976. [self pause];
  977. [self removeAll];
  978. [[RTCService sharedInstance] playAllRemoteUserAudioStream];
  979. }
  980. }
  981. #pragma mark --- RTCServiceDelegate 自己发布流成功回调
  982. - (void)successPublishLocalStream {
  983. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  984. // 检测是否需要通知
  985. [self notiferTeacherRefreshSongStatus];
  986. [self checkNodePlayAndSongDownload];
  987. });
  988. }
  989. - (void)notiferTeacherRefreshSongStatus {
  990. NSArray *songArray = [ClassroomService sharedService].currentRoom.currentMember.songMessage;
  991. if (songArray.count) {
  992. [[ClassroomService sharedService] adjustAccompanySongNotifer:nil status:YES];
  993. }
  994. }
  995. #pragma mark - RongRTCRoomDelegate
  996. - (void)didPublishStreams:(NSArray <RCRTCInputStream *>*)streams {
  997. NSString *displayUserId;
  998. if (([ClassroomService sharedService].currentRoom.currentDisplayType == DisplayTeacher)) {
  999. displayUserId = [ClassroomService sharedService].currentRoom.teacher.userId;
  1000. }
  1001. else if ([ClassroomService sharedService].currentRoom.currentDisplayType == DisplayTypeStudent) {
  1002. displayUserId = [ClassroomService sharedService].currentRoom.currentDisplayURI;
  1003. }
  1004. // 当用户非正常退出的时候,如果再次进入需要重新订阅流
  1005. RCRTCInputStream *firstStream = [streams lastObject];
  1006. if ([[KSRemoteUserManager shareInstance] isContentUser:firstStream.userId]) {
  1007. [[KSRemoteUserManager shareInstance] removeUser:firstStream.userId];
  1008. }
  1009. for (RCRTCInputStream *stream in streams) {
  1010. if (stream.mediaType == RTCMediaTypeVideo) {
  1011. if ([stream.userId isEqualToString:displayUserId]) {
  1012. [self renderMainContainerView];
  1013. }
  1014. [self.videoListView updateUserVideo:stream.userId];
  1015. }
  1016. }
  1017. }
  1018. - (void)didConnectToStream:(RCRTCInputStream *)stream {
  1019. if (stream.streamId && stream.streamId.length > 0) {
  1020. NSLog(@"didConnectToStream userId:%@ streamID:%@",stream.userId,stream.streamId);
  1021. }
  1022. }
  1023. #pragma mark ----- ClassVideoListViewDelegate
  1024. - (void)videoListView:(ClassVideoListView *)view didTap:(RoomMember *)member {
  1025. self.titleView.isDisplay = YES;
  1026. self.timeManager.isDisplay = YES;
  1027. /*
  1028. // 将视频投到主屏幕上
  1029. NSString *currentDisplayUserId = member.userId;
  1030. NSString *lastDisplayUserId = [ClassroomService sharedService].currentRoom.currentDisplayURI;
  1031. if([currentDisplayUserId isEqualToString:lastDisplayUserId]) {
  1032. [self.videoListView updateUserVideo:lastDisplayUserId];
  1033. return;
  1034. }
  1035. DisplayType type = member.role == RoleTeacher ? DisplayTeacher : DisplayTypeStudent;
  1036. [ClassroomService sharedService].currentRoom.currentDisplayURI = currentDisplayUserId;
  1037. [ClassroomService sharedService].currentRoom.currentDisplayType = type;
  1038. // 如果之前有切换成大流的视频
  1039. if (![NSString isEmptyString:lastDisplayUserId] && ![lastDisplayUserId isEqualToString:[ClassroomService sharedService].currentRoom.currentMemberId]) {
  1040. [[RTCService sharedInstance] exchangeRemoteUserAVStreamToTinySteam:lastDisplayUserId];
  1041. }
  1042. if(![[ClassroomService sharedService].currentRoom.currentMemberId isEqualToString:member.userId]) {
  1043. // 记录当前播放音频的学生
  1044. [ClassroomService sharedService].currentRoom.currentPlayUserId = currentDisplayUserId;
  1045. [[RTCService sharedInstance] exchangeRemoteUserAVStreamToNomalSteam:member.userId];
  1046. }
  1047. [self renderMainContainerView];
  1048. */
  1049. }
  1050. #pragma mark - Getters & setters
  1051. - (ClassroomTimerManager *)timeManager {
  1052. if (!_timeManager) {
  1053. _timeManager = [[ClassroomTimerManager alloc] initWithDelegate:self];
  1054. _timeManager.autoClose = [ClassroomService sharedService].currentRoom.autoCloseFlag;
  1055. // 统一15分钟关闭
  1056. _timeManager.autoCloseNetworkRoomTime = [ClassroomService sharedService].currentRoom.autoCloseNetworkRoomTime;
  1057. }
  1058. return _timeManager;
  1059. }
  1060. - (UILabel *)timeLabel {
  1061. if (!_timeLabel) {
  1062. _timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(kScreenWidth - self.videoListView.width - 40 - TTimeLableWidth - iPhoneXSafeBottomMargin, 25, TTimeLableWidth, TTimeLableHeight)];
  1063. _timeLabel.font = [UIFont systemFontOfSize:16];
  1064. _timeLabel.textAlignment = NSTextAlignmentRight;
  1065. _timeLabel.textColor = HexRGB(0xFF5500);
  1066. }
  1067. return _timeLabel;
  1068. }
  1069. - (KSTipsView *)tipsView {
  1070. if (!_tipsView) {
  1071. _tipsView = [[KSTipsView alloc] initWithFrame:CGRectZero];
  1072. }
  1073. return _tipsView;
  1074. }
  1075. - (ClassTitleView *)titleView {
  1076. if (!_titleView) {
  1077. _titleView = [[ClassTitleView alloc] initWithFrame:CGRectMake(iPhoneXSafeTopMargin, 0, kScreenWidth - iPhoneXSafeTopMargin - iPhoneXSafeBottomMargin, TitleViewHeight)];
  1078. }
  1079. return _titleView;
  1080. }
  1081. - (ClassVideoListView *)videoListView {
  1082. if(!_videoListView) {
  1083. CGFloat width = 0.0f;
  1084. CGFloat height = 0.0f;
  1085. height = kScreenHeight - 10;
  1086. width = height / 3.0f * 4;
  1087. if (width > kScreenWidth - 160) {
  1088. width = kScreenWidth - 160;
  1089. }
  1090. _videoListView = [[ClassVideoListView alloc] initWithFrame:CGRectMake(width + iPhoneXSafeTopMargin + 5, 10, kScreenWidth - (width + 10 + iPhoneXSafeTopMargin) - iPhoneXSafeBottomMargin, height)];
  1091. _videoListView.delegate = self;
  1092. }
  1093. return _videoListView;
  1094. }
  1095. - (ClassroomMainContainer *)containerView {
  1096. if (!_containerView) {
  1097. _containerView = [[ClassroomMainContainer alloc] initWithFrame:[self mainContainerViewFrame]];
  1098. _containerView.delegate = self;
  1099. }
  1100. return _containerView;
  1101. }
  1102. - (ChatAreaView *)chatAreaView {
  1103. if(!_chatAreaView) {
  1104. _chatAreaView = [[ChatAreaView alloc] initWithFrame:CGRectMake(kScreenWidth - iPhoneXSafeBottomMargin - 300,0, 300, UIScreenHeight) conversationType:ConversationType_GROUP targetId:[ClassroomService sharedService].currentRoom.roomId];
  1105. }
  1106. return _chatAreaView;
  1107. }
  1108. - (KSWhiteboardControl *)wBoardCtrl {
  1109. if (!_wBoardCtrl) {
  1110. CGRect frame = [self getWBoardFrame];
  1111. _wBoardCtrl = [[KSWhiteboardControl alloc] initWithDelegate:self viewFrame:frame];
  1112. NSString *roomId = [ClassroomService sharedService].currentRoom.roomId;
  1113. // if ([roomId containsString:@"S"] || [roomId containsString:@"I"]) {
  1114. // roomId = [roomId substringFromIndex:1];
  1115. // }
  1116. _wBoardCtrl.roomId = roomId;
  1117. _wBoardCtrl.isRatationImage = self.isRorationImage;
  1118. [_wBoardCtrl moveToSuperView:self.view];
  1119. }
  1120. return _wBoardCtrl;
  1121. }
  1122. #pragma mark ---- 网络监测
  1123. - (void)didReportStatusForm:(RCRTCStatusForm *)form {
  1124. const CGFloat lossRateBase = 0.15;
  1125. if (self.packetLossStore.count + form.sendStats.count > 10) {
  1126. NSInteger diff = (self.packetLossStore.count + form.sendStats.count) - 10;
  1127. [self.packetLossStore removeObjectsInRange:NSMakeRange(0,diff)];
  1128. }
  1129. for (RCRTCStreamStat* stat in form.sendStats) {
  1130. [self.packetLossStore addObject:@(stat.packetLoss)];
  1131. }
  1132. float totalSum = 0;
  1133. for (NSNumber* lossRate in self.packetLossStore) {
  1134. totalSum += [lossRate floatValue];
  1135. }
  1136. if (totalSum > 0 && self.packetLossStore.count) {
  1137. float avgLoss = totalSum / self.packetLossStore.count;
  1138. [self showNetworkingStatus:avgLoss];
  1139. if (avgLoss > lossRateBase) { // 网络质量不佳
  1140. dispatch_async(dispatch_get_main_queue(), ^{
  1141. [self.view addSubview:self.networkLabel];
  1142. [self.view bringSubviewToFront:self.networkLabel];
  1143. self.networkLabel.hidden = NO;
  1144. self.networkLabel.text = @"当前通话连接质量不佳";
  1145. });
  1146. }
  1147. else { // 正常
  1148. dispatch_async(dispatch_get_main_queue(), ^{
  1149. self.networkLabel.hidden = YES;
  1150. self.networkLabel.text = @"";
  1151. [self.networkLabel removeFromSuperview];
  1152. });
  1153. }
  1154. }
  1155. }
  1156. - (void)showNetworkingStatus:(float)packageLose {
  1157. dispatch_async(dispatch_get_main_queue(), ^{
  1158. if (packageLose < 0.05) {
  1159. self.titleView.netStatus = NetWorkingStatus_Good;
  1160. }
  1161. else if (packageLose <= 0.1) {
  1162. self.titleView.netStatus = NetWorkingStatus_Well;
  1163. }
  1164. else if (packageLose <= 0.15) {
  1165. self.titleView.netStatus = NetWorkingStatus_Bad;
  1166. }
  1167. else {
  1168. self.titleView.netStatus = NetWorkingStatus_Poor;
  1169. }
  1170. });
  1171. }
  1172. - (NSMutableArray<NSNumber*>*)packetLossStore {
  1173. if (!_packetLossStore) {
  1174. _packetLossStore = [[NSMutableArray alloc] initWithCapacity:10];
  1175. }
  1176. return _packetLossStore;
  1177. }
  1178. - (UILabel *)networkLabel {
  1179. if (!_networkLabel) {
  1180. _networkLabel = [[UILabel alloc] initWithFrame:CGRectMake((kScreenWidth - 200) / 2.0f, (kScreenHeight - 60) / 2.0f, 200, 60)];
  1181. _networkLabel.backgroundColor = HexRGBAlpha(0x000000, 0.8f);
  1182. _networkLabel.textColor = HexRGB(0xffffff);
  1183. _networkLabel.textAlignment = NSTextAlignmentCenter;
  1184. _networkLabel.font = [UIFont systemFontOfSize:14.0f];
  1185. _networkLabel.layer.cornerRadius = 5.0f;
  1186. }
  1187. return _networkLabel;
  1188. }
  1189. #pragma mark ---- 修改声音模式
  1190. - (void)changeModeAction:(BOOL)enable {
  1191. if (enable) { // 音乐模式切换
  1192. [[RCRTCEngine sharedInstance].defaultAudioStream setAudioQuality:RCRTCAudioQualityMusicHigh Scenario:RCRTCAudioScenarioMusicClassRoom];
  1193. }
  1194. else {
  1195. [[RCRTCEngine sharedInstance].defaultAudioStream setAudioQuality:RCRTCAudioQualityMusicHigh Scenario:RCRTCAudioScenarioDefault];
  1196. }
  1197. }
  1198. - (void)dealloc {
  1199. [[NSNotificationCenter defaultCenter] removeObserver:self];
  1200. if (_timer) {
  1201. [_timer invalidate];
  1202. _timer = nil;
  1203. }
  1204. }
  1205. - (void)setMetronomeStat:(KSMetronomeType)metronomeStat {
  1206. _metronomeStat = metronomeStat;
  1207. self.currentTotalNo = 0;
  1208. if (_timer && _isPlay) {
  1209. [self resetTimer];
  1210. }
  1211. }
  1212. - (void)setRate:(int)rate{
  1213. _rate = rate;
  1214. CGFloat rateFloat = rate;
  1215. self.timerLength = 1 /(rateFloat /60);
  1216. if (self.lastRate == 0) {
  1217. self.lastRate = _rate;
  1218. }
  1219. // 超过10间隔再重置定时器,设置最新间隔秒数
  1220. if (abs(self.lastRate -_rate) >= 10 && _isPlay) {
  1221. self.lastRate = _rate;
  1222. [self resetTimer];
  1223. }
  1224. if (abs(self.lastRate -_rate) >= 10 && !_isPlay) {
  1225. if (_timer) {
  1226. [_timer invalidate];
  1227. _timer = nil;
  1228. }
  1229. }
  1230. }
  1231. - (void)play{
  1232. _isPlay = YES;
  1233. [ClassroomService sharedService].currentRoom.isPlayBeat = YES;
  1234. [self.timer setFireDate:[NSDate distantPast]];
  1235. }
  1236. - (void)pause{
  1237. [[RCRTCAudioMixer sharedInstance] stop];
  1238. [self.timer setFireDate:[NSDate distantFuture]];//暂停计时器
  1239. _isPlay = NO;
  1240. [ClassroomService sharedService].currentRoom.isPlayBeat = NO;
  1241. }
  1242. - (NSTimer *)timer{
  1243. if (!_timer) {
  1244. _timer = [NSTimer scheduledTimerWithTimeInterval:self.timerLength target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
  1245. [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];
  1246. }
  1247. return _timer;
  1248. }
  1249. - (void)timerAction {
  1250. if (_metronomeStat ==KSMetronomeType1V4) {
  1251. [self playAction:YES];
  1252. }
  1253. else {
  1254. if (_metronomeStat == KSMetronomeType2V4 && self.currentTotalNo %2 == 0) {
  1255. [self playAction:YES];
  1256. }else if (_metronomeStat == KSMetronomeType3V4 && self.currentTotalNo %3 == 0){
  1257. [self playAction:YES];
  1258. }else if (_metronomeStat == KSMetronomeType4V4 && self.currentTotalNo %4 == 0){
  1259. [self playAction:YES];
  1260. }else if (_metronomeStat == KSMetronomeType6V8 && self.currentTotalNo %6 == 0){
  1261. [self playAction:YES];
  1262. }else{
  1263. [self playAction:NO];
  1264. }
  1265. }
  1266. [self.videoListView showBeatNode:self.currentTotalNo];
  1267. self.currentTotalNo ++;
  1268. }
  1269. - (void)playAction:(BOOL)isDing {
  1270. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  1271. NSString *path = isDing ? self.pathDing : self.pathDong;
  1272. [[RCRTCAudioMixer sharedInstance] stop];
  1273. @try {
  1274. [RCRTCAudioMixer sharedInstance].mixingVolume = self.volume;
  1275. [RCRTCAudioMixer sharedInstance].playingVolume = self.volume;
  1276. [[RCRTCAudioMixer sharedInstance] startMixingWithURL:[NSURL fileURLWithPath:path] playback:YES mixerMode:RCRTCMixerModeMixing loopCount:1];
  1277. } @catch (NSException *exception) {
  1278. NSLog(@"%@", exception);
  1279. } @finally {
  1280. }
  1281. });
  1282. }
  1283. - (NSString *)pathDing {
  1284. if (!_pathDing) {
  1285. _pathDing = [[NSBundle mainBundle] pathForResource:@"tick" ofType:@"wav"];
  1286. }
  1287. return _pathDing;
  1288. }
  1289. - (NSString *)pathDong {
  1290. if (!_pathDong) {
  1291. _pathDong = [[NSBundle mainBundle] pathForResource:@"tock" ofType:@"wav"];
  1292. }
  1293. return _pathDong;
  1294. }
  1295. #pragma mark -- 重置定时器
  1296. - (void)resetTimer {
  1297. [_timer invalidate];
  1298. _timer = nil;
  1299. [self.timer setFireDate:[NSDate distantPast]];
  1300. }
  1301. - (void)removeAll {
  1302. if (_timer) {
  1303. [_timer invalidate];
  1304. _timer = nil;
  1305. }
  1306. }
  1307. #pragma mark --- KSWhiteboardControlDelegate
  1308. - (void)didTurnPage:(NSInteger)pageNum {
  1309. }
  1310. - (void)whiteboardViewDidChangeZoomScale:(float)scale {
  1311. }
  1312. // 白板连接状态回调
  1313. - (void)connectionWhiteboardStatus:(JOINROOMSTATUS)status {
  1314. if (status == JOINROOMSTATUS_FAILED) {
  1315. [self.tipsView showTipsMessage:@"白板连接失败,请检查您的网络" inView:self.view];
  1316. }
  1317. else if (status == JOINROOMSTATUS_PARAMERROR) {
  1318. [self.tipsView showTipsMessage:@"获取白板参数错误" inView:self.view];
  1319. }
  1320. }
  1321. #pragma mark -- mainContainer
  1322. - (void)mainContainerOpertionMember:(RoomMember *)member tapButton:(UIButton *)button {
  1323. // 🙋操作
  1324. if (button.selected) { // 取消举手
  1325. if (self.alertView && self.alertView.isShow) {
  1326. [self.alertView dismissAlertView];
  1327. }
  1328. self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"确认取消举手吗?" leftTitle:@"取消" rightTitle:@"确认" cancel:^{
  1329. } confirm:^{
  1330. [[ClassroomService sharedService] enableDevice:NO withType:DeviceTypeHandup];
  1331. }];
  1332. }
  1333. else {
  1334. if (self.alertView && self.alertView.isShow) {
  1335. [self.alertView dismissAlertView];
  1336. }
  1337. self.alertView = [KSNormalAlertView ks_showAlertWithTitle:@"确认举手吗?" leftTitle:@"取消" rightTitle:@"确认" cancel:^{
  1338. } confirm:^{
  1339. [[ClassroomService sharedService] enableDevice:YES withType:DeviceTypeHandup];
  1340. }];
  1341. }
  1342. }
  1343. #pragma mark ------ 创建MBProgress
  1344. - (MBProgressHUD *)createProgressHUD {
  1345. [MBProgressHUD hideHUD];
  1346. MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
  1347. hud.mode = MBProgressHUDModeIndeterminate;
  1348. hud.contentColor = [UIColor whiteColor];
  1349. hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor;
  1350. hud.bezelView.backgroundColor = [UIColor colorWithHexString:@"#000000" alpha:0.8];
  1351. [hud hideAnimated:YES afterDelay:60.0f]; // 自动关闭时间60s
  1352. return hud;
  1353. }
  1354. /*
  1355. #pragma mark - Navigation
  1356. // In a storyboard-based application, you will often want to do a little preparation before navigation
  1357. - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
  1358. // Get the new view controller using [segue destinationViewController].
  1359. // Pass the selected object to the new view controller.
  1360. }
  1361. */
  1362. @end