Merge MatrixKit develop with commit hash: b85b736313bec0592bd1cabc68035d97f5331137

This commit is contained in:
SBiOSoftWhare
2021-12-03 11:47:24 +01:00
parent af65f71177
commit f57941177e
475 changed files with 87437 additions and 0 deletions
@@ -0,0 +1,170 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <UIKit/UIKit.h>
#import <AddressBook/AddressBook.h>
#import "MXKCellData.h"
#import "MXKEmail.h"
#import "MXKPhoneNumber.h"
/**
Posted when the contact thumbnail is updated.
The notification object is a contact Id.
*/
extern NSString *const kMXKContactThumbnailUpdateNotification;
extern NSString *const kMXKContactLocalContactPrefixId;
extern NSString *const kMXKContactMatrixContactPrefixId;
extern NSString *const kMXKContactDefaultContactPrefixId;
@interface MXKContact : MXKCellData <NSCoding>
/**
The unique identifier
*/
@property (nonatomic, readonly) NSString * contactID;
/**
The display name
*/
@property (nonatomic, readwrite) NSString *displayName;
/**
The sorting display name built by trimming the symbols [_!~`@#$%^&*-+();:={}[],.<>?\/"'] from the display name.
*/
@property (nonatomic) NSString* sortingDisplayName;
/**
The contact thumbnail. Default size: 256 X 256 pixels
*/
@property (nonatomic, copy, readonly) UIImage *thumbnail;
/**
YES if the contact does not exist in the contacts book
the contact has been created from a MXUser or MXRoomThirdPartyInvite
*/
@property (nonatomic) BOOL isMatrixContact;
/**
YES if the contact is coming from MXRoomThirdPartyInvite event (NO by default).
*/
@property (nonatomic) BOOL isThirdPartyInvite;
/**
The array of MXKPhoneNumber
*/
@property (nonatomic, readonly) NSArray *phoneNumbers;
/**
The array of MXKEmail
*/
@property (nonatomic, readonly) NSArray *emailAddresses;
/**
The array of matrix identifiers
*/
@property (nonatomic, readonly) NSArray* matrixIdentifiers;
/**
The matrix avatar url used (if any) to build the current thumbnail, nil by default.
*/
@property (nonatomic, readonly) NSString* matrixAvatarURL;
/**
Reset the current thumbnail if it is retrieved from a matrix url. May be used in case of the matrix avatar url change.
A new thumbnail will be automatically restored from the contact data.
*/
- (void)resetMatrixThumbnail;
/**
The contact ID from native phonebook record
*/
+ (NSString*)contactID:(ABRecordRef)record;
/**
Create a local contact from a device contact
@param record device contact id
@return MXKContact instance
*/
- (id)initLocalContactWithABRecord:(ABRecordRef)record;
/**
Create a matrix contact with the dedicated info
@param displayName the contact display name
@param matrixID the contact matrix id
@return MXKContact instance
*/
- (id)initMatrixContactWithDisplayName:(NSString*)displayName andMatrixID:(NSString*)matrixID;
/**
Create a matrix contact with the dedicated info
@param displayName the contact display name
@param matrixID the contact matrix id
@param matrixAvatarURL the matrix avatar url
@return MXKContact instance
*/
- (id)initMatrixContactWithDisplayName:(NSString*)displayName matrixID:(NSString*)matrixID andMatrixAvatarURL:(NSString*)matrixAvatarURL;
/**
Create a contact with the dedicated info
@param displayName the contact display name
@param emails an array of emails
@param phones an array of phone numbers
@param thumbnail the contact thumbnail
@return MXKContact instance
*/
- (id)initContactWithDisplayName:(NSString*)displayName
emails:(NSArray<MXKEmail*> *)emails
phoneNumbers:(NSArray<MXKPhoneNumber*> *)phones
andThumbnail:(UIImage *)thumbnail;
/**
The contact thumbnail with a prefered size.
If the thumbnail is already loaded, this method returns this one by ignoring prefered size.
The prefered size is used only if a server request is required.
@return thumbnail with a prefered size
*/
- (UIImage*)thumbnailWithPreferedSize:(CGSize)size;
/**
Tell whether a component of the contact's displayName, or one of his matrix id/email has the provided prefix.
@param prefix a non empty string.
@return YES when at least one matrix id, email or a component of the display name has this prefix.
*/
- (BOOL)hasPrefix:(NSString*)prefix;
/**
Check if the patterns can match with this contact
*/
- (BOOL)matchedWithPatterns:(NSArray*)patterns;
/**
The default ISO 3166-1 country code used to internationalize the contact phone numbers.
*/
@property (nonatomic) NSString *defaultCountryCode;
@end
@@ -0,0 +1,659 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2019 The Matrix.org Foundation C.I.C
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "MXKContact.h"
#import "MXKEmail.h"
#import "MXKPhoneNumber.h"
NSString *const kMXKContactThumbnailUpdateNotification = @"kMXKContactThumbnailUpdateNotification";
NSString *const kMXKContactLocalContactPrefixId = @"Local_";
NSString *const kMXKContactMatrixContactPrefixId = @"Matrix_";
NSString *const kMXKContactDefaultContactPrefixId = @"Default_";
@interface MXKContact()
{
UIImage* contactThumbnail;
UIImage* matrixThumbnail;
// The matrix id of the contact (used when the contact is not defined in the contacts book)
MXKContactField *matrixIdField;
}
@end
@implementation MXKContact
@synthesize isMatrixContact, isThirdPartyInvite;
+ (NSString*)contactID:(ABRecordRef)record
{
return [NSString stringWithFormat:@"%@%d", kMXKContactLocalContactPrefixId, ABRecordGetRecordID(record)];
}
- (id)init
{
self = [super init];
if (self)
{
matrixIdField = nil;
isMatrixContact = NO;
_matrixAvatarURL = nil;
isThirdPartyInvite = NO;
}
return self;
}
- (id)initLocalContactWithABRecord:(ABRecordRef)record
{
self = [self init];
if (self)
{
// compute a contact ID
_contactID = [MXKContact contactID:record];
// use the contact book display name
_displayName = (__bridge NSString*) ABRecordCopyCompositeName(record);
// avoid nil display name
// the display name is used to sort contacts
if (!_displayName)
{
_displayName = @"";
}
// extract the phone numbers and their related label
ABMultiValueRef multi = ABRecordCopyValue(record, kABPersonPhoneProperty);
CFIndex nCount = ABMultiValueGetCount(multi);
NSMutableArray* pns = [[NSMutableArray alloc] initWithCapacity:nCount];
for (int i = 0; i < nCount; i++)
{
CFTypeRef phoneRef = ABMultiValueCopyValueAtIndex(multi, i);
NSString *phoneVal = (__bridge NSString*)phoneRef;
// sanity check
if (0 != [phoneVal length])
{
CFStringRef lblRef = ABMultiValueCopyLabelAtIndex(multi, i);
CFStringRef localizedLblRef = nil;
NSString *lbl = @"";
if (lblRef != nil)
{
localizedLblRef = ABAddressBookCopyLocalizedLabel(lblRef);
if (localizedLblRef)
{
lbl = (__bridge NSString*)localizedLblRef;
}
else
{
lbl = (__bridge NSString*)lblRef;
}
}
else
{
localizedLblRef = ABAddressBookCopyLocalizedLabel(kABOtherLabel);
if (localizedLblRef)
{
lbl = (__bridge NSString*)localizedLblRef;
}
}
[pns addObject:[[MXKPhoneNumber alloc] initWithTextNumber:phoneVal type:lbl contactID:_contactID matrixID:nil]];
if (lblRef)
{
CFRelease(lblRef);
}
if (localizedLblRef)
{
CFRelease(localizedLblRef);
}
}
// release meory
if (phoneRef)
{
CFRelease(phoneRef);
}
}
CFRelease(multi);
_phoneNumbers = pns;
// extract the emails
multi = ABRecordCopyValue(record, kABPersonEmailProperty);
nCount = ABMultiValueGetCount(multi);
NSMutableArray *emails = [[NSMutableArray alloc] initWithCapacity:nCount];
for (int i = 0; i < nCount; i++)
{
CFTypeRef emailValRef = ABMultiValueCopyValueAtIndex(multi, i);
NSString *emailVal = (__bridge NSString*)emailValRef;
// sanity check
if ((nil != emailVal) && (0 != [emailVal length]))
{
CFStringRef lblRef = ABMultiValueCopyLabelAtIndex(multi, i);
CFStringRef localizedLblRef = nil;
NSString *lbl = @"";
if (lblRef != nil)
{
localizedLblRef = ABAddressBookCopyLocalizedLabel(lblRef);
if (localizedLblRef)
{
lbl = (__bridge NSString*)localizedLblRef;
}
else
{
lbl = (__bridge NSString*)lblRef;
}
}
else
{
localizedLblRef = ABAddressBookCopyLocalizedLabel(kABOtherLabel);
if (localizedLblRef)
{
lbl = (__bridge NSString*)localizedLblRef;
}
}
[emails addObject: [[MXKEmail alloc] initWithEmailAddress:emailVal type:lbl contactID:_contactID matrixID:nil]];
if (lblRef)
{
CFRelease(lblRef);
}
if (localizedLblRef)
{
CFRelease(localizedLblRef);
}
}
if (emailValRef)
{
CFRelease(emailValRef);
}
}
CFRelease(multi);
_emailAddresses = emails;
// thumbnail/picture
// check whether the contact has a picture
if (ABPersonHasImageData(record))
{
CFDataRef dataRef;
dataRef = ABPersonCopyImageDataWithFormat(record, kABPersonImageFormatThumbnail);
if (dataRef)
{
contactThumbnail = [UIImage imageWithData:(__bridge NSData*)dataRef];
CFRelease(dataRef);
}
}
}
return self;
}
- (id)initMatrixContactWithDisplayName:(NSString*)displayName andMatrixID:(NSString*)matrixID
{
self = [self init];
if (self)
{
_contactID = [NSString stringWithFormat:@"%@%@", kMXKContactMatrixContactPrefixId, [[NSUUID UUID] UUIDString]];
// Sanity check
if (matrixID.length)
{
// used when the contact is not defined in the contacts book
matrixIdField = [[MXKContactField alloc] initWithContactID:_contactID matrixID:matrixID];
isMatrixContact = YES;
}
// _displayName must not be nil
// it is used to sort the contacts
if (displayName)
{
_displayName = displayName;
}
else
{
_displayName = @"";
}
}
return self;
}
- (id)initMatrixContactWithDisplayName:(NSString*)displayName matrixID:(NSString*)matrixID andMatrixAvatarURL:(NSString*)matrixAvatarURL
{
self = [self initMatrixContactWithDisplayName:displayName andMatrixID:matrixID];
if (self)
{
matrixIdField.matrixAvatarURL = matrixAvatarURL;
}
return self;
}
- (id)initContactWithDisplayName:(NSString*)displayName
emails:(NSArray<MXKEmail*> *)emails
phoneNumbers:(NSArray<MXKPhoneNumber*> *)phones
andThumbnail:(UIImage *)thumbnail
{
self = [self init];
if (self)
{
_contactID = [NSString stringWithFormat:@"%@%@", kMXKContactDefaultContactPrefixId, [[NSUUID UUID] UUIDString]];
// _displayName must not be nil
// it is used to sort the contacts
if (displayName)
{
_displayName = displayName;
}
else
{
_displayName = @"";
}
_emailAddresses = emails;
_phoneNumbers = phones;
contactThumbnail = thumbnail;
}
return self;
}
#pragma mark -
- (NSString*)sortingDisplayName
{
if (!_sortingDisplayName)
{
// Sanity check - display name should not be nil here
if (self.displayName)
{
NSCharacterSet *specialCharacterSet = [NSCharacterSet characterSetWithCharactersInString:@"_!~`@#$%^&*-+();:={}[],.<>?\\/\"\'"];
_sortingDisplayName = [self.displayName stringByTrimmingCharactersInSet:specialCharacterSet];
}
else
{
return @"";
}
}
return _sortingDisplayName;
}
- (BOOL)hasPrefix:(NSString*)prefix
{
prefix = [prefix lowercaseString];
// Check first display name
if (_displayName.length)
{
NSString *lowercaseString = [_displayName lowercaseString];
if ([lowercaseString hasPrefix:prefix])
{
return YES;
}
NSArray *components = [lowercaseString componentsSeparatedByString:@" "];
for (NSString *component in components)
{
NSString *theComponent = [component stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if ([theComponent hasPrefix:prefix])
{
return YES;
}
}
}
// Check matrix identifiers
NSArray *identifiers = self.matrixIdentifiers;
NSString *idPrefix = prefix;
if (![prefix hasPrefix:@"@"])
{
idPrefix = [NSString stringWithFormat:@"@%@", prefix];
}
for (NSString* mxId in identifiers)
{
if ([[mxId lowercaseString] hasPrefix:idPrefix])
{
return YES;
}
}
// Check email
for (MXKEmail* email in _emailAddresses)
{
if ([email.emailAddress hasPrefix:prefix])
{
return YES;
}
}
// Check phones
for (MXKPhoneNumber* phone in _phoneNumbers)
{
if ([phone hasPrefix:prefix])
{
return YES;
}
}
return NO;
}
- (BOOL)matchedWithPatterns:(NSArray*)patterns
{
BOOL matched = NO;
if (patterns.count > 0)
{
matched = YES;
// test first display name
for (NSString* pattern in patterns)
{
if ([_displayName rangeOfString:pattern options:NSCaseInsensitiveSearch].location == NSNotFound)
{
matched = NO;
break;
}
}
NSArray *identifiers = self.matrixIdentifiers;
if (!matched && identifiers.count > 0)
{
for (NSString* mxId in identifiers)
{
// Consider only the first part of the matrix id (ignore homeserver name)
NSRange range = [mxId rangeOfString:@":"];
if (range.location != NSNotFound)
{
NSString *mxIdName = [mxId substringToIndex:range.location];
for (NSString* pattern in patterns)
{
if ([mxIdName rangeOfString:pattern options:NSCaseInsensitiveSearch].location != NSNotFound)
{
matched = YES;
break;
}
}
if (matched)
{
break;
}
}
}
}
if (!matched && _phoneNumbers.count > 0)
{
for (MXKPhoneNumber* phonenumber in _phoneNumbers)
{
if ([phonenumber matchedWithPatterns:patterns])
{
matched = YES;
break;
}
}
}
if (!matched && _emailAddresses.count > 0)
{
for (MXKEmail* email in _emailAddresses)
{
if ([email matchedWithPatterns:patterns])
{
matched = YES;
break;
}
}
}
}
else
{
// if there is no pattern to search, it should always matched
matched = YES;
}
return matched;
}
- (void)setDefaultCountryCode:(NSString *)defaultCountryCode
{
for (MXKPhoneNumber* phonenumber in _phoneNumbers)
{
phonenumber.defaultCountryCode = defaultCountryCode;
}
_defaultCountryCode = defaultCountryCode;
}
#pragma mark - getter/setter
- (NSArray*)matrixIdentifiers
{
NSMutableArray* identifiers = [[NSMutableArray alloc] init];
if (matrixIdField)
{
[identifiers addObject:matrixIdField.matrixID];
}
for (MXKEmail* email in _emailAddresses)
{
if (email.matrixID && ([identifiers indexOfObject:email.matrixID] == NSNotFound))
{
[identifiers addObject:email.matrixID];
}
}
for (MXKPhoneNumber* pn in _phoneNumbers)
{
if (pn.matrixID && ([identifiers indexOfObject:pn.matrixID] == NSNotFound))
{
[identifiers addObject:pn.matrixID];
}
}
return identifiers;
}
- (void)setDisplayName:(NSString *)displayName
{
// a display name must not be emptied
// it is used to sort the contacts
if (displayName.length == 0)
{
_displayName = _contactID;
}
else
{
_displayName = displayName;
}
}
- (void)resetMatrixThumbnail
{
matrixThumbnail = nil;
_matrixAvatarURL = nil;
// Reset the avatar in the contact fields too.
[matrixIdField resetMatrixAvatar];
for (MXKEmail* email in _emailAddresses)
{
[email resetMatrixAvatar];
}
}
- (UIImage*)thumbnailWithPreferedSize:(CGSize)size
{
// Consider first the local thumbnail if any.
if (contactThumbnail)
{
return contactThumbnail;
}
// Check whether a matrix thumbnail is already found.
if (matrixThumbnail)
{
return matrixThumbnail;
}
// Look for a thumbnail from the matrix identifiers
MXKContactField* firstField = matrixIdField;
if (firstField)
{
if (firstField.avatarImage)
{
matrixThumbnail = firstField.avatarImage;
_matrixAvatarURL = firstField.matrixAvatarURL;
return matrixThumbnail;
}
}
// try to replace the thumbnail by the matrix one
if (_emailAddresses.count > 0)
{
// list the linked email
// search if one email field has a dedicated thumbnail
for (MXKEmail* email in _emailAddresses)
{
if (email.avatarImage)
{
matrixThumbnail = email.avatarImage;
_matrixAvatarURL = email.matrixAvatarURL;
return matrixThumbnail;
}
else if (!firstField && email.matrixID)
{
firstField = email;
}
}
}
if (_phoneNumbers.count > 0)
{
// list the linked phones
// search if one phone field has a dedicated thumbnail
for (MXKPhoneNumber* phoneNb in _phoneNumbers)
{
if (phoneNb.avatarImage)
{
matrixThumbnail = phoneNb.avatarImage;
_matrixAvatarURL = phoneNb.matrixAvatarURL;
return matrixThumbnail;
}
else if (!firstField && phoneNb.matrixID)
{
firstField = phoneNb;
}
}
}
// if no thumbnail has been found
// try to load the first field one
if (firstField)
{
// should be retrieved by the cell info
[firstField loadAvatarWithSize:size];
}
return nil;
}
- (UIImage*)thumbnail
{
return [self thumbnailWithPreferedSize:CGSizeMake(256, 256)];
}
#pragma mark NSCoding
- (id)initWithCoder:(NSCoder *)coder
{
_contactID = [coder decodeObjectForKey:@"contactID"];
_displayName = [coder decodeObjectForKey:@"displayName"];
matrixIdField = [coder decodeObjectForKey:@"matrixIdField"];
_phoneNumbers = [coder decodeObjectForKey:@"phoneNumbers"];
_emailAddresses = [coder decodeObjectForKey:@"emailAddresses"];
NSData *data = [coder decodeObjectForKey:@"contactThumbnail"];
if (!data)
{
// Check the legacy storage.
data = [coder decodeObjectForKey:@"contactBookThumbnail"];
}
if (data)
{
contactThumbnail = [UIImage imageWithData:data];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:_contactID forKey:@"contactID"];
[coder encodeObject:_displayName forKey:@"displayName"];
if (matrixIdField)
{
[coder encodeObject:matrixIdField forKey:@"matrixIdField"];
}
if (_phoneNumbers.count)
{
[coder encodeObject:_phoneNumbers forKey:@"phoneNumbers"];
}
if (_emailAddresses.count)
{
[coder encodeObject:_emailAddresses forKey:@"emailAddresses"];
}
if (contactThumbnail)
{
@autoreleasepool
{
NSData *data = UIImageJPEGRepresentation(contactThumbnail, 0.8);
[coder encodeObject:data forKey:@"contactThumbnail"];
}
}
}
@end
@@ -0,0 +1,50 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <UIKit/UIKit.h>
@interface MXKContactField : NSObject<NSCoding>
/**
The identifier of the contact to whom the data belongs to.
*/
@property (nonatomic, readonly) NSString *contactID;
/**
The linked matrix identifier if any
*/
@property (nonatomic, readwrite) NSString *matrixID;
/**
The matrix avatar url (Matrix Content URI), nil by default.
*/
@property (nonatomic) NSString* matrixAvatarURL;
/**
The current avatar downloaded by using the avatar url if any
*/
@property (nonatomic, readonly) UIImage *avatarImage;
- (id)initWithContactID:(NSString*)contactID matrixID:(NSString*)matrixID;
- (void)loadAvatarWithSize:(CGSize)avatarSize;
/**
Reset the current avatar. May be used in case of the matrix avatar url change.
A new avatar will be automatically restored from the matrix data.
*/
- (void)resetMatrixAvatar;
@end
@@ -0,0 +1,235 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018 New Vector Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "MXKContactField.h"
@import MatrixSDK.MXMediaManager;
#import "MXKContactManager.h"
@interface MXKContactField()
{
// Tell whether we already check the contact avatar definition.
BOOL shouldCheckAvatarURL;
// The media manager of the session used to retrieve the contect avatar url
// This manager is used to download this avatar if need
MXMediaManager *mediaManager;
// The current download id
NSString *downloadId;
}
@end
@implementation MXKContactField
- (void)initFields
{
// init members
_contactID = nil;
_matrixID = nil;
[self resetMatrixAvatar];
}
- (id)initWithContactID:(NSString*)contactID matrixID:(NSString*)matrixID
{
self = [super init];
if (self)
{
[self initFields];
_contactID = contactID;
_matrixID = matrixID;
}
return self;
}
- (void)resetMatrixAvatar
{
_avatarImage = nil;
_matrixAvatarURL = nil;
shouldCheckAvatarURL = YES;
mediaManager = nil;
downloadId = nil;
}
- (void)loadAvatarWithSize:(CGSize)avatarSize
{
// Check whether the avatar image is already set
if (_avatarImage)
{
return;
}
// Sanity check
if (_matrixID)
{
if (shouldCheckAvatarURL)
{
// Consider here all sessions reported into contact manager
NSArray* mxSessions = [MXKContactManager sharedManager].mxSessions;
if (mxSessions.count)
{
// Check whether a matrix user is already known
MXUser* user;
MXSession *mxSession;
for (mxSession in mxSessions)
{
user = [mxSession userWithUserId:_matrixID];
if (user)
{
_matrixAvatarURL = user.avatarUrl;
if (_matrixAvatarURL)
{
shouldCheckAvatarURL = NO;
mediaManager = mxSession.mediaManager;
[self downloadAvatarImage:avatarSize];
}
break;
}
}
// Trigger a server request if this url has not been found.
if (shouldCheckAvatarURL)
{
MXWeakify(self);
[mxSession.matrixRestClient avatarUrlForUser:_matrixID
success:^(NSString *mxAvatarUrl) {
MXStrongifyAndReturnIfNil(self);
self.matrixAvatarURL = mxAvatarUrl;
self->shouldCheckAvatarURL = NO;
self->mediaManager = mxSession.mediaManager;
[self downloadAvatarImage:avatarSize];
} failure:nil];
}
}
}
else if (_matrixAvatarURL)
{
[self downloadAvatarImage:avatarSize];
}
// Do nothing if the avatar url has been checked, and it is null.
}
}
- (void)downloadAvatarImage:(CGSize)avatarSize
{
// the avatar image is already done
if (_avatarImage)
{
return;
}
if (_matrixAvatarURL)
{
NSString *cacheFilePath = [MXMediaManager thumbnailCachePathForMatrixContentURI:_matrixAvatarURL
andType:nil
inFolder:kMXMediaManagerAvatarThumbnailFolder
toFitViewSize:avatarSize
withMethod:MXThumbnailingMethodCrop];
_avatarImage = [MXMediaManager loadPictureFromFilePath:cacheFilePath];
// the image is already in the cache
if (_avatarImage)
{
MXWeakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
MXStrongifyAndReturnIfNil(self);
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKContactThumbnailUpdateNotification object:self.contactID userInfo:nil];
});
}
else
{
NSString *downloadId = [MXMediaManager thumbnailDownloadIdForMatrixContentURI:_matrixAvatarURL inFolder:kMXMediaManagerAvatarThumbnailFolder toFitViewSize:avatarSize withMethod:MXThumbnailingMethodCrop];
MXMediaLoader* loader = [MXMediaManager existingDownloaderWithIdentifier:downloadId];
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onMediaDownloadEnd:) name:kMXMediaLoaderStateDidChangeNotification object:loader];
if (!loader && mediaManager)
{
[mediaManager downloadThumbnailFromMatrixContentURI:_matrixAvatarURL
withType:nil
inFolder:kMXMediaManagerAvatarThumbnailFolder
toFitViewSize:avatarSize
withMethod:MXThumbnailingMethodCrop
success:nil
failure:nil];
}
}
}
}
- (void)onMediaDownloadEnd:(NSNotification *)notif
{
MXMediaLoader *loader = (MXMediaLoader*)notif.object;
if ([loader.downloadId isEqualToString:downloadId])
{
// update the image
switch (loader.state) {
case MXMediaLoaderStateDownloadCompleted:
{
UIImage *image = [MXMediaManager loadPictureFromFilePath:loader.downloadOutputFilePath];
if (image)
{
_avatarImage = image;
MXWeakify(self);
dispatch_async(dispatch_get_main_queue(), ^{
MXStrongifyAndReturnIfNil(self);
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKContactThumbnailUpdateNotification object:self.contactID userInfo:nil];
});
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:nil];
downloadId = nil;
break;
}
case MXMediaLoaderStateDownloadFailed:
case MXMediaLoaderStateCancelled:
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXMediaLoaderStateDidChangeNotification object:nil];
downloadId = nil;
break;
default:
break;
}
}
}
#pragma mark NSCoding
- (id)initWithCoder:(NSCoder *)coder
{
if (self)
{
[self initFields];
_contactID = [coder decodeObjectForKey:@"contactID"];
_matrixID = [coder decodeObjectForKey:@"matrixID"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeObject:_contactID forKey:@"contactID"];
[coder encodeObject:_matrixID forKey:@"matrixID"];
}
@end
@@ -0,0 +1,243 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2019 The Matrix.org Foundation C.I.C
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <Foundation/Foundation.h>
#import <Contacts/Contacts.h>
#import <MatrixSDK/MatrixSDK.h>
#import "MXKSectionedContacts.h"
#import "MXKContact.h"
/**
Posted when the matrix contact list is loaded or updated.
The notification object is:
- a contact Id when a matrix contact has been added/updated/removed.
or
- nil when all matrix contacts are concerned.
*/
extern NSString * _Nonnull const kMXKContactManagerDidUpdateMatrixContactsNotification;
/**
Posted when the local contact list is loaded and updated.
The notification object is:
- a contact Id when a local contact has been added/updated/removed.
or
- nil when all local contacts are concerned.
*/
extern NSString * _Nonnull const kMXKContactManagerDidUpdateLocalContactsNotification;
/**
Posted when local contact matrix ids is updated.
The notification object is:
- a contact Id when a local contact has been added/updated/removed.
or
- nil when all local contacts are concerned.
*/
extern NSString * _Nonnull const kMXKContactManagerDidUpdateLocalContactMatrixIDsNotification;
/**
Posted when the presence of a matrix user linked at least to one contact has changed.
The notification object is the matrix Id. The `userInfo` dictionary contains an `MXPresenceString` object under the `kMXKContactManagerMatrixPresenceKey` key, representing the matrix user presence.
*/
extern NSString * _Nonnull const kMXKContactManagerMatrixUserPresenceChangeNotification;
extern NSString * _Nonnull const kMXKContactManagerMatrixPresenceKey;
/**
Posted when all phonenumbers of local contacts have been internationalized.
The notification object is nil.
*/
extern NSString * _Nonnull const kMXKContactManagerDidInternationalizeNotification;
/**
Used to identify the type of data when requesting MXKeyProvider
*/
extern NSString * _Nonnull const MXKContactManagerDataType;
/**
Define the contact creation for the room members
*/
typedef NS_ENUM(NSInteger, MXKContactManagerMXRoomSource) {
MXKContactManagerMXRoomSourceNone = 0, // the MXMember does not create any new contact.
MXKContactManagerMXRoomSourceDirectChats = 1, // the direct chat users have their own contact even if they are not defined in the device contacts book
MXKContactManagerMXRoomSourceAll = 2, // all the room members have their own contact even if they are not defined in the device contacts book
};
/**
This manager handles 2 kinds of contact list:
- The local contacts retrieved from the device phonebook.
- The matrix contacts retrieved from the matrix one-to-one rooms.
Note: The local contacts handling depends on the 'syncLocalContacts' and 'phonebookCountryCode' properties
of the shared application settings object '[MXKAppSettings standardAppSettings]'.
*/
@interface MXKContactManager : NSObject
/**
The shared instance of contact manager.
*/
+ (MXKContactManager* _Nonnull)sharedManager;
/**
Block called (if any) to discover the Matrix users bound to a set of third-party identifiers (email addresses, phone numbers).
If this property is unset, the contact manager will consider the potential identity server URL (see the `identityServer` property)
to build its own Restclient and trigger `lookup3PIDs` requests.
@param threepids the list of 3rd party ids: [[<(MX3PIDMedium)media1>, <(NSString*)address1>], [<(MX3PIDMedium)media2>, <(NSString*)address2>], ...].
@param success a block object called when the operation succeeds. It provides the array of the discovered users:
[[<(MX3PIDMedium)media>, <(NSString*)address>, <(NSString*)userId>], ...].
@param failure a block object called when the operation fails.
*/
typedef void(^MXKContactManagerDiscoverUsersBoundTo3PIDs)(NSArray<NSArray<NSString *> *> * _Nonnull threepids,
void (^ _Nonnull success)(NSArray<NSArray<NSString *> *> *_Nonnull),
void (^ _Nonnull failure)(NSError *_Nonnull));
@property (nonatomic, nullable) MXKContactManagerDiscoverUsersBoundTo3PIDs discoverUsersBoundTo3PIDsBlock;
/**
Define if the room member must have their dedicated contact even if they are not define in the device contacts book.
The default value is MXKContactManagerMXRoomSourceDirectChats;
*/
@property (nonatomic) MXKContactManagerMXRoomSource contactManagerMXRoomSource;
/**
Associated matrix sessions (empty by default).
*/
@property (nonatomic, readonly, nonnull) NSArray *mxSessions;
/**
The current list of the contacts extracted from matrix data. Depends on 'contactManagerMXRoomSource'.
*/
@property (nonatomic, readonly, nullable) NSArray *matrixContacts;
/**
The current list of the local contacts (nil by default until the contacts are loaded).
*/
@property (nonatomic, readonly, nullable) NSArray *localContacts;
/**
The current list of the local contacts who have contact methods which can be used to invite them or to discover matrix users.
*/
@property (nonatomic, readonly, nullable) NSArray *localContactsWithMethods;
/**
The contacts list obtained by splitting each local contact by contact method.
This list is alphabetically sorted.
Each contact has one and only one contact method.
*/
//- (void)localContactsSplitByContactMethod:(void (^)(NSArray<MXKContact*> *localContactsSplitByContactMethod))onComplete;
@property (nonatomic, readonly, nullable) NSArray *localContactsSplitByContactMethod;
/**
The current list of the contacts for whom a direct chat exists.
*/
@property (nonatomic, readonly, nonnull) NSArray *directMatrixContacts;
/// Flag to allow local contacts access or not. Default value is YES.
@property (nonatomic, assign) BOOL allowLocalContactsAccess;
/**
Add/remove matrix session. The matrix contact list is automatically updated (see kMXKContactManagerDidUpdateMatrixContactsNotification event).
*/
- (void)addMatrixSession:(MXSession* _Nonnull)mxSession;
- (void)removeMatrixSession:(MXSession* _Nonnull)mxSession;
/**
Takes into account the state of the identity service's terms, local contacts access authorization along with
whether the user has left the app for the Settings app to update the contacts access, and enables/disables
the `syncLocalContacts` property of `MXKAppSettings` when necessary.
@param mxSession The session who's identity service shall be used.
*/
- (void)validateSyncLocalContactsStateForSession:(MXSession *)mxSession;
/**
Load and/or refresh the local contacts. Observe kMXKContactManagerDidUpdateLocalContactsNotification to know when local contacts are available.
*/
- (void)refreshLocalContacts;
/**
Delete contacts info
*/
- (void)reset;
/**
Get contact by its identifier.
@param contactID the contact identifier.
@return the contact defined with the provided id.
*/
- (MXKContact* _Nullable)contactWithContactID:(NSString* _Nonnull)contactID;
/**
Refresh matrix IDs for a specific local contact. See kMXKContactManagerDidUpdateLocalContactMatrixIDsNotification
posted when update is done.
@param contact the local contact to refresh.
*/
- (void)updateMatrixIDsForLocalContact:(MXKContact* _Nonnull)contact;
/**
Refresh matrix IDs for all local contacts. See kMXKContactManagerDidUpdateLocalContactMatrixIDsNotification
posted when update for all local contacts is done.
*/
- (void)updateMatrixIDsForAllLocalContacts;
/**
The contacts list obtained by splitting each local contact by contact method.
This list is alphabetically sorted.
Each contact has one and only one contact method.
*/
//- (void)localContactsSplitByContactMethod:(void (^)(NSArray<MXKContact*> *localContactsSplitByContactMethod))onComplete;
/**
Sort a contacts array in sectioned arrays to be displayable in a UITableview
*/
- (MXKSectionedContacts* _Nullable)getSectionedContacts:(NSArray* _Nonnull)contactList;
/**
Sort alphabetically an array of contacts.
@param contactsArray the array of contacts to sort.
*/
- (void)sortAlphabeticallyContacts:(NSMutableArray<MXKContact*> * _Nonnull)contactsArray;
/**
Sort an array of contacts by last active, with "active now" first.
...and then alphabetically.
@param contactsArray the array of contacts to sort.
*/
- (void)sortContactsByLastActiveInformation:(NSMutableArray<MXKContact*> * _Nonnull)contactsArray;
/**
Refresh the international phonenumber of the local contacts (See kMXKContactManagerDidInternationalizeNotification).
@param countryCode the country code.
*/
- (void)internationalizePhoneNumbers:(NSString* _Nonnull)countryCode;
/**
Request user permission for syncing local contacts.
@param viewController the view controller to attach the dialog to the user.
@param handler the block called with the result of requesting access
*/
+ (void)requestUserConfirmationForLocalContactsSyncInViewController:(UIViewController* _Nonnull)viewController
completionHandler:(void (^_Nonnull)(BOOL granted))handler;
@end
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,30 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <UIKit/UIKit.h>
#import "MXKContactField.h"
@interface MXKEmail : MXKContactField
// email info (the address is stored in lowercase)
@property (nonatomic, readonly) NSString *type;
@property (nonatomic, readonly) NSString *emailAddress;
- (id)initWithEmailAddress:(NSString*)anEmailAddress type:(NSString*)aType contactID:(NSString*)aContactID matrixID:(NSString*)matrixID;
- (BOOL)matchedWithPatterns:(NSArray*)patterns;
@end
@@ -0,0 +1,91 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2019 The Matrix.org Foundation C.I.C
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "MXKEmail.h"
@implementation MXKEmail
- (id)init
{
self = [super init];
if (self)
{
_emailAddress = nil;
_type = nil;
}
return self;
}
- (id)initWithEmailAddress:(NSString*)anEmailAddress type:(NSString*)aType contactID:(NSString*)aContactID matrixID:(NSString*)matrixID
{
self = [super initWithContactID:aContactID matrixID:matrixID];
if (self)
{
_emailAddress = [anEmailAddress lowercaseString];
_type = aType;
}
return self;
}
- (BOOL)matchedWithPatterns:(NSArray*)patterns
{
// no number -> cannot match
if (_emailAddress.length == 0)
{
return NO;
}
if (patterns.count > 0)
{
for(NSString *pattern in patterns)
{
if ([_emailAddress rangeOfString:pattern options:NSCaseInsensitiveSearch].location == NSNotFound)
{
return NO;
}
}
}
return YES;
}
#pragma mark NSCoding
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self)
{
_type = [coder decodeObjectForKey:@"type"];
_emailAddress = [[coder decodeObjectForKey:@"emailAddress"] lowercaseString];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[super encodeWithCoder:coder];
[coder encodeObject:_type forKey:@"type"];
[coder encodeObject:_emailAddress forKey:@"emailAddress"];
}
@end
@@ -0,0 +1,78 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "MXKContactField.h"
@class NBPhoneNumber;
@interface MXKPhoneNumber : MXKContactField
/**
The phone number information
*/
@property (nonatomic, readonly) NSString *type;
@property (nonatomic, readonly) NSString *textNumber;
@property (nonatomic, readonly) NSString *cleanedPhonenumber;
/**
When the number is considered to be a possible number. We expose here
the corresponding NBPhoneNumber instance. Use the NBPhoneNumberUtil interface
to format this phone number, or check whether the number is actually a
valid number.
*/
@property (nonatomic, readonly) NBPhoneNumber* nbPhoneNumber;
/**
The default ISO 3166-1 country code used to parse the text number,
and create the nbPhoneNumber instance.
*/
@property (nonatomic) NSString *defaultCountryCode;
/**
The Mobile Station International Subscriber Directory Number.
Available when the nbPhoneNumber is not nil.
*/
@property (nonatomic, readonly) NSString *msisdn;
/**
Create a new MXKPhoneNumber instance
@param textNumber the phone number
@param type the phone number type
@param contactID The identifier of the contact to whom the data belongs to.
@param matrixID The linked matrix identifier if any.
*/
- (id)initWithTextNumber:(NSString*)textNumber type:(NSString*)type contactID:(NSString*)contactID matrixID:(NSString*)matrixID;
/**
Return YES when all the provided patterns are found in the phone number or its msisdn.
@param patterns an array of patterns (The potential "+" (or "00") prefix is ignored during the msisdn handling).
*/
- (BOOL)matchedWithPatterns:(NSArray*)patterns;
/**
Tell whether the phone number or its msisdn has the provided prefix.
@param prefix a non empty string (The potential "+" (or "00") prefix is ignored during the msisdn handling).
@return YES when the phone number or its msisdn has the provided prefix.
*/
- (BOOL)hasPrefix:(NSString*)prefix;
@end
@@ -0,0 +1,213 @@
/*
Copyright 2015 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "MXKPhoneNumber.h"
@import libPhoneNumber_iOS;
@implementation MXKPhoneNumber
@synthesize msisdn;
- (id)initWithTextNumber:(NSString*)textNumber type:(NSString*)type contactID:(NSString*)contactID matrixID:(NSString*)matrixID
{
self = [super initWithContactID:contactID matrixID:matrixID];
if (self)
{
_type = type ? type : @"";
_textNumber = textNumber ? textNumber : @"" ;
_cleanedPhonenumber = [MXKPhoneNumber cleanPhonenumber:_textNumber];
_defaultCountryCode = nil;
msisdn = nil;
_nbPhoneNumber = [[NBPhoneNumberUtil sharedInstance] parse:_cleanedPhonenumber defaultRegion:nil error:nil];
}
return self;
}
// remove the unuseful characters in a phonenumber
+ (NSString*)cleanPhonenumber:(NSString*)phoneNumber
{
// sanity check
if (nil == phoneNumber)
{
return nil;
}
// empty string
if (0 == [phoneNumber length])
{
return @"";
}
static NSCharacterSet *invertedPhoneCharSet = nil;
if (!invertedPhoneCharSet)
{
invertedPhoneCharSet = [[NSCharacterSet characterSetWithCharactersInString:@"0123456789+*#,()"] invertedSet];
}
return [[phoneNumber componentsSeparatedByCharactersInSet:invertedPhoneCharSet] componentsJoinedByString:@""];
}
- (BOOL)matchedWithPatterns:(NSArray*)patterns
{
// no number -> cannot match
if (_textNumber.length == 0)
{
return NO;
}
if (patterns.count > 0)
{
for (NSString *pattern in patterns)
{
if ([_textNumber rangeOfString:pattern].location == NSNotFound)
{
NSString *cleanPattern = [[pattern componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] componentsJoinedByString:@""];
if ([_cleanedPhonenumber rangeOfString:cleanPattern].location == NSNotFound)
{
NSString *msisdnPattern;
if ([cleanPattern hasPrefix:@"+"])
{
msisdnPattern = [cleanPattern substringFromIndex:1];
}
else if ([cleanPattern hasPrefix:@"00"])
{
msisdnPattern = [cleanPattern substringFromIndex:2];
}
else
{
msisdnPattern = cleanPattern;
}
// Check the msisdn
if (!self.msisdn || !msisdnPattern.length || [self.msisdn rangeOfString:msisdnPattern].location == NSNotFound)
{
return NO;
}
}
}
}
}
return YES;
}
- (BOOL)hasPrefix:(NSString*)prefix
{
// no number -> cannot match
if (_textNumber.length == 0)
{
return NO;
}
if ([_textNumber hasPrefix:prefix])
{
return YES;
}
// Remove whitespace before checking the cleaned phone number
prefix = [[prefix componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] componentsJoinedByString:@""];
if ([_cleanedPhonenumber hasPrefix:prefix])
{
return YES;
}
if (self.msisdn)
{
if ([prefix hasPrefix:@"+"])
{
prefix = [prefix substringFromIndex:1];
}
else if ([prefix hasPrefix:@"00"])
{
prefix = [prefix substringFromIndex:2];
}
return [self.msisdn hasPrefix:prefix];
}
return NO;
}
- (void)setDefaultCountryCode:(NSString *)defaultCountryCode
{
if (![defaultCountryCode isEqualToString:_defaultCountryCode])
{
_nbPhoneNumber = [[NBPhoneNumberUtil sharedInstance] parse:_cleanedPhonenumber defaultRegion:defaultCountryCode error:nil];
_defaultCountryCode = defaultCountryCode;
msisdn = nil;
}
}
- (NSString*)msisdn
{
if (!msisdn && _nbPhoneNumber)
{
NSString *e164 = [[NBPhoneNumberUtil sharedInstance] format:_nbPhoneNumber numberFormat:NBEPhoneNumberFormatE164 error:nil];
if ([e164 hasPrefix:@"+"])
{
msisdn = [e164 substringFromIndex:1];
}
else if ([e164 hasPrefix:@"00"])
{
msisdn = [e164 substringFromIndex:2];
}
}
return msisdn;
}
#pragma mark NSCoding
- (id)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self)
{
_type = [coder decodeObjectForKey:@"type"];
_textNumber = [coder decodeObjectForKey:@"textNumber"];
_cleanedPhonenumber = [coder decodeObjectForKey:@"cleanedPhonenumber"];
_defaultCountryCode = [coder decodeObjectForKey:@"countryCode"];
_nbPhoneNumber = [[NBPhoneNumberUtil sharedInstance] parse:_cleanedPhonenumber defaultRegion:_defaultCountryCode error:nil];
msisdn = nil;
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[super encodeWithCoder:coder];
[coder encodeObject:_type forKey:@"type"];
[coder encodeObject:_textNumber forKey:@"textNumber"];
[coder encodeObject:_cleanedPhonenumber forKey:@"cleanedPhonenumber"];
[coder encodeObject:_defaultCountryCode forKey:@"countryCode"];
}
@end
@@ -0,0 +1,33 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <Foundation/Foundation.h>
#import "MXKContact.h"
@interface MXKSectionedContacts : NSObject {
int contactsCount;
NSArray<NSString*> *sectionTitles;
NSArray<NSArray<MXKContact*>*> *sectionedContacts;
}
@property (nonatomic, readonly) int contactsCount;
@property (nonatomic, readonly) NSArray<NSString*> *sectionTitles;
@property (nonatomic, readonly) NSArray<NSArray<MXKContact*>*> *sectionedContacts;
- (instancetype)initWithContacts:(NSArray<NSArray<MXKContact*>*> *)inSectionedContacts andTitles:(NSArray<NSString*> *)titles andCount:(int)count;
@end
@@ -0,0 +1,32 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "MXKSectionedContacts.h"
@implementation MXKSectionedContacts
@synthesize contactsCount, sectionTitles, sectionedContacts;
-(id)initWithContacts:(NSArray<NSArray<MXKContact*> *> *)inSectionedContacts andTitles:(NSArray<NSString *> *)titles andCount:(int)count {
if (self = [super init]) {
contactsCount = count;
sectionedContacts = inSectionedContacts;
sectionTitles = titles;
}
return self;
}
@end