session-ios/Signal/src/view controllers/InCallViewController.m

305 lines
10 KiB
Objective-C

#import "AppAudioManager.h"
#import "CallFailedServerMessage.h"
#import "InCallViewController.h"
#import "LocalizableText.h"
#import "RecentCallManager.h"
#import "Util.h"
#import "CallAudioManager.h"
#import "PhoneManager.h"
#import <MediaPlayer/MPMusicPlayerController.h>
#define BUTTON_BORDER_WIDTH 1.0f
#define CONTACT_IMAGE_BORDER_WIDTH 2.0f
#define RINGING_ROTATION_DURATION 0.375f
#define CONNECTING_FLASH_DURATION 0.5f
#define END_CALL_CLEANUP_DELAY (int)(3.1f * NSEC_PER_SEC)
static NSString *const SPINNER_CONNECTING_IMAGE_NAME = @"spinner_connecting";
static NSString *const SPINNER_CONNECTING_FLASH_IMAGE_NAME = @"spinner_connecting_flash";
static NSString *const SPINNER_RINGING_IMAGE_NAME = @"spinner_ringing";
static NSString *const SPINNER_ERROR_FLASH_IMAGE_NAME = @"spinner_error";
static NSInteger connectingFlashCounter = 0;
@interface InCallViewController () {
BOOL _isMusicPaused;
CallAudioManager *_callAudioManager;
NSTimer *_connectingFlashTimer;
NSTimer *_ringingAnimationTimer;
}
@end
@implementation InCallViewController
+(InCallViewController*) inCallViewControllerWithCallState:(CallState*)callState
andOptionallyKnownContact:(Contact*)contact {
require(callState != nil);
InCallViewController* controller = [InCallViewController new];
controller->_potentiallyKnownContact = contact;
controller->_callState = callState;
return controller;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self showCallState];
[self pauseMusicIfPlaying];
[self setupButtonBorders];
[self localizeButtons];
[[UIDevice currentDevice] setProximityMonitoringEnabled:YES];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self startConnectingFlashAnimation];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self stopRingingAnimation];
[self stopConnectingFlashAnimation];
[[AppAudioManager sharedInstance] cancellAllAudio];
}
- (void)dealloc {
[[UIDevice currentDevice] setProximityMonitoringEnabled:NO];
}
-(void) showCallState {
[self clearDetails];
[self populateImmediateDetails];
[self handleIncomingDetails];
}
- (void)pauseMusicIfPlaying {
if ([[MPMusicPlayerController iPodMusicPlayer] playbackState] == MPMusicPlaybackStatePlaying) {
_isMusicPaused = YES;
[[MPMusicPlayerController iPodMusicPlayer] pause];
}
}
- (void)startConnectingFlashAnimation {
if(![_ringingAnimationTimer isValid]){
_connectingFlashTimer = [NSTimer scheduledTimerWithTimeInterval:CONNECTING_FLASH_DURATION
target:self
selector:@selector(flashConnectingIndicator)
userInfo:nil
repeats:YES];
}
}
- (void)flashConnectingIndicator {
NSString *newImageName;
if (connectingFlashCounter % 2 == 0) {
newImageName = SPINNER_CONNECTING_IMAGE_NAME;
} else {
newImageName = SPINNER_CONNECTING_FLASH_IMAGE_NAME;
}
[_connectingIndicatorImageView setImage:[UIImage imageNamed:newImageName]];
connectingFlashCounter++;
}
- (void)startRingingAnimation {
[self stopConnectingFlashAnimation];
_ringingAnimationTimer = [NSTimer scheduledTimerWithTimeInterval:RINGING_ROTATION_DURATION
target:self
selector:@selector(rotateConnectingIndicator)
userInfo:nil
repeats:YES];
[_ringingAnimationTimer fire];
}
- (void)rotateConnectingIndicator {
[_connectingIndicatorImageView setImage:[UIImage imageNamed:SPINNER_RINGING_IMAGE_NAME]];
[UIView animateWithDuration:RINGING_ROTATION_DURATION delay:0.0 options:UIViewAnimationOptionCurveLinear animations:^{
_connectingIndicatorImageView.transform = CGAffineTransformRotate(_connectingIndicatorImageView.transform, (float)M_PI_2);
} completion:nil];
}
- (void)performCallInSessionAnimation {
[UIView animateWithDuration:0.5f animations:^{
[_callStateImageContainerView setFrame:CGRectMake(0, _callStateImageContainerView.frame.origin.y, _callStateImageContainerView.frame.size.width, _callStateImageContainerView.frame.size.height)];
}];
}
- (void)stopRingingAnimation {
if (_ringingAnimationTimer) {
[_ringingAnimationTimer invalidate];
}
}
- (void)stopConnectingFlashAnimation {
if (_connectingFlashTimer) {
[_connectingFlashTimer invalidate];
}
}
- (void)showConnectingError {
[self stopRingingAnimation];
[self stopConnectingFlashAnimation];
[_connectingIndicatorImageView setImage:[UIImage imageNamed:SPINNER_ERROR_FLASH_IMAGE_NAME]];
}
- (void)localizeButtons {
[_endButton setTitle:END_CALL_BUTTON_TITLE forState:UIControlStateNormal];
[_answerButton setTitle:ANSWER_CALL_BUTTON_TITLE forState:UIControlStateNormal];
[_rejectButton setTitle:REJECT_CALL_BUTTON_TITLE forState:UIControlStateNormal];
}
- (void)setupButtonBorders {
_muteButton.layer.borderColor = [UIUtil blueColor].CGColor;
_speakerButton.layer.borderColor = [UIUtil blueColor].CGColor;
_muteButton.layer.borderWidth = BUTTON_BORDER_WIDTH;
_speakerButton.layer.borderWidth = BUTTON_BORDER_WIDTH;
if (_potentiallyKnownContact) {
if (_potentiallyKnownContact.image) {
[UIUtil applyRoundedBorderToImageView:&_contactImageView];
}
_nameLabel.text = [_potentiallyKnownContact fullName];
} else {
_nameLabel.text = UNKNOWN_CONTACT_NAME;
}
}
-(void) clearDetails {
_callStatusLabel.text = @"";
_nameLabel.text = @"";
_phoneNumberLabel.text = @"";
_authenicationStringLabel.text = @"";
_contactImageView.image = nil;
_authenicationStringLabel.hidden = YES;
[self displayAcceptRejectButtons:NO];
}
-(void) populateImmediateDetails {
_phoneNumberLabel.text = [_callState.remoteNumber localizedDescriptionForUser];
if (_potentiallyKnownContact) {
_nameLabel.text = [_potentiallyKnownContact fullName];
if (_potentiallyKnownContact.image) {
_contactImageView.image = _potentiallyKnownContact.image;
}
}
}
-(void) handleIncomingDetails {
[[_callState futureShortAuthenticationString] thenDo:^(NSString* sas) {
_authenicationStringLabel.hidden = NO;
_authenicationStringLabel.text = sas;
[self performCallInSessionAnimation];
}];
[[_callState observableProgress] watchLatestValue:^(CallProgress* latestProgress) {
[self onCallProgressed:latestProgress];
} onThread:[NSThread mainThread] untilCancelled:nil];
}
-(void) onCallProgressed:(CallProgress*)latestProgress {
BOOL showAcceptRejectButtons = !_callState.initiatedLocally && [latestProgress type] <= CallProgressType_Ringing;
[self displayAcceptRejectButtons:showAcceptRejectButtons];
[[AppAudioManager sharedInstance] respondToProgressChange:[latestProgress type]
forLocallyInitiatedCall:_callState.initiatedLocally];
if ([latestProgress type] == CallProgressType_Ringing) {
[self startRingingAnimation];
}
if ([latestProgress type] == CallProgressType_Terminated) {
[[_callState futureTermination] thenDo:^(CallTermination* termination) {
[self onCallEnded:termination];
[[AppAudioManager sharedInstance] respondToTerminationType:[termination type]];
}];
} else {
_callStatusLabel.text = [latestProgress localizedDescriptionForUser];
}
}
-(void) onCallEnded:(CallTermination*)termination {
[self updateViewForTermination:termination];
[[Environment phoneManager] hangupOrDenyCall];
[self dismissViewWithOptionalDelay: [termination type] != CallTerminationType_ReplacedByNext ];
if (_isMusicPaused) {
[[MPMusicPlayerController iPodMusicPlayer] play];
}
}
- (void)endCallTapped {
[[Environment phoneManager] hangupOrDenyCall];
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)muteButtonTapped {
_muteButton.selected = [[Environment phoneManager] toggleMute];
}
- (void)speakerButtonTapped {
_speakerButton.selected = [[AppAudioManager sharedInstance] toggleSpeakerPhone];
}
- (void)answerButtonTapped {
[self displayAcceptRejectButtons:NO];
[[Environment phoneManager] answerCall];
}
- (void)rejectButtonTapped {
[self displayAcceptRejectButtons:NO];
[[Environment phoneManager] hangupOrDenyCall];
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void) updateViewForTermination:(CallTermination*) termination{
NSString* message = [termination localizedDescriptionForUser];
if ([termination type] == CallTerminationType_ServerMessage) {
CallFailedServerMessage* serverMessage = [termination messageInfo];
// @todo: forcing it to be a prefix is not particularly localizable
message = [message stringByAppendingString:[serverMessage text]];
}
_endButton.backgroundColor = [UIColor grayColor];
_callStatusLabel.textColor = [UIColor redColor];
[self showConnectingError];
_callStatusLabel.text = message;
}
-(void) dismissViewWithOptionalDelay:(BOOL) useDelay {
if(useDelay && UIApplicationStateActive == [[UIApplication sharedApplication] applicationState]){
[self dismissViewControllerAfterDelay:END_CALL_CLEANUP_DELAY];
}else{
[self dismissViewControllerAnimated:NO completion:nil];
}
}
-(void) dismissViewControllerAfterDelay:(int) delay {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay), dispatch_get_main_queue(), ^{
[self dismissViewControllerAnimated:YES completion:nil];
});
}
-(void) displayAcceptRejectButtons:(BOOL) enable{
_answerButton.hidden = !enable;
_rejectButton.hidden = !enable;
_endButton.hidden = enable;
}
@end