Merge pull request #1477 from morozkin/incoming_call_screen

Add screen for incoming calls
This commit is contained in:
manuroe
2017-08-31 14:05:16 +02:00
committed by GitHub
8 changed files with 571 additions and 82 deletions
+20
View File
@@ -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 = "<group>"; };
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 = "<group>"; };
92324BE11F4F66D3009DE194 /* IncomingCallView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IncomingCallView.h; sourceTree = "<group>"; };
92324BE21F4F66D3009DE194 /* IncomingCallView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IncomingCallView.m; sourceTree = "<group>"; };
92324BE41F4F6A60009DE194 /* CircleButton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CircleButton.h; sourceTree = "<group>"; };
92324BE51F4F6A60009DE194 /* CircleButton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CircleButton.m; sourceTree = "<group>"; };
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 = "<group>"; };
F0131DE31F2200D600CBF707 /* RiotSplitViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RiotSplitViewController.h; sourceTree = "<group>"; };
F0131DE41F2200D600CBF707 /* RiotSplitViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RiotSplitViewController.m; sourceTree = "<group>"; };
@@ -1412,6 +1418,17 @@
name = Pods;
sourceTree = "<group>";
};
92324BE01F4F668F009DE194 /* Calls */ = {
isa = PBXGroup;
children = (
92324BE11F4F66D3009DE194 /* IncomingCallView.h */,
92324BE21F4F66D3009DE194 /* IncomingCallView.m */,
92324BE41F4F6A60009DE194 /* CircleButton.h */,
92324BE51F4F6A60009DE194 /* CircleButton.m */,
);
path = Calls;
sourceTree = "<group>";
};
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 */,
+2 -82
View File
@@ -166,7 +166,6 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
}
@property (strong, nonatomic) UIAlertController *mxInAppNotification;
@property (strong, nonatomic) UIAlertController *incomingCallNotification;
@property (nonatomic, nullable, copy) void (^registrationForRemoteNotificationsCompletion)(NSError *);
@@ -565,13 +564,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];
@@ -1848,81 +1841,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];
}
}];
}
+2
View File
@@ -478,6 +478,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.";
+33
View File
@@ -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
+55
View File
@@ -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 <UIKit/UIKit.h>
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
+86
View File
@@ -0,0 +1,86 @@
/*
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)setDefaultBackgroundColor:(UIColor *)defaultBackgroundColor
{
_defaultBackgroundColor = defaultBackgroundColor;
self.backgroundColor = defaultBackgroundColor;
}
- (void)layoutSubviews
{
[super layoutSubviews];
self.layer.cornerRadius = CGRectGetWidth(self.bounds) / 2.0;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesBegan:touches withEvent:event];
[self animateBackgroundColor:self.highlightBackgroundColor tintColor:self.highlightTintColor];
}
- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[super touchesEnded:touches withEvent:event];
[self animateBackgroundColor:self.defaultBackgroundColor tintColor:self.defaultTintColor];
}
- (void)touchesCancelled:(NSSet<UITouch *> *)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
+47
View File
@@ -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 <UIKit/UIKit.h>
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
+326
View File
@@ -0,0 +1,326 @@
/*
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 <MatrixKit/MXKImageView.h>
#import <MatrixSDK/MXMediaManager.h>
#import "CircleButton.h"
#import "RiotDesignValues.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 = kRiotPrimaryBgColor;
self.opaque = YES;
self.callerImageView = [[MXKImageView alloc] init];
self.callerImageView.backgroundColor = kRiotPrimaryBgColor;
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 = 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 = kRiotPrimaryBgColor;
self.callInfoLabel.textColor = kRiotSecondaryTextColor;
self.callInfoLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular];
self.callInfoLabel.text = callInfo;
self.callInfoLabel.textAlignment = NSTextAlignmentCenter;
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 = kRiotPrimaryBgColor;
self.answerTitleLabel.textColor = answerButtonBorderColor;
self.answerTitleLabel.font = [UIFont systemFontOfSize:18.0 weight:UIFontWeightRegular];
self.answerTitleLabel.text = NSLocalizedStringFromTable(@"accept", @"Vector", nil);
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 = kRiotPrimaryBgColor;
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:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self
attribute:NSLayoutAttributeLeading
multiplier:1.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
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.callInfoLabel
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.callerNameLabel
attribute:NSLayoutAttributeWidth
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