From ce343aaa51689bcc72c091c053d7f3143a71d6d4 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Fri, 25 Aug 2017 03:02:29 +0300 Subject: [PATCH 1/4] Add screen for incoming calls --- Riot.xcodeproj/project.pbxproj | 20 ++ Riot/AppDelegate.m | 75 +----- Riot/Assets/en.lproj/Vector.strings | 2 + Riot/ViewController/CallViewController.m | 33 +++ Riot/Views/Calls/CircleButton.h | 55 ++++ Riot/Views/Calls/CircleButton.m | 80 ++++++ Riot/Views/Calls/IncomingCallView.h | 47 ++++ Riot/Views/Calls/IncomingCallView.m | 305 +++++++++++++++++++++++ 8 files changed, 543 insertions(+), 74 deletions(-) create mode 100644 Riot/Views/Calls/CircleButton.h create mode 100644 Riot/Views/Calls/CircleButton.m create mode 100644 Riot/Views/Calls/IncomingCallView.h create mode 100644 Riot/Views/Calls/IncomingCallView.m diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 6d8ab14c7..7f4d1ddbc 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -76,6 +76,8 @@ 32D392191EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32D392171EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib */; }; 32FD0A3D1EB0CD9B0072B066 /* BugReportViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FD0A3B1EB0CD9B0072B066 /* BugReportViewController.m */; }; 32FD0A3E1EB0CD9B0072B066 /* BugReportViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32FD0A3C1EB0CD9B0072B066 /* BugReportViewController.xib */; }; + 92324BE31F4F66D3009DE194 /* IncomingCallView.m in Sources */ = {isa = PBXBuildFile; fileRef = 92324BE21F4F66D3009DE194 /* IncomingCallView.m */; }; + 92324BE61F4F6A60009DE194 /* CircleButton.m in Sources */ = {isa = PBXBuildFile; fileRef = 92324BE51F4F6A60009DE194 /* CircleButton.m */; }; A27ECCE3FC4971745D2CB78D /* libPods-RiotShareExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7246451C668D6782166E22EC /* libPods-RiotShareExtension.a */; }; F0131DE51F2200D600CBF707 /* RiotSplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F0131DE41F2200D600CBF707 /* RiotSplitViewController.m */; }; F02C1A861E8EB04C0045A404 /* PeopleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F02C1A841E8EB04C0045A404 /* PeopleViewController.m */; }; @@ -646,6 +648,10 @@ 7246451C668D6782166E22EC /* libPods-RiotShareExtension.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-RiotShareExtension.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 765F5104DB3EC39713DEB3A4 /* Pods-RiotShareExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RiotShareExtension.release.xcconfig"; path = "Pods/Target Support Files/Pods-RiotShareExtension/Pods-RiotShareExtension.release.xcconfig"; sourceTree = ""; }; 839BB91240D350D5607D55BA /* Pods-Riot.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Riot.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Riot/Pods-Riot.debug.xcconfig"; sourceTree = ""; }; + 92324BE11F4F66D3009DE194 /* IncomingCallView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IncomingCallView.h; sourceTree = ""; }; + 92324BE21F4F66D3009DE194 /* IncomingCallView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IncomingCallView.m; sourceTree = ""; }; + 92324BE41F4F6A60009DE194 /* CircleButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircleButton.h; sourceTree = ""; }; + 92324BE51F4F6A60009DE194 /* CircleButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CircleButton.m; sourceTree = ""; }; C195C53961EA28E6900AEB68 /* Pods-Riot.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Riot.release.xcconfig"; path = "Pods/Target Support Files/Pods-Riot/Pods-Riot.release.xcconfig"; sourceTree = ""; }; F0131DE31F2200D600CBF707 /* RiotSplitViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RiotSplitViewController.h; sourceTree = ""; }; F0131DE41F2200D600CBF707 /* RiotSplitViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RiotSplitViewController.m; sourceTree = ""; }; @@ -1412,6 +1418,17 @@ name = Pods; sourceTree = ""; }; + 92324BE01F4F668F009DE194 /* Calls */ = { + isa = PBXGroup; + children = ( + 92324BE11F4F66D3009DE194 /* IncomingCallView.h */, + 92324BE21F4F66D3009DE194 /* IncomingCallView.m */, + 92324BE41F4F6A60009DE194 /* CircleButton.h */, + 92324BE51F4F6A60009DE194 /* CircleButton.m */, + ); + path = Calls; + sourceTree = ""; + }; F083BB021E7005FD00A9B29C /* RiotTests */ = { isa = PBXGroup; children = ( @@ -1920,6 +1937,7 @@ F083BC571E7009EC00A9B29C /* Views */ = { isa = PBXGroup; children = ( + 92324BE01F4F668F009DE194 /* Calls */, F0B4CBAD1F4215E3008E99C5 /* Event */, F083BC581E7009EC00A9B29C /* Authentication */, F083BC5F1E7009EC00A9B29C /* Contact */, @@ -2993,6 +3011,7 @@ F083BE9C1E7009ED00A9B29C /* TableViewCellWithCheckBoxAndLabel.m in Sources */, 32471CE11F13AC1500BDF50A /* RoomMembershipExpandedWithPaginationTitleBubbleCell.m in Sources */, F083BDFE1E7009ED00A9B29C /* RecentCellData.m in Sources */, + 92324BE61F4F6A60009DE194 /* CircleButton.m in Sources */, F083BE3A1E7009ED00A9B29C /* RoomIncomingEncryptedAttachmentBubbleCell.m in Sources */, 3205ED841E97725E003D65FA /* DirectoryServerTableViewCell.m in Sources */, F083BEA21E7009ED00A9B29C /* TableViewCellWithPhoneNumberTextField.m in Sources */, @@ -3026,6 +3045,7 @@ F083BE481E7009ED00A9B29C /* RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, F06CDD691EF01E3900870B75 /* RoomEmptyBubbleCell.m in Sources */, F083BE041E7009ED00A9B29C /* Tools.m in Sources */, + 92324BE31F4F66D3009DE194 /* IncomingCallView.m in Sources */, F083BE6A1E7009ED00A9B29C /* RoomOutgoingAttachmentBubbleCell.m in Sources */, F083BDEF1E7009ED00A9B29C /* UINavigationController+Riot.m in Sources */, F083BE761E7009ED00A9B29C /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m in Sources */, diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 37989a831..3804f4757 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1812,81 +1812,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN currentCallViewController = [CallViewController callViewController:mxCall]; currentCallViewController.delegate = self; - if (mxCall.isIncoming) - { - // Prompt user before presenting the call view controller - NSString *callPromptFormat = mxCall.isVideoCall ? NSLocalizedStringFromTable(@"call_incoming_video_prompt", @"Vector", nil) : NSLocalizedStringFromTable(@"call_incoming_voice_prompt", @"Vector", nil); - NSString *callerName = currentCallViewController.peer.displayname; - if (!callerName.length) - { - callerName = currentCallViewController.peer.userId; - } - NSString *callPrompt = [NSString stringWithFormat:callPromptFormat, callerName]; - - __weak typeof(self) weakSelf = self; - - // Removing existing notification (if any) - [_incomingCallNotification dismissViewControllerAnimated:NO completion:nil]; - - - - _incomingCallNotification = [UIAlertController alertControllerWithTitle:callPrompt - message:nil - preferredStyle:UIAlertControllerStyleAlert]; - - [_incomingCallNotification addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"decline", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - - // Reject the call. - // Note: Do not reset the incoming call notification before this operation, because it is used to release properly the dismissed call view controller. - if (self->currentCallViewController) - { - [self->currentCallViewController onButtonPressed:self->currentCallViewController.rejectCallButton]; - - currentCallViewController = nil; - } - - self.incomingCallNotification = nil; - - mxCall.delegate = nil; - } - - }]]; - - [_incomingCallNotification addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"accept", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - - self.incomingCallNotification = nil; - - if (self->currentCallViewController) - { - [self->currentCallViewController onButtonPressed:self->currentCallViewController.answerCallButton]; - - [self presentCallViewController:nil]; - } - } - - }]]; - - [_incomingCallNotification mxk_setAccessibilityIdentifier:@"AppDelegateIncomingCallAlert"]; - [self showNotificationAlert:_incomingCallNotification]; - } - else - { - [self presentCallViewController:nil]; - } + [self presentCallViewController:nil]; } - }]; } diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 6c73d198d..c246df64d 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -476,6 +476,8 @@ // Call "call_incoming_voice_prompt" = "Incoming voice call from %@"; "call_incoming_video_prompt" = "Incoming video call from %@"; +"call_incoming_voice" = "Incoming call..."; +"call_incoming_video" = "Incoming video call..."; "call_already_displayed" = "There is already a call in progress."; "call_jitsi_error" = "Failed to join the conference call."; diff --git a/Riot/ViewController/CallViewController.m b/Riot/ViewController/CallViewController.m index 830fa7281..558039dfc 100644 --- a/Riot/ViewController/CallViewController.m +++ b/Riot/ViewController/CallViewController.m @@ -27,6 +27,8 @@ #import "RiotNavigationController.h" +#import "IncomingCallView.h" + @interface CallViewController () { // Display a gradient view above the screen @@ -196,6 +198,37 @@ gradientMaskLayer = nil; } +- (UIView *)createIncomingCallView +{ + NSString *avatarThumbURL = [self.mainSession.matrixRestClient urlOfContentThumbnail:self.peer.avatarUrl + toFitViewSize:IncomingCallView.callerAvatarSize + withMethod:MXThumbnailingMethodCrop]; + + NSString *callInfo; + if (self.mxCall.isVideoCall) + callInfo = NSLocalizedStringFromTable(@"call_incoming_video", @"Vector", nil); + else + callInfo = NSLocalizedStringFromTable(@"call_incoming_voice", @"Vector", nil); + + IncomingCallView *incomingCallView = [[IncomingCallView alloc] initWithCallerAvatarURL:avatarThumbURL + placeholderImage:self.picturePlaceholder + callerName:self.peer.displayname + callInfo:callInfo]; + + // Incoming call is retained by call vc so use weak to avoid retain cycle + __weak typeof(self) weakSelf = self; + + incomingCallView.onAnswer = ^{ + [weakSelf onButtonPressed:weakSelf.answerCallButton]; + }; + + incomingCallView.onReject = ^{ + [weakSelf onButtonPressed:weakSelf.rejectCallButton]; + }; + + return incomingCallView; +} + #pragma mark - MXCallDelegate - (void)call:(MXCall *)call didEncounterError:(NSError *)error diff --git a/Riot/Views/Calls/CircleButton.h b/Riot/Views/Calls/CircleButton.h new file mode 100644 index 000000000..61780be30 --- /dev/null +++ b/Riot/Views/Calls/CircleButton.h @@ -0,0 +1,55 @@ +/* + 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 + +NS_ASSUME_NONNULL_BEGIN + +@interface CircleButton : UIButton + +/** + Background color that is used for highlighted state + + By default the same as borderColor + */ +@property (nonatomic) UIColor *highlightBackgroundColor; + +/** + Background color that is used for normal state + + By default white + */ +@property (nonatomic) UIColor *defaultBackgroundColor; + +/** + Tint color that is used for highlighted state + + By default is white + */ +@property (nonatomic) UIColor *highlightTintColor; + +/** + Tint color that is used for normal state + + By default is the same as borderColor + */ +@property (nonatomic) UIColor *defaultTintColor; + +- (instancetype)initWithImage:(UIImage *)image borderColor:(UIColor *)borderColor; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Riot/Views/Calls/CircleButton.m b/Riot/Views/Calls/CircleButton.m new file mode 100644 index 000000000..b49b108dc --- /dev/null +++ b/Riot/Views/Calls/CircleButton.m @@ -0,0 +1,80 @@ +/* + 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 "CircleButton.h" + +@implementation CircleButton + +- (instancetype)initWithImage:(UIImage *)image borderColor:(UIColor *)borderColor +{ + self = [[super class] buttonWithType:UIButtonTypeCustom]; + + self.adjustsImageWhenDisabled = NO; + self.adjustsImageWhenHighlighted = NO; + + self.layer.borderWidth = 1.0; + self.layer.borderColor = borderColor.CGColor; + + self.defaultBackgroundColor = [UIColor whiteColor]; + self.highlightTintColor = [UIColor whiteColor]; + + self.highlightBackgroundColor = borderColor; + self.defaultTintColor = borderColor; + self.tintColor = borderColor; + + [self setImage:[image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] forState:UIControlStateNormal]; + + return self; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + self.layer.cornerRadius = CGRectGetWidth(self.bounds) / 2.0; +} + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesBegan:touches withEvent:event]; + [self animateBackgroundColor:self.highlightBackgroundColor tintColor:self.highlightTintColor]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesEnded:touches withEvent:event]; + [self animateBackgroundColor:self.defaultBackgroundColor tintColor:self.defaultTintColor]; +} + +- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event +{ + [super touchesCancelled:touches withEvent:event]; + [self animateBackgroundColor:self.defaultBackgroundColor tintColor:self.defaultTintColor]; +} + +- (void)animateBackgroundColor:(UIColor *)color tintColor:(UIColor *)tintColor +{ + [UIView animateWithDuration:0.25 + delay:0.0 + options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionAllowUserInteraction + animations:^{ + self.backgroundColor = color; + self.tintColor = tintColor; + } + completion:nil]; +} + +@end diff --git a/Riot/Views/Calls/IncomingCallView.h b/Riot/Views/Calls/IncomingCallView.h new file mode 100644 index 000000000..9b437c1ee --- /dev/null +++ b/Riot/Views/Calls/IncomingCallView.h @@ -0,0 +1,47 @@ +/* + 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 + +NS_ASSUME_NONNULL_BEGIN + +typedef void (^IncomingCallViewAction)(); + +@interface IncomingCallView : UIView + +/** + Size that is applied to displayed user avatar + */ +@property (class, readonly) CGSize callerAvatarSize; + +/** + Block which is performed on call answer action + */ +@property (nonatomic, nullable, copy) IncomingCallViewAction onAnswer; + +/** + Block which is performed on call reject + */ +@property (nonatomic, nullable, copy) IncomingCallViewAction onReject; + +- (instancetype)initWithCallerAvatarURL:(NSString *)callerAvatarURL + placeholderImage:(UIImage *)placeholderImage + callerName:(NSString *)callerName + callInfo:(NSString *)callInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Riot/Views/Calls/IncomingCallView.m b/Riot/Views/Calls/IncomingCallView.m new file mode 100644 index 000000000..88857cb2c --- /dev/null +++ b/Riot/Views/Calls/IncomingCallView.m @@ -0,0 +1,305 @@ +/* + 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 "IncomingCallView.h" + +#import +#import + +#import "CircleButton.h" + +static const CGFloat kAvatarSize = 100.0; +static const CGFloat kButtonSize = 80.0; + +@interface IncomingCallView () + +@property (nonatomic) MXKImageView *callerImageView; +@property (nonatomic) UILabel *callerNameLabel; +@property (nonatomic) UILabel *callInfoLabel; + +@property (nonatomic) CircleButton *answerButton; +@property (nonatomic) UILabel *answerTitleLabel; + +@property (nonatomic) CircleButton *rejectButton; +@property (nonatomic) UILabel *rejectTitleLabel; + +@end + +@implementation IncomingCallView + ++ (CGSize)callerAvatarSize +{ + return CGSizeMake(kAvatarSize, kAvatarSize); +} + +- (instancetype)initWithCallerAvatarURL:(NSString *)callerAvatarURL placeholderImage:(UIImage *)placeholderImage callerName:(NSString *)callerName callInfo:(NSString *)callInfo +{ + self = [super initWithFrame:CGRectZero]; + if (self) + { + self.backgroundColor = [UIColor whiteColor]; + self.opaque = YES; + + self.callerImageView = [[MXKImageView alloc] init]; + self.callerImageView.backgroundColor = [UIColor whiteColor]; + self.callerImageView.clipsToBounds = YES; + self.callerImageView.mediaFolder = kMXMediaManagerAvatarThumbnailFolder; + self.callerImageView.enableInMemoryCache = YES; + [self.callerImageView setImageURL:callerAvatarURL + withType:nil + andImageOrientation:UIImageOrientationUp + previewImage:placeholderImage]; + + self.callerNameLabel = [[UILabel alloc] init]; + self.callerNameLabel.backgroundColor = [UIColor whiteColor]; + self.callerNameLabel.textColor = [UIColor colorWithRed:60.0/255.0 green:60.0/255.0 blue:60.0/255.0 alpha:1.0]; + self.callerNameLabel.font = [UIFont systemFontOfSize:24.0 weight:UIFontWeightMedium]; + self.callerNameLabel.text = callerName; + + self.callInfoLabel = [[UILabel alloc] init]; + self.callInfoLabel.backgroundColor = [UIColor whiteColor]; + self.callInfoLabel.textColor = [UIColor colorWithRed:142.0/255.0 green:142.0/255.0 blue:147.0/255.0 alpha:1.0]; + self.callInfoLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular]; + self.callInfoLabel.text = callInfo; + + UIColor *answerButtonBorderColor = [UIColor colorWithRed:98.0/255.0 green:206.0/255.0 blue:156.0/255.0 alpha:1.0]; + + self.answerButton = [[CircleButton alloc] initWithImage:[UIImage imageNamed:@"voice_call_icon"] + borderColor:answerButtonBorderColor]; + [self.answerButton addTarget:self action:@selector(didTapAnswerButton) forControlEvents:UIControlEventTouchUpInside]; + + self.answerTitleLabel = [[UILabel alloc] init]; + self.answerTitleLabel.backgroundColor = [UIColor whiteColor]; + self.answerTitleLabel.textColor = answerButtonBorderColor; + self.answerTitleLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular]; + self.answerTitleLabel.text = NSLocalizedStringFromTable(@"accept", @"Vector", nil); + + UIColor *rejectButtonBorderColor = [UIColor colorWithRed:1.0 green:0.0 blue:100.0/255.0 alpha:1.0]; + + self.rejectButton = [[CircleButton alloc] initWithImage:[UIImage imageNamed:@"call_hangup_icon"] + borderColor:rejectButtonBorderColor]; + [self.rejectButton addTarget:self action:@selector(didTapRejectButton) forControlEvents:UIControlEventTouchUpInside]; + + self.rejectTitleLabel = [[UILabel alloc] init]; + self.rejectTitleLabel.backgroundColor = [UIColor whiteColor]; + self.rejectTitleLabel.textColor = rejectButtonBorderColor; + self.rejectTitleLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular]; + self.rejectTitleLabel.text = NSLocalizedStringFromTable(@"decline", @"Vector", nil); + + [self setupLayout]; + } + + return self; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + self.callerImageView.layer.cornerRadius = CGRectGetWidth(self.callerImageView.bounds) / 2.0; +} + +- (void)setupLayout +{ + NSArray *views = @[self.callerImageView, self.callerNameLabel, self.callInfoLabel, self.answerButton, self.answerTitleLabel, self.rejectButton, self.rejectTitleLabel]; + for (UIView *view in views) + { + view.translatesAutoresizingMaskIntoConstraints = NO; + [self addSubview:view]; + } + + [NSLayoutConstraint activateConstraints:@[ + [NSLayoutConstraint constraintWithItem:self.callerImageView + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:62.0], + + [NSLayoutConstraint constraintWithItem:self.callerImageView + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:0.0], + + [NSLayoutConstraint constraintWithItem:self.callerImageView + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:kAvatarSize], + + [NSLayoutConstraint constraintWithItem:self.callerImageView + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:self.callerImageView + attribute:NSLayoutAttributeWidth + multiplier:1.0 + constant:0.0], + + [NSLayoutConstraint constraintWithItem:self.callerNameLabel + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.callerImageView + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:18.0], + + [NSLayoutConstraint constraintWithItem:self.callerNameLabel + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:0.0], + + [NSLayoutConstraint constraintWithItem:self.callInfoLabel + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.callerNameLabel + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:7.0], + + [NSLayoutConstraint constraintWithItem:self.callInfoLabel + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:0.0], + + [NSLayoutConstraint constraintWithItem:self.rejectButton + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:kButtonSize], + + [NSLayoutConstraint constraintWithItem:self.rejectButton + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:self.rejectButton + attribute:NSLayoutAttributeWidth + multiplier:1.0 + constant:0.0], + + [NSLayoutConstraint constraintWithItem:self.rejectButton + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:-22.5], + + [NSLayoutConstraint constraintWithItem:self.rejectTitleLabel + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.rejectButton + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:8.0], + + [NSLayoutConstraint constraintWithItem:self.rejectTitleLabel + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self.rejectButton + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:0.0], + + [NSLayoutConstraint constraintWithItem:self.rejectTitleLabel + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:-16.0], + + [NSLayoutConstraint constraintWithItem:self.answerButton + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:kButtonSize], + + [NSLayoutConstraint constraintWithItem:self.answerButton + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:self.answerButton + attribute:NSLayoutAttributeWidth + multiplier:1.0 + constant:0.0], + + [NSLayoutConstraint constraintWithItem:self.answerButton + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:22.5], + + [NSLayoutConstraint constraintWithItem:self.answerTitleLabel + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.answerButton + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:8.0], + + [NSLayoutConstraint constraintWithItem:self.answerTitleLabel + attribute:NSLayoutAttributeCenterX + relatedBy:NSLayoutRelationEqual + toItem:self.answerButton + attribute:NSLayoutAttributeCenterX + multiplier:1.0 + constant:0.0], + + [NSLayoutConstraint constraintWithItem:self.answerTitleLabel + attribute:NSLayoutAttributeBottom + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeBottom + multiplier:1.0 + constant:-16.0] + ]]; +} + +// MARK: - Actions + +- (void)didTapAnswerButton +{ + if (self.onAnswer) + { + self.onAnswer(); + } +} + +- (void)didTapRejectButton +{ + if (self.onReject) + { + self.onReject(); + } +} + +@end From 2e69e3d97cf8d2ed5319061327320462dbfb11f0 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 26 Aug 2017 18:18:34 +0300 Subject: [PATCH 2/4] Fix layout --- Riot/Views/Calls/IncomingCallView.m | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/Riot/Views/Calls/IncomingCallView.m b/Riot/Views/Calls/IncomingCallView.m index 88857cb2c..5fa165709 100644 --- a/Riot/Views/Calls/IncomingCallView.m +++ b/Riot/Views/Calls/IncomingCallView.m @@ -68,12 +68,14 @@ static const CGFloat kButtonSize = 80.0; self.callerNameLabel.textColor = [UIColor colorWithRed:60.0/255.0 green:60.0/255.0 blue:60.0/255.0 alpha:1.0]; self.callerNameLabel.font = [UIFont systemFontOfSize:24.0 weight:UIFontWeightMedium]; self.callerNameLabel.text = callerName; + self.callerNameLabel.textAlignment = NSTextAlignmentCenter; self.callInfoLabel = [[UILabel alloc] init]; self.callInfoLabel.backgroundColor = [UIColor whiteColor]; self.callInfoLabel.textColor = [UIColor colorWithRed:142.0/255.0 green:142.0/255.0 blue:147.0/255.0 alpha:1.0]; self.callInfoLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular]; self.callInfoLabel.text = callInfo; + self.callInfoLabel.textAlignment = NSTextAlignmentCenter; UIColor *answerButtonBorderColor = [UIColor colorWithRed:98.0/255.0 green:206.0/255.0 blue:156.0/255.0 alpha:1.0]; @@ -163,12 +165,20 @@ static const CGFloat kButtonSize = 80.0; constant:18.0], [NSLayoutConstraint constraintWithItem:self.callerNameLabel - attribute:NSLayoutAttributeCenterX + attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self - attribute:NSLayoutAttributeCenterX + attribute:NSLayoutAttributeLeading multiplier:1.0 - constant:0.0], + constant:15.0], + + [NSLayoutConstraint constraintWithItem:self.callerNameLabel + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:-15.0], [NSLayoutConstraint constraintWithItem:self.callInfoLabel attribute:NSLayoutAttributeTop @@ -186,6 +196,14 @@ static const CGFloat kButtonSize = 80.0; multiplier:1.0 constant:0.0], + [NSLayoutConstraint constraintWithItem:self.callInfoLabel + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:self.callerNameLabel + attribute:NSLayoutAttributeWidth + multiplier:1.0 + constant:0.0], + [NSLayoutConstraint constraintWithItem:self.rejectButton attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual From 2c5586f069efac8b39c79f36340a0ad399122d64 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sun, 27 Aug 2017 16:43:42 +0300 Subject: [PATCH 3/4] Remove incomingCallNotification property --- Riot/AppDelegate.m | 26 ++------------------------ 1 file changed, 2 insertions(+), 24 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 3804f4757..10a5f7802 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -166,7 +166,6 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN } @property (strong, nonatomic) UIAlertController *mxInAppNotification; -@property (strong, nonatomic) UIAlertController *incomingCallNotification; @end @@ -563,13 +562,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Enable error notifications isErrorNotificationSuspended = NO; - // Restore call alert if any - if (_incomingCallNotification) - { - NSLog(@"[AppDelegate] restoreInitialDisplay: keep visible incoming call alert"); - [self showNotificationAlert:_incomingCallNotification]; - } - else if (noCallSupportAlert) + if (noCallSupportAlert) { NSLog(@"[AppDelegate] restoreInitialDisplay: keep visible noCall support alert"); [self showNotificationAlert:noCallSupportAlert]; @@ -2359,22 +2352,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN { if (currentCallViewController && callViewController == currentCallViewController) { - if (_incomingCallNotification) - { - // The user was prompted for an incoming call which ended - // The call view controller was not presented yet. - [_incomingCallNotification dismissViewControllerAnimated:NO completion:nil]; - _incomingCallNotification = nil; - - // Release properly - [currentCallViewController destroy]; - - if (completion) - { - completion(); - } - } - else if (callViewController.isBeingPresented) + if (callViewController.isBeingPresented) { // Here the presentation of the call view controller is in progress // Postpone the dismiss From f7937a40608d93cb7ad1296631808111f680954e Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Thu, 31 Aug 2017 12:20:18 +0300 Subject: [PATCH 4/4] Add theme support --- Riot/Views/Calls/CircleButton.m | 6 ++++++ Riot/Views/Calls/IncomingCallView.m | 23 +++++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Riot/Views/Calls/CircleButton.m b/Riot/Views/Calls/CircleButton.m index b49b108dc..d277935ea 100644 --- a/Riot/Views/Calls/CircleButton.m +++ b/Riot/Views/Calls/CircleButton.m @@ -40,6 +40,12 @@ return self; } +- (void)setDefaultBackgroundColor:(UIColor *)defaultBackgroundColor +{ + _defaultBackgroundColor = defaultBackgroundColor; + self.backgroundColor = defaultBackgroundColor; +} + - (void)layoutSubviews { [super layoutSubviews]; diff --git a/Riot/Views/Calls/IncomingCallView.m b/Riot/Views/Calls/IncomingCallView.m index 5fa165709..0d442232c 100644 --- a/Riot/Views/Calls/IncomingCallView.m +++ b/Riot/Views/Calls/IncomingCallView.m @@ -20,6 +20,7 @@ #import #import "CircleButton.h" +#import "RiotDesignValues.h" static const CGFloat kAvatarSize = 100.0; static const CGFloat kButtonSize = 80.0; @@ -50,11 +51,11 @@ static const CGFloat kButtonSize = 80.0; self = [super initWithFrame:CGRectZero]; if (self) { - self.backgroundColor = [UIColor whiteColor]; + self.backgroundColor = kRiotPrimaryBgColor; self.opaque = YES; self.callerImageView = [[MXKImageView alloc] init]; - self.callerImageView.backgroundColor = [UIColor whiteColor]; + self.callerImageView.backgroundColor = kRiotPrimaryBgColor; self.callerImageView.clipsToBounds = YES; self.callerImageView.mediaFolder = kMXMediaManagerAvatarThumbnailFolder; self.callerImageView.enableInMemoryCache = YES; @@ -64,39 +65,41 @@ static const CGFloat kButtonSize = 80.0; previewImage:placeholderImage]; self.callerNameLabel = [[UILabel alloc] init]; - self.callerNameLabel.backgroundColor = [UIColor whiteColor]; - self.callerNameLabel.textColor = [UIColor colorWithRed:60.0/255.0 green:60.0/255.0 blue:60.0/255.0 alpha:1.0]; + self.callerNameLabel.backgroundColor = kRiotPrimaryBgColor; + self.callerNameLabel.textColor = kRiotPrimaryTextColor; self.callerNameLabel.font = [UIFont systemFontOfSize:24.0 weight:UIFontWeightMedium]; self.callerNameLabel.text = callerName; self.callerNameLabel.textAlignment = NSTextAlignmentCenter; self.callInfoLabel = [[UILabel alloc] init]; - self.callInfoLabel.backgroundColor = [UIColor whiteColor]; - self.callInfoLabel.textColor = [UIColor colorWithRed:142.0/255.0 green:142.0/255.0 blue:147.0/255.0 alpha:1.0]; + self.callInfoLabel.backgroundColor = kRiotPrimaryBgColor; + self.callInfoLabel.textColor = kRiotSecondaryTextColor; self.callInfoLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular]; self.callInfoLabel.text = callInfo; self.callInfoLabel.textAlignment = NSTextAlignmentCenter; - UIColor *answerButtonBorderColor = [UIColor colorWithRed:98.0/255.0 green:206.0/255.0 blue:156.0/255.0 alpha:1.0]; + UIColor *answerButtonBorderColor = kRiotColorGreen; self.answerButton = [[CircleButton alloc] initWithImage:[UIImage imageNamed:@"voice_call_icon"] borderColor:answerButtonBorderColor]; + self.answerButton.defaultBackgroundColor = kRiotPrimaryBgColor; [self.answerButton addTarget:self action:@selector(didTapAnswerButton) forControlEvents:UIControlEventTouchUpInside]; self.answerTitleLabel = [[UILabel alloc] init]; - self.answerTitleLabel.backgroundColor = [UIColor whiteColor]; + self.answerTitleLabel.backgroundColor = kRiotPrimaryBgColor; self.answerTitleLabel.textColor = answerButtonBorderColor; self.answerTitleLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular]; self.answerTitleLabel.text = NSLocalizedStringFromTable(@"accept", @"Vector", nil); - UIColor *rejectButtonBorderColor = [UIColor colorWithRed:1.0 green:0.0 blue:100.0/255.0 alpha:1.0]; + UIColor *rejectButtonBorderColor = kRiotColorPinkRed; self.rejectButton = [[CircleButton alloc] initWithImage:[UIImage imageNamed:@"call_hangup_icon"] borderColor:rejectButtonBorderColor]; + self.rejectButton.defaultBackgroundColor = kRiotPrimaryBgColor; [self.rejectButton addTarget:self action:@selector(didTapRejectButton) forControlEvents:UIControlEventTouchUpInside]; self.rejectTitleLabel = [[UILabel alloc] init]; - self.rejectTitleLabel.backgroundColor = [UIColor whiteColor]; + self.rejectTitleLabel.backgroundColor = kRiotPrimaryBgColor; self.rejectTitleLabel.textColor = rejectButtonBorderColor; self.rejectTitleLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular]; self.rejectTitleLabel.text = NSLocalizedStringFromTable(@"decline", @"Vector", nil);