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;
107 @implementation BeforeTranslateInfoBarController {
108 __weak translate::TranslateInfoBarDelegate* _translateInfoBarDelegate;
109 // A fullscreen view that catches all touch events and contains a UIPickerView
110 // and a UINavigationBar.
111 base::scoped_nsobject<UIView> _languageSelectionView;
112 // Stores whether the user is currently choosing in the UIPickerView the
113 // original language, or the target language.
114 NSUInteger _languageSelectionType;
115 // The language picker.
116 base::scoped_nsobject<UIPickerView> _languagePicker;
117 // Navigation bar associated with the picker with "Done" and "Cancel" buttons.
118 base::scoped_nsobject<UINavigationBar> _navigationBar;
119 // The controller of the languagePicker. Needs to be an ivar because
120 // |_languagePicker| does not retain it.
121 base::scoped_nsobject<LanguagePickerController> _languagePickerController;
125 #pragma mark InfoBarControllerProtocol
127 - (void)layoutForDelegate:(infobars::InfoBarDelegate*)delegate
128 frame:(CGRect)frame {
129 _translateInfoBarDelegate = delegate->AsTranslateInfoBarDelegate();
130 infobars::InfoBarDelegate* infoBarDelegate =
131 static_cast<infobars::InfoBarDelegate*>(_translateInfoBarDelegate);
132 DCHECK(!infoBarView_);
133 infoBarView_.reset([ios::GetChromeBrowserProvider()->CreateInfoBarView()
136 isWarning:infoBarDelegate->GetInfoBarType() ==
137 infobars::InfoBarDelegate::WARNING_TYPE]);
139 gfx::Image icon = _translateInfoBarDelegate->GetIcon();
141 [infoBarView_ addLeftIcon:icon.ToUIImage()];
144 [self updateInfobarLabel];
147 [infoBarView_ addCloseButtonWithTag:TranslateInfoBarIOSTag::BEFORE_DENY
149 action:@selector(infoBarButtonDidPress:)];
151 NSString* buttonAccept = l10n_util::GetNSString(IDS_TRANSLATE_INFOBAR_ACCEPT);
152 NSString* buttonDeny = l10n_util::GetNSString(IDS_TRANSLATE_INFOBAR_DENY);
153 [infoBarView_ addButton1:buttonAccept
154 tag1:TranslateInfoBarIOSTag::BEFORE_ACCEPT
156 tag2:TranslateInfoBarIOSTag::BEFORE_DENY
158 action:@selector(infoBarButtonDidPress:)];
161 - (void)updateInfobarLabel {
162 NSString* originalLanguage =
163 base::SysUTF16ToNSString(_translateInfoBarDelegate->language_name_at(
164 _translateInfoBarDelegate->original_language_index()));
165 NSString* targetLanguage =
166 base::SysUTF16ToNSString(_translateInfoBarDelegate->language_name_at(
167 _translateInfoBarDelegate->target_language_index()));
168 base::string16 originalLanguageWithLink =
169 base::SysNSStringToUTF16([[infoBarView_ class]
170 stringAsLink:originalLanguage
171 tag:TranslateInfoBarIOSTag::BEFORE_SOURCE_LANGUAGE]);
172 base::string16 targetLanguageWithLink =
173 base::SysNSStringToUTF16([[infoBarView_ class]
174 stringAsLink:targetLanguage
175 tag:TranslateInfoBarIOSTag::BEFORE_TARGET_LANGUAGE]);
177 l10n_util::GetNSStringF(IDS_TRANSLATE_INFOBAR_BEFORE_MESSAGE_IOS,
178 originalLanguageWithLink, targetLanguageWithLink);
179 [infoBarView_ addLabel:label
181 action:@selector(infobarLinkDidPress:)];
184 - (void)languageSelectionDone {
185 size_t selectedRow = [_languagePicker selectedRowInComponent:0];
186 if (_languageSelectionType ==
187 TranslateInfoBarIOSTag::BEFORE_SOURCE_LANGUAGE &&
188 selectedRow != _translateInfoBarDelegate->target_language_index()) {
189 _translateInfoBarDelegate->UpdateOriginalLanguageIndex(selectedRow);
191 if (_languageSelectionType ==
192 TranslateInfoBarIOSTag::BEFORE_TARGET_LANGUAGE &&
193 selectedRow != _translateInfoBarDelegate->original_language_index()) {
194 _translateInfoBarDelegate->UpdateTargetLanguageIndex(selectedRow);
196 [self updateInfobarLabel];
197 [self dismissLanguageSelectionView];
200 - (void)dismissLanguageSelectionView {
201 DCHECK_EQ(_languagePicker == nil, _navigationBar == nil);
202 if (_languagePicker == nil)
204 // Sets the picker's delegate and data source to nil, because the
205 // |_languagePickerController| may be destroyed before the picker is hidden,
206 // and even though the interactions with the picker are disabled, it might
207 // still be turning and requesting data from the data source or calling the
209 [_languagePicker setDataSource:nil];
210 [_languagePicker setDelegate:nil];
211 _languagePickerController.reset();
212 // Animate the picker away.
213 CGRect languagePickerFrame = [_languagePicker frame];
214 CGRect navigationBarFrame = [_navigationBar frame];
215 const CGFloat animationHeight =
216 languagePickerFrame.size.height + navigationBarFrame.size.height;
217 languagePickerFrame.origin.y += animationHeight;
218 navigationBarFrame.origin.y += animationHeight;
219 auto blockLanguagePicker(_languagePicker);
220 auto blockNavigationBar(_navigationBar);
221 _languagePicker.reset();
222 _navigationBar.reset();
223 void (^animations)(void) = ^{
224 blockLanguagePicker.get().frame = languagePickerFrame;
225 blockNavigationBar.get().frame = navigationBarFrame;
227 auto blockSelectionView(_languageSelectionView);
228 _languageSelectionView.reset();
229 void (^completion)(BOOL finished) = ^(BOOL finished) {
230 [blockSelectionView removeFromSuperview];
232 [UIView animateWithDuration:kPickerAnimationDurationInSeconds
233 animations:animations
234 completion:completion];
237 #pragma mark - Handling of User Events
239 - (void)infoBarButtonDidPress:(id)sender {
240 // This press might have occurred after the user has already pressed a button,
241 // in which case the view has been detached from the delegate and this press
242 // should be ignored.
246 if ([sender isKindOfClass:[UIButton class]]) {
247 NSUInteger buttonId = static_cast<UIButton*>(sender).tag;
248 DCHECK(buttonId == TranslateInfoBarIOSTag::BEFORE_ACCEPT ||
249 buttonId == TranslateInfoBarIOSTag::BEFORE_DENY);
250 delegate_->InfoBarButtonDidPress(buttonId);
254 - (void)infobarLinkDidPress:(NSNumber*)tag {
255 DCHECK([tag isKindOfClass:[NSNumber class]]);
256 _languageSelectionType = [tag unsignedIntegerValue];
257 DCHECK(_languageSelectionType ==
258 TranslateInfoBarIOSTag::BEFORE_SOURCE_LANGUAGE ||
259 _languageSelectionType ==
260 TranslateInfoBarIOSTag::BEFORE_TARGET_LANGUAGE);
261 if (_languagePicker != nil)
262 return; // The UIPickerView is already up.
264 // Creates and adds the view containing the UIPickerView and the
267 [[UIApplication sharedApplication] keyWindow].rootViewController.view;
268 // Convert the parent frame to handle device rotation.
270 CGRectApplyAffineTransform([parentView frame], [parentView transform]);
271 const CGFloat totalPickerHeight = kUIPickerHeight + kNavigationBarHeight;
272 CGRect languageSelectionViewFrame =
273 CGRectMake(0, parentFrame.size.height - totalPickerHeight,
274 parentFrame.size.width, totalPickerHeight);
275 _languageSelectionView.reset(
276 [[UIView alloc] initWithFrame:languageSelectionViewFrame]);
277 [_languageSelectionView
278 setAutoresizingMask:UIViewAutoresizingFlexibleWidth |
279 UIViewAutoresizingFlexibleTopMargin];
280 [parentView addSubview:_languageSelectionView];
282 // Creates the navigation bar and its buttons.
283 CGRect finalPickerFrame = CGRectMake(
284 0, [_languageSelectionView frame].size.height - kUIPickerHeight,
285 [_languageSelectionView frame].size.width, kUIPickerHeight);
286 CGRect finalNavigationBarFrame = CGRectMake(
287 0, 0, [_languageSelectionView frame].size.width, kNavigationBarHeight);
288 // The language picker animates from the bottom of the screen.
289 CGRect initialPickerFrame = finalPickerFrame;
290 initialPickerFrame.origin.y += totalPickerHeight;
291 CGRect initialNavigationBarFrame = finalNavigationBarFrame;
292 initialNavigationBarFrame.origin.y += totalPickerHeight;
294 _navigationBar.reset(
295 [[UINavigationBar alloc] initWithFrame:initialNavigationBarFrame]);
296 const UIViewAutoresizing resizingMask =
297 UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
298 [_navigationBar setAutoresizingMask:resizingMask];
299 base::scoped_nsobject<UIBarButtonItem> doneButton([[UIBarButtonItem alloc]
300 initWithBarButtonSystemItem:UIBarButtonSystemItemDone
302 action:@selector(languageSelectionDone)]);
303 base::scoped_nsobject<UIBarButtonItem> cancelButton([[UIBarButtonItem alloc]
304 initWithBarButtonSystemItem:UIBarButtonSystemItemCancel
306 action:@selector(dismissLanguageSelectionView)]);
307 base::scoped_nsobject<UINavigationItem> item(
308 [[UINavigationItem alloc] initWithTitle:nil]);
309 [item setRightBarButtonItem:doneButton];
310 [item setLeftBarButtonItem:cancelButton];
311 [item setHidesBackButton:YES];
312 [_navigationBar pushNavigationItem:item animated:NO];
314 // Creates the PickerView and its controller.
315 NSInteger selectedRow;
316 NSInteger disabledRow;
317 if (_languageSelectionType ==
318 TranslateInfoBarIOSTag::BEFORE_SOURCE_LANGUAGE) {
319 selectedRow = _translateInfoBarDelegate->original_language_index();
320 disabledRow = _translateInfoBarDelegate->target_language_index();
322 selectedRow = _translateInfoBarDelegate->target_language_index();
323 disabledRow = _translateInfoBarDelegate->original_language_index();
325 _languagePickerController.reset([[LanguagePickerController alloc]
326 initWithDelegate:_translateInfoBarDelegate
327 initialRow:selectedRow
328 disabledRow:disabledRow]);
329 _languagePicker.reset(
330 [[UIPickerView alloc] initWithFrame:initialPickerFrame]);
331 [_languagePicker setAutoresizingMask:resizingMask];
332 [_languagePicker setShowsSelectionIndicator:YES];
333 [_languagePicker setDataSource:_languagePickerController];
334 [_languagePicker setDelegate:_languagePickerController];
335 [_languagePicker setShowsSelectionIndicator:YES];
336 [_languagePicker setBackgroundColor:[infoBarView_ backgroundColor]];
337 [_languagePicker selectRow:selectedRow inComponent:0 animated:NO];
339 auto blockLanguagePicker(_languagePicker);
340 auto blockNavigationBar(_navigationBar);
341 [UIView animateWithDuration:kPickerAnimationDurationInSeconds
343 blockLanguagePicker.get().frame = finalPickerFrame;
344 blockNavigationBar.get().frame = finalNavigationBarFrame;
348 [_languageSelectionView addSubview:_languagePicker];
349 [_languageSelectionView addSubview:_navigationBar];
354 [self dismissLanguageSelectionView];
359 [self dismissLanguageSelectionView];