Merge branch 'feature/sms_invitation'

This commit is contained in:
Danmei Chen 2018-10-05 17:27:22 +02:00
commit e704c7317a
37 changed files with 1898 additions and 549 deletions

View file

@ -13,8 +13,17 @@ Group changes to describe their impact on the project, as follows:
## [Unreleased]
### Added
- Auto-layout of images in chat messages
- Selection of multiple images to send in a chat message
- Sending text with image
- Latest Calls widget
- Latest Chatrooms widget
- Homescreen quick action : New message
- Rich message notifications with Linphone UI
- Support of H265 video format based on Apple's VideoToolbox framework.
### Changed
- Use of Photokit instead of Asset Library for image handling
### Fixed
- Fix Bluetooth management

View file

@ -18,6 +18,8 @@
<outlet property="chatView" destination="49" id="Rxt-Zy-19x"/>
<outlet property="composeIndicatorView" destination="fx4-ao-53M" id="xk5-nK-lur"/>
<outlet property="composeLabel" destination="fpY-Fv-ht2" id="4L6-ik-ZAe"/>
<outlet property="imagesCollectionView" destination="JGQ-p2-HCX" id="6dt-1f-jpa"/>
<outlet property="imagesView" destination="3qd-ys-t2L" id="f9L-FU-PMI"/>
<outlet property="infoButton" destination="Vqb-Un-4xv" id="pa1-Iz-5QQ"/>
<outlet property="landscapeView" destination="VoU-7Q-fgp" id="iRJ-sh-thF"/>
<outlet property="listSwipeGestureRecognizer" destination="dzw-n4-l9i" id="JVP-Vl-lIa"/>
@ -269,6 +271,28 @@
</view>
</subviews>
</view>
<view clipsSubviews="YES" contentMode="scaleToFill" misplaced="YES" id="3qd-ys-t2L" userLabel="imagesView">
<rect key="frame" x="0.0" y="427" width="375" height="0.0"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" image="color_F.png" id="B6X-D7-Bak" userLabel="backgroundColor">
<rect key="frame" x="2" y="1" width="375" height="0.0"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<collectionView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" misplaced="YES" showsVerticalScrollIndicator="NO" dataMode="none" id="JGQ-p2-HCX" userLabel="imagesCollectionView">
<rect key="frame" x="2" y="0.0" width="374" height="0.0"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" red="0.98780487804878048" green="1" blue="1" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<collectionViewFlowLayout key="collectionViewLayout" scrollDirection="horizontal" minimumLineSpacing="10" minimumInteritemSpacing="10" id="c7z-F2-r1y">
<size key="itemSize" width="50" height="100"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
</collectionView>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</view>
</subviews>
</view>
</subviews>

View file

@ -27,8 +27,7 @@
@protocol ChatConversationDelegate <NSObject>
- (BOOL)startImageUpload:(UIImage *)image url:(NSURL *)url withQuality:(float)quality;
- (BOOL)startFileUpload:(NSData *)data withUrl:(NSURL *)url;
- (BOOL)startImageUpload:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)quality;
- (void)resendChat:(NSString *)message withExternalUrl:(NSString *)url;
- (void)tableViewIsScrolling;
@ -41,6 +40,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];
@ -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];

View file

@ -27,12 +27,13 @@
#import "UIRoundedImageView.h"
#import "UIBackToCallButton.h"
#import "Utils/HPGrowingTextView/HPGrowingTextView.h"
#import "UIImageViewDeletable.h"
#include "linphone/linphonecore.h"
@interface ChatConversationView
: TPMultiLayoutViewController <HPGrowingTextViewDelegate, UICompositeViewDelegate, ImagePickerDelegate, ChatConversationDelegate,
UIDocumentInteractionControllerDelegate, UISearchBarDelegate> {
: TPMultiLayoutViewController <HPGrowingTextViewDelegate, UICompositeViewDelegate, ImagePickerDelegate, ChatConversationDelegate, UIDocumentPickerDelegate,
UIDocumentInteractionControllerDelegate, UISearchBarDelegate, UIImageViewDeletableDelegate, UICollectionViewDataSource> {
OrderedDictionary *imageQualities;
BOOL scrollOnGrowingEnabled;
BOOL composingVisible;
@ -60,6 +61,13 @@
@property (weak, nonatomic) IBOutlet UIIconButton *infoButton;
@property (weak, nonatomic) IBOutlet UILabel *particpantsLabel;
@property (nonatomic, strong) UIDocumentInteractionController *documentInteractionController;
@property (nonatomic, strong) UIDocumentPickerViewController *documentPicker;
@property NSMutableArray <UIImage *> *imagesArray;
@property NSMutableArray <NSString *> *assetIdsArray;
@property NSMutableArray <NSNumber *> *qualitySettingsArray;
@property (weak, nonatomic) IBOutlet UICollectionView *imagesCollectionView;
@property (weak, nonatomic) IBOutlet UIView *imagesView;
+ (void)markAsRead:(LinphoneChatRoom *)chatRoom;
- (void)configureForRoom:(BOOL)editing;
@ -73,6 +81,8 @@
- (IBAction)onDeleteClick:(id)sender;
- (IBAction)onEditionChangeClick:(id)sender;
- (void)update;
- (void)openResults:(NSString *) filePath;
- (void)getIcloudFiles;
- (void)openFile:(NSString *) filePath;
- (void)clearMessageView;
@end

View file

@ -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"
@ -99,6 +101,8 @@ static UICompositeViewDescription *compositeDescription = nil;
_messageField.contentInset = UIEdgeInsetsMake(-15, 0, 0, 0);
// _messageField.internalTextView.scrollIndicatorInsets = UIEdgeInsetsMake(0, 0, 0, 10);
[_tableController setChatRoomDelegate:self];
[_imagesCollectionView registerClass:[UIImageViewDeletable class] forCellWithReuseIdentifier:NSStringFromClass([UIImageViewDeletable class])];
[_imagesCollectionView setDataSource:self];
}
- (void)viewWillAppear:(BOOL)animated {
@ -123,6 +127,24 @@ static UICompositeViewDescription *compositeDescription = nil;
selector:@selector(callUpdateEvent:)
name:kLinphoneCallUpdate
object:nil];
if ([_imagesArray count] > 0) {
[UIView animateWithDuration:0
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
// resizing imagesView
CGRect imagesFrame = [_imagesView frame];
imagesFrame.origin.y = [_messageView frame].origin.y - 100;
imagesFrame.size.height = 100;
[_imagesView setFrame:imagesFrame];
// resizing chatTable
CGRect tableViewFrame = [_tableController.tableView frame];
tableViewFrame.size.height -= 100;
[_tableController.tableView setFrame:tableViewFrame];
}
completion:nil];
}
}
- (void)viewWillDisappear:(BOOL)animated {
@ -151,6 +173,7 @@ static UICompositeViewDescription *compositeDescription = nil;
[self configureForRoom:true];
_backButton.hidden = _tableController.isEditing;
[_tableController scrollToBottom:true];
[self refreshImageDrawer];
}
#pragma mark -
@ -201,7 +224,6 @@ static UICompositeViewDescription *compositeDescription = nil;
_messageField.editable = !linphone_chat_room_has_been_left(_chatRoom);
_pictureButton.enabled = !linphone_chat_room_has_been_left(_chatRoom);
_messageView.userInteractionEnabled = !linphone_chat_room_has_been_left(_chatRoom);
[_messageField setText:@""];
[_tableController setChatRoom:_chatRoom];
_chatView.hidden = NO;
@ -211,38 +233,34 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)shareFile {
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:groupName];
NSDictionary *dict = [defaults valueForKey:@"img"];
NSDictionary *dictWeb = [defaults valueForKey:@"web"];
NSDictionary *dictFile = [defaults valueForKey:@"mov"];
NSDictionary *dictText = [defaults valueForKey:@"text"];
NSDictionary *dict = [defaults valueForKey:@"photoData"];
NSDictionary *dictFile = [defaults valueForKey:@"icloudData"];
NSDictionary *dictUrl = [defaults valueForKey:@"url"];
if (dict) {
//share photo
NSData *data = dict[@"nsData"];
UIImage *image = [[UIImage alloc] initWithData:data];
[self chooseImageQuality:image url:nil];
[defaults removeObjectForKey:@"img"];
} else if (dictWeb) {
//share url, if local file, then upload file
NSString *url = dictWeb[@"url"];
NSURL *fileUrl = [NSURL fileURLWithPath:url];
if ([url hasPrefix:@"file"]) {
//local file
NSData *data = dictWeb[@"nsData"];
[self confirmShare:data url:fileUrl text:nil];
//file shared from photo lib
NSString *fileName = dict[@"url"];
NSString *key = [[fileName componentsSeparatedByString:@"."] firstObject];
NSMutableDictionary <NSString *, PHAsset *> * assetDict = [LinphoneUtils photoAssetsDictionary];
if ([fileName hasSuffix:@"JPG"] || [fileName hasSuffix:@"PNG"]) {
UIImage *image = [[UIImage alloc] initWithData:dict[@"nsData"]];
[self chooseImageQuality:image assetId:[[assetDict objectForKey:key] localIdentifier]];
} else if ([fileName hasSuffix:@"MOV"]) {
[self confirmShare:dict[@"nsData"] url:nil fileName:nil assetId:[[assetDict objectForKey:key] localIdentifier]];
} else {
[self confirmShare:nil url:nil text:url];
LOGE(@"Unable to parse file %@",fileName);
}
[defaults removeObjectForKey:@"web"];
}else if (dictFile) {
//share file
NSData *data = dictFile[@"nsData"];
[self confirmShare:data url:[NSURL fileURLWithPath:dictFile[@"url"]] text:nil];
[defaults removeObjectForKey:@"mov"];
}else if (dictText) {
//share text
[self confirmShare:nil url:nil text:dictText[@"name"]];
[defaults removeObjectForKey:@"text"];
[defaults removeObjectForKey:@"photoData"];
} else if (dictFile) {
NSString *fileName = dictFile[@"url"];
[self confirmShare:dictFile[@"nsData"] url:nil fileName:fileName assetId:nil];
[defaults removeObjectForKey:@"icloudData"];
} else if (dictUrl) {
NSString *url = dictUrl[@"url"];
[self confirmShare:nil url:url fileName:nil assetId:nil];
[defaults removeObjectForKey:@"url"];
}
}
@ -308,38 +326,15 @@ 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{
[_imagesArray addObject:image];
[_assetIdsArray addObject:phAssetId];
[_qualitySettingsArray addObject:@(quality)];
[self refreshImageDrawer];
}
- (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 +344,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];
@ -359,17 +354,17 @@ static UICompositeViewDescription *compositeDescription = nil;
});
}
- (void)confirmShare:(NSData *)data url:(NSURL *)url text:(NSString *)text {
- (void)confirmShare:(NSData *)data url:(NSString *)url fileName:(NSString *)fileName assetId:(NSString *)phAssetId {
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"", nil)];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[sheet addButtonWithTitle:@"send to this friend"
block:^() {
if(data && url)
[self startFileUpload:data withUrl:url];
if (url)
[self sendMessage:url withExterlBodyUrl:nil withInternalURL:nil];
else if (fileName)
[self startFileUpload:data withName:fileName];
else
[self sendMessage:text withExterlBodyUrl:nil withInternalURL:nil];
[self startFileUpload:data assetId:phAssetId];
}];
[sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel", nil) block:nil];
@ -481,6 +476,12 @@ static UICompositeViewDescription *compositeDescription = nil;
messageRect.size.height += diff;
[_messageView setFrame:messageRect];
if ([_imagesArray count] > 0) {
CGRect _imagesRect = [_imagesView frame];
_imagesRect.origin.y -= diff;
[_imagesView setFrame:_imagesRect];
}
// Always stay at bottom
if (scrollOnGrowingEnabled) {
CGRect tableFrame = [_tableController.view frame];
@ -518,6 +519,15 @@ static UICompositeViewDescription *compositeDescription = nil;
}
- (IBAction)onSendClick:(id)event {
if ([_imagesArray count] > 0) {
int i = 0;
for (i = 0; i < [_imagesArray count] - 1; ++i) {
[self startImageUpload:[_imagesArray objectAtIndex:i] assetId:[_assetIdsArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue];
}
[self startImageUpload:[_imagesArray objectAtIndex:i] assetId:[_assetIdsArray objectAtIndex:i] withQuality:[_qualitySettingsArray objectAtIndex:i].floatValue andMessage:[self.messageField text]];
[self clearMessageView];
return;
}
if ([self sendMessage:[_messageField text] withExterlBodyUrl:nil withInternalURL:nil]) {
scrollOnGrowingEnabled = FALSE;
[_messageField setText:@""];
@ -601,16 +611,31 @@ 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;
}
- (BOOL)startFileUpload:(NSData *)data withUrl:(NSURL *)url {
- (BOOL)startImageUpload:(UIImage *)image assetId:(NSString *)phAssetId withQuality:(float)quality andMessage:(NSString *)message {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
[fileTransfer uploadFile:data forChatRoom:_chatRoom withUrl:url];
[fileTransfer setText:message];
[fileTransfer upload:image withassetId:phAssetId forChatRoom:_chatRoom withQuality:quality];
[_tableController scrollToBottom:true];
return TRUE;
}
- (BOOL)startFileUpload:(NSData *)data assetId:phAssetId {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
[fileTransfer uploadVideo:data withassetId:phAssetId forChatRoom:_chatRoom];
[_tableController scrollToBottom:true];
return TRUE;
}
- (BOOL)startFileUpload:(NSData *)data withName:(NSString *)name {
FileTransferDelegate *fileTransfer = [[FileTransferDelegate alloc] init];
[fileTransfer uploadFile:data forChatRoom:_chatRoom withName:name];
[_tableController scrollToBottom:true];
return TRUE;
}
@ -621,7 +646,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 +660,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 {
@ -651,6 +674,9 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)keyboardWillHide:(NSNotification *)notif {
NSTimeInterval duration = [[[notif userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
int heightDiff = UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) ? 55 : 105;
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
@ -694,6 +720,19 @@ static UICompositeViewDescription *compositeDescription = nil;
}
}
}
if ([_imagesArray count] > 0){
// resizing imagesView
CGRect imagesFrame = [_imagesView frame];
imagesFrame.origin.y = [_messageView frame].origin.y - heightDiff;
imagesFrame.size.height = heightDiff;
[_imagesView setFrame:imagesFrame];
// resizing chatTable
CGRect tableViewFrame = [_tableController.tableView frame];
tableViewFrame.size.height = imagesFrame.origin.y - tableViewFrame.origin.y;
[_tableController.tableView setFrame:tableViewFrame];
}
}
completion:^(BOOL finished){
@ -702,7 +741,9 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)keyboardWillShow:(NSNotification *)notif {
NSTimeInterval duration = [[[notif userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];
int heightDiff = UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) ? 55 : 105;
[UIView animateWithDuration:duration
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
@ -749,6 +790,18 @@ static UICompositeViewDescription *compositeDescription = nil;
[_messageView frame].origin.y - tableFrame.origin.y - composeIndicatorCompensation;
[_tableController.view setFrame:tableFrame];
}
if ([_imagesArray count] > 0){
// resizing imagesView
CGRect imagesFrame = [_imagesView frame];
imagesFrame.origin.y = [_messageView frame].origin.y - heightDiff;
imagesFrame.size.height = heightDiff;
[_imagesView setFrame:imagesFrame];
// resizing chatTable
CGRect tableViewFrame = [_tableController.tableView frame];
tableViewFrame.size.height = imagesFrame.origin.y - tableViewFrame.origin.y;
[_tableController.tableView setFrame:tableViewFrame];
}
// Scroll
NSInteger lastSection = [_tableController.tableView numberOfSections] - 1;
@ -761,8 +814,10 @@ static UICompositeViewDescription *compositeDescription = nil;
animated:FALSE];
}
}
}
completion:^(BOOL finished){
}];
}
@ -852,7 +907,17 @@ void on_chat_room_conference_left(LinphoneChatRoom *cr, const LinphoneEventLog *
[view.tableController scrollToBottom:true];
}
- (void)openResults:(NSString *) filePath
- (void)getIcloudFiles
{
_documentPicker = [[UIDocumentPickerViewController alloc] initWithDocumentTypes:@[@"public.data"]
inMode:UIDocumentPickerModeImport];
_documentPicker.delegate = self;
_documentPicker.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentViewController:_documentPicker animated:YES completion:nil];
}
- (void)openFile:(NSString *) filePath
{
// Open the controller.
_documentInteractionController = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:filePath]];
@ -866,4 +931,95 @@ void on_chat_room_conference_left(LinphoneChatRoom *cr, const LinphoneEventLog *
}
}
- (void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url {
NSFileCoordinator *fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:nil];
[fileCoordinator coordinateReadingItemAtURL:url options:NSFileCoordinatorReadingWithoutChanges error:nil byAccessor:^(NSURL * _Nonnull newURL) {
NSString *fileName = [newURL lastPathComponent];
NSData *data = [NSData dataWithContentsOfURL:newURL];
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:fileName];
[[NSFileManager defaultManager] createFileAtPath:filePath contents:data attributes:nil];
[self openFile:filePath];
}];
}
- (void)deleteImageWithAssetId:(NSString *)assetId {
NSUInteger key = [_assetIdsArray indexOfObject:assetId];
[_imagesArray removeObjectAtIndex:key];
[_assetIdsArray removeObjectAtIndex:key];
[self refreshImageDrawer];
}
- (void)clearMessageView {
[_messageField setText:@""];
_imagesArray = [NSMutableArray array];
_assetIdsArray = [NSMutableArray array];
[self refreshImageDrawer];
}
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
return [_imagesArray count];
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {
UIImageViewDeletable *imgView = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([UIImageViewDeletable class]) forIndexPath:indexPath];
CGRect imgFrame = imgView.frame;
imgFrame.origin.y = 5;
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
imgFrame.size.height = 50;
} else {
imgFrame.size.height = 100;
}
[imgView.image setImage:[UIImage resizeImage:[_imagesArray objectAtIndex:[indexPath item]] withMaxWidth:imgFrame.size.width andMaxHeight:imgFrame.size.height]];
[imgView setAssetId:[_assetIdsArray objectAtIndex:[indexPath item]]];
[imgView setDeleteDelegate:self];
[imgView setFrame:imgFrame];
[_sendButton setEnabled:TRUE];
return imgView;
}
- (void)refreshImageDrawer {
int heightDiff = UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation]) ? 55 : 105;
if ([_imagesArray count] == 0) {
[UIView animateWithDuration:0
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
// resizing imagesView
CGRect imagesFrame = [_imagesView frame];
imagesFrame.origin.y = [_messageView frame].origin.y;
imagesFrame.size.height = 0;
[_imagesView setFrame:imagesFrame];
// resizing chatTable
CGRect tableViewFrame = [_tableController.tableView frame];
tableViewFrame.size.height = imagesFrame.origin.y - tableViewFrame.origin.y;
[_tableController.tableView setFrame:tableViewFrame];
}
completion:nil];
if ([_messageField.text isEqualToString:@""])
[_sendButton setEnabled:FALSE];
} else {
[UIView animateWithDuration:0
delay:0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{
// resizing imagesView
CGRect imagesFrame = [_imagesView frame];
imagesFrame.origin.y = [_messageView frame].origin.y - heightDiff;
imagesFrame.size.height = heightDiff;
[_imagesView setFrame:imagesFrame];
// resizing chatTable
CGRect tableViewFrame = [_tableController.tableView frame];
tableViewFrame.size.height = imagesFrame.origin.y - tableViewFrame.origin.y;
[_tableController.tableView setFrame:tableViewFrame];
}
completion:^(BOOL result){[_imagesCollectionView reloadData];}];
}
}
@end

View file

@ -141,6 +141,10 @@ static int sorted_history_comparison(LinphoneChatRoom *to_insert, LinphoneChatRo
while (sorted) {
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
LinphoneChatRoom *cr = sorted->data;
if (!cr) {
sorted = sorted->next;
continue;
}
const LinphoneAddress *peer_address = linphone_chat_room_get_peer_address(cr);
const LinphoneAddress *local_address = linphone_chat_room_get_local_address(cr);
NSString *display;
@ -148,9 +152,17 @@ static int sorted_history_comparison(LinphoneChatRoom *to_insert, LinphoneChatRo
forKey:@"peer"];
[dict setObject:[NSString stringWithUTF8String:linphone_address_as_string_uri_only(local_address)]
forKey:@"local"];
if (linphone_chat_room_get_conference_address(cr))
if (linphone_chat_room_get_conference_address(cr)) {
if (!linphone_chat_room_get_subject(cr)) {
sorted = sorted->next;
continue;
}
display = [NSString stringWithUTF8String:linphone_chat_room_get_subject(cr)];
else {
} else {
if (!linphone_address_get_username(peer_address)) {
sorted = sorted->next;
continue;
}
display = [NSString stringWithUTF8String:linphone_address_get_display_name(peer_address)?:linphone_address_get_username(peer_address)];
if ([FastAddressBook imageForAddress:peer_address])
[dict setObject:UIImageJPEGRepresentation([UIImage resizeImage:[FastAddressBook imageForAddress:peer_address]

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

@ -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,42 @@ 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];
}
NSURL *alassetURL = [info objectForKey:UIImagePickerControllerReferenceURL];
PHFetchResult<PHAsset *> *phFetchResult = [PHAsset fetchAssetsWithALAssetURLs:@[alassetURL] options:nil];
PHAsset *phasset = [phFetchResult firstObject];
//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 +249,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

View file

@ -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

View file

@ -264,7 +264,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];
@ -289,6 +288,7 @@ struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_pre
}
[self migrateFromUserPrefs];
[self loadAvatar];
}
return self;
}
@ -1231,7 +1231,39 @@ static void linphone_iphone_popup_password_request(LinphoneCore *lc, LinphoneAut
}
content.sound = [UNNotificationSound soundNamed:@"msg.caf"];
content.categoryIdentifier = @"msg_cat";
content.userInfo = @{@"from" : from, @"peer_addr" : peer_uri, @"local_addr" : local_uri, @"CallId" : callID};
// save data to user info for rich notification content
NSMutableArray *msgs = [NSMutableArray array];
bctbx_list_t *history = linphone_chat_room_get_history(room, 6);
while (history) {
NSMutableDictionary *msgData = [NSMutableDictionary dictionary];
LinphoneChatMessage *msg = history->data;
const char *state = linphone_chat_message_state_to_string(linphone_chat_message_get_state(msg));
bool_t isOutgoing = linphone_chat_message_is_outgoing(msg);
bool_t isFileTransfer = (linphone_chat_message_get_file_transfer_information(msg) != NULL);
const LinphoneAddress *fromAddress = linphone_chat_message_get_from_address(msg);
NSString *displayNameDate = [NSString stringWithFormat:@"%@ - %@", [LinphoneUtils timeToString:linphone_chat_message_get_time(msg)
withFormat:LinphoneDateChatBubble],
[FastAddressBook displayNameForAddress:fromAddress]];
UIImage *fromImage = [UIImage resizeImage:[FastAddressBook imageForAddress:fromAddress]
withMaxWidth:200
andMaxHeight:200];
NSData *fromImageData = UIImageJPEGRepresentation(fromImage, 1);
[msgData setObject:[NSString stringWithUTF8String:state] forKey:@"state"];
[msgData setObject:displayNameDate forKey:@"displayNameDate"];
[msgData setObject:[NSNumber numberWithBool:isFileTransfer] forKey:@"isFileTransfer"];
[msgData setObject:fromImageData forKey:@"fromImageData"];
if (isFileTransfer) {
LinphoneContent *file = linphone_chat_message_get_file_transfer_information(msg);
const char *filename = linphone_content_get_name(file);
[msgData setObject:[NSString stringWithUTF8String:filename] forKey:@"msg"];
} else {
[msgData setObject:[UIChatBubbleTextCell TextMessageForChat:msg] forKey:@"msg"];
}
[msgData setObject:[NSNumber numberWithBool:isOutgoing] forKey:@"isOutgoing"];
[msgs addObject:msgData];
history = bctbx_list_next(history);
}
content.userInfo = @{@"from" : from, @"peer_addr" : peer_uri, @"local_addr" : local_uri, @"CallId" : callID, @"msgs" : msgs};
content.accessibilityLabel = @"Message notif";
UNNotificationRequest *req = [UNNotificationRequest requestWithIdentifier:@"call_request" content:content trigger:NULL];
[[UNUserNotificationCenter currentNotificationCenter]
@ -2888,20 +2920,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
@ -3078,4 +3109,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

@ -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,12 +21,14 @@
<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"/>
<outlet property="imdmIcon" destination="LPj-VT-0fH" id="yYh-pv-EJs"/>
<outlet property="imdmLabel" destination="44j-me-Iqi" id="m5R-Dm-V8g"/>
<outlet property="messageImageView" destination="yMW-cT-bpU" id="MNr-F2-abQ"/>
<outlet property="messageText" destination="cx9-0K-P9L" id="kPh-s4-Ioy"/>
<outlet property="openRecognizer" destination="NYA-II-xYn" id="pVM-vD-4Rg"/>
<outlet property="playButton" destination="cvc-tl-Pcf" id="eKJ-2T-LUl"/>
<outlet property="resendRecognizer" destination="5ZI-Ip-lGl" id="G2r-On-6mV"/>
@ -35,19 +37,19 @@
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" misplaced="YES" id="UGz-WT-BUv">
<rect key="frame" x="0.0" y="0.0" width="377" height="301"/>
<view contentMode="scaleToFill" id="UGz-WT-BUv">
<rect key="frame" x="0.0" y="0.0" width="377" height="351"/>
<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">
<rect key="frame" x="6" y="5" width="365" height="291"/>
<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="341"/>
<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">
<rect key="frame" x="0.0" y="0.0" width="365" height="291"/>
<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="341"/>
<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 +57,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 +65,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">
<rect key="frame" x="39" y="55" width="298" height="190"/>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="8I3-n2-0kS" userLabel="view">
<rect key="frame" x="39" y="55" width="298" height="230"/>
<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">
<rect key="frame" x="0.0" y="0.0" width="298" height="122"/>
<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="148"/>
<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,16 +82,16 @@
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" id="GmN-7v-uuO" userLabel="imageSubView">
<rect key="frame" x="0.0" y="128" width="297" height="62"/>
<view contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="GmN-7v-uuO" userLabel="imageSubView">
<rect key="frame" x="0.0" y="155" width="297" height="75"/>
<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">
<rect key="frame" x="10" y="29" width="277" height="2"/>
<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="42" 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">
<rect key="frame" x="84" y="33" width="115" height="27"/>
<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="46" width="115" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Download"/>
<state key="normal" title="DOWNLOAD" backgroundImage="color_G.png">
@ -100,8 +102,8 @@
<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">
<rect key="frame" x="84" y="33" width="115" height="27"/>
<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="46" width="115" height="27"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Cancel"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/>
@ -117,16 +119,16 @@
</view>
</subviews>
</view>
<view contentMode="scaleToFill" misplaced="YES" id="VYJ-RC-Jmg">
<rect key="frame" x="8" y="55" width="349" height="213"/>
<view clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" preservesSuperviewLayoutMargins="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VYJ-RC-Jmg" userLabel="finalAssetView">
<rect key="frame" x="0.0" y="55" width="365" height="230"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" id="gzv-K4-5OL" userLabel="finalImage">
<rect key="frame" x="0.0" y="0.0" width="349" height="213"/>
<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="365" height="230"/>
<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,28 +147,35 @@
<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">
<rect key="frame" x="0.0" y="290" width="365" height="1"/>
<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="340" 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">
<rect key="frame" x="283" y="274" width="64" height="20"/>
<textView clipsSubviews="YES" contentMode="scaleToFill" fixedFrame="YES" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" editable="NO" text="Lore ipsum..." translatesAutoresizingMaskIntoConstraints="NO" id="cx9-0K-P9L" userLabel="messageText" customClass="UITextViewNoDefine">
<rect key="frame" x="8" y="285" width="349" height="47"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView>
<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="324" 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">
<rect key="frame" x="349" y="276" width="13" height="13"/>
<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="326" width="13" height="13"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Delivery failed"/>
</imageView>
@ -179,7 +188,7 @@
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="simulatedStatusBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="-73.5" y="246.5"/>
<point key="canvasLocation" x="-73.5" y="271.5"/>
</view>
<tapGestureRecognizer id="5ZI-Ip-lGl" userLabel="resendClick">
<connections>

View file

@ -1,11 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13772"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>

View file

@ -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">

View file

@ -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;

View file

@ -30,16 +30,14 @@
@implementation UIChatBubblePhotoCell {
FileTransferDelegate *_ftd;
CGSize imageSize, bubbleSize, videoDefaultSize;
int actualAvailableWidth;
ChatConversationTableView *chatTableView;
//CGImageRef displayedImage;
BOOL assetIsLoaded;
}
#pragma mark - Lifecycle Functions
- (id)initWithIdentifier:(NSString *)identifier {
if ((self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]) != nil) {
// TODO: remove text cell subview
NSArray *arrayOfViews =
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil];
// resize cell to match .nib size. It is needed when resized the cell to
@ -51,15 +49,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 +77,7 @@
_finalImage.image = nil;
_finalImage.hidden = TRUE;
_fileTransferProgress.progress = 0;
assetIsLoaded = FALSE;
[self disconnectFromFileDelegate];
if (amessage) {
@ -98,54 +100,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) loadVideoAsset: (AVAsset *) asset {
// Calculate a time for the snapshot - I'm using the half way mark.
CMTime duration = [asset duration];
CMTime snapshot = CMTimeMake(duration.value / 2, duration.timescale);
// Create a generator and copy image at the time.
// I'm not capturing the actual time or an error.
AVAssetImageGenerator *generator =
[AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
CGImageRef imageRef = [generator copyCGImageAtTime:snapshot
actualTime:nil
error:nil];
UIImage *thumb = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
UIGraphicsBeginImageContext(videoDefaultSize);
[thumb drawInRect:CGRectMake(0, 0, videoDefaultSize.width, videoDefaultSize.height)];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self loadImageAsset:nil thumb:thumb image:image];
// put the play button in the top
CGRect newFrame = _playButton.frame;
newFrame.origin.x = _finalImage.frame.origin.x/2;
newFrame.origin.y = _finalImage.frame.origin.y/2;
_playButton.frame = newFrame;
- (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 - 40];
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) loadFileAsset {
@ -156,13 +137,25 @@
});
}
- (void) loadPlaceholder {
dispatch_async(dispatch_get_main_queue(), ^{
// Change this to load placeholder image when no asset id
//[_finalImage setImage:image];
//[_messageImageView setAsset:asset];
[_messageImageView stopLoading];
_messageImageView.hidden = YES;
_imageGestureRecognizer.enabled = YES;
_finalImage.hidden = NO;
[self layoutSubviews];
});
}
- (void)update {
if (self.message == nil) {
LOGW(@"Cannot update message room cell: NULL message");
return;
}
[super update];
[super update];
const char *url = linphone_chat_message_get_external_body_url(self.message);
BOOL is_external =
(url && (strstr(url, "http") == url)) || linphone_chat_message_get_file_transfer_information(self.message);
@ -183,52 +176,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");
}];
[self loadFirstImage:localImage type:PHAssetMediaTypeImage];
}
} else if (localVideo) {
}
else if (localVideo) {
if (_messageImageView.image == nil) {
[_messageImageView startLoading];
// read video from Documents
NSString *filePath = [LinphoneManager documentFile:localVideo];
NSURL *url = [NSURL fileURLWithPath:filePath];
AVAsset *asset = [AVAsset assetWithURL:url];
if (asset)
[self loadVideoAsset:asset];
[self loadFirstImage:localVideo type:PHAssetMediaTypeVideo];
_imageGestureRecognizer.enabled = NO;
}
} else if (localFile) {
}
else if (localFile) {
NSString *text = [NSString stringWithFormat:@"📎 %@",localFile];
_fileName.text = text;
[self loadFileAsset];
@ -246,20 +208,32 @@
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)loadFirstImage:(NSString *)key type:(PHAssetMediaType)type {
[_messageImageView startLoading];
PHFetchResult<PHAsset *> *assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:key] options:nil];
UIImage *img = nil;
img = [chatTableView.imagesInChatroom objectForKey:key];
PHAsset *asset = [assets firstObject];
if (!asset)
[self loadPlaceholder];
else if (asset.mediaType == type)
img = nil;
if (img)
[self loadImageAsset:asset image:img];
else
[self loadAsset:asset];
}
- (void)fileErrorBlock {
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"Can't open this file", nil)];
DTActionSheet *sheet = [[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"Can't find this file", nil)];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[sheet addCancelButtonWithTitle:NSLocalizedString(@"OK", nil) block:nil];
dispatch_async(dispatch_get_main_queue(), ^{
@ -280,33 +254,35 @@
}
- (IBAction)onPlayClick:(id)sender {
NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:self.message];
NSString *filePath = [LinphoneManager documentFile:localVideo];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath]) {
// create a player view controller
AVPlayer *player = [AVPlayer playerWithURL:[[NSURL alloc] initFileURLWithPath:filePath]];
AVPlayerViewController *controller = [[AVPlayerViewController alloc] init];
[PhoneMainView.instance presentViewController:controller animated:YES completion:nil];
controller.player = player;
[player play];
} else {
[self fileErrorBlock];
}
PHAsset *asset = [_messageImageView asset];
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
// options.synchronous = TRUE;
[[PHImageManager defaultManager] requestPlayerItemForVideo:asset options:options resultHandler:^(AVPlayerItem * _Nullable playerItem, NSDictionary * _Nullable info) {
if(playerItem) {
AVPlayer *player = [AVPlayer playerWithPlayerItem:playerItem];
AVPlayerViewController *controller = [[AVPlayerViewController alloc] init];
[PhoneMainView.instance presentViewController:controller animated:YES completion:nil];
controller.player = player;
[player play];
}
else {
[self fileErrorBlock];
}
}];
}
- (IBAction)onOpenClick:(id)event {
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:self.message];
NSString *filePath = [LinphoneManager documentFile:localFile];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:filePath]) {
ChatConversationView *view = VIEW(ChatConversationView);
[view openResults:filePath];
} else {
[self fileErrorBlock];
}
ChatConversationView *view = VIEW(ChatConversationView);
NSString *cachedFile = [LinphoneManager getMessageAppDataForKey:@"cachedfile" inMessage:self.message];
if (cachedFile) {
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:cachedFile]) {
[view openFile:cachedFile];
} else {
[self fileErrorBlock];
}
} else
[view getIcloudFiles];
}
@ -336,9 +312,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 +351,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 +385,7 @@
}
}
+ (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;
BOOL is_outgoing = linphone_chat_message_is_outgoing(super.message);
CGRect bubbleFrame = super.bubbleView.frame;
int origin_x;
@ -433,7 +394,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);
@ -442,27 +403,30 @@
bubbleFrame.origin.x = origin_x;
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;
// Resizing Image view
if (_finalImage.image) {
CGRect imgFrame = self.finalAssetView.frame;
imgFrame.size = [UIChatBubbleTextCell getMediaMessageSizefromOriginalSize:[_finalImage.image size] withWidth:chatTableView.tableView.frame.size.width - 40];
imgFrame.origin.x = (bubbleFrame.size.width - imgFrame.size.width)/2;
self.finalAssetView.frame = imgFrame;
// Positioning text message
const char *utf8Text = linphone_chat_message_get_text_content(self.message);
CGRect textFrame = self.messageText.frame;
textFrame.origin = CGPointMake(textFrame.origin.x, self.finalAssetView.frame.origin.y + self.finalAssetView.frame.size.height);
if (!utf8Text) {
textFrame.size.height = 0;
} else {
textFrame.size.height = bubbleFrame.size.height - textFrame.origin.x;
}
self.messageText.frame = textFrame;
LOGD([NSString stringWithFormat:@"Text of the photoCell: %@, size of the text of the photoCell: %@", [self.messageText text], NSStringFromCGSize(textFrame.size)]);
}
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

@ -55,5 +55,6 @@
+ (CGSize)ViewHeightForMessage:(LinphoneChatMessage *)chat withWidth:(int)width;
+ (NSString *)TextMessageForChat:(LinphoneChatMessage *)message;
+ (CGSize)computeBoundingBox:(NSString *)text size:(CGSize)size font:(UIFont *)font;
+ (NSString *)ContactDateForChat:(LinphoneChatMessage *)message;
@end

View file

@ -96,9 +96,11 @@
const LinphoneContent *last_content = linphone_chat_message_get_file_transfer_information(message);
// Last message was a file transfer (image) so display a picture...
if (url || last_content) {
if (linphone_chat_message_get_text_content(message))
return [NSString stringWithUTF8String:linphone_chat_message_get_text_content(message)];
return @"🗻";
} else {
const char *text = linphone_chat_message_get_text_content(message) ?: "";
const char *text = linphone_chat_message_get_text_content(message) ?: "";
return [NSString stringWithUTF8String:text] ?: [NSString stringWithCString:text encoding:NSASCIIStringEncoding]
?: NSLocalizedString(@"(invalid string)", nil);
}
@ -127,6 +129,7 @@
_statusInProgressSpinner.accessibilityLabel = @"Delivery in progress";
if (_messageText) {
LOGD(_messageText.text);
[_messageText setHidden:FALSE];
/* We need to use an attributed string here so that data detector don't mess
* with the text style. See http://stackoverflow.com/a/20669356 */
@ -232,29 +235,45 @@
if (linphone_chat_message_get_file_transfer_information(_message) != NULL) {
NSString *localImage = [LinphoneManager getMessageAppDataForKey:@"localimage" inMessage:_message];
NSNumber *uploadQuality =[LinphoneManager getMessageAppDataForKey:@"uploadQuality" inMessage:_message];
NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:_message];
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:_message];
NSString *fileName = localVideo ? localVideo : localFile;
NSURL *imageUrl = [NSURL URLWithString:localImage];
// TODO: do resend for video and files
/*NSString *localVideo = [LinphoneManager getMessageAppDataForKey:@"localvideo" inMessage:_message];
NSString *localFile = [LinphoneManager getMessageAppDataForKey:@"localfile" inMessage:_message];*/
[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");
}];
} else if(fileName) {
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) {
@ -324,8 +343,6 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 52; // 44;
[[UIChatBubbleTextCell alloc] initWithIdentifier:NSStringFromClass(UIChatBubbleTextCell.class)];
messageFont = cell.messageText.font;
}
// UITableView *tableView = VIEW(ChatConversationView).tableController.tableView;
// if (tableView.isEditing)
width -= 40; /*checkbox */
CGSize size;
const char *url = linphone_chat_message_get_external_body_url(chat);
@ -341,30 +358,33 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 52; // 44;
if(localFile) {
CGSize fileSize = CGSizeMake(200, 80);
size = [self getMediaMessageSizefromOriginalSize:fileSize withWidth:width];
} else if (localVideo) {
CGSize videoSize = CGSizeMake(320, 240);
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 && !localVideo) {
//We are loading the image
return CGSizeMake(CELL_MIN_WIDTH + CELL_MESSAGE_X_MARGIN, CELL_MIN_HEIGHT + CELL_MESSAGE_Y_MARGIN);
}
PHFetchResult<PHAsset *> *assets;
if(localImage)
assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localImage] options:nil];
else
assets = [PHAsset fetchAssetsWithLocalIdentifiers:[NSArray arrayWithObject:localVideo] 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 += 40;
size.width -= CELL_MESSAGE_X_MARGIN;
if (![messageText isEqualToString:@"🗻"]) {
CGSize textSize = [self computeBoundingBox:messageText
size:CGSizeMake(width - CELL_MESSAGE_X_MARGIN - 4, CGFLOAT_MAX)
font:messageFont];
size.height += textSize.height;
size.width = MAX(textSize.width, size.width);
}
}
}
@ -416,7 +436,7 @@ static const CGFloat CELL_MESSAGE_Y_MARGIN = 52; // 44;
+ (CGSize)getMediaMessageSizefromOriginalSize:(CGSize)originalSize withWidth:(int)width {
CGSize mediaSize = CGSizeMake(0, 0);
int availableWidth = width - CELL_MESSAGE_X_MARGIN;
int availableWidth = width;
if (UIInterfaceOrientationIsLandscape([[UIApplication sharedApplication] statusBarOrientation])) {
availableWidth = availableWidth /3;
}

View file

@ -0,0 +1,26 @@
//
// UIImageViewDeletable.h
// linphone
//
// Created by benjamin_verdier on 28/06/2018.
//
#import <UIKit/UIKit.h>
@protocol UIImageViewDeletableDelegate
@required
- (void)deleteImageWithAssetId:(NSString *)assetId;
@end
@interface UIImageViewDeletable : UICollectionViewCell
@property NSString *assetId;
@property(nonatomic, strong) id<UIImageViewDeletableDelegate> deleteDelegate;
@property (weak, nonatomic) IBOutlet UIImageView *image;
- (IBAction)onDeletePressed;
@end

View file

@ -0,0 +1,59 @@
//
// UIImageViewDeletable.m
// linphone
//
// Created by benjamin_verdier on 28/06/2018.
//
#import "UIImageViewDeletable.h"
@interface UIImageViewDeletable ()
@end
@implementation UIImageViewDeletable
- (UIImageViewDeletable *)init {
self = [super init];
if (self) {
NSArray *arrayOfViews =
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil];
// resize cell to match .nib size. It is needed when resized the cell to
// correctly adapt its height too
UIView *sub = ((UIView *)[arrayOfViews objectAtIndex:arrayOfViews.count - 1]);
[self setFrame:CGRectMake(0, 0, sub.frame.size.width, sub.frame.size.height)];
[self addSubview:sub];
}
return self;
}
- (UIImageViewDeletable *)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
NSArray *arrayOfViews =
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass(self.class) owner:self options:nil];
// resize cell to match .nib size. It is needed when resized the cell to
// correctly adapt its height too
UIView *sub = ((UIView *)[arrayOfViews objectAtIndex:arrayOfViews.count - 1]);
[self setFrame:frame];
[self addSubview:sub];
}
return self;
}
- (IBAction)onDeletePressed {
[_deleteDelegate deleteImageWithAssetId:_assetId];
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="UIImageViewDeletable">
<connections>
<outlet property="image" destination="y2f-LK-bVa" id="VPy-6D-kNb"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="50" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="y2f-LK-bVa">
<rect key="frame" x="0.0" y="0.0" width="50" height="100"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
</imageView>
<button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="J60-eT-n9R">
<rect key="frame" x="30" y="0.0" width="20" height="20"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration">
<accessibilityTraits key="traits" button="YES" image="YES"/>
</accessibility>
<state key="normal" image="delete_img.png"/>
<connections>
<action selector="onDeletePressed" destination="-1" eventType="touchUpInside" id="cmV-ac-rq7"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<viewLayoutGuide key="safeArea" id="Q5M-cg-NOt"/>
<point key="canvasLocation" x="7" y="47"/>
</view>
</objects>
<resources>
<image name="delete_img.png" width="50" height="50"/>
</resources>
</document>

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

@ -923,6 +923,8 @@ static RootViewManager *rootViewManagerInstance = nil;
linphone_chat_room_remove_callbacks(view.chatRoom, view.chatRoomCbs);
view.chatRoomCbs = NULL;
if (view.chatRoom != cr)
[view clearMessageView];
view.chatRoom = cr;
self.currentRoom = view.chatRoom;
if (PhoneMainView.instance.currentView == view.compositeViewDescription)

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) {
@ -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

View file

@ -12,11 +12,13 @@
@interface FileTransferDelegate : NSObject
- (void)upload:(UIImage *)image withURL:(NSURL *)url forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality;
- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withUrl:(NSURL *)url;
- (void)upload:(UIImage *)image withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom withQuality:(float)quality;
- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name;
- (void)uploadVideo:(NSData *)data withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom;
- (void)cancel;
- (BOOL)download:(LinphoneChatMessage *)message;
- (void)stopAndDestroy;
@property() LinphoneChatMessage *message;
@property() NSString *text;
@end

View file

@ -75,60 +75,117 @@ 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),
}];
} else {
[thiz stopAndDestroy];
CFRelease((__bridge CFTypeRef)thiz);
});
}];
} else if([fileType isEqualToString:@"video"]) {
CFBridgingRetain(thiz);
[[LinphoneManager.instance fileTransferDelegates] removeObject:thiz];
NSString *name =[NSString stringWithUTF8String:linphone_content_get_name(content)];
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name];
[[NSFileManager defaultManager] createFileAtPath:filePath
contents:thiz.data
attributes:nil];
// until image is properly saved, keep a reminder on it so that the
// chat bubble is aware of the fact that image is being saved to device
[LinphoneManager setValueInMessageAppData:@"saving..." forKey:@"localvideo" inMessage:message];
NSString *key = [fileType isEqualToString:@"file"] ? @"localfile" : @"localvideo";
__block PHObjectPlaceholder *placeHolder;
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetCreationRequest *request = [PHAssetCreationRequest creationRequestForAssetFromVideoAtFileURL:[NSURL fileURLWithPath:filePath]];
placeHolder = [request placeholderForCreatedAsset];
} completionHandler:^(BOOL success, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
LOGE(@"Cannot save video data downloaded [%@]", [error localizedDescription]);
[LinphoneManager setValueInMessageAppData:nil forKey:@"localvideo" inMessage:message];
UIAlertController *errView = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Transfer error", nil)
message:NSLocalizedString(@"Cannot write video 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(@"video saved to [%@]", [placeHolder localIdentifier]);
[LinphoneManager setValueInMessageAppData:[placeHolder localIdentifier]
forKey:@"localvideo"
inMessage:message];
}
[NSNotificationCenter.defaultCenter
postNotificationName:kLinphoneFileTransferRecvUpdate
object:thiz
userInfo:@{
@"state" : @(LinphoneChatMessageStateDelivered), // we dont want to
// trigger
// FileTransferDone here
@"progress" : @(1.f),
}];
[thiz stopAndDestroy];
CFRelease((__bridge CFTypeRef)thiz);
});
}];
} else {
[[LinphoneManager.instance fileTransferDelegates] removeObject:thiz];
NSString *key = @"localfile" ;
NSString *name =[NSString stringWithUTF8String:linphone_content_get_name(content)];
[LinphoneManager setValueInMessageAppData:@"saving..." forKey:key inMessage:message];
//write file to path
dispatch_async(dispatch_get_main_queue(), ^{
NSString *filePath = [LinphoneManager documentFile:name];
NSString *filePath = [[LinphoneManager cacheDirectory] stringByAppendingPathComponent:name];
//NSString *filePath = [LinphoneManager documentFile:name];
[[NSFileManager defaultManager] createFileAtPath:filePath
contents:thiz.data
attributes:nil];
[LinphoneManager setValueInMessageAppData:name forKey:key inMessage:message];
[LinphoneManager setValueInMessageAppData:filePath forKey:@"cachedfile" inMessage:message];
[NSNotificationCenter.defaultCenter
postNotificationName:kLinphoneFileTransferRecvUpdate
@ -205,14 +262,15 @@ static LinphoneBuffer *linphone_iphone_file_transfer_send(LinphoneChatMessage *m
linphone_content_set_subtype(content, [subtype UTF8String]);
linphone_content_set_name(content, [name UTF8String]);
linphone_content_set_size(content, _data.length);
_message = linphone_chat_room_create_file_transfer_message(chatRoom, content);
//linphone_chat_message_add_text_content(_message, [_text UTF8String]);
linphone_content_unref(content);
linphone_chat_message_cbs_set_file_transfer_send(linphone_chat_message_get_callbacks(_message),
linphone_iphone_file_transfer_send);
// internal url is saved in the appdata for display and later save
LOGE(@"nnnn %@ %@",key, keyData);
[LinphoneManager setValueInMessageAppData:keyData forKey:key inMessage:_message];
[LinphoneManager setValueInMessageAppData:qualityData forKey:@"uploadQuality" inMessage:_message];
@ -224,26 +282,24 @@ 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];
}
- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withUrl:(NSURL *)url {
NSString *name = [url lastPathComponent];
//save file to Documents
NSString *filePath = [LinphoneManager documentFile:name];
[[NSFileManager defaultManager] createFileAtPath:filePath
contents:[NSMutableData dataWithData:data]
attributes:nil];
if ([[url pathExtension] isEqualToString:@"MOV"])
[self uploadData:data forChatRoom:chatRoom type:nil subtype:nil name:name key:@"localvideo" keyData:name qualityData:nil];
- (void)uploadVideo:(NSData *)data withassetId:(NSString *)phAssetId forChatRoom:(LinphoneChatRoom *)chatRoom {
NSString *name = [NSString stringWithFormat:@"IMG-%f.MOV", [NSDate timeIntervalSinceReferenceDate]];
if (phAssetId)
[self uploadData:data forChatRoom:chatRoom type:@"video" subtype:nil name:name key:@"localvideo" keyData:phAssetId qualityData:nil];
else
[self uploadData:data forChatRoom:chatRoom type:@"file" subtype:nil name:name key:@"localfile" keyData:name qualityData:nil];
[self uploadData:data forChatRoom:chatRoom type:@"video" subtype:nil name:name key:@"localvideo" keyData:@"ending..." qualityData:nil];
}
- (void)uploadFile:(NSData *)data forChatRoom:(LinphoneChatRoom *)chatRoom withName:(NSString *)name {
[self uploadData:data forChatRoom:chatRoom type:@"file" subtype:nil name:name key:@"localfile" keyData:name qualityData:nil];
}

View file

@ -49,6 +49,8 @@ typedef enum {
+ (NSString *)durationToString:(int)duration;
+ (NSString *)intervalToString:(NSTimeInterval)interval ;
+ (NSMutableDictionary <NSString *, PHAsset *> *)photoAssetsDictionary;
@end
@interface NSNumber (HumanReadableSize)
@ -102,6 +104,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 {
@ -81,6 +55,41 @@
return [formatter stringFromTimeInterval:interval];
}
+ (NSMutableDictionary <NSString *, PHAsset *> *)photoAssetsDictionary {
NSMutableDictionary <NSString *, PHAsset *> *assetDict = [NSMutableDictionary dictionary];
PHFetchOptions *options = [[PHFetchOptions alloc] init];
[options setIncludeHiddenAssets:YES];
[options setIncludeAllBurstAssets:YES];
PHFetchResult *fetchRes = [PHAsset fetchAssetsWithOptions:options];
for (PHAsset *asset in fetchRes) {
NSString *key = [asset valueForKey:@"filename"];
[assetDict setObject:asset forKey:[[key componentsSeparatedByString:@"."] firstObject]];
}
return assetDict;
}
/*+ (NSMutableDictionary <NSString *, PHAsset *> *)videoAssetsDictionary {
NSMutableDictionary <NSString *, PHAsset *> *assetDict = [NSMutableDictionary dictionary];
PHFetchOptions *options = [[PHFetchOptions alloc] init];
[options setIncludeHiddenAssets:YES];
[options setIncludeAllBurstAssets:YES];
PHFetchResult *fetchRes = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeVideo options:options];
for (PHAsset *asset in fetchRes) {
NSString *key = [asset valueForKey:@"filename"];
[assetDict setObject:asset forKey:[[key componentsSeparatedByString:@"."] firstObject]];
}
return assetDict;
}*/
+ (NSString *)timeToString:(time_t)time withFormat:(LinphoneDateFormat)format {
NSString *formatstr;
NSDate *todayDate = [[NSDate alloc] init];
@ -777,3 +786,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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -63,12 +63,23 @@
570742671D5A63DB004B9C84 /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 570742661D5A63DB004B9C84 /* StoreKit.framework */; };
5E10ED4820D00630002BF6FE /* avatar.png in Resources */ = {isa = PBXBuildFile; fileRef = 633FEBE61D3CD5570014B822 /* avatar.png */; };
5E10ED4920D006C9002BF6FE /* chat_group_avatar.png in Resources */ = {isa = PBXBuildFile; fileRef = 8C2A81941F87B8000012A66B /* chat_group_avatar.png */; };
5E223A9920E244B400D06A36 /* NotificationTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E223A9820E244B400D06A36 /* NotificationTableViewCell.m */; };
5E31290120D7A37E00CF3AAE /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EF0C33820C806A5005081B0 /* NotificationCenter.framework */; };
5E31290520D7A37E00CF3AAE /* TodayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E31290420D7A37E00CF3AAE /* TodayViewController.m */; };
5E31290820D7A37E00CF3AAE /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E31290620D7A37E00CF3AAE /* MainInterface.storyboard */; };
5E31290C20D7A37E00CF3AAE /* latestChatroomsWidget.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 5E31290020D7A37E00CF3AAE /* latestChatroomsWidget.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
5E31291320D7AAA100CF3AAE /* avatar.png in Resources */ = {isa = PBXBuildFile; fileRef = 633FEBE61D3CD5570014B822 /* avatar.png */; };
5E31291A20D7AAAD00CF3AAE /* chat_group_avatar.png in Resources */ = {isa = PBXBuildFile; fileRef = 8C2A81941F87B8000012A66B /* chat_group_avatar.png */; };
5E32944520E4C29000BBA896 /* chat_delivered.png in Resources */ = {isa = PBXBuildFile; fileRef = 244523AC1E8266CC0037A187 /* chat_delivered.png */; };
5E32944C20E4C29300BBA896 /* chat_error.png in Resources */ = {isa = PBXBuildFile; fileRef = 244523AD1E8266CC0037A187 /* chat_error.png */; };
5E32944D20E4C29700BBA896 /* chat_read.png in Resources */ = {isa = PBXBuildFile; fileRef = 244523AE1E8266CC0037A187 /* chat_read.png */; };
5E3391EC20E387E000F66299 /* color_A.png in Resources */ = {isa = PBXBuildFile; fileRef = 633FEC701D3CD5570014B822 /* color_A.png */; };
5E3391F320E387E700F66299 /* color_D.png in Resources */ = {isa = PBXBuildFile; fileRef = 633FEC721D3CD5570014B822 /* color_D.png */; };
5E58962420DCE5700030868C /* UserNotifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8C73477B1D9BA3A00022EE8C /* UserNotifications.framework */; };
5E58962620DCE5700030868C /* UserNotificationsUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E58962520DCE5700030868C /* UserNotificationsUI.framework */; };
5E58962A20DCE5700030868C /* NotificationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5E58962920DCE5700030868C /* NotificationViewController.m */; };
5E58962D20DCE5700030868C /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5E58962B20DCE5700030868C /* MainInterface.storyboard */; };
5E58963120DCE5710030868C /* richNotifications.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 5E58962320DCE5700030868C /* richNotifications.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
5EEE8F9B20C80C23006E4176 /* NotificationCenter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5EF0C33820C806A5005081B0 /* NotificationCenter.framework */; };
5EEE8F9F20C80C23006E4176 /* TodayViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5EEE8F9E20C80C23006E4176 /* TodayViewController.m */; };
5EEE8FA220C80C23006E4176 /* MainInterface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5EEE8FA020C80C23006E4176 /* MainInterface.storyboard */; };
@ -730,6 +741,9 @@
8CF25D9D1F9F76BD00BEA0C1 /* chat_group_informations.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CF25D9B1F9F76BC00BEA0C1 /* chat_group_informations.png */; };
8CF25D9E1F9F76BD00BEA0C1 /* chat_group_informations@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 8CF25D9C1F9F76BD00BEA0C1 /* chat_group_informations@2x.png */; };
C90FAA7915AF54E6002091CB /* HistoryDetailsView.m in Sources */ = {isa = PBXBuildFile; fileRef = C90FAA7715AF54E6002091CB /* HistoryDetailsView.m */; };
CF15F21E20E4F9A3008B1DE6 /* UIImageViewDeletable.m in Sources */ = {isa = PBXBuildFile; fileRef = CF15F21C20E4F9A3008B1DE6 /* UIImageViewDeletable.m */; };
CF15F21F20E4F9A3008B1DE6 /* UIImageViewDeletable.xib in Resources */ = {isa = PBXBuildFile; fileRef = CF15F21D20E4F9A3008B1DE6 /* UIImageViewDeletable.xib */; };
CFBD7A2A20E504AE007C5286 /* delete_img.png in Resources */ = {isa = PBXBuildFile; fileRef = CFBD7A2320E504AD007C5286 /* delete_img.png */; };
D306459E1611EC2A00BB571E /* UILoadingImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = D306459D1611EC2900BB571E /* UILoadingImageView.m */; };
D3128FE115AABC7E00A2147A /* ContactDetailsView.m in Sources */ = {isa = PBXBuildFile; fileRef = D3128FDF15AABC7E00A2147A /* ContactDetailsView.m */; };
D31AAF5E159B3919002C6B02 /* CallPausedTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = D31AAF5D159B3919002C6B02 /* CallPausedTableView.m */; };
@ -831,6 +845,13 @@
remoteGlobalIDString = 5E3128FF20D7A37E00CF3AAE;
remoteInfo = latestChatroomsWidget;
};
5E58962F20DCE5710030868C /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
proxyType = 1;
remoteGlobalIDString = 5E58962220DCE5700030868C;
remoteInfo = richNotifications;
};
5EEE8FA420C80C23006E4176 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 29B97313FDCFA39411CA2CEA /* Project object */;
@ -934,6 +955,7 @@
61AE365620C00B370089D9D3 /* linphoneExtension.appex in Embed App Extensions */,
5E31290C20D7A37E00CF3AAE /* latestChatroomsWidget.appex in Embed App Extensions */,
5EEE8FA620C80C23006E4176 /* latestCallsWidget.appex in Embed App Extensions */,
5E58963120DCE5710030868C /* richNotifications.appex in Embed App Extensions */,
);
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
@ -1075,12 +1097,20 @@
570742601D5A09B8004B9C84 /* ShopView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShopView.h; sourceTree = "<group>"; };
570742631D5A1860004B9C84 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/ShopView.strings; sourceTree = "<group>"; };
570742661D5A63DB004B9C84 /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = System/Library/Frameworks/StoreKit.framework; sourceTree = SDKROOT; };
5E223A9720E244B400D06A36 /* NotificationTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationTableViewCell.h; sourceTree = "<group>"; };
5E223A9820E244B400D06A36 /* NotificationTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationTableViewCell.m; sourceTree = "<group>"; };
5E30780420D8F00D00256DAE /* latestChatroomsWidget.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = latestChatroomsWidget.entitlements; sourceTree = "<group>"; };
5E31290020D7A37E00CF3AAE /* latestChatroomsWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = latestChatroomsWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; };
5E31290320D7A37E00CF3AAE /* TodayViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TodayViewController.h; sourceTree = "<group>"; };
5E31290420D7A37E00CF3AAE /* TodayViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TodayViewController.m; sourceTree = "<group>"; };
5E31290720D7A37E00CF3AAE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
5E31290920D7A37E00CF3AAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5E58962320DCE5700030868C /* richNotifications.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = richNotifications.appex; sourceTree = BUILT_PRODUCTS_DIR; };
5E58962520DCE5700030868C /* UserNotificationsUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UserNotificationsUI.framework; path = System/Library/Frameworks/UserNotificationsUI.framework; sourceTree = SDKROOT; };
5E58962820DCE5700030868C /* NotificationViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NotificationViewController.h; sourceTree = "<group>"; };
5E58962920DCE5700030868C /* NotificationViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationViewController.m; sourceTree = "<group>"; };
5E58962C20DCE5700030868C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/MainInterface.storyboard; sourceTree = "<group>"; };
5E58962E20DCE5710030868C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5EEE8F9A20C80C23006E4176 /* latestCallsWidget.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = latestCallsWidget.appex; sourceTree = BUILT_PRODUCTS_DIR; };
5EEE8F9D20C80C23006E4176 /* TodayViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TodayViewController.h; sourceTree = "<group>"; };
5EEE8F9E20C80C23006E4176 /* TodayViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TodayViewController.m; sourceTree = "<group>"; };
@ -1829,6 +1859,10 @@
C90FAA7615AF54E6002091CB /* HistoryDetailsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HistoryDetailsView.h; sourceTree = "<group>"; };
C90FAA7715AF54E6002091CB /* HistoryDetailsView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HistoryDetailsView.m; sourceTree = "<group>"; };
C9B3A6FD15B485DB006F52EE /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = Utils/Utils.h; sourceTree = "<group>"; };
CF15F21B20E4F9A3008B1DE6 /* UIImageViewDeletable.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UIImageViewDeletable.h; sourceTree = "<group>"; };
CF15F21C20E4F9A3008B1DE6 /* UIImageViewDeletable.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UIImageViewDeletable.m; sourceTree = "<group>"; };
CF15F21D20E4F9A3008B1DE6 /* UIImageViewDeletable.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UIImageViewDeletable.xib; sourceTree = "<group>"; };
CFBD7A2320E504AD007C5286 /* delete_img.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = delete_img.png; sourceTree = "<group>"; };
D306459C1611EC2900BB571E /* UILoadingImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UILoadingImageView.h; sourceTree = "<group>"; };
D306459D1611EC2900BB571E /* UILoadingImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = UILoadingImageView.m; sourceTree = "<group>"; };
D3128FDE15AABC7E00A2147A /* ContactDetailsView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContactDetailsView.h; sourceTree = "<group>"; };
@ -2068,6 +2102,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
5E58962020DCE5700030868C /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5E58962620DCE5700030868C /* UserNotificationsUI.framework in Frameworks */,
5E58962420DCE5700030868C /* UserNotifications.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5EEE8F9720C80C23006E4176 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -2282,6 +2325,7 @@
61AE364B20C00B370089D9D3 /* linphoneExtension.appex */,
5EEE8F9A20C80C23006E4176 /* latestCallsWidget.appex */,
5E31290020D7A37E00CF3AAE /* latestChatroomsWidget.appex */,
5E58962320DCE5700030868C /* richNotifications.appex */,
);
name = Products;
sourceTree = "<group>";
@ -2289,6 +2333,9 @@
2214EB7012F84668002A5394 /* LinphoneUI */ = {
isa = PBXGroup;
children = (
CF15F21B20E4F9A3008B1DE6 /* UIImageViewDeletable.h */,
CF15F21C20E4F9A3008B1DE6 /* UIImageViewDeletable.m */,
CF15F21D20E4F9A3008B1DE6 /* UIImageViewDeletable.xib */,
63F1DF421BCE618E00EDED90 /* UIAddressTextField.h */,
63F1DF431BCE618E00EDED90 /* UIAddressTextField.m */,
63C441C11BBC23ED0053DC5E /* UIAssistantTextField.h */,
@ -2399,6 +2446,7 @@
61AE364C20C00B370089D9D3 /* linphoneExtension */,
5EEE8F9C20C80C23006E4176 /* latestCallsWidget */,
5E31290220D7A37E00CF3AAE /* latestChatroomsWidget */,
5E58962720DCE5700030868C /* richNotifications */,
29B97323FDCFA39411CA2CEA /* Frameworks */,
F0938158188E629800A55DFA /* iTunesArtwork */,
63058A0C1B4E821E00EFAE36 /* LiblinphoneTester */,
@ -2490,6 +2538,7 @@
2264B6D111200342002C2C53 /* SystemConfiguration.framework */,
1DF5F4DF0D08C38300B7A737 /* UIKit.framework */,
5EF0C33820C806A5005081B0 /* NotificationCenter.framework */,
5E58962520DCE5700030868C /* UserNotificationsUI.framework */,
);
name = Frameworks;
sourceTree = "<group>";
@ -2506,6 +2555,19 @@
path = latestChatroomsWidget;
sourceTree = "<group>";
};
5E58962720DCE5700030868C /* richNotifications */ = {
isa = PBXGroup;
children = (
5E58962820DCE5700030868C /* NotificationViewController.h */,
5E58962920DCE5700030868C /* NotificationViewController.m */,
5E58962B20DCE5700030868C /* MainInterface.storyboard */,
5E58962E20DCE5710030868C /* Info.plist */,
5E223A9720E244B400D06A36 /* NotificationTableViewCell.h */,
5E223A9820E244B400D06A36 /* NotificationTableViewCell.m */,
);
path = richNotifications;
sourceTree = "<group>";
};
5EEE8F9C20C80C23006E4176 /* latestCallsWidget */ = {
isa = PBXGroup;
children = (
@ -2632,6 +2694,7 @@
633FEBE11D3CD5570014B822 /* images */ = {
isa = PBXGroup;
children = (
CFBD7A2320E504AD007C5286 /* delete_img.png */,
24BFAA9B209B062F004F47A7 /* callkit_logo.png */,
24BFAA99209B062E004F47A7 /* contacts_sip_default.png */,
24BFAA94209B062C004F47A7 /* contacts_sip_default@2x.png */,
@ -3325,6 +3388,7 @@
61AE365520C00B370089D9D3 /* PBXTargetDependency */,
5EEE8FA520C80C23006E4176 /* PBXTargetDependency */,
5E31290B20D7A37E00CF3AAE /* PBXTargetDependency */,
5E58963020DCE5710030868C /* PBXTargetDependency */,
);
name = linphone;
productName = linphone;
@ -3348,6 +3412,23 @@
productReference = 5E31290020D7A37E00CF3AAE /* latestChatroomsWidget.appex */;
productType = "com.apple.product-type.app-extension";
};
5E58962220DCE5700030868C /* richNotifications */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5E58963C20DCE5710030868C /* Build configuration list for PBXNativeTarget "richNotifications" */;
buildPhases = (
5E58961F20DCE5700030868C /* Sources */,
5E58962020DCE5700030868C /* Frameworks */,
5E58962120DCE5700030868C /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = richNotifications;
productName = richNotifications;
productReference = 5E58962320DCE5700030868C /* richNotifications.appex */;
productType = "com.apple.product-type.app-extension";
};
5EEE8F9920C80C23006E4176 /* latestCallsWidget */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5EEE8FA720C80C24006E4176 /* Build configuration list for PBXNativeTarget "latestCallsWidget" */;
@ -3462,6 +3543,9 @@
com.apple.Push = {
enabled = 1;
};
com.apple.iCloud = {
enabled = 0;
};
};
};
5E3128FF20D7A37E00CF3AAE = {
@ -3474,6 +3558,11 @@
};
};
};
5E58962220DCE5700030868C = {
CreatedOnToolsVersion = 9.4;
DevelopmentTeam = Z2V957B3D6;
ProvisioningStyle = Automatic;
};
5EEE8F9920C80C23006E4176 = {
CreatedOnToolsVersion = 9.4;
DevelopmentTeam = Z2V957B3D6;
@ -3548,6 +3637,7 @@
61AE364A20C00B370089D9D3 /* linphoneExtension */,
5EEE8F9920C80C23006E4176 /* latestCallsWidget */,
5E3128FF20D7A37E00CF3AAE /* latestChatroomsWidget */,
5E58962220DCE5700030868C /* richNotifications */,
);
};
/* End PBXProject section */
@ -3691,6 +3781,7 @@
633FEE401D3CD5590014B822 /* contacts_all_selected.png in Resources */,
633FEE0C1D3CD5590014B822 /* chat_attachment_disabled.png in Resources */,
633FEF001D3CD55A0014B822 /* options_default.png in Resources */,
CF15F21F20E4F9A3008B1DE6 /* UIImageViewDeletable.xib in Resources */,
633FEE951D3CD55A0014B822 /* micro_default@2x.png in Resources */,
633FEE6A1D3CD5590014B822 /* footer_dialer_default.png in Resources */,
633FEEC91D3CD55A0014B822 /* numpad_5~ipad.png in Resources */,
@ -3822,6 +3913,7 @@
633FEED71D3CD55A0014B822 /* numpad_7_over~ipad.png in Resources */,
633FEF2A1D3CD55A0014B822 /* route_earpiece_disabled.png in Resources */,
633FEDDB1D3CD5590014B822 /* call_start_body_disabled@2x.png in Resources */,
CFBD7A2A20E504AE007C5286 /* delete_img.png in Resources */,
633FEDFD1D3CD5590014B822 /* camera_switch_default@2x.png in Resources */,
633FEEC51D3CD55A0014B822 /* numpad_5_over.png in Resources */,
633FEE721D3CD5590014B822 /* history_all_default.png in Resources */,
@ -4159,6 +4251,19 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
5E58962120DCE5700030868C /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5E32944C20E4C29300BBA896 /* chat_error.png in Resources */,
5E32944D20E4C29700BBA896 /* chat_read.png in Resources */,
5E32944520E4C29000BBA896 /* chat_delivered.png in Resources */,
5E3391F320E387E700F66299 /* color_D.png in Resources */,
5E58962D20DCE5700030868C /* MainInterface.storyboard in Resources */,
5E3391EC20E387E000F66299 /* color_A.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5EEE8F9820C80C23006E4176 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@ -4278,6 +4383,7 @@
1D60589B0D05DD56006BFB54 /* main.m in Sources */,
8CD99A3C2090B9FA008A7CDA /* ChatConversationImdnView.m in Sources */,
1D3623260D0F684500981E51 /* LinphoneAppDelegate.m in Sources */,
CF15F21E20E4F9A3008B1DE6 /* UIImageViewDeletable.m in Sources */,
22F2508E107141E100AC9B3F /* DialerView.m in Sources */,
633756451B67D2B200E21BAD /* SideMenuView.m in Sources */,
8CD99A422090CE6F008A7CDA /* UIChatConversationImdnTableViewCell.m in Sources */,
@ -4407,6 +4513,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
5E58961F20DCE5700030868C /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5E223A9920E244B400D06A36 /* NotificationTableViewCell.m in Sources */,
5E58962A20DCE5700030868C /* NotificationViewController.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5EEE8F9620C80C23006E4176 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@ -4466,6 +4581,11 @@
target = 5E3128FF20D7A37E00CF3AAE /* latestChatroomsWidget */;
targetProxy = 5E31290A20D7A37E00CF3AAE /* PBXContainerItemProxy */;
};
5E58963020DCE5710030868C /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5E58962220DCE5700030868C /* richNotifications */;
targetProxy = 5E58962F20DCE5710030868C /* PBXContainerItemProxy */;
};
5EEE8FA520C80C23006E4176 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5EEE8F9920C80C23006E4176 /* latestCallsWidget */;
@ -4512,6 +4632,14 @@
name = MainInterface.storyboard;
sourceTree = "<group>";
};
5E58962B20DCE5700030868C /* MainInterface.storyboard */ = {
isa = PBXVariantGroup;
children = (
5E58962C20DCE5700030868C /* Base */,
);
name = MainInterface.storyboard;
sourceTree = "<group>";
};
5EEE8FA020C80C23006E4176 /* MainInterface.storyboard */ = {
isa = PBXVariantGroup;
children = (
@ -5590,6 +5718,178 @@
};
name = DistributionAdhoc;
};
5E58963220DCE5710030868C /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = Z2V957B3D6;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = richNotifications/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MTL_ENABLE_DEBUG_INFO = YES;
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.richNotifications;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
5E58963320DCE5710030868C /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = richNotifications/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.richNotifications;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
5E58963420DCE5710030868C /* Distribution */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = richNotifications/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.richNotifications;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Distribution;
};
5E58963520DCE5710030868C /* DistributionAdhoc */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_IDENTITY = "iPhone Developer";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = Z2V957B3D6;
ENABLE_NS_ASSERTIONS = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
INFOPLIST_FILE = richNotifications/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
MTL_ENABLE_DEBUG_INFO = NO;
PRODUCT_BUNDLE_IDENTIFIER = org.linphone.phone.richNotifications;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "";
PROVISIONING_PROFILE_SPECIFIER = "";
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = DistributionAdhoc;
};
5EEE8FA820C80C24006E4176 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -6595,6 +6895,17 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
5E58963C20DCE5710030868C /* Build configuration list for PBXNativeTarget "richNotifications" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5E58963220DCE5710030868C /* Debug */,
5E58963320DCE5710030868C /* Release */,
5E58963420DCE5710030868C /* Distribution */,
5E58963520DCE5710030868C /* DistributionAdhoc */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Debug;
};
5EEE8FA720C80C24006E4176 /* Build configuration list for PBXNativeTarget "latestCallsWidget" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View file

@ -20,32 +20,35 @@ static NSString* groupName = @"group.belledonne-communications.linphone";
- (void)didSelectPost {
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
BOOL support = TRUE;
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
for (NSExtensionItem *item in self.extensionContext.inputItems) {
for (NSItemProvider *provider in item.attachments) {
NSUserDefaults *defaults = [[NSUserDefaults alloc] initWithSuiteName:groupName];
// TODO: Use [provider registeredTypeIdentifiersWithFileOptions:0]; to get all type identifiers of the provider instead of this if/else if structure
support = TRUE;
if ([provider hasItemConformingToTypeIdentifier:@"public.jpeg"]) {
[self loadItem:provider typeIdentifier:@"public.jpeg" defaults:defaults key:@"img"];
[self loadItem:provider typeIdentifier:@"public.jpeg" defaults:defaults];
} else if ([provider hasItemConformingToTypeIdentifier:@"com.compuserve.gif"]) {
[self loadItem:provider typeIdentifier:@"com.compuserve.gif" defaults:defaults];
} else if ([provider hasItemConformingToTypeIdentifier:@"public.url"]) {
[self loadItem:provider typeIdentifier:@"public.url" defaults:defaults key:@"web"];
[self loadItem:provider typeIdentifier:@"public.url" defaults:defaults];
} else if ([provider hasItemConformingToTypeIdentifier:@"public.movie"]) {
[self loadItem:provider typeIdentifier:@"public.movie" defaults:defaults key:@"mov"];
} else if ([provider hasItemConformingToTypeIdentifier:@"public.plain-text"]) {
[self loadItem:provider typeIdentifier:@"public.plain-text" defaults:defaults key:@"text"];
} else if ([provider hasItemConformingToTypeIdentifier:@"com.adobe.pdf"]) {
[self loadItem:provider typeIdentifier:@"com.adobe.pdf" defaults:defaults key:@"web"];
}
/*else if ([provider hasItemConformingToTypeIdentifier:@"public.png"]) {
[self loadItem:provider typeIdentifier:@"public.png" defaults:defaults key:@"img"];
}*/
else{
[self loadItem:provider typeIdentifier:@"public.movie" defaults:defaults];
} else if ([provider hasItemConformingToTypeIdentifier:@"com.apple.mapkit.map-item"]) {
[self loadItem:provider typeIdentifier:@"com.apple.mapkit.map-item" defaults:defaults];
} else if ([provider hasItemConformingToTypeIdentifier:@"com.adobe.pdf"]) {
[self loadItem:provider typeIdentifier:@"com.adobe.pdf" defaults:defaults];
} else if ([provider hasItemConformingToTypeIdentifier:@"public.png"]) {
[self loadItem:provider typeIdentifier:@"public.png" defaults:defaults];
} else{
NSLog(@"Unkown itemprovider = %@", provider);
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
support = false;
}
}
}
if (!support)
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
}
- (NSArray *)configurationItems {
@ -53,46 +56,57 @@ static NSString* groupName = @"group.belledonne-communications.linphone";
return @[];
}
- (void)loadItem:(NSItemProvider *)provider typeIdentifier:(NSString *)typeIdentifier defaults:(NSUserDefaults *)defaults key:(NSString *)key {
- (void)loadItem:(NSItemProvider *)provider typeIdentifier:(NSString *)typeIdentifier defaults:(NSUserDefaults *)defaults
{
[provider loadItemForTypeIdentifier:typeIdentifier options:nil completionHandler:^(id<NSSecureCoding> _Nullable item, NSError * _Null_unspecified error) {
/*if([(NSObject*)item isKindOfClass:[NSDictionary class]]) {
NSDictionary *dico = (NSDictionary *)item;
if (dico) {
return;
}
}*/
if([(NSObject*)item isKindOfClass:[NSURL class]]) {
NSData *nsData = [NSData dataWithContentsOfURL:(NSURL*)item];
NSURL *url = (NSURL *)item;
NSData *nsData = [NSData dataWithContentsOfURL:url];
if (nsData) {
NSDictionary *dict = @{@"nsData" : nsData,
@"url" : [(NSURL*)item absoluteString]};
[defaults setObject:dict forKey:key];
NSString *imgPath = url.path;
NSString *filename = [imgPath lastPathComponent];
if([imgPath containsString:@"var/mobile/Media/PhotoData"]) {
// We get the corresponding PHAsset identifier so we can display the image in the app without having to duplicate it.
NSDictionary *dict = @{@"nsData" : nsData,
@"url" : filename};
[defaults setObject:dict forKey:@"photoData"];
} else if ([imgPath containsString:@"var/mobile/Library/Mobile Documents/com~apple~CloudDocs"]) {
// shared files from icloud drive
NSDictionary *dict = @{@"nsData" : nsData,
@"url" : filename};
[defaults setObject:dict forKey:@"icloudData"];
}else {
//Others
NSDictionary *dict = @{@"url" : [url absoluteString]};
[defaults setObject:dict forKey:@"url"];
}
} else {
NSLog(@"NSExtensionItem Error, provider = %@", provider);
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
}
}
else if ([(NSObject*)item isKindOfClass:[UIImage class]]) {
NSData *imgData = UIImagePNGRepresentation((UIImage*)item);
if (imgData) {
NSDictionary *dict = @{@"nsData" : imgData,
};
[defaults setObject:dict forKey:key];
} else {
NSLog(@"NSExtensionItem Error, provider = %@", provider);
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
UIResponder *responder = self;
while (responder != nil) {
if ([responder respondsToSelector:@selector(openURL:)]) {
[responder performSelector:@selector(openURL:)
withObject:[NSURL URLWithString:@"message-linphone://" ]];
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
break;
}
responder = [responder nextResponder];
}
[defaults synchronize];
} else {
//share text
NSLog(@"Unsupported provider = %@", provider);
}
else {
NSDictionary *dict = @{@"name" : self.contentText};
[defaults setObject:dict forKey:key];
}
UIResponder *responder = self;
while (responder != nil) {
if ([responder respondsToSelector:@selector(openURL:)]) {
[responder performSelector:@selector(openURL:)
withObject:[NSURL URLWithString:@"message-linphone://" ]];
[self.extensionContext completeRequestReturningItems:@[] completionHandler:nil];
break;
}
responder = [responder nextResponder];
}
[defaults synchronize];
}];
}

View file

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="X9Y-jO-gVr">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Notification View Controller-->
<scene sceneID="ciY-6l-Mki">
<objects>
<tableViewController id="X9Y-jO-gVr" customClass="NotificationViewController" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" showsHorizontalScrollIndicator="NO" dataMode="prototypes" style="plain" separatorStyle="none" allowsSelection="NO" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="xjB-Tn-AYr">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="notificationCell" id="4dO-Sq-LtB" customClass="NotificationTableViewCell">
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="4dO-Sq-LtB" id="Q3t-C0-U1O">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="OXj-8D-5BS">
<rect key="frame" x="60" y="10" width="310" height="32"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="WZa-ow-o0L">
<rect key="frame" x="0.0" y="0.0" width="310" height="17"/>
<constraints>
<constraint firstAttribute="height" constant="17" id="tvJ-aj-CIf"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" text="Lorem ipsum " textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="3tK-2u-Ixe">
<rect key="frame" x="0.0" y="17" width="310" height="1.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<stackView opaque="NO" contentMode="scaleToFill" spacing="5" translatesAutoresizingMaskIntoConstraints="NO" id="rnc-rL-KN8">
<rect key="frame" x="0.0" y="18.5" width="310" height="13.5"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gv1-l8-gh3">
<rect key="frame" x="0.0" y="0.0" width="292" height="13.5"/>
<fontDescription key="fontDescription" type="system" pointSize="11"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="VYP-sN-Q5F">
<rect key="frame" x="297" y="0.0" width="13" height="13.5"/>
<constraints>
<constraint firstAttribute="height" constant="13" id="Drj-Lw-Eis"/>
<constraint firstAttribute="width" constant="13" id="NvK-gG-A0n"/>
</constraints>
</imageView>
</subviews>
<constraints>
<constraint firstAttribute="trailing" secondItem="VYP-sN-Q5F" secondAttribute="trailing" id="6ov-Ze-XHU"/>
</constraints>
</stackView>
</subviews>
<constraints>
<constraint firstItem="WZa-ow-o0L" firstAttribute="leading" secondItem="OXj-8D-5BS" secondAttribute="leading" id="7wZ-7m-XoH"/>
<constraint firstItem="3tK-2u-Ixe" firstAttribute="leading" secondItem="OXj-8D-5BS" secondAttribute="leading" id="A4k-hw-bfV"/>
<constraint firstItem="3tK-2u-Ixe" firstAttribute="top" secondItem="WZa-ow-o0L" secondAttribute="bottom" id="XWZ-K9-hVC"/>
<constraint firstItem="WZa-ow-o0L" firstAttribute="top" secondItem="OXj-8D-5BS" secondAttribute="top" id="dSr-F5-pKr"/>
<constraint firstAttribute="bottom" secondItem="rnc-rL-KN8" secondAttribute="bottom" id="o3L-83-aYX"/>
<constraint firstAttribute="trailing" secondItem="rnc-rL-KN8" secondAttribute="trailing" id="xIZ-Pw-t6a"/>
</constraints>
</stackView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="SKQ-W1-Lmw">
<rect key="frame" x="10" y="10" width="40" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="d6N-Hv-RS0"/>
<constraint firstAttribute="width" constant="40" id="kWJ-Qu-esd"/>
</constraints>
</imageView>
<imageView userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_A.png" translatesAutoresizingMaskIntoConstraints="NO" id="Wh2-bk-96C" userLabel="BottomBar">
<rect key="frame" x="0.0" y="43" width="375" height="1"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="xx0-Om-qom"/>
</constraints>
</imageView>
<imageView userInteractionEnabled="NO" alpha="0.20000000000000001" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_A.png" translatesAutoresizingMaskIntoConstraints="NO" id="FHB-G3-atz">
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
</imageView>
</subviews>
<constraints>
<constraint firstItem="OXj-8D-5BS" firstAttribute="top" secondItem="Q3t-C0-U1O" secondAttribute="top" constant="10" id="9no-0X-SIc"/>
<constraint firstItem="SKQ-W1-Lmw" firstAttribute="top" secondItem="Q3t-C0-U1O" secondAttribute="top" constant="10" id="EqG-ZA-H0w"/>
<constraint firstItem="Wh2-bk-96C" firstAttribute="centerX" secondItem="Q3t-C0-U1O" secondAttribute="centerX" id="GiJ-VV-sUL"/>
<constraint firstItem="FHB-G3-atz" firstAttribute="centerX" secondItem="Q3t-C0-U1O" secondAttribute="centerX" id="Il9-x1-er0"/>
<constraint firstItem="FHB-G3-atz" firstAttribute="centerY" secondItem="Q3t-C0-U1O" secondAttribute="centerY" id="M94-ru-dxk"/>
<constraint firstAttribute="trailing" secondItem="OXj-8D-5BS" secondAttribute="trailing" constant="5" id="Oq5-ST-lUC"/>
<constraint firstItem="FHB-G3-atz" firstAttribute="top" secondItem="Q3t-C0-U1O" secondAttribute="top" id="UHW-lR-QSP"/>
<constraint firstItem="SKQ-W1-Lmw" firstAttribute="leading" secondItem="Q3t-C0-U1O" secondAttribute="leading" constant="10" id="ZPR-9n-0E4"/>
<constraint firstItem="FHB-G3-atz" firstAttribute="leading" secondItem="Q3t-C0-U1O" secondAttribute="leading" id="d7N-zS-IjG"/>
<constraint firstItem="OXj-8D-5BS" firstAttribute="leading" secondItem="Q3t-C0-U1O" secondAttribute="leading" constant="60" id="e0Q-na-hor"/>
<constraint firstItem="Wh2-bk-96C" firstAttribute="leading" secondItem="Q3t-C0-U1O" secondAttribute="leading" id="hOW-4m-Ke6"/>
<constraint firstItem="OXj-8D-5BS" firstAttribute="bottom" secondItem="Q3t-C0-U1O" secondAttribute="bottom" constant="-2" id="mVA-WG-D1q"/>
<constraint firstAttribute="bottom" secondItem="Wh2-bk-96C" secondAttribute="bottom" id="scj-em-N61"/>
</constraints>
</tableViewCellContentView>
<connections>
<outlet property="background" destination="FHB-G3-atz" id="x5b-Bw-EFi"/>
<outlet property="bottomBarColor" destination="Wh2-bk-96C" id="wxV-mb-eTf"/>
<outlet property="contactImage" destination="SKQ-W1-Lmw" id="nsD-4b-g0W"/>
<outlet property="imdm" destination="gv1-l8-gh3" id="ceS-Ho-kpi"/>
<outlet property="imdmImage" destination="VYP-sN-Q5F" id="hNY-Ig-KqX"/>
<outlet property="msgText" destination="3tK-2u-Ixe" id="aVq-ra-Sd7"/>
<outlet property="nameDate" destination="WZa-ow-o0L" id="WdA-QI-xv1"/>
</connections>
</tableViewCell>
</prototypes>
<sections/>
<connections>
<outlet property="dataSource" destination="X9Y-jO-gVr" id="TwP-dT-I3s"/>
<outlet property="delegate" destination="X9Y-jO-gVr" id="2OC-kx-QgD"/>
</connections>
</tableView>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="EXl-Zq-25w" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-263.19999999999999" y="18.440779610194905"/>
</scene>
</scenes>
<resources>
<image name="color_A.png" width="2" height="2"/>
</resources>
</document>

View file

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>richNotifications</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>UNNotificationExtensionDefaultContentHidden</key>
<true/>
<key>UNNotificationExtensionCategory</key>
<string>msg_cat</string>
<key>UNNotificationExtensionInitialContentSizeRatio</key>
<real>1</real>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.content-extension</string>
</dict>
</dict>
</plist>

View file

@ -0,0 +1,24 @@
//
// NotificationTableViewCell.h
// richNotifications
//
// Created by David Idmansour on 26/06/2018.
//
#import <UIKit/UIKit.h>
@interface NotificationTableViewCell : UITableViewCell
@property (weak, nonatomic) IBOutlet UIImageView *contactImage;
@property (weak, nonatomic) IBOutlet UILabel *nameDate;
@property (strong, nonatomic) IBOutlet UITextView *msgText;
@property (weak, nonatomic) IBOutlet UILabel *imdm;
@property (weak, nonatomic) IBOutlet UIImageView *background;
@property (weak, nonatomic) IBOutlet UIImageView *bottomBarColor;
@property (weak, nonatomic) IBOutlet UIImageView *imdmImage;
@property BOOL isOutgoing;
@property float width;
@property float height;
- (CGSize)ViewSizeForMessage:(NSString *)chat withWidth:(int)width;
@end

View file

@ -0,0 +1,94 @@
//
// NotificationTableViewCell.m
// richNotifications
//
// Created by David Idmansour on 26/06/2018.
//
#import "NotificationTableViewCell.h"
@implementation NotificationTableViewCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
_contactImage.layer.cornerRadius = _contactImage.frame.size.height / 2;
_contactImage.clipsToBounds = YES;
[self.contentView sendSubviewToBack:_background];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
- (void)layoutSubviews {
[super layoutSubviews];
CGRect bubbleFrame = self.contentView.frame;
int originX;
bubbleFrame.size = CGSizeMake(_width, _height);
originX = (_isOutgoing ? self.frame.size.width - bubbleFrame.size.width - 5 : 5);
bubbleFrame.origin.x = originX;
self.contentView.frame = bubbleFrame;
_msgText.textContainerInset = UIEdgeInsetsZero;
_msgText.textContainer.lineFragmentPadding = 0;
}
#pragma mark - Bubble size computing
- (CGSize)computeBoundingBox:(NSString *)text size:(CGSize)size font:(UIFont *)font {
if (!text || text.length == 0)
return CGSizeMake(0, 0);
return [text boundingRectWithSize:size
options:(NSStringDrawingUsesLineFragmentOrigin |
NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesFontLeading)
attributes:@{
NSFontAttributeName : font
}
context:nil].size;
}
static const CGFloat CELL_MIN_HEIGHT = 60.0f;
static const CGFloat CELL_MIN_WIDTH = 190.0f;
static const CGFloat CELL_MESSAGE_X_MARGIN = 78 + 10.0f;
static const CGFloat CELL_MESSAGE_Y_MARGIN = 52; // 44;
- (CGSize)ViewHeightForMessage:(NSString *)messageText withWidth:(int)width {
static UIFont *messageFont = nil;
if (!messageFont) {
messageFont = _msgText.font;
}
CGSize size;
size = [self computeBoundingBox:messageText
size:CGSizeMake(width - CELL_MESSAGE_X_MARGIN - 4, CGFLOAT_MAX)
font:messageFont];
size.width = MAX(size.width + CELL_MESSAGE_X_MARGIN, CELL_MIN_WIDTH);
size.height = MAX(size.height + CELL_MESSAGE_Y_MARGIN, CELL_MIN_HEIGHT);
return size;
}
- (CGSize)ViewSizeForMessage:(NSString *)chat withWidth:(int)width {
static UIFont *dateFont = nil;
static CGSize dateViewSize;
if (!dateFont) {
dateFont = _nameDate.font;
dateViewSize = _nameDate.frame.size;
dateViewSize.width = CGFLOAT_MAX;
}
CGSize messageSize = [self ViewHeightForMessage:chat withWidth:width];
CGSize dateSize = [self computeBoundingBox:_nameDate.text size:dateViewSize font:dateFont];
messageSize.width = MAX(MAX(messageSize.width, MIN(dateSize.width + CELL_MESSAGE_X_MARGIN, width)), CELL_MIN_WIDTH);
return messageSize;
}
@end

View file

@ -0,0 +1,12 @@
//
// NotificationViewController.h
// richNotifications
//
// Created by David Idmansour on 22/06/2018.
//
#import <UIKit/UIKit.h>
@interface NotificationViewController : UITableViewController
@end

View file

@ -0,0 +1,110 @@
//
// NotificationViewController.m
// richNotifications
//
// Created by David Idmansour on 22/06/2018.
//
#import "NotificationViewController.h"
#import "NotificationTableViewCell.h"
#import <UserNotifications/UserNotifications.h>
#import <UserNotificationsUI/UserNotificationsUI.h>
@interface NotificationViewController () <UNNotificationContentExtension>
@end
@implementation NotificationViewController {
@private
NSMutableArray *msgs;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.tableView.scrollEnabled = TRUE;
// Do any required interface initialization here.
}
- (void)didReceiveNotification:(UNNotification *)notification {
// static float initialHeight = -1;
// if (initialHeight < 0)
// initialHeight = self.tableView.frame.size.height;
if (msgs)
[msgs addObject:[((NSArray *)[[[[notification request] content] userInfo] objectForKey:@"msgs"]) lastObject]];
else
msgs = [NSMutableArray arrayWithArray:[[[[notification request] content] userInfo] objectForKey:@"msgs"]];
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:msgs.count - 1 inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
// float height = 0;
// for (int i = 0 ; i < self->msgs.count ; i++) {
// height += [self tableView:self.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
// }
// if (height > initialHeight) {
// CGRect frame = self.tableView.frame;
// frame.size.height = height;
// frame.origin = CGPointMake(0, 0);
// self.tableView.frame = frame;
// self.tableView.bounds = frame;
// self.preferredContentSize = CGSizeMake(self.preferredContentSize.width, height);
// }
}
#pragma mark - UITableViewDataSource Functions
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return msgs.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
BOOL isOutgoing = ((NSNumber *)[msgs[indexPath.row] objectForKey:@"isOutgoing"]).boolValue;
NSString *display = ((NSString *)[msgs[indexPath.row] objectForKey:@"displayNameDate"]);
NSString *msgText = ((NSString *)[msgs[indexPath.row] objectForKey:@"msg"]);
NSString *imdm = ((NSString *)[msgs[indexPath.row] objectForKey:@"state"]);
NSData *imageData = [msgs[indexPath.row] objectForKey:@"fromImageData"];
NotificationTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"notificationCell" forIndexPath:indexPath];
cell.background.image = cell.bottomBarColor.image = [UIImage imageNamed:isOutgoing ? @"color_A" : @"color_D.png"];
cell.contactImage.image = [UIImage imageWithData:imageData];
cell.nameDate.text = display;
cell.msgText.text = msgText;
cell.isOutgoing = isOutgoing;
CGSize size = [cell ViewSizeForMessage:msgText withWidth:self.view.bounds.size.width - 10];
cell.width = size.width;
cell.height = size.height;
cell.nameDate.textColor = [UIColor colorWithPatternImage:cell.background.image];
cell.msgText.textColor = [UIColor darkGrayColor];
cell.imdm.hidden = cell.imdmImage.hidden = !isOutgoing;
if ([imdm isEqualToString:@"LinphoneChatMessageStateDelivered"] || [imdm isEqualToString:@"LinphoneChatMessageStateDeliveredToUser"]) {
cell.imdm.text = NSLocalizedString(@"Delivered", nil);
cell.imdm.textColor = [UIColor grayColor];
cell.imdmImage.image = [UIImage imageNamed:@"chat_delivered.png"];
} else if ([imdm isEqualToString:@"LinphoneChatMessageStateDisplayed"]) {
cell.imdm.text = NSLocalizedString(@"Read", nil);
cell.imdm.textColor = [UIColor colorWithRed:(24 / 255.0) green:(167 / 255.0) blue:(175 / 255.0) alpha:1.0];
cell.imdmImage.image = [UIImage imageNamed:@"chat_read.png"];
} else if ([imdm isEqualToString:@"LinphoneChatMessageStateNotDelivered"] || [imdm isEqualToString:@"LinphoneChatMessageStateFileTransferError"]) {
cell.imdm.text = NSLocalizedString(@"Error", nil);
cell.imdm.textColor = [UIColor redColor];
cell.imdmImage.image = [UIImage imageNamed:@"chat_error.png"];
} else
cell.imdm.hidden = YES;
return cell;
}
#pragma mark - UITableViewDelegate Functions
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NotificationTableViewCell *cell = [[NotificationTableViewCell alloc] init];
cell.msgText = [[UITextView alloc] init];
cell.msgText.text = ((NSString *)[msgs[indexPath.row] objectForKey:@"msg"]);
cell.msgText.font = [UIFont systemFontOfSize:17];
return [cell ViewSizeForMessage:cell.msgText.text withWidth:self.view.bounds.size.width - 10].height + 5;
}
@end