From a5810fdc20497008d58c02dad521f8ee258db372 Mon Sep 17 00:00:00 2001 From: Guillaume BIENKOWSKI Date: Mon, 19 Jan 2015 17:00:06 +0100 Subject: [PATCH] Try to mitigate crash and memory leak when changing the contact image multiple times. --- Classes/LinphoneUI/UIContactDetailsHeader.h | 2 ++ Classes/LinphoneUI/UIContactDetailsHeader.m | 31 ++++++++++++++------- Classes/Utils/FastAddressBook.h | 1 + Classes/Utils/FastAddressBook.m | 9 ++++++ 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/Classes/LinphoneUI/UIContactDetailsHeader.h b/Classes/LinphoneUI/UIContactDetailsHeader.h index af82ed75a..e76058bad 100644 --- a/Classes/LinphoneUI/UIContactDetailsHeader.h +++ b/Classes/LinphoneUI/UIContactDetailsHeader.h @@ -39,6 +39,8 @@ @property (nonatomic, retain) IBOutlet UITableView *tableView; @property (nonatomic, retain) IBOutlet id contactDetailsDelegate; +@property (retain, nonatomic) ImagePickerViewController* popoverController; + @property(nonatomic,getter=isEditing) BOOL editing; - (IBAction)onAvatarClick:(id)event; diff --git a/Classes/LinphoneUI/UIContactDetailsHeader.m b/Classes/LinphoneUI/UIContactDetailsHeader.m index be4d05af9..f3787a4f8 100644 --- a/Classes/LinphoneUI/UIContactDetailsHeader.m +++ b/Classes/LinphoneUI/UIContactDetailsHeader.m @@ -36,6 +36,7 @@ @synthesize editView; @synthesize tableView; @synthesize contactDetailsDelegate; +@synthesize popoverController; #pragma mark - Lifecycle Functions @@ -243,11 +244,13 @@ - (IBAction)onAvatarClick:(id)event { if(self.isEditing) { - void (^block)(UIImagePickerControllerSourceType) = ^(UIImagePickerControllerSourceType type) { + void (^showAppropriateController)(UIImagePickerControllerSourceType) = ^(UIImagePickerControllerSourceType type) { UICompositeViewDescription *description = [ImagePickerViewController compositeViewDescription]; ImagePickerViewController *controller; if([LinphoneManager runningOnIpad]) { controller = DYNAMIC_CAST([[PhoneMainView instance].mainViewController getCachedController:description.content], ImagePickerViewController); + // keep a reference to this controller so that in case of memory pressure we keep it + self.popoverController = controller; } else { controller = DYNAMIC_CAST([[PhoneMainView instance] changeCurrentView:description push:TRUE], ImagePickerViewController); } @@ -271,12 +274,12 @@ DTActionSheet *sheet = [[[DTActionSheet alloc] initWithTitle:NSLocalizedString(@"Select picture source",nil)] autorelease]; if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) { [sheet addButtonWithTitle:NSLocalizedString(@"Camera",nil) block:^(){ - block(UIImagePickerControllerSourceTypeCamera); + showAppropriateController(UIImagePickerControllerSourceTypeCamera); }]; } if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary]) { [sheet addButtonWithTitle:NSLocalizedString(@"Photo library",nil) block:^(){ - block(UIImagePickerControllerSourceTypePhotoLibrary); + showAppropriateController(UIImagePickerControllerSourceTypePhotoLibrary); }]; } if([FastAddressBook getContactImage:contact thumbnail:true] != nil) { @@ -288,8 +291,10 @@ [self update]; }]; } - [sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel",nil) block:nil]; - + [sheet addCancelButtonWithTitle:NSLocalizedString(@"Cancel",nil) block:^{ + self.popoverController = nil; + }]; + [sheet showInView:[PhoneMainView instance].view]; } } @@ -304,19 +309,25 @@ ImagePickerViewController *controller = DYNAMIC_CAST([[PhoneMainView instance].mainViewController getCachedController:description.content], ImagePickerViewController); if(controller != nil) { [controller.popoverController dismissPopoverAnimated:TRUE]; + self.popoverController = nil; } } + FastAddressBook* fab = [LinphoneManager instance].fastAddressBook; NSError* error = NULL; if(!ABPersonRemoveImageData(contact, (CFErrorRef*)error)) { [LinphoneLogger log:LinphoneLoggerLog format:@"Can't remove entry: %@", [error localizedDescription]]; } NSData *dataRef = UIImageJPEGRepresentation(image, 0.9f); CFDataRef cfdata = CFDataCreate(NULL,[dataRef bytes], [dataRef length]); - - if(!ABPersonSetImageData(contact, cfdata, (CFErrorRef*)error)) { - [LinphoneLogger log:LinphoneLoggerLog format:@"Can't add entry: %@", [error localizedDescription]]; - } - + + [fab saveAddressBook]; + + if(!ABPersonSetImageData(contact, cfdata, (CFErrorRef*)error)) { + [LinphoneLogger log:LinphoneLoggerLog format:@"Can't add entry: %@", [error localizedDescription]]; + } else { + [fab saveAddressBook]; + } + CFRelease(cfdata); [self update]; diff --git a/Classes/Utils/FastAddressBook.h b/Classes/Utils/FastAddressBook.h index d93549dae..34723eecc 100644 --- a/Classes/Utils/FastAddressBook.h +++ b/Classes/Utils/FastAddressBook.h @@ -31,6 +31,7 @@ + (UIImage*)getContactImage:(ABRecordRef)contact thumbnail:(BOOL)thumbnail; - (ABRecordRef)getContact:(NSString*)address; - (void)reload; +- (void)saveAddressBook; + (BOOL)isAuthorized; + (NSString*)appendCountryCodeIfPossible:(NSString*)number; + (NSString*)normalizePhoneNumber:(NSString*)number; diff --git a/Classes/Utils/FastAddressBook.m b/Classes/Utils/FastAddressBook.m index 35fb6d6ec..47e5a5b05 100644 --- a/Classes/Utils/FastAddressBook.m +++ b/Classes/Utils/FastAddressBook.m @@ -161,6 +161,15 @@ static void sync_address_book (ABAddressBookRef addressBook, CFDictionaryRef inf return self; } +- (void)saveAddressBook { + if( addressBook != nil ){ + NSError* err = nil; + if( !ABAddressBookSave(addressBook, (CFErrorRef*)err) ){ + Linphone_warn(@"Couldn't save Address Book"); + } + } +} + - (void)reload { if(addressBook != nil) { ABAddressBookUnregisterExternalChangeCallback(addressBook, sync_address_book, self);