WFCUChatInputBar.m 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188
  1. //
  2. // ChatInputBar.m
  3. // WFChat UIKit
  4. //
  5. // Created by WF Chat on 2017/10/28.
  6. // Copyright © 2017年 WildFireChat. All rights reserved.
  7. //
  8. #import <AVFoundation/AVFoundation.h>
  9. #import <CoreText/CoreText.h>
  10. #import "WFCUChatInputBar.h"
  11. #import "WFCUFaceBoard.h"
  12. #import "WFCUVoiceRecordView.h"
  13. #import "WFCUPluginBoardView.h"
  14. #import "WFCUUtilities.h"
  15. #import "WFCULocationViewController.h"
  16. #import "WFCULocationPoint.h"
  17. #import "WFCUSelectFileViewController.h"
  18. #import "KZVideoViewController.h"
  19. #import "UIView+Toast.h"
  20. #import <WFChatClient/WFCChatClient.h>
  21. #import "WFCUContactListViewController.h"
  22. #import "MBProgressHUD.h"
  23. #import "UIColor+YH.h"
  24. #if WFCU_SUPPORT_VOIP
  25. #import <WFAVEngineKit/WFAVEngineKit.h>
  26. #endif
  27. #define CHAT_INPUT_BAR_PADDING 8
  28. #define CHAT_INPUT_BAR_ICON_SIZE (CHAT_INPUT_BAR_HEIGHT - CHAT_INPUT_BAR_PADDING - CHAT_INPUT_BAR_PADDING)
  29. @implementation WFCUMetionInfo
  30. - (instancetype)initWithType:(int)type target:(NSString *)target range:(NSRange)range {
  31. self = [super init];
  32. if (self) {
  33. self.mentionType = type;
  34. self.target = target;
  35. self.range = range;
  36. }
  37. return self;
  38. }
  39. @end
  40. //@implementation TextInfo
  41. //
  42. //@end
  43. @interface WFCUChatInputBar () <UITextViewDelegate, WFCUFaceBoardDelegate, UIImagePickerControllerDelegate, AVAudioRecorderDelegate, AVAudioPlayerDelegate, WFCUPluginBoardViewDelegate, UIImagePickerControllerDelegate, LocationViewControllerDelegate, UIActionSheetDelegate, KZVideoViewControllerDelegate>
  44. @property (nonatomic, assign)BOOL textInput;
  45. @property (nonatomic, assign)BOOL voiceInput;
  46. @property (nonatomic, assign)BOOL emojInput;
  47. @property (nonatomic, assign)BOOL pluginInput;
  48. @property (nonatomic, strong)UIButton *voiceSwitchBtn;
  49. @property (nonatomic, strong)UIButton *emojSwitchBtn;
  50. @property (nonatomic, strong)UIButton *pluginSwitchBtn;
  51. @property (nonatomic, strong)UITextView *textInputView;
  52. @property (nonatomic, strong)UIView *inputCoverView;
  53. @property (nonatomic, strong)UIButton *voiceInputBtn;
  54. @property (nonatomic, strong)UIView *emojInputView;
  55. @property (nonatomic, strong)UIView *pluginInputView;
  56. @property(nonatomic, weak)id<WFCUChatInputBarDelegate> delegate;
  57. @property (nonatomic, strong)WFCUVoiceRecordView *recordView;
  58. @property(nonatomic) AVAudioRecorder *recorder;
  59. @property(nonatomic) NSTimer *recordingTimer;
  60. @property(nonatomic) NSTimer *updateMeterTimer;
  61. @property(nonatomic, assign) int seconds;
  62. @property(nonatomic) BOOL recordCanceled;
  63. @property(nonatomic, weak)UIView *parentView;
  64. @property (nonatomic, strong)NSMutableArray<WFCUMetionInfo *> *mentionInfos;
  65. @property (nonatomic, strong)WFCCConversation *conversation;
  66. @property (nonatomic, assign)double lastTypingTime;
  67. @property (nonatomic, strong)UIColor *textInputViewTintColor;
  68. @property (nonatomic, assign)CGRect backupFrame;
  69. @end
  70. @implementation WFCUChatInputBar
  71. - (instancetype)initWithParentView:(UIView *)parentView conversation:(WFCCConversation *)conversation delegate:(id<WFCUChatInputBarDelegate>)delegate {
  72. self = [super initWithFrame:CGRectMake(0, parentView.bounds.size.height - CHAT_INPUT_BAR_HEIGHT, parentView.bounds.size.width, CHAT_INPUT_BAR_HEIGHT)];
  73. if (self) {
  74. [parentView addSubview:self];
  75. [self initSubViews];
  76. self.delegate = delegate;
  77. self.parentView = parentView;
  78. self.mentionInfos = [[NSMutableArray alloc] init];
  79. self.conversation = conversation;
  80. self.lastTypingTime = 0;
  81. self.backupFrame = CGRectZero;
  82. }
  83. return self;
  84. }
  85. - (void)initSubViews {
  86. [[NSNotificationCenter defaultCenter] addObserver:self
  87. selector:@selector(keyboardWillShow:)
  88. name:UIKeyboardWillShowNotification
  89. object:nil];
  90. [[NSNotificationCenter defaultCenter] addObserver:self
  91. selector:@selector(keyboardWillHide:)
  92. name:UIKeyboardWillHideNotification
  93. object:nil];
  94. [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(keyboardDidHide:) name:UIKeyboardDidHideNotification object:nil];
  95. self.backgroundColor = [UIColor colorWithHexString:@"0xf7f7f7"];
  96. CGRect parentRect = self.bounds;
  97. self.voiceSwitchBtn = [[UIButton alloc] initWithFrame:CGRectMake(CHAT_INPUT_BAR_PADDING, CHAT_INPUT_BAR_PADDING, CHAT_INPUT_BAR_ICON_SIZE, CHAT_INPUT_BAR_ICON_SIZE)];
  98. [self.voiceSwitchBtn setImage:[UIImage imageNamed:@"chat_input_bar_voice"] forState:UIControlStateNormal];
  99. [self.voiceSwitchBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  100. [self.voiceSwitchBtn addTarget:self action:@selector(onSwitchBtn:) forControlEvents:UIControlEventTouchDown];
  101. [self addSubview:self.voiceSwitchBtn];
  102. self.pluginSwitchBtn = [[UIButton alloc] initWithFrame:CGRectMake(parentRect.size.width - CHAT_INPUT_BAR_HEIGHT + CHAT_INPUT_BAR_PADDING, CHAT_INPUT_BAR_PADDING, CHAT_INPUT_BAR_ICON_SIZE, CHAT_INPUT_BAR_ICON_SIZE)];
  103. [self.pluginSwitchBtn setImage:[UIImage imageNamed:@"chat_input_bar_plugin"] forState:UIControlStateNormal];
  104. [self.pluginSwitchBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  105. [self.pluginSwitchBtn addTarget:self action:@selector(onSwitchBtn:) forControlEvents:UIControlEventTouchDown];
  106. [self addSubview:self.pluginSwitchBtn];
  107. self.emojSwitchBtn = [[UIButton alloc] initWithFrame:CGRectMake(parentRect.size.width - CHAT_INPUT_BAR_HEIGHT - CHAT_INPUT_BAR_ICON_SIZE, CHAT_INPUT_BAR_PADDING, CHAT_INPUT_BAR_ICON_SIZE, CHAT_INPUT_BAR_ICON_SIZE)];
  108. [self.emojSwitchBtn setImage:[UIImage imageNamed:@"chat_input_bar_emoj"] forState:UIControlStateNormal];
  109. [self.emojSwitchBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  110. [self.emojSwitchBtn addTarget:self action:@selector(onSwitchBtn:) forControlEvents:UIControlEventTouchDown];
  111. [self addSubview:self.emojSwitchBtn];
  112. self.textInputView = [[UITextView alloc] initWithFrame:CGRectMake(CHAT_INPUT_BAR_HEIGHT, CHAT_INPUT_BAR_PADDING, parentRect.size.width - CHAT_INPUT_BAR_HEIGHT - CHAT_INPUT_BAR_HEIGHT - CHAT_INPUT_BAR_HEIGHT + CHAT_INPUT_BAR_PADDING, CHAT_INPUT_BAR_ICON_SIZE)];
  113. self.textInputView.delegate = self;
  114. self.textInputView.layoutManager.allowsNonContiguousLayout = NO;
  115. [self.textInputView setExclusiveTouch:YES];
  116. [self.textInputView setTextColor:[UIColor blackColor]];
  117. [self.textInputView setFont:[UIFont systemFontOfSize:16]];
  118. [self.textInputView setReturnKeyType:UIReturnKeySend];
  119. self.textInputView.backgroundColor = [UIColor whiteColor];
  120. self.textInputView.enablesReturnKeyAutomatically = YES;
  121. self.textInputView.userInteractionEnabled = YES;
  122. [self addSubview:self.textInputView];
  123. self.inputCoverView = [[UIView alloc] initWithFrame:self.textInputView.bounds];
  124. self.inputCoverView.backgroundColor = [UIColor clearColor];
  125. [self.textInputView addSubview:self.inputCoverView];
  126. UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapInputView:)];
  127. tap.numberOfTapsRequired = 1;
  128. [self.inputCoverView addGestureRecognizer:tap];
  129. self.voiceInputBtn = [[UIButton alloc] initWithFrame:CGRectMake(CHAT_INPUT_BAR_HEIGHT, CHAT_INPUT_BAR_PADDING, parentRect.size.width - CHAT_INPUT_BAR_HEIGHT - CHAT_INPUT_BAR_HEIGHT - CHAT_INPUT_BAR_HEIGHT + CHAT_INPUT_BAR_PADDING, CHAT_INPUT_BAR_ICON_SIZE)];
  130. [self.voiceInputBtn setTitle:WFCString(@"HoldToTalk") forState:UIControlStateNormal];
  131. [self.voiceInputBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
  132. self.voiceInputBtn.layer.cornerRadius = 4;
  133. self.voiceInputBtn.layer.masksToBounds = YES;
  134. self.voiceInputBtn.layer.borderWidth = 0.5f;
  135. self.voiceInputBtn.layer.borderColor = HEXCOLOR(0xdbdbdd).CGColor;
  136. [self addSubview:self.voiceInputBtn];
  137. self.layer.borderWidth = 0.5f;
  138. self.layer.borderColor = HEXCOLOR(0xdbdbdd).CGColor;
  139. self.inputBarStatus = ChatInputBarDefaultStatus;
  140. [self.voiceInputBtn addTarget:self action:@selector(onTouchDown:) forControlEvents:UIControlEventTouchDown];
  141. [self.voiceInputBtn addTarget:self action:@selector(onTouchDragExit:) forControlEvents:UIControlEventTouchDragExit];
  142. [self.voiceInputBtn addTarget:self action:@selector(onTouchDragEnter:) forControlEvents:UIControlEventTouchDragEnter];
  143. [self.voiceInputBtn addTarget:self action:@selector(onTouchUpInside:) forControlEvents:UIControlEventTouchUpInside];
  144. [self.voiceInputBtn addTarget:self action:@selector(onTouchUpOutside:) forControlEvents:UIControlEventTouchUpOutside];
  145. [self.voiceInputBtn addTarget:self action:@selector(onTouchUpOutside:) forControlEvents:UIControlEventTouchCancel];
  146. self.voiceInputBtn.hidden = YES;
  147. self.textInputView.returnKeyType = UIReturnKeySend;
  148. self.textInputView.delegate = self;
  149. }
  150. - (void)onTapInputView:(id)sender {
  151. NSLog(@"on tap input view");
  152. self.inputBarStatus = ChatInputBarKeyboardStatus;
  153. }
  154. - (void)onTouchDown:(id)sender {
  155. if ([self canRecord]) {
  156. _recordView = [[WFCUVoiceRecordView alloc] initWithFrame:CGRectMake(self.parentView.bounds.size.width/2 - 70, self.parentView.bounds.size.height/2 - 70, 140, 140)];
  157. _recordView.center = self.parentView.center;
  158. [self.parentView addSubview:_recordView];
  159. [self.parentView bringSubviewToFront:_recordView];
  160. [self recordStart];
  161. }
  162. }
  163. - (void)willAppear {
  164. if (self.backupFrame.size.height) {
  165. [self.delegate willChangeFrame:self.backupFrame withDuration:0.5 keyboardShowing:NO];
  166. }
  167. }
  168. - (void)recordStart {
  169. if (self.recorder.recording) {
  170. return;
  171. }
  172. //[self stopPlayer];
  173. [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
  174. if (granted) {
  175. AVAudioSession *session = [AVAudioSession sharedInstance];
  176. [session setCategory:AVAudioSessionCategoryRecord error:nil];
  177. BOOL r = [session setActive:YES error:nil];
  178. if (!r) {
  179. NSLog(@"activate audio session fail");
  180. return;
  181. }
  182. NSLog(@"start record...");
  183. NSArray *pathComponents = [NSArray arrayWithObjects:
  184. [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject],
  185. @"voice.wav",
  186. nil];
  187. NSURL *outputFileURL = [NSURL fileURLWithPathComponents:pathComponents];
  188. // Define the recorder setting
  189. NSMutableDictionary *recordSetting = [[NSMutableDictionary alloc] init];
  190. [recordSetting setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey];
  191. [recordSetting setValue:[NSNumber numberWithFloat:8000] forKey:AVSampleRateKey];
  192. [recordSetting setValue:[NSNumber numberWithInt:2] forKey:AVNumberOfChannelsKey];
  193. self.recorder = [[AVAudioRecorder alloc] initWithURL:outputFileURL settings:recordSetting error:NULL];
  194. self.recorder.delegate = self;
  195. self.recorder.meteringEnabled = YES;
  196. if (![self.recorder prepareToRecord]) {
  197. NSLog(@"prepare record fail");
  198. return;
  199. }
  200. if (![self.recorder record]) {
  201. NSLog(@"start record fail");
  202. return;
  203. }
  204. self.recordCanceled = NO;
  205. self.seconds = 0;
  206. self.recordingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
  207. self.updateMeterTimer = [NSTimer scheduledTimerWithTimeInterval:0.05
  208. target:self
  209. selector:@selector(updateMeter:)
  210. userInfo:nil
  211. repeats:YES];
  212. } else {
  213. [[[UIAlertView alloc] initWithTitle:@"警告" message:@"无法录音,请到设置-隐私-麦克风,允许程序访问" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil] show];
  214. }
  215. }];
  216. }
  217. - (void)recordCancel {
  218. NSLog(@"touch cancel");
  219. if (self.recorder.recording) {
  220. NSLog(@"cancel record...");
  221. self.recordCanceled = YES;
  222. [self stopRecord];
  223. }
  224. }
  225. - (void)onTouchDragExit:(id)sender {
  226. [self.recordView recordButtonDragOutside];
  227. }
  228. - (void)onTouchDragEnter:(id)sender {
  229. [self.recordView recordButtonDragInside];
  230. }
  231. - (void)onTouchUpInside:(id)sender {
  232. [self.recordView removeFromSuperview];
  233. [self recordEnd];
  234. }
  235. - (void)onTouchUpOutside:(id)sender {
  236. [self.recordView removeFromSuperview];
  237. [self recordCancel];
  238. }
  239. - (BOOL)canRecord {
  240. __block BOOL bCanRecord = YES;
  241. if ([[AVAudioSession sharedInstance]
  242. respondsToSelector:@selector(requestRecordPermission:)]) {
  243. [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) {
  244. bCanRecord = granted;
  245. dispatch_async(dispatch_get_main_queue(), ^{
  246. bCanRecord = granted;
  247. if (granted) {
  248. bCanRecord = YES;
  249. } else {
  250. }
  251. });
  252. }];
  253. }
  254. return bCanRecord;
  255. }
  256. - (void)timerFired:(NSTimer*)timer {
  257. self.seconds = self.seconds + 1;
  258. int minute = self.seconds/60;
  259. int s = self.seconds%60;
  260. NSString *str = [NSString stringWithFormat:@"%02d:%02d", minute, s];
  261. NSLog(@"timer:%@", str);
  262. int countdown = 60 - self.seconds;
  263. if (countdown <= 10) {
  264. [self.recordView setCountdown:countdown];
  265. }
  266. if (countdown <= 0) {
  267. [self.recordView removeFromSuperview];
  268. [self recordEnd];
  269. } else {
  270. [self notifyTyping:1];
  271. }
  272. }
  273. - (void)updateMeter:(NSTimer*)timer {
  274. double voiceMeter = 0;
  275. if ([self.recorder isRecording]) {
  276. [self.recorder updateMeters];
  277. //获取音量的平均值 [recorder averagePowerForChannel:0];
  278. //音量的最大值 [recorder peakPowerForChannel:0];
  279. double lowPassResults = pow(10, (0.05 * [self.recorder peakPowerForChannel:0]));
  280. voiceMeter = lowPassResults;
  281. }
  282. [self.recordView setVoiceImage:voiceMeter];
  283. }
  284. -(void)recordEnd {
  285. if (self.recorder.recording) {
  286. NSLog(@"stop record...");
  287. self.recordCanceled = NO;
  288. [self stopRecord];
  289. }
  290. }
  291. -(void)stopRecord {
  292. [self.recorder stop];
  293. [self.recordingTimer invalidate];
  294. self.recordingTimer = nil;
  295. [self.updateMeterTimer invalidate];
  296. self.updateMeterTimer = nil;
  297. AVAudioSession *audioSession = [AVAudioSession sharedInstance];
  298. BOOL r = [audioSession setActive:NO error:nil];
  299. if (!r) {
  300. NSLog(@"deactivate audio session fail");
  301. }
  302. }
  303. - (void)resetInputBarStatue {
  304. if (self.inputBarStatus != ChatInputBarRecordStatus && self.inputBarStatus != ChatInputBarMuteStatus) {
  305. self.inputBarStatus = ChatInputBarDefaultStatus;
  306. }
  307. }
  308. - (void)onSwitchBtn:(id)sender {
  309. if (sender == self.voiceSwitchBtn) {
  310. if (self.voiceInput && self.inputBarStatus != ChatInputBarDefaultStatus) {
  311. self.inputBarStatus = ChatInputBarKeyboardStatus;
  312. } else {
  313. self.inputBarStatus = ChatInputBarRecordStatus;
  314. }
  315. } else if(sender == self.emojSwitchBtn) {
  316. if (self.emojInput && self.inputBarStatus != ChatInputBarDefaultStatus) {
  317. self.inputBarStatus = ChatInputBarKeyboardStatus;
  318. } else {
  319. self.inputBarStatus = ChatInputBarEmojiStatus;
  320. }
  321. } else if (sender == self.pluginSwitchBtn) {
  322. if (self.pluginInput && self.inputBarStatus != ChatInputBarDefaultStatus) {
  323. self.inputBarStatus = ChatInputBarKeyboardStatus;
  324. } else {
  325. self.inputBarStatus = ChatInputBarPluginStatus;
  326. }
  327. }
  328. }
  329. - (void)setInputBarStatus:(ChatInputBarStatus)inputBarStatus {
  330. if (inputBarStatus == _inputBarStatus) {
  331. return;
  332. }
  333. if (_inputBarStatus == ChatInputBarMuteStatus) {
  334. [self.textInputView setUserInteractionEnabled:YES];
  335. [self.voiceInputBtn setEnabled:YES];
  336. [self.voiceSwitchBtn setEnabled:YES];
  337. [self.emojSwitchBtn setEnabled:YES];
  338. [self.pluginSwitchBtn setEnabled:YES];
  339. }
  340. _inputBarStatus = inputBarStatus;
  341. switch (inputBarStatus) {
  342. case ChatInputBarKeyboardStatus:
  343. self.voiceInput = NO;
  344. self.emojInput = NO;
  345. self.pluginInput = NO;
  346. self.textInput = YES;
  347. break;
  348. case ChatInputBarPluginStatus:
  349. self.voiceInput = NO;
  350. self.emojInput = NO;
  351. self.pluginInput = YES;
  352. self.textInput = NO;
  353. break;
  354. case ChatInputBarEmojiStatus:
  355. self.voiceInput = NO;
  356. self.emojInput = YES;
  357. self.pluginInput = NO;
  358. self.textInput = NO;
  359. break;
  360. case ChatInputBarRecordStatus:
  361. self.voiceInput = YES;
  362. self.emojInput = NO;
  363. self.pluginInput = NO;
  364. self.textInput = NO;
  365. break;
  366. case ChatInputBarPublicStatus:
  367. self.voiceInput = NO;
  368. self.emojInput = NO;
  369. self.pluginInput = NO;
  370. self.textInput = NO;
  371. break;
  372. case ChatInputBarDefaultStatus:
  373. self.voiceInput = NO;
  374. self.emojInput = NO;
  375. self.pluginInput = NO;
  376. self.textInput = YES;
  377. [self.textInputView resignFirstResponder];
  378. break;
  379. case ChatInputBarMuteStatus:
  380. self.voiceInput = NO;
  381. self.emojInput = NO;
  382. self.pluginInput = NO;
  383. self.textInput = YES;
  384. [self.textInputView setUserInteractionEnabled:NO];
  385. [self.voiceInputBtn setEnabled:NO];
  386. [self.voiceSwitchBtn setEnabled:NO];
  387. [self.emojSwitchBtn setEnabled:NO];
  388. [self.pluginSwitchBtn setEnabled:NO];
  389. break;
  390. default:
  391. break;
  392. }
  393. if (inputBarStatus != ChatInputBarKeyboardStatus) {
  394. if (self.textInputView.tintColor != [UIColor clearColor]) {
  395. self.textInputViewTintColor = self.textInputView.tintColor;
  396. }
  397. self.textInputView.tintColor = [UIColor clearColor];
  398. self.inputCoverView.hidden = NO;
  399. } else {
  400. self.textInputView.tintColor = self.textInputViewTintColor;
  401. self.inputCoverView.hidden = YES;
  402. }
  403. }
  404. - (void)setVoiceInput:(BOOL)voiceInput {
  405. _voiceInput = voiceInput;
  406. if (voiceInput) {
  407. [self.textInputView setHidden:YES];
  408. [self.voiceInputBtn setHidden:NO];
  409. if (self.textInputView.isFirstResponder) {
  410. [self.textInputView resignFirstResponder];
  411. }
  412. [self.voiceSwitchBtn setImage:[UIImage imageNamed:@"chat_input_bar_keyboard"] forState:UIControlStateNormal];
  413. } else {
  414. [self.textInputView setHidden:NO];
  415. [self.voiceInputBtn setHidden:YES];
  416. [self.voiceSwitchBtn setImage:[UIImage imageNamed:@"chat_input_bar_voice"] forState:UIControlStateNormal];
  417. }
  418. }
  419. - (void)setEmojInput:(BOOL)emojInput {
  420. _emojInput = emojInput;
  421. if (emojInput) {
  422. [self.textInputView setHidden:NO];
  423. [self.voiceInputBtn setHidden:YES];
  424. self.textInputView.inputView = self.emojInputView;
  425. if (!self.textInputView.isFirstResponder) {
  426. [self.textInputView becomeFirstResponder];
  427. }
  428. [self.textInputView reloadInputViews];
  429. [self.emojSwitchBtn setImage:[UIImage imageNamed:@"chat_input_bar_keyboard"] forState:UIControlStateNormal];
  430. } else {
  431. [self.emojSwitchBtn setImage:[UIImage imageNamed:@"chat_input_bar_emoj"] forState:UIControlStateNormal];
  432. }
  433. }
  434. - (void)setPluginInput:(BOOL)pluginInput {
  435. _pluginInput = pluginInput;
  436. if (pluginInput) {
  437. [self.textInputView setHidden:NO];
  438. [self.voiceInputBtn setHidden:YES];
  439. self.textInputView.inputView = self.pluginInputView;
  440. if (!self.textInputView.isFirstResponder) {
  441. [self.textInputView becomeFirstResponder];
  442. }
  443. [self.textInputView reloadInputViews];
  444. }
  445. }
  446. - (void)setTextInput:(BOOL)textInput {
  447. _textInput = textInput;
  448. if (textInput) {
  449. [self.textInputView setHidden:NO];
  450. [self.voiceInputBtn setHidden:YES];
  451. self.textInputView.inputView = nil;
  452. if (!self.textInputView.isFirstResponder && _inputBarStatus == ChatInputBarKeyboardStatus) {
  453. [self.textInputView becomeFirstResponder];
  454. }
  455. if (_inputBarStatus == ChatInputBarKeyboardStatus) {
  456. [self.textInputView reloadInputViews];
  457. }
  458. }
  459. }
  460. - (void)notifyTyping:(WFCCTypingType)type {
  461. double now = [[NSDate date] timeIntervalSince1970];
  462. if (self.lastTypingTime + TYPING_INTERVAL < now) {
  463. if ([self.delegate respondsToSelector:@selector(onTyping:)]) {
  464. [self.delegate onTyping:type];
  465. }
  466. self.lastTypingTime = now;
  467. }
  468. }
  469. - (void)setDraft:(NSString *)draft {
  470. NSError *__error = nil;
  471. NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:[draft dataUsingEncoding:NSUTF8StringEncoding]
  472. options:kNilOptions
  473. error:&__error];
  474. BOOL hasMentionInfo = NO;
  475. NSString *text = nil;
  476. NSMutableArray<WFCUMetionInfo *> *mentionInfos = [[NSMutableArray alloc] init];
  477. if (!__error) {
  478. if (dictionary[@"text"] != nil && [dictionary[@"mentions"] isKindOfClass:[NSArray class]]) {
  479. hasMentionInfo = YES;
  480. text = dictionary[@"text"];
  481. NSArray *mentions = dictionary[@"mentions"];
  482. [mentions enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  483. NSDictionary *dic = (NSDictionary *)obj;
  484. WFCUMetionInfo *mentionInfo = [[WFCUMetionInfo alloc] init];
  485. mentionInfo.target = dic[@"target"];
  486. mentionInfo.mentionType = [dic[@"type"] intValue];
  487. mentionInfo.range = NSMakeRange([dic[@"loc"] integerValue], [dic[@"len"] integerValue]);
  488. [mentionInfos addObject:mentionInfo];
  489. }];
  490. }
  491. }
  492. if (hasMentionInfo) {
  493. draft = text;
  494. }
  495. //防止弹出@选项
  496. if ([draft isEqualToString:@"@"]) {
  497. draft = @"@ ";
  498. }
  499. [self textView:self.textInputView shouldChangeTextInRange:NSMakeRange(0, 0) replacementText:draft];
  500. self.textInputView.text = draft;
  501. self.mentionInfos = mentionInfos;
  502. }
  503. - (NSString *)draft {
  504. if (self.mentionInfos.count) {
  505. NSMutableDictionary *dataDict = [NSMutableDictionary dictionary];
  506. [dataDict setObject:self.textInputView.text forKey:@"text"];
  507. NSMutableArray *mentions = [[NSMutableArray alloc] init];
  508. [self.mentionInfos enumerateObjectsUsingBlock:^(WFCUMetionInfo * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  509. NSMutableDictionary *dic = [[NSMutableDictionary alloc] init];
  510. [dic setObject:obj.target forKey:@"target"];
  511. [dic setObject:@(obj.mentionType) forKey:@"type"];
  512. [dic setObject:@(obj.range.location) forKey:@"loc"];
  513. [dic setObject:@(obj.range.length) forKey:@"len"];
  514. [mentions addObject:dic];
  515. }];
  516. [dataDict setObject:mentions forKey:@"mentions"];
  517. NSData *data = [NSJSONSerialization dataWithJSONObject:dataDict
  518. options:kNilOptions
  519. error:nil];
  520. return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
  521. } else {
  522. return self.textInputView.text;
  523. }
  524. }
  525. - (void)appendText:(NSString *)text {
  526. [self textView:self.textInputView shouldChangeTextInRange:NSMakeRange(self.textInputView.text.length, 0) replacementText:text];
  527. self.textInputView.text = [self.textInputView.text stringByAppendingString:text];
  528. }
  529. - (UIView *)emojInputView {
  530. if (!_emojInputView) {
  531. _emojInputView = [[WFCUFaceBoard alloc] init];
  532. ((WFCUFaceBoard*)_emojInputView).delegate = self;
  533. }
  534. return _emojInputView;
  535. }
  536. - (UIView *)pluginInputView {
  537. if (!_pluginInputView) {
  538. #if WFCU_SUPPORT_VOIP
  539. BOOL hasVoip = self.conversation.type == Single_Type || (self.conversation.type == Group_Type && [WFAVEngineKit sharedEngineKit].supportMultiCall);
  540. #else
  541. BOOL hasVoip = NO;
  542. #endif
  543. _pluginInputView = [[WFCUPluginBoardView alloc] initWithDelegate:self withVoip:hasVoip];
  544. }
  545. return _pluginInputView;
  546. }
  547. - (void)keyboardWillShow:(NSNotification *)notification {
  548. if (![self.textInputView isFirstResponder]) {
  549. return;
  550. }
  551. NSDictionary *userInfo = [notification userInfo];
  552. NSValue *value = [userInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
  553. CGRect keyboardRect = [value CGRectValue];
  554. int height = keyboardRect.size.height - kTabbarSafeBottomMargin;
  555. CGFloat duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
  556. CGRect frame = CGRectMake(0, self.superview.bounds.size.height - self.bounds.size.height - height, self.superview.bounds.size.width, self.bounds.size.height);
  557. [self.delegate willChangeFrame:frame withDuration:duration keyboardShowing:YES];
  558. self.backupFrame = frame;
  559. [UIView animateWithDuration:duration animations:^{
  560. self.frame = frame;
  561. }];
  562. }
  563. - (void)keyboardWillHide:(NSNotification *)notification {
  564. NSDictionary *userInfo = [notification userInfo];
  565. CGFloat duration = [[userInfo objectForKey:UIKeyboardAnimationDurationUserInfoKey] floatValue];
  566. CGRect frame = CGRectMake(0, self.superview.bounds.size.height - self.bounds.size.height, self.superview.bounds.size.width, self.bounds.size.height);
  567. [self.delegate willChangeFrame:frame withDuration:duration keyboardShowing:NO];
  568. self.backupFrame = frame;
  569. [UIView animateWithDuration:duration animations:^{
  570. self.frame = frame;
  571. }];
  572. }
  573. -(void)keyboardDidHide:(NSNotification *)notification{
  574. if ((self.emojInput || self.pluginInput || self.textInput) && self.inputBarStatus != ChatInputBarDefaultStatus) {
  575. [self.textInputView becomeFirstResponder];
  576. }
  577. }
  578. - (BOOL)appendMention:(NSString *)userId name:(NSString *)userName {
  579. if (self.conversation.type == Group_Type) {
  580. NSString *mentionText = [NSString stringWithFormat:@"@%@ ", userName];
  581. BOOL needDelay = NO;
  582. if(self.inputBarStatus == ChatInputBarDefaultStatus || self.inputBarStatus == ChatInputBarPluginStatus || self.inputBarStatus == ChatInputBarRecordStatus) {
  583. self.inputBarStatus = ChatInputBarKeyboardStatus;
  584. needDelay = YES;
  585. }
  586. if (needDelay) {
  587. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  588. [self didMentionType:1 user:userId range:NSMakeRange(self.textInputView.selectedRange.location, mentionText.length) text:mentionText];
  589. });
  590. } else {
  591. [self didMentionType:1 user:userId range:NSMakeRange(self.textInputView.selectedRange.location, mentionText.length) text:mentionText];
  592. }
  593. return YES;
  594. } else {
  595. return NO;
  596. }
  597. }
  598. - (void)paste:(id)sender {
  599. [self.textInputView paste:sender];
  600. }
  601. #pragma mark - AVAudioRecorderDelegate
  602. - (void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag {
  603. NSLog(@"record finish:%d", flag);
  604. if (!flag) {
  605. return;
  606. }
  607. if (self.recordCanceled) {
  608. return;
  609. }
  610. if (self.seconds < 1) {
  611. NSLog(@"record time too short");
  612. [[[UIAlertView alloc] initWithTitle:@"警告" message:@"录音时间太短了" delegate:nil cancelButtonTitle:@"确定" otherButtonTitles:nil, nil] show];
  613. return;
  614. }
  615. [self.delegate recordDidEnd:[recorder.url path] duration:self.seconds error:nil];
  616. [[NSFileManager defaultManager] removeItemAtURL:recorder.url error:nil];
  617. }
  618. #pragma mark - FaceBoardDelegate
  619. - (void)didTouchEmoj:(NSString *)emojString {
  620. NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc]initWithString:emojString];
  621. UIFont *font = [UIFont fontWithName:@"Heiti SC-Bold" size:16];
  622. [attStr addAttribute:(__bridge NSString*)kCTFontAttributeName value:(id)CFBridgingRelease(CTFontCreateWithName((CFStringRef)font.fontName,
  623. 16,
  624. NULL)) range:NSMakeRange(0, emojString.length)];
  625. NSInteger cursorPosition;
  626. if (self.textInputView.selectedTextRange) {
  627. cursorPosition = self.textInputView.selectedRange.location ;
  628. } else {
  629. cursorPosition = 0;
  630. }
  631. //获取光标位置
  632. if(cursorPosition> self.textInputView.textStorage.length)
  633. cursorPosition = self.textInputView.textStorage.length;
  634. [self.textInputView.textStorage
  635. insertAttributedString:attStr atIndex:cursorPosition];
  636. NSRange range;
  637. range.location = self.textInputView.selectedRange.location + emojString.length;
  638. range.length = 1;
  639. self.textInputView.selectedRange = range;
  640. }
  641. - (void)didTouchBackEmoj {
  642. [self.textInputView deleteBackward];
  643. }
  644. - (void)didTouchSendEmoj {
  645. [self sendAndCleanTextView];
  646. }
  647. - (void)sendAndCleanTextView {
  648. [self.delegate didTouchSend:self.textInputView.text withMentionInfos:self.mentionInfos];
  649. self.textInputView.text = nil;
  650. [self.mentionInfos removeAllObjects];
  651. [self changeTextViewHeight:32 needUpdateText:NO updateRange:NSMakeRange(0, 0)];
  652. }
  653. - (void)didSelectedSticker:(NSString *)stickerPath {
  654. if ([self.delegate respondsToSelector:@selector(didSelectSticker:)]) {
  655. [self.delegate didSelectSticker:stickerPath];
  656. }
  657. }
  658. #pragma mark - UITextViewDelegate
  659. - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
  660. if ([text isEqualToString:@"\n"]){ //判断输入的字是否是回车,即按下return
  661. [self sendAndCleanTextView];
  662. return NO;
  663. }
  664. BOOL needUpdateText = NO;
  665. if(self.conversation.type == Group_Type) {
  666. if ([text isEqualToString:@"@"]) {
  667. WFCUContactListViewController *pvc = [[WFCUContactListViewController alloc] init];
  668. pvc.selectContact = YES;
  669. pvc.multiSelect = NO;
  670. NSMutableArray *disabledUser = [[NSMutableArray alloc] init];
  671. [disabledUser addObject:[WFCCNetworkService sharedInstance].userId];
  672. pvc.disableUsers = disabledUser;
  673. NSMutableArray *candidateUser = [[NSMutableArray alloc] init];
  674. NSArray<WFCCGroupMember *> *members = [[WFCCIMService sharedWFCIMService] getGroupMembers:self.conversation.target forceUpdate:NO];
  675. for (WFCCGroupMember *member in members) {
  676. [candidateUser addObject:member.memberId];
  677. }
  678. pvc.candidateUsers = candidateUser;
  679. pvc.withoutCheckBox = YES;
  680. __weak typeof(self)ws = self;
  681. WFCCGroupInfo *groupInfo = [[WFCCIMService sharedWFCIMService] getGroupInfo:self.conversation.target refresh:NO];
  682. WFCCGroupMember *member = [[WFCCIMService sharedWFCIMService] getGroupMember:self.conversation.target memberId:[WFCCNetworkService sharedInstance].userId];
  683. if ([groupInfo.owner isEqualToString:[WFCCNetworkService sharedInstance].userId] || member.type == Member_Type_Manager) {
  684. pvc.showMentionAll = YES;
  685. pvc.mentionAll = ^{
  686. NSString *text = WFCString(@"@All");
  687. [ws didMentionType:2 user:nil range:NSMakeRange(range.location, text.length) text:text];
  688. };
  689. }
  690. pvc.selectResult = ^(NSArray<NSString *> *contacts) {
  691. if (contacts.count == 1) {
  692. WFCCUserInfo *userInfo = [[WFCCIMService sharedWFCIMService] getUserInfo:[contacts objectAtIndex:0] inGroup:self.conversation.target refresh:NO];
  693. NSString *name = userInfo.displayName;
  694. if (userInfo.groupAlias.length) {
  695. name = userInfo.groupAlias;
  696. }
  697. NSString *text = [NSString stringWithFormat:@"@%@ ", name];
  698. [ws didMentionType:1 user:[contacts objectAtIndex:0] range:NSMakeRange(range.location, text.length) text:text];
  699. } else {
  700. [ws didCancelMentionAtRange:range];
  701. }
  702. };
  703. pvc.cancelSelect = ^(void) {
  704. [ws didCancelMentionAtRange:range];
  705. };
  706. pvc.disableUsersSelected = YES;
  707. UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:pvc];
  708. [[self.delegate requireNavi] presentViewController:navi animated:YES completion:nil];
  709. return NO;
  710. }
  711. if (text.length == 0) {
  712. WFCUMetionInfo *deletedMention;
  713. for (WFCUMetionInfo *mentionInfo in self.mentionInfos) {
  714. if ((mentionInfo.range.location >= range.location && mentionInfo.range.location < range.location + range.length) ||
  715. (range.location >= mentionInfo.range.location && range.location < mentionInfo.range.location + mentionInfo.range.length)) {
  716. deletedMention = mentionInfo;
  717. }
  718. }
  719. if (deletedMention) {
  720. range = deletedMention.range;
  721. [self.mentionInfos removeObject:deletedMention];
  722. needUpdateText = YES;
  723. }
  724. } else {
  725. for (WFCUMetionInfo *mentionInfo in self.mentionInfos) {
  726. if (range.location <= mentionInfo.range.location) {
  727. mentionInfo.range = NSMakeRange(mentionInfo.range.location - range.length + text.length, mentionInfo.range.length);
  728. }
  729. }
  730. }
  731. }
  732. NSString *oldStr = textView.text;
  733. NSString *newStr = [oldStr stringByReplacingCharactersInRange:range withString:text];
  734. CGFloat textAreaWidth = textView.frame.size.width - 2 * textView.textContainer.lineFragmentPadding;
  735. CGSize size = [WFCUUtilities getTextDrawingSize:newStr font:[UIFont systemFontOfSize:16] constrainedSize:CGSizeMake(textAreaWidth, 1000)];
  736. [self changeTextViewHeight:size.height needUpdateText:needUpdateText updateRange:range];
  737. return YES;
  738. }
  739. - (void)changeTextViewHeight:(CGFloat)height needUpdateText:(BOOL)needUpdateText updateRange:(NSRange)range {
  740. CGRect tvFrame = self.textInputView.frame;
  741. CGRect baseFrame = self.frame;
  742. CGRect voiceFrame = self.voiceSwitchBtn.frame;
  743. CGRect emojFrame = self.emojSwitchBtn.frame;
  744. CGRect extendFrame = self.pluginSwitchBtn.frame;
  745. CGFloat diff = 0;
  746. if (height <= 32.f) {
  747. tvFrame.size.height = 32.f;
  748. diff = (48.f - baseFrame.size.height);
  749. baseFrame.size.height = 48.f;
  750. } else if (height > 32.f && height < 50.f) {
  751. tvFrame.size.height = 50.f;
  752. diff = (66.f - baseFrame.size.height);
  753. baseFrame.size.height = 66.f;
  754. } else {
  755. tvFrame.size.height = 65.f;
  756. diff = (81.f - baseFrame.size.height);
  757. baseFrame.size.height = 81.f;
  758. }
  759. baseFrame.origin.y -= diff;
  760. voiceFrame.origin.y += diff;
  761. emojFrame.origin.y += diff;
  762. extendFrame.origin.y += diff;
  763. float duration = 0.5f;
  764. [self.delegate willChangeFrame:baseFrame withDuration:duration keyboardShowing:YES];
  765. self.backupFrame = baseFrame;
  766. __weak typeof(self)ws = self;
  767. [UIView animateWithDuration:duration animations:^{
  768. ws.textInputView.frame = tvFrame;
  769. ws.inputCoverView.frame = ws.textInputView.bounds;
  770. self.frame = baseFrame;
  771. self.voiceSwitchBtn.frame = voiceFrame;
  772. self.emojSwitchBtn.frame = emojFrame;
  773. self.pluginSwitchBtn.frame = extendFrame;
  774. if(needUpdateText) {
  775. [ws.textInputView.textStorage replaceCharactersInRange:range withString:@" "];
  776. }
  777. }];
  778. }
  779. - (void)textViewDidChangeSelection:(UITextView *)textView {
  780. if (self.textInputView == textView && self.conversation.type == Group_Type) {
  781. NSRange range = textView.selectedRange;
  782. for (WFCUMetionInfo *mention in self.mentionInfos) {
  783. if (range.location > mention.range.location && range.location < mention.range.location + mention.range.length) {
  784. if (range.length == 0) {
  785. if(range.location == mention.range.location + mention.range.length - 1) {
  786. range.location = mention.range.location;
  787. } else {
  788. range = NSMakeRange(mention.range.location + mention.range.length, 0);
  789. }
  790. } else {
  791. long length = range.length - (mention.range.location + mention.range.length) + range.location;
  792. if (length < 0) {
  793. length = 0;
  794. }
  795. range = NSMakeRange(mention.range.location + mention.range.length, length);
  796. }
  797. textView.selectedRange = range;
  798. break;
  799. }
  800. }
  801. }
  802. }
  803. - (void)textViewDidChange:(UITextView *)textView {
  804. if (textView.text.length > 0) {
  805. [self notifyTyping:0];
  806. }
  807. }
  808. - (BOOL)textViewShouldBeginEditing:(UITextView *)textView {
  809. if (self.inputBarStatus == ChatInputBarDefaultStatus) {
  810. self.inputBarStatus = ChatInputBarKeyboardStatus;
  811. }
  812. return YES;
  813. }
  814. - (BOOL)textViewShouldEndEditing:(UITextView *)textView {
  815. return YES;
  816. }
  817. #pragma mark - PluginBoardViewDelegate
  818. - (void)onItemClicked:(NSUInteger)itemTag {
  819. UINavigationController *navi = [self.delegate requireNavi];
  820. self.inputBarStatus = ChatInputBarDefaultStatus;
  821. if (itemTag == 1) {
  822. UIImagePickerController *picker = [[UIImagePickerController alloc] init];
  823. picker.delegate = self;
  824. #if TARGET_IPHONE_SIMULATOR
  825. picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
  826. #else
  827. if (itemTag == 1) {
  828. picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
  829. } else if(itemTag == 2){
  830. picker.sourceType = UIImagePickerControllerSourceTypeCamera;
  831. }
  832. #endif
  833. picker.videoExportPreset = AVAssetExportPresetPassthrough;
  834. picker.mediaTypes = [UIImagePickerController availableMediaTypesForSourceType:picker.sourceType];
  835. [navi presentViewController:picker animated:YES completion:nil];
  836. [self checkAndAlertCameraAccessRight];
  837. } else if(itemTag == 2) {
  838. #if TARGET_IPHONE_SIMULATOR
  839. [self makeToast:@"模拟器不支持相机" duration:1 position:CSToastPositionCenter];
  840. UIImagePickerController *picker = [[UIImagePickerController alloc] init];
  841. picker.delegate = self;
  842. picker.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
  843. [navi presentViewController:picker animated:YES completion:nil];
  844. [self checkAndAlertCameraAccessRight];
  845. #else
  846. KZVideoViewController *videoVC = [[KZVideoViewController alloc] init];
  847. videoVC.delegate = self;
  848. [videoVC startAnimationWithType:KZVideoViewShowTypeSingle selectExist:NO];
  849. double now = [[NSDate date] timeIntervalSince1970];
  850. [self notifyTyping:2];
  851. #endif
  852. } else if(itemTag == 3){
  853. WFCULocationViewController *vc = [[WFCULocationViewController alloc] initWithDelegate:self];
  854. UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc];
  855. [navi presentViewController:nav animated:YES completion:nil];
  856. [self notifyTyping:3];
  857. return;
  858. } else if(itemTag == 4) {
  859. #if WFCU_SUPPORT_VOIP
  860. UIActionSheet *actionSheet =
  861. [[UIActionSheet alloc] initWithTitle:nil
  862. delegate:self
  863. cancelButtonTitle:WFCString(@"Cancel")
  864. destructiveButtonTitle:@"视频"
  865. otherButtonTitles:@"音频", nil];
  866. [actionSheet showInView:self.parentView];
  867. #endif
  868. } else if(itemTag == 5) {
  869. WFCUSelectFileViewController *sfvc = [[WFCUSelectFileViewController alloc] init];
  870. UINavigationController *navi = [[UINavigationController alloc] initWithRootViewController:sfvc];
  871. __weak typeof(self) ws = self;
  872. sfvc.selectResult = ^(NSArray *selectFiles) {
  873. [ws.delegate didSelectFiles:selectFiles];
  874. };
  875. [[self.delegate requireNavi] presentViewController:navi animated:YES completion:nil];
  876. [self notifyTyping:4];
  877. }
  878. }
  879. - (void)checkAndAlertCameraAccessRight {
  880. AVAuthorizationStatus authStatus =
  881. [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
  882. if (authStatus == AVAuthorizationStatusDenied ||
  883. authStatus == AVAuthorizationStatusRestricted) {
  884. UIAlertView *alertView = [[UIAlertView alloc]
  885. initWithTitle:@"拍照权限"
  886. message:@"需要拍照权限,请在设置里打开"
  887. delegate:nil
  888. cancelButtonTitle:@"确认"
  889. otherButtonTitles:nil, nil];
  890. [alertView show];
  891. }
  892. }
  893. #pragma mark - UIImagePickerControllerDelegate<NSObject>
  894. //- (void)imagePickerController:(UIImagePickerController *)picker
  895. // didFinishPickingImage:(UIImage *)image
  896. // editingInfo:(NSDictionary *)editingInfo {
  897. // [picker dismissViewControllerAnimated:YES completion:nil];
  898. // [self.delegate imageDidCapture:image];
  899. //}
  900. - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey, id> *)info {
  901. NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
  902. if([mediaType isEqualToString:@"public.movie"]) {
  903. NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
  904. NSString *url = [videoURL absoluteString];
  905. url = [url stringByReplacingOccurrencesOfString:@"file:///private" withString:@""];
  906. //获取视频的thumbnail
  907. AVURLAsset *asset1 = [[AVURLAsset alloc] initWithURL:videoURL options:nil];
  908. AVAssetImageGenerator *generate1 = [[AVAssetImageGenerator alloc] initWithAsset:asset1];
  909. generate1.appliesPreferredTrackTransform = YES;
  910. NSError *err = NULL;
  911. CMTime time = CMTimeMake(1, 2);
  912. CGImageRef oneRef = [generate1 copyCGImageAtTime:time actualTime:NULL error:&err];
  913. UIImage *thumbnail = [[UIImage alloc] initWithCGImage:oneRef];
  914. thumbnail = [WFCCUtilities generateThumbnail:thumbnail withWidth:120 withHeight:120];
  915. AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
  916. NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
  917. NSString *CompressionVideoPaht = [WFCCUtilities getDocumentPathWithComponent:@"/VIDEO"];
  918. AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:@"AVAssetExportPresetMediumQuality"];
  919. NSDateFormatter *formater = [[NSDateFormatter alloc] init];// 用时间, 给文件重新命名, 防止视频存储覆盖,
  920. [formater setDateFormat:@"yyyy-MM-dd-HH:mm:ss"];
  921. NSFileManager *manager = [NSFileManager defaultManager];
  922. BOOL isExists = [manager fileExistsAtPath:CompressionVideoPaht];
  923. if (!isExists) {
  924. [manager createDirectoryAtPath:CompressionVideoPaht withIntermediateDirectories:YES attributes:nil error:nil];
  925. }
  926. //
  927. NSString *resultPath = [CompressionVideoPaht stringByAppendingPathComponent:[NSString stringWithFormat:@"outputJFVideo-%@.mov", [formater stringFromDate:[NSDate date]]]];
  928. NSLog(@"resultPath = %@",resultPath);
  929. exportSession.outputURL = [NSURL fileURLWithPath:resultPath];
  930. exportSession.outputFileType = AVFileTypeMPEG4;
  931. exportSession.shouldOptimizeForNetworkUse = YES;
  932. MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:picker.view animated:YES];
  933. hud.label.text = @"处理中...";
  934. [hud showAnimated:YES];
  935. __weak typeof(self)ws = self;
  936. [exportSession exportAsynchronouslyWithCompletionHandler:^(void)
  937. {
  938. if (exportSession.status == AVAssetExportSessionStatusCompleted) {
  939. NSData *data = [NSData dataWithContentsOfFile:resultPath];
  940. float memorySize = (float)data.length / 1024 / 1024;
  941. NSLog(@"视频压缩后大小 %f", memorySize);
  942. dispatch_async(dispatch_get_main_queue(), ^{
  943. [hud hideAnimated:YES];
  944. [picker dismissViewControllerAnimated:YES completion:nil];
  945. [ws.delegate videoDidCapture:resultPath thumbnail:thumbnail duration:10];
  946. });
  947. } else {
  948. dispatch_async(dispatch_get_main_queue(), ^{
  949. [hud hideAnimated:YES];
  950. [picker dismissViewControllerAnimated:YES completion:nil];
  951. });
  952. NSLog(@"压缩失败");
  953. }
  954. }];
  955. } else if ([mediaType isEqualToString:@"public.image"]) {
  956. [picker dismissViewControllerAnimated:YES completion:nil];
  957. UIImage* image = [info objectForKey:UIImagePickerControllerEditedImage];
  958. if (!image)
  959. image = [info objectForKey:UIImagePickerControllerOriginalImage];
  960. [self.delegate imageDidCapture:image];
  961. }
  962. }
  963. - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
  964. [picker dismissViewControllerAnimated:YES completion:nil];
  965. }
  966. #pragma mark - LocationViewControllerDelegate <NSObject>
  967. - (void)onSendLocation:(WFCULocationPoint *)locationPoint {
  968. [self.delegate locationDidSelect:locationPoint.coordinate locationName:locationPoint.title mapScreenShot:locationPoint.thumbnail];
  969. }
  970. #pragma mark - UIActionSheetDelegate
  971. - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
  972. if (buttonIndex == 0) {
  973. #if WFCU_SUPPORT_VOIP
  974. [self.delegate didTouchVideoBtn:NO];
  975. #endif
  976. } else if(buttonIndex == 1) {
  977. #if WFCU_SUPPORT_VOIP
  978. [self.delegate didTouchVideoBtn:YES];
  979. #endif
  980. }
  981. }
  982. #pragma mark - KZVideoViewControllerDelegate
  983. - (void)videoViewController:(KZVideoViewController *)videoController didCaptureImage:(UIImage *)image {
  984. [self.delegate imageDidCapture:image];
  985. }
  986. - (void)videoViewController:(KZVideoViewController *)videoController didRecordVideo:(KZVideoModel *)videoModel {
  987. [self.delegate videoDidCapture:videoModel.videoAbsolutePath thumbnail:[UIImage imageWithContentsOfFile:videoModel.thumAbsolutePath] duration:10];
  988. }
  989. - (void)videoViewControllerDidCancel:(KZVideoViewController *)videoController {
  990. }
  991. #pragma mark - WFCUMentionUserDelegate
  992. - (void)didMentionType:(int)type user:(NSString *)userId range:(NSRange)range text:(NSString *)text {
  993. [self textView:self.textInputView shouldChangeTextInRange:NSMakeRange(range.location, 0) replacementText:text];
  994. [self.mentionInfos addObject:[[WFCUMetionInfo alloc] initWithType:type target:userId range:NSMakeRange(range.location, range.length)]];
  995. NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc]initWithString:text];
  996. UIFont *font = [UIFont fontWithName:@"Heiti SC-Bold" size:16];
  997. [attStr addAttribute:(__bridge NSString*)kCTFontAttributeName value:(id)CFBridgingRelease(CTFontCreateWithName((CFStringRef)font.fontName,
  998. 16,
  999. NULL)) range:NSMakeRange(0, text.length)];
  1000. [self.textInputView.textStorage
  1001. insertAttributedString:attStr atIndex:range.location];
  1002. range.location += range.length;
  1003. range.length = 0;
  1004. self.textInputView.selectedRange = range;
  1005. dispatch_async(dispatch_get_main_queue(), ^{
  1006. if (!self.textInputView.isFirstResponder) {
  1007. [self.textInputView becomeFirstResponder];
  1008. }
  1009. });
  1010. }
  1011. - (void)didCancelMentionAtRange:(NSRange)range {
  1012. NSMutableAttributedString *attStr = [[NSMutableAttributedString alloc]initWithString:@"@"];
  1013. UIFont *font = [UIFont fontWithName:@"Heiti SC-Bold" size:16];
  1014. [attStr addAttribute:(__bridge NSString*)kCTFontAttributeName value:(id)CFBridgingRelease(CTFontCreateWithName((CFStringRef)font.fontName,
  1015. 16,
  1016. NULL)) range:NSMakeRange(0, 1)];
  1017. [self.textInputView.textStorage
  1018. insertAttributedString:attStr atIndex:range.location];
  1019. range.location += 1;
  1020. range.length = 0;
  1021. self.textInputView.selectedRange = range;
  1022. dispatch_async(dispatch_get_main_queue(), ^{
  1023. if (!self.textInputView.isFirstResponder) {
  1024. [self.textInputView becomeFirstResponder];
  1025. }
  1026. });
  1027. }
  1028. - (void)dealloc {
  1029. [[NSNotificationCenter defaultCenter] removeObserver:self];
  1030. }
  1031. @end