forked from mirrors/linphone-iphone
Merge branch 'dev_alasset_to_photokit' into dev_group_chat
This commit is contained in:
commit
03a4c7174a
19 changed files with 471 additions and 389 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
|
@ -10,6 +10,18 @@ Group changes to describe their impact on the project, as follows:
|
|||
Fixed for any bug fixes.
|
||||
Security to invite users to upgrade in case of vulnerabilities.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Auto-layout of images in chat messages
|
||||
|
||||
### Changed
|
||||
- Use of Photokit instead of Asset Library for image handling
|
||||
|
||||
### Fixed
|
||||
|
||||
### Removed
|
||||
|
||||
## [4.0] - 2018-06-11
|
||||
|
||||
### Added
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
@ -98,16 +104,16 @@
|
|||
}
|
||||
[self.tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:index inSection:0]]
|
||||
withRowAnimation:FALSE]; // just reload
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#import <Photos/PHAssetChangeRequest.h>
|
||||
|
||||
#import "ChatConversationView.h"
|
||||
#import "PhoneMainView.h"
|
||||
#import "Utils.h"
|
||||
|
|
@ -220,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
|
||||
|
|
@ -308,38 +310,11 @@ static UICompositeViewDescription *compositeDescription = nil;
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
- (void)saveAndSend:(UIImage *)image url:(NSURL *)url withQuality:(float)quality{
|
||||
// photo from Camera, must be saved first
|
||||
if (url == nil) {
|
||||
[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]);
|
||||
|
||||
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 {
|
||||
LOGI(@"Image saved to [%@]", [assetURL absoluteString]);
|
||||
[self startImageUpload:image url:assetURL withQuality:quality];
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
[self startImageUpload:image url:url withQuality:quality];
|
||||
}
|
||||
- (void)saveAndSend:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)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]) {
|
||||
|
|
@ -349,7 +324,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];
|
||||
|
|
@ -601,9 +576,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;
|
||||
}
|
||||
|
|
@ -621,7 +596,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) {
|
||||
|
|
@ -635,9 +610,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 {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
|
||||
@protocol ImagePickerDelegate <NSObject>
|
||||
|
||||
- (void)imagePickerDelegateImage:(UIImage *)image info:(NSDictionary *)info;
|
||||
- (void)imagePickerDelegateImage:(UIImage *)image info:(NSString *)phAssetId;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@
|
|||
#import <MobileCoreServices/UTCoreTypes.h>
|
||||
#import <AVFoundation/AVCaptureDevice.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import <Photos/Photos.h>
|
||||
#import "ImagePickerView.h"
|
||||
#import "PhoneMainView.h"
|
||||
|
||||
|
|
@ -162,15 +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 && imagePickerDelegate != nil) {
|
||||
[imagePickerDelegate imagePickerDelegateImage:image info: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) 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];
|
||||
}
|
||||
|
|
@ -223,35 +245,57 @@ 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];
|
||||
if ([PHPhotoLibrary authorizationStatus] == 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 {
|
||||
[PHPhotoLibrary requestAuthorization:^(PHAuthorizationStatus status) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if ([PHPhotoLibrary authorizationStatus] == 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];
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <SystemConfiguration/SCNetworkReachability.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AssetsLibrary/ALAssetsLibrary.h>
|
||||
#import <Photos/Photos.h>
|
||||
#import <CoreTelephony/CTCallCenter.h>
|
||||
|
||||
#import <sqlite3.h>
|
||||
|
|
@ -208,6 +208,8 @@ typedef struct _LinphoneManagerSounds {
|
|||
|
||||
- (void)checkNewVersion;
|
||||
|
||||
- (void)loadAvatar;
|
||||
|
||||
@property ProviderDelegate *providerDelegate;
|
||||
|
||||
@property (readonly) BOOL isTesting;
|
||||
|
|
@ -225,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);
|
||||
|
|
@ -238,5 +239,6 @@ typedef struct _LinphoneManagerSounds {
|
|||
@property NSDictionary *pushDict;
|
||||
@property(strong, nonatomic) OrderedDictionary *linphoneManagerAddressBookMap;
|
||||
@property (nonatomic, assign) BOOL contactsUpdated;
|
||||
@property UIImage *avatar;
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -263,7 +263,6 @@ struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_pre
|
|||
_fileTransferDelegates = [[NSMutableArray alloc] init];
|
||||
_linphoneManagerAddressBookMap = [[OrderedDictionary alloc] init];
|
||||
pushCallIDs = [[NSMutableArray alloc] init];
|
||||
_photoLibrary = [[ALAssetsLibrary alloc] init];
|
||||
_isTesting = [LinphoneManager isRunningTests];
|
||||
[self renameDefaultSettings];
|
||||
[self copyDefaultSettings];
|
||||
|
|
@ -288,6 +287,7 @@ struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_pre
|
|||
}
|
||||
|
||||
[self migrateFromUserPrefs];
|
||||
[self loadAvatar];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
|
@ -2887,20 +2887,19 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
|
|||
}
|
||||
|
||||
+ (void)setValueInMessageAppData:(id)value forKey:(NSString *)key inMessage:(LinphoneChatMessage *)msg {
|
||||
NSMutableDictionary *appDataDict = [NSMutableDictionary dictionary];
|
||||
const char *appData = linphone_chat_message_get_appdata(msg);
|
||||
if (appData) {
|
||||
appDataDict = [NSJSONSerialization JSONObjectWithData:[NSData dataWithBytes:appData length:strlen(appData)]
|
||||
options:NSJSONReadingMutableContainers
|
||||
error:nil];
|
||||
}
|
||||
|
||||
NSMutableDictionary *appDataDict = [NSMutableDictionary dictionary];
|
||||
const char *appData = linphone_chat_message_get_appdata(msg);
|
||||
if (appData) {
|
||||
appDataDict = [NSJSONSerialization JSONObjectWithData:[NSData dataWithBytes:appData length:strlen(appData)]
|
||||
options:NSJSONReadingMutableContainers
|
||||
error:nil];
|
||||
}
|
||||
[appDataDict setValue:value forKey:key];
|
||||
|
||||
[appDataDict setValue:value forKey:key];
|
||||
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:appDataDict options:0 error:nil];
|
||||
NSString *appdataJSON = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
linphone_chat_message_set_appdata(msg, [appdataJSON UTF8String]);
|
||||
NSData *data = [NSJSONSerialization dataWithJSONObject:appDataDict options:0 error:nil];
|
||||
NSString *appdataJSON = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
||||
linphone_chat_message_set_appdata(msg, [appdataJSON UTF8String]);
|
||||
}
|
||||
|
||||
#pragma mark - LPConfig Functions
|
||||
|
|
@ -3077,4 +3076,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
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
|
|
@ -21,6 +21,7 @@
|
|||
<outlet property="downloadButton" destination="N75-gL-R6t" id="EgN-Ab-Ded"/>
|
||||
<outlet property="fileName" destination="Dho-UV-6Ev" id="Iro-II-w8a"/>
|
||||
<outlet property="fileTransferProgress" destination="USm-wC-GvG" id="POt-YD-NCG"/>
|
||||
<outlet property="finalAssetView" destination="VYJ-RC-Jmg" id="fnb-mY-nPR"/>
|
||||
<outlet property="finalImage" destination="gzv-K4-5OL" id="YIw-kM-Ld6"/>
|
||||
<outlet property="imageGestureRecognizer" destination="aDF-hC-ddO" id="2jh-Rr-eKk"/>
|
||||
<outlet property="imageSubView" destination="GmN-7v-uuO" id="k9r-Xc-csv"/>
|
||||
|
|
@ -35,19 +36,19 @@
|
|||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" misplaced="YES" id="UGz-WT-BUv">
|
||||
<view contentMode="scaleToFill" id="UGz-WT-BUv">
|
||||
<rect key="frame" x="0.0" y="0.0" width="377" height="301"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" id="Y7i-Gm-AdY" userLabel="innerView">
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Y7i-Gm-AdY" userLabel="innerView">
|
||||
<rect key="frame" x="6" y="5" width="365" height="291"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView userInteractionEnabled="NO" alpha="0.20000000298023224" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_A.png" id="U2P-5n-gg8" userLabel="backgroundColorImage">
|
||||
<imageView userInteractionEnabled="NO" alpha="0.20000000298023224" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="color_A.png" translatesAutoresizingMaskIntoConstraints="NO" id="U2P-5n-gg8" userLabel="backgroundColorImage">
|
||||
<rect key="frame" x="0.0" y="0.0" width="365" height="291"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
</imageView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" image="avatar.png" id="hD2-19-7IH" userLabel="avatarImage" customClass="UIRoundedImageView">
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" fixedFrame="YES" image="avatar.png" translatesAutoresizingMaskIntoConstraints="NO" id="hD2-19-7IH" userLabel="avatarImage" customClass="UIRoundedImageView">
|
||||
<rect key="frame" x="7" y="7" width="40" height="40"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Contact avatar">
|
||||
|
|
@ -55,7 +56,7 @@
|
|||
<bool key="isElement" value="YES"/>
|
||||
</accessibility>
|
||||
</imageView>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" text="11:35 John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="JyR-RQ-uwF" userLabel="contactDateLabel">
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" fixedFrame="YES" text="11:35 John Doe" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JyR-RQ-uwF" userLabel="contactDateLabel">
|
||||
<rect key="frame" x="55" y="8" width="286" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Contact name"/>
|
||||
|
|
@ -63,16 +64,16 @@
|
|||
<color key="textColor" red="0.98766469955444336" green="0.27512490749359131" blue="0.029739789664745331" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" misplaced="YES" id="8I3-n2-0kS" userLabel="view">
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8I3-n2-0kS" userLabel="view">
|
||||
<rect key="frame" x="39" y="55" width="298" height="190"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="linphone_logo.png" id="yMW-cT-bpU" userLabel="image" customClass="UILoadingImageView">
|
||||
<imageView contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="linphone_logo.png" translatesAutoresizingMaskIntoConstraints="NO" id="yMW-cT-bpU" userLabel="image" customClass="UILoadingImageView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="298" height="122"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<gestureRecognizers/>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Label" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="Dho-UV-6Ev" userLabel="fileName">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Label" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Dho-UV-6Ev" userLabel="fileName">
|
||||
<rect key="frame" x="0.0" y="0.0" width="200" height="50"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<color key="backgroundColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
|
|
@ -80,15 +81,15 @@
|
|||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" id="GmN-7v-uuO" userLabel="imageSubView">
|
||||
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GmN-7v-uuO" userLabel="imageSubView">
|
||||
<rect key="frame" x="0.0" y="128" width="297" height="62"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" progress="0.5" id="USm-wC-GvG" userLabel="transferProgress">
|
||||
<progressView opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="750" fixedFrame="YES" progress="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="USm-wC-GvG" userLabel="transferProgress">
|
||||
<rect key="frame" x="10" y="29" width="277" height="2"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
</progressView>
|
||||
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="N75-gL-R6t" userLabel="downloadButton" customClass="UIRoundBorderedButton">
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="N75-gL-R6t" userLabel="downloadButton" customClass="UIRoundBorderedButton">
|
||||
<rect key="frame" x="84" y="33" width="115" height="27"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Download"/>
|
||||
|
|
@ -100,7 +101,7 @@
|
|||
<action selector="onDownloadClick:" destination="-1" eventType="touchUpInside" id="8BO-9E-iOX"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="6dl-Nz-rdv" userLabel="cancelButton" customClass="UIRoundBorderedButton">
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6dl-Nz-rdv" userLabel="cancelButton" customClass="UIRoundBorderedButton">
|
||||
<rect key="frame" x="84" y="33" width="115" height="27"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Cancel"/>
|
||||
|
|
@ -117,16 +118,16 @@
|
|||
</view>
|
||||
</subviews>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" misplaced="YES" id="VYJ-RC-Jmg">
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" preservesSuperviewLayoutMargins="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VYJ-RC-Jmg" userLabel="finalAssetView">
|
||||
<rect key="frame" x="8" y="55" width="349" height="213"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" id="gzv-K4-5OL" userLabel="finalImage">
|
||||
<imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="gzv-K4-5OL" userLabel="finalImage">
|
||||
<rect key="frame" x="0.0" y="0.0" width="349" height="213"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<gestureRecognizers/>
|
||||
</imageView>
|
||||
<button opaque="NO" contentMode="scaleToFill" misplaced="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" id="cvc-tl-Pcf" userLabel="playButton" customClass="UIRoundBorderedButton">
|
||||
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="cvc-tl-Pcf" userLabel="playButton" customClass="UIRoundBorderedButton">
|
||||
<rect key="frame" x="125" y="93" width="50" height="25"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Cancel"/>
|
||||
|
|
@ -145,27 +146,27 @@
|
|||
<outletCollection property="gestureRecognizers" destination="NYA-II-xYn" appends="YES" id="fK6-ld-zOX"/>
|
||||
</connections>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_A.png" id="6dA-3U-OPW" userLabel="bottomBarColor">
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="color_A.png" translatesAutoresizingMaskIntoConstraints="NO" id="6dA-3U-OPW" userLabel="bottomBarColor">
|
||||
<rect key="frame" x="0.0" y="290" width="365" height="1"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
||||
</imageView>
|
||||
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="gray" id="Eab-ND-ix3" userLabel="statusInprogressSpinner">
|
||||
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" fixedFrame="YES" hidesWhenStopped="YES" animating="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="Eab-ND-ix3" userLabel="statusInprogressSpinner">
|
||||
<rect key="frame" x="345" y="0.0" width="20" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
</activityIndicatorView>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" image="chat_unsecure.png" id="IST-5o-DCu" userLabel="LIMEKO">
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="chat_unsecure.png" translatesAutoresizingMaskIntoConstraints="NO" id="IST-5o-DCu" userLabel="LIMEKO">
|
||||
<rect key="frame" x="351" y="6" width="8" height="12"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Delivery failed"/>
|
||||
</imageView>
|
||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" text="Delivered" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="44j-me-Iqi" userLabel="imdmLabel">
|
||||
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="Delivered" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="44j-me-Iqi" userLabel="imdmLabel">
|
||||
<rect key="frame" x="283" y="274" width="64" height="20"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="11"/>
|
||||
<color key="textColor" red="1" green="0.36862745099999999" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" image="valid_default.png" id="LPj-VT-0fH" userLabel="imdmIcon">
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" image="valid_default.png" translatesAutoresizingMaskIntoConstraints="NO" id="LPj-VT-0fH" userLabel="imdmIcon">
|
||||
<rect key="frame" x="349" y="276" width="13" height="13"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Delivery failed"/>
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@
|
|||
</subviews>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" image="chat_read.png" id="aa2-Kl-c1H" userLabel="imdmIcon">
|
||||
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" image="chat_read.png" id="aa2-Kl-c1H" userLabel="imdmIcon">
|
||||
<rect key="frame" x="59" y="39" width="13" height="13"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Delivery failed">
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
@property(weak, nonatomic) IBOutlet UIView *imageSubView;
|
||||
@property (strong, nonatomic) IBOutlet UITapGestureRecognizer *openRecognizer;
|
||||
@property(weak, nonatomic) IBOutlet UIView *totalView;
|
||||
@property (weak, nonatomic) IBOutlet UIView *finalAssetView;
|
||||
@property (weak, nonatomic) IBOutlet UIImageView *finalImage;
|
||||
@property(strong, nonatomic) IBOutlet UITapGestureRecognizer *imageGestureRecognizer;
|
||||
|
||||
|
|
|
|||
|
|
@ -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,15 +50,18 @@
|
|||
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;
|
||||
}
|
||||
|
||||
- (void)onDelete {
|
||||
[super onDelete];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
- (void)setEvent:(LinphoneEventLog *)event {
|
||||
if (!event || !(linphone_event_log_get_type(event) == LinphoneEventLogTypeConferenceChatMessage))
|
||||
|
|
@ -76,6 +78,7 @@
|
|||
_finalImage.image = nil;
|
||||
_finalImage.hidden = TRUE;
|
||||
_fileTransferProgress.progress = 0;
|
||||
assetIsLoaded = FALSE;
|
||||
[self disconnectFromFileDelegate];
|
||||
|
||||
if (amessage) {
|
||||
|
|
@ -98,25 +101,33 @@
|
|||
[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;
|
||||
_finalImage.hidden = NO;
|
||||
[self layoutSubviews];
|
||||
});
|
||||
}
|
||||
|
||||
- (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 {
|
||||
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 +150,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;
|
||||
|
|
@ -183,40 +194,21 @@
|
|||
if ([localImage isEqualToString:@"saving..."] || [localVideo isEqualToString:@"saving..."] || [localFile isEqualToString:@"saving..."]) {
|
||||
_cancelButton.hidden = _fileTransferProgress.hidden = _downloadButton.hidden = _playButton.hidden = _fileName.hidden = YES;
|
||||
fullScreenImage = YES;
|
||||
} else {
|
||||
} else if(!assetIsLoaded) {
|
||||
assetIsLoaded = TRUE;
|
||||
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) {
|
||||
|
|
@ -246,16 +238,11 @@
|
|||
fullScreenImage = YES;
|
||||
_playButton.hidden = localVideo ? NO : YES;
|
||||
_fileName.hidden = localFile ? NO : YES;
|
||||
// Should fix cell not resizing after doanloading image.
|
||||
[self layoutSubviews];
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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 +323,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");
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -366,7 +362,8 @@
|
|||
}
|
||||
|
||||
- (void)disconnectFromFileDelegate {
|
||||
[NSNotificationCenter.defaultCenter removeObserver:self];
|
||||
[NSNotificationCenter.defaultCenter removeObserver:self name:kLinphoneFileTransferSendUpdate object:_ftd];
|
||||
[NSNotificationCenter.defaultCenter removeObserver:self name:kLinphoneFileTransferRecvUpdate object:_ftd];
|
||||
_ftd = nil;
|
||||
}
|
||||
|
||||
|
|
@ -399,32 +396,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;
|
||||
UITableView *tableView = VIEW(ChatConversationView).tableController.tableView;
|
||||
//[super layoutSubviews];
|
||||
BOOL is_outgoing = linphone_chat_message_is_outgoing(super.message);
|
||||
CGRect bubbleFrame = super.bubbleView.frame;
|
||||
int origin_x;
|
||||
|
|
@ -433,7 +406,7 @@
|
|||
|
||||
bubbleFrame.size = bubbleSize;
|
||||
|
||||
if (tableView.isEditing) {
|
||||
if (chatTableView.tableView.isEditing) {
|
||||
origin_x = 0;
|
||||
} else {
|
||||
origin_x = (is_outgoing ? self.frame.size.width - bubbleFrame.size.width : 0);
|
||||
|
|
@ -444,25 +417,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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -235,26 +235,40 @@
|
|||
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");
|
||||
}];
|
||||
ChatConversationTableView *tableView = VIEW(ChatConversationView).tableController;
|
||||
UIImage *img = [tableView.imagesInChatroom objectForKey:localImage];
|
||||
if (img) {
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, (unsigned long)NULL),
|
||||
^(void) {
|
||||
[_chatRoomDelegate startImageUpload:img assetId:localImage withQuality:(uploadQuality ? [uploadQuality floatValue] : 0.9)];
|
||||
});
|
||||
} else {
|
||||
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:img 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]];
|
||||
}
|
||||
} else {
|
||||
[self onDelete];
|
||||
[self onDelete];
|
||||
double delayInSeconds = 0.4;
|
||||
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
|
||||
dispatch_after(popTime, dispatch_get_main_queue(), ^(void) {
|
||||
|
|
@ -346,25 +360,19 @@ 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);
|
||||
|
||||
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;
|
||||
if (!localImage) {
|
||||
//We are loading the image
|
||||
return CGSizeMake(CELL_MIN_WIDTH + CELL_MESSAGE_X_MARGIN, CELL_MIN_HEIGHT + CELL_MESSAGE_Y_MARGIN);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
@ -113,6 +113,10 @@
|
|||
image = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
}
|
||||
|
||||
[LinphoneManager.instance lpConfigSetString:phAssetId forKey:@"avatar"];
|
||||
_avatarImage.image = [LinphoneUtils selfAvatar];
|
||||
[LinphoneManager.instance loadAvatar];
|
||||
|
||||
// Dismiss popover on iPad
|
||||
if (IPAD) {
|
||||
|
|
@ -120,27 +124,6 @@
|
|||
} else {
|
||||
[PhoneMainView.instance.mainViewController hideSideMenu:NO];
|
||||
}
|
||||
|
||||
NSURL *url = [info valueForKey:UIImagePickerControllerReferenceURL];
|
||||
|
||||
// taken from camera, must be saved to device first
|
||||
if (!url) {
|
||||
[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]);
|
||||
} else {
|
||||
LOGI(@"Image saved to [%@]", [assetURL absoluteString]);
|
||||
}
|
||||
[LinphoneManager.instance lpConfigSetString:assetURL.absoluteString forKey:@"avatar"];
|
||||
_avatarImage.image = [LinphoneUtils selfAvatar];
|
||||
}];
|
||||
} else {
|
||||
[LinphoneManager.instance lpConfigSetString:url.absoluteString forKey:@"avatar"];
|
||||
_avatarImage.image = [LinphoneUtils selfAvatar];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -75,44 +75,47 @@ 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) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
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 [%@]", [placeHolder localIdentifier]);
|
||||
[LinphoneManager setValueInMessageAppData:[placeHolder localIdentifier]
|
||||
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);
|
||||
});
|
||||
}];
|
||||
} else {
|
||||
[[LinphoneManager.instance fileTransferDelegates] removeObject:thiz];
|
||||
|
||||
|
|
@ -224,10 +227,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];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -463,47 +437,42 @@
|
|||
}
|
||||
|
||||
+ (LinphoneAddress *)normalizeSipOrPhoneAddress:(NSString *)value {
|
||||
if (!value || [value isEqualToString:@""]) {
|
||||
return NULL;
|
||||
}
|
||||
LinphoneProxyConfig *cfg = linphone_core_get_default_proxy_config(LC);
|
||||
const char *normvalue;
|
||||
if (linphone_proxy_config_is_phone_number(cfg, value.UTF8String)) {
|
||||
normvalue = linphone_proxy_config_normalize_phone_number(cfg, value.UTF8String);
|
||||
} else {
|
||||
normvalue = value.UTF8String;
|
||||
}
|
||||
LinphoneAddress *addr = linphone_proxy_config_normalize_sip_uri(cfg, normvalue);
|
||||
// first try to find a friend with the given address
|
||||
Contact *c = [FastAddressBook getContactWithAddress:addr];
|
||||
|
||||
if (c && c.friend) {
|
||||
LinphoneFriend *f = c.friend;
|
||||
const LinphonePresenceModel *m = f ? linphone_friend_get_presence_model_for_uri_or_tel(f, value.UTF8String) : NULL;
|
||||
const char *contact = m ? linphone_presence_model_get_contact(m) : NULL;
|
||||
if (contact) {
|
||||
LinphoneAddress *contact_addr = linphone_address_new(contact);
|
||||
if (contact_addr) {
|
||||
linphone_address_destroy(addr);
|
||||
return contact_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// since user wants to escape plus, we assume it expects to have phone
|
||||
// numbers by default
|
||||
if (addr) {
|
||||
if (cfg || (linphone_proxy_config_get_dial_escape_plus(cfg))) {
|
||||
if (linphone_proxy_config_is_phone_number(cfg, normvalue)) {
|
||||
linphone_address_set_username(addr, normvalue);
|
||||
}
|
||||
} else {
|
||||
if (linphone_proxy_config_is_phone_number(cfg, value.UTF8String)) {
|
||||
linphone_address_set_username(addr, value.UTF8String);
|
||||
}
|
||||
}
|
||||
}
|
||||
return addr;
|
||||
if (!value || [value isEqualToString:@""])
|
||||
return NULL;
|
||||
|
||||
LinphoneProxyConfig *cfg = linphone_core_get_default_proxy_config(LC);
|
||||
const char *normvalue;
|
||||
normvalue = linphone_proxy_config_is_phone_number(cfg, value.UTF8String)
|
||||
? linphone_proxy_config_normalize_phone_number(cfg, value.UTF8String)
|
||||
: value.UTF8String;
|
||||
|
||||
LinphoneAddress *addr = linphone_proxy_config_normalize_sip_uri(cfg, normvalue);
|
||||
// first try to find a friend with the given address
|
||||
Contact *c = [FastAddressBook getContactWithAddress:addr];
|
||||
|
||||
if (c && c.friend) {
|
||||
LinphoneFriend *f = c.friend;
|
||||
const LinphonePresenceModel *m = f
|
||||
? linphone_friend_get_presence_model_for_uri_or_tel(f, value.UTF8String)
|
||||
: NULL;
|
||||
const char *contact = m ? linphone_presence_model_get_contact(m) : NULL;
|
||||
if (contact) {
|
||||
LinphoneAddress *contact_addr = linphone_address_new(contact);
|
||||
if (contact_addr) {
|
||||
linphone_address_destroy(addr);
|
||||
return contact_addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// since user wants to escape plus, we assume it expects to have phone
|
||||
// numbers by default
|
||||
if (addr && cfg) {
|
||||
const char *username = linphone_proxy_config_get_dial_escape_plus(cfg) ? normvalue : value.UTF8String;
|
||||
if (linphone_proxy_config_is_phone_number(cfg, username))
|
||||
linphone_address_set_username(addr, username);
|
||||
}
|
||||
return addr;
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -782,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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue