提交
This commit is contained in:
42
TUIKit/TUIChat/BaseCellData/Chat/TUIFaceMessageCellData.h
Normal file
42
TUIKit/TUIChat/BaseCellData/Chat/TUIFaceMessageCellData.h
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This file declares the TUIFaceMessageCellData class.
|
||||
* This class inherits from TUIMessageCellData and is used to store a series of data and information required by the emoticon message unit.
|
||||
*/
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* 【Module name】TUIFaceMessageCellData
|
||||
* 【Function description】Emoticon message unit data source.
|
||||
* - The emoticon message unit is the message unit used and displayed when displaying animated emoticons.
|
||||
* - The emoticon message unit data source is a class that provides a series of required data for the display of the emoticon message unit.
|
||||
*/
|
||||
@interface TUIFaceMessageCellData : TUIBubbleMessageCellData
|
||||
|
||||
/**
|
||||
*
|
||||
* The index of emoticon groups
|
||||
* - The subscript of the group where the emoticon is located, which is used to locate the emoticon group where the emoticon is located.
|
||||
*/
|
||||
@property(nonatomic, assign) NSInteger groupIndex;
|
||||
|
||||
/**
|
||||
* The path of the emoticon file
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *path;
|
||||
|
||||
/**
|
||||
* The name of emoticon.
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *faceName;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
34
TUIKit/TUIChat/BaseCellData/Chat/TUIFaceMessageCellData.m
Normal file
34
TUIKit/TUIChat/BaseCellData/Chat/TUIFaceMessageCellData.m
Normal file
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// TFaceMessageCellData.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIFaceMessageCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
|
||||
@implementation TUIFaceMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
V2TIMFaceElem *elem = message.faceElem;
|
||||
TUIFaceMessageCellData *faceData = [[TUIFaceMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
|
||||
faceData.groupIndex = elem.index;
|
||||
faceData.faceName = [[NSString alloc] initWithData:elem.data encoding:NSUTF8StringEncoding];
|
||||
for (TUIFaceGroup *group in [TIMConfig defaultConfig].faceGroups) {
|
||||
if (group.groupIndex == faceData.groupIndex) {
|
||||
NSString *path = [group.groupPath stringByAppendingPathComponent:faceData.faceName];
|
||||
faceData.path = path;
|
||||
break;
|
||||
}
|
||||
}
|
||||
faceData.reuseId = TFaceMessageCell_ReuseId;
|
||||
return faceData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
return TIMCommonLocalizableString(TUIKitMessageTypeAnimateEmoji);
|
||||
}
|
||||
|
||||
@end
|
||||
72
TUIKit/TUIChat/BaseCellData/Chat/TUIFileMessageCellData.h
Normal file
72
TUIKit/TUIChat/BaseCellData/Chat/TUIFileMessageCellData.h
Normal file
@@ -0,0 +1,72 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIFileMessageCellData : TUIBubbleMessageCellData <TUIMessageCellDataFileUploadProtocol, TUIMessageCellDataFileDownloadProtocol>
|
||||
|
||||
/**
|
||||
* File path
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *path;
|
||||
|
||||
/**
|
||||
* File name, including suffix.
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *fileName;
|
||||
|
||||
/**
|
||||
* Inner ID for file
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *uuid;
|
||||
|
||||
/**
|
||||
* File size, used to display the data volume information of the file.
|
||||
*/
|
||||
@property(nonatomic, assign) int length;
|
||||
|
||||
/**
|
||||
* The progress of file uploading, which maintained by the cellData.
|
||||
*/
|
||||
@property(nonatomic, assign) NSUInteger uploadProgress;
|
||||
|
||||
/**
|
||||
* The progress of file downloading, which maintained by the cellData.
|
||||
*/
|
||||
@property(nonatomic, assign) NSUInteger downladProgress;
|
||||
|
||||
/**
|
||||
* The flag of indicating whether the file is downloading
|
||||
* YES: dowloading; NO: not download
|
||||
*/
|
||||
@property(nonatomic, assign) BOOL isDownloading;
|
||||
|
||||
/**
|
||||
* Downloading the file
|
||||
* This method integrates and calls the IM SDK, and obtains the file through the interface provided by the SDK.
|
||||
* 1. Before downloading the file from server, it will try to read file from local when the file exists in the local.
|
||||
* 2. When the file not exists in the local, it will download from server through the api provided by IMSDK. But if there is downloading task, it will wait for
|
||||
* the task finished.
|
||||
* - The download progress (percentage value) is updated through the callback of the IMSDK.
|
||||
* - There are two parameters which is @curSize and @totalSize in the callback of IMSDK. The progress value equals to curSize * 100 / totalSize.
|
||||
* 3. When finished download, the file will be storaged to the @path.
|
||||
*/
|
||||
- (void)downloadFile;
|
||||
|
||||
/**
|
||||
* Determine if the file is already downloaded to local
|
||||
* This method will first try to get the file path from the local, if the acquisition is successful, record the path and return YES. Otherwise return NO.
|
||||
*/
|
||||
- (BOOL)isLocalExist;
|
||||
|
||||
/**
|
||||
* Getting the file path and it will return the flag of whether the file exists through @isExist.
|
||||
*/
|
||||
- (NSString *)getFilePath:(BOOL *)isExist;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
148
TUIKit/TUIChat/BaseCellData/Chat/TUIFileMessageCellData.m
Normal file
148
TUIKit/TUIChat/BaseCellData/Chat/TUIFileMessageCellData.m
Normal file
@@ -0,0 +1,148 @@
|
||||
//
|
||||
// TUIFileMessageCellData.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIFileMessageCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/NSString+TUIUtil.h>
|
||||
#import "TUIMessageProgressManager.h"
|
||||
|
||||
@interface TUIFileMessageCellData ()
|
||||
@property(nonatomic, strong) NSMutableArray *progressBlocks;
|
||||
@property(nonatomic, strong) NSMutableArray *responseBlocks;
|
||||
@end
|
||||
|
||||
@implementation TUIFileMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
V2TIMFileElem *elem = message.fileElem;
|
||||
TUIFileMessageCellData *fileData = [[TUIFileMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
|
||||
fileData.path = [elem.path safePathString];
|
||||
fileData.fileName = elem.filename;
|
||||
fileData.length = elem.fileSize;
|
||||
fileData.uuid = elem.uuid;
|
||||
fileData.reuseId = TFileMessageCell_ReuseId;
|
||||
return fileData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
return TIMCommonLocalizableString(TUIkitMessageTypeFile); // @"[File]";
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewDataClass {
|
||||
return NSClassFromString(@"TUIFileReplyQuoteViewData");
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewClass {
|
||||
return NSClassFromString(@"TUIFileReplyQuoteView");
|
||||
}
|
||||
|
||||
- (int)length {
|
||||
if (self.innerMessage) {
|
||||
_length = self.innerMessage.fileElem.fileSize;
|
||||
}
|
||||
return _length;
|
||||
}
|
||||
|
||||
- (instancetype)initWithDirection:(TMsgDirection)direction {
|
||||
self = [super initWithDirection:direction];
|
||||
if (self) {
|
||||
_uploadProgress = 100;
|
||||
_downladProgress = 100;
|
||||
_isDownloading = NO;
|
||||
_progressBlocks = [NSMutableArray array];
|
||||
_responseBlocks = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)downloadFile {
|
||||
BOOL isExist = NO;
|
||||
NSString *path = [self getFilePath:&isExist];
|
||||
if (isExist) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSInteger progress = [TUIMessageProgressManager.shareManager downloadProgressForMessage:self.msgID];
|
||||
if (progress != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.isDownloading) return;
|
||||
self.isDownloading = YES;
|
||||
@weakify(self);
|
||||
if (self.innerMessage.elemType == V2TIM_ELEM_TYPE_FILE) {
|
||||
NSString *msgID = self.msgID;
|
||||
[self.innerMessage.fileElem downloadFile:path
|
||||
progress:^(NSInteger curSize, NSInteger totalSize) {
|
||||
@strongify(self);
|
||||
NSInteger progress = curSize * 100 / totalSize;
|
||||
[self updateDownalodProgress:MIN(progress, 99)];
|
||||
[TUIMessageProgressManager.shareManager appendDownloadProgress:msgID progress:MIN(progress, 99)];
|
||||
}
|
||||
succ:^{
|
||||
@strongify(self);
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
||||
self.isDownloading = NO;
|
||||
[self updateDownalodProgress:100];
|
||||
[TUIMessageProgressManager.shareManager appendDownloadProgress:msgID progress:100];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.path = path;
|
||||
});
|
||||
});
|
||||
}
|
||||
fail:^(int code, NSString *msg) {
|
||||
@strongify(self);
|
||||
self.isDownloading = NO;
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateDownalodProgress:(NSUInteger)progress {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.downladProgress = progress;
|
||||
});
|
||||
}
|
||||
|
||||
- (BOOL)isLocalExist {
|
||||
BOOL isExist;
|
||||
[self getFilePath:&isExist];
|
||||
return isExist;
|
||||
}
|
||||
|
||||
- (NSString *)getFilePath:(BOOL *)isExist {
|
||||
NSString *path = nil;
|
||||
BOOL isDir = NO;
|
||||
*isExist = NO;
|
||||
if (self.direction == MsgDirectionOutgoing) {
|
||||
// The origin file path is valid when uploading
|
||||
path = [NSString stringWithFormat:@"%@%@", TUIKit_File_Path, _path.lastPathComponent];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (!isDir) {
|
||||
*isExist = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!*isExist) {
|
||||
path = [NSString stringWithFormat:@"%@%@%@", TUIKit_File_Path,self.uuid, _fileName];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (!isDir) {
|
||||
*isExist = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*isExist) {
|
||||
_path = path;
|
||||
}
|
||||
|
||||
// TODO: uuid
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@end
|
||||
22
TUIKit/TUIChat/BaseCellData/Chat/TUIGroupNoticeCellData.h
Normal file
22
TUIKit/TUIChat/BaseCellData/Chat/TUIGroupNoticeCellData.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// TUIGroupNoticeCellData.h
|
||||
// TUIGroup
|
||||
//
|
||||
// Created by harvy on 2022/1/11.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIGroupNoticeCellData : NSObject
|
||||
|
||||
@property(nonatomic, copy) NSString *name;
|
||||
@property(nonatomic, copy) NSString *desc;
|
||||
@property(nonatomic, weak) id target;
|
||||
@property(nonatomic, assign) SEL selector;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
13
TUIKit/TUIChat/BaseCellData/Chat/TUIGroupNoticeCellData.m
Normal file
13
TUIKit/TUIChat/BaseCellData/Chat/TUIGroupNoticeCellData.m
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// TUIGroupNoticeCellData.m
|
||||
// TUIGroup
|
||||
//
|
||||
// Created by harvy on 2022/1/11.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIGroupNoticeCellData.h"
|
||||
|
||||
@implementation TUIGroupNoticeCellData
|
||||
|
||||
@end
|
||||
106
TUIKit/TUIChat/BaseCellData/Chat/TUIImageMessageCellData.h
Normal file
106
TUIKit/TUIChat/BaseCellData/Chat/TUIImageMessageCellData.h
Normal file
@@ -0,0 +1,106 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import "TUIChatDefine.h"
|
||||
#import "TUIMessageItem.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIImageMessageCellData
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
*
|
||||
* 【Module name】 TUIImageMessageCellData
|
||||
* 【Function description】It is used to realize the picture bubble in the chat window, including the display of picture message sending progress.
|
||||
* At the same time, this module already supports three different types of "thumbnail", "large image" and "original image", and
|
||||
* has handled the business logic of displaying the corresponding image type under appropriate circumstances:
|
||||
* 1. Thumbnail - By default, you see thumbnails in the chat window, which is smaller and saves traffic.
|
||||
* 2. Large image - If the user clicks on the thumbnail, they see a larger image with a better resolution.
|
||||
* 3. Original image - If the sender chooses to send the original image, the recipient will see the "original image" button which can click to download the
|
||||
* image with the original size.
|
||||
*/
|
||||
@interface TUIImageMessageCellData : TUIBubbleMessageCellData <TUIMessageCellDataFileUploadProtocol>
|
||||
|
||||
@property(nonatomic, strong) UIImage *thumbImage;
|
||||
@property(nonatomic, strong) UIImage *originImage;
|
||||
@property(nonatomic, strong) UIImage *largeImage;
|
||||
|
||||
/**
|
||||
*
|
||||
* The file storage path
|
||||
*
|
||||
* @note
|
||||
* @path is maintained by the program by default, you can directly obtain the demo storage path by importing TIMDefine.h and referencing TUIKit_Image_Path
|
||||
* Other routes are also available if you have further individual needs
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *path;
|
||||
@property(nonatomic, assign) NSInteger length;
|
||||
|
||||
/**
|
||||
*
|
||||
* The set of image items
|
||||
*
|
||||
* @note
|
||||
* There are usually three imageItems stored in @items, namely thumb (thumb image), origin (original image), and large (large image), which is convenient to
|
||||
* obtain images flexibly according to needs.
|
||||
*
|
||||
*/
|
||||
@property(nonatomic, strong) NSMutableArray *items;
|
||||
|
||||
/**
|
||||
* The progress of loading thumbnail
|
||||
*/
|
||||
@property(nonatomic, assign) NSUInteger thumbProgress;
|
||||
|
||||
/**
|
||||
* The progress of loading origin image
|
||||
*/
|
||||
@property(nonatomic, assign) NSUInteger originProgress;
|
||||
|
||||
/**
|
||||
* The progress of loading large image
|
||||
*/
|
||||
@property(nonatomic, assign) NSUInteger largeProgress;
|
||||
|
||||
/**
|
||||
* The progress of uploading (sending)
|
||||
*/
|
||||
@property(nonatomic, assign) NSUInteger uploadProgress;
|
||||
|
||||
@property(nonatomic, assign) BOOL isSuperLongImage;
|
||||
|
||||
/**
|
||||
|
||||
* Downloading image.
|
||||
* This method integrates and calls the IM SDK, and obtains images from sever through the interface provided by the SDK.
|
||||
* 1. Before downloading the file from server, it will try to read file from local when the file exists in the local.
|
||||
* 2. If the file is not exist in the local, it will download from server through the api named @getImage which provided by the class of TIMImage in the IMSDK.
|
||||
* - The download progress (percentage value) is updated through the callback of the IMSDK.
|
||||
* - There are two parameters which is @curSize and @totalSize in the callback of IMSDK. The progress value equals to curSize * 100 / totalSize.
|
||||
* - The type of items in the image message is TIMElem. You can obtain image list from the paramter named imageList provided by TIMElem, which including
|
||||
* original image、large image and thumbnail and you can obtain the image from it with the @imageType.
|
||||
* 3. The image obtained through the SDK interface is a binary file, which needs to be decoded first, converted to CGIamge for decoding, and then packaged as a
|
||||
* UIImage before it can be used.
|
||||
* 4. When finished download, the image will be storaged to the @path.
|
||||
*/
|
||||
- (void)downloadImage:(TUIImageType)type;
|
||||
- (void)downloadImage:(TUIImageType)type finish:(TUIImageMessageDownloadCallback)finish;
|
||||
|
||||
/**
|
||||
*
|
||||
* Decode the image and assign the image to a variable of the corresponding type (@thumbImage, @largeImage or @originImage).
|
||||
*/
|
||||
- (void)decodeImage:(TUIImageType)type;
|
||||
|
||||
/**
|
||||
*
|
||||
* Getting image file path
|
||||
*/
|
||||
- (NSString *)getImagePath:(TUIImageType)type isExist:(BOOL *)isExist;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
227
TUIKit/TUIChat/BaseCellData/Chat/TUIImageMessageCellData.m
Normal file
227
TUIKit/TUIChat/BaseCellData/Chat/TUIImageMessageCellData.m
Normal file
@@ -0,0 +1,227 @@
|
||||
//
|
||||
// TUIImageMessageCellData.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIImageMessageCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/NSString+TUIUtil.h>
|
||||
|
||||
@interface TUIImageMessageCellData ()
|
||||
@property(nonatomic, assign) BOOL isDownloading;
|
||||
@property(nonatomic, copy) TUIImageMessageDownloadCallback onFinish;
|
||||
@end
|
||||
|
||||
@implementation TUIImageMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
V2TIMImageElem *elem = message.imageElem;
|
||||
TUIImageMessageCellData *imageData = [[TUIImageMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
|
||||
imageData.path = [elem.path safePathString];
|
||||
imageData.items = [NSMutableArray array];
|
||||
for (V2TIMImage *item in elem.imageList) {
|
||||
TUIImageItem *itemData = [[TUIImageItem alloc] init];
|
||||
itemData.uuid = item.uuid;
|
||||
itemData.size = CGSizeMake(item.width, item.height);
|
||||
// itemData.url = item.url;
|
||||
if (item.type == V2TIM_IMAGE_TYPE_THUMB) {
|
||||
itemData.type = TImage_Type_Thumb;
|
||||
} else if (item.type == V2TIM_IMAGE_TYPE_LARGE) {
|
||||
itemData.type = TImage_Type_Large;
|
||||
} else if (item.type == V2TIM_IMAGE_TYPE_ORIGIN) {
|
||||
itemData.type = TImage_Type_Origin;
|
||||
}
|
||||
[imageData.items addObject:itemData];
|
||||
}
|
||||
imageData.reuseId = TImageMessageCell_ReuseId;
|
||||
return imageData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
return TIMCommonLocalizableString(TUIkitMessageTypeImage); // @"[Image]";
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewDataClass {
|
||||
return NSClassFromString(@"TUIImageReplyQuoteViewData");
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewClass {
|
||||
return NSClassFromString(@"TUIImageReplyQuoteView");
|
||||
}
|
||||
|
||||
- (instancetype)initWithDirection:(TMsgDirection)direction {
|
||||
self = [super initWithDirection:direction];
|
||||
if (self) {
|
||||
_uploadProgress = 100;
|
||||
if (direction == MsgDirectionIncoming) {
|
||||
self.cellLayout = [TUIMessageCellLayout incommingImageMessageLayout];
|
||||
} else {
|
||||
self.cellLayout = [TUIMessageCellLayout outgoingImageMessageLayout];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)getImagePath:(TUIImageType)type isExist:(BOOL *)isExist {
|
||||
NSString *path = nil;
|
||||
BOOL isDir = NO;
|
||||
*isExist = NO;
|
||||
if (self.direction == MsgDirectionOutgoing) {
|
||||
path = [NSString stringWithFormat:@"%@%@", TUIKit_Image_Path, _path.lastPathComponent];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (!isDir) {
|
||||
*isExist = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!*isExist) {
|
||||
TUIImageItem *tImageItem = [self getTImageItem:type];
|
||||
path = [NSString stringWithFormat:@"%@%@_%ld", TUIKit_Image_Path, tImageItem.uuid, (long)tImageItem.type];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (!isDir) {
|
||||
*isExist = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
- (void)downloadImage:(TUIImageType)type finish:(TUIImageMessageDownloadCallback)finish {
|
||||
self.onFinish = finish;
|
||||
[self downloadImage:type];
|
||||
}
|
||||
|
||||
- (void)downloadImage:(TUIImageType)type {
|
||||
BOOL isExist = NO;
|
||||
NSString *path = [self getImagePath:type isExist:&isExist];
|
||||
if (isExist) {
|
||||
[self decodeImage:type];
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.isDownloading) {
|
||||
return;
|
||||
}
|
||||
self.isDownloading = YES;
|
||||
|
||||
V2TIMImage *imImage = [self getIMImage:type];
|
||||
|
||||
@weakify(self);
|
||||
[imImage downloadImage:path
|
||||
progress:^(NSInteger curSize, NSInteger totalSize) {
|
||||
@strongify(self);
|
||||
NSInteger progress = curSize * 100 / totalSize;
|
||||
[self updateProgress:MIN(progress, 99) withType:type];
|
||||
}
|
||||
succ:^{
|
||||
@strongify(self);
|
||||
self.isDownloading = NO;
|
||||
[self updateProgress:100 withType:type];
|
||||
[self decodeImage:type];
|
||||
}
|
||||
fail:^(int code, NSString *msg) {
|
||||
@strongify(self);
|
||||
self.isDownloading = NO;
|
||||
/**
|
||||
* If the uuid of the picture is the same (the same user sends
|
||||
* the same picture continuously), the same path may trigger multiple download operations. Except for the first time, subsequent downloads will report
|
||||
* an error. At this time, it is necessary to judge whether the local file exists.
|
||||
*/
|
||||
[self decodeImage:type];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateProgress:(NSUInteger)progress withType:(TUIImageType)type {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (type == TImage_Type_Thumb) self.thumbProgress = progress;
|
||||
if (type == TImage_Type_Large) self.largeProgress = progress;
|
||||
if (type == TImage_Type_Origin) self.originProgress = progress;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)decodeImage:(TUIImageType)type {
|
||||
BOOL isExist = NO;
|
||||
NSString *path = [self getImagePath:type isExist:&isExist];
|
||||
if (!isExist) {
|
||||
return;
|
||||
}
|
||||
|
||||
__weak typeof(self) weakSelf = self;
|
||||
void (^finishBlock)(UIImage *) = ^(UIImage *image) {
|
||||
if (type == TImage_Type_Thumb) {
|
||||
weakSelf.thumbImage = image;
|
||||
weakSelf.thumbProgress = 100;
|
||||
weakSelf.uploadProgress = 100;
|
||||
}
|
||||
if (type == TImage_Type_Large) {
|
||||
weakSelf.largeImage = image;
|
||||
weakSelf.largeProgress = 100;
|
||||
}
|
||||
if (type == TImage_Type_Origin) {
|
||||
weakSelf.originImage = image;
|
||||
weakSelf.originProgress = 100;
|
||||
}
|
||||
if (weakSelf.onFinish) {
|
||||
weakSelf.onFinish();
|
||||
}
|
||||
};
|
||||
|
||||
NSString *cacheKey = [path substringFromIndex:TUIKit_Image_Path.length];
|
||||
|
||||
UIImage *cacheImage = [[SDImageCache sharedImageCache] imageFromCacheForKey:cacheKey];
|
||||
if (cacheImage) {
|
||||
finishBlock(cacheImage);
|
||||
} else {
|
||||
[TUITool asyncDecodeImage:path
|
||||
complete:^(NSString *path, UIImage *image) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSLog(@"image.sd_imageFormat: %ld path:%@ image.sd_imageData.length :%lu",(long)image.sd_imageFormat,path,(unsigned long)image.sd_imageData.length);
|
||||
if (![path tui_containsString:@".gif"] || (image.sd_imageFormat != SDImageFormatGIF) ) {
|
||||
[[SDImageCache sharedImageCache] storeImageToMemory:image forKey:cacheKey];
|
||||
}
|
||||
else {
|
||||
/**
|
||||
* The gif image is too large to be cached in memory
|
||||
* Only cache images less than 1M
|
||||
*/
|
||||
if (image.sd_imageData.length < 1 * 1024 * 1024) {
|
||||
[[SDImageCache sharedImageCache] storeImageToMemory:image forKey:cacheKey];
|
||||
}
|
||||
}
|
||||
finishBlock(image);
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (TUIImageItem *)getTImageItem:(TUIImageType)type {
|
||||
for (TUIImageItem *item in self.items) {
|
||||
if (item.type == type) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (V2TIMImage *)getIMImage:(TUIImageType)type {
|
||||
V2TIMMessage *imMsg = self.innerMessage;
|
||||
if (imMsg.elemType == V2TIM_ELEM_TYPE_IMAGE) {
|
||||
for (V2TIMImage *imImage in imMsg.imageElem.imageList) {
|
||||
if (type == TImage_Type_Thumb && imImage.type == V2TIM_IMAGE_TYPE_THUMB) {
|
||||
return imImage;
|
||||
} else if (type == TImage_Type_Origin && imImage.type == V2TIM_IMAGE_TYPE_ORIGIN) {
|
||||
return imImage;
|
||||
} else if (type == TImage_Type_Large && imImage.type == V2TIM_IMAGE_TYPE_LARGE) {
|
||||
return imImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
/**
|
||||
*
|
||||
*
|
||||
* This file declares the TUIJoinGroupMessageCellData class.
|
||||
* This document is responsible for realizing the function of the small gray bar for entering the group, and can also be further extended to a group message
|
||||
* unit with a single operator. That is, this file can highlight the operator's nickname in blue and provide a response interface for the highlighted part in
|
||||
* blue.
|
||||
*/
|
||||
#import <TIMCommon/TUISystemMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIJoinGroupMessageCellData : TUISystemMessageCellData
|
||||
|
||||
/**
|
||||
*
|
||||
* Operator nickname. For example, "Tom joined the group", the variable content is "Tom"
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *opUserName;
|
||||
|
||||
/**
|
||||
* The nickname of the operator.
|
||||
*/
|
||||
@property(nonatomic, strong) NSMutableArray<NSString *> *userNameList;
|
||||
|
||||
/**
|
||||
* Operator Id.
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *opUserID;
|
||||
|
||||
/**
|
||||
* List of the operator IDs.
|
||||
*/
|
||||
@property(nonatomic, strong) NSMutableArray<NSString *> *userIDList;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
#import "TUIJoinGroupMessageCellData.h"
|
||||
|
||||
@implementation TUIJoinGroupMessageCellData
|
||||
|
||||
- (instancetype)initWithDirection:(TMsgDirection)direction {
|
||||
self = [super initWithDirection:direction];
|
||||
if (self) {
|
||||
self.userNameList = [NSMutableArray array];
|
||||
self.userIDList = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
25
TUIKit/TUIChat/BaseCellData/Chat/TUIMenuCellData.h
Normal file
25
TUIKit/TUIChat/BaseCellData/Chat/TUIMenuCellData.h
Normal file
@@ -0,0 +1,25 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIMenuCellData
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface TUIMenuCellData : NSObject
|
||||
|
||||
/**
|
||||
* Access path for grouped thumbnails in grouping units
|
||||
*/
|
||||
@property(nonatomic, strong) NSString *path;
|
||||
|
||||
@property(nonatomic, assign) BOOL isSelected;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
9
TUIKit/TUIChat/BaseCellData/Chat/TUIMenuCellData.m
Normal file
9
TUIKit/TUIChat/BaseCellData/Chat/TUIMenuCellData.m
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import "TUIMenuCellData.h"
|
||||
|
||||
@implementation TUIMenuCellData
|
||||
|
||||
@end
|
||||
28
TUIKit/TUIChat/BaseCellData/Chat/TUIMergeMessageCellData.h
Normal file
28
TUIKit/TUIChat/BaseCellData/Chat/TUIMergeMessageCellData.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// TUIMergeMessageCellData.h
|
||||
// Pods
|
||||
//
|
||||
// Created by harvy on 2020/12/9.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUIMergeMessageCellData : TUIMessageCellData
|
||||
|
||||
@property(nonatomic, copy) NSString *title;
|
||||
@property(nonatomic, strong) NSArray<NSString *> *abstractList;
|
||||
@property(nonatomic, strong) V2TIMMergerElem *mergerElem;
|
||||
@property(nonatomic, assign) CGSize abstractSize;
|
||||
@property(nonatomic, assign) CGSize abstractRow1Size;
|
||||
@property(nonatomic, assign) CGSize abstractRow2Size;
|
||||
@property(nonatomic, assign) CGSize abstractRow3Size;
|
||||
@property(nonatomic, strong) NSArray<NSDictionary *> *abstractSendDetailList;
|
||||
- (NSAttributedString *)abstractAttributedString;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
119
TUIKit/TUIChat/BaseCellData/Chat/TUIMergeMessageCellData.m
Normal file
119
TUIKit/TUIChat/BaseCellData/Chat/TUIMergeMessageCellData.m
Normal file
@@ -0,0 +1,119 @@
|
||||
//
|
||||
// TUIMergeMessageCellData.m
|
||||
// Pods
|
||||
//
|
||||
// Created by harvy on 2020/12/9.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIMergeMessageCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import "TUITextMessageCellData.h"
|
||||
#import <TIMCommon/NSString+TUIEmoji.h>
|
||||
|
||||
@implementation TUIMergeMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
V2TIMMergerElem *elem = message.mergerElem;
|
||||
if (elem.layersOverLimit) {
|
||||
TUITextMessageCellData *limitCell = [[TUITextMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
|
||||
limitCell.content = TIMCommonLocalizableString(TUIKitRelayLayerLimitTips);
|
||||
return limitCell;
|
||||
}
|
||||
|
||||
TUIMergeMessageCellData *mergeData = [[TUIMergeMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
|
||||
mergeData.title = elem.title;
|
||||
mergeData.abstractList = [NSArray arrayWithArray:elem.abstractList];
|
||||
mergeData.abstractSendDetailList = [self.class formatAbstractSendDetailList:elem.abstractList];
|
||||
mergeData.mergerElem = elem;
|
||||
mergeData.reuseId = TMergeMessageCell_ReuserId;
|
||||
return mergeData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
return [NSString stringWithFormat:@"[%@]", TIMCommonLocalizableString(TUIKitRelayChatHistory)];
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewDataClass {
|
||||
return NSClassFromString(@"TUIMergeReplyQuoteViewData");
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewClass {
|
||||
return NSClassFromString(@"TUIMergeReplyQuoteView");
|
||||
}
|
||||
|
||||
- (NSAttributedString *)abstractAttributedString {
|
||||
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
|
||||
style.lineSpacing = 4;
|
||||
style.alignment = isRTL()? NSTextAlignmentRight:NSTextAlignmentLeft;
|
||||
NSDictionary *attribute = @{
|
||||
NSForegroundColorAttributeName : [UIColor colorWithRed:187 / 255.0 green:187 / 255.0 blue:187 / 255.0 alpha:1 / 1.0],
|
||||
NSFontAttributeName : [UIFont systemFontOfSize:12.0],
|
||||
NSParagraphStyleAttributeName : style
|
||||
};
|
||||
|
||||
NSMutableAttributedString *abstr = [[NSMutableAttributedString alloc] initWithString:@""];
|
||||
int i = 0;
|
||||
for (NSString *ab in self.abstractList) {
|
||||
if (i >= 4) {
|
||||
break;
|
||||
}
|
||||
NSString *resultStr = [NSString stringWithFormat:@"%@\n", ab];
|
||||
NSString *str = resultStr;
|
||||
[abstr appendAttributedString:[[NSAttributedString alloc] initWithString:str attributes:attribute]];
|
||||
i++;
|
||||
}
|
||||
return abstr;
|
||||
}
|
||||
|
||||
+ (NSMutableArray *)formatAbstractSendDetailList:(NSArray *)originAbstractList {
|
||||
NSMutableArray *array = [NSMutableArray arrayWithCapacity:3];
|
||||
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
|
||||
style.alignment = isRTL()? NSTextAlignmentRight:NSTextAlignmentLeft;
|
||||
style.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
NSDictionary *attribute = @{
|
||||
NSForegroundColorAttributeName : [UIColor colorWithRed:187 / 255.0 green:187 / 255.0 blue:187 / 255.0 alpha:1 / 1.0],
|
||||
NSFontAttributeName : [UIFont systemFontOfSize:12.0],
|
||||
NSParagraphStyleAttributeName : style
|
||||
};
|
||||
int i = 0;
|
||||
for (NSString *ab in originAbstractList) {
|
||||
if (i >= 4) {
|
||||
break;
|
||||
}
|
||||
NSString *str = ab;
|
||||
NSString * splitStr = @":";
|
||||
if ([str tui_containsString:@"\u202C:"]) {
|
||||
splitStr = @"\u202C:";
|
||||
}
|
||||
NSArray<NSString *> *result = [str componentsSeparatedByString:splitStr];
|
||||
NSString *sender = result[0];
|
||||
NSString *detail = result[1];
|
||||
sender = [NSString stringWithFormat:@"%@",sender];
|
||||
detail = [NSString stringWithFormat:@"%@",detail.getLocalizableStringWithFaceContent];
|
||||
NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:3];
|
||||
if(sender.length>0 ){
|
||||
NSMutableAttributedString *abstr = [[NSMutableAttributedString alloc] initWithString:@""];
|
||||
[abstr appendAttributedString:[[NSAttributedString alloc] initWithString:sender attributes:attribute]];
|
||||
[dic setObject:abstr forKey:@"sender"];
|
||||
}
|
||||
if(detail.length>0 ){
|
||||
NSMutableAttributedString *abstr = [[NSMutableAttributedString alloc] initWithString:@""];
|
||||
[abstr appendAttributedString:[[NSAttributedString alloc] initWithString:detail attributes:attribute]];
|
||||
[dic setObject:abstr forKey:@"detail"];
|
||||
}
|
||||
[array addObject:dic];
|
||||
|
||||
i++;
|
||||
}
|
||||
return array;
|
||||
}
|
||||
- (BOOL)isArString:(NSString *)text {
|
||||
NSString *isoLangCode = (__bridge_transfer NSString *)CFStringTokenizerCopyBestStringLanguage((__bridge CFStringRef)text, CFRangeMake(0, text.length));
|
||||
|
||||
if ([isoLangCode isEqualToString:@"ar"]) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
@end
|
||||
19
TUIKit/TUIChat/BaseCellData/Chat/TUITypingStatusCellData.h
Normal file
19
TUIKit/TUIChat/BaseCellData/Chat/TUITypingStatusCellData.h
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// TUITypingStatusCellData.h
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/7/4.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface TUITypingStatusCellData : TUIMessageCellData
|
||||
|
||||
@property(nonatomic, assign) NSInteger typingStatus;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
25
TUIKit/TUIChat/BaseCellData/Chat/TUITypingStatusCellData.m
Normal file
25
TUIKit/TUIChat/BaseCellData/Chat/TUITypingStatusCellData.m
Normal file
@@ -0,0 +1,25 @@
|
||||
//
|
||||
// TUITypingStatusCellData.m
|
||||
// TUIChat
|
||||
//
|
||||
// Created by wyl on 2022/7/4.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUITypingStatusCellData.h"
|
||||
|
||||
@implementation TUITypingStatusCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
NSDictionary *param = [NSJSONSerialization JSONObjectWithData:message.customElem.data options:NSJSONReadingAllowFragments error:nil];
|
||||
TUITypingStatusCellData *cellData = [[TUITypingStatusCellData alloc] initWithDirection:message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming];
|
||||
cellData.msgID = message.msgID;
|
||||
|
||||
if ([param.allKeys containsObject:@"typingStatus"]) {
|
||||
cellData.typingStatus = [param[@"typingStatus"] intValue];
|
||||
}
|
||||
|
||||
return cellData;
|
||||
}
|
||||
|
||||
@end
|
||||
60
TUIKit/TUIChat/BaseCellData/Chat/TUIVideoMessageCellData.h
Normal file
60
TUIKit/TUIChat/BaseCellData/Chat/TUIVideoMessageCellData.h
Normal file
@@ -0,0 +1,60 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
/**
|
||||
*
|
||||
* 1. This file declares the TUIVideoItem class, TUISnapshotItem class, and TUIVideoMessageCellData class.
|
||||
* - TUIVideoItem corresponds to V2TIMVideoElem in the IM SDK. We convert the classes in the SDK to TUIVideoItem, which is convenient for us to further
|
||||
* process and operate the data.
|
||||
* - TUISnapshotItem corresponds to the video cover class in the IM SDK. It is still an image in essence, but is bound to the corresponding Video.
|
||||
* - TUIVideoMessageCellData inherits from the TUIMessageCellData class and is used to store a series of data and information required by the image message
|
||||
* unit.
|
||||
* 2. The business logic for obtaining video information and cover information has been implemented in this document. When you need to get video and cover
|
||||
* data, you can directly call the relevant member functions declared in this file
|
||||
*/
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import "TUIChatDefine.h"
|
||||
#import "TUIMessageItem.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TUIVideoMessageCellData
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@interface TUIVideoMessageCellData : TUIBubbleMessageCellData <TUIMessageCellDataFileUploadProtocol>
|
||||
|
||||
@property(nonatomic, strong) UIImage *thumbImage;
|
||||
@property(nonatomic, strong) NSString *videoPath;
|
||||
@property(nonatomic, strong) NSString *snapshotPath;
|
||||
@property(nonatomic, strong) TUIVideoItem *videoItem;
|
||||
@property(nonatomic, strong) TUISnapshotItem *snapshotItem;
|
||||
@property(nonatomic, assign) NSUInteger uploadProgress;
|
||||
@property(nonatomic, assign) NSUInteger thumbProgress;
|
||||
@property(nonatomic, assign) NSUInteger videoProgress;
|
||||
/// Is the current message a custom message
|
||||
@property(nonatomic, assign) BOOL isPlaceHolderCellData;
|
||||
|
||||
+ (TUIMessageCellData *)placeholderCellDataWithSnapshotUrl:(NSString *)snapshotUrl thubImage:(UIImage *)thubImage;
|
||||
|
||||
- (void)getVideoUrl:(void (^)(NSString *url))urlCallBack;
|
||||
|
||||
/**
|
||||
* Downloading the cover image of the video. It will download from server if the image not exist in local.
|
||||
*/
|
||||
- (void)downloadThumb;
|
||||
- (void)downloadThumb:(TUIVideoMessageDownloadCallback)finish;
|
||||
|
||||
/**
|
||||
* Downloading the video file. It will download from server if the video not exist in local.
|
||||
*/
|
||||
- (void)downloadVideo;
|
||||
|
||||
- (BOOL)isVideoExist;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
290
TUIKit/TUIChat/BaseCellData/Chat/TUIVideoMessageCellData.m
Normal file
290
TUIKit/TUIChat/BaseCellData/Chat/TUIVideoMessageCellData.m
Normal file
@@ -0,0 +1,290 @@
|
||||
//
|
||||
// TUIVideoMessageCellData.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIVideoMessageCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/NSString+TUIUtil.h>
|
||||
#import <TUICore/TUILogin.h>
|
||||
|
||||
#define TVideo_Block_Progress @"TVideo_Block_Progress";
|
||||
#define TVideo_Block_Response @"TVideo_Block_Response";
|
||||
|
||||
@interface TUIVideoMessageCellData ()
|
||||
@property(nonatomic, strong) NSString *videoUrl;
|
||||
@property(nonatomic, assign) BOOL isDownloadingSnapshot;
|
||||
@property(nonatomic, assign) BOOL isDownloadingVideo;
|
||||
@property(nonatomic, copy) TUIVideoMessageDownloadCallback onFinish;
|
||||
@end
|
||||
|
||||
@implementation TUIVideoMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
V2TIMVideoElem *elem = message.videoElem;
|
||||
TUIVideoMessageCellData *videoData = [[TUIVideoMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
|
||||
videoData.videoPath = [elem.videoPath safePathString];
|
||||
videoData.snapshotPath = [elem.snapshotPath safePathString];
|
||||
|
||||
videoData.videoItem = [[TUIVideoItem alloc] init];
|
||||
videoData.videoItem.uuid = elem.videoUUID;
|
||||
videoData.videoItem.type = elem.videoType;
|
||||
videoData.videoItem.length = elem.videoSize;
|
||||
videoData.videoItem.duration = elem.duration;
|
||||
|
||||
videoData.snapshotItem = [[TUISnapshotItem alloc] init];
|
||||
videoData.snapshotItem.uuid = elem.snapshotUUID;
|
||||
// videoData.snapshotItem.type = elem.snaps;
|
||||
videoData.snapshotItem.length = elem.snapshotSize;
|
||||
videoData.snapshotItem.size = CGSizeMake(elem.snapshotWidth, elem.snapshotHeight);
|
||||
videoData.reuseId = TVideoMessageCell_ReuseId;
|
||||
return videoData;
|
||||
}
|
||||
|
||||
+ (TUIMessageCellData *)placeholderCellDataWithSnapshotUrl:(NSString *)snapshotUrl thubImage:(UIImage *)thubImage {
|
||||
TUIVideoMessageCellData *videoData = [[TUIVideoMessageCellData alloc] initWithDirection:(MsgDirectionOutgoing)];
|
||||
videoData.thumbImage = thubImage;
|
||||
videoData.snapshotPath = [snapshotUrl safePathString];
|
||||
videoData.videoItem = [[TUIVideoItem alloc] init];
|
||||
videoData.snapshotItem = [[TUISnapshotItem alloc] init];
|
||||
videoData.snapshotItem.size = CGSizeEqualToSize(thubImage.size, CGSizeZero) ? CGSizeMake(kScale375(100), kScale375(100)) : thubImage.size;
|
||||
videoData.reuseId = TVideoMessageCell_ReuseId;
|
||||
videoData.avatarUrl = [NSURL URLWithString:[TUILogin getFaceUrl]];
|
||||
videoData.isPlaceHolderCellData = YES;
|
||||
return videoData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
return TIMCommonLocalizableString(TUIkitMessageTypeVideo);
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewDataClass {
|
||||
return NSClassFromString(@"TUIVideoReplyQuoteViewData");
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewClass {
|
||||
return NSClassFromString(@"TUIVideoReplyQuoteView");
|
||||
}
|
||||
|
||||
- (instancetype)initWithDirection:(TMsgDirection)direction {
|
||||
self = [super initWithDirection:direction];
|
||||
if (self) {
|
||||
_uploadProgress = 100;
|
||||
_isDownloadingVideo = NO;
|
||||
_isDownloadingSnapshot = NO;
|
||||
if (direction == MsgDirectionIncoming) {
|
||||
self.cellLayout = [TUIMessageCellLayout incommingVideoMessageLayout];
|
||||
} else {
|
||||
self.cellLayout = [TUIMessageCellLayout outgoingVideoMessageLayout];
|
||||
}
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)downloadThumb:(TUIVideoMessageDownloadCallback)finish {
|
||||
self.onFinish = finish;
|
||||
[self downloadThumb];
|
||||
}
|
||||
|
||||
- (void)downloadThumb {
|
||||
BOOL isExist = NO;
|
||||
NSString *path = [self getSnapshotPath:&isExist];
|
||||
if (isExist) {
|
||||
[self decodeThumb];
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.isDownloadingSnapshot) {
|
||||
return;
|
||||
}
|
||||
self.isDownloadingSnapshot = YES;
|
||||
|
||||
@weakify(self);
|
||||
V2TIMMessage *imMsg = self.innerMessage;
|
||||
if (imMsg.elemType == V2TIM_ELEM_TYPE_VIDEO) {
|
||||
// Avoid large files that slow down callback progress.
|
||||
[self updateThumbProgress:1];
|
||||
[imMsg.videoElem downloadSnapshot:path
|
||||
progress:^(NSInteger curSize, NSInteger totalSize) {
|
||||
[self updateThumbProgress:MAX(1, curSize * 100 / totalSize)];
|
||||
}
|
||||
succ:^{
|
||||
@strongify(self);
|
||||
self.isDownloadingSnapshot = NO;
|
||||
[self updateThumbProgress:100];
|
||||
[self decodeThumb];
|
||||
}
|
||||
fail:^(int code, NSString *msg) {
|
||||
@strongify(self);
|
||||
self.isDownloadingSnapshot = NO;
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateThumbProgress:(NSUInteger)progress {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.thumbProgress = progress;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)decodeThumb {
|
||||
BOOL isExist = NO;
|
||||
NSString *path = [self getSnapshotPath:&isExist];
|
||||
if (!isExist) {
|
||||
return;
|
||||
}
|
||||
@weakify(self);
|
||||
[TUITool asyncDecodeImage:path
|
||||
complete:^(NSString *path, UIImage *image) {
|
||||
@strongify(self);
|
||||
@weakify(self);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@strongify(self);
|
||||
self.thumbImage = image;
|
||||
self.thumbProgress = 100;
|
||||
if (self.onFinish) {
|
||||
self.onFinish();
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)downloadVideo {
|
||||
BOOL isExist = NO;
|
||||
NSString *path = [self getVideoPath:&isExist];
|
||||
if (isExist) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self.isDownloadingVideo) {
|
||||
return;
|
||||
}
|
||||
self.isDownloadingVideo = YES;
|
||||
|
||||
@weakify(self);
|
||||
V2TIMMessage *imMsg = self.innerMessage;
|
||||
if (imMsg.elemType == V2TIM_ELEM_TYPE_VIDEO) {
|
||||
[imMsg.videoElem downloadVideo:path
|
||||
progress:^(NSInteger curSize, NSInteger totalSize) {
|
||||
@strongify(self);
|
||||
[self updateVideoProgress:curSize * 100 / totalSize];
|
||||
}
|
||||
succ:^{
|
||||
@strongify(self);
|
||||
self.isDownloadingVideo = NO;
|
||||
[self updateVideoProgress:100];
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.videoPath = path;
|
||||
});
|
||||
}
|
||||
fail:^(int code, NSString *msg) {
|
||||
@strongify(self);
|
||||
self.isDownloadingVideo = NO;
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateVideoProgress:(NSUInteger)progress {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
self.videoProgress = progress;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)getVideoUrl:(void (^)(NSString *url))urlCallBack {
|
||||
if (!urlCallBack) {
|
||||
return;
|
||||
}
|
||||
if (self.videoUrl) {
|
||||
urlCallBack(self.videoUrl);
|
||||
}
|
||||
@weakify(self);
|
||||
V2TIMMessage *imMsg = self.innerMessage;
|
||||
if (imMsg.elemType == V2TIM_ELEM_TYPE_VIDEO) {
|
||||
[imMsg.videoElem getVideoUrl:^(NSString *url) {
|
||||
@strongify(self);
|
||||
self.videoUrl = url;
|
||||
urlCallBack(self.videoUrl);
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isVideoExist {
|
||||
BOOL isExist;
|
||||
[self getVideoPath:&isExist];
|
||||
return isExist;
|
||||
}
|
||||
|
||||
- (NSString *)getVideoPath:(BOOL *)isExist {
|
||||
NSString *path = nil;
|
||||
BOOL isDir = NO;
|
||||
*isExist = NO;
|
||||
if (_videoPath && _videoPath.lastPathComponent.length) {
|
||||
path = _videoPath;
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (!isDir) {
|
||||
*isExist = YES;
|
||||
}
|
||||
}
|
||||
else {
|
||||
path = [NSString stringWithFormat:@"%@%@", TUIKit_Video_Path, _videoPath.lastPathComponent];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (!isDir) {
|
||||
*isExist = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!*isExist) {
|
||||
if (_videoItem) {
|
||||
if (_videoItem.uuid && _videoItem.uuid.length && _videoItem.type && _videoItem.type.length) {
|
||||
path = [NSString stringWithFormat:@"%@%@.%@", TUIKit_Video_Path, _videoItem.uuid, _videoItem.type];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (!isDir) {
|
||||
*isExist = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*isExist) {
|
||||
_videoPath = path;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
- (NSString *)getSnapshotPath:(BOOL *)isExist {
|
||||
NSString *path = nil;
|
||||
BOOL isDir = NO;
|
||||
*isExist = NO;
|
||||
if (_snapshotPath && _snapshotPath.length) {
|
||||
path = [NSString stringWithFormat:@"%@%@", TUIKit_Video_Path, _snapshotPath.lastPathComponent];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (!isDir) {
|
||||
*isExist = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!*isExist) {
|
||||
if (_snapshotItem) {
|
||||
if (_snapshotItem.uuid && _snapshotItem.uuid.length) {
|
||||
path = [NSString stringWithFormat:@"%@%@", TUIKit_Video_Path, _snapshotItem.uuid];
|
||||
path = [TUIKit_Video_Path stringByAppendingString:_snapshotItem.uuid];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (!isDir) {
|
||||
*isExist = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
@end
|
||||
66
TUIKit/TUIChat/BaseCellData/Chat/TUIVoiceMessageCellData.h
Normal file
66
TUIKit/TUIChat/BaseCellData/Chat/TUIVoiceMessageCellData.h
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
// Created by Tencent on 2023/06/09.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
|
||||
#import <TIMCommon/TUIBubbleMessageCellData.h>
|
||||
#import <TIMCommon/TUIMessageCellData.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NS_ENUM(NSUInteger, TUIVoiceAudioPlaybackStyle) {
|
||||
TUIVoiceAudioPlaybackStyleLoudspeaker = 1,
|
||||
TUIVoiceAudioPlaybackStyleHandset = 2,
|
||||
};
|
||||
|
||||
@interface TUIVoiceMessageCellData : TUIBubbleMessageCellData
|
||||
|
||||
@property(nonatomic, strong) NSString *path;
|
||||
@property(nonatomic, strong) NSString *uuid;
|
||||
@property(nonatomic, assign) int duration;
|
||||
@property(nonatomic, assign) int length;
|
||||
@property(nonatomic, assign) BOOL isDownloading;
|
||||
@property(nonatomic, assign) BOOL isPlaying;
|
||||
@property(nonatomic, assign) CGFloat voiceHeight;
|
||||
@property(nonatomic, assign) NSTimeInterval currentTime;
|
||||
|
||||
/**
|
||||
*
|
||||
* Play animation image
|
||||
* An animation used to implement the "sonic image" fade of the speech as it plays.
|
||||
* If you want to customize the implementation of other kinds of animation icons, you can refer to the implementation of voiceAnimationIamges here.
|
||||
*/
|
||||
@property NSArray<UIImage *> *voiceAnimationImages;
|
||||
|
||||
/**
|
||||
*
|
||||
* voice icon
|
||||
* Animated icon to show when the speech is not playing.
|
||||
*/
|
||||
@property UIImage *voiceImage;
|
||||
@property(nonatomic, assign) CGFloat voiceTop;
|
||||
|
||||
/**
|
||||
*
|
||||
* Top margin of voice message
|
||||
* This value is used to determine the position of the bubble, which is convenient for UI layout of the content in the bubble.
|
||||
* If the value is abnormal or set arbitrarily, UI errors such as message position dislocation will occur.
|
||||
*/
|
||||
@property(nonatomic, class) CGFloat incommingVoiceTop;
|
||||
@property(nonatomic, class) CGFloat outgoingVoiceTop;
|
||||
|
||||
- (void)stopVoiceMessage;
|
||||
|
||||
/**
|
||||
* Begin to play voice. It will download the voice file from server if it not exists in local.
|
||||
*/
|
||||
- (void)playVoiceMessage;
|
||||
|
||||
@property(nonatomic, copy) void (^audioPlayerDidFinishPlayingBlock)(void);
|
||||
|
||||
|
||||
//The style of audio playback.
|
||||
+ (TUIVoiceAudioPlaybackStyle)getAudioplaybackStyle;
|
||||
+ (void)changeAudioPlaybackStyle;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
263
TUIKit/TUIChat/BaseCellData/Chat/TUIVoiceMessageCellData.m
Normal file
263
TUIKit/TUIChat/BaseCellData/Chat/TUIVoiceMessageCellData.m
Normal file
@@ -0,0 +1,263 @@
|
||||
//
|
||||
// TUIVoiceMessageCellData.m
|
||||
// TXIMSDK_TUIKit_iOS
|
||||
//
|
||||
// Created by annidyfeng on 2019/5/21.
|
||||
// Copyright © 2023 Tencent. All rights reserved.
|
||||
//
|
||||
|
||||
#import "TUIVoiceMessageCellData.h"
|
||||
#import <TIMCommon/TIMDefine.h>
|
||||
#import <TUICore/TUIThemeManager.h>
|
||||
@import AVFoundation;
|
||||
|
||||
@interface TUIVoiceMessageCellData () <AVAudioPlayerDelegate>
|
||||
@property AVAudioPlayer *audioPlayer;
|
||||
@property NSString *wavPath;
|
||||
@property(nonatomic, strong) NSTimer *timer;
|
||||
@end
|
||||
|
||||
@implementation TUIVoiceMessageCellData
|
||||
|
||||
+ (TUIMessageCellData *)getCellData:(V2TIMMessage *)message {
|
||||
V2TIMSoundElem *elem = message.soundElem;
|
||||
TUIVoiceMessageCellData *soundData = [[TUIVoiceMessageCellData alloc] initWithDirection:(message.isSelf ? MsgDirectionOutgoing : MsgDirectionIncoming)];
|
||||
soundData.duration = elem.duration;
|
||||
soundData.length = elem.dataSize;
|
||||
soundData.uuid = elem.uuid;
|
||||
soundData.reuseId = TVoiceMessageCell_ReuseId;
|
||||
soundData.path = elem.path;
|
||||
return soundData;
|
||||
}
|
||||
|
||||
+ (NSString *)getDisplayString:(V2TIMMessage *)message {
|
||||
return TIMCommonLocalizableString(TUIKitMessageTypeVoice); // @"[Voice]";
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewDataClass {
|
||||
return NSClassFromString(@"TUIVoiceReplyQuoteViewData");
|
||||
}
|
||||
|
||||
- (Class)getReplyQuoteViewClass {
|
||||
return NSClassFromString(@"TUIVoiceReplyQuoteView");
|
||||
}
|
||||
|
||||
- (instancetype)initWithDirection:(TMsgDirection)direction {
|
||||
self = [super initWithDirection:direction];
|
||||
if (self) {
|
||||
if (direction == MsgDirectionIncoming) {
|
||||
self.cellLayout = [TUIMessageCellLayout incommingVoiceMessageLayout];
|
||||
_voiceImage = TUIChatDynamicImage(@"chat_voice_message_receiver_voice_normal_img",
|
||||
[[TUIImageCache sharedInstance] getResourceFromCache:TUIChatImagePath(@"message_voice_receiver_normal")]);
|
||||
_voiceImage = [_voiceImage rtl_imageFlippedForRightToLeftLayoutDirection];
|
||||
_voiceAnimationImages = [NSArray arrayWithObjects:[self.class formatImageByName:@"message_voice_receiver_playing_1"],
|
||||
[self.class formatImageByName:@"message_voice_receiver_playing_2"],
|
||||
[self.class formatImageByName:@"message_voice_receiver_playing_3"], nil];
|
||||
_voiceTop = [[self class] incommingVoiceTop];
|
||||
} else {
|
||||
self.cellLayout = [TUIMessageCellLayout outgoingVoiceMessageLayout];
|
||||
_voiceImage = TUIChatDynamicImage(@"chat_voice_message_sender_voice_normal_img",
|
||||
[[TUIImageCache sharedInstance] getResourceFromCache:TUIChatImagePath(@"message_voice_sender_normal")]);
|
||||
_voiceImage = [_voiceImage rtl_imageFlippedForRightToLeftLayoutDirection];
|
||||
_voiceAnimationImages = [NSArray arrayWithObjects:[self.class formatImageByName:@"message_voice_sender_playing_1"],
|
||||
[self.class formatImageByName:@"message_voice_sender_playing_2"],
|
||||
[self.class formatImageByName:@"message_voice_sender_playing_3"], nil];
|
||||
_voiceTop = [[self class] outgoingVoiceTop];
|
||||
}
|
||||
_voiceHeight = 21;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (UIImage *)formatImageByName:(NSString *)imgName {
|
||||
NSString *path = TUIChatImagePath(imgName);
|
||||
UIImage *img = [[TUIImageCache sharedInstance] getResourceFromCache:path];
|
||||
return [img rtl_imageFlippedForRightToLeftLayoutDirection];
|
||||
}
|
||||
|
||||
- (NSString *)getVoicePath:(BOOL *)isExist {
|
||||
NSString *path = nil;
|
||||
BOOL isDir = false;
|
||||
*isExist = NO;
|
||||
if (self.direction == MsgDirectionOutgoing) {
|
||||
if (_path.length) {
|
||||
path = [NSString stringWithFormat:@"%@%@", TUIKit_Voice_Path, _path.lastPathComponent];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (!isDir) {
|
||||
*isExist = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!*isExist) {
|
||||
if (_uuid.length) {
|
||||
path = [NSString stringWithFormat:@"%@%@.amr", TUIKit_Voice_Path, _uuid];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir]) {
|
||||
if (!isDir) {
|
||||
*isExist = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
- (V2TIMSoundElem *)getIMSoundElem {
|
||||
V2TIMMessage *imMsg = self.innerMessage;
|
||||
if (imMsg.elemType == V2TIM_ELEM_TYPE_SOUND) {
|
||||
return imMsg.soundElem;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)playVoiceMessage {
|
||||
if (self.isPlaying) {
|
||||
[self stopVoiceMessage];
|
||||
return;
|
||||
}
|
||||
self.isPlaying = YES;
|
||||
|
||||
if (self.innerMessage.localCustomInt == 0) self.innerMessage.localCustomInt = 1;
|
||||
|
||||
V2TIMSoundElem *imSound = [self getIMSoundElem];
|
||||
BOOL isExist = NO;
|
||||
if (self.uuid.length == 0) {
|
||||
self.uuid = imSound.uuid;
|
||||
}
|
||||
NSString *path = [self getVoicePath:&isExist];
|
||||
if (isExist) {
|
||||
[self playInternal:path];
|
||||
} else {
|
||||
if (self.isDownloading) {
|
||||
return;
|
||||
}
|
||||
//
|
||||
self.isDownloading = YES;
|
||||
@weakify(self);
|
||||
[imSound downloadSound:path
|
||||
progress:^(NSInteger curSize, NSInteger totalSize) {
|
||||
|
||||
}
|
||||
succ:^{
|
||||
@strongify(self);
|
||||
self.isDownloading = NO;
|
||||
[self playInternal:path];
|
||||
}
|
||||
fail:^(int code, NSString *msg) {
|
||||
@strongify(self);
|
||||
self.isDownloading = NO;
|
||||
[self stopVoiceMessage];
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)playInternal:(NSString *)path {
|
||||
if (!self.isPlaying) return;
|
||||
// play current
|
||||
TUIVoiceAudioPlaybackStyle playbackStyle = [self.class getAudioplaybackStyle];
|
||||
if(playbackStyle == TUIVoiceAudioPlaybackStyleHandset) {
|
||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord error:nil];
|
||||
}
|
||||
else {
|
||||
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
|
||||
}
|
||||
NSURL *url = [NSURL fileURLWithPath:path];
|
||||
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
|
||||
self.audioPlayer.delegate = self;
|
||||
bool result = [self.audioPlayer play];
|
||||
if (!result) {
|
||||
self.wavPath = [[path stringByDeletingPathExtension] stringByAppendingString:@".wav"];
|
||||
NSURL *url = [NSURL fileURLWithPath:self.wavPath];
|
||||
[self.audioPlayer stop];
|
||||
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:nil];
|
||||
self.audioPlayer.delegate = self;
|
||||
[self.audioPlayer play];
|
||||
}
|
||||
|
||||
@weakify(self);
|
||||
if (@available(iOS 10.0, *)) {
|
||||
self.timer = [NSTimer scheduledTimerWithTimeInterval:0.1
|
||||
repeats:YES
|
||||
block:^(NSTimer *_Nonnull timer) {
|
||||
@strongify(self);
|
||||
[self updateProgress];
|
||||
}];
|
||||
} else {
|
||||
// Fallback on earlier versions
|
||||
}
|
||||
}
|
||||
//The style of audio playback.
|
||||
+ (TUIVoiceAudioPlaybackStyle)getAudioplaybackStyle {
|
||||
NSString *style = [NSUserDefaults.standardUserDefaults objectForKey:@"tui_audioPlaybackStyle"];
|
||||
if ([style isEqualToString:@"1"]) {
|
||||
return TUIVoiceAudioPlaybackStyleLoudspeaker;
|
||||
} else if ([style isEqualToString:@"2"]) {
|
||||
return TUIVoiceAudioPlaybackStyleHandset;
|
||||
}
|
||||
return TUIVoiceAudioPlaybackStyleLoudspeaker;
|
||||
}
|
||||
|
||||
+ (void)changeAudioPlaybackStyle {
|
||||
TUIVoiceAudioPlaybackStyle style = [self getAudioplaybackStyle];
|
||||
if (style == TUIVoiceAudioPlaybackStyleLoudspeaker) {
|
||||
[NSUserDefaults.standardUserDefaults setObject:@"2" forKey:@"tui_audioPlaybackStyle"];
|
||||
}
|
||||
else {
|
||||
[NSUserDefaults.standardUserDefaults setObject:@"1" forKey:@"tui_audioPlaybackStyle"];
|
||||
}
|
||||
[NSUserDefaults.standardUserDefaults synchronize];
|
||||
|
||||
}
|
||||
- (void)updateProgress {
|
||||
@weakify(self);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
@strongify(self);
|
||||
self.currentTime = self.audioPlayer.currentTime;
|
||||
});
|
||||
}
|
||||
|
||||
- (void)stopVoiceMessage {
|
||||
if ([self.audioPlayer isPlaying]) {
|
||||
[self.audioPlayer stop];
|
||||
self.audioPlayer = nil;
|
||||
}
|
||||
if (self.timer) {
|
||||
[self.timer invalidate];
|
||||
self.timer = nil;
|
||||
}
|
||||
self.isPlaying = NO;
|
||||
}
|
||||
|
||||
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)flag;
|
||||
{
|
||||
[self stopVoiceMessage];
|
||||
[[NSFileManager defaultManager] removeItemAtPath:self.wavPath error:nil];
|
||||
|
||||
if (self.audioPlayerDidFinishPlayingBlock) {
|
||||
self.audioPlayerDidFinishPlayingBlock();
|
||||
}
|
||||
}
|
||||
|
||||
static CGFloat gIncommingVoiceTop = 12;
|
||||
|
||||
+ (void)setIncommingVoiceTop:(CGFloat)incommingVoiceTop {
|
||||
gIncommingVoiceTop = incommingVoiceTop;
|
||||
}
|
||||
|
||||
+ (CGFloat)incommingVoiceTop {
|
||||
return gIncommingVoiceTop;
|
||||
}
|
||||
|
||||
static CGFloat gOutgoingVoiceTop = 12;
|
||||
|
||||
+ (void)setOutgoingVoiceTop:(CGFloat)outgoingVoiceTop {
|
||||
gOutgoingVoiceTop = outgoingVoiceTop;
|
||||
}
|
||||
|
||||
+ (CGFloat)outgoingVoiceTop {
|
||||
return gOutgoingVoiceTop;
|
||||
}
|
||||
|
||||
@end
|
||||
Reference in New Issue
Block a user