OLD | NEW |
| (Empty) |
1 // Copyright (c) 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. | |
4 | |
5 #include "chrome/browser/ui/android/autofill/autofill_dialog_view_android.h" | |
6 | |
7 #include "base/android/jni_android.h" | |
8 #include "base/android/jni_array.h" | |
9 #include "base/android/jni_string.h" | |
10 #include "base/android/scoped_java_ref.h" | |
11 #include "base/strings/utf_string_conversions.h" | |
12 #include "chrome/browser/browser_process.h" | |
13 #include "chrome/browser/ui/android/window_android_helper.h" | |
14 #include "chrome/browser/ui/autofill/data_model_wrapper.h" | |
15 #include "components/autofill/core/browser/autofill_profile.h" | |
16 #include "components/autofill/core/browser/autofill_type.h" | |
17 #include "components/autofill/core/browser/credit_card.h" | |
18 #include "content/public/browser/web_contents.h" | |
19 #include "grit/generated_resources.h" | |
20 #include "jni/AutofillDialogGlue_jni.h" | |
21 #include "ui/android/window_android.h" | |
22 #include "ui/base/l10n/l10n_util.h" | |
23 #include "ui/base/models/combobox_model.h" | |
24 #include "ui/base/models/menu_model.h" | |
25 #include "ui/gfx/android/java_bitmap.h" | |
26 #include "ui/gfx/rect.h" | |
27 | |
28 namespace autofill { | |
29 | |
30 // AutofillDialogView ---------------------------------------------------------- | |
31 | |
32 // static | |
33 AutofillDialogView* AutofillDialogView::Create( | |
34 AutofillDialogController* controller) { | |
35 return new AutofillDialogViewAndroid(controller); | |
36 } | |
37 | |
38 // AutofillDialogView ---------------------------------------------------------- | |
39 | |
40 AutofillDialogViewAndroid::AutofillDialogViewAndroid( | |
41 AutofillDialogController* controller) | |
42 : controller_(controller) {} | |
43 | |
44 AutofillDialogViewAndroid::~AutofillDialogViewAndroid() { | |
45 JNIEnv* env = base::android::AttachCurrentThread(); | |
46 Java_AutofillDialogGlue_onDestroy(env, java_object_.obj()); | |
47 } | |
48 | |
49 void AutofillDialogViewAndroid::Show() { | |
50 JNIEnv* env = base::android::AttachCurrentThread(); | |
51 java_object_.Reset(Java_AutofillDialogGlue_create( | |
52 env, | |
53 reinterpret_cast<jint>(this), | |
54 WindowAndroidHelper::FromWebContents(controller_->web_contents())-> | |
55 GetWindowAndroid()->GetJavaObject().obj())); | |
56 ModelChanged(); | |
57 UpdateNotificationArea(); | |
58 UpdateAccountChooser(); | |
59 } | |
60 | |
61 void AutofillDialogViewAndroid::Hide() { | |
62 JNIEnv* env = base::android::AttachCurrentThread(); | |
63 // This will call DialogDismissed(), and that could cause |this| to be | |
64 // deleted by the |controller|. | |
65 Java_AutofillDialogGlue_dismissDialog(env, java_object_.obj()); | |
66 } | |
67 | |
68 void AutofillDialogViewAndroid::UpdateNotificationArea() { | |
69 JNIEnv* env = base::android::AttachCurrentThread(); | |
70 std::vector<DialogNotification> notifications = | |
71 controller_->CurrentNotifications(); | |
72 const size_t count = notifications.size(); | |
73 ScopedJavaLocalRef<jobjectArray> notification_array = | |
74 Java_AutofillDialogGlue_createAutofillDialogNotificationArray( | |
75 env, count); | |
76 for (size_t i = 0; i < count; ++i) { | |
77 ScopedJavaLocalRef<jstring> text = | |
78 base::android::ConvertUTF16ToJavaString( | |
79 env, notifications[i].display_text()); | |
80 | |
81 Java_AutofillDialogGlue_addToAutofillDialogNotificationArray( | |
82 env, | |
83 notification_array.obj(), | |
84 i, | |
85 static_cast<int>(notifications[i].type()), | |
86 static_cast<int>(notifications[i].GetBackgroundColor()), | |
87 static_cast<int>(notifications[i].GetTextColor()), | |
88 notifications[i].HasArrow(), | |
89 notifications[i].HasCheckbox(), | |
90 notifications[i].checked(), | |
91 notifications[i].interactive(), | |
92 text.obj()); | |
93 } | |
94 | |
95 Java_AutofillDialogGlue_updateNotificationArea(env, | |
96 java_object_.obj(), | |
97 notification_array.obj()); | |
98 } | |
99 | |
100 void AutofillDialogViewAndroid::UpdateAccountChooser() { | |
101 JNIEnv* env = base::android::AttachCurrentThread(); | |
102 std::vector<string16> account_names; | |
103 int selected_account_index = -1; | |
104 | |
105 ui::MenuModel* model = controller_->MenuModelForAccountChooser(); | |
106 if (controller_->ShouldShowSpinner()) { | |
107 // Do not show accounts if not yet known. | |
108 } else if (!model) { | |
109 // TODO(aruslan): http://crbug.com/177495 Publish Android accounts. | |
110 account_names.push_back(controller_->AccountChooserText()); | |
111 selected_account_index = 0; | |
112 } else { | |
113 for (int i = 0; i < model->GetItemCount(); ++i) { | |
114 if (model->IsItemCheckedAt(i)) | |
115 selected_account_index = i; | |
116 account_names.push_back(model->GetLabelAt(i)); | |
117 } | |
118 } | |
119 | |
120 ScopedJavaLocalRef<jobjectArray> jaccount_names = | |
121 base::android::ToJavaArrayOfStrings(env, account_names); | |
122 Java_AutofillDialogGlue_updateAccountChooser( | |
123 env, java_object_.obj(), jaccount_names.obj(), selected_account_index); | |
124 } | |
125 | |
126 void AutofillDialogViewAndroid::UpdateButtonStrip() { | |
127 NOTIMPLEMENTED(); | |
128 } | |
129 | |
130 void AutofillDialogViewAndroid::UpdateDetailArea() { | |
131 NOTIMPLEMENTED(); | |
132 } | |
133 | |
134 void AutofillDialogViewAndroid::UpdateForErrors() { | |
135 NOTIMPLEMENTED(); | |
136 } | |
137 | |
138 void AutofillDialogViewAndroid::UpdateAutocheckoutStepsArea() { | |
139 NOTIMPLEMENTED(); | |
140 } | |
141 | |
142 void AutofillDialogViewAndroid::UpdateSection(DialogSection section) { | |
143 UpdateOrFillSectionToJava(section, true, UNKNOWN_TYPE); | |
144 } | |
145 | |
146 void AutofillDialogViewAndroid::FillSection( | |
147 DialogSection section, | |
148 const DetailInput& originating_input) { | |
149 UpdateOrFillSectionToJava(section, false, originating_input.type); | |
150 } | |
151 | |
152 void AutofillDialogViewAndroid::GetUserInput(DialogSection section, | |
153 DetailOutputMap* output) { | |
154 GetUserInputImpl(section, output); | |
155 } | |
156 | |
157 string16 AutofillDialogViewAndroid::GetCvc() { | |
158 JNIEnv* env = base::android::AttachCurrentThread(); | |
159 ScopedJavaLocalRef<jstring> cvc = | |
160 Java_AutofillDialogGlue_getCvc(env, java_object_.obj()); | |
161 return base::android::ConvertJavaStringToUTF16(env, cvc.obj()); | |
162 } | |
163 | |
164 bool AutofillDialogViewAndroid::SaveDetailsLocally() { | |
165 JNIEnv* env = base::android::AttachCurrentThread(); | |
166 return Java_AutofillDialogGlue_shouldSaveDetailsLocally(env, | |
167 java_object_.obj()); | |
168 } | |
169 | |
170 const content::NavigationController* AutofillDialogViewAndroid::ShowSignIn() { | |
171 NOTIMPLEMENTED(); | |
172 return NULL; | |
173 } | |
174 | |
175 void AutofillDialogViewAndroid::HideSignIn() { | |
176 NOTIMPLEMENTED(); | |
177 } | |
178 | |
179 // TODO(aruslan): bind to the automatic sign-in. | |
180 bool AutofillDialogViewAndroid::StartAutomaticSignIn( | |
181 const std::string& username) { | |
182 if (username.empty()) | |
183 return false; | |
184 JNIEnv* env = base::android::AttachCurrentThread(); | |
185 ScopedJavaLocalRef<jstring> jusername = | |
186 base::android::ConvertUTF8ToJavaString(env, username); | |
187 Java_AutofillDialogGlue_startAutomaticSignIn(env, java_object_.obj(), | |
188 jusername.obj()); | |
189 return true; | |
190 } | |
191 | |
192 void AutofillDialogViewAndroid::UpdateProgressBar(double value) { | |
193 JNIEnv* env = base::android::AttachCurrentThread(); | |
194 Java_AutofillDialogGlue_updateProgressBar(env, java_object_.obj(), value); | |
195 } | |
196 | |
197 void AutofillDialogViewAndroid::ModelChanged() { | |
198 JNIEnv* env = base::android::AttachCurrentThread(); | |
199 Java_AutofillDialogGlue_modelChanged( | |
200 env, java_object_.obj(), | |
201 controller_->ShouldShowSpinner()); | |
202 UpdateSection(SECTION_EMAIL); | |
203 UpdateSection(SECTION_CC); | |
204 UpdateSection(SECTION_BILLING); | |
205 UpdateSection(SECTION_CC_BILLING); | |
206 UpdateSection(SECTION_SHIPPING); | |
207 } | |
208 | |
209 void AutofillDialogViewAndroid::OnSignInResize(const gfx::Size& pref_size) { | |
210 NOTIMPLEMENTED(); | |
211 } | |
212 | |
213 TestableAutofillDialogView* AutofillDialogViewAndroid::GetTestableView() { | |
214 return NULL; | |
215 } | |
216 | |
217 // TODO(aruslan): bind to the list of accounts population. | |
218 std::vector<std::string> AutofillDialogViewAndroid::GetAvailableUserAccounts() { | |
219 std::vector<std::string> account_names; | |
220 JNIEnv* env = base::android::AttachCurrentThread(); | |
221 ScopedJavaLocalRef<jobjectArray> jaccount_names = | |
222 Java_AutofillDialogGlue_getUserAccountNames(env, java_object_.obj()); | |
223 base::android::AppendJavaStringArrayToStringVector( | |
224 env, jaccount_names.obj(), &account_names); | |
225 return account_names; | |
226 } | |
227 | |
228 // Calls from Java to C++ | |
229 | |
230 void AutofillDialogViewAndroid::ItemSelected(JNIEnv* env, jobject obj, | |
231 jint section, jint index) { | |
232 ui::MenuModel* menuModel = | |
233 GetMenuModelForSection(static_cast<DialogSection>(section)); | |
234 if (menuModel) | |
235 menuModel->ActivatedAt(index); | |
236 } | |
237 | |
238 ScopedJavaLocalRef<jobject> AutofillDialogViewAndroid::GetIconForField( | |
239 JNIEnv* env, | |
240 jobject obj, | |
241 jint field_id, | |
242 jstring jinput) { | |
243 string16 input = base::android::ConvertJavaStringToUTF16(env, jinput); | |
244 return GetJavaBitmap(controller_->IconForField( | |
245 static_cast<AutofillFieldType>(field_id), input)); | |
246 } | |
247 | |
248 ScopedJavaLocalRef<jstring> AutofillDialogViewAndroid::GetPlaceholderForField( | |
249 JNIEnv* env, | |
250 jobject obj, | |
251 jint section, | |
252 jint field_id) { | |
253 if (static_cast<int>(field_id) == CREDIT_CARD_VERIFICATION_CODE) { | |
254 return base::android::ConvertUTF16ToJavaString( | |
255 env, | |
256 l10n_util::GetStringUTF16(IDS_AUTOFILL_DIALOG_PLACEHOLDER_CVC)); | |
257 } | |
258 return ScopedJavaLocalRef<jstring>(); | |
259 } | |
260 | |
261 ScopedJavaLocalRef<jstring> AutofillDialogViewAndroid::GetDialogButtonText( | |
262 JNIEnv* env, | |
263 jobject obj, | |
264 jint dialog_button_id) { | |
265 switch (static_cast<ui::DialogButton>(dialog_button_id)) { | |
266 case ui::DIALOG_BUTTON_OK: | |
267 return base::android::ConvertUTF16ToJavaString( | |
268 env, | |
269 controller_->ConfirmButtonText()); | |
270 | |
271 case ui::DIALOG_BUTTON_CANCEL: | |
272 return base::android::ConvertUTF16ToJavaString( | |
273 env, | |
274 controller_->CancelButtonText()); | |
275 | |
276 case ui::DIALOG_BUTTON_NONE: | |
277 break; | |
278 } | |
279 | |
280 NOTREACHED(); | |
281 return ScopedJavaLocalRef<jstring>(); | |
282 } | |
283 | |
284 jboolean AutofillDialogViewAndroid::IsDialogButtonEnabled( | |
285 JNIEnv* env, | |
286 jobject obj, | |
287 jint dialog_button_id) { | |
288 return static_cast<jboolean>( | |
289 controller_->IsDialogButtonEnabled( | |
290 static_cast<ui::DialogButton>(dialog_button_id))); | |
291 } | |
292 | |
293 ScopedJavaLocalRef<jstring> AutofillDialogViewAndroid::GetSaveLocallyText( | |
294 JNIEnv* env, | |
295 jobject obj) { | |
296 return base::android::ConvertUTF16ToJavaString( | |
297 env, | |
298 controller_->SaveLocallyText()); | |
299 } | |
300 | |
301 ScopedJavaLocalRef<jstring> AutofillDialogViewAndroid::GetLegalDocumentsText( | |
302 JNIEnv* env, | |
303 jobject obj) { | |
304 return base::android::ConvertUTF16ToJavaString( | |
305 env, | |
306 controller_->LegalDocumentsText()); | |
307 } | |
308 | |
309 jboolean AutofillDialogViewAndroid::IsTheAddItem( | |
310 JNIEnv* env, | |
311 jobject obj, | |
312 jint section, | |
313 jint index) { | |
314 return IsTheAddMenuItem(static_cast<DialogSection>(section), | |
315 static_cast<int>(index)); | |
316 } | |
317 | |
318 void AutofillDialogViewAndroid::AccountSelected(JNIEnv* env, jobject obj, | |
319 jint index) { | |
320 ui::MenuModel* model = controller_->MenuModelForAccountChooser(); | |
321 if (!model) | |
322 return; | |
323 | |
324 model->ActivatedAt(index); | |
325 } | |
326 | |
327 void AutofillDialogViewAndroid::NotificationCheckboxStateChanged( | |
328 JNIEnv* env, | |
329 jobject obj, | |
330 jint type, | |
331 jboolean checked) { | |
332 controller_->NotificationCheckboxStateChanged( | |
333 static_cast<DialogNotification::Type>(type), | |
334 static_cast<bool>(checked)); | |
335 } | |
336 | |
337 void AutofillDialogViewAndroid::EditingStart(JNIEnv* env, jobject obj, | |
338 jint jsection) { | |
339 const DialogSection section = static_cast<DialogSection>(jsection); | |
340 // TODO(estade): respect |suggestion_state.text_style|. | |
341 const SuggestionState& suggestion_state = | |
342 controller_->SuggestionStateForSection(section); | |
343 if (suggestion_state.text.empty()) { | |
344 // The section is already in the editing mode, or it's the "Add...". | |
345 return; | |
346 } | |
347 | |
348 controller_->EditClickedForSection(section); | |
349 UpdateOrFillSectionToJava(section, false, UNKNOWN_TYPE); | |
350 } | |
351 | |
352 jboolean AutofillDialogViewAndroid::EditingComplete(JNIEnv* env, | |
353 jobject obj, | |
354 jint jsection) { | |
355 // Unfortunately, edits are not sent to the models, http://crbug.com/223919. | |
356 const DialogSection section = static_cast<DialogSection>(jsection); | |
357 if (ValidateSection(section, VALIDATE_FINAL)) { | |
358 UpdateOrFillSectionToJava(section, false, UNKNOWN_TYPE); | |
359 return true; | |
360 } | |
361 | |
362 return false; | |
363 } | |
364 | |
365 void AutofillDialogViewAndroid::EditingCancel(JNIEnv* env, | |
366 jobject obj, | |
367 jint jsection) { | |
368 const DialogSection section = static_cast<DialogSection>(jsection); | |
369 controller_->EditCancelledForSection(section); | |
370 UpdateSection(section); | |
371 } | |
372 | |
373 void AutofillDialogViewAndroid::EditedOrActivatedField(JNIEnv* env, | |
374 jobject obj, | |
375 jint jsection, | |
376 jint detail_input, | |
377 jint view_android, | |
378 jstring value, | |
379 jboolean was_edit) { | |
380 const DialogSection section = static_cast<DialogSection>(jsection); | |
381 DetailInput* input = reinterpret_cast<DetailInput*>(detail_input); | |
382 ui::ViewAndroid* view = reinterpret_cast<ui::ViewAndroid*>(view_android); | |
383 gfx::Rect rect = gfx::Rect(0, 0, 0, 0); | |
384 string16 value16 = base::android::ConvertJavaStringToUTF16( | |
385 env, value); | |
386 controller_->UserEditedOrActivatedInput( | |
387 section, input, view, rect, value16, was_edit); | |
388 } | |
389 | |
390 ScopedJavaLocalRef<jstring> AutofillDialogViewAndroid::ValidateField( | |
391 JNIEnv* env, | |
392 jobject obj, | |
393 jint jsection, | |
394 jint type, | |
395 jstring value) { | |
396 string16 field_value = base::android::ConvertJavaStringToUTF16(env, value); | |
397 const DialogSection section = static_cast<DialogSection>(jsection); | |
398 AutofillFieldType field_type = static_cast<AutofillFieldType>(type); | |
399 string16 error_message = | |
400 controller_->InputValidityMessage(section, field_type, field_value); | |
401 ScopedJavaLocalRef<jstring> error_text = | |
402 base::android::ConvertUTF16ToJavaString(env, error_message); | |
403 return error_text; | |
404 } | |
405 | |
406 void AutofillDialogViewAndroid::ValidateSection(JNIEnv* env, | |
407 jobject obj, | |
408 jint section) { | |
409 ValidateSection(static_cast<DialogSection>(section), VALIDATE_EDIT); | |
410 } | |
411 | |
412 void AutofillDialogViewAndroid::DialogSubmit(JNIEnv* env, jobject obj) { | |
413 // TODO(aurimas): add validation step. | |
414 controller_->OnAccept(); | |
415 } | |
416 | |
417 void AutofillDialogViewAndroid::DialogCancel(JNIEnv* env, jobject obj) { | |
418 controller_->OnCancel(); | |
419 Hide(); | |
420 } | |
421 | |
422 void AutofillDialogViewAndroid::DialogDismissed(JNIEnv* env, jobject obj) { | |
423 // This might delete |this|, as |this| is owned by the |controller_|. | |
424 controller_->ViewClosed(); | |
425 } | |
426 | |
427 ScopedJavaLocalRef<jstring> AutofillDialogViewAndroid::GetLabelForSection( | |
428 JNIEnv* env, | |
429 jobject obj, | |
430 jint section) { | |
431 string16 label(controller_->LabelForSection( | |
432 static_cast<DialogSection>(section))); | |
433 return base::android::ConvertUTF16ToJavaString(env, label); | |
434 } | |
435 | |
436 ScopedJavaLocalRef<jobjectArray> AutofillDialogViewAndroid::GetListForField( | |
437 JNIEnv* env, | |
438 jobject obj, | |
439 jint field) { | |
440 ui::ComboboxModel* model = controller_->ComboboxModelForAutofillType( | |
441 static_cast<AutofillFieldType>(field)); | |
442 if (!model) | |
443 return ScopedJavaLocalRef<jobjectArray>(); | |
444 | |
445 std::vector<string16> list(model->GetItemCount()); | |
446 for (int i = 0; i < model->GetItemCount(); ++i) { | |
447 list[i] = model->GetItemAt(i); | |
448 } | |
449 return base::android::ToJavaArrayOfStrings(env, list); | |
450 } | |
451 | |
452 void AutofillDialogViewAndroid::ContinueAutomaticSignin( | |
453 JNIEnv* env, jobject obj, | |
454 jstring jaccount_name, jstring jsid, jstring jlsid) { | |
455 const std::string account_name = | |
456 base::android::ConvertJavaStringToUTF8(env, jaccount_name); | |
457 const std::string sid = | |
458 base::android::ConvertJavaStringToUTF8(env, jsid); | |
459 const std::string lsid = | |
460 base::android::ConvertJavaStringToUTF8(env, jlsid); | |
461 // TODO(aruslan): bind to the automatic sign-in. | |
462 // controller_->ContinueAutomaticSignIn(account_name, sid, lsid); | |
463 } | |
464 | |
465 // static | |
466 bool AutofillDialogViewAndroid::RegisterAutofillDialogViewAndroid(JNIEnv* env) { | |
467 return RegisterNativesImpl(env); | |
468 } | |
469 | |
470 bool AutofillDialogViewAndroid::ValidateSection(DialogSection section, | |
471 ValidationType type) { | |
472 DetailOutputMap detail_outputs; | |
473 GetUserInput(section, &detail_outputs); | |
474 ValidityData invalid_inputs = | |
475 controller_->InputsAreValid(section, detail_outputs, type); | |
476 | |
477 const size_t item_count = invalid_inputs.size(); | |
478 if (item_count == 0) return true; | |
479 | |
480 JNIEnv* env = base::android::AttachCurrentThread(); | |
481 ScopedJavaLocalRef<jobjectArray> error_array = | |
482 Java_AutofillDialogGlue_createAutofillDialogFieldError(env, item_count); | |
483 size_t i = 0; | |
484 for (ValidityData::const_iterator iter = | |
485 invalid_inputs.begin(); iter != invalid_inputs.end(); ++iter, ++i) { | |
486 ScopedJavaLocalRef<jstring> error_text = | |
487 base::android::ConvertUTF16ToJavaString(env, iter->second); | |
488 Java_AutofillDialogGlue_addToAutofillDialogFieldErrorArray( | |
489 env, error_array.obj(), i, iter->first, error_text.obj()); | |
490 } | |
491 Java_AutofillDialogGlue_updateSectionErrors(env, | |
492 java_object_.obj(), | |
493 section, | |
494 error_array.obj()); | |
495 return false; | |
496 } | |
497 | |
498 void AutofillDialogViewAndroid::UpdateSaveLocallyCheckBox() { | |
499 JNIEnv* env = base::android::AttachCurrentThread(); | |
500 Java_AutofillDialogGlue_updateSaveLocallyCheckBox( | |
501 env, java_object_.obj(), controller_->ShouldOfferToSaveInChrome()); | |
502 } | |
503 | |
504 void AutofillDialogViewAndroid::UpdateOrFillSectionToJava( | |
505 DialogSection section, | |
506 bool clobber_inputs, | |
507 int field_type_to_always_clobber) { | |
508 JNIEnv* env = base::android::AttachCurrentThread(); | |
509 const DetailInputs& updated_inputs = | |
510 controller_->RequestedFieldsForSection(section); | |
511 | |
512 const size_t input_count = updated_inputs.size(); | |
513 ScopedJavaLocalRef<jobjectArray> field_array = | |
514 Java_AutofillDialogGlue_createAutofillDialogFieldArray( | |
515 env, input_count); | |
516 | |
517 for (size_t i = 0; i < input_count; ++i) { | |
518 const DetailInput& input = updated_inputs[i]; | |
519 | |
520 ScopedJavaLocalRef<jstring> autofilled = | |
521 base::android::ConvertUTF16ToJavaString(env, input.initial_value); | |
522 | |
523 string16 placeholder16; | |
524 if (input.placeholder_text_rid > 0) | |
525 placeholder16 = l10n_util::GetStringUTF16(input.placeholder_text_rid); | |
526 ScopedJavaLocalRef<jstring> placeholder = | |
527 base::android::ConvertUTF16ToJavaString(env, placeholder16); | |
528 | |
529 Java_AutofillDialogGlue_addToAutofillDialogFieldArray( | |
530 env, | |
531 field_array.obj(), | |
532 i, | |
533 reinterpret_cast<jint>(&input), | |
534 input.type, | |
535 placeholder.obj(), | |
536 autofilled.obj()); | |
537 } | |
538 | |
539 ui::MenuModel* menu_model = GetMenuModelForSection(section); | |
540 const int item_count = menu_model->GetItemCount(); | |
541 | |
542 // Never show the "Manage..." item. | |
543 // TODO(aruslan): fix this once we have UI design http://crbug.com/235639. | |
544 const int jitem_count = item_count >= 1 ? item_count - 1 : 0; | |
545 | |
546 ScopedJavaLocalRef<jobjectArray> menu_array = | |
547 Java_AutofillDialogGlue_createAutofillDialogMenuItemArray(env, | |
548 jitem_count); | |
549 const int selected_item = GetSelectedItemIndexForSection(section); | |
550 | |
551 for (int i = 0; i < jitem_count; ++i) { | |
552 const MenuItemButtonType button_type = GetMenuItemButtonType(section, i); | |
553 string16 line1_value = menu_model->GetLabelAt(i); | |
554 string16 line2_value = menu_model->GetSublabelAt(i); | |
555 gfx::Image icon_value; | |
556 menu_model->GetIconAt(i, &icon_value); | |
557 | |
558 if (i == selected_item) { | |
559 CollapseUserDataIntoMenuItem(section, | |
560 &line1_value, &line2_value, | |
561 &icon_value); | |
562 } | |
563 | |
564 ScopedJavaLocalRef<jstring> line1 = | |
565 base::android::ConvertUTF16ToJavaString(env, line1_value); | |
566 ScopedJavaLocalRef<jstring> line2 = | |
567 base::android::ConvertUTF16ToJavaString(env, line2_value); | |
568 ScopedJavaLocalRef<jobject> icon = GetJavaBitmap(icon_value); | |
569 | |
570 Java_AutofillDialogGlue_addToAutofillDialogMenuItemArray( | |
571 env, menu_array.obj(), i, | |
572 line1.obj(), line2.obj(), icon.obj(), | |
573 static_cast<int>(button_type)); | |
574 } | |
575 | |
576 const SuggestionState& suggestion_state = | |
577 controller_->SuggestionStateForSection(section); | |
578 ScopedJavaLocalRef<jstring> suggestion_text = | |
579 base::android::ConvertUTF16ToJavaString(env, suggestion_state.text); | |
580 ScopedJavaLocalRef<jstring> suggestion_text_extra = | |
581 base::android::ConvertUTF16ToJavaString(env, suggestion_state.extra_text); | |
582 ScopedJavaLocalRef<jobject> suggestion_icon = | |
583 GetJavaBitmap(suggestion_state.icon); | |
584 ScopedJavaLocalRef<jobject> suggestion_icon_extra = | |
585 GetJavaBitmap(suggestion_state.extra_icon); | |
586 | |
587 Java_AutofillDialogGlue_updateSection(env, | |
588 java_object_.obj(), | |
589 section, | |
590 controller_->SectionIsActive(section), | |
591 field_array.obj(), | |
592 suggestion_text.obj(), | |
593 suggestion_icon.obj(), | |
594 suggestion_text_extra.obj(), | |
595 suggestion_icon_extra.obj(), | |
596 menu_array.obj(), | |
597 selected_item, | |
598 clobber_inputs, | |
599 field_type_to_always_clobber); | |
600 | |
601 UpdateSaveLocallyCheckBox(); | |
602 } | |
603 | |
604 void AutofillDialogViewAndroid::GetUserInputImpl( | |
605 DialogSection section, | |
606 DetailOutputMap* output) const { | |
607 JNIEnv* env = base::android::AttachCurrentThread(); | |
608 ScopedJavaLocalRef<jobjectArray> fields = | |
609 Java_AutofillDialogGlue_getSection(env, java_object_.obj(), section); | |
610 if (fields.is_null()) | |
611 return; | |
612 | |
613 const int arrayLength = env->GetArrayLength(fields.obj()); | |
614 for (int i = 0; i < arrayLength; ++i) { | |
615 ScopedJavaLocalRef<jobject> field( | |
616 env, env->GetObjectArrayElement(fields.obj(), i)); | |
617 DetailInput* input = reinterpret_cast<DetailInput*>( | |
618 Java_AutofillDialogGlue_getFieldNativePointer(env, field.obj())); | |
619 string16 value = base::android::ConvertJavaStringToUTF16( | |
620 env, Java_AutofillDialogGlue_getFieldValue(env, field.obj()).obj()); | |
621 output->insert(std::make_pair(input, value)); | |
622 } | |
623 } | |
624 | |
625 ui::MenuModel* AutofillDialogViewAndroid::GetMenuModelForSection( | |
626 DialogSection section) const { | |
627 // TODO(aruslan/estade): This should use MenuModelForSectionHack(), which | |
628 // never returns NULL. Later the controller should return a proper platform- | |
629 // specific menu model to match the platform UI flow (or it shouldn't attempt | |
630 // to control the presentation details). | |
631 return controller_->MenuModelForSectionHack(section); | |
632 } | |
633 | |
634 // Returns the index of the currently selected item in |section|, or -1. | |
635 int AutofillDialogViewAndroid::GetSelectedItemIndexForSection( | |
636 DialogSection section) const { | |
637 ui::MenuModel* menuModel = GetMenuModelForSection(section); | |
638 if (!menuModel) return -1; | |
639 | |
640 for (int i = 0; i < menuModel->GetItemCount(); ++i) { | |
641 if (menuModel->IsItemCheckedAt(i)) | |
642 return i; | |
643 } | |
644 | |
645 return -1; | |
646 } | |
647 | |
648 // Returns true if the item at |index| in |section| is the "Add...". | |
649 bool AutofillDialogViewAndroid::IsTheAddMenuItem( | |
650 DialogSection section, int index) const { | |
651 ui::MenuModel* menuModel = GetMenuModelForSection(section); | |
652 return menuModel && index == menuModel->GetItemCount() - 2; | |
653 } | |
654 | |
655 // Returns true if the item at |index| in |section| is the "Manage...". | |
656 bool AutofillDialogViewAndroid::IsTheManageMenuItem( | |
657 DialogSection section, int index) const { | |
658 ui::MenuModel* menuModel = GetMenuModelForSection(section); | |
659 return menuModel && index == menuModel->GetItemCount() - 1; | |
660 } | |
661 | |
662 // Returns an |image| converted to a Java image, or null if |image| is empty. | |
663 ScopedJavaLocalRef<jobject> AutofillDialogViewAndroid::GetJavaBitmap( | |
664 const gfx::Image& image) const { | |
665 ScopedJavaLocalRef<jobject> bitmap; | |
666 const SkBitmap& sk_bitmap = image.AsBitmap(); | |
667 if (!sk_bitmap.isNull() && sk_bitmap.bytesPerPixel() != 0) | |
668 bitmap = gfx::ConvertToJavaBitmap(&sk_bitmap); | |
669 return bitmap; | |
670 } | |
671 | |
672 // Returns a MenuItemButtonType for a menu item at |index| in |section|. | |
673 AutofillDialogViewAndroid::MBT AutofillDialogViewAndroid::GetMenuItemButtonType( | |
674 DialogSection section, | |
675 int index) const { | |
676 // TODO(aruslan): Remove/fix this once http://crbug.com/224162 is closed. | |
677 ui::MenuModel* menu_model = GetMenuModelForSection(section); | |
678 if (!menu_model) | |
679 return MENU_ITEM_BUTTON_TYPE_NONE; | |
680 | |
681 // "Use billing for shipping" is not editable and it's always first. | |
682 if (section == SECTION_SHIPPING && index == 0) | |
683 return MENU_ITEM_BUTTON_TYPE_NONE; | |
684 | |
685 // The last item ("Manage...") is never editable. | |
686 if (IsTheManageMenuItem(section, index)) | |
687 return MENU_ITEM_BUTTON_TYPE_NONE; | |
688 | |
689 // The second last item ("Add..."): | |
690 if (IsTheAddMenuItem(section, index)) { | |
691 const bool currently_selected = | |
692 index == GetSelectedItemIndexForSection(section); | |
693 // - should show "Add..." button if not selected; | |
694 if (!currently_selected) | |
695 return MENU_ITEM_BUTTON_TYPE_ADD; | |
696 | |
697 // - should show "Add..." button if selected and doesn't have user data; | |
698 string16 label, sublabel; | |
699 gfx::Image icon; | |
700 const bool has_user_data = | |
701 CollapseUserDataIntoMenuItem(section, &label, &sublabel, &icon); | |
702 if (!has_user_data) | |
703 return MENU_ITEM_BUTTON_TYPE_ADD; | |
704 | |
705 // - otherwise, should show the "Edit..." button (as any "normal" items). | |
706 } | |
707 | |
708 return MENU_ITEM_BUTTON_TYPE_EDIT; | |
709 } | |
710 | |
711 // TODO(aruslan): Remove/fix this once http://crbug.com/230685 is closed. | |
712 namespace { | |
713 | |
714 bool IsCreditCardType(AutofillFieldType type) { | |
715 return AutofillType(type).group() == AutofillType::CREDIT_CARD; | |
716 } | |
717 | |
718 class CollapsedAutofillProfileWrapper : public AutofillProfileWrapper { | |
719 public: | |
720 explicit CollapsedAutofillProfileWrapper(const AutofillProfile* profile) | |
721 : AutofillProfileWrapper(profile, 0) { | |
722 } | |
723 virtual ~CollapsedAutofillProfileWrapper() { | |
724 } | |
725 | |
726 virtual string16 GetDisplayText() OVERRIDE { | |
727 const string16 name_full = GetInfo(NAME_FULL); | |
728 const string16 comma = ASCIIToUTF16(", "); | |
729 const string16 address2 = GetInfo(ADDRESS_HOME_LINE2); | |
730 | |
731 string16 label; | |
732 if (!name_full.empty()) | |
733 label = name_full + comma; | |
734 label += GetInfo(ADDRESS_HOME_LINE1); | |
735 if (!address2.empty()) | |
736 label += comma + address2; | |
737 return label; | |
738 } | |
739 | |
740 string16 GetSublabel() { | |
741 const string16 comma = ASCIIToUTF16(", "); | |
742 return | |
743 GetInfo(ADDRESS_HOME_CITY) + comma + | |
744 GetInfo(ADDRESS_HOME_STATE) + ASCIIToUTF16(" ") + | |
745 GetInfo(ADDRESS_HOME_ZIP); | |
746 } | |
747 | |
748 private: | |
749 DISALLOW_COPY_AND_ASSIGN(CollapsedAutofillProfileWrapper); | |
750 }; | |
751 | |
752 | |
753 // Get billing info from |output| and put it into |card|, |cvc|, and |profile|. | |
754 // These outparams are required because |card|/|profile| accept different types | |
755 // of raw info. | |
756 void GetBillingInfoFromOutputs(const DetailOutputMap& output, | |
757 CreditCard* card, | |
758 AutofillProfile* profile) { | |
759 for (DetailOutputMap::const_iterator it = output.begin(); | |
760 it != output.end(); ++it) { | |
761 string16 trimmed; | |
762 TrimWhitespace(it->second, TRIM_ALL, &trimmed); | |
763 | |
764 if (it->first->type == ADDRESS_HOME_COUNTRY || | |
765 it->first->type == ADDRESS_BILLING_COUNTRY) { | |
766 profile->SetInfo(it->first->type, | |
767 trimmed, | |
768 g_browser_process->GetApplicationLocale()); | |
769 } else { | |
770 // Copy the credit card name to |profile| in addition to |card| as | |
771 // wallet::Instrument requires a recipient name for its billing address. | |
772 if (profile && it->first->type == CREDIT_CARD_NAME) | |
773 profile->SetRawInfo(NAME_FULL, trimmed); | |
774 | |
775 if (IsCreditCardType(it->first->type)) { | |
776 if (card) | |
777 card->SetRawInfo(it->first->type, trimmed); | |
778 } else if (profile) { | |
779 profile->SetRawInfo(it->first->type, trimmed); | |
780 } | |
781 } | |
782 } | |
783 } | |
784 | |
785 } // namespace | |
786 | |
787 // Returns true and fills in the |label|, |sublabel| and |icon if | |
788 // a given |section| has the user input. | |
789 // TODO(aruslan): Remove/fix this once http://crbug.com/230685 is closed. | |
790 bool AutofillDialogViewAndroid::CollapseUserDataIntoMenuItem( | |
791 DialogSection section, | |
792 string16* label_to_set, | |
793 string16* sublabel_to_set, | |
794 gfx::Image* icon_to_set) const { | |
795 const SuggestionState& suggestion_state = | |
796 controller_->SuggestionStateForSection(section); | |
797 if (!suggestion_state.text.empty()) | |
798 return false; | |
799 | |
800 DetailOutputMap inputs; | |
801 GetUserInputImpl(section, &inputs); | |
802 | |
803 string16 label; | |
804 string16 sublabel; | |
805 gfx::Image icon; | |
806 | |
807 switch (section) { | |
808 case SECTION_CC: { | |
809 CreditCard card; | |
810 GetBillingInfoFromOutputs(inputs, &card, NULL); | |
811 AutofillCreditCardWrapper ccw(&card); | |
812 label = card.Label(); | |
813 icon = ccw.GetIcon(); | |
814 break; | |
815 } | |
816 | |
817 case SECTION_BILLING: { | |
818 AutofillProfile profile; | |
819 GetBillingInfoFromOutputs(inputs, NULL, &profile); | |
820 CollapsedAutofillProfileWrapper pw(&profile); | |
821 label = pw.GetDisplayText(); | |
822 sublabel = pw.GetSublabel(); | |
823 break; | |
824 } | |
825 | |
826 case SECTION_CC_BILLING: { | |
827 CreditCard card; | |
828 AutofillProfile profile; | |
829 GetBillingInfoFromOutputs(inputs, &card, &profile); | |
830 AutofillCreditCardWrapper ccw(&card); | |
831 CollapsedAutofillProfileWrapper pw(&profile); | |
832 label = ccw.GetDisplayText(); | |
833 sublabel = pw.GetDisplayText(); | |
834 icon = ccw.GetIcon(); | |
835 break; | |
836 } | |
837 | |
838 case SECTION_SHIPPING: { | |
839 AutofillProfile profile; | |
840 GetBillingInfoFromOutputs(inputs, NULL, &profile); | |
841 CollapsedAutofillProfileWrapper pw(&profile); | |
842 label = pw.GetDisplayText(); | |
843 sublabel = pw.GetSublabel(); | |
844 break; | |
845 } | |
846 | |
847 case SECTION_EMAIL: { | |
848 for (DetailOutputMap::const_iterator iter = inputs.begin(); | |
849 iter != inputs.end(); ++iter) { | |
850 if (iter->first->type == EMAIL_ADDRESS) | |
851 label = iter->second; | |
852 } | |
853 break; | |
854 } | |
855 } | |
856 | |
857 if (label.empty()) | |
858 return false; | |
859 | |
860 *label_to_set = label; | |
861 *sublabel_to_set = sublabel; | |
862 *icon_to_set = icon; | |
863 return true; | |
864 } | |
865 } // namespace autofill | |
OLD | NEW |