IMAMsg.m 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647
  1. //
  2. // IMAMsg.m
  3. // TIMAdapter
  4. //
  5. // Created by AlexiChen on 16/1/29.
  6. // Copyright © 2016年 AlexiChen. All rights reserved.
  7. //
  8. #import "IMAMsg.h"
  9. #import "CustomElemCmd.h"
  10. @interface IMAMsg ()
  11. @property (nonatomic, strong) NSMutableDictionary *affixParams;
  12. @property (nonatomic, assign) NSInteger status;
  13. @end
  14. @implementation IMAMsg
  15. - (instancetype)initWith:(TIMMessage *)msg type:(IMAMSGType)type
  16. {
  17. if (self = [super init])
  18. {
  19. _msg = msg;
  20. _type = type;
  21. _status = EIMAMsg_Init;
  22. }
  23. return self;
  24. }
  25. + (instancetype)msgWithText:(NSString *)text
  26. {
  27. TIMTextElem *elem = [[TIMTextElem alloc] init];
  28. elem.text = text;
  29. TIMMessage *msg = [[TIMMessage alloc] init];
  30. [msg addElem:elem];
  31. return [[IMAMsg alloc] initWith:msg type:EIMAMSG_Text];
  32. }
  33. + (instancetype)msgWithImage:(UIImage *)image isOrignal:(BOOL)origal
  34. {
  35. // MyMsgPicModel* model = [[MyMsgPicModel alloc] init];
  36. CGFloat scale = 1;
  37. scale = MIN(kChatPicThumbMaxHeight/image.size.height, kChatPicThumbMaxWidth/image.size.width);
  38. CGFloat picHeight = image.size.height;
  39. CGFloat picWidth = image.size.width;
  40. NSInteger picThumbHeight = (NSInteger) (picHeight * scale + 1);
  41. NSInteger picThumbWidth = (NSInteger) (picWidth * scale + 1);
  42. NSFileManager *fileManager = [NSFileManager defaultManager];
  43. NSString *nsTmpDIr = NSTemporaryDirectory();
  44. NSString *filePath = [NSString stringWithFormat:@"%@uploadFile%3.f", nsTmpDIr, [NSDate timeIntervalSinceReferenceDate]];
  45. BOOL isDirectory = NO;
  46. NSError *err = nil;
  47. // 当前sdk仅支持文件路径上传图片,将图片存在本地
  48. if ([fileManager fileExistsAtPath:filePath isDirectory:&isDirectory])
  49. {
  50. if (![fileManager removeItemAtPath:nsTmpDIr error:&err])
  51. {
  52. DebugLog(@"Upload Image Failed: same upload filename: %@", err);
  53. return nil;
  54. }
  55. }
  56. if (![fileManager createFileAtPath:filePath contents:UIImageJPEGRepresentation(image, 1) attributes:nil])
  57. {
  58. DebugLog(@"Upload Image Failed: fail to create uploadfile: %@", err);
  59. return nil;
  60. }
  61. NSString *thumbPath = [NSString stringWithFormat:@"%@uploadFile%3.f_ThumbImage", nsTmpDIr, [NSDate timeIntervalSinceReferenceDate]];
  62. UIImage *thumbImage = [image thumbnailWithSize:CGSizeMake(picThumbWidth, picThumbHeight)];
  63. if (![fileManager createFileAtPath:thumbPath contents:UIImageJPEGRepresentation(thumbImage, 1) attributes:nil])
  64. {
  65. DebugLog(@"Upload Image Failed: fail to create uploadfile: %@", err);
  66. return nil;
  67. }
  68. TIMImageElem *elem = [[TIMImageElem alloc] init];
  69. elem.path = filePath;
  70. if (origal)
  71. {
  72. elem.level = TIM_IMAGE_COMPRESS_ORIGIN;
  73. }
  74. else
  75. {
  76. elem.level = TIM_IMAGE_COMPRESS_HIGH;
  77. }
  78. TIMMessage *msg = [[TIMMessage alloc] init];
  79. [msg addElem:elem];
  80. IMAMsg *imamsg = [[IMAMsg alloc] initWith:msg type:EIMAMSG_Image];
  81. [imamsg addInteger:picThumbHeight forKey:kIMAMSG_Image_ThumbHeight];
  82. [imamsg addInteger:picThumbWidth forKey:kIMAMSG_Image_ThumbWidth];
  83. [imamsg addString:filePath forKey:kIMAMSG_Image_OrignalPath];
  84. [imamsg addString:thumbPath forKey:kIMAMSG_Image_ThumbPath];
  85. return imamsg;
  86. }
  87. + (instancetype)msgWithDate:(NSDate *)timetip
  88. {
  89. TIMCustomElem *elem = [[TIMCustomElem alloc] init];
  90. [elem setFollowTime:timetip];
  91. TIMMessage *msg = [[TIMMessage alloc] init];
  92. [msg addElem:elem];
  93. IMAMsg *imamsg = [[IMAMsg alloc] initWith:msg type:EIMAMSG_TimeTip];
  94. return imamsg;
  95. }
  96. + (instancetype)msgWithRevoked:(NSString *)sender
  97. {
  98. TIMCustomElem *elem = [[TIMCustomElem alloc] init];
  99. NSDictionary *dataDic = @{@"sender":sender, @"REVOKED":@1};
  100. NSError *error = nil;
  101. elem.data = [NSJSONSerialization dataWithJSONObject:dataDic options:NSJSONWritingPrettyPrinted error:&error];
  102. TIMMessage *msg = [[TIMMessage alloc] init];
  103. [msg addElem:elem];
  104. IMAMsg *imamsg = [[IMAMsg alloc] initWith:msg type:EIMAMSG_RevokedTip];
  105. return imamsg;
  106. }
  107. + (instancetype)msgWithFilePath:(NSURL *)filePath
  108. {
  109. if (!filePath)
  110. {
  111. return nil;
  112. }
  113. TIMFileElem *elem = [[TIMFileElem alloc] init];
  114. // elem.data = [NSData dataWithContentsOfURL:filePath];
  115. elem.path = [filePath absoluteString];
  116. elem.fileSize = (int)elem.fileSize;
  117. elem.filename = [filePath absoluteString];
  118. TIMMessage *msg = [[TIMMessage alloc] init];
  119. [msg addElem:elem];
  120. return [[IMAMsg alloc] initWith:msg type:EIMAMSG_File];
  121. }
  122. + (instancetype)msgWithSound:(NSData *)data duration:(NSInteger)dur
  123. //+ (instancetype)msgWithSoundPath:(NSString *)path duration:(NSInteger)dur
  124. {
  125. if (!data)
  126. {
  127. return nil;
  128. }
  129. //保存到本地
  130. NSString *cache = [PathUtility getCachePath];
  131. NSString *loginId = [[TIMManager sharedInstance] getLoginUser];
  132. NSDate *date = [[NSDate alloc] init];
  133. NSTimeInterval time = [date timeIntervalSince1970];
  134. NSString *timeStr = [NSString stringWithFormat:@"%llu",(unsigned long long)time];
  135. NSString *soundSaveDir = [NSString stringWithFormat:@"%@/%@/Audio",cache,loginId];
  136. if (![PathUtility isExistFile:soundSaveDir])
  137. {
  138. BOOL isCreateDir = [[NSFileManager defaultManager] createDirectoryAtPath:soundSaveDir withIntermediateDirectories:YES attributes:nil error:nil];
  139. if (!isCreateDir)
  140. {
  141. return nil;
  142. }
  143. }
  144. NSString *soundSavePath = [NSString stringWithFormat:@"%@/%@",soundSaveDir,timeStr];
  145. if (![PathUtility isExistFile:soundSavePath])
  146. {
  147. BOOL isCreate = [[NSFileManager defaultManager] createFileAtPath:soundSavePath contents:nil attributes:nil];
  148. if (!isCreate)
  149. {
  150. return nil;
  151. }
  152. }
  153. BOOL isWrite = [data writeToFile:soundSavePath atomically:YES];
  154. if (!isWrite)
  155. {
  156. return nil;
  157. }
  158. TIMSoundElem *elem = [[TIMSoundElem alloc] init];
  159. elem.path = soundSavePath;
  160. elem.second = (int)dur;
  161. TIMMessage *msg = [[TIMMessage alloc] init];
  162. [msg addElem:elem];
  163. return [[IMAMsg alloc] initWith:msg type:EIMAMSG_Sound];
  164. }
  165. + (instancetype)msgWithEmptySound
  166. {
  167. TIMSoundElem *elem = [[TIMSoundElem alloc] init];
  168. TIMMessage *msg = [[TIMMessage alloc] init];
  169. [msg addElem:elem];
  170. return [[IMAMsg alloc] initWith:msg type:EIMAMSG_Sound];
  171. }
  172. + (instancetype)msgWithCustom:(NSInteger)command
  173. {
  174. return [IMAMsg msgWithCustom:command param:nil];
  175. }
  176. + (instancetype)msgWithCustom:(NSInteger)command param:(NSString *)param
  177. {
  178. CustomElemCmd *cmd = [[CustomElemCmd alloc] initWith:command param:param];
  179. TIMCustomElem *elem = [[TIMCustomElem alloc] init];
  180. elem.data = [cmd packToSendData];
  181. TIMMessage *customMsg = [[TIMMessage alloc] init];
  182. [customMsg addElem:elem];
  183. return [[IMAMsg alloc] initWith:customMsg type:command];
  184. }
  185. + (instancetype)msgWith:(TIMMessage *)msg
  186. {
  187. // EIMAMSG_Text, // 文本
  188. // EIMAMSG_Image, // 图片
  189. // EIMAMSG_File, // 文件
  190. // EIMAMSG_Sound, // 语音
  191. // EIMAMSG_Face, // 表情
  192. // EIMAMSG_Location, // 定位
  193. // EIMAMSG_Video, // 视频消息
  194. // EIMAMSG_Custom, // 自定义
  195. // EIMAMSG_GroupTips, // 群提醒
  196. // EIMAMSG_GroupSystem, // 群系统消息
  197. // EIMAMSG_SNSSystem, // 关系链消息
  198. // EIMAMSG_ProfileSystem, // 资料变更消息
  199. if (msg.elemCount == 0)
  200. {
  201. return nil;
  202. }
  203. IMAMSGType type = EIMAMSG_Unknown;
  204. #if kTestChatAttachment
  205. if (msg.elemCount > 1)
  206. {
  207. // TODO:后期使用富文本显示
  208. type = EIMAMSG_Multi;
  209. }
  210. else
  211. #endif
  212. {
  213. TIMElem *elem = [msg getElem:0];
  214. Class eleCls = [elem class];
  215. if (eleCls == [TIMTextElem class])
  216. {
  217. type = EIMAMSG_Text;
  218. }
  219. else if (eleCls == [TIMImageElem class])
  220. {
  221. type = EIMAMSG_Image;
  222. }
  223. else if (eleCls == [TIMFileElem class])
  224. {
  225. type = EIMAMSG_File;
  226. }
  227. else if (eleCls == [TIMFaceElem class])
  228. {
  229. type = EIMAMSG_Face;
  230. }
  231. else if (eleCls == [TIMLocationElem class])
  232. {
  233. type = EIMAMSG_Location;
  234. }
  235. else if (eleCls == [TIMSoundElem class])
  236. {
  237. type = EIMAMSG_Sound;
  238. }
  239. else if (eleCls == [TIMUGCElem class])
  240. {
  241. type = EIMAMSG_Video;
  242. }
  243. else if (eleCls == [TIMCustomElem class])
  244. {
  245. type = EIMAMSG_Custom;
  246. }
  247. else if (eleCls == [TIMGroupTipsElem class])
  248. {
  249. type = EIMAMSG_GroupTips;
  250. }
  251. else if (eleCls == [TIMGroupSystemElem class])
  252. {
  253. type = EIMAMSG_GroupSystem;
  254. }
  255. else if (eleCls == [TIMProfileSystemElem class])
  256. {
  257. type = EIMAMSG_ProfileSystem;
  258. }
  259. else if (eleCls == [TIMSNSSystemElem class])
  260. {
  261. type = EIMAMSG_SNSSystem;
  262. }
  263. }
  264. // if (type == EIMAMSG_Unknown)
  265. // {
  266. // DebugLog(ASLocalizedString(@"不支持的消息类型"));
  267. // return ni;
  268. // }
  269. IMAMsg *imamsg = [[IMAMsg alloc] initWith:msg type:type];
  270. [imamsg changeTo:(NSInteger)msg.status needRefresh:NO];
  271. return imamsg;
  272. }
  273. + (instancetype)msgWithVideoPath:(NSString *)videoPath;
  274. {
  275. if (!videoPath)
  276. {
  277. return nil;
  278. }
  279. //视频截图
  280. AVURLAsset *urlAsset = [[AVURLAsset alloc] initWithURL:[NSURL fileURLWithPath:videoPath] options:nil];
  281. AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:urlAsset];
  282. imageGenerator.appliesPreferredTrackTransform = YES; // 截图的时候调整到正确的方向
  283. CMTime time = CMTimeMakeWithSeconds(1.0, 30); // 1.0为截取视频1.0秒处的图片,30为每秒30帧
  284. CGImageRef cgImage = [imageGenerator copyCGImageAtTime:time actualTime:nil error:nil];
  285. UIImage *image = [UIImage imageWithCGImage:cgImage];
  286. UIGraphicsBeginImageContext(CGSizeMake(240, 320));
  287. // 绘制改变大小的图片
  288. [image drawInRect:CGRectMake(0,0, 240, 320)];
  289. // 从当前context中创建一个改变大小后的图片
  290. UIImage* scaledImage =UIGraphicsGetImageFromCurrentImageContext();
  291. // 使当前的context出堆栈
  292. UIGraphicsEndImageContext();
  293. NSData *snapshotData = UIImageJPEGRepresentation(scaledImage, 0.75);
  294. //保存截图到临时目录
  295. NSString *tempDir = NSTemporaryDirectory();
  296. NSString *snapshotPath = [NSString stringWithFormat:@"%@%3.f", tempDir, [NSDate timeIntervalSinceReferenceDate]];
  297. NSError *err;
  298. NSFileManager *fileMgr = [NSFileManager defaultManager];
  299. if (![fileMgr createFileAtPath:snapshotPath contents:snapshotData attributes:nil])
  300. {
  301. DebugLog(@"Upload Image Failed: fail to create uploadfile: %@", err);
  302. return nil;
  303. }
  304. //创建 TIMUGCElem
  305. TIMUGCVideo* video = [[TIMUGCVideo alloc] init];
  306. video.type = @"mp4";
  307. video.duration = (int)urlAsset.duration.value/urlAsset.duration.timescale;
  308. TIMUGCCover *corver = [[TIMUGCCover alloc] init];
  309. corver.type = @"jpg";
  310. corver.width = scaledImage.size.width;
  311. corver.height = scaledImage.size.height;
  312. TIMUGCElem* elem = [[TIMUGCElem alloc] init];
  313. elem.video = video;
  314. elem.videoPath = videoPath;
  315. elem.coverPath = snapshotPath;
  316. elem.cover = corver;
  317. TIMMessage* msg = [[TIMMessage alloc] init];
  318. [msg addElem:elem];
  319. IMAMsg *videoMsg = [[IMAMsg alloc] initWith:msg type:EIMAMSG_Video];
  320. return videoMsg;
  321. }
  322. + (void)convertToMP4:(AVURLAsset*)avAsset videoPath:(NSString*)videoPath succ:(void (^)())succ fail:(void (^)())fail
  323. {
  324. NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
  325. if ([compatiblePresets containsObject:AVAssetExportPresetHighestQuality])
  326. {
  327. AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:avAsset presetName:AVAssetExportPresetHighestQuality];
  328. exportSession.outputURL = [NSURL fileURLWithPath:videoPath];
  329. exportSession.outputFileType = AVFileTypeMPEG4;
  330. CMTime start = CMTimeMakeWithSeconds(0, avAsset.duration.timescale);
  331. CMTime duration = avAsset.duration;
  332. CMTimeRange range = CMTimeRangeMake(start, duration);
  333. exportSession.timeRange = range;
  334. [exportSession exportAsynchronouslyWithCompletionHandler:^{
  335. switch ([exportSession status])
  336. {
  337. case AVAssetExportSessionStatusFailed:
  338. NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]);
  339. fail();
  340. break;
  341. case AVAssetExportSessionStatusCancelled:
  342. NSLog(@"Export canceled");
  343. fail();
  344. break;
  345. default:
  346. succ();
  347. break;
  348. }
  349. }];
  350. }
  351. }
  352. - (NSString *)msgTime
  353. {
  354. NSDate *date = [_msg timestamp];
  355. NSString *time = [date shortTimeTextOfDate];
  356. return time;
  357. }
  358. - (NSInteger)status
  359. {
  360. if (_status == EIMAMsg_Init || _status == EIMAMsg_WillSending)
  361. {
  362. return _status;
  363. }
  364. else
  365. {
  366. return [_msg status];
  367. }
  368. }
  369. - (IMAUser *)getSender
  370. {
  371. if ([[_msg getConversation] getType] == TIM_C2C)
  372. {
  373. // NSString *sender = [_msg sender];
  374. // IMAUser *user = [[IMAPlatform sharedInstance].contactMgr getUserByUserId:sender];
  375. IMAUser *user = [[IMAUser alloc] initWith:[_msg sender]];
  376. return user;
  377. }
  378. else if ([[_msg getConversation] getType] == TIM_GROUP)
  379. {
  380. IMAGroupMember *member = [[IMAGroupMember alloc] initWithMemberInfo:[_msg getSenderGroupMemberProfile]];
  381. if (member)
  382. {
  383. IMAUser *user = [[IMAUser alloc] initWithUserInfo:[_msg getSenderProfile]];
  384. [member setIcon:user.icon];
  385. if (member.memberInfo.nameCard.length <= 0)
  386. {
  387. [member setNickName:[user showTitle]];
  388. }
  389. }
  390. return member;
  391. }
  392. return nil;
  393. }
  394. - (NSString *)messageTip
  395. {
  396. TIMConversation *conv = [_msg getConversation];
  397. TIMConversationType type = [conv getType];
  398. TIMElem *elem = (TIMElem *)[_msg getElem:0];
  399. if (elem)
  400. {
  401. if (type == TIM_C2C)
  402. {
  403. return [elem showDescriptionOf:self];
  404. }
  405. else if (type == TIM_GROUP)
  406. {
  407. if ([_msg isSelf])
  408. {
  409. return [elem showDescriptionOf:self];
  410. }
  411. else
  412. {
  413. if (self.type == EIMAMSG_GroupTips)
  414. {
  415. return [elem showDescriptionOf:self];
  416. }
  417. else
  418. {
  419. TIMGroupMemberInfo *profile = [_msg getSenderGroupMemberProfile];
  420. if (profile)
  421. {
  422. return [NSString stringWithFormat:@"%@:%@", [profile showTitle], [elem showDescriptionOf:self]];
  423. }
  424. else
  425. {
  426. IMAUser *sender = [self getSender];
  427. if (sender)
  428. {
  429. return [NSString stringWithFormat:@"%@:%@", [sender showTitle], [elem showDescriptionOf:self]];
  430. }
  431. else
  432. {
  433. return [NSString stringWithFormat:@"%@:%@", [_msg sender], [elem showDescriptionOf:self]];
  434. }
  435. }
  436. }
  437. }
  438. }
  439. else if (type == TIM_SYSTEM)
  440. {
  441. return [NSString stringWithFormat:@"%@:%@", ASLocalizedString(@"系统消息"), [elem showDescriptionOf:self]];
  442. }
  443. }
  444. return nil;
  445. }
  446. - (void)remove
  447. {
  448. if (self.type == EIMAMSG_TimeTip || self.type == EIMAMSG_SaftyTip)
  449. {
  450. // 属于自定义的类型,不在IMSDK数据库里面,不能调remove接口
  451. return;
  452. }
  453. BOOL succ = [_msg remove];
  454. DebugLog(@"删除成功:%d", succ);
  455. }
  456. - (void)changeTo:(IMAMsgStatus)status needRefresh:(BOOL)need
  457. {
  458. if (_status != status)
  459. {
  460. if (need)
  461. {
  462. self.status = status;
  463. }
  464. else
  465. {
  466. _status = status;
  467. }
  468. }
  469. }
  470. - (BOOL)isMineMsg
  471. {
  472. return [_msg isSelf];
  473. }
  474. - (BOOL)isC2CMsg
  475. {
  476. return [[self.msg getConversation] getType] == TIM_C2C;
  477. }
  478. - (BOOL)isGroupMsg
  479. {
  480. return [[self.msg getConversation] getType] == TIM_GROUP;
  481. }
  482. - (BOOL)isSystemMsg
  483. {
  484. return self.type == TIM_SYSTEM;
  485. }
  486. - (BOOL)isVailedType
  487. {
  488. return self.type != EIMAMSG_TimeTip && self.type != EIMAMSG_SaftyTip;
  489. }
  490. - (BOOL)isMultiMsg
  491. {
  492. return _type == EIMAMSG_Face || _msg.elemCount > 1;
  493. }
  494. - (NSMutableDictionary *)affixParams
  495. {
  496. if (!_affixParams)
  497. {
  498. _affixParams = [NSMutableDictionary dictionary];
  499. }
  500. return _affixParams;
  501. }
  502. - (void)addString:(NSString *)aValue forKey:(id<NSCopying>)aKey
  503. {
  504. [self.affixParams addString:aValue forKey:aKey];
  505. }
  506. - (void)addInteger:(NSInteger)aValue forKey:(id<NSCopying>)aKey
  507. {
  508. [self.affixParams addInteger:aValue forKey:aKey];
  509. }
  510. - (void)addCGFloat:(CGFloat)aValue forKey:(id<NSCopying>)aKey
  511. {
  512. [self.affixParams addCGFloat:aValue forKey:aKey];
  513. }
  514. - (void)addBOOL:(BOOL)aValue forKey:(id<NSCopying>)aKey
  515. {
  516. [self.affixParams addBOOL:aValue forKey:aKey];
  517. }
  518. - (NSString *)stringForKey:(id<NSCopying>)key
  519. {
  520. return [self.affixParams stringForKey:key];
  521. }
  522. - (NSInteger)integerForKey:(id<NSCopying>)key
  523. {
  524. return [self.affixParams integerForKey:key];
  525. }
  526. - (BOOL)boolForKey:(id<NSCopying>)key
  527. {
  528. return [self.affixParams boolForKey:key];
  529. }
  530. - (CGFloat)floatForKey:(id<NSCopying>)key
  531. {
  532. return [self.affixParams floatForKey:key];
  533. }
  534. - (BOOL)isEqual:(id)object
  535. {
  536. BOOL isEqual = [super isEqual:object];
  537. if (!isEqual)
  538. {
  539. if ([object isKindOfClass:[IMAMsg class]])
  540. {
  541. IMAMsg *msg = (IMAMsg *)object;
  542. isEqual = self.msg.msgId == msg.msg.msgId;
  543. }
  544. }
  545. return isEqual;
  546. }
  547. @end