WFCUFloatingWindow.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. //
  2. // WFCUFloatingWindow.m
  3. // WFDemo
  4. //
  5. // Created by heavyrain on 17/9/27.
  6. // Copyright © 2017年 WildFireChat. All rights reserved.
  7. //
  8. #if WFCU_SUPPORT_VOIP
  9. #import "WFCUFloatingWindow.h"
  10. #import <CoreTelephony/CTCall.h>
  11. #import <CoreTelephony/CTCallCenter.h>
  12. #import <UIKit/UIKit.h>
  13. @interface WFCUFloatingWindow () <WFAVCallSessionDelegate>
  14. @property(nonatomic, strong) NSTimer *activeTimer;
  15. @property(nonatomic, copy) void (^touchedBlock)(WFAVCallSession *callSession);
  16. @property(nonatomic, strong) CTCallCenter *callCenter;
  17. @end
  18. static WFCUFloatingWindow *staticWindow = nil;
  19. static NSString *kFloatingWindowPosX = @"kFloatingWindowPosX";
  20. static NSString *kFloatingWindowPosY = @"kFloatingWindowPosY";
  21. @implementation WFCUFloatingWindow
  22. + (void)startCallFloatingWindow:(WFAVCallSession *)callSession
  23. withTouchedBlock:(void (^)(WFAVCallSession *callSession))touchedBlock {
  24. staticWindow = [[WFCUFloatingWindow alloc] init];
  25. staticWindow.callSession = callSession;
  26. [staticWindow.callSession setDelegate:staticWindow];
  27. staticWindow.touchedBlock = touchedBlock;
  28. [staticWindow initWindow];
  29. }
  30. + (void)stopCallFloatingWindow {
  31. [staticWindow hideCallFloatingWindow];
  32. [staticWindow clearCallFloatingWindow];
  33. staticWindow = nil;
  34. }
  35. - (void)initWindow {
  36. if (self.callSession.state == kWFAVEngineStateIdle) {
  37. [self performSelector:@selector(clearCallFloatingWindow) withObject:nil afterDelay:2];
  38. }
  39. [self updateActiveTimer];
  40. [self startActiveTimer];
  41. [self updateWindow];
  42. [self registerTelephonyEvent];
  43. [[NSNotificationCenter defaultCenter] addObserver:self
  44. selector:@selector(onOrientationChanged:)
  45. name:UIApplicationDidChangeStatusBarOrientationNotification
  46. object:nil];
  47. [self addProximityMonitoringObserver];
  48. }
  49. - (void)registerTelephonyEvent {
  50. self.callCenter = [[CTCallCenter alloc] init];
  51. __weak __typeof(self) weakSelf = self;
  52. self.callCenter.callEventHandler = ^(CTCall *call) {
  53. if ([call.callState isEqualToString:CTCallStateConnected]) {
  54. [weakSelf.callSession endCall];
  55. }
  56. };
  57. }
  58. - (void)onOrientationChanged:(NSNotification *)notification {
  59. [self updateWindow];
  60. }
  61. - (void)startActiveTimer {
  62. self.activeTimer = [NSTimer scheduledTimerWithTimeInterval:1
  63. target:self
  64. selector:@selector(updateActiveTimer)
  65. userInfo:nil
  66. repeats:YES];
  67. [self.activeTimer fire];
  68. }
  69. - (void)stopActiveTimer {
  70. if (self.activeTimer) {
  71. [self.activeTimer invalidate];
  72. self.activeTimer = nil;
  73. }
  74. }
  75. - (void)updateActiveTimer {
  76. long sec = [[NSDate date] timeIntervalSince1970] - self.callSession.connectedTime / 1000;
  77. if (self.callSession.state == kWFAVEngineStateConnected && ![self isVideoViewEnabledSession]) {
  78. NSString *timeStr;
  79. if (sec < 60 * 60) {
  80. timeStr = [NSString stringWithFormat:@"%02ld:%02ld", sec / 60, sec % 60];
  81. } else {
  82. timeStr = [NSString stringWithFormat:@"%02ld:%02ld:%02ld", sec / 60 / 60, (sec / 60) % 60, sec % 60];
  83. }
  84. [self.floatingButton setTitle:timeStr forState:UIControlStateNormal];
  85. [self layoutTextUnderImageButton:self.floatingButton];
  86. }
  87. }
  88. - (void)updateWindow {
  89. CGFloat posX = [[[NSUserDefaults standardUserDefaults] objectForKey:kFloatingWindowPosX] floatValue];
  90. CGFloat posY = [[[NSUserDefaults standardUserDefaults] objectForKey:kFloatingWindowPosY] floatValue];
  91. posX = posX ? posX : 30;
  92. posY = posY ? posY : 30;
  93. CGRect screenBounds = [UIScreen mainScreen].bounds;
  94. posX = (posX + 30) > screenBounds.size.width ? (screenBounds.size.width - 30) : posX;
  95. posY = (posY + 48) > screenBounds.size.height ? (screenBounds.size.height - 48) : posY;
  96. if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft &&
  97. [self isSupportOrientation:UIInterfaceOrientationLandscapeLeft]) {
  98. self.window.transform = CGAffineTransformMakeRotation(M_PI / 2);
  99. self.window.frame = CGRectMake(posX, posY, 64, 96);
  100. self.floatingButton.frame = CGRectMake(0, 0, 96, 64);
  101. if ([self isVideoViewEnabledSession]) {
  102. self.videoView.frame = CGRectMake(0, 0, 96, 64);
  103. }
  104. } else if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight &&
  105. [self isSupportOrientation:UIInterfaceOrientationLandscapeRight]) {
  106. self.window.transform = CGAffineTransformMakeRotation(-M_PI / 2);
  107. self.window.frame = CGRectMake(posX, posY, 64, 96);
  108. self.floatingButton.frame = CGRectMake(0, 0, 96, 64);
  109. if ([self isVideoViewEnabledSession]) {
  110. self.videoView.frame = CGRectMake(0, 0, 96, 64);
  111. }
  112. } else {
  113. if ([UIDevice currentDevice].orientation == UIDeviceOrientationPortraitUpsideDown &&
  114. [self isSupportOrientation:UIInterfaceOrientationPortraitUpsideDown]) {
  115. self.window.transform = CGAffineTransformMakeRotation(M_PI);
  116. } else {
  117. self.window.transform = CGAffineTransformMakeRotation(0);
  118. }
  119. self.window.frame = CGRectMake(posX, posY, 64, 96);
  120. self.floatingButton.frame = CGRectMake(0, 0, 64, 96);
  121. if ([self isVideoViewEnabledSession]) {
  122. self.videoView.frame = CGRectMake(0, 0, 64, 96);
  123. }
  124. }
  125. if ([self isVideoViewEnabledSession]) {
  126. if (self.callSession.state == kWFAVEngineStateConnected) {
  127. [self.callSession setupRemoteVideoView:self.videoView scalingType:kWFAVVideoScalingTypeAspectBalanced];
  128. [self.callSession setupLocalVideoView:nil scalingType:kWFAVVideoScalingTypeAspectBalanced];
  129. } else if (self.callSession.state == kWFAVEngineStateIdle) {
  130. UILabel *videoStopTips =
  131. [[UILabel alloc] initWithFrame:CGRectMake(0, self.videoView.frame.size.height / 2 - 10,
  132. self.videoView.frame.size.width, 20)];
  133. videoStopTips.textAlignment = NSTextAlignmentCenter;
  134. videoStopTips.text = @"已结束";
  135. videoStopTips.textColor = HEXCOLOR(0x0195ff);
  136. [self.videoView addSubview:videoStopTips];
  137. }
  138. } else {
  139. if (self.callSession.state == kWFAVEngineStateConnected) {
  140. [self.floatingButton setBackgroundColor:[UIColor clearColor]];
  141. } else if (self.callSession.state == kWFAVEngineStateIdle) {
  142. [self.floatingButton setTitle:@"已结束"
  143. forState:UIControlStateNormal];
  144. }
  145. }
  146. }
  147. - (UIWindow *)window {
  148. if (!_window) {
  149. CGFloat posX = [[[NSUserDefaults standardUserDefaults] objectForKey:kFloatingWindowPosX] floatValue];
  150. CGFloat posY = [[[NSUserDefaults standardUserDefaults] objectForKey:kFloatingWindowPosY] floatValue];
  151. posX = (posX - 30) ? posX : 30;
  152. posY = (posY - 48) ? posY : 48;
  153. CGRect screenBounds = [UIScreen mainScreen].bounds;
  154. posX = (posX + 30) > screenBounds.size.width ? (screenBounds.size.width - 30) : posX;
  155. posY = (posY + 48) > screenBounds.size.height ? (screenBounds.size.height - 48) : posY;
  156. _window = [[UIWindow alloc] initWithFrame:CGRectMake(posX, posY, 64, 96)];
  157. _window.backgroundColor = [UIColor whiteColor];
  158. _window.windowLevel = UIWindowLevelAlert + 1;
  159. _window.layer.cornerRadius = 4;
  160. _window.layer.masksToBounds = YES;
  161. _window.layer.borderWidth = 1;
  162. _window.layer.borderColor = [HEXCOLOR(0x0A88E1) CGColor];
  163. [_window makeKeyAndVisible]; //关键语句,显示window
  164. UIPanGestureRecognizer *panGestureRecognizer =
  165. [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGestures:)];
  166. panGestureRecognizer.minimumNumberOfTouches = 1;
  167. panGestureRecognizer.maximumNumberOfTouches = 1;
  168. [_window addGestureRecognizer:panGestureRecognizer];
  169. }
  170. return _window;
  171. }
  172. - (UIView *)videoView {
  173. if (!_videoView) {
  174. _videoView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 64, 96)];
  175. _videoView.backgroundColor = [UIColor blackColor];
  176. CGRect windowFrame = self.window.frame;
  177. windowFrame.size.width = _videoView.frame.size.width;
  178. windowFrame.size.height = _videoView.frame.size.height;
  179. self.window.frame = windowFrame;
  180. [self.window addSubview:_videoView];
  181. UITapGestureRecognizer *tap =
  182. [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(touchedWindow:)];
  183. [_videoView addGestureRecognizer:tap];
  184. }
  185. return _videoView;
  186. }
  187. - (UIButton *)floatingButton {
  188. if (!_floatingButton) {
  189. _floatingButton = [UIButton buttonWithType:UIButtonTypeCustom];
  190. if (false /*self.callSession.mediaType == Audio*/) {
  191. [_floatingButton setImage:[UIImage imageNamed:@"floatingaudio"]
  192. forState:UIControlStateNormal];
  193. } else {
  194. [_floatingButton setImage:[UIImage imageNamed:@"floatingvideo"]
  195. forState:UIControlStateNormal];
  196. }
  197. [_floatingButton setTitle:@"" forState:UIControlStateNormal];
  198. _floatingButton.backgroundColor = [UIColor clearColor];
  199. _floatingButton.frame = CGRectMake(0, 0, 64, 96);
  200. CGRect windowFrame = self.window.frame;
  201. windowFrame.size.width = _floatingButton.frame.size.width;
  202. windowFrame.size.height = _floatingButton.frame.size.height;
  203. self.window.frame = windowFrame;
  204. [_floatingButton addTarget:self action:@selector(touchedWindow:) forControlEvents:UIControlEventTouchUpInside];
  205. [self.window addSubview:_floatingButton];
  206. }
  207. return _floatingButton;
  208. }
  209. - (BOOL)isSupportOrientation:(UIInterfaceOrientation)orientation {
  210. UIInterfaceOrientationMask mask =
  211. [[UIApplication sharedApplication] supportedInterfaceOrientationsForWindow:self.window];
  212. return mask & (1 << orientation);
  213. }
  214. - (void)handlePanGestures:(UIPanGestureRecognizer *)paramSender {
  215. if (paramSender.state != UIGestureRecognizerStateEnded && paramSender.state != UIGestureRecognizerStateFailed) {
  216. CGPoint location = [paramSender locationInView:[UIApplication sharedApplication].windows[0]];
  217. if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeLeft &&
  218. [self isSupportOrientation:UIInterfaceOrientationLandscapeLeft]) {
  219. CGFloat tmp = location.x;
  220. location.x = [UIScreen mainScreen].bounds.size.height - location.y;
  221. location.y = tmp;
  222. } else if ([UIDevice currentDevice].orientation == UIDeviceOrientationLandscapeRight &&
  223. [self isSupportOrientation:UIInterfaceOrientationLandscapeRight]) {
  224. CGFloat tmp = location.x;
  225. location.x = location.y;
  226. location.y = [UIScreen mainScreen].bounds.size.width - tmp;
  227. } else if ([UIDevice currentDevice].orientation == UIDeviceOrientationPortraitUpsideDown &&
  228. [self isSupportOrientation:UIInterfaceOrientationPortraitUpsideDown]) {
  229. CGFloat tmp = location.x;
  230. location.x = [UIScreen mainScreen].bounds.size.height - location.y;
  231. location.y = [UIScreen mainScreen].bounds.size.width - tmp;
  232. }
  233. CGRect frame = self.window.frame;
  234. frame.origin.x = location.x - frame.size.width / 2;
  235. frame.origin.y = location.y - frame.size.height / 2;
  236. if (frame.origin.x < 0) {
  237. frame.origin.x = 2;
  238. }
  239. if (frame.origin.y < 0) {
  240. frame.origin.y = 2;
  241. }
  242. CGRect screenBounds = [UIScreen mainScreen].bounds;
  243. BOOL isLandscape = screenBounds.size.width > screenBounds.size.height;
  244. if (isLandscape && [self isSupportOrientation:(UIInterfaceOrientation)[UIDevice currentDevice].orientation]) {
  245. if (frame.origin.y + frame.size.height > [UIScreen mainScreen].bounds.size.width) {
  246. frame.origin.y = [UIScreen mainScreen].bounds.size.width - 2 - frame.size.height;
  247. }
  248. if (frame.origin.x + frame.size.width > [UIScreen mainScreen].bounds.size.height) {
  249. frame.origin.x = [UIScreen mainScreen].bounds.size.height - 2 - frame.size.width;
  250. }
  251. } else {
  252. if (frame.origin.x + frame.size.width > [UIScreen mainScreen].bounds.size.width) {
  253. frame.origin.x = [UIScreen mainScreen].bounds.size.width - 2 - frame.size.width;
  254. }
  255. if (frame.origin.y + frame.size.height > [UIScreen mainScreen].bounds.size.height) {
  256. frame.origin.y = [UIScreen mainScreen].bounds.size.height - 2 - frame.size.height;
  257. }
  258. }
  259. self.window.frame = frame;
  260. } else if (paramSender.state == UIGestureRecognizerStateEnded) {
  261. CGRect frame = self.window.frame;
  262. [[NSUserDefaults standardUserDefaults] setObject:@(frame.origin.x) forKey:kFloatingWindowPosX];
  263. [[NSUserDefaults standardUserDefaults] setObject:@(frame.origin.y) forKey:kFloatingWindowPosY];
  264. [[NSUserDefaults standardUserDefaults] synchronize];
  265. }
  266. }
  267. - (void)touchedWindow:(id)sender {
  268. [self hideCallFloatingWindow];
  269. if (self.touchedBlock) {
  270. self.touchedBlock(self.callSession);
  271. }
  272. [self clearCallFloatingWindow];
  273. }
  274. - (void)hideCallFloatingWindow {
  275. [self stopActiveTimer];
  276. if (_videoView) {
  277. [_videoView removeFromSuperview];
  278. _videoView = nil;
  279. }
  280. if (_floatingButton) {
  281. [_floatingButton removeFromSuperview];
  282. _floatingButton = nil;
  283. }
  284. [_window setHidden:YES];
  285. }
  286. - (void)clearCallFloatingWindow {
  287. [[NSNotificationCenter defaultCenter] removeObserver:self];
  288. _activeTimer = nil;
  289. _callSession = nil;
  290. _touchedBlock = nil;
  291. _floatingButton = nil;
  292. _videoView = nil;
  293. _window = nil;
  294. staticWindow = nil;
  295. }
  296. - (void)layoutTextUnderImageButton:(UIButton *)button {
  297. [button.titleLabel setFont:[UIFont systemFontOfSize:16]];
  298. [button setTitleColor:HEXCOLOR(0x0195ff) forState:UIControlStateNormal];
  299. button.titleEdgeInsets = UIEdgeInsetsMake(0, -button.imageView.frame.size.width,
  300. -button.imageView.frame.size.height - 6 / 2, 0);
  301. // button.imageEdgeInsets =
  302. // UIEdgeInsetsMake(-button.titleLabel.frame.size.height-offset/2, 0, 0,
  303. // -button.titleLabel.frame.size.width);
  304. // 由于iOS8中titleLabel的size为0,用上面这样设置有问题,修改一下即可
  305. button.imageEdgeInsets = UIEdgeInsetsMake(-button.titleLabel.intrinsicContentSize.height - 6 / 2,
  306. 0, 0, -button.titleLabel.intrinsicContentSize.width);
  307. }
  308. - (BOOL)isVideoViewEnabledSession {
  309. if (YES/*self.callSession.mediaType == video && !self.callSession.isMultiCall*/) {
  310. return YES;
  311. } else {
  312. return NO;
  313. }
  314. }
  315. #pragma mark - WFAVCallSessionDelegate
  316. - (void)didChangeState:(WFAVEngineState)state {
  317. switch (state) {
  318. case kWFAVEngineStateIdle:
  319. [self updateWindow];
  320. [self performSelector:@selector(clearCallFloatingWindow) withObject:nil afterDelay:2];
  321. [self removeProximityMonitoringObserver];
  322. break;
  323. case kWFAVEngineStateConnected:
  324. [self updateWindow];
  325. break;
  326. default:
  327. break;
  328. }
  329. }
  330. - (void)didCallEndWithReason:(WFAVCallEndReason)reason {
  331. }
  332. - (void)didError:(NSError *)error {
  333. }
  334. - (void)didGetStats:(NSArray *)stats {
  335. }
  336. - (void)didCreateLocalVideoTrack:(RTCVideoTrack *)localVideoTrack {
  337. }
  338. - (void)didReceiveRemoteVideoTrack:(RTCVideoTrack *)remoteVideoTrack {
  339. }
  340. - (void)didChangeMode:(BOOL)isAudioOnly {
  341. [self.videoView removeFromSuperview];
  342. [self initWindow];
  343. }
  344. - (void)addProximityMonitoringObserver {
  345. [UIDevice currentDevice].proximityMonitoringEnabled = YES;
  346. [[NSNotificationCenter defaultCenter] addObserver:self
  347. selector:@selector(proximityStatueChanged:)
  348. name:UIDeviceProximityStateDidChangeNotification
  349. object:nil];
  350. }
  351. - (void)removeProximityMonitoringObserver {
  352. [UIDevice currentDevice].proximityMonitoringEnabled = NO;
  353. [[NSNotificationCenter defaultCenter] removeObserver:self
  354. name:UIDeviceProximityStateDidChangeNotification
  355. object:nil];
  356. }
  357. - (void)proximityStatueChanged:(NSNotificationCenter *)notification {
  358. }
  359. @end
  360. #endif