1/*
2 *  Copyright 2016 The WebRTC Project Authors. All rights reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#import "ARDSettingsViewController.h"
12#import "ARDSettingsModel.h"
13#import "RTCVideoCodecInfo+HumanReadable.h"
14
15NS_ASSUME_NONNULL_BEGIN
16
17typedef NS_ENUM(int, ARDSettingsSections) {
18  ARDSettingsSectionAudioSettings = 0,
19  ARDSettingsSectionVideoResolution,
20  ARDSettingsSectionVideoCodec,
21  ARDSettingsSectionBitRate,
22};
23
24typedef NS_ENUM(int, ARDAudioSettingsOptions) {
25  ARDAudioSettingsAudioOnly = 0,
26  ARDAudioSettingsCreateAecDump,
27  ARDAudioSettingsUseManualAudioConfig,
28};
29
30@interface ARDSettingsViewController () <UITextFieldDelegate> {
31  ARDSettingsModel *_settingsModel;
32}
33
34@end
35
36@implementation ARDSettingsViewController
37
38- (instancetype)initWithStyle:(UITableViewStyle)style
39                settingsModel:(ARDSettingsModel *)settingsModel {
40  self = [super initWithStyle:style];
41  if (self) {
42    _settingsModel = settingsModel;
43  }
44  return self;
45}
46
47#pragma mark - View lifecycle
48
49- (void)viewDidLoad {
50  [super viewDidLoad];
51  self.title = @"Settings";
52  [self addDoneBarButton];
53}
54
55- (void)viewWillAppear:(BOOL)animated {
56  [super viewWillAppear:animated];
57}
58
59#pragma mark - Data source
60
61- (NSArray<NSString *> *)videoResolutionArray {
62  return [_settingsModel availableVideoResolutions];
63}
64
65- (NSArray<RTC_OBJC_TYPE(RTCVideoCodecInfo) *> *)videoCodecArray {
66  return [_settingsModel availableVideoCodecs];
67}
68
69#pragma mark -
70
71- (void)addDoneBarButton {
72  UIBarButtonItem *barItem =
73      [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
74                                                    target:self
75                                                    action:@selector(dismissModally:)];
76  self.navigationItem.leftBarButtonItem = barItem;
77}
78
79#pragma mark - Dismissal of view controller
80
81- (void)dismissModally:(id)sender {
82  [self dismissViewControllerAnimated:YES completion:nil];
83}
84
85#pragma mark - Table view data source
86
87- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
88  return 4;
89}
90
91- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
92  switch (section) {
93    case ARDSettingsSectionAudioSettings:
94      return 3;
95    case ARDSettingsSectionVideoResolution:
96      return self.videoResolutionArray.count;
97    case ARDSettingsSectionVideoCodec:
98      return self.videoCodecArray.count;
99    default:
100      return 1;
101  }
102}
103
104#pragma mark - Table view delegate helpers
105
106- (void)removeAllAccessories:(UITableView *)tableView
107                   inSection:(int)section
108{
109  for (int i = 0; i < [tableView numberOfRowsInSection:section]; i++) {
110    NSIndexPath *rowPath = [NSIndexPath indexPathForRow:i inSection:section];
111    UITableViewCell *cell = [tableView cellForRowAtIndexPath:rowPath];
112    cell.accessoryType = UITableViewCellAccessoryNone;
113  }
114}
115
116- (void)tableView:(UITableView *)tableView
117updateListSelectionAtIndexPath:(NSIndexPath *)indexPath
118        inSection:(int)section {
119  [self removeAllAccessories:tableView inSection:section];
120  UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
121  cell.accessoryType = UITableViewCellAccessoryCheckmark;
122  [tableView deselectRowAtIndexPath:indexPath animated:YES];
123}
124
125#pragma mark - Table view delegate
126
127- (nullable NSString *)tableView:(UITableView *)tableView
128         titleForHeaderInSection:(NSInteger)section {
129  switch (section) {
130    case ARDSettingsSectionAudioSettings:
131      return @"Audio";
132    case ARDSettingsSectionVideoResolution:
133      return @"Video resolution";
134    case ARDSettingsSectionVideoCodec:
135      return @"Video codec";
136    case ARDSettingsSectionBitRate:
137      return @"Maximum bitrate";
138    default:
139      return @"";
140  }
141}
142
143- (UITableViewCell *)tableView:(UITableView *)tableView
144         cellForRowAtIndexPath:(NSIndexPath *)indexPath {
145  switch (indexPath.section) {
146    case ARDSettingsSectionAudioSettings:
147      return [self audioSettingsTableViewCellForTableView:tableView atIndexPath:indexPath];
148
149    case ARDSettingsSectionVideoResolution:
150      return [self videoResolutionTableViewCellForTableView:tableView atIndexPath:indexPath];
151
152    case ARDSettingsSectionVideoCodec:
153      return [self videoCodecTableViewCellForTableView:tableView atIndexPath:indexPath];
154
155    case ARDSettingsSectionBitRate:
156      return [self bitrateTableViewCellForTableView:tableView atIndexPath:indexPath];
157
158    default:
159      return [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
160                                    reuseIdentifier:@"identifier"];
161  }
162}
163
164- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
165  switch (indexPath.section) {
166    case ARDSettingsSectionVideoResolution:
167      [self tableView:tableView disSelectVideoResolutionAtIndex:indexPath];
168      break;
169
170    case ARDSettingsSectionVideoCodec:
171      [self tableView:tableView didSelectVideoCodecCellAtIndexPath:indexPath];
172      break;
173  }
174}
175
176#pragma mark - Table view delegate(Video Resolution)
177
178- (UITableViewCell *)videoResolutionTableViewCellForTableView:(UITableView *)tableView
179                                                  atIndexPath:(NSIndexPath *)indexPath {
180  NSString *dequeueIdentifier = @"ARDSettingsVideoResolutionViewCellIdentifier";
181  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:dequeueIdentifier];
182  if (!cell) {
183    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
184                                  reuseIdentifier:dequeueIdentifier];
185  }
186  NSString *resolution = self.videoResolutionArray[indexPath.row];
187  cell.textLabel.text = resolution;
188  if ([resolution isEqualToString:[_settingsModel currentVideoResolutionSettingFromStore]]) {
189    cell.accessoryType = UITableViewCellAccessoryCheckmark;
190  } else {
191    cell.accessoryType = UITableViewCellAccessoryNone;
192  }
193
194  return cell;
195}
196
197- (void)tableView:(UITableView *)tableView
198    disSelectVideoResolutionAtIndex:(NSIndexPath *)indexPath {
199  [self tableView:tableView
200      updateListSelectionAtIndexPath:indexPath
201                           inSection:ARDSettingsSectionVideoResolution];
202
203  NSString *videoResolution = self.videoResolutionArray[indexPath.row];
204  [_settingsModel storeVideoResolutionSetting:videoResolution];
205}
206
207#pragma mark - Table view delegate(Video Codec)
208
209- (UITableViewCell *)videoCodecTableViewCellForTableView:(UITableView *)tableView
210                                             atIndexPath:(NSIndexPath *)indexPath {
211  NSString *dequeueIdentifier = @"ARDSettingsVideoCodecCellIdentifier";
212  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:dequeueIdentifier];
213  if (!cell) {
214    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
215                                  reuseIdentifier:dequeueIdentifier];
216  }
217  RTC_OBJC_TYPE(RTCVideoCodecInfo) *codec = self.videoCodecArray[indexPath.row];
218  cell.textLabel.text = [codec humanReadableDescription];
219  if ([codec isEqualToCodecInfo:[_settingsModel currentVideoCodecSettingFromStore]]) {
220    cell.accessoryType = UITableViewCellAccessoryCheckmark;
221  } else {
222    cell.accessoryType = UITableViewCellAccessoryNone;
223  }
224
225  return cell;
226}
227
228- (void)tableView:(UITableView *)tableView
229    didSelectVideoCodecCellAtIndexPath:(NSIndexPath *)indexPath {
230  [self tableView:tableView
231    updateListSelectionAtIndexPath:indexPath
232        inSection:ARDSettingsSectionVideoCodec];
233
234  RTC_OBJC_TYPE(RTCVideoCodecInfo) *videoCodec = self.videoCodecArray[indexPath.row];
235  [_settingsModel storeVideoCodecSetting:videoCodec];
236}
237
238#pragma mark - Table view delegate(Bitrate)
239
240- (UITableViewCell *)bitrateTableViewCellForTableView:(UITableView *)tableView
241                                          atIndexPath:(NSIndexPath *)indexPath {
242  NSString *dequeueIdentifier = @"ARDSettingsBitrateCellIdentifier";
243  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:dequeueIdentifier];
244  if (!cell) {
245    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
246                                  reuseIdentifier:dequeueIdentifier];
247
248    UITextField *textField = [[UITextField alloc]
249        initWithFrame:CGRectMake(10, 0, cell.bounds.size.width - 20, cell.bounds.size.height)];
250    NSString *currentMaxBitrate = [_settingsModel currentMaxBitrateSettingFromStore].stringValue;
251    textField.text = currentMaxBitrate;
252    textField.placeholder = @"Enter max bit rate (kbps)";
253    textField.keyboardType = UIKeyboardTypeNumberPad;
254    textField.delegate = self;
255
256    // Numerical keyboards have no return button, we need to add one manually.
257    UIToolbar *numberToolbar =
258        [[UIToolbar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 50)];
259    numberToolbar.items = @[
260      [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace
261                                                    target:nil
262                                                    action:nil],
263      [[UIBarButtonItem alloc] initWithTitle:@"Apply"
264                                       style:UIBarButtonItemStyleDone
265                                      target:self
266                                      action:@selector(numberTextFieldDidEndEditing:)]
267    ];
268    [numberToolbar sizeToFit];
269
270    textField.inputAccessoryView = numberToolbar;
271    [cell addSubview:textField];
272  }
273  return cell;
274}
275
276- (void)numberTextFieldDidEndEditing:(id)sender {
277  [self.view endEditing:YES];
278}
279
280- (void)textFieldDidEndEditing:(UITextField *)textField {
281  NSNumber *bitrateNumber = nil;
282
283  if (textField.text.length != 0) {
284    bitrateNumber = [NSNumber numberWithInteger:textField.text.intValue];
285  }
286
287  [_settingsModel storeMaxBitrateSetting:bitrateNumber];
288}
289
290#pragma mark - Table view delegate(Audio settings)
291
292- (UITableViewCell *)audioSettingsTableViewCellForTableView:(UITableView *)tableView
293                                                atIndexPath:(NSIndexPath *)indexPath {
294  NSString *dequeueIdentifier = @"ARDSettingsAudioSettingsCellIdentifier";
295  UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:dequeueIdentifier];
296  if (!cell) {
297    cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
298                                  reuseIdentifier:dequeueIdentifier];
299    cell.selectionStyle = UITableViewCellSelectionStyleNone;
300    UISwitch *switchView = [[UISwitch alloc] initWithFrame:CGRectZero];
301    switchView.tag = indexPath.row;
302    [switchView addTarget:self
303                   action:@selector(audioSettingSwitchChanged:)
304         forControlEvents:UIControlEventValueChanged];
305    cell.accessoryView = switchView;
306  }
307
308  cell.textLabel.text = [self labelForAudioSettingAtIndexPathRow:indexPath.row];
309  UISwitch *switchView = (UISwitch *)cell.accessoryView;
310  switchView.on = [self valueForAudioSettingAtIndexPathRow:indexPath.row];
311
312  return cell;
313}
314
315- (NSString *)labelForAudioSettingAtIndexPathRow:(NSInteger)setting {
316  switch (setting) {
317    case ARDAudioSettingsAudioOnly:
318      return @"Audio only";
319    case ARDAudioSettingsCreateAecDump:
320      return @"Create AecDump";
321    case ARDAudioSettingsUseManualAudioConfig:
322      return @"Use manual audio config";
323    default:
324      return @"";
325  }
326}
327
328- (BOOL)valueForAudioSettingAtIndexPathRow:(NSInteger)setting {
329  switch (setting) {
330    case ARDAudioSettingsAudioOnly:
331      return [_settingsModel currentAudioOnlySettingFromStore];
332    case ARDAudioSettingsCreateAecDump:
333      return [_settingsModel currentCreateAecDumpSettingFromStore];
334    case ARDAudioSettingsUseManualAudioConfig:
335      return [_settingsModel currentUseManualAudioConfigSettingFromStore];
336    default:
337      return NO;
338  }
339}
340
341- (void)audioSettingSwitchChanged:(UISwitch *)sender {
342  switch (sender.tag) {
343    case ARDAudioSettingsAudioOnly: {
344      [_settingsModel storeAudioOnlySetting:sender.isOn];
345      break;
346    }
347    case ARDAudioSettingsCreateAecDump: {
348      [_settingsModel storeCreateAecDumpSetting:sender.isOn];
349      break;
350    }
351    case ARDAudioSettingsUseManualAudioConfig: {
352      [_settingsModel storeUseManualAudioConfigSetting:sender.isOn];
353      break;
354    }
355    default:
356      break;
357  }
358}
359
360@end
361NS_ASSUME_NONNULL_END
362