1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ios/chrome/browser/translate/before_translate_infobar_controller.h"
7 #import <UIKit/UIKit.h>
9 #include "base/logging.h"
10 #include "base/strings/sys_string_conversions.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "components/translate/core/browser/translate_infobar_delegate.h"
13 #include "grit/components_strings.h"
14 #include "ios/chrome/browser/translate/translate_infobar_tags.h"
15 #include "ios/public/provider/chrome/browser/chrome_browser_provider.h"
16 #import "ios/public/provider/chrome/browser/ui/infobar_view_delegate.h"
17 #import "ios/public/provider/chrome/browser/ui/infobar_view_protocol.h"
18 #include "ui/base/l10n/l10n_util.h"
19 #include "ui/gfx/image/image.h"
23 CGFloat kNavigationBarHeight = 44;
24 CGFloat kUIPickerHeight = 216;
25 CGFloat kUIPickerFontSize = 26;
26 NSTimeInterval kPickerAnimationDurationInSeconds = 0.2;
30 // The class is a data source and delegate to the UIPickerView that contains the
32 @interface LanguagePickerController
33 : UIViewController<UIPickerViewDataSource, UIPickerViewDelegate> {
34 __weak translate::TranslateInfoBarDelegate* _translateInfoBarDelegate;
35 NSInteger _initialRow; // Displayed in bold font.
36 NSInteger _disabledRow; // Grayed out.
40 @implementation LanguagePickerController
42 - (instancetype)initWithDelegate:(translate::TranslateInfoBarDelegate*)
43 translateInfoBarDelegate
44 initialRow:(NSInteger)initialRow
45 disabledRow:(NSInteger)disabledRow {
46 if ((self = [super init])) {
47 _translateInfoBarDelegate = translateInfoBarDelegate;
48 _initialRow = initialRow;
49 _disabledRow = disabledRow;
55 #pragma mark UIPickerViewDataSource
57 - (NSInteger)pickerView:(UIPickerView*)pickerView
58 numberOfRowsInComponent:(NSInteger)component {
59 NSUInteger numRows = _translateInfoBarDelegate->num_languages();
63 - (NSInteger)numberOfComponentsInPickerView:(UIPickerView*)pickerView {
68 #pragma mark UIPickerViewDelegate
70 - (UIView*)pickerView:(UIPickerView*)pickerView
71 viewForRow:(NSInteger)row
72 forComponent:(NSInteger)component
73 reusingView:(UIView*)view {
74 DCHECK_EQ(0, component);
75 UILabel* label = [view isKindOfClass:[UILabel class]]
77 : [[[UILabel alloc] init] autorelease];
78 [label setText:base::SysUTF16ToNSString(
79 _translateInfoBarDelegate->language_name_at(row))];
80 [label setTextAlignment:NSTextAlignmentCenter];
81 UIFont* font = [UIFont systemFontOfSize:kUIPickerFontSize];
83 if (row == _initialRow)
84 font = [UIFont boldSystemFontOfSize:kUIPickerFontSize];
85 else if (row == _disabledRow)
88 [label setEnabled:enabled];
94 @interface BeforeTranslateInfoBarController ()
96 // Action for any of the user defined buttons.
97 - (void)infoBarButtonDidPress:(id)sender;
98 // Action for any of the user defined links.
99 - (void)infobarLinkDidPress:(NSNumber*)tag;
100 // Action for the language selection "Done" button.
101 - (void)languageSelectionDone;
102 // Dismisses the language selection view.
103 - (void)dismissLanguageSelectionView;
104 // Changes the text on the view to match the language.
105 - (void)updateInfobarLabelOnView:(UIView<InfoBarViewProtocol>*)view;
109 @implementation BeforeTranslateInfoBarController {
110 __weak translate::TranslateInfoBarDelegate* _translateInfoBarDelegate;
111 // A fullscreen view that catches all touch events and contains a UIPickerView
112 // and a UINavigationBar.
113 base::scoped_nsobject<UIView> _languageSelectionView;
114 // Stores whether the user is currently choosing in the UIPickerView the
115 // original language, or the target language.
116 NSUInteger _languageSelectionType;
117 // The language picker.
118 base::scoped_nsobject<UIPickerView> _languagePicker;
119 // Navigation bar associated with the picker with "Done" and "Cancel" buttons.
120 base::scoped_nsobject<UINavigationBar> _navigationBar;
121 // The controller of the languagePicker. Needs to be an ivar because
122 // |_languagePicker| does not retain it.
123 base::scoped_nsobject<LanguagePickerController> _languagePickerController;
127 #pragma mark InfoBarControllerProtocol
129 - (base::scoped_nsobject<UIView<InfoBarViewProtocol>>)
130 viewForDelegate:(infobars::InfoBarDelegate*)delegate
131 frame:(CGRect)frame {
132 base::scoped_nsobject<UIView<InfoBarViewProtocol>> infoBarView;
133 _translateInfoBarDelegate = delegate->AsTranslateInfoBarDelegate();
135 ios::GetChromeBrowserProvider()->CreateInfoBarView(frame, self.delegate));
137 gfx::Image icon = _translateInfoBarDelegate->GetIcon();
139 [infoBarView addLeftIcon:icon.ToUIImage()];
142 [self updateInfobarLabelOnView:infoBarView];
145 [infoBarView addCloseButtonWithTag:TranslateInfoBarIOSTag::BEFORE_DENY
147 action:@selector(infoBarButtonDidPress:)];
149 NSString* buttonAccept = l10n_util::GetNSString(IDS_TRANSLATE_INFOBAR_ACCEPT);
150 NSString* buttonDeny = l10n_util::GetNSString(IDS_TRANSLATE_INFOBAR_DENY);
151 [infoBarView addButton1:buttonAccept
152 tag1:TranslateInfoBarIOSTag::BEFORE_ACCEPT
154 tag2:TranslateInfoBarIOSTag::BEFORE_DENY
156 action:@selector(infoBarButtonDidPress:)];
160 - (void)updateInfobarLabelOnView:(UIView<InfoBarViewProtocol>*)view {
161 NSString* originalLanguage =
162 base::SysUTF16ToNSString(_translateInfoBarDelegate->language_name_at(
163 _translateInfoBarDelegate->original_language_index()));
164 NSString* targetLanguage =
165 base::SysUTF16ToNSString(_translateInfoBarDelegate->language_name_at(
166 _translateInfoBarDelegate->target_language_index()));
167 base::string16 originalLanguageWithLink =
168 base::SysNSStringToUTF16([[view class]
169 stringAsLink:originalLanguage
170 tag:TranslateInfoBarIOSTag::BEFORE_SOURCE_LANGUAGE]);
171 base::string16 targetLanguageWithLink = base::SysNSStringToUTF16([[view class]
172 stringAsLink:targetLanguage
173 tag:TranslateInfoBarIOSTag::BEFORE_TARGET_LANGUAGE]);
175 l10n_util::GetNSStringF(IDS_TRANSLATE_INFOBAR_BEFORE_MESSAGE_IOS,
176 originalLanguageWithLink, targetLanguageWithLink);
177 [view addLabel:label target:self action:@selector(infobarLinkDidPress:)];
180 - (void)languageSelectionDone {
181 size_t selectedRow = [_languagePicker selectedRowInComponent:0];
182 if (_languageSelectionType ==
183 TranslateInfoBarIOSTag::BEFORE_SOURCE_LANGUAGE &&
184 selectedRow != _translateInfoBarDelegate->target_language_index()) {
185 _translateInfoBarDelegate->UpdateOriginalLanguageIndex(selectedRow);
187 if (_languageSelectionType ==
188 TranslateInfoBarIOSTag::BEFORE_TARGET_LANGUAGE &&
189 selectedRow != _translateInfoBarDelegate->original_language_index()) {
190 _translateInfoBarDelegate->UpdateTargetLanguageIndex(selectedRow);
192 [self updateInfobarLabelOnView:self.view];
193 [self dismissLanguageSelectionView];
196 - (void)dismissLanguageSelectionView {
197 DCHECK_EQ(_languagePicker == nil, _navigationBar == nil);
198 if (_languagePicker == nil)
200 // Sets the picker's delegate and data source to nil, because the
201 // |_languagePickerController| may be destroyed before the picker is hidden,
202 // and even though the interactions with the picker are disabled, it might
203 // still be turning and requesting data from the data source or calling the
205 [_languagePicker setDataSource:nil];
206 [_languagePicker setDelegate:nil];
207 _languagePickerController.reset();
208 // Animate the picker away.
209 CGRect languagePickerFrame = [_languagePicker frame];
210 CGRect navigationBarFrame = [_navigationBar frame];
211 const CGFloat animationHeight =
212 languagePickerFrame.size.height + navigationBarFrame.size.height;
213 languagePickerFrame.origin.y += animationHeight;
214 navigationBarFrame.origin.y += animationHeight;
215 auto blockLanguagePicker(_languagePicker);
216 auto blockNavigationBar(_navigationBar);
217 _languagePicker.reset();
218 _navigationBar.reset();
219 void (^animations)(void) = ^{
220 blockLanguagePicker.get().frame = languagePickerFrame;
221 blockNavigationBar.get().frame = navigationBarFrame;
223 auto blockSelectionView(_languageSelectionView);
224 _languageSelectionView.reset();
225 void (^completion)(BOOL finished) = ^(BOOL finished) {
226 [blockSelectionView removeFromSuperview];
228 [UIView animateWithDuration:kPickerAnimationDurationInSeconds
229 animations:animations
230 completion:completion];
233 #pragma mark - Handling of User Events
235 - (void)infoBarButtonDidPress:(id)sender {
236 // This press might have occurred after the user has already pressed a button,
237 // in which case the view has been detached from the delegate and this press
238 // should be ignored.
239 if (!self.delegate) {
242 if ([sender isKindOfClass:[UIButton class]]) {
243 NSUInteger buttonId = static_cast<UIButton*>(sender).tag;
244 DCHECK(buttonId == TranslateInfoBarIOSTag::BEFORE_ACCEPT ||
245 buttonId == TranslateInfoBarIOSTag::BEFORE_DENY);
246 self.delegate->InfoBarButtonDidPress(buttonId);
250 - (void)infobarLinkDidPress:(NSNumber*)tag {
251 DCHECK([tag isKindOfClass:[NSNumber class]]);
252 _languageSelectionType = [tag unsignedIntegerValue];
253 DCHECK(_languageSelectionType ==
254 TranslateInfoBarIOSTag::BEFORE_SOURCE_LANGUAGE ||
255 _languageSelectionType ==
256 TranslateInfoBarIOSTag::BEFORE_TARGET_LANGUAGE);
257 if (_languagePicker != nil)
258 return; // The UIPickerView is already up.
260 // Creates and adds the view containing the UIPickerView and the
263 [[UIApplication sharedApplication] keyWindow].rootViewController.view;
264 // Convert the parent frame to handle device rotation.
266 CGRectApplyAffineTransform([parentView frame], [parentView transform]);
267 const CGFloat totalPickerHeight = kUIPickerHeight + kNavigationBarHeight;
268 CGRect languageSelectionViewFrame =
269 CGRectMake(0, parentFrame.size.height - totalPickerHeight,
270 parentFrame.size.width, totalPickerHeight);
271 _languageSelectionView.reset(
272 [[UIView alloc] initWithFrame:languageSelectionViewFrame]);
273 [_languageSelectionView
274 setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
275 UIViewAutoresizingFlexibleTopMargin];
276 [parentView addSubview:_languageSelectionView];
278 // Creates the navigation bar and its buttons.
279 CGRect finalPickerFrame = CGRectMake(
280 0, [_languageSelectionView frame].size.height - kUIPickerHeight,
281 [_languageSelectionView frame].size.width, kUIPickerHeight);
282 CGRect finalNavigationBarFrame = CGRectMake(
283 0, 0, [_languageSelectionView frame].size.width, kNavigationBarHeight);
284 // The language picker animates from the bottom of the screen.
285 CGRect initialPickerFrame = finalPickerFrame;
286 initialPickerFrame.origin.y += totalPickerHeight;
287 CGRect initialNavigationBarFrame = finalNavigationBarFrame;
288 initialNavigationBarFrame.origin.y += totalPickerHeight;
290 _navigationBar.reset(
291 [[UINavigationBar alloc] initWithFrame:initialNavigationBarFrame]);
292 const UIViewAutoresizing resizingMask =
293 UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
294 [_navigationBar setAutoresizingMask:resizingMask];
295 base::scoped_nsobject<UIBarButtonItem> doneButton([[UIBarButtonItem alloc]
296 initWithBarButtonSystemItem:UIBarButtonSystemItemDone
298 action:@selector(languageSelectionDone)]);
299 base::scoped_nsobject<UIBarButtonItem> cancelButton([[UIBarButtonItem alloc]
300 initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
302 action:@selector(dismissLanguageSelectionView)]);
303 base::scoped_nsobject<UINavigationItem> item(
304 [[UINavigationItem alloc] initWithTitle:@""]);
305 [item setRightBarButtonItem:doneButton];
306 [item setLeftBarButtonItem:cancelButton];
307 [item setHidesBackButton:YES];
308 [_navigationBar pushNavigationItem:item animated:NO];
310 // Creates the PickerView and its controller.
311 NSInteger selectedRow;
312 NSInteger disabledRow;
313 if (_languageSelectionType ==
314 TranslateInfoBarIOSTag::BEFORE_SOURCE_LANGUAGE) {
315 selectedRow = _translateInfoBarDelegate->original_language_index();
316 disabledRow = _translateInfoBarDelegate->target_language_index();
318 selectedRow = _translateInfoBarDelegate->target_language_index();
319 disabledRow = _translateInfoBarDelegate->original_language_index();
321 _languagePickerController.reset([[LanguagePickerController alloc]
322 initWithDelegate:_translateInfoBarDelegate
323 initialRow:selectedRow
324 disabledRow:disabledRow]);
325 _languagePicker.reset(
326 [[UIPickerView alloc] initWithFrame:initialPickerFrame]);
327 [_languagePicker setAutoresizingMask:resizingMask];
328 [_languagePicker setShowsSelectionIndicator:YES];
329 [_languagePicker setDataSource:_languagePickerController];
330 [_languagePicker setDelegate:_languagePickerController];
331 [_languagePicker setShowsSelectionIndicator:YES];
332 [_languagePicker setBackgroundColor:[self.view backgroundColor]];
333 [_languagePicker selectRow:selectedRow inComponent:0 animated:NO];
335 auto blockLanguagePicker(_languagePicker);
336 auto blockNavigationBar(_navigationBar);
337 [UIView animateWithDuration:kPickerAnimationDurationInSeconds
339 blockLanguagePicker.get().frame = finalPickerFrame;
340 blockNavigationBar.get().frame = finalNavigationBarFrame;
344 [_languageSelectionView addSubview:_languagePicker];
345 [_languageSelectionView addSubview:_navigationBar];
350 [self dismissLanguageSelectionView];
355 [self dismissLanguageSelectionView];