Index: webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m |
diff --git a/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d9522045aebaa41635ac82da6c63c7d256afbb6c |
--- /dev/null |
+++ b/webrtc/examples/objc/AppRTCDemo/ios/ARDMainView.m |
@@ -0,0 +1,392 @@ |
+/* |
+ * 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 = @"AppRTCDemo"; |
+ _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 |