| /* |
| * Copyright 2015 The WebRTC Project Authors. All rights reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #import "ARDMainView.h" |
| |
| #import "UIImage+ARDUtilities.h" |
| |
| // TODO(tkchin): retrieve status bar height dynamically. |
| static CGFloat const kStatusBarHeight = 20; |
| |
| static CGFloat const kRoomTextButtonSize = 40; |
| static CGFloat const kRoomTextFieldHeight = 40; |
| static CGFloat const kRoomTextFieldMargin = 8; |
| static CGFloat const kCallControlMargin = 8; |
| |
| // Helper view that contains a text field and a clear button. |
| @interface ARDRoomTextField : UIView <UITextFieldDelegate> |
| @property(nonatomic, readonly) NSString *roomText; |
| @end |
| |
| @implementation ARDRoomTextField { |
| UITextField *_roomText; |
| UIButton *_clearButton; |
| } |
| |
| - (instancetype)initWithFrame:(CGRect)frame { |
| if (self = [super initWithFrame:frame]) { |
| _roomText = [[UITextField alloc] initWithFrame:CGRectZero]; |
| _roomText.borderStyle = UITextBorderStyleNone; |
| _roomText.font = [UIFont fontWithName:@"Roboto" size:12]; |
| _roomText.placeholder = @"Room name"; |
| _roomText.autocorrectionType = UITextAutocorrectionTypeNo; |
| _roomText.autocapitalizationType = UITextAutocapitalizationTypeNone; |
| _roomText.delegate = self; |
| [_roomText addTarget:self |
| action:@selector(textFieldDidChange:) |
| forControlEvents:UIControlEventEditingChanged]; |
| [self addSubview:_roomText]; |
| |
| _clearButton = [UIButton buttonWithType:UIButtonTypeCustom]; |
| UIImage *image = [UIImage imageForName:@"ic_clear_black_24dp.png" |
| color:[UIColor colorWithWhite:0 alpha:.4]]; |
| |
| [_clearButton setImage:image forState:UIControlStateNormal]; |
| [_clearButton addTarget:self |
| action:@selector(onClear:) |
| forControlEvents:UIControlEventTouchUpInside]; |
| _clearButton.hidden = YES; |
| [self addSubview:_clearButton]; |
| |
| // Give rounded corners and a light gray border. |
| self.layer.borderWidth = 1; |
| self.layer.borderColor = [[UIColor lightGrayColor] CGColor]; |
| self.layer.cornerRadius = 2; |
| } |
| return self; |
| } |
| |
| - (void)layoutSubviews { |
| CGRect bounds = self.bounds; |
| _clearButton.frame = CGRectMake(CGRectGetMaxX(bounds) - kRoomTextButtonSize, |
| CGRectGetMinY(bounds), |
| kRoomTextButtonSize, |
| kRoomTextButtonSize); |
| _roomText.frame = CGRectMake( |
| CGRectGetMinX(bounds) + kRoomTextFieldMargin, |
| CGRectGetMinY(bounds), |
| CGRectGetMinX(_clearButton.frame) - CGRectGetMinX(bounds) - |
| kRoomTextFieldMargin, |
| kRoomTextFieldHeight); |
| } |
| |
| - (CGSize)sizeThatFits:(CGSize)size { |
| size.height = kRoomTextFieldHeight; |
| return size; |
| } |
| |
| - (NSString *)roomText { |
| return _roomText.text; |
| } |
| |
| #pragma mark - UITextFieldDelegate |
| |
| - (BOOL)textFieldShouldReturn:(UITextField *)textField { |
| // There is no other control that can take focus, so manually resign focus |
| // when return (Join) is pressed to trigger |textFieldDidEndEditing|. |
| [textField resignFirstResponder]; |
| return YES; |
| } |
| |
| #pragma mark - Private |
| |
| - (void)textFieldDidChange:(id)sender { |
| [self updateClearButton]; |
| } |
| |
| - (void)onClear:(id)sender { |
| _roomText.text = @""; |
| [self updateClearButton]; |
| [_roomText resignFirstResponder]; |
| } |
| |
| - (void)updateClearButton { |
| _clearButton.hidden = _roomText.text.length == 0; |
| } |
| |
| @end |
| |
| @implementation ARDMainView { |
| UILabel *_appLabel; |
| ARDRoomTextField *_roomText; |
| UILabel *_callOptionsLabel; |
| UISwitch *_audioOnlySwitch; |
| UILabel *_audioOnlyLabel; |
| UISwitch *_aecdumpSwitch; |
| UILabel *_aecdumpLabel; |
| UISwitch *_levelControlSwitch; |
| UILabel *_levelControlLabel; |
| UISwitch *_loopbackSwitch; |
| UILabel *_loopbackLabel; |
| UISwitch *_useManualAudioSwitch; |
| UILabel *_useManualAudioLabel; |
| UIButton *_startCallButton; |
| UIButton *_audioLoopButton; |
| } |
| |
| @synthesize delegate = _delegate; |
| @synthesize isAudioLoopPlaying = _isAudioLoopPlaying; |
| |
| - (instancetype)initWithFrame:(CGRect)frame { |
| if (self = [super initWithFrame:frame]) { |
| _appLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
| _appLabel.text = @"AppRTCMobile"; |
| _appLabel.font = [UIFont fontWithName:@"Roboto" size:34]; |
| _appLabel.textColor = [UIColor colorWithWhite:0 alpha:.2]; |
| [_appLabel sizeToFit]; |
| [self addSubview:_appLabel]; |
| |
| _roomText = [[ARDRoomTextField alloc] initWithFrame:CGRectZero]; |
| [self addSubview:_roomText]; |
| |
| UIFont *controlFont = [UIFont fontWithName:@"Roboto" size:20]; |
| UIColor *controlFontColor = [UIColor colorWithWhite:0 alpha:.6]; |
| |
| _callOptionsLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
| _callOptionsLabel.text = @"Call Options"; |
| _callOptionsLabel.font = controlFont; |
| _callOptionsLabel.textColor = controlFontColor; |
| [_callOptionsLabel sizeToFit]; |
| [self addSubview:_callOptionsLabel]; |
| |
| _audioOnlySwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; |
| [_audioOnlySwitch sizeToFit]; |
| [self addSubview:_audioOnlySwitch]; |
| |
| _audioOnlyLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
| _audioOnlyLabel.text = @"Audio only"; |
| _audioOnlyLabel.font = controlFont; |
| _audioOnlyLabel.textColor = controlFontColor; |
| [_audioOnlyLabel sizeToFit]; |
| [self addSubview:_audioOnlyLabel]; |
| |
| _loopbackSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; |
| [_loopbackSwitch sizeToFit]; |
| [self addSubview:_loopbackSwitch]; |
| |
| _loopbackLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
| _loopbackLabel.text = @"Loopback mode"; |
| _loopbackLabel.font = controlFont; |
| _loopbackLabel.textColor = controlFontColor; |
| [_loopbackLabel sizeToFit]; |
| [self addSubview:_loopbackLabel]; |
| |
| _aecdumpSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; |
| [_aecdumpSwitch sizeToFit]; |
| [self addSubview:_aecdumpSwitch]; |
| |
| _aecdumpLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
| _aecdumpLabel.text = @"Create AecDump"; |
| _aecdumpLabel.font = controlFont; |
| _aecdumpLabel.textColor = controlFontColor; |
| [_aecdumpLabel sizeToFit]; |
| [self addSubview:_aecdumpLabel]; |
| |
| _levelControlSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; |
| [_levelControlSwitch sizeToFit]; |
| [self addSubview:_levelControlSwitch]; |
| |
| _levelControlLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
| _levelControlLabel.text = @"Use level controller"; |
| _levelControlLabel.font = controlFont; |
| _levelControlLabel.textColor = controlFontColor; |
| [_levelControlLabel sizeToFit]; |
| [self addSubview:_levelControlLabel]; |
| |
| _useManualAudioSwitch = [[UISwitch alloc] initWithFrame:CGRectZero]; |
| [_useManualAudioSwitch sizeToFit]; |
| _useManualAudioSwitch.on = YES; |
| [self addSubview:_useManualAudioSwitch]; |
| |
| _useManualAudioLabel = [[UILabel alloc] initWithFrame:CGRectZero]; |
| _useManualAudioLabel.text = @"Use manual audio config"; |
| _useManualAudioLabel.font = controlFont; |
| _useManualAudioLabel.textColor = controlFontColor; |
| [_useManualAudioLabel sizeToFit]; |
| [self addSubview:_useManualAudioLabel]; |
| |
| _startCallButton = [UIButton buttonWithType:UIButtonTypeSystem]; |
| _startCallButton.backgroundColor = [UIColor blueColor]; |
| _startCallButton.layer.cornerRadius = 10; |
| _startCallButton.clipsToBounds = YES; |
| _startCallButton.contentEdgeInsets = UIEdgeInsetsMake(5, 10, 5, 10); |
| [_startCallButton setTitle:@"Start call" |
| forState:UIControlStateNormal]; |
| _startCallButton.titleLabel.font = controlFont; |
| [_startCallButton setTitleColor:[UIColor whiteColor] |
| forState:UIControlStateNormal]; |
| [_startCallButton setTitleColor:[UIColor lightGrayColor] |
| forState:UIControlStateSelected]; |
| [_startCallButton sizeToFit]; |
| [_startCallButton addTarget:self |
| action:@selector(onStartCall:) |
| forControlEvents:UIControlEventTouchUpInside]; |
| [self addSubview:_startCallButton]; |
| |
| // Used to test what happens to sounds when calls are in progress. |
| _audioLoopButton = [UIButton buttonWithType:UIButtonTypeSystem]; |
| _audioLoopButton.layer.cornerRadius = 10; |
| _audioLoopButton.clipsToBounds = YES; |
| _audioLoopButton.contentEdgeInsets = UIEdgeInsetsMake(5, 10, 5, 10); |
| _audioLoopButton.titleLabel.font = controlFont; |
| [_audioLoopButton setTitleColor:[UIColor whiteColor] |
| forState:UIControlStateNormal]; |
| [_audioLoopButton setTitleColor:[UIColor lightGrayColor] |
| forState:UIControlStateSelected]; |
| [self updateAudioLoopButton]; |
| [_audioLoopButton addTarget:self |
| action:@selector(onToggleAudioLoop:) |
| forControlEvents:UIControlEventTouchUpInside]; |
| [self addSubview:_audioLoopButton]; |
| |
| self.backgroundColor = [UIColor whiteColor]; |
| } |
| return self; |
| } |
| |
| - (void)setIsAudioLoopPlaying:(BOOL)isAudioLoopPlaying { |
| if (_isAudioLoopPlaying == isAudioLoopPlaying) { |
| return; |
| } |
| _isAudioLoopPlaying = isAudioLoopPlaying; |
| [self updateAudioLoopButton]; |
| } |
| |
| - (void)layoutSubviews { |
| CGRect bounds = self.bounds; |
| CGFloat roomTextWidth = bounds.size.width - 2 * kRoomTextFieldMargin; |
| CGFloat roomTextHeight = [_roomText sizeThatFits:bounds.size].height; |
| _roomText.frame = CGRectMake(kRoomTextFieldMargin, |
| kStatusBarHeight + kRoomTextFieldMargin, |
| roomTextWidth, |
| roomTextHeight); |
| _appLabel.center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds)); |
| |
| CGFloat callOptionsLabelTop = |
| CGRectGetMaxY(_roomText.frame) + kCallControlMargin * 4; |
| _callOptionsLabel.frame = CGRectMake(kCallControlMargin, |
| callOptionsLabelTop, |
| _callOptionsLabel.frame.size.width, |
| _callOptionsLabel.frame.size.height); |
| |
| CGFloat audioOnlyTop = |
| CGRectGetMaxY(_callOptionsLabel.frame) + kCallControlMargin * 2; |
| CGRect audioOnlyRect = CGRectMake(kCallControlMargin * 3, |
| audioOnlyTop, |
| _audioOnlySwitch.frame.size.width, |
| _audioOnlySwitch.frame.size.height); |
| _audioOnlySwitch.frame = audioOnlyRect; |
| CGFloat audioOnlyLabelCenterX = CGRectGetMaxX(audioOnlyRect) + |
| kCallControlMargin + _audioOnlyLabel.frame.size.width / 2; |
| _audioOnlyLabel.center = CGPointMake(audioOnlyLabelCenterX, |
| CGRectGetMidY(audioOnlyRect)); |
| |
| CGFloat loopbackModeTop = |
| CGRectGetMaxY(_audioOnlySwitch.frame) + kCallControlMargin; |
| CGRect loopbackModeRect = CGRectMake(kCallControlMargin * 3, |
| loopbackModeTop, |
| _loopbackSwitch.frame.size.width, |
| _loopbackSwitch.frame.size.height); |
| _loopbackSwitch.frame = loopbackModeRect; |
| CGFloat loopbackModeLabelCenterX = CGRectGetMaxX(loopbackModeRect) + |
| kCallControlMargin + _loopbackLabel.frame.size.width / 2; |
| _loopbackLabel.center = CGPointMake(loopbackModeLabelCenterX, |
| CGRectGetMidY(loopbackModeRect)); |
| |
| CGFloat aecdumpModeTop = |
| CGRectGetMaxY(_loopbackSwitch.frame) + kCallControlMargin; |
| CGRect aecdumpModeRect = CGRectMake(kCallControlMargin * 3, |
| aecdumpModeTop, |
| _aecdumpSwitch.frame.size.width, |
| _aecdumpSwitch.frame.size.height); |
| _aecdumpSwitch.frame = aecdumpModeRect; |
| CGFloat aecdumpModeLabelCenterX = CGRectGetMaxX(aecdumpModeRect) + |
| kCallControlMargin + _aecdumpLabel.frame.size.width / 2; |
| _aecdumpLabel.center = CGPointMake(aecdumpModeLabelCenterX, |
| CGRectGetMidY(aecdumpModeRect)); |
| |
| CGFloat levelControlModeTop = |
| CGRectGetMaxY(_aecdumpSwitch.frame) + kCallControlMargin; |
| CGRect levelControlModeRect = CGRectMake(kCallControlMargin * 3, |
| levelControlModeTop, |
| _levelControlSwitch.frame.size.width, |
| _levelControlSwitch.frame.size.height); |
| _levelControlSwitch.frame = levelControlModeRect; |
| CGFloat levelControlModeLabelCenterX = CGRectGetMaxX(levelControlModeRect) + |
| kCallControlMargin + _levelControlLabel.frame.size.width / 2; |
| _levelControlLabel.center = CGPointMake(levelControlModeLabelCenterX, |
| CGRectGetMidY(levelControlModeRect)); |
| |
| CGFloat useManualAudioTop = |
| CGRectGetMaxY(_levelControlSwitch.frame) + kCallControlMargin; |
| CGRect useManualAudioRect = |
| CGRectMake(kCallControlMargin * 3, |
| useManualAudioTop, |
| _useManualAudioSwitch.frame.size.width, |
| _useManualAudioSwitch.frame.size.height); |
| _useManualAudioSwitch.frame = useManualAudioRect; |
| CGFloat useManualAudioLabelCenterX = CGRectGetMaxX(useManualAudioRect) + |
| kCallControlMargin + _useManualAudioLabel.frame.size.width / 2; |
| _useManualAudioLabel.center = |
| CGPointMake(useManualAudioLabelCenterX, |
| CGRectGetMidY(useManualAudioRect)); |
| |
| CGFloat audioLoopTop = |
| CGRectGetMaxY(useManualAudioRect) + kCallControlMargin * 3; |
| _audioLoopButton.frame = CGRectMake(kCallControlMargin, |
| audioLoopTop, |
| _audioLoopButton.frame.size.width, |
| _audioLoopButton.frame.size.height); |
| |
| CGFloat startCallTop = |
| CGRectGetMaxY(_audioLoopButton.frame) + kCallControlMargin * 3; |
| _startCallButton.frame = CGRectMake(kCallControlMargin, |
| startCallTop, |
| _startCallButton.frame.size.width, |
| _startCallButton.frame.size.height); |
| } |
| |
| #pragma mark - Private |
| |
| - (void)updateAudioLoopButton { |
| if (_isAudioLoopPlaying) { |
| _audioLoopButton.backgroundColor = [UIColor redColor]; |
| [_audioLoopButton setTitle:@"Stop sound" |
| forState:UIControlStateNormal]; |
| [_audioLoopButton sizeToFit]; |
| } else { |
| _audioLoopButton.backgroundColor = [UIColor greenColor]; |
| [_audioLoopButton setTitle:@"Play sound" |
| forState:UIControlStateNormal]; |
| [_audioLoopButton sizeToFit]; |
| } |
| } |
| |
| - (void)onToggleAudioLoop:(id)sender { |
| [_delegate mainViewDidToggleAudioLoop:self]; |
| } |
| |
| - (void)onStartCall:(id)sender { |
| NSString *room = _roomText.roomText; |
| // If this is a loopback call, allow a generated room name. |
| if (!room.length && _loopbackSwitch.isOn) { |
| room = [[NSUUID UUID] UUIDString]; |
| } |
| room = [room stringByReplacingOccurrencesOfString:@"-" withString:@""]; |
| [_delegate mainView:self |
| didInputRoom:room |
| isLoopback:_loopbackSwitch.isOn |
| isAudioOnly:_audioOnlySwitch.isOn |
| shouldMakeAecDump:_aecdumpSwitch.isOn |
| shouldUseLevelControl:_levelControlSwitch.isOn |
| useManualAudio:_useManualAudioSwitch.isOn]; |
| } |
| |
| @end |