PhotoKit integration done. Need small bug fix.

This commit is contained in:
Benjamin Verdier 2018-06-22 12:06:59 +02:00
parent 0ec09ef8d5
commit 6023bbff6c
15 changed files with 359 additions and 308 deletions

View file

@ -27,7 +27,7 @@
@protocol ChatConversationDelegate <NSObject>
- (BOOL)startImageUpload:(UIImage *)image url:(NSURL *)url withQuality:(float)quality;
- (BOOL)startImageUpload:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)quality;
- (BOOL)startFileUpload:(NSData *)data withUrl:(NSURL *)url;
- (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url;
- (void)tableViewIsScrolling;
@ -41,6 +41,7 @@
@property(nonatomic) LinphoneChatRoom *chatRoom;
@property(nonatomic, strong) id<ChatConversationDelegate> chatRoomDelegate;
@property NSMutableDictionary<NSString *, UIImage *> *imagesInChatroom;
- (void)addEventEntry:(LinphoneEventLog *)event;
- (void)scrollToBottom:(BOOL)animated;

View file

@ -39,29 +39,36 @@
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.tableView.accessibilityIdentifier = @"ChatRoom list";
_imagesInChatroom = [NSMutableDictionary dictionary];
}
#pragma mark -
- (void)clearEventList {
[eventList removeAllObjects];
for (NSValue *value in eventList) {
LinphoneEventLog *event = value.pointerValue;
linphone_event_log_unref(event);
}
[eventList removeAllObjects];
}
- (void)updateData {
[self clearEventList];
if (!_chatRoom)
return;
[self clearEventList];
LinphoneChatRoomCapabilitiesMask capabilities = linphone_chat_room_get_capabilities(_chatRoom);
bctbx_list_t *chatRoomEvents = (capabilities & LinphoneChatRoomCapabilitiesOneToOne)
? linphone_chat_room_get_history_message_events(_chatRoom, 0)
: linphone_chat_room_get_history_events(_chatRoom, 0);
bctbx_list_t *head = chatRoomEvents;
eventList = [[NSMutableArray alloc] initWithCapacity:bctbx_list_size(chatRoomEvents)];
while (chatRoomEvents) {
LinphoneEventLog *event = (LinphoneEventLog *)chatRoomEvents->data;
[eventList addObject:[NSValue valueWithPointer:linphone_event_log_ref(event)]];
chatRoomEvents = chatRoomEvents->next;
}
bctbx_list_free_with_data(head, (bctbx_list_free_func)linphone_event_log_unref);
for (FileTransferDelegate *ftd in [LinphoneManager.instance fileTransferDelegates]) {
const LinphoneAddress *ftd_peer =
@ -83,7 +90,6 @@
- (void)addEventEntry:(LinphoneEventLog *)event {
[eventList addObject:[NSValue valueWithPointer:linphone_event_log_ref(event)]];
int pos = (int)eventList.count - 1;
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:pos inSection:0];
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:@[ indexPath ] withRowAnimation:UITableViewRowAnimationFade];
@ -102,12 +108,12 @@
}
- (void)scrollToBottom:(BOOL)animated {
[self.tableView reloadData];
//[self.tableView reloadData];
size_t count = eventList.count;
if (!count)
return;
[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:(count - 1) inSection:0]];
//[self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:(count - 1) inSection:0]];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:(count - 1) inSection:0]
atScrollPosition:UITableViewScrollPositionBottom
animated:YES];
@ -231,7 +237,6 @@ static const CGFloat MESSAGE_SPACING_PERCENTAGE = 5.f;
if (nextEvent) {
LinphoneChatMessage *nextChat = linphone_event_log_get_chat_message(nextEvent);
if (!linphone_address_equal(linphone_chat_message_get_from_address(nextChat), linphone_chat_message_get_from_address(chat))) {
LOGD(@"BITE");
height += tableView.frame.size.height * MESSAGE_SPACING_PERCENTAGE / 100;
}
}

View file

@ -222,7 +222,7 @@ static UICompositeViewDescription *compositeDescription = nil;
//share photo
NSData *data = dict[@"nsData"];
UIImage *image = [[UIImage alloc] initWithData:data];
[self chooseImageQuality:image url:nil];
[self chooseImageQuality:image assetId:nil];
[defaults removeObjectForKey:@"img"];
} else if (dictWeb) {
//share url, if local file, then upload file
@ -310,38 +310,12 @@ static UICompositeViewDescription *compositeDescription = nil;
return TRUE;
}
- (void)saveAndSend:(UIImage *)image url:(NSURL *)url withQuality:(float)quality{
- (void)saveAndSend:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)quality{
// photo from Camera, must be saved first
if (url == nil) {
__block NSURL *assetURL = nil;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
[PHAssetChangeRequest creationRequestForAssetFromImage:image];
} completionHandler:^(BOOL success, NSError *error) {
if (success) {
LOGI(@"Image saved to [%@]", [assetURL absoluteString]);
[self startImageUpload:image url:assetURL withQuality:quality];
} else {
LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
message:NSLocalizedString(@"Cannot write image to photo library",
nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[self presentViewController:errView animated:YES completion:nil];
}
}];
} else {
[self startImageUpload:image url:url withQuality:quality];
}
[self startImageUpload:image assetId:phAssetId withQuality:quality];
}
- (void)chooseImageQuality:(UIImage *)image url:(NSURL *)url {
- (void)chooseImageQuality:(UIImage *)image assetId:(NSString *)phAssetId {
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"Choose the image size", nil)];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSString *key in [imageQualities allKeys]) {
@ -351,7 +325,7 @@ static UICompositeViewDescription *compositeDescription = nil;
NSString *text = [NSString stringWithFormat:@"%@ (%@)", key, [size toHumanReadableSize]];
[sheet addButtonWithTitle:text
block:^() {
[self saveAndSend:image url:url withQuality:[quality floatValue]];
[self saveAndSend:image assetId:phAssetId withQuality:[quality floatValue]];
}];
}
[sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil];
@ -603,9 +577,9 @@ static UICompositeViewDescription *compositeDescription = nil;
#pragma mark ChatRoomDelegate
- (BOOL)startImageUpload:(UIImage *)image url:(NSURL *)url withQuality:(float)quality {
- (BOOL)startImageUpload:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)quality {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
[fileTransfer upload:image withURL:url forChatRoom:_chatRoom withQuality:quality];
[fileTransfer upload:image withassetId:phAssetId forChatRoom:_chatRoom withQuality:quality];
[_tableController scrollToBottom:true];
return TRUE;
}
@ -623,7 +597,7 @@ static UICompositeViewDescription *compositeDescription = nil;
#pragma mark ImagePickerDelegate
- (void)imagePickerDelegateImage:(UIImage *)image info:(NSDictionary *)info {
- (void)imagePickerDelegateImage:(UIImage *)image info:(NSString *)phAssetId {
// When getting image from the camera, it may be 90° rotated due to orientation
// (image.imageOrientation = UIImageOrientationRight). Just rotate it to be face up.
if (image.imageOrientation != UIImageOrientationUp) {
@ -637,9 +611,7 @@ static UICompositeViewDescription *compositeDescription = nil;
if (IPAD) {
[VIEW(ImagePickerView).popoverController dismissPopoverAnimated:TRUE];
}
NSURL *url = [info valueForKey:UIImagePickerControllerReferenceURL];
[self chooseImageQuality:image url:url];
[self chooseImageQuality:image assetId:phAssetId];
}
- (void)tableViewIsScrolling {

View file

@ -21,7 +21,7 @@
@protocol ImagePickerDelegate <NSObject>
- (void)imagePickerDelegateImage:(UIImage *)image info:(NSDictionary *)info;
- (void)imagePickerDelegateImage:(UIImage *)image info:(NSString *)phAssetId;
@end

View file

@ -161,27 +161,38 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[self dismiss];
UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];
if (image == nil) {
image = [info objectForKey:UIImagePickerControllerOriginalImage];
}
if (image != nil) {
if (![info objectForKey:UIImagePickerControllerReferenceURL]) {
//Saving image. Supports picture only, no video
//Maybe add a completion target to send the saved image to, like self, and we would call it manually if the image was not taken.
UIImageWriteToSavedPhotosAlbum(image, self, @selector(savedImage:didFinishSavingWithError:contextInfo:), (__bridge void *)info);
} else {
[self savedImage:image didFinishSavingWithError:nil contextInfo:(__bridge void *)info];
PHAsset *phasset = [info objectForKey:UIImagePickerControllerPHAsset];
UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage] ? [info objectForKey:UIImagePickerControllerEditedImage] : [info objectForKey:UIImagePickerControllerOriginalImage];
if (!phasset) {
__block PHObjectPlaceholder *placeHolder;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromImage:image];
placeHolder = [request placeholderForCreatedAsset];
} completionHandler:^(BOOL success, NSError *error) {
if (success) {
LOGI(@"Image saved to [%@]", [placeHolder localIdentifier]);
[self passImageToDelegate:image PHAssetId:[placeHolder localIdentifier]];
} else {
LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
}
}
];
return;
}
[self passImageToDelegate:image PHAssetId:[phasset localIdentifier]];
}
- (void)savedImage:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
- (void) passImageToDelegate:(UIImage *)image PHAssetId:(NSString *)assetId {
if (imagePickerDelegate != nil) {
[imagePickerDelegate imagePickerDelegateImage:image info:(NSString *)assetId];
}
}
/*
if (imagePickerDelegate != nil) {
[imagePickerDelegate imagePickerDelegateImage:image info:(__bridge NSDictionary *)contextInfo];
}
}
*/
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
[self dismiss];
}
@ -234,35 +245,62 @@ static UICompositeViewDescription *compositeDescription = nil;
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
}
};
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"Select the source", nil)];
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[sheet addButtonWithTitle:NSLocalizedString(@"Camera", nil)
block:^() {
if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusAuthorized ){
if([PHPhotoLibrary authorizationStatus] != PHAuthorizationStatusDenied ){
block(UIImagePickerControllerSourceTypeCamera);
}else{
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show];
}
}else {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Camera's permission", nil) message:NSLocalizedString(@"Camera not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show];
}
}];
}
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
[sheet addButtonWithTitle:NSLocalizedString(@"Photo library", nil)
block:^() {
if([PHPhotoLibrary authorizationStatus] != PHAuthorizationStatusDenied ){
block(UIImagePickerControllerSourceTypePhotoLibrary);
}else{
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show];
}
}];
}
[sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil];
[sheet showInView:PhoneMainView.instance.view];
[PHPhotoLibrary authorizationStatus];
PHAuthorizationStatus status = [PHPhotoLibrary authorizationStatus];
if (status == PHAuthorizationStatusNotDetermined) {
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
if(status != PHAuthorizationStatusAuthorized) {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show];
return;
}
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"Select the source", nil)];
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[sheet addButtonWithTitle:NSLocalizedString(@"Camera", nil)
block:^() {
if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] != AVAuthorizationStatusAuthorized ) {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Camera's permission", nil) message:NSLocalizedString(@"Camera not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show];
return;
}
block(UIImagePickerControllerSourceTypeCamera);
}];
}
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
[sheet addButtonWithTitle:NSLocalizedString(@"Photo library", nil)
block:^() {
block(UIImagePickerControllerSourceTypePhotoLibrary);
}];
}
[sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil];
[sheet showInView:PhoneMainView.instance.view];
}];
} else if (status == PHAuthorizationStatusAuthorized) {
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"Select the source", nil)];
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
[sheet addButtonWithTitle:NSLocalizedString(@"Camera", nil)
block:^() {
if([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] != AVAuthorizationStatusAuthorized ) {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Camera's permission", nil) message:NSLocalizedString(@"Camera not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show];
return;
}
block(UIImagePickerControllerSourceTypeCamera);
}];
}
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) {
[sheet addButtonWithTitle:NSLocalizedString(@"Photo library", nil)
block:^() {
block(UIImagePickerControllerSourceTypePhotoLibrary);
}];
}
[sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil];
[sheet showInView:PhoneMainView.instance.view];
} else {
[[[UIAlertView alloc] initWithTitle:NSLocalizedString(@"Photo's permission", nil) message:NSLocalizedString(@"Photo not authorized", nil) delegate:nil cancelButtonTitle:nil otherButtonTitles:@"Continue", nil] show];
return;
}
}
@end

View file

@ -20,7 +20,6 @@
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SCNetworkReachability.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AssetsLibrary/ALAssetsLibrary.h>
#import <Photos/Photos.h>
#import <CoreTelephony/CTCallCenter.h>
@ -209,6 +208,8 @@ typedef struct _LinphoneManagerSounds {
- (void)checkNewVersion;
- (void)loadAvatar;
@property ProviderDelegate *providerDelegate;
@property (readonly) BOOL isTesting;
@ -226,7 +227,6 @@ typedef struct _LinphoneManagerSounds {
@property (nonatomic, assign) BOOL speakerEnabled;
@property (nonatomic, assign) BOOL bluetoothAvailable;
@property (nonatomic, assign) BOOL bluetoothEnabled;
@property (readonly) ALAssetsLibrary *photoLibrary;
@property (readonly) NSString* contactSipField;
@property (readonly,copy) NSString* contactFilter;
@property (copy) void (^silentPushCompletion)(UIBackgroundFetchResult);
@ -239,5 +239,6 @@ typedef struct _LinphoneManagerSounds {
@property NSDictionary *pushDict;
@property(strong, nonatomic) OrderedDictionary *linphoneManagerAddressBookMap;
@property (nonatomic, assign) BOOL contactsUpdated;
@property UIImage *avatar;
@end

View file

@ -287,6 +287,7 @@ struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_pre
}
[self migrateFromUserPrefs];
[self loadAvatar];
}
return self;
}
@ -3076,4 +3077,33 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
const char *curVersionCString = [curVersion cStringUsingEncoding:NSUTF8StringEncoding];
linphone_core_check_for_update(theLinphoneCore, curVersionCString);
}
- (void)loadAvatar {
NSString *assetId = [self lpConfigStringForKey:@"avatar"];
__block UIImage *ret = nil;
if (assetId) {
PHFetchResult<PHAsset *> *assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:assetId] options:nil];
if (![assets firstObject]) {
LOGE(@"Can't fetch avatar image.");
}
PHAsset *asset = [assets firstObject];
// load avatar synchronously so that we can return UIIMage* directly - since we are
// only using thumbnail, it must be pretty fast to fetch even without cache.
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.synchronous = TRUE;
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:options
resultHandler:^(UIImage *image, NSDictionary * info) {
if (image)
ret = [UIImage UIImageThumbnail:image thumbSize:150];
else
LOGE(@"Can't read avatar");
}];
}
if (!ret) {
ret = [UIImage imageNamed:@"avatar.png"];
}
_avatar = ret;
}
@end

View file

@ -30,9 +30,8 @@
@implementation UIChatBubblePhotoCell {
FileTransferDelegate *_ftd;
CGSize imageSize, bubbleSize, videoDefaultSize;
int actualAvailableWidth;
ChatConversationTableView *chatTableView;
//CGImageRef displayedImage;
BOOL assetIsLoaded;
}
#pragma mark - Lifecycle Functions
@ -51,11 +50,10 @@
break;
}
}
[self setFrame:CGRectMake(0, 0, 5, 100)];
[self addSubview:sub];
chatTableView = VIEW(ChatConversationView).tableController;
actualAvailableWidth = chatTableView.tableView.frame.size.width;
videoDefaultSize = CGSizeMake(320, 240);
assetIsLoaded = FALSE;
}
return self;
}
@ -76,6 +74,7 @@
_finalImage.image = nil;
_finalImage.hidden = TRUE;
_fileTransferProgress.progress = 0;
assetIsLoaded = FALSE;
[self disconnectFromFileDelegate];
if (amessage) {
@ -98,11 +97,10 @@
[super setChatMessage:amessage];
}
- (void) loadImageAsset:(ALAsset*) asset thumb:(UIImage *)thumb image:(UIImage *)image {
- (void) loadImageAsset:(PHAsset*) asset image:(UIImage *)image {
dispatch_async(dispatch_get_main_queue(), ^{
[_finalImage setImage:image];
[_messageImageView setImage:thumb];
[_messageImageView setFullImageUrl:asset];
[_messageImageView setAsset:asset];
[_messageImageView stopLoading];
_messageImageView.hidden = YES;
_imageGestureRecognizer.enabled = YES;
@ -110,13 +108,22 @@
});
}
- (void) loadAsset:(ALAsset*) asset {
UIImage *thumb = [[UIImage alloc] initWithCGImage:[asset thumbnail]];
ALAssetRepresentation *representation = [asset defaultRepresentation];
imageSize = [UIChatBubbleTextCell getMediaMessageSizefromOriginalSize:[representation dimensions] withWidth:chatTableView.tableView.frame.size.width];
CGImageRef tmpImg = [self cropImageFromRepresentation:representation];
UIImage *image = [[UIImage alloc] initWithCGImage:tmpImg];
[self loadImageAsset:asset thumb:thumb image:image];
- (void) loadAsset:(PHAsset *) asset {
LOGD(@"SALOPE");
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.synchronous = TRUE;
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:options
resultHandler:^(UIImage *image, NSDictionary * info) {
if (image) {
imageSize = [UIChatBubbleTextCell getMediaMessageSizefromOriginalSize:[image size] withWidth:chatTableView.tableView.frame.size.width];
UIImage *newImage = [UIImage UIImageResize:image toSize:imageSize];
[chatTableView.imagesInChatroom setObject:newImage forKey:[asset localIdentifier]];
[self loadImageAsset:asset image:newImage];
}
else {
LOGE(@"Can't read image");
}
}];
}
- (void) loadVideoAsset: (AVAsset *) asset {
@ -139,7 +146,7 @@
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self loadImageAsset:nil thumb:thumb image:image];
[self loadImageAsset:nil image:image];
// put the play button in the top
CGRect newFrame = _playButton.frame;
@ -184,39 +191,21 @@
_cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = _playButton.hidden = _fileName.hidden = YES;
fullScreenImage = YES;
} else {
assetIsLoaded = TRUE;
LOGD(@"POUET");
if (localImage) {
// we did not load the image yet, so start doing so
if (_messageImageView.image == nil) {
NSURL *imageUrl = [NSURL URLWithString:localImage];
[_messageImageView startLoading];
__block LinphoneChatMessage *achat = self.message;
[LinphoneManager.instance.photoLibrary assetForURL:imageUrl resultBlock:^(ALAsset *asset) { dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL), ^(void) {
if (achat != self.message) // Avoid glitch and scrolling
return;
if (asset) {
[self loadAsset:asset];
}
else {
[LinphoneManager.instance.photoLibrary
enumerateGroupsWithTypes:ALAssetsGroupAll
usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
[group enumerateAssetsWithOptions:NSEnumerationReverse
usingBlock:^(ALAsset *result, NSUInteger index, BOOL *stop) {
if([result.defaultRepresentation.url isEqual:imageUrl]) {
[self loadAsset:result];
*stop = YES;
}
}];
}
failureBlock:^(NSError *error) {
LOGE(@"Error: Cannot load asset from photo stream - %@", [error localizedDescription]);
}];
}
});
} failureBlock:^(NSError *error) {
LOGE(@"Can't read image");
}];
PHFetchResult<PHAsset *> *assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localImage] options:nil];
UIImage *img = [chatTableView.imagesInChatroom objectForKey:localImage];
if (![assets firstObject])
return;
PHAsset *asset = [assets firstObject];
if (img)
[self loadImageAsset:asset image:img];
else
[self loadAsset:asset];
}
} else if (localVideo) {
if (_messageImageView.image == nil) {
@ -249,13 +238,6 @@
}
}
}
// resize image so that it take the full bubble space available
CGRect newFrame = _totalView.frame;
newFrame.origin.x = newFrame.origin.y = 0;
if (!fullScreenImage) {
newFrame.size.height -= _imageSubView.frame.size.height;
}
_messageImageView.frame = newFrame;
}
- (void)fileErrorBlock {
@ -336,9 +318,18 @@
if (![_messageImageView isLoading]) {
ImageView *view = VIEW(ImageView);
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
CGImageRef fullScreenRef = [[_messageImageView.fullImageUrl defaultRepresentation] fullScreenImage];
UIImage *fullScreen = [UIImage imageWithCGImage:fullScreenRef];
[view setImage:fullScreen];
PHAsset *asset = [_messageImageView asset];
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.synchronous = TRUE;
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:options
resultHandler:^(UIImage *image, NSDictionary * info) {
if (image) {
[view setImage:image];
}
else {
LOGE(@"Can't read image");
}
}];
}
}
}
@ -399,31 +390,8 @@
}
}
+ (CGImageRef)resizeCGImage:(CGImageRef)image toWidth:(int)width andHeight:(int)height {
// create context, keeping original image properties
CGColorSpaceRef colorspace = CGImageGetColorSpace(image);
CGContextRef context = CGBitmapContextCreate(NULL, width, height,
CGImageGetBitsPerComponent(image),
CGImageGetBytesPerRow(image),
colorspace,
CGImageGetAlphaInfo(image));
CGColorSpaceRelease(colorspace);
if(context == NULL)
return nil;
// draw image to context (resizing it)
CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
// extract resulting image from context
CGImageRef imgRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
return imgRef;
}
- (void)layoutSubviews {
[super layoutSubviews];
//if (!isAssetLoaded) return;
//[super layoutSubviews];
UITableView *tableView = VIEW(ChatConversationView).tableController.tableView;
BOOL is_outgoing = linphone_chat_message_is_outgoing(super.message);
CGRect bubbleFrame = super.bubbleView.frame;
@ -444,25 +412,6 @@
super.bubbleView.frame = bubbleFrame;
}
- (CGImageRef)cropImageFromRepresentation:(ALAssetRepresentation*)rep {
CGImageRef newImage = [rep fullResolutionImage];
CGSize originalSize = [rep dimensions];
float originalAspectRatio = originalSize.width / originalSize.height;
// We resize in width and crop in height
if (originalSize.width > imageSize.width) {
int height = imageSize.width / originalAspectRatio;
newImage = [self.class resizeCGImage:newImage toWidth:imageSize.width andHeight:height];
originalSize.height = height;
}
CGRect cropRect = CGRectMake(0, 0, imageSize.width, imageSize.height);
if (imageSize.height < originalSize.height) cropRect.origin.y = (originalSize.height - imageSize.height)/2;
newImage = CGImageCreateWithImageInRect(newImage, cropRect);
LOGD([NSString stringWithFormat:@"Image size : width = %g, height = %g", imageSize.width, imageSize.height]);
LOGD([NSString stringWithFormat:@"Bubble size : width = %g, height = %g", super.bubbleView.frame.size.width, super.bubbleView.frame.size.height]);
return newImage;
}
@end

View file

@ -235,20 +235,26 @@
NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:_message];
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:_message];
NSString *fileName = localVideo ? localVideo : localFile;
NSURL *imageUrl = [NSURL URLWithString:localImage];
[self onDelete];
if(localImage){
[LinphoneManager.instance.photoLibrary assetForURL:imageUrl
resultBlock:^(ALAsset *asset) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL),
^(void) {
UIImage *image = [[UIImage alloc] initWithCGImage:[[asset defaultRepresentation] fullResolutionImage]];
[_chatRoomDelegate startImageUpload:image url:imageUrl withQuality:(uploadQuality ? [uploadQuality floatValue] : 0.9)];
});
}
failureBlock:^(NSError *error) {
LOGE(@"Can't read image");
}];
PHFetchResult<PHAsset *> *assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localImage] options:nil];
if (![assets firstObject]) {
return;
}
PHAsset *asset = [assets firstObject];
PHImageRequestOptions *options = [[PHImageRequestOptions alloc] init];
options.synchronous = TRUE;
[[PHImageManager defaultManager] requestImageForAsset:asset targetSize:PHImageManagerMaximumSize contentMode:PHImageContentModeDefault options:options
resultHandler:^(UIImage *image, NSDictionary * info) {
if (image) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL),
^(void) {
[_chatRoomDelegate startImageUpload:image assetId:localImage withQuality:(uploadQuality ? [uploadQuality floatValue] : 0.9)];
});
} else {
LOGE(@"Can't read image");
}
}];
} else if(fileName) {
NSString *filePath = [LinphoneManager documentFile:fileName];
[_chatRoomDelegate startFileUpload:[NSData dataWithContentsOfFile:filePath] withUrl:[NSURL URLWithString:filePath]];
@ -346,22 +352,16 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 52; // 44;
size = [self getMediaMessageSizefromOriginalSize:videoSize withWidth:width];
size.height += CELL_MESSAGE_X_MARGIN;
} else {
NSURL *imageUrl = [NSURL URLWithString:localImage];
__block CGSize originalImageSize = CGSizeMake(0, 0);
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
[LinphoneManager.instance.photoLibrary assetForURL:imageUrl
resultBlock:^(ALAsset *asset) {
originalImageSize = [[asset defaultRepresentation] dimensions];
dispatch_semaphore_signal(sema);
}
failureBlock:^(NSError *error) {
LOGE(@"Can't read image");
dispatch_semaphore_signal(sema);
}];
});
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
LOGE(@"LA BITE EN BOIS");
if (!localImage) {
return CGSizeMake(CELL_MIN_WIDTH, CELL_MIN_HEIGHT);
}
PHFetchResult<PHAsset *> *assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localImage] options:nil];
if (![assets firstObject]) {
return CGSizeMake(CELL_MIN_WIDTH, CELL_MIN_HEIGHT);
}
PHAsset *asset = [assets firstObject];
CGSize originalImageSize = CGSizeMake([asset pixelWidth], [asset pixelHeight]);
size = [self getMediaMessageSizefromOriginalSize:originalImageSize withWidth:width];
//This fixes the image being too small. I think the issue comes form the fact that the display is retina. This should probably be changed in the future.
size.height += CELL_MESSAGE_X_MARGIN;

View file

@ -28,7 +28,7 @@
- (BOOL)isLoading;
- (void)stopLoading;
@property(nonatomic, strong) ALAsset *fullImageUrl;
@property(nonatomic, strong) PHAsset *asset;
@property (nonatomic, readonly) IBOutlet UIActivityIndicatorView *waitIndicatorView;
@end

View file

@ -104,7 +104,7 @@
#pragma mark - Image picker delegate
- (void)imagePickerDelegateImage:(UIImage *)image info:(NSDictionary *)info {
- (void)imagePickerDelegateImage:(UIImage *)image info:(NSString *)phAssetId {
// When getting image from the camera, it may be 90° rotated due to orientation
// (image.imageOrientation = UIImageOrientationRight). Just rotate it to be face up.
if (image.imageOrientation != UIImageOrientationUp) {
@ -120,27 +120,10 @@
} else {
[PhoneMainView.instance.mainViewController hideSideMenu:NO];
}
NSURL *url = [info valueForKey:UIImagePickerControllerReferenceURL];
// taken from camera, must be saved to device first
if (!url) {
__block NSURL *assetURL = nil;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
[PHAssetChangeRequest creationRequestForAssetFromImage:image];
} completionHandler:^(BOOL success, NSError *error) {
if (success) {
LOGI(@"Image saved to [%@]", [assetURL absoluteString]);
} else {
LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
}
[LinphoneManager.instance lpConfigSetString:assetURL.absoluteString forKey:@"avatar"];
_avatarImage.image = [LinphoneUtils selfAvatar];
}];
} else {
[LinphoneManager.instance lpConfigSetString:url.absoluteString forKey:@"avatar"];
_avatarImage.image = [LinphoneUtils selfAvatar];
}
[LinphoneManager.instance lpConfigSetString:phAssetId forKey:@"avatar"];
_avatarImage.image = [LinphoneUtils selfAvatar];
[LinphoneManager.instance loadAvatar];
}
@end

View file

@ -12,7 +12,7 @@
@interface FileTransferDelegate : NSObject
- (void)upload:(UIImage *)image withURL:(NSURL *)url forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality;
- (void)upload:(UIImage *)image withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality;
- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withUrl:(NSURL *)url;
- (void)cancel;
- (BOOL)download:(LinphoneChatMessage *)message;

View file

@ -75,44 +75,44 @@ static void linphone_iphone_file_transfer_recv(LinphoneChatMessage *message, con
// chat bubble is aware of the fact that image is being saved to device
[LinphoneManager setValueInMessageAppData:@"saving..." forKey:@"localimage" inMessage:message];
[LinphoneManager.instance.photoLibrary
writeImageToSavedPhotosAlbum:image.CGImage
orientation:(ALAssetOrientation)[image imageOrientation]
completionBlock:^(NSURL *assetURL, NSError *error) {
if (error) {
LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
[LinphoneManager setValueInMessageAppData:nil forKey:@"localimage" inMessage:message];
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
message:NSLocalizedString(@"Cannot write image to photo library",
nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
} else {
LOGI(@"Image saved to [%@]", [assetURL absoluteString]);
[LinphoneManager setValueInMessageAppData:[assetURL absoluteString]
forKey:@"localimage"
inMessage:message];
}
[NSNotificationCenter.defaultCenter
postNotificationName:kLinphoneFileTransferRecvUpdate
object:thiz
userInfo:@{
@"state" : @(LinphoneChatMessageStateDelivered), // we dont want to
// trigger
// FileTransferDone here
@"image" : image,
@"progress" : @(1.f),
}];
[thiz stopAndDestroy];
CFRelease((__bridge CFTypeRef)thiz);
}];
__block PHObjectPlaceholder *placeHolder;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromImage:image];
placeHolder = [request placeholderForCreatedAsset];
} completionHandler:^(BOOL success, NSError *error) {
if (success) {
LOGI(@"Image saved to [%@]", [placeHolder localIdentifier]);
[LinphoneManager setValueInMessageAppData:[placeHolder localIdentifier]
forKey:@"localimage"
inMessage:message];
} else {
LOGE(@"Cannot save image data downloaded [%@]", [error localizedDescription]);
[LinphoneManager setValueInMessageAppData:nil forKey:@"localimage" inMessage:message];
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
message:NSLocalizedString(@"Cannot write image to photo library",
nil)
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[errView addAction:defaultAction];
[PhoneMainView.instance presentViewController:errView animated:YES completion:nil];
}
[NSNotificationCenter.defaultCenter
postNotificationName:kLinphoneFileTransferRecvUpdate
object:thiz
userInfo:@{
@"state" : @(LinphoneChatMessageStateDelivered),
// we dont want to trigger FileTransferDone here
@"image" : image,
@"progress" : @(1.f),
}];
[thiz stopAndDestroy];
CFRelease((__bridge CFTypeRef)thiz);
}];
} else {
[[LinphoneManager.instance fileTransferDelegates] removeObject:thiz];
@ -224,10 +224,10 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m
}
}
- (void)upload:(UIImage *)image withURL:(NSURL *)url forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality {
- (void)upload:(UIImage *)image withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality {
NSString *name = [NSString stringWithFormat:@"%li-%f.jpg", (long)image.hash, [NSDate timeIntervalSinceReferenceDate]];
if (url)
[self uploadData:UIImageJPEGRepresentation(image, quality) forChatRoom:chatRoom type:@"image" subtype:@"jpeg" name:name key:@"localimage" keyData:[url absoluteString] qualityData:[NSNumber numberWithFloat:quality]];
if (phAssetId)
[self uploadData:UIImageJPEGRepresentation(image, quality) forChatRoom:chatRoom type:@"image" subtype:@"jpeg" name:name key:@"localimage" keyData:phAssetId qualityData:[NSNumber numberWithFloat:quality]];
else
[self uploadData:UIImageJPEGRepresentation(image, quality) forChatRoom:chatRoom type:@"image" subtype:@"jpeg" name:name key:@"localimage" keyData:nil qualityData:nil];
}

View file

@ -102,6 +102,16 @@ typedef enum {
@end
@interface UIImage (ResizeAndThumbnail)
+ (UIImage *)UIImageThumbnail:(UIImage *)image thumbSize:(CGFloat) tbSize;
+ (UIImage *)UIImageResize:(UIImage *)image toSize:(CGSize) newSize;
+ (CGImageRef)resizeCGImage:(CGImageRef)image toWidth:(int)width andHeight:(int)height;
@end
/* Use that macro when you want to invoke a custom initialisation method on your class,
whatever is using it (xib, source code, etc., tableview cell) */
#define INIT_WITH_COMMON_C \

View file

@ -32,36 +32,10 @@
@implementation LinphoneUtils
+ (BOOL)hasSelfAvatar {
return [NSURL URLWithString:[LinphoneManager.instance lpConfigStringForKey:@"avatar"]] != nil;
return [LinphoneManager.instance lpConfigStringForKey:@"avatar"] != nil;
}
+ (UIImage *)selfAvatar {
NSURL *url = [NSURL URLWithString:[LinphoneManager.instance lpConfigStringForKey:@"avatar"]];
__block UIImage *ret = nil;
if (url) {
__block NSConditionLock *photoLock = [[NSConditionLock alloc] initWithCondition:1];
// load avatar synchronously so that we can return UIIMage* directly - since we are
// only using thumbnail, it must be pretty fast to fetch even without cache.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[LinphoneManager.instance.photoLibrary assetForURL:url
resultBlock:^(ALAsset *asset) {
ret = [[UIImage alloc] initWithCGImage:[asset thumbnail]];
[photoLock lock];
[photoLock unlockWithCondition:0];
}
failureBlock:^(NSError *error) {
LOGE(@"Can't read avatar");
[photoLock lock];
[photoLock unlockWithCondition:0];
}];
});
[photoLock lockWhenCondition:0];
[photoLock unlock];
}
if (!ret) {
ret = [UIImage imageNamed:@"avatar.png"];
}
return ret;
return [LinphoneManager.instance avatar];
}
+ (NSString *)durationToString:(int)duration {
@ -777,3 +751,91 @@
}
@end
@implementation UIImage (ResizeAndThumbnail)
+ (UIImage *)UIImageThumbnail:(UIImage *)image thumbSize:(CGFloat) tbSize {
// Create a thumbnail version of the image for the event object.
CGSize size = image.size;
CGSize croppedSize;
CGFloat offsetX = 0.0;
CGFloat offsetY = 0.0;
CGFloat actualTbSize = MAX(tbSize, MAX(size.height, size.width));
// check the size of the image, we want to make it
// a square with sides the size of the smallest end
if (size.width > size.height) {
offsetX = (size.height - size.width) / 2;
croppedSize = CGSizeMake(size.height, size.height);
} else {
offsetY = (size.width - size.height) / 2;
croppedSize = CGSizeMake(size.width, size.width);
}
// Crop the image before resize
CGRect clippedRect = CGRectMake(offsetX * -1,
offsetY * -1,
croppedSize.width,
croppedSize.height);
CGImageRef imageRef = CGImageCreateWithImageInRect([image CGImage],
clippedRect);
UIImage *cropped = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
// Done cropping
// Resize the image
CGRect rect = CGRectMake(0, 0, actualTbSize, actualTbSize);
UIGraphicsBeginImageContext(rect.size);
[cropped drawInRect:rect];
UIImage *thumbnail = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// Done Resizing
return thumbnail;
}
+ (UIImage *)UIImageResize:(UIImage *)image toSize:(CGSize) newSize {
CGImageRef newImage = [image CGImage];
CGSize originalSize = [image size];
float originalAspectRatio = originalSize.width / originalSize.height;
// We resize in width and crop in height
if (originalSize.width > newSize.width) {
int height = newSize.width / originalAspectRatio;
newImage = [UIImage resizeCGImage:newImage toWidth:newSize.width andHeight:height];
originalSize.height = height;
}
CGRect cropRect = CGRectMake(0, 0, newSize.width, newSize.height);
if (newSize.height < originalSize.height) cropRect.origin.y = (originalSize.height - newSize.height)/2;
newImage = CGImageCreateWithImageInRect(newImage, cropRect);
UIImage *cropped = [UIImage imageWithCGImage:newImage];
CGImageRelease(newImage);
return cropped;
}
+ (CGImageRef)resizeCGImage:(CGImageRef)image toWidth:(int)width andHeight:(int)height {
// create context, keeping original image properties
CGColorSpaceRef colorspace = CGImageGetColorSpace(image);
CGContextRef context = CGBitmapContextCreate(NULL, width, height,
CGImageGetBitsPerComponent(image),
CGImageGetBytesPerRow(image),
colorspace,
CGImageGetAlphaInfo(image));
CGColorSpaceRelease(colorspace);
if(context == NULL)
return nil;
// draw image to context (resizing it)
CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
// extract resulting image from context
CGImageRef imgRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
return imgRef;
}
@end