Merge branch 'dev_phonenumber'

This commit is contained in:
Gautier Pelloux-Prayer 2016-08-03 16:33:57 +02:00
commit 8a0a5e910a
33 changed files with 3702 additions and 2089 deletions

View file

@ -21,6 +21,7 @@
#import <XMLRPCConnectionDelegate.h>
#import "UICompositeView.h"
#import "TPKeyboardAvoidingScrollView.h"
#import "PhoneMainView.h"
@interface AssistantView : UIViewController <UITextFieldDelegate, UICompositeViewDelegate> {
@private
@ -30,6 +31,8 @@
NSMutableArray *historyViews;
LinphoneProxyConfig *new_config;
size_t number_of_configs_before;
BOOL mustRestoreView;
long phone_number_length;
}
@property(nonatomic, strong) IBOutlet TPKeyboardAvoidingScrollView *contentView;
@ -38,11 +41,12 @@
@property(nonatomic, strong) IBOutlet UIView *welcomeView;
@property(nonatomic, strong) IBOutlet UIView *createAccountView;
@property(nonatomic, strong) IBOutlet UIView *createAccountActivationView;
@property(nonatomic, strong) IBOutlet UIView *createAccountActivateEmailView;
@property(nonatomic, strong) IBOutlet UIView *linphoneLoginView;
@property(nonatomic, strong) IBOutlet UIView *loginView;
@property(nonatomic, strong) IBOutlet UIView *remoteProvisioningLoginView;
@property(strong, nonatomic) IBOutlet UIView *remoteProvisioningView;
@property (strong, nonatomic) IBOutlet UIView *createAccountActivateSMSView;
@property(nonatomic, strong) IBOutlet UIImageView *welcomeLogoImage;
@property(nonatomic, strong) IBOutlet UIButton *gotoCreateAccountButton;
@ -50,6 +54,10 @@
@property(nonatomic, strong) IBOutlet UIButton *gotoLoginButton;
@property(nonatomic, strong) IBOutlet UIButton *gotoRemoteProvisioningButton;
@property (weak, nonatomic) IBOutlet UILabel *accountLabel;
@property (weak, nonatomic) IBOutlet UIButton *countryButton;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *createAccountNextButtonPositionConstraint;
+ (NSString *)errorForStatus:(LinphoneAccountCreatorStatus)status;
- (void)reset;
@ -69,5 +77,11 @@
- (IBAction)onLoginClick:(id)sender;
- (IBAction)onRemoteProvisioningLoginClick:(id)sender;
- (IBAction)onRemoteProvisioningDownloadClick:(id)sender;
- (IBAction)onCreateAccountCheckActivatedClick:(id)sender;
- (IBAction)onFormSwitchToggle:(id)sender;
- (IBAction)onCountryCodeClick:(id)sender;
- (IBAction)onCountryCodeFieldChange:(id)sender;
- (IBAction)onCountryCodeFieldEnd:(id)sender;
- (IBAction)onPhoneNumberDisclosureClick:(id)sender;
@end

View file

@ -18,11 +18,16 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import <CoreTelephony/CTCarrier.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import "AssistantView.h"
#import "LinphoneManager.h"
#import "PhoneMainView.h"
#import "UITextField+DoneButton.h"
#import "UIAssistantTextField.h"
#import "CountryListViewController.h"
#import <XMLRPCConnection.h>
#import <XMLRPCConnectionManager.h>
@ -37,10 +42,16 @@ typedef enum _ViewElement {
ViewElement_Domain = 104,
ViewElement_URL = 105,
ViewElement_DisplayName = 106,
ViewElement_TextFieldCount = 7,
ViewElement_Phone = 107,
ViewElement_SMSCode = 108,
ViewElement_PhoneCC = 109,
ViewElement_TextFieldCount = ViewElement_PhoneCC - 100 + 1,
ViewElement_Transport = 110,
ViewElement_Username_Label = 120,
ViewElement_NextButton = 130,
ViewElement_UsernameFormView = 181,
ViewElement_EmailFormView = 182,
} ViewElement;
@implementation AssistantView
@ -53,6 +64,7 @@ typedef enum _ViewElement {
[[NSBundle mainBundle] loadNibNamed:@"AssistantViewScreens" owner:self options:nil];
historyViews = [[NSMutableArray alloc] init];
currentView = nil;
mustRestoreView = NO;
}
return self;
}
@ -82,8 +94,17 @@ static UICompositeViewDescription *compositeDescription = nil;
#pragma mark - ViewController Functions
- (void)viewDidLoad {
[super viewDidLoad];
_countryButton.layer.borderWidth = .8;
_countryButton.layer.borderColor = _countryButton.backgroundColor.CGColor;
_countryButton.layer.cornerRadius = 4.f;
_countryButton.layer.masksToBounds = YES;
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(registrationUpdateEvent:)
name:kLinphoneRegistrationUpdate
@ -93,10 +114,13 @@ static UICompositeViewDescription *compositeDescription = nil;
name:kLinphoneConfiguringStateUpdate
object:nil];
new_config = NULL;
[self resetTextFields];
[self changeView:_welcomeView back:FALSE animation:FALSE];
number_of_configs_before = bctbx_list_size(linphone_core_get_proxy_config_list(LC));
if (!mustRestoreView) {
new_config = NULL;
number_of_configs_before = bctbx_list_size(linphone_core_get_proxy_config_list(LC));
[self resetTextFields];
[self changeView:_welcomeView back:FALSE animation:FALSE];
}
mustRestoreView = NO;
}
- (void)viewWillDisappear:(BOOL)animated {
@ -130,12 +154,20 @@ static UICompositeViewDescription *compositeDescription = nil;
LC, [LinphoneManager.instance lpConfigStringForKey:@"xmlrpc_url" inSection:@"assistant" withDefault:@""]
.UTF8String);
linphone_account_creator_set_user_data(account_creator, (__bridge void *)(self));
linphone_account_creator_cbs_set_existence_tested(linphone_account_creator_get_callbacks(account_creator),
assistant_existence_tested);
linphone_account_creator_cbs_set_is_account_used(linphone_account_creator_get_callbacks(account_creator),
assistant_is_account_used);
linphone_account_creator_cbs_set_create_account(linphone_account_creator_get_callbacks(account_creator),
assistant_create_account);
linphone_account_creator_cbs_set_validation_tested(linphone_account_creator_get_callbacks(account_creator),
assistant_validation_tested);
linphone_account_creator_cbs_set_activate_account(linphone_account_creator_get_callbacks(account_creator),
assistant_activate_account);
linphone_account_creator_cbs_set_is_account_activated(linphone_account_creator_get_callbacks(account_creator),
assistant_is_account_activated);
linphone_account_creator_cbs_set_link_phone_number_with_account(linphone_account_creator_get_callbacks(account_creator),
assistant_link_phone_number_with_account);
linphone_account_creator_cbs_set_activate_phone_number_link(linphone_account_creator_get_callbacks(account_creator),
assistant_activate_phone_number_link);
}
- (void)loadAssistantConfig:(NSString *)rcFilename {
NSString *fullPath = [@"file://" stringByAppendingString:[LinphoneManager bundleFile:rcFilename]];
@ -173,6 +205,11 @@ static UICompositeViewDescription *compositeDescription = nil;
case LinphoneAccountCreatorUsernameInvalidSize:
return usePhoneNumber ? NSLocalizedString(@"Phone number length invalid.", nil)
: NSLocalizedString(@"Username length invalid.", nil);
case LinphoneAccountCreatorPhoneNumberTooShort:
case LinphoneAccountCreatorPhoneNumberTooLong:
return nil; /* this is not an error, just user has to finish typing */
case LinphoneAccountCreatorPhoneNumberInvalid:
return NSLocalizedString(@"Invalid phone number.", nil);
case LinphoneAccountCreatorPasswordTooShort:
return NSLocalizedString(@"Password too short.", nil);
case LinphoneAccountCreatorPasswordTooLong:
@ -191,8 +228,9 @@ static UICompositeViewDescription *compositeDescription = nil;
case LinphoneAccountCreatorAccountExist:
case LinphoneAccountCreatorAccountNotCreated:
case LinphoneAccountCreatorAccountNotExist:
case LinphoneAccountCreatorAccountNotValidated:
case LinphoneAccountCreatorAccountValidated:
case LinphoneAccountCreatorAccountNotActivated:
case LinphoneAccountCreatorAccountAlreadyActivated:
case LinphoneAccountCreatorAccountActivated:
case LinphoneAccountCreatorOK:
break;
}
@ -336,10 +374,9 @@ static UICompositeViewDescription *compositeDescription = nil;
currentView = view;
[_contentView insertSubview:currentView atIndex:0];
[_contentView setContentOffset:CGPointMake(0, -_contentView.contentInset.top) animated:NO];
[self fitContent];
// Resize next button to fix text length
UIButton *button = [self findButton:ViewElement_NextButton];
UIRoundBorderedButton *button = [self findButton:ViewElement_NextButton];
CGSize size = [button.titleLabel.text sizeWithFont:button.titleLabel.font];
size.width += 60;
CGRect frame = button.frame;
@ -347,7 +384,38 @@ static UICompositeViewDescription *compositeDescription = nil;
frame.size.width = size.width;
[button setFrame:frame];
[self fitContent];
// also force next button alignement on create account page
if (currentView == _createAccountView) {
CTTelephonyNetworkInfo *networkInfo = [CTTelephonyNetworkInfo new];
CTCarrier *carrier = networkInfo.subscriberCellularProvider;
NSDictionary *country = [CountryListViewController countryWithIso:carrier.isoCountryCode];
if (!IPAD) {
UISwitch *emailSwitch = (UISwitch *)[self findView:ViewElement_EmailFormView inView:self.contentView ofType:UISwitch.class];
UILabel *emailLabel = (UILabel *)[self findView:ViewElement_EmailFormView inView:self.contentView ofType:UILabel.class];
emailSwitch.hidden = emailLabel.hidden = YES;
}
if (!country) {
//fetch phone locale
for (NSString* lang in [NSLocale preferredLanguages]) {
NSUInteger idx2 = [lang rangeOfString:@"-"].location;
if (idx2 == NSNotFound) idx2 = 0;
if ((country = [CountryListViewController countryWithIso:[lang substringFromIndex:idx2]]) != nil)
break;
}
}
if (country) {
[self didSelectCountry:country];
}
[self onFormSwitchToggle:nil];
}
[self prepareErrorLabels];
}
- (void)fillDefaultValues {
@ -380,7 +448,8 @@ static UICompositeViewDescription *compositeDescription = nil;
_createAccountView,
_linphoneLoginView,
_loginView,
_createAccountActivationView,
_createAccountActivateEmailView,
_createAccountActivateSMSView,
_remoteProvisioningLoginView
]) {
[AssistantView cleanTextField:view];
@ -390,6 +459,7 @@ static UICompositeViewDescription *compositeDescription = nil;
atf.text = @"test.linphone.org";
#endif
}
phone_number_length = 0;
}
- (void)displayUsernameAsPhoneOrUsername {
@ -418,20 +488,9 @@ static UICompositeViewDescription *compositeDescription = nil;
}
}
- (void)shouldEnableNextButton {
BOOL invalidInputs = NO;
for (int i = 0; i < ViewElement_TextFieldCount; i++) {
UIAssistantTextField *field = [self findTextField:100 + i];
if (field) {
invalidInputs |= field.isInvalid;
}
}
[self findButton:ViewElement_NextButton].enabled = !invalidInputs;
}
- (UIView *)findView:(ViewElement)tag inView:view ofType:(Class)type {
for (UIView *child in [view subviews]) {
if (child.tag == tag) {
if (child.tag == tag && child.class == type) {
return child;
} else {
UIView *o = [self findView:tag inView:child ofType:type];
@ -446,8 +505,8 @@ static UICompositeViewDescription *compositeDescription = nil;
return (UIAssistantTextField *)[self findView:tag inView:self.contentView ofType:[UIAssistantTextField class]];
}
- (UIButton *)findButton:(ViewElement)tag {
return (UIButton *)[self findView:tag inView:self.contentView ofType:[UIButton class]];
- (UIRoundBorderedButton *)findButton:(ViewElement)tag {
return (UIRoundBorderedButton *)[self findView:tag inView:self.contentView ofType:[UIRoundBorderedButton class]];
}
- (UILabel *)findLabel:(ViewElement)tag {
@ -460,15 +519,35 @@ static UICompositeViewDescription *compositeDescription = nil;
when:^BOOL(NSString *inputEntry) {
LinphoneAccountCreatorStatus s =
linphone_account_creator_set_username(account_creator, inputEntry.UTF8String);
if (s != LinphoneAccountCreatorOK) linphone_account_creator_set_username(account_creator, NULL);
createUsername.errorLabel.text = [AssistantView errorForStatus:s];
return s != LinphoneAccountCreatorOK;
}];
UIAssistantTextField *createPhone = [self findTextField:ViewElement_Phone];
[createPhone showError:[AssistantView errorForStatus:LinphoneAccountCreatorPhoneNumberInvalid]
when:^BOOL(NSString *inputEntry) {
UIAssistantTextField* countryCodeField = [self findTextField:ViewElement_PhoneCC];
NSString* prefix = countryCodeField.text.length > 0 ? [countryCodeField.text substringFromIndex:1] : nil;
LinphoneAccountCreatorStatus s =
linphone_account_creator_set_phone_number(account_creator, inputEntry.length > 0 ? inputEntry.UTF8String : NULL, prefix.UTF8String);
if (s != LinphoneAccountCreatorOK) linphone_account_creator_set_phone_number(account_creator, NULL, NULL);
// if phone is empty and username is empty, this is wrong
if (linphone_account_creator_get_phone_number(account_creator) == NULL) {
s = LinphoneAccountCreatorPhoneNumberTooShort;
}
createPhone.errorLabel.text = [AssistantView errorForStatus:s];
return s != LinphoneAccountCreatorOK;
}];
UIAssistantTextField *password = [self findTextField:ViewElement_Password];
[password showError:[AssistantView errorForStatus:LinphoneAccountCreatorPasswordTooShort]
when:^BOOL(NSString *inputEntry) {
LinphoneAccountCreatorStatus s =
linphone_account_creator_set_password(account_creator, inputEntry.UTF8String);
linphone_account_creator_set_password(account_creator, inputEntry.UTF8String);
password.errorLabel.text = [AssistantView errorForStatus:s];
return s != LinphoneAccountCreatorOK;
}];
@ -521,9 +600,27 @@ static UICompositeViewDescription *compositeDescription = nil;
return s != LinphoneAccountCreatorOK;
}];
UIAssistantTextField *smsCode = [self findTextField:ViewElement_SMSCode];
[smsCode showError:nil when:^BOOL(NSString *inputEntry) {
return inputEntry.length != 4;
}];
[self shouldEnableNextButton];
}
- (void)shouldEnableNextButton {
BOOL invalidInputs = NO;
for (int i = 0; !invalidInputs && i < ViewElement_TextFieldCount; i++) {
ViewElement ve = (ViewElement)100+i;
if ([self findTextField:ve].isInvalid) {
invalidInputs = YES;
break;
}
}
[self findButton:ViewElement_NextButton].enabled = !invalidInputs;
}
#pragma mark - Event Functions
- (void)registrationUpdateEvent:(NSNotification *)notif {
@ -612,16 +709,29 @@ static UICompositeViewDescription *compositeDescription = nil;
}
}
- (void)genericError {
UIAlertView *errorView = [[UIAlertView alloc]
initWithTitle:NSLocalizedString(@"Account creation issue", nil)
message:NSLocalizedString(@"Your account could not be created, please try again later.", nil)
delegate:nil
cancelButtonTitle:NSLocalizedString(@"Continue", nil)
otherButtonTitles:nil, nil];
[errorView show];
}
#pragma mark - Account creator callbacks
void assistant_existence_tested(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status) {
void assistant_is_account_used(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status) {
AssistantView *thiz = (__bridge AssistantView *)(linphone_account_creator_get_user_data(creator));
thiz.waitView.hidden = YES;
if (status == LinphoneAccountCreatorAccountExist) {
[[thiz findTextField:ViewElement_Username] showError:NSLocalizedString(@"This name is already taken.", nil)];
ViewElement ve = ([thiz findTextField:ViewElement_Username].isVisible) ? ViewElement_Username : ViewElement_Phone;
[[thiz findTextField:ve] showError:NSLocalizedString(@"This account already exists.", nil)];
[thiz findButton:ViewElement_NextButton].enabled = NO;
} else if (status == LinphoneAccountCreatorAccountNotExist) {
linphone_account_creator_create_account(thiz->account_creator);
} else {
[thiz genericError];
}
}
@ -629,24 +739,43 @@ void assistant_create_account(LinphoneAccountCreator *creator, LinphoneAccountCr
AssistantView *thiz = (__bridge AssistantView *)(linphone_account_creator_get_user_data(creator));
thiz.waitView.hidden = YES;
if (status == LinphoneAccountCreatorAccountCreated) {
[thiz changeView:thiz.createAccountActivationView back:FALSE animation:TRUE];
if (linphone_account_creator_get_phone_number(creator)) {
[thiz changeView:thiz.createAccountActivateSMSView back:FALSE animation:TRUE];
} else {
[thiz changeView:thiz.createAccountActivateEmailView back:FALSE animation:TRUE];
}
} else {
UIAlertView *errorView = [[UIAlertView alloc]
initWithTitle:NSLocalizedString(@"Account creation issue", nil)
message:NSLocalizedString(@"Your account could not be created, please try again later.", nil)
delegate:nil
cancelButtonTitle:NSLocalizedString(@"Continue", nil)
otherButtonTitles:nil, nil];
[errorView show];
[thiz genericError];
}
}
void assistant_validation_tested(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status) {
void assistant_activate_account(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status) {
AssistantView *thiz = (__bridge AssistantView *)(linphone_account_creator_get_user_data(creator));
thiz.waitView.hidden = YES;
if (status == LinphoneAccountCreatorAccountValidated) {
if (status == LinphoneAccountCreatorAccountActivated) {
[thiz configureProxyConfig];
} else if (status == LinphoneAccountCreatorAccountNotValidated) {
} else if (status == LinphoneAccountCreatorAccountNotActivated) {
UIAlertView *errorView = [[UIAlertView alloc]
initWithTitle:NSLocalizedString(@"Account activation issue", nil)
message:NSLocalizedString(@"Your account could not be activated, please check SMS code.", nil)
delegate:nil
cancelButtonTitle:NSLocalizedString(@"Continue", nil)
otherButtonTitles:nil, nil];
[errorView show];
} else if (status == LinphoneAccountCreatorAccountAlreadyActivated) {
// in case we are actually trying to link account, let's try it now
linphone_account_creator_activate_phone_number_link(creator);
} else {
[thiz genericError];
}
}
void assistant_is_account_activated(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status) {
AssistantView *thiz = (__bridge AssistantView *)(linphone_account_creator_get_user_data(creator));
thiz.waitView.hidden = YES;
if (status == LinphoneAccountCreatorAccountActivated) {
[thiz configureProxyConfig];
} else if (status == LinphoneAccountCreatorAccountNotActivated) {
DTAlertView *alert = [[DTAlertView alloc]
initWithTitle:NSLocalizedString(@"Account validation failed", nil)
message:
@ -660,11 +789,50 @@ void assistant_validation_tested(LinphoneAccountCreator *creator, LinphoneAccoun
[PhoneMainView.instance popToView:DialerView.compositeViewDescription];
}];
[alert show];
} else {
[thiz genericError];
}
}
void assistant_link_phone_number_with_account(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status) {
AssistantView *thiz = (__bridge AssistantView *)(linphone_account_creator_get_user_data(creator));
thiz.waitView.hidden = YES;
if (status == LinphoneAccountCreatorOK) {
[thiz changeView:thiz.createAccountActivateSMSView back:FALSE animation:TRUE];
} else {
UIAlertView *errorView = [[UIAlertView alloc]
initWithTitle:NSLocalizedString(@"Account link issue", nil)
message:NSLocalizedString(@"Could not link your phone number with your account, please try again later.", nil)
delegate:nil
cancelButtonTitle:NSLocalizedString(@"Continue", nil)
otherButtonTitles:nil, nil];
[errorView show];
}
}
void assistant_activate_phone_number_link(LinphoneAccountCreator *creator, LinphoneAccountCreatorStatus status) {
AssistantView *thiz = (__bridge AssistantView *)(linphone_account_creator_get_user_data(creator));
thiz.waitView.hidden = YES;
if (status == LinphoneAccountCreatorOK) {
[thiz configureProxyConfig];
} else {
UIAlertView *errorView = [[UIAlertView alloc]
initWithTitle:NSLocalizedString(@"Account link issue", nil)
message:NSLocalizedString(@"Could not link your phone number with your account, please try again later.", nil)
delegate:nil
cancelButtonTitle:NSLocalizedString(@"Continue", nil)
otherButtonTitles:nil, nil];
[errorView show];
}
}
#pragma mark - UITextFieldDelegate Functions
- (void)textFieldDidBeginEditing:(UITextField *)textField {
UIAssistantTextField *atf = (UIAssistantTextField *)textField;
[atf textFieldDidBeginEditing:atf];
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
UIAssistantTextField *atf = (UIAssistantTextField *)textField;
[atf textFieldDidEndEditing:atf];
@ -672,18 +840,10 @@ void assistant_validation_tested(LinphoneAccountCreator *creator, LinphoneAccoun
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField {
UIAssistantTextField *atf = (UIAssistantTextField *)textField;
[textField resignFirstResponder];
if (textField.returnKeyType == UIReturnKeyNext) {
// text fields must be ordored by increasing tag value
NSInteger tag = textField.tag + 1;
while (tag < ViewElement_NextButton) {
UIView *v = [self.view viewWithTag:tag];
if ([v isKindOfClass:UITextField.class]) {
[v becomeFirstResponder];
break;
}
tag++;
}
[atf.nextFieldResponder becomeFirstResponder];
} else if (textField.returnKeyType == UIReturnKeyDone) {
[[self findButton:ViewElement_NextButton] sendActionsForControlEvents:UIControlEventTouchUpInside];
}
@ -693,74 +853,211 @@ void assistant_validation_tested(LinphoneAccountCreator *creator, LinphoneAccoun
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {
UIAssistantTextField *atf = (UIAssistantTextField *)textField;
BOOL replace = YES;
// if we are hitting backspace on secure entry, this will clear all text
if ([string isEqual:@""] && textField.isSecureTextEntry) {
range = NSMakeRange(0, atf.text.length);
if (textField.tag == ViewElement_SMSCode) {
// max 4 length
return range.location + range.length <= 4;
} else {
UIAssistantTextField *atf = (UIAssistantTextField *)textField;
BOOL replace = YES;
// if we are hitting backspace on secure entry, this will clear all text
if ([string isEqual:@""] && textField.isSecureTextEntry) {
range = NSMakeRange(0, atf.text.length);
}
[atf textField:atf shouldChangeCharactersInRange:range replacementString:string];
if (atf.tag == ViewElement_Username && currentView == _createAccountView) {
atf.text = [atf.text stringByReplacingCharactersInRange:range withString:string.lowercaseString];
replace = NO;
}
if (textField.tag == ViewElement_Phone || textField.tag == ViewElement_Username) {
[self refreshYourUsername];
}
[self shouldEnableNextButton];
return replace;
}
[atf textField:atf shouldChangeCharactersInRange:range replacementString:string];
if (atf.tag == ViewElement_Username && currentView == _createAccountView) {
atf.text = [atf.text stringByReplacingCharactersInRange:range withString:string.lowercaseString];
replace = NO;
}
[self shouldEnableNextButton];
return replace;
}
// Change button color and wait the display of this
#define ONCLICKBUTTON(button, timewaitmsec, body) \
[button setBackgroundColor:[UIColor lightGrayColor]]; \
_waitView.hidden = NO; \
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (timewaitmsec * NSEC_PER_MSEC)); \
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ \
body \
[button setBackgroundColor:[UIColor clearColor]]; \
_waitView.hidden = YES; \
}); \
#pragma mark - Action Functions
- (IBAction)onGotoCreateAccountClick:(id)sender {
nextView = _createAccountView;
[self loadAssistantConfig:@"assistant_linphone_create.rc"];
ONCLICKBUTTON(sender, 100, {
nextView = _createAccountView;
[self loadAssistantConfig:@"assistant_linphone_create.rc"];
});
}
- (IBAction)onGotoLinphoneLoginClick:(id)sender {
nextView = _linphoneLoginView;
[self loadAssistantConfig:@"assistant_linphone_existing.rc"];
ONCLICKBUTTON(sender, 100, {
nextView = _linphoneLoginView;
[self loadAssistantConfig:@"assistant_linphone_existing.rc"];
});
}
- (IBAction)onGotoLoginClick:(id)sender {
nextView = _loginView;
[self loadAssistantConfig:@"assistant_external_sip.rc"];
ONCLICKBUTTON(sender, 100, {
nextView = _loginView;
[self loadAssistantConfig:@"assistant_external_sip.rc"];
});
}
- (IBAction)onGotoRemoteProvisioningClick:(id)sender {
nextView = _remoteProvisioningView;
[self loadAssistantConfig:@"assistant_remote.rc"];
[self findTextField:ViewElement_URL].text =
[LinphoneManager.instance lpConfigStringForKey:@"config-uri" inSection:@"misc"];
ONCLICKBUTTON(sender, 100, {
nextView = _remoteProvisioningView;
[self loadAssistantConfig:@"assistant_remote.rc"];
[self findTextField:ViewElement_URL].text =
[LinphoneManager.instance lpConfigStringForKey:@"config-uri" inSection:@"misc"];
});
}
- (IBAction)onCreateAccountClick:(id)sender {
_waitView.hidden = NO;
linphone_account_creator_test_existence(account_creator);
ONCLICKBUTTON(sender, 100, {
_waitView.hidden = NO;
linphone_account_creator_is_account_used(account_creator);
});
}
- (IBAction)onCreateAccountActivationClick:(id)sender {
_waitView.hidden = NO;
linphone_account_creator_test_validation(account_creator);
ONCLICKBUTTON(sender, 100, {
_waitView.hidden = NO;
linphone_account_creator_set_activation_code(account_creator, ((UITextField*)[self findView:ViewElement_SMSCode inView:_contentView ofType:UITextField.class]).text.UTF8String);
linphone_account_creator_activate_account(account_creator);
});
}
- (IBAction)onCreateAccountCheckActivatedClick:(id)sender {
ONCLICKBUTTON(sender, 100, {
_waitView.hidden = NO;
linphone_account_creator_is_account_activated(account_creator);
});
}
- (IBAction)onLinphoneLoginClick:(id)sender {
_waitView.hidden = NO;
[self configureProxyConfig];
ONCLICKBUTTON(sender, 100, {
_waitView.hidden = NO;
NSString *phone = [self findTextField:ViewElement_Phone].text;
if (phone.length > 0) {
linphone_account_creator_link_phone_number_with_account(account_creator);
} else {
[self configureProxyConfig];
}
});
}
- (IBAction)onLoginClick:(id)sender {
_waitView.hidden = NO;
[self configureProxyConfig];
ONCLICKBUTTON(sender, 100, {
_waitView.hidden = NO;
[self configureProxyConfig];
});
}
- (IBAction)onRemoteProvisioningLoginClick:(id)sender {
_waitView.hidden = NO;
[LinphoneManager.instance lpConfigSetInt:1 forKey:@"transient_provisioning" inSection:@"misc"];
[self configureProxyConfig];
ONCLICKBUTTON(sender, 100, {
_waitView.hidden = NO;
[LinphoneManager.instance lpConfigSetInt:1 forKey:@"transient_provisioning" inSection:@"misc"];
[self configureProxyConfig];
});
}
- (IBAction)onRemoteProvisioningDownloadClick:(id)sender {
[_waitView setHidden:false];
[self resetLiblinphone];
ONCLICKBUTTON(sender, 100, {
[_waitView setHidden:false];
[self resetLiblinphone];
});
}
- (void)refreshYourUsername {
UIAssistantTextField *username = [self findTextField:ViewElement_Username];
UIAssistantTextField *phone = [self findTextField:ViewElement_Phone];
const char* uri = NULL;
if (!username.superview.hidden) {
uri = linphone_account_creator_get_username(account_creator);
} else if (!phone.superview.hidden) {
uri = linphone_account_creator_get_phone_number(account_creator);
}
if (uri) {
_accountLabel.text = [NSString stringWithFormat:NSLocalizedString(@"Your SIP address will be sip:%s@sip.linphone.org", nil), uri];
} else if (!username.superview.hidden) {
_accountLabel.text = NSLocalizedString(@"Please enter your username", nil);
} else {
_accountLabel.text = NSLocalizedString(@"Please enter your phone number", nil);
}
}
- (IBAction)onFormSwitchToggle:(UISwitch*)sender {
UISwitch *usernameSwitch = (UISwitch *)[self findView:ViewElement_UsernameFormView inView:self.contentView ofType:UISwitch.class];
UISwitch *emailSwitch = (UISwitch *)[self findView:ViewElement_EmailFormView inView:self.contentView ofType:UISwitch.class];
UIView * usernameView = [self findView:ViewElement_UsernameFormView inView:self.contentView ofType:UIView.class];
UIView * emailView = [self findView:ViewElement_EmailFormView inView:self.contentView ofType:UIView.class];
usernameView.hidden = !usernameSwitch.isOn && !emailSwitch.isOn;
emailView.hidden = !emailSwitch.isOn;
UIAssistantTextField* countryCodeField = [self findTextField:ViewElement_PhoneCC];
usernameSwitch.enabled = _countryButton.enabled = countryCodeField.enabled = countryCodeField.userInteractionEnabled = [self findTextField:ViewElement_Phone].userInteractionEnabled = [self findTextField:ViewElement_Phone].enabled = !emailSwitch.isOn;
[self refreshYourUsername];
// put next button right after latest field (avoid blanks)
int old = _createAccountNextButtonPositionConstraint.constant;
_createAccountNextButtonPositionConstraint.constant = IPAD || !usernameView.hidden ? 21 : -10;
if (!usernameView.hidden) {
_createAccountNextButtonPositionConstraint.constant += usernameView.frame.size.height;
}
if (!emailView.hidden) {
_createAccountNextButtonPositionConstraint.constant += emailView.frame.size.height;
}
// make view scrollable only if next button is too away
CGRect viewframe = currentView.frame;
viewframe.size.height = 30 + _createAccountNextButtonPositionConstraint.constant - old + [self findButton:ViewElement_NextButton].frame.origin.y + [self findButton:ViewElement_NextButton].frame.size.height;
[_contentView setContentSize:viewframe.size];
[self shouldEnableNextButton];
}
- (IBAction)onCountryCodeClick:(id)sender {
mustRestoreView = YES;
CountryListViewController *view = VIEW(CountryListViewController);
[view setDelegate:(id)self];
[PhoneMainView.instance changeCurrentView:view.compositeViewDescription];
}
- (void)updateCountry:(BOOL)force {
UIAssistantTextField* countryCodeField = [self findTextField:ViewElement_PhoneCC];
NSDictionary *c = [CountryListViewController countryWithCountryCode:countryCodeField.text];
if (c || force) {
[_countryButton setTitle:c ? [c objectForKey:@"name"] : NSLocalizedString(@"Unknown country code", nil) forState:UIControlStateNormal];
}
}
- (IBAction)onCountryCodeFieldChange:(id)sender {
[self updateCountry:NO];
}
- (IBAction)onCountryCodeFieldEnd:(id)sender {
[self updateCountry:YES];
}
- (IBAction)onPhoneNumberDisclosureClick:(id)sender {
UIAlertView *errorView = [[UIAlertView alloc]
initWithTitle:NSLocalizedString(@"What is my phone number for?", nil)
message:NSLocalizedString(@"A SMS code will be sent to your phone number to validate your account.", nil)
delegate:nil
cancelButtonTitle:NSLocalizedString(@"OK", nil)
otherButtonTitles:nil, nil];
[errorView show];
}
- (IBAction)onBackClick:(id)sender {
@ -782,4 +1079,14 @@ void assistant_validation_tested(LinphoneAccountCreator *creator, LinphoneAccoun
}
}
#pragma mark - select country delegate
- (void)didSelectCountry:(NSDictionary *)country{
[_countryButton setTitle:[country objectForKey:@"name"] forState:UIControlStateNormal];
UIAssistantTextField* countryCodeField = [self findTextField:ViewElement_PhoneCC];
countryCodeField.text = countryCodeField.lastText = [country objectForKey:@"code"];
phone_number_length = [[country objectForKey:@"phone_length"] integerValue];
[self shouldEnableNextButton];
}
@end

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="CountryListViewController">
<connections>
<outlet property="searchDisplayController" destination="Fzt-cO-ZZd" id="yWw-yG-tfg"/>
<outlet property="tableView" destination="UcW-gD-iwL" id="878-PR-Gn9"/>
<outlet property="view" destination="1" id="3"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="1">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view tag="2" contentMode="scaleToFill" id="418-8u-hzT" userLabel="topBar">
<rect key="frame" x="0.0" y="0.0" width="375" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView userInteractionEnabled="NO" tag="3" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="color_F.png" id="Rq2-JR-1jE" userLabel="backgroundColor">
<rect key="frame" x="0.0" y="0.0" width="375" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" tag="5" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="SELECT YOUR COUNTRY" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="MLt-gO-M3J" userLabel="titleLabel">
<rect key="frame" x="83" y="0.0" width="209" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<color key="textColor" red="1" green="0.36862745099999999" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" tag="6" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="hOj-Kl-K9W" userLabel="backButton" customClass="UIIconButton">
<rect key="frame" x="0.0" y="0.0" width="75" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" heightSizable="YES"/>
<accessibility key="accessibilityConfiguration" label="Back"/>
<inset key="titleEdgeInsets" minX="0.0" minY="18" maxX="0.0" maxY="0.0"/>
<state key="normal" image="back_default.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<state key="disabled" image="back_disabled.png"/>
<state key="highlighted" backgroundImage="color_E.png"/>
<connections>
<action selector="onCancelClick:" destination="-1" eventType="touchUpInside" id="DTU-Ur-aPJ"/>
</connections>
</button>
</subviews>
</view>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" rowHeight="40" sectionHeaderHeight="22" sectionFooterHeight="22" id="UcW-gD-iwL">
<rect key="frame" x="0.0" y="109" width="375" height="558"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="separatorColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<inset key="separatorInset" minX="15" minY="0.0" maxX="0.0" maxY="0.0"/>
<connections>
<outlet property="dataSource" destination="-1" id="29f-d6-ctV"/>
<outlet property="delegate" destination="-1" id="Dg5-gz-Qgm"/>
</connections>
</tableView>
<searchBar contentMode="redraw" id="Ipz-Nn-2z3">
<rect key="frame" x="0.0" y="65" width="375" height="44"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
<textInputTraits key="textInputTraits"/>
<connections>
<outlet property="delegate" destination="-1" id="iL7-Xf-Ila"/>
</connections>
</searchBar>
</subviews>
<color key="backgroundColor" red="0.94901960784313721" green="0.95294117647058818" blue="0.96078431372549022" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="simulatedStatusBarMetrics"/>
<simulatedScreenMetrics key="simulatedDestinationMetrics" type="retina47"/>
</view>
<searchDisplayController id="Fzt-cO-ZZd">
<connections>
<outlet property="delegate" destination="-1" id="TiS-Oi-O2O"/>
<outlet property="searchBar" destination="Ipz-Nn-2z3" id="MMD-qU-wyt"/>
<outlet property="searchContentsController" destination="-1" id="CXr-Rc-c3m"/>
<outlet property="searchResultsDataSource" destination="-1" id="D0N-dF-fAE"/>
<outlet property="searchResultsDelegate" destination="-1" id="hKg-ZM-V4D"/>
</connections>
</searchDisplayController>
</objects>
<resources>
<image name="back_default.png" width="24" height="21"/>
<image name="back_disabled.png" width="24" height="21"/>
<image name="color_E.png" width="2" height="2"/>
<image name="color_F.png" width="2" height="2"/>
</resources>
</document>

View file

@ -25,6 +25,39 @@
if (_person) {
[self loadProperties];
const char* key = [NSString stringWithFormat:@"ab%d", ABRecordGetRecordID(aperson)].UTF8String;
// try to find friend associated with that person
_friend = linphone_friend_list_find_friend_by_ref_key(linphone_core_get_default_friend_list(LC), key);
if (!_friend) {
_friend = linphone_friend_ref(linphone_core_create_friend(LC));
linphone_friend_set_ref_key(_friend, key);
linphone_friend_set_name(_friend, [NSString stringWithFormat:@"%@ %@", _firstName, _lastName].UTF8String);
for (NSString* sipAddr in _sipAddresses) {
LinphoneAddress* addr = linphone_core_interpret_url(LC, sipAddr.UTF8String);
if (addr) {
linphone_address_set_display_name(addr, [self displayName].UTF8String);
linphone_friend_add_address(_friend, addr);
linphone_address_destroy(addr);
}
}
for (NSString* phone in _phoneNumbers) {
LOGI(@"fixme! use linphone_friend_add_phone_number for phone numbers");
char* normalized_phone = linphone_proxy_config_normalize_phone_number(linphone_core_get_default_proxy_config(LC), phone.UTF8String);
if (normalized_phone) {
LinphoneAddress* addr = linphone_core_interpret_url(LC, normalized_phone);
if (addr) {
linphone_address_set_display_name(addr, [self displayName].UTF8String);
linphone_friend_add_address(_friend, addr);
linphone_address_destroy(addr);
}
ms_free(normalized_phone);
}
// linphone_friend_add_phone_number(_friend, phone.UTF8String);
}
linphone_core_add_friend(LC, _friend);
}
linphone_friend_ref(_friend);
} else if (_friend) {
[self loadFriend];
} else {
@ -59,6 +92,12 @@
}
- (NSString *)displayName {
if (_friend) {
// const char *dp = linphone_address_get_display_name(linphone_friend_get_address(_friend));
//if (dp)
// return [NSString stringWithUTF8String:dp];
}
if (_person != nil) {
NSString *lFirstName = CFBridgingRelease(ABRecordCopyValue(_person, kABPersonFirstNameProperty));
NSString *lLocalizedFirstName = [FastAddressBook localizedLabel:lFirstName];
@ -77,10 +116,6 @@
} else {
return (NSString *)lLocalizedOrganization;
}
} else if (_friend) {
const char *dp = linphone_address_get_display_name(linphone_friend_get_address(_friend));
if (dp)
return [NSString stringWithUTF8String:dp];
}
if (_lastName || _firstName) {

View file

@ -163,8 +163,8 @@
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[_tableController.tableView removeObserver:self forKeyPath:@"contentSize"];
[super viewWillDisappear:animated];
}
#pragma mark - UICompositeViewDelegate Functions

View file

@ -0,0 +1,28 @@
//
// CountryListViewController.h
// Country List
//
// Created by Pradyumna Doddala on 18/12/13.
// Copyright (c) 2013 Pradyumna Doddala. All rights reserved.
//
#import <UIKit/UIKit.h>
#import "PhoneMainView.h"
@protocol CountryListViewDelegate <NSObject,UISearchDisplayDelegate,UISearchBarDelegate>
- (void)didSelectCountry:(NSDictionary *)country;
@end
@interface CountryListViewController : UIViewController<UICompositeViewDelegate>
@property (nonatomic, weak) id<CountryListViewDelegate>delegate;
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope;
- (IBAction)onCancelClick:(id)sender;
+ (NSDictionary *)countryWithIso:(NSString*)iso;
+ (NSDictionary *)countryWithCountryCode:(NSString*)cc;
@end

View file

@ -0,0 +1,153 @@
//
// CountryListViewController.m
// Country List
//
// Created by Pradyumna Doddala on 18/12/13.
// Copyright (c) 2013 Pradyumna Doddala. All rights reserved.
//
#import "CountryListViewController.h"
#import "linphone/linphonecore_utils.h"
@interface CountryListViewController ()
@property (strong, nonatomic) IBOutlet UITableView *tableView;
@property (strong, nonatomic) NSArray *searchResults;
@end
@implementation CountryListViewController
static NSMutableArray * dataRows = nil;
+ (NSArray*) getData {
if (!dataRows) {
dataRows = [[NSMutableArray alloc] init];
for (const LinphoneDialPlan* dial_plan=linphone_dial_plan_get_all(); dial_plan->country!=NULL; dial_plan++) {
[dataRows addObject:@{
@"name":[NSString stringWithUTF8String:dial_plan->country],
@"iso":[NSString stringWithUTF8String:dial_plan->iso_country_code],
@"code":[NSString stringWithFormat:@"+%s",dial_plan->ccc],
@"phone_length":@(dial_plan->nnl)
}];
}
}
return dataRows;
}
#pragma mark - UICompositeViewDelegate Functions
static UICompositeViewDescription *compositeDescription = nil;
+ (UICompositeViewDescription *)compositeViewDescription {
if (compositeDescription == nil) {
compositeDescription = [[UICompositeViewDescription alloc] init:self.class
statusBar:StatusBarView.class
tabBar:nil
sideMenu:SideMenuView.class
fullscreen:false
isLeftFragment:YES
fragmentWith:nil];
}
return compositeDescription;
}
- (UICompositeViewDescription *)compositeViewDescription {
return self.class.compositeViewDescription;
}
#pragma mark - Other
- (void)viewDidLoad {
[super viewDidLoad];
_searchResults = [[NSArray alloc] init];
[_tableView reloadData];
}
#pragma mark - UITableView Datasource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.searchDisplayController.searchResultsTableView){
return _searchResults.count;
}else{
return [self.class getData].count;
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if(cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:cellIdentifier];
}
if (tableView == self.searchDisplayController.searchResultsTableView) {
cell.textLabel.text = [[_searchResults objectAtIndex:indexPath.row] valueForKey:@"name"];
cell.detailTextLabel.text = [[_searchResults objectAtIndex:indexPath.row] valueForKey:@"code"];
}else{
cell.textLabel.text = [[[self.class getData] objectAtIndex:indexPath.row] valueForKey:@"name"];
cell.detailTextLabel.text = [[[self.class getData] objectAtIndex:indexPath.row] valueForKey:@"code"];
}
return cell;
}
#pragma mark - UITableView Delegate methods
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
if ([_delegate respondsToSelector:@selector(didSelectCountry:)]) {
NSDictionary* dict = nil;
if (tableView == self.searchDisplayController.searchResultsTableView) {
dict = [_searchResults objectAtIndex:indexPath.row];
}else{
dict = [[self.class getData] objectAtIndex:indexPath.row];
}
[self.delegate didSelectCountry:dict];
}
[PhoneMainView.instance popCurrentView];
}
#pragma mark - Filtering
- (void)filterContentForSearchText:(NSString*)searchText scope:(NSString*)scope{
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"name contains[c] %@ or code contains %@", searchText, searchText];
_searchResults = [[self.class getData] filteredArrayUsingPredicate:predicate];
}
- (IBAction)onCancelClick:(id)sender {
[PhoneMainView.instance popCurrentView];
}
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString{
[self filterContentForSearchText:searchString scope:[[self.searchDisplayController.searchBar scopeButtonTitles] objectAtIndex:[self.searchDisplayController.searchBar selectedScopeButtonIndex]]];
return YES;
}
+ (NSDictionary *)countryWithIso:(NSString *)iso {
for (NSDictionary *dict in [self.class getData]) {
if ([[dict objectForKey:@"iso"] isEqualToString:iso.uppercaseString]) {
return dict;
}
}
return nil;
}
+ (NSDictionary *)countryWithCountryCode:(NSString *)cc {
for (NSDictionary *dict in [self.class getData]) {
if ([[dict objectForKey:@"code"] isEqualToString:cc.uppercaseString]) {
return dict;
}
}
return nil;
}
@end

View file

@ -133,6 +133,11 @@ static UICompositeViewDescription *compositeDescription = nil;
case LinphoneAccountCreatorUsernameInvalidSize:
return usePhoneNumber ? NSLocalizedString(@"Phone number length invalid.", nil)
: NSLocalizedString(@"Username length invalid.", nil);
case LinphoneAccountCreatorPhoneNumberTooShort:
case LinphoneAccountCreatorPhoneNumberTooLong:
return nil; /* this is not an error, just user has to finish typing */
case LinphoneAccountCreatorPhoneNumberInvalid:
return NSLocalizedString(@"Invalid phone number.", nil);
case LinphoneAccountCreatorPasswordTooShort:
return NSLocalizedString(@"Password too short.", nil);
case LinphoneAccountCreatorPasswordTooLong:
@ -151,8 +156,9 @@ static UICompositeViewDescription *compositeDescription = nil;
case LinphoneAccountCreatorAccountExist:
case LinphoneAccountCreatorAccountNotCreated:
case LinphoneAccountCreatorAccountNotExist:
case LinphoneAccountCreatorAccountNotValidated:
case LinphoneAccountCreatorAccountValidated:
case LinphoneAccountCreatorAccountNotActivated:
case LinphoneAccountCreatorAccountAlreadyActivated:
case LinphoneAccountCreatorAccountActivated:
case LinphoneAccountCreatorOK:
break;
}

View file

@ -33,6 +33,7 @@
#import "InAppProductsManager.h"
#include "linphone/linphonecore.h"
#include "bctoolbox/list.h"
extern NSString *const LINPHONERC_APPLICATION_KEY;
@ -50,6 +51,7 @@ extern NSString *const kLinphoneBluetoothAvailabilityUpdate;
extern NSString *const kLinphoneConfiguringStateUpdate;
extern NSString *const kLinphoneGlobalStateUpdate;
extern NSString *const kLinphoneNotifyReceived;
extern NSString *const kLinphoneNotifyPresenceReceived;
extern NSString *const kLinphoneCallEncryptionChanged;
extern NSString *const kLinphoneFileTransferSendUpdate;
extern NSString *const kLinphoneFileTransferRecvUpdate;

View file

@ -66,6 +66,7 @@ NSString *const kLinphoneBluetoothAvailabilityUpdate = @"LinphoneBluetoothAvaila
NSString *const kLinphoneConfiguringStateUpdate = @"LinphoneConfiguringStateUpdate";
NSString *const kLinphoneGlobalStateUpdate = @"LinphoneGlobalStateUpdate";
NSString *const kLinphoneNotifyReceived = @"LinphoneNotifyReceived";
NSString *const kLinphoneNotifyPresenceReceived = @"LinphoneNotifyPresenceReceived";
NSString *const kLinphoneCallEncryptionChanged = @"LinphoneCallEncryptionChanged";
NSString *const kLinphoneFileTransferSendUpdate = @"LinphoneFileTransferSendUpdate";
NSString *const kLinphoneFileTransferRecvUpdate = @"LinphoneFileTransferRecvUpdate";
@ -1040,6 +1041,17 @@ static void linphone_iphone_notify_received(LinphoneCore *lc, LinphoneEvent *lev
content:body];
}
- (void)onNotifyPresenceReceived:(LinphoneCore *)lc friend:(LinphoneFriend *)lf {
// Post event
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
[dict setObject:[NSValue valueWithPointer:lf] forKey:@"friend"];
[NSNotificationCenter.defaultCenter postNotificationName:kLinphoneNotifyPresenceReceived object:self userInfo:dict];
}
static void linphone_iphone_notify_presence_received(LinphoneCore *lc, LinphoneFriend *lf) {
[(__bridge LinphoneManager *)linphone_core_get_user_data(lc) onNotifyPresenceReceived:lc friend:lf];
}
static void linphone_iphone_call_encryption_changed(LinphoneCore *lc, LinphoneCall *call, bool_t on,
const char *authentication_token) {
[(__bridge LinphoneManager *)linphone_core_get_user_data(lc) onCallEncryptionChanged:lc
@ -1325,7 +1337,7 @@ void networkReachabilityCallBack(SCNetworkReachabilityRef target, SCNetworkReach
static LinphoneCoreVTable linphonec_vtable = {
.call_state_changed = (LinphoneCoreCallStateChangedCb)linphone_iphone_call_state,
.registration_state_changed = linphone_iphone_registration_state,
.notify_presence_received = NULL,
.notify_presence_received = linphone_iphone_notify_presence_received,
.new_subscription_requested = NULL,
.auth_info_requested = linphone_iphone_popup_password_request,
.message_received = linphone_iphone_message_received,
@ -1397,16 +1409,20 @@ static LinphoneCoreVTable linphonec_vtable = {
/*DETECT cameras*/
_frontCamId = _backCamId = nil;
char **camlist = (char **)linphone_core_get_video_devices(theLinphoneCore);
for (char *cam = *camlist; *camlist != NULL; cam = *++camlist) {
if (strcmp(FRONT_CAM_NAME, cam) == 0) {
_frontCamId = cam;
// great set default cam to front
LOGI(@"Setting default camera [%s]", _frontCamId);
linphone_core_set_video_device(theLinphoneCore, _frontCamId);
}
if (strcmp(BACK_CAM_NAME, cam) == 0) {
_backCamId = cam;
if (camlist) {
for (char *cam = *camlist; *camlist != NULL; cam = *++camlist) {
if (strcmp(FRONT_CAM_NAME, cam) == 0) {
_frontCamId = cam;
// great set default cam to front
LOGI(@"Setting default camera [%s]", _frontCamId);
linphone_core_set_video_device(theLinphoneCore, _frontCamId);
}
if (strcmp(BACK_CAM_NAME, cam) == 0) {
_backCamId = cam;
}
}
} else {
LOGW(@"No camera detected!");
}
if (![LinphoneManager isNotIphone3G]) {
@ -1419,6 +1435,8 @@ static LinphoneCoreVTable linphonec_vtable = {
linphone_core_enable_video_capture(theLinphoneCore, FALSE);
}
[self enableProxyPublish:YES];
LOGI(@"Linphone [%s] started on [%s]", linphone_core_get_version(), [[UIDevice currentDevice].model UTF8String]);
// Post event
@ -1667,10 +1685,42 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
LOGI(@"Long running task started, remaining [%g s] because at least one call is paused",
[[UIApplication sharedApplication] backgroundTimeRemaining]);
}
- (void)enableProxyPublish:(BOOL)enabled {
if (linphone_core_get_global_state(LC) != LinphoneGlobalOn || !linphone_core_get_default_friend_list(LC)) {
LOGW(@"Not changing presence configuration because linphone core not ready yet");
return;
}
if ([self lpConfigBoolForKey:@"publish_presence"]) {
// set present to "tv", because "available" does not work yet
if (enabled) {
linphone_core_set_presence_model(
LC, linphone_core_create_presence_model_with_activity(LC, LinphonePresenceActivityTV, NULL));
}
const MSList *proxies = linphone_core_get_proxy_config_list(LC);
while (proxies) {
LinphoneProxyConfig *cfg = proxies->data;
linphone_proxy_config_edit(cfg);
linphone_proxy_config_enable_publish(cfg, enabled);
linphone_proxy_config_done(cfg);
proxies = proxies->next;
}
// force registration update first, then update friend list subscription
linphone_core_iterate(theLinphoneCore);
}
linphone_friend_list_enable_subscriptions(linphone_core_get_default_friend_list(LC), enabled);
}
- (BOOL)enterBackgroundMode {
LinphoneProxyConfig *proxyCfg = linphone_core_get_default_proxy_config(theLinphoneCore);
BOOL shouldEnterBgMode = FALSE;
// disable presence
[self enableProxyPublish:NO];
// handle proxy config if any
if (proxyCfg) {
const char *refkey = proxyCfg ? linphone_proxy_config_get_ref_key(proxyCfg) : NULL;
@ -1725,7 +1775,7 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
LOGI(@"Entering [%s] bg mode", shouldEnterBgMode ? "normal" : "lite");
if (!shouldEnterBgMode) {
const char *refkey = linphone_proxy_config_get_ref_key(proxyCfg);
const char *refkey = proxyCfg ? linphone_proxy_config_get_ref_key(proxyCfg) : NULL;
BOOL pushNotifEnabled = (refkey && strcmp(refkey, "push_notification") == 0);
if (pushNotifEnabled) {
LOGI(@"Keeping lc core to handle push");
@ -1741,6 +1791,8 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
}
- (void)becomeActive {
// enable presence
[self refreshRegisters];
if (pausedCallBgTask) {
[[UIApplication sharedApplication] endBackgroundTask:pausedCallBgTask];
@ -1766,6 +1818,8 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
LOGW(@"keepalive handler was called for the last time at %@", datestr);
}
}
[self enableProxyPublish:YES];
}
- (void)beginInterruption {
@ -2040,10 +2094,14 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
}
_pushNotificationToken = apushNotificationToken;
const MSList *proxies = linphone_core_get_proxy_config_list(LC);
while (proxies) {
[self configurePushTokenForProxyConfig:proxies->data];
proxies = proxies->next;
@try {
const MSList *proxies = linphone_core_get_proxy_config_list(LC);
while (proxies) {
[self configurePushTokenForProxyConfig:proxies->data];
proxies = proxies->next;
}
} @catch (NSException* e) {
LOGW(@"%s: linphone core not ready yet, ignoring push token", __FUNCTION__);
}
}

View file

@ -8,18 +8,21 @@
#import <UIKit/UIKit.h>
typedef BOOL (^UIDisplayError)(NSString *inputEntry);
typedef BOOL (^DisplayErrorPred)(NSString *inputEntry);
@interface UIAssistantTextField : UITextField <UITextFieldDelegate>
@property(nonatomic, strong) IBOutlet UIView* nextFieldResponder;
@property(nonatomic, strong) IBOutlet UILabel *errorLabel;
@property(nonatomic, readonly) UIDisplayError showErrorPredicate;
@property(nonatomic, readonly) DisplayErrorPred showErrorPredicate;
@property(nonatomic, strong) NSString *lastText;
// we should show error only when user finished editted the field at least once
@property(atomic) BOOL canShowError;
- (void)showError:(NSString *)msg when:(UIDisplayError)pred;
- (void)showError:(NSString *)msg when:(DisplayErrorPred)pred;
- (void)showError:(NSString *)msg;
- (BOOL)isInvalid;
- (BOOL)isVisible;
@end

View file

@ -27,7 +27,7 @@ INIT_WITH_COMMON_CF {
self.layer.borderColor = _errorLabel.hidden ? [[UIColor clearColor] CGColor] : [[UIColor redColor] CGColor];
}
- (void)showError:(NSString *)msg when:(UIDisplayError)apred {
- (void)showError:(NSString *)msg when:(DisplayErrorPred)apred {
_showErrorPredicate = apred;
[self showError:msg];
[self checkDisplayError];
@ -38,8 +38,21 @@ INIT_WITH_COMMON_CF {
self.layer.borderColor = _errorLabel.hidden ? [[UIColor clearColor] CGColor] : [[UIColor redColor] CGColor];
}
- (BOOL)isVisible {
UIView* aview = self;
while (aview) {
if (aview.isHidden || !aview.isUserInteractionEnabled) return NO;
aview = aview.superview;
}
return YES;
}
- (BOOL)isInvalid {
return _showErrorPredicate && _showErrorPredicate(_lastText);
return self.isVisible && _showErrorPredicate && _showErrorPredicate(_lastText);
}
- (void)setEnabled:(BOOL)enabled {
self.backgroundColor = [self.backgroundColor colorWithAlphaComponent:enabled?1:0.5];
}
#pragma mark - UITextFieldDelegate Functions
@ -47,14 +60,23 @@ INIT_WITH_COMMON_CF {
- (BOOL)textField:(UITextField *)textField
shouldChangeCharactersInRange:(NSRange)range
replacementString:(NSString *)string {
// we must not show any error until user typed at least one character
_canShowError |= (string.length > 0);
_lastText = [textField.text stringByReplacingCharactersInRange:range withString:string];
[self checkDisplayError];
return YES;
}
- (void)textFieldDidBeginEditing:(UITextField *)textField {
if (self.nextFieldResponder && !self.nextFieldResponder.hidden) {
self.returnKeyType = UIReturnKeyNext;
} else {
self.returnKeyType = UIReturnKeyDone;
}
}
- (void)textFieldDidEndEditing:(UITextField *)textField {
_lastText = textField.text;
_canShowError = YES;
[self checkDisplayError];
}

View file

@ -0,0 +1,14 @@
//
// UIAvatarPresence.h
// linphone
//
// Created by Gautier Pelloux-Prayer on 12/04/16.
//
//
@interface UIAvatarPresence : UIRoundedImageView
@property(nonatomic, setter=setFriend:) LinphoneFriend *friend;
@property(nonatomic, readonly) UIImageView *presenceImage;
@end

View file

@ -0,0 +1,85 @@
//
// UIAvatarPresence.m
// linphone
//
// Created by Gautier Pelloux-Prayer on 12/04/16.
//
//
#import "UIAvatarPresence.h"
@implementation UIAvatarPresence
INIT_WITH_COMMON_CF {
[NSNotificationCenter.defaultCenter addObserver:self
selector:@selector(onPresenceChanged:)
name:kLinphoneNotifyPresenceReceived
object:nil];
if (!_presenceImage) {
_presenceImage = [[UIImageView alloc] init];
_presenceImage.tag = 883;
[self addSubview:_presenceImage];
}
CGSize s = self.frame.size;
int is = MIN(s.width, s.height);
// place it in bottom right corner
_presenceImage.frame = CGRectMake(.5 * (s.width - is) + .7 * is, .5 * (s.height - is) + .7 * is, .2 * is, .2 * is);
_presenceImage.image = [UIImage imageNamed:@"presence_unregistered"];
return self;
}
- (void)dealloc {
[NSNotificationCenter.defaultCenter removeObserver:self];
};
- (void)setFrame:(CGRect)frame {
[super setFrame:frame];
CGSize s = self.frame.size;
int is = MIN(s.width, s.height);
// place it in bottom right corner
_presenceImage.frame = CGRectMake(.5 * (s.width - is) + .7 * is, .5 * (s.height - is) + .7 * is, .2 * is, .2 * is);
}
- (void)onPresenceChanged:(NSNotification *)k {
LinphoneFriend *f = [[k.userInfo valueForKey:@"friend"] pointerValue];
// only consider event if it's about us
if (!_friend ||
!linphone_address_weak_equal(linphone_friend_get_address(f), linphone_friend_get_address(_friend))) {
return;
}
[self updatePresenceImage];
}
- (void)updatePresenceImage {
LinphonePresenceBasicStatus basic =
_friend ? linphone_presence_model_get_basic_status(linphone_friend_get_presence_model(_friend))
: LinphonePresenceBasicStatusClosed;
const LinphonePresenceModel *model = _friend ? linphone_friend_get_presence_model(_friend) : NULL;
LinphonePresenceActivity *activity =
model ? linphone_presence_model_get_activity(model) ?: LinphonePresenceActivityOffline : NULL;
LOGE(@"Friend %s status is now %s/%s since %@", _friend ? linphone_friend_get_name(_friend) : "NULL",
basic == LinphonePresenceBasicStatusOpen ? "open" : "closed", linphone_presence_activity_to_string(activity),
[NSDate dateWithTimeIntervalSince1970:linphone_presence_model_get_timestamp(model)]);
NSString *imageName;
if (basic == LinphonePresenceBasicStatusClosed) {
imageName =
(_friend && linphone_friend_is_presence_received(_friend)) ? @"presence_away" : @"presence_unregistered";
} else if (linphone_presence_activity_get_type(activity) == LinphonePresenceActivityTV) {
imageName = @"presence_online";
} else {
imageName = @"presence_away";
}
_presenceImage.image = [UIImage imageNamed:imageName];
}
- (void)setFriend:(LinphoneFriend *) friend {
_friend = friend;
[self updatePresenceImage];
}
@end

View file

@ -52,7 +52,7 @@
- (void)setContact:(Contact *)acontact {
_contact = acontact;
[ContactDisplay setDisplayNameLabel:_nameLabel forContact:_contact];
_linphoneImage.hidden = !([FastAddressBook contactHasValidSipDomain:_contact]);
_linphoneImage.hidden = ! (_contact.friend && linphone_presence_model_get_basic_status(linphone_friend_get_presence_model(_contact.friend)) == LinphonePresenceBasicStatusOpen);
}
#pragma mark -

View file

@ -1,3 +1,4 @@
//
// UIRoundBorderedButton.m
// linphone
@ -52,4 +53,12 @@
self.layer.borderColor = [self.titleLabel.textColor CGColor];
}
- (BOOL)becomeFirstResponder {
if ([super becomeFirstResponder]) {
[LinphoneUtils findAndResignFirstResponder:self.superview];
return YES;
}
return NO;
}
@end

View file

@ -170,8 +170,11 @@ static void sync_address_book(ABAddressBookRef addressBook, CFDictionaryRef info
const MSList *friends = linphone_friend_list_get_friends(fl);
while (friends) {
LinphoneFriend *f = friends->data;
Contact *contact = [[Contact alloc] initWithFriend:f];
[self registerAddrsFor:contact];
// only append friends that are not native contacts (already added above)
if (linphone_friend_get_ref_key(f) == NULL) {
Contact *contact = [[Contact alloc] initWithFriend:f];
[self registerAddrsFor:contact];
}
friends = friends->next;
}
lists = lists->next;

View file

@ -445,6 +445,9 @@
else if ([machine isEqual:@"iPod7,1"])
return @"iPod touch 6G";
else if ([machine isEqual:@"x86_64"])
return @"simulator 64bits";
// none matched: cf https://www.theiphonewiki.com/wiki/Models for the whole list
LOGW(@"%s: Oops, unknown machine %@... consider completing me!", __FUNCTION__, machine);
return machine;

View file

@ -13,6 +13,12 @@
<entry name="realm" overwrite="true"></entry>
</section>
<section name="sip">
<entry name="rls_uri" overwrite="true"></entry>
<entry name="use_rls_presence" overwrite="true"></entry>
</section>
<section name="assistant">
<entry name="domain" overwrite="true"></entry>
<entry name="password_max_length" overwrite="true">-1</entry>

View file

@ -15,6 +15,11 @@
<entry name="realm" overwrite="true">sip.linphone.org</entry>
</section>
<section name="sip">
<entry name="rls_uri" overwrite="true">sip:rls@sip.linphone.org</entry>
<entry name="use_rls_presence" overwrite="true">1</entry>
</section>
<section name="assistant">
<entry name="domain" overwrite="true">sip.linphone.org</entry>
<entry name="password_max_length" overwrite="true">-1</entry>
@ -23,6 +28,6 @@
<entry name="username_max_length" overwrite="true">64</entry>
<entry name="username_min_length" overwrite="true">1</entry>
<entry name="username_regex" overwrite="true">^[a-z0-9_.\-]*$</entry>
<entry name="xmlrpc_url" overwrite="true">https://www.linphone.org/assistant.php</entry>
<entry name="xmlrpc_url" overwrite="true">https://sip3.linphone.org:444/xmlrpc.php</entry>
</section>
</config>

View file

@ -2,7 +2,7 @@
<config xmlns="http://www.linphone.org/xsds/lpconfig.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.linphone.org/xsds/lpconfig.xsd lpconfig.xsd">
<section name="proxy_default_values">
<entry name="avpf" overwrite="true">1</entry>
<entry name="avpf" overwrite="t rue">1</entry>
<entry name="dial_escape_plus" overwrite="true">0</entry>
<entry name="publish" overwrite="true">0</entry>
<entry name="quality_reporting_collector" overwrite="true">sip:voip-metrics@sip.linphone.org;transport=tls</entry>
@ -15,6 +15,11 @@
<entry name="realm" overwrite="true">sip.linphone.org</entry>
</section>
<section name="sip">
<entry name="rls_uri" overwrite="true">sip:rls@sip.linphone.org</entry>
<entry name="use_rls_presence" overwrite="true">1</entry>
</section>
<section name="assistant">
<entry name="domain" overwrite="true">sip.linphone.org</entry>
<entry name="password_max_length" overwrite="true">34</entry>
@ -23,6 +28,6 @@
<entry name="username_max_length" overwrite="true">64</entry>
<entry name="username_min_length" overwrite="true">1</entry>
<entry name="username_regex" overwrite="true">^[a-z0-9_.\-]*$</entry>
<entry name="xmlrpc_url" overwrite="true">https://www.linphone.org/assistant.php</entry>
<entry name="xmlrpc_url" overwrite="true">https://sip3.linphone.org:444/xmlrpc.php</entry>
</section>
</config>

View file

@ -13,6 +13,12 @@
<entry name="realm" overwrite="true"></entry>
</section>
<section name="sip">
<entry name="rls_uri" overwrite="true"></entry>
<entry name="use_rls_presence" overwrite="true"></entry>
</section>
<section name="assistant">
<entry name="domain" overwrite="true"></entry>
<entry name="password_max_length" overwrite="true">-1</entry>

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 617 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

View file

@ -5,6 +5,7 @@
debug_popup_email=linphone-iphone@belledonne-communications.com
send_logs_include_linphonerc_and_chathistory=0
#use_phone_number=0
publish_presence=0
[assistant]
password_length=-1
@ -17,6 +18,7 @@ history_max_size=-1
[sip]
sip_random_port=0
store_ha1_passwd=0
#handle_content_encoding=none
[sound]
dtmf_player_amp=0.007

File diff suppressed because it is too large Load diff