Move setting switches in AppRTCMobile to Settings screen

All setting switches except "Loopback mode" is now in the Settings
screen instead of the main screen. They are also persisted across app
launches.

Bug: webrtc:7748
Change-Id: Iafd84e5e39639770118e2503148d1bf7fb9c3d8d
Reviewed-on: https://chromium-review.googlesource.com/527034
Commit-Queue: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#18626}
diff --git a/webrtc/examples/objc/AppRTCMobile/ARDAppClient+Internal.h b/webrtc/examples/objc/AppRTCMobile/ARDAppClient+Internal.h
index a21ac72..3cf4040 100644
--- a/webrtc/examples/objc/AppRTCMobile/ARDAppClient+Internal.h
+++ b/webrtc/examples/objc/AppRTCMobile/ARDAppClient+Internal.h
@@ -42,9 +42,6 @@
 @property(nonatomic, strong) NSURL *webSocketURL;
 @property(nonatomic, strong) NSURL *webSocketRestURL;
 @property(nonatomic, readonly) BOOL isLoopback;
-@property(nonatomic, readonly) BOOL isAudioOnly;
-@property(nonatomic, readonly) BOOL shouldMakeAecDump;
-@property(nonatomic, readonly) BOOL shouldUseLevelControl;
 
 @property(nonatomic, strong)
     RTCMediaConstraints *defaultPeerConnectionConstraints;
diff --git a/webrtc/examples/objc/AppRTCMobile/ARDAppClient.h b/webrtc/examples/objc/AppRTCMobile/ARDAppClient.h
index b613ec9..8c27b34 100644
--- a/webrtc/examples/objc/AppRTCMobile/ARDAppClient.h
+++ b/webrtc/examples/objc/AppRTCMobile/ARDAppClient.h
@@ -70,16 +70,9 @@
 // Establishes a connection with the AppRTC servers for the given room id.
 // |settings| is an object containing settings such as video codec for the call.
 // If |isLoopback| is true, the call will connect to itself.
-// If |isAudioOnly| is true, video will be disabled for the call.
-// If |shouldMakeAecDump| is true, an aecdump will be created for the call.
-// If |shouldUseLevelControl| is true, the level controller will be used
-// in the call.
 - (void)connectToRoomWithId:(NSString *)roomId
                    settings:(ARDSettingsModel *)settings
-                 isLoopback:(BOOL)isLoopback
-                isAudioOnly:(BOOL)isAudioOnly
-          shouldMakeAecDump:(BOOL)shouldMakeAecDump
-      shouldUseLevelControl:(BOOL)shouldUseLevelControl;
+                 isLoopback:(BOOL)isLoopback;
 
 // Disconnects from the AppRTC servers and any connected clients.
 - (void)disconnect;
diff --git a/webrtc/examples/objc/AppRTCMobile/ARDAppClient.m b/webrtc/examples/objc/AppRTCMobile/ARDAppClient.m
index 7e06612..7e9fbda 100644
--- a/webrtc/examples/objc/AppRTCMobile/ARDAppClient.m
+++ b/webrtc/examples/objc/AppRTCMobile/ARDAppClient.m
@@ -126,9 +126,6 @@
 @synthesize defaultPeerConnectionConstraints =
     _defaultPeerConnectionConstraints;
 @synthesize isLoopback = _isLoopback;
-@synthesize isAudioOnly = _isAudioOnly;
-@synthesize shouldMakeAecDump = _shouldMakeAecDump;
-@synthesize shouldUseLevelControl = _shouldUseLevelControl;
 
 - (instancetype)init {
   return [self initWithDelegate:nil];
@@ -214,17 +211,11 @@
 
 - (void)connectToRoomWithId:(NSString *)roomId
                    settings:(ARDSettingsModel *)settings
-                 isLoopback:(BOOL)isLoopback
-                isAudioOnly:(BOOL)isAudioOnly
-          shouldMakeAecDump:(BOOL)shouldMakeAecDump
-      shouldUseLevelControl:(BOOL)shouldUseLevelControl {
+                 isLoopback:(BOOL)isLoopback {
   NSParameterAssert(roomId.length);
   NSParameterAssert(_state == kARDAppClientStateDisconnected);
   _settings = settings;
   _isLoopback = isLoopback;
-  _isAudioOnly = isAudioOnly;
-  _shouldMakeAecDump = shouldMakeAecDump;
-  _shouldUseLevelControl = shouldUseLevelControl;
   self.state = kARDAppClientStateConnecting;
 
 #if defined(WEBRTC_IOS)
@@ -569,7 +560,7 @@
   }
 
   // Start aecdump diagnostic recording.
-  if (_shouldMakeAecDump) {
+  if ([_settings currentCreateAecDumpSettingFromStore]) {
     NSString *filePath = [self documentsFilePathForFileName:@"webrtc-audio.aecdump"];
     if (![_factory startAecDumpWithFilePath:filePath
                              maxSizeInBytes:kARDAppClientAecDumpMaxSizeInBytes]) {
@@ -719,7 +710,7 @@
   // support or emulation (http://goo.gl/rHAnC1) so don't bother
   // trying to open a local stream.
 #if !TARGET_IPHONE_SIMULATOR
-  if (!_isAudioOnly) {
+  if (![_settings currentAudioOnlySettingFromStore]) {
     RTCVideoSource *source = [_factory videoSource];
     RTCCameraVideoCapturer *capturer = [[RTCCameraVideoCapturer alloc] initWithDelegate:source];
     [_delegate appClient:self didCreateLocalCapturer:capturer];
@@ -758,8 +749,9 @@
 #pragma mark - Defaults
 
  - (RTCMediaConstraints *)defaultMediaAudioConstraints {
-   NSString *valueLevelControl = _shouldUseLevelControl ?
-       kRTCMediaConstraintsValueTrue : kRTCMediaConstraintsValueFalse;
+   NSString *valueLevelControl = [_settings currentUseLevelControllerSettingFromStore] ?
+       kRTCMediaConstraintsValueTrue :
+       kRTCMediaConstraintsValueFalse;
    NSDictionary *mandatoryConstraints = @{ kRTCMediaConstraintsLevelControl : valueLevelControl };
    RTCMediaConstraints *constraints =
        [[RTCMediaConstraints alloc] initWithMandatoryConstraints:mandatoryConstraints
diff --git a/webrtc/examples/objc/AppRTCMobile/ARDSettingsModel.h b/webrtc/examples/objc/AppRTCMobile/ARDSettingsModel.h
index 0f07389..8b2679f 100644
--- a/webrtc/examples/objc/AppRTCMobile/ARDSettingsModel.h
+++ b/webrtc/examples/objc/AppRTCMobile/ARDSettingsModel.h
@@ -11,6 +11,7 @@
 #import <Foundation/Foundation.h>
 
 NS_ASSUME_NONNULL_BEGIN
+
 /**
  * Model class for user defined settings.
  *
@@ -79,5 +80,55 @@
  */
 - (void)storeMaxBitrateSetting:(nullable NSNumber *)bitrate;
 
+/**
+ * Returns current audio only setting from store if present or default (NO) otherwise.
+ */
+- (BOOL)currentAudioOnlySettingFromStore;
+
+/**
+ * Stores the provided audio only setting into the store.
+ *
+ * @param setting the boolean value to be stored.
+ */
+- (void)storeAudioOnlySetting:(BOOL)audioOnly;
+
+/**
+ * Returns current create AecDump setting from store if present or default (NO) otherwise.
+ */
+- (BOOL)currentCreateAecDumpSettingFromStore;
+
+/**
+ * Stores the provided create AecDump setting into the store.
+ *
+ * @param setting the boolean value to be stored.
+ */
+- (void)storeCreateAecDumpSetting:(BOOL)createAecDump;
+
+/**
+ * Returns current setting whether to use level controller from store if present or default (NO)
+ * otherwise.
+ */
+- (BOOL)currentUseLevelControllerSettingFromStore;
+
+/**
+ * Stores the provided use level controller setting into the store.
+ *
+ * @param setting the boolean value to be stored.
+ */
+- (void)storeUseLevelControllerSetting:(BOOL)useLevelController;
+
+/**
+ * Returns current setting whether to use manual audio config from store if present or default (YES)
+ * otherwise.
+ */
+- (BOOL)currentUseManualAudioConfigSettingFromStore;
+
+/**
+ * Stores the provided use manual audio config setting into the store.
+ *
+ * @param setting the boolean value to be stored.
+ */
+- (void)storeUseManualAudioConfigSetting:(BOOL)useManualAudioConfig;
+
 @end
 NS_ASSUME_NONNULL_END
diff --git a/webrtc/examples/objc/AppRTCMobile/ARDSettingsModel.m b/webrtc/examples/objc/AppRTCMobile/ARDSettingsModel.m
index 78d404a..0d3ca7c 100644
--- a/webrtc/examples/objc/AppRTCMobile/ARDSettingsModel.m
+++ b/webrtc/examples/objc/AppRTCMobile/ARDSettingsModel.m
@@ -56,13 +56,8 @@
 }
 
 - (NSString *)currentVideoResolutionSettingFromStore {
-  NSString *resolution = [[self settingsStore] videoResolution];
-  if (!resolution) {
-    resolution = [self defaultVideoResolutionSetting];
-    // To ensure consistency add the default to the store.
-    [[self settingsStore] setVideoResolution:resolution];
-  }
-  return resolution;
+  [self registerStoreDefaults];
+  return [[self settingsStore] videoResolution];
 }
 
 - (BOOL)storeVideoResolutionSetting:(NSString *)resolution {
@@ -78,12 +73,8 @@
 }
 
 - (NSString *)currentVideoCodecSettingFromStore {
-  NSString *videoCodec = [[self settingsStore] videoCodec];
-  if (!videoCodec) {
-    videoCodec = [self defaultVideoCodecSetting];
-    [[self settingsStore] setVideoCodec:videoCodec];
-  }
-  return videoCodec;
+  [self registerStoreDefaults];
+  return [[self settingsStore] videoCodec];
 }
 
 - (BOOL)storeVideoCodecSetting:(NSString *)videoCodec {
@@ -95,6 +86,7 @@
 }
 
 - (nullable NSNumber *)currentMaxBitrateSettingFromStore {
+  [self registerStoreDefaults];
   return [[self settingsStore] maxBitrate];
 }
 
@@ -102,11 +94,44 @@
   [[self settingsStore] setMaxBitrate:bitrate];
 }
 
+- (BOOL)currentAudioOnlySettingFromStore {
+  return [[self settingsStore] audioOnly];
+}
+
+- (void)storeAudioOnlySetting:(BOOL)audioOnly {
+  [[self settingsStore] setAudioOnly:audioOnly];
+}
+
+- (BOOL)currentCreateAecDumpSettingFromStore {
+  return [[self settingsStore] createAecDump];
+}
+
+- (void)storeCreateAecDumpSetting:(BOOL)createAecDump {
+  [[self settingsStore] setCreateAecDump:createAecDump];
+}
+
+- (BOOL)currentUseLevelControllerSettingFromStore {
+  return [[self settingsStore] useLevelController];
+}
+
+- (void)storeUseLevelControllerSetting:(BOOL)useLevelController {
+  [[self settingsStore] setUseLevelController:useLevelController];
+}
+
+- (BOOL)currentUseManualAudioConfigSettingFromStore {
+  return [[self settingsStore] useManualAudioConfig];
+}
+
+- (void)storeUseManualAudioConfigSetting:(BOOL)useManualAudioConfig {
+  [[self settingsStore] setUseManualAudioConfig:useManualAudioConfig];
+}
+
 #pragma mark - Testable
 
 - (ARDSettingsStore *)settingsStore {
   if (!_settingsStore) {
     _settingsStore = [[ARDSettingsStore alloc] init];
+    [self registerStoreDefaults];
   }
   return _settingsStore;
 }
@@ -128,6 +153,10 @@
   return [self availableVideoResolutions][0];
 }
 
+- (NSString *)defaultVideoCodecSetting {
+  return videoCodecsStaticValues()[0];
+}
+
 - (int)videoResolutionComponentAtIndex:(int)index inString:(NSString *)resolution {
   if (index != 0 && index != 1) {
     return 0;
@@ -139,8 +168,14 @@
   return components[index].intValue;
 }
 
-- (NSString *)defaultVideoCodecSetting {
-  return videoCodecsStaticValues()[0];
+- (void)registerStoreDefaults {
+  [ARDSettingsStore setDefaultsForVideoResolution:[self defaultVideoResolutionSetting]
+                                       videoCodec:[self defaultVideoCodecSetting]
+                                          bitrate:nil
+                                        audioOnly:NO
+                                    createAecDump:NO
+                               useLevelController:NO
+                             useManualAudioConfig:YES];
 }
 
 @end
diff --git a/webrtc/examples/objc/AppRTCMobile/ARDSettingsStore.h b/webrtc/examples/objc/AppRTCMobile/ARDSettingsStore.h
index 3dd0f64..3f5f791 100644
--- a/webrtc/examples/objc/AppRTCMobile/ARDSettingsStore.h
+++ b/webrtc/examples/objc/AppRTCMobile/ARDSettingsStore.h
@@ -19,6 +19,18 @@
  */
 @interface ARDSettingsStore : NSObject
 
+/**
+ * Set fallback values in case the setting has not been written by the user.
+ * @param dictionary of values to store
+ */
++ (void)setDefaultsForVideoResolution:(NSString *)videoResolution
+                           videoCodec:(NSString *)videoCodec
+                              bitrate:(nullable NSNumber *)bitrate
+                            audioOnly:(BOOL)audioOnly
+                        createAecDump:(BOOL)createAecDump
+                   useLevelController:(BOOL)useLevelController
+                 useManualAudioConfig:(BOOL)useManualAudioConfig;
+
 @property(nonatomic) NSString *videoResolution;
 @property(nonatomic) NSString *videoCodec;
 
@@ -33,5 +45,10 @@
  */
 - (void)setMaxBitrate:(nullable NSNumber *)value;
 
+@property(nonatomic) BOOL audioOnly;
+@property(nonatomic) BOOL createAecDump;
+@property(nonatomic) BOOL useLevelController;
+@property(nonatomic) BOOL useManualAudioConfig;
+
 @end
 NS_ASSUME_NONNULL_END
diff --git a/webrtc/examples/objc/AppRTCMobile/ARDSettingsStore.m b/webrtc/examples/objc/AppRTCMobile/ARDSettingsStore.m
index e25b288..8ccc438 100644
--- a/webrtc/examples/objc/AppRTCMobile/ARDSettingsStore.m
+++ b/webrtc/examples/objc/AppRTCMobile/ARDSettingsStore.m
@@ -13,6 +13,10 @@
 static NSString *const kVideoResolutionKey = @"rtc_video_resolution_key";
 static NSString *const kVideoCodecKey = @"rtc_video_codec_key";
 static NSString *const kBitrateKey = @"rtc_max_bitrate_key";
+static NSString *const kAudioOnlyKey = @"rtc_audio_only_key";
+static NSString *const kCreateAecDumpKey = @"rtc_create_aec_dump_key";
+static NSString *const kUseLevelControllerKey = @"rtc_use_level_controller_key";
+static NSString *const kUseManualAudioConfigKey = @"rtc_use_manual_audio_config_key";
 
 NS_ASSUME_NONNULL_BEGIN
 @interface ARDSettingsStore () {
@@ -23,6 +27,27 @@
 
 @implementation ARDSettingsStore
 
++ (void)setDefaultsForVideoResolution:(NSString *)videoResolution
+                           videoCodec:(NSString *)videoCodec
+                              bitrate:(nullable NSNumber *)bitrate
+                            audioOnly:(BOOL)audioOnly
+                        createAecDump:(BOOL)createAecDump
+                   useLevelController:(BOOL)useLevelController
+                 useManualAudioConfig:(BOOL)useManualAudioConfig {
+  NSMutableDictionary<NSString *, id> *defaultsDictionary = [@{
+    kVideoResolutionKey : videoResolution,
+    kVideoCodecKey : videoCodec,
+    kAudioOnlyKey : @(audioOnly),
+    kCreateAecDumpKey : @(createAecDump),
+    kUseLevelControllerKey : @(useLevelController),
+    kUseManualAudioConfigKey : @(useManualAudioConfig)
+  } mutableCopy];
+  if (bitrate) {
+    [defaultsDictionary setObject:bitrate forKey:kBitrateKey];
+  }
+  [[NSUserDefaults standardUserDefaults] registerDefaults:defaultsDictionary];
+}
+
 - (NSUserDefaults *)storage {
   if (!_storage) {
     _storage = [NSUserDefaults standardUserDefaults];
@@ -57,5 +82,41 @@
   [self.storage synchronize];
 }
 
+- (BOOL)audioOnly {
+  return [self.storage boolForKey:kAudioOnlyKey];
+}
+
+- (void)setAudioOnly:(BOOL)audioOnly {
+  [self.storage setBool:audioOnly forKey:kAudioOnlyKey];
+  [self.storage synchronize];
+}
+
+- (BOOL)createAecDump {
+  return [self.storage boolForKey:kCreateAecDumpKey];
+}
+
+- (void)setCreateAecDump:(BOOL)createAecDump {
+  [self.storage setBool:createAecDump forKey:kCreateAecDumpKey];
+  [self.storage synchronize];
+}
+
+- (BOOL)useLevelController {
+  return [self.storage boolForKey:kUseLevelControllerKey];
+}
+
+- (void)setUseLevelController:(BOOL)useLevelController {
+  [self.storage setBool:useLevelController forKey:kUseLevelControllerKey];
+  [self.storage synchronize];
+}
+
+- (BOOL)useManualAudioConfig {
+  return [self.storage boolForKey:kUseManualAudioConfigKey];
+}
+
+- (void)setUseManualAudioConfig:(BOOL)useManualAudioConfig {
+  [self.storage setBool:useManualAudioConfig forKey:kUseManualAudioConfigKey];
+  [self.storage synchronize];
+}
+
 @end
 NS_ASSUME_NONNULL_END
diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDMainView.h b/webrtc/examples/objc/AppRTCMobile/ios/ARDMainView.h
index 1f2497a..c6691c2 100644
--- a/webrtc/examples/objc/AppRTCMobile/ios/ARDMainView.h
+++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDMainView.h
@@ -14,14 +14,7 @@
 
 @protocol ARDMainViewDelegate <NSObject>
 
-- (void)mainView:(ARDMainView *)mainView
-             didInputRoom:(NSString *)room
-               isLoopback:(BOOL)isLoopback
-              isAudioOnly:(BOOL)isAudioOnly
-        shouldMakeAecDump:(BOOL)shouldMakeAecDump
-    shouldUseLevelControl:(BOOL)shouldUseLevelControl
-           useManualAudio:(BOOL)useManualAudio;
-
+- (void)mainView:(ARDMainView *)mainView didInputRoom:(NSString *)room isLoopback:(BOOL)isLoopback;
 - (void)mainViewDidToggleAudioLoop:(ARDMainView *)mainView;
 
 @end
diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDMainView.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDMainView.m
index 0308ea0..fcce3c3 100644
--- a/webrtc/examples/objc/AppRTCMobile/ios/ARDMainView.m
+++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDMainView.m
@@ -74,16 +74,8 @@
 @implementation ARDMainView {
   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;
 }
@@ -106,17 +98,6 @@
     [_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];
@@ -128,40 +109,6 @@
     [_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 setTitle:@"Start call"
                       forState:UIControlStateNormal];
@@ -208,20 +155,7 @@
                                        _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;
+  CGFloat loopbackModeTop = CGRectGetMaxY(_callOptionsLabel.frame) + kCallControlMargin * 2;
   CGRect loopbackModeRect = CGRectMake(kCallControlMargin * 3,
                                        loopbackModeTop,
                                        _loopbackSwitch.frame.size.width,
@@ -232,46 +166,7 @@
   _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;
+  CGFloat audioLoopTop = CGRectGetMaxY(loopbackModeRect) + kCallControlMargin * 3;
   _audioLoopButton.frame = CGRectMake(kCallControlMargin,
                                       audioLoopTop,
                                       _audioLoopButton.frame.size.width,
@@ -304,13 +199,7 @@
 }
 
 - (void)onStartCall:(id)sender {
-  [_delegate mainView:self
-               didInputRoom:_roomText.roomText
-                 isLoopback:_loopbackSwitch.isOn
-                isAudioOnly:_audioOnlySwitch.isOn
-          shouldMakeAecDump:_aecdumpSwitch.isOn
-      shouldUseLevelControl:_levelControlSwitch.isOn
-             useManualAudio:_useManualAudioSwitch.isOn];
+  [_delegate mainView:self didInputRoom:_roomText.roomText isLoopback:_loopbackSwitch.isOn];
 }
 
 @end
diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.m
index 0e93aae..c57dace 100644
--- a/webrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.m
+++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDMainViewController.m
@@ -44,13 +44,7 @@
 - (void)viewDidLoad {
   [super viewDidLoad];
   if ([[[NSProcessInfo processInfo] arguments] containsObject:loopbackLaunchProcessArgument]) {
-    [self mainView:nil
-                 didInputRoom:@""
-                   isLoopback:YES
-                  isAudioOnly:NO
-            shouldMakeAecDump:NO
-        shouldUseLevelControl:NO
-               useManualAudio:NO];
+    [self mainView:nil didInputRoom:@"" isLoopback:YES];
   }
 }
 
@@ -91,13 +85,7 @@
 
 #pragma mark - ARDMainViewDelegate
 
-- (void)mainView:(ARDMainView *)mainView
-             didInputRoom:(NSString *)room
-               isLoopback:(BOOL)isLoopback
-              isAudioOnly:(BOOL)isAudioOnly
-        shouldMakeAecDump:(BOOL)shouldMakeAecDump
-    shouldUseLevelControl:(BOOL)shouldUseLevelControl
-           useManualAudio:(BOOL)useManualAudio {
+- (void)mainView:(ARDMainView *)mainView didInputRoom:(NSString *)room isLoopback:(BOOL)isLoopback {
   if (!room.length) {
     if (isLoopback) {
       // If this is a loopback call, allow a generated room name.
@@ -132,17 +120,16 @@
     return;
   }
 
+  ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init];
+
   RTCAudioSession *session = [RTCAudioSession sharedInstance];
-  session.useManualAudio = useManualAudio;
+  session.useManualAudio = [settingsModel currentUseManualAudioConfigSettingFromStore];
   session.isAudioEnabled = NO;
 
   // Kick off the video call.
   ARDVideoCallViewController *videoCallViewController =
       [[ARDVideoCallViewController alloc] initForRoom:trimmedRoom
                                            isLoopback:isLoopback
-                                          isAudioOnly:isAudioOnly
-                                    shouldMakeAecDump:shouldMakeAecDump
-                                shouldUseLevelControl:shouldUseLevelControl
                                              delegate:self];
   videoCallViewController.modalTransitionStyle =
       UIModalTransitionStyleCrossDissolve;
diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m
index 09a3d6d..fce5df7 100644
--- a/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m
+++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDSettingsViewController.m
@@ -14,11 +14,19 @@
 NS_ASSUME_NONNULL_BEGIN
 
 typedef NS_ENUM(int, ARDSettingsSections) {
-  ARDSettingsSectionVideoResolution = 0,
+  ARDSettingsSectionAudioSettings = 0,
+  ARDSettingsSectionVideoResolution,
   ARDSettingsSectionVideoCodec,
   ARDSettingsSectionBitRate,
 };
 
+typedef NS_ENUM(int, ARDAudioSettingsOptions) {
+  ARDAudioSettingsAudioOnly = 0,
+  ARDAudioSettingsCreateAecDump,
+  ARDAudioSettingsUseLevelController,
+  ARDAudioSettingsUseManualAudioConfig,
+};
+
 @interface ARDSettingsViewController () <UITextFieldDelegate> {
   ARDSettingsModel *_settingsModel;
 }
@@ -46,22 +54,16 @@
 
 - (void)viewWillAppear:(BOOL)animated {
   [super viewWillAppear:animated];
-  [self addCheckmarkInSection:ARDSettingsSectionVideoResolution
-                    withArray:[self videoResolutionArray]
-                    selecting:[_settingsModel currentVideoResolutionSettingFromStore]];
-  [self addCheckmarkInSection:ARDSettingsSectionVideoCodec
-                    withArray:[self videoCodecArray]
-                    selecting:[_settingsModel currentVideoCodecSettingFromStore]];
 }
 
 #pragma mark - Data source
 
 - (NSArray<NSString *> *)videoResolutionArray {
-  return _settingsModel.availableVideoResolutions;
+  return [_settingsModel availableVideoResolutions];
 }
 
 - (NSArray<NSString *> *)videoCodecArray {
-  return _settingsModel.availableVideoCodecs;
+  return [_settingsModel availableVideoCodecs];
 }
 
 #pragma mark -
@@ -74,16 +76,6 @@
   self.navigationItem.leftBarButtonItem = barItem;
 }
 
-- (void)addCheckmarkInSection:(int)section
-                    withArray:(NSArray<NSString*>*) array
-                    selecting:(NSString*)selection {
-  NSUInteger indexOfSelection = [array indexOfObject:selection];
-  NSIndexPath *pathToBeDecorated = [NSIndexPath indexPathForRow:indexOfSelection
-                                                      inSection:section];
-  UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:pathToBeDecorated];
-  cell.accessoryType = UITableViewCellAccessoryCheckmark;
-}
-
 #pragma mark - Dismissal of view controller
 
 - (void)dismissModally:(id)sender {
@@ -93,11 +85,13 @@
 #pragma mark - Table view data source
 
 - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
-  return 3;
+  return 4;
 }
 
 - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
   switch (section) {
+    case ARDSettingsSectionAudioSettings:
+      return 4;
     case ARDSettingsSectionVideoResolution:
       return self.videoResolutionArray.count;
     case ARDSettingsSectionVideoCodec:
@@ -133,6 +127,8 @@
 - (nullable NSString *)tableView:(UITableView *)tableView
          titleForHeaderInSection:(NSInteger)section {
   switch (section) {
+    case ARDSettingsSectionAudioSettings:
+      return @"Audio";
     case ARDSettingsSectionVideoResolution:
       return @"Video resolution";
     case ARDSettingsSectionVideoCodec:
@@ -147,6 +143,9 @@
 - (UITableViewCell *)tableView:(UITableView *)tableView
          cellForRowAtIndexPath:(NSIndexPath *)indexPath {
   switch (indexPath.section) {
+    case ARDSettingsSectionAudioSettings:
+      return [self audioSettingsTableViewCellForTableView:tableView atIndexPath:indexPath];
+
     case ARDSettingsSectionVideoResolution:
       return [self videoResolutionTableViewCellForTableView:tableView atIndexPath:indexPath];
 
@@ -184,7 +183,14 @@
     cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                   reuseIdentifier:dequeueIdentifier];
   }
-  cell.textLabel.text = self.videoResolutionArray[indexPath.row];
+  NSString *resolution = self.videoResolutionArray[indexPath.row];
+  cell.textLabel.text = resolution;
+  if ([resolution isEqualToString:[_settingsModel currentVideoResolutionSettingFromStore]]) {
+    cell.accessoryType = UITableViewCellAccessoryCheckmark;
+  } else {
+    cell.accessoryType = UITableViewCellAccessoryNone;
+  }
+
   return cell;
 }
 
@@ -208,7 +214,13 @@
     cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                   reuseIdentifier:dequeueIdentifier];
   }
-  cell.textLabel.text = self.videoCodecArray[indexPath.row];
+  NSString *codec = self.videoCodecArray[indexPath.row];
+  cell.textLabel.text = codec;
+  if ([codec isEqualToString:[_settingsModel currentVideoCodecSettingFromStore]]) {
+    cell.accessoryType = UITableViewCellAccessoryCheckmark;
+  } else {
+    cell.accessoryType = UITableViewCellAccessoryNone;
+  }
 
   return cell;
 }
@@ -275,5 +287,83 @@
   [_settingsModel storeMaxBitrateSetting:bitrateNumber];
 }
 
+#pragma mark - Table view delegate(Audio settings)
+
+- (UITableViewCell *)audioSettingsTableViewCellForTableView:(UITableView *)tableView
+                                                atIndexPath:(NSIndexPath *)indexPath {
+  NSString *dequeueIdentifier = @"ARDSettingsAudioSettingsCellIdentifier";
+  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:dequeueIdentifier];
+  if (!cell) {
+    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
+                                  reuseIdentifier:dequeueIdentifier];
+    cell.selectionStyle = UITableViewCellSelectionStyleNone;
+    UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
+    switchView.tag = indexPath.row;
+    [switchView addTarget:self
+                   action:@selector(audioSettingSwitchChanged:)
+         forControlEvents:UIControlEventValueChanged];
+    cell.accessoryView = switchView;
+  }
+
+  cell.textLabel.text = [self labelForAudioSettingAtIndexPathRow:indexPath.row];
+  UISwitch *switchView = (UISwitch *)cell.accessoryView;
+  switchView.on = [self valueForAudioSettingAtIndexPathRow:indexPath.row];
+
+  return cell;
+}
+
+- (NSString *)labelForAudioSettingAtIndexPathRow:(NSInteger)setting {
+  switch (setting) {
+    case ARDAudioSettingsAudioOnly:
+      return @"Audio only";
+    case ARDAudioSettingsCreateAecDump:
+      return @"Create AecDump";
+    case ARDAudioSettingsUseLevelController:
+      return @"Use level controller";
+    case ARDAudioSettingsUseManualAudioConfig:
+      return @"Use manual audio config";
+    default:
+      return @"";
+  }
+}
+
+- (BOOL)valueForAudioSettingAtIndexPathRow:(NSInteger)setting {
+  switch (setting) {
+    case ARDAudioSettingsAudioOnly:
+      return [_settingsModel currentAudioOnlySettingFromStore];
+    case ARDAudioSettingsCreateAecDump:
+      return [_settingsModel currentCreateAecDumpSettingFromStore];
+    case ARDAudioSettingsUseLevelController:
+      return [_settingsModel currentUseLevelControllerSettingFromStore];
+    case ARDAudioSettingsUseManualAudioConfig:
+      return [_settingsModel currentUseManualAudioConfigSettingFromStore];
+    default:
+      return NO;
+  }
+}
+
+- (void)audioSettingSwitchChanged:(UISwitch *)sender {
+  switch (sender.tag) {
+    case ARDAudioSettingsAudioOnly: {
+      [_settingsModel storeAudioOnlySetting:sender.isOn];
+      break;
+    }
+    case ARDAudioSettingsCreateAecDump: {
+      [_settingsModel storeCreateAecDumpSetting:sender.isOn];
+      break;
+    }
+    case ARDAudioSettingsUseLevelController: {
+      [_settingsModel storeUseLevelControllerSetting:sender.isOn];
+      break;
+    }
+    case ARDAudioSettingsUseManualAudioConfig: {
+      [_settingsModel storeUseManualAudioConfigSetting:sender.isOn];
+      break;
+    }
+    default:
+      break;
+  }
+}
+
 @end
 NS_ASSUME_NONNULL_END
diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.h b/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.h
index 3ca2dc2..bdb8747 100644
--- a/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.h
+++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.h
@@ -23,9 +23,6 @@
 
 - (instancetype)initForRoom:(NSString *)room
                  isLoopback:(BOOL)isLoopback
-                isAudioOnly:(BOOL)isAudioOnly
-          shouldMakeAecDump:(BOOL)shouldMakeAecDump
-      shouldUseLevelControl:(BOOL)shouldUseLevelControl
                    delegate:(id<ARDVideoCallViewControllerDelegate>)delegate;
 
 @end
diff --git a/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m b/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m
index fd33e01..03ce626 100644
--- a/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m
+++ b/webrtc/examples/objc/AppRTCMobile/ios/ARDVideoCallViewController.m
@@ -40,21 +40,13 @@
 
 - (instancetype)initForRoom:(NSString *)room
                  isLoopback:(BOOL)isLoopback
-                isAudioOnly:(BOOL)isAudioOnly
-          shouldMakeAecDump:(BOOL)shouldMakeAecDump
-      shouldUseLevelControl:(BOOL)shouldUseLevelControl
                    delegate:(id<ARDVideoCallViewControllerDelegate>)delegate {
   if (self = [super init]) {
     ARDSettingsModel *settingsModel = [[ARDSettingsModel alloc] init];
     _delegate = delegate;
 
     _client = [[ARDAppClient alloc] initWithDelegate:self];
-    [_client connectToRoomWithId:room
-                        settings:settingsModel
-                      isLoopback:isLoopback
-                     isAudioOnly:isAudioOnly
-               shouldMakeAecDump:shouldMakeAecDump
-           shouldUseLevelControl:shouldUseLevelControl];
+    [_client connectToRoomWithId:room settings:settingsModel isLoopback:isLoopback];
   }
   return self;
 }
diff --git a/webrtc/examples/objc/AppRTCMobile/mac/APPRTCViewController.m b/webrtc/examples/objc/AppRTCMobile/mac/APPRTCViewController.m
index ab65b7e..cb82e88 100644
--- a/webrtc/examples/objc/AppRTCMobile/mac/APPRTCViewController.m
+++ b/webrtc/examples/objc/AppRTCMobile/mac/APPRTCViewController.m
@@ -402,10 +402,7 @@
   ARDAppClient* client = [[ARDAppClient alloc] initWithDelegate:self];
   [client connectToRoomWithId:roomId
                      settings:[[ARDSettingsModel alloc] init]  // Use default settings.
-                   isLoopback:isLoopback
-                  isAudioOnly:NO
-            shouldMakeAecDump:NO
-        shouldUseLevelControl:NO];
+                   isLoopback:isLoopback];
   _client = client;
 }
 
diff --git a/webrtc/examples/objc/AppRTCMobile/tests/ARDAppClient_xctest.mm b/webrtc/examples/objc/AppRTCMobile/tests/ARDAppClient_xctest.mm
index fc7b768..b1ce5f0 100644
--- a/webrtc/examples/objc/AppRTCMobile/tests/ARDAppClient_xctest.mm
+++ b/webrtc/examples/objc/AppRTCMobile/tests/ARDAppClient_xctest.mm
@@ -116,6 +116,15 @@
   return mockTURNClient;
 }
 
+- (id)mockSettingsModel {
+  ARDSettingsModel *model = [[ARDSettingsModel alloc] init];
+  id partialMock = [OCMockObject partialMockForObject:model];
+  [[[partialMock stub] andReturn:@[ @"640x480", @"960x540", @"1280x720" ]]
+      availableVideoResolutions];
+
+  return model;
+}
+
 - (ARDAppClient *)createAppClientForRoomId:(NSString *)roomId
                                   clientId:(NSString *)clientId
                                isInitiator:(BOOL)isInitiator
@@ -209,18 +218,8 @@
   weakAnswerer = answerer;
 
   // Kick off connection.
-  [caller connectToRoomWithId:roomId
-                     settings:[[ARDSettingsModel alloc] init]
-                   isLoopback:NO
-                  isAudioOnly:NO
-            shouldMakeAecDump:NO
-        shouldUseLevelControl:NO];
-  [answerer connectToRoomWithId:roomId
-                       settings:[[ARDSettingsModel alloc] init]
-                     isLoopback:NO
-                    isAudioOnly:NO
-              shouldMakeAecDump:NO
-          shouldUseLevelControl:NO];
+  [caller connectToRoomWithId:roomId settings:[self mockSettingsModel] isLoopback:NO];
+  [answerer connectToRoomWithId:roomId settings:[self mockSettingsModel] isLoopback:NO];
   [self waitForExpectationsWithTimeout:20 handler:^(NSError *error) {
     if (error) {
       XCTFail(@"Expectation failed with error %@.", error);
@@ -253,11 +252,8 @@
 
   // Kick off connection.
   [caller connectToRoomWithId:roomId
-                     settings:[[ARDSettingsModel alloc] init]
-                   isLoopback:NO
-                  isAudioOnly:NO
-            shouldMakeAecDump:NO
-        shouldUseLevelControl:NO];
+                     settings:[self mockSettingsModel]
+                   isLoopback:NO];
   [self waitForExpectationsWithTimeout:20 handler:^(NSError *error) {
     if (error) {
       XCTFail("Expectation timed out with error: %@.", error);
diff --git a/webrtc/examples/objc/AppRTCMobile/tests/ARDSettingsModel_xctest.mm b/webrtc/examples/objc/AppRTCMobile/tests/ARDSettingsModel_xctest.mm
index b139b97..b44f935 100644
--- a/webrtc/examples/objc/AppRTCMobile/tests/ARDSettingsModel_xctest.mm
+++ b/webrtc/examples/objc/AppRTCMobile/tests/ARDSettingsModel_xctest.mm
@@ -25,9 +25,8 @@
 
 @implementation ARDSettingsModelTests
 
-- (id)setupMockStoreWithVideoResolution:(NSString *)videoResolution {
+- (id)setupMockStore {
   id storeMock = [OCMockObject mockForClass:[ARDSettingsStore class]];
-  [([[storeMock stub] andReturn:videoResolution])videoResolution];
 
   id partialMock = [OCMockObject partialMockForObject:_model];
   [[[partialMock stub] andReturn:storeMock] settingsStore];
@@ -41,39 +40,57 @@
   _model = [[ARDSettingsModel alloc] init];
 }
 
-- (void)testDefaultMediaFromStore {
-  id storeMock = [self setupMockStoreWithVideoResolution:nil];
-  [[storeMock expect] setVideoResolution:@"640x480"];
-
+- (void)testRetrievingSetting {
+  id storeMock = [self setupMockStore];
+  [[[storeMock expect] andReturn:@"640x480"] videoResolution];
   NSString *string = [_model currentVideoResolutionSettingFromStore];
 
   XCTAssertEqualObjects(string, @"640x480");
-  [storeMock verify];
 }
 
 - (void)testStoringInvalidConstraintReturnsNo {
-  __unused id storeMock = [self setupMockStoreWithVideoResolution:@"960x480"];
+  id storeMock = [self setupMockStore];
+  [([[storeMock stub] andReturn:@"960x480"])videoResolution];
   XCTAssertFalse([_model storeVideoResolutionSetting:@"960x480"]);
 }
 
 - (void)testWidthConstraintFromStore {
-  [self setupMockStoreWithVideoResolution:@"1270x480"];
+  id storeMock = [self setupMockStore];
+  [([[storeMock stub] andReturn:@"1270x480"])videoResolution];
   int width = [_model currentVideoResolutionWidthFromStore];
 
   XCTAssertEqual(width, 1270);
 }
 
 - (void)testHeightConstraintFromStore {
-  [self setupMockStoreWithVideoResolution:@"960x540"];
+  id storeMock = [self setupMockStore];
+  [([[storeMock stub] andReturn:@"960x540"])videoResolution];
   int height = [_model currentVideoResolutionHeightFromStore];
 
   XCTAssertEqual(height, 540);
 }
 
 - (void)testConstraintComponentIsNilWhenInvalidConstraintString {
-  [self setupMockStoreWithVideoResolution:@"invalid"];
+  id storeMock = [self setupMockStore];
+  [([[storeMock stub] andReturn:@"invalid"])videoResolution];
   int width = [_model currentVideoResolutionWidthFromStore];
 
   XCTAssertEqual(width, 0);
 }
+
+- (void)testStoringAudioSetting {
+  id storeMock = [self setupMockStore];
+  [[storeMock expect] setAudioOnly:YES];
+
+  [_model storeAudioOnlySetting:YES];
+  [storeMock verify];
+}
+
+- (void)testReturningDefaultCallOption {
+  id storeMock = [self setupMockStore];
+  [[[storeMock stub] andReturnValue:@YES] useManualAudioConfig];
+
+  XCTAssertTrue([_model currentUseManualAudioConfigSettingFromStore]);
+}
+
 @end