2 * Copyright (C) 2010 Google Inc.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.i18n.addressinput;
19 import android.app.ProgressDialog;
20 import android.content.Context;
21 import android.os.Handler;
22 import android.telephony.TelephonyManager;
23 import android.util.Log;
24 import android.view.LayoutInflater;
25 import android.view.View;
26 import android.view.ViewGroup;
27 import android.widget.AdapterView;
28 import android.widget.ArrayAdapter;
29 import android.widget.EditText;
30 import android.widget.LinearLayout;
31 import android.widget.LinearLayout.LayoutParams;
32 import android.widget.Spinner;
33 import android.widget.TextView;
35 import com.android.i18n.addressinput.AddressField.WidthType;
36 import com.android.i18n.addressinput.AddressUiComponent.UiComponent;
37 import com.android.i18n.addressinput.LookupKey.KeyType;
38 import com.android.i18n.addressinput.LookupKey.ScriptType;
40 import java.text.Collator;
41 import java.util.ArrayList;
42 import java.util.Collections;
43 import java.util.EnumMap;
44 import java.util.HashMap;
45 import java.util.List;
46 import java.util.Locale;
50 * Address widget that lays out address fields, validate and format addresses according to local
53 public class AddressWidget implements AdapterView.OnItemSelectedListener {
54 private Context context;
56 private ViewGroup rootView;
58 private LayoutInflater inflater;
60 private CacheData cacheData;
62 // A map for all address fields.
63 private final EnumMap<AddressField, AddressUiComponent> inputWidgets =
64 new EnumMap<AddressField, AddressUiComponent>(AddressField.class);
66 private FormController formController;
68 private FormatInterpreter formatInterpreter;
70 private FormOptions formOptions;
72 private StandardAddressVerifier verifier;
74 private ProgressDialog progressDialog;
76 private String currentRegion;
78 // The current language the widget uses in BCP47 format. It differs from the default locale of
79 // the phone in that it contains information on the script to use.
80 private String widgetLocale;
82 private ScriptType script;
84 // Possible labels that could be applied to the admin area field of the current country.
85 // Examples include "state", "province", "emirate", etc.
86 private static final Map<String, Integer> ADMIN_LABELS;
87 // Possible labels that could be applied to the locality (city) field of the current country.
88 // Examples include "city" or "district".
89 private static final Map<String, Integer> LOCALITY_LABELS;
90 // Possible labels that could be applied to the sublocality field of the current country.
91 // Examples include "suburb" or "neighborhood".
92 private static final Map<String, Integer> SUBLOCALITY_LABELS;
94 private static final FormOptions SHOW_ALL_FIELDS = new FormOptions.Builder().build();
96 // The appropriate label that should be applied to the zip code field of the current country.
97 private enum ZipLabel {
102 private ZipLabel zipLabel;
105 Map<String, Integer> adminLabelMap = new HashMap<String, Integer>(15);
106 adminLabelMap.put("area", R.string.i18n_area);
107 adminLabelMap.put("county", R.string.i18n_county);
108 adminLabelMap.put("department", R.string.i18n_department);
109 adminLabelMap.put("district", R.string.i18n_district);
110 adminLabelMap.put("do_si", R.string.i18n_do_si);
111 adminLabelMap.put("emirate", R.string.i18n_emirate);
112 adminLabelMap.put("island", R.string.i18n_island);
113 adminLabelMap.put("oblast", R.string.i18n_oblast);
114 adminLabelMap.put("parish", R.string.i18n_parish);
115 adminLabelMap.put("prefecture", R.string.i18n_prefecture);
116 adminLabelMap.put("province", R.string.i18n_province);
117 adminLabelMap.put("state", R.string.i18n_state);
118 ADMIN_LABELS = Collections.unmodifiableMap(adminLabelMap);
120 Map<String, Integer> localityLabelMap = new HashMap<String, Integer>(2);
121 localityLabelMap.put("city", R.string.i18n_locality_label);
122 localityLabelMap.put("district", R.string.i18n_district);
123 localityLabelMap.put("post_town", R.string.i18n_post_town);
124 LOCALITY_LABELS = Collections.unmodifiableMap(localityLabelMap);
126 Map<String, Integer> sublocalityLabelMap = new HashMap<String, Integer>(2);
127 sublocalityLabelMap.put("suburb", R.string.i18n_suburb);
128 sublocalityLabelMap.put("district", R.string.i18n_district);
129 sublocalityLabelMap.put("neighborhood", R.string.i18n_neighborhood);
130 sublocalityLabelMap.put("village_township", R.string.i18n_village_township);
131 SUBLOCALITY_LABELS = Collections.unmodifiableMap(sublocalityLabelMap);
134 // Need handler for callbacks to the UI thread
135 final Handler handler = new Handler();
137 final Runnable updateMultipleFields = new Runnable() {
144 private class UpdateRunnable implements Runnable {
145 private AddressField myId;
147 public UpdateRunnable(AddressField id) {
153 updateInputWidget(myId);
157 private static class AddressSpinnerInfo {
158 private Spinner view;
160 private AddressField id;
162 private AddressField parentId;
164 private ArrayAdapter<String> adapter;
166 private List<RegionData> currentRegions;
168 @SuppressWarnings("unchecked")
169 public AddressSpinnerInfo(Spinner view, AddressField id, AddressField parentId) {
172 this.parentId = parentId;
173 this.adapter = (ArrayAdapter<String>) view.getAdapter();
176 public void setSpinnerList(List<RegionData> list, String defaultKey) {
177 currentRegions = list;
179 for (RegionData item : list) {
180 adapter.add(item.getDisplayName());
182 adapter.sort(Collator.getInstance(Locale.getDefault()));
183 if (defaultKey.length() == 0) {
184 view.setSelection(0);
186 int position = adapter.getPosition(defaultKey);
187 view.setSelection(position);
191 // Returns the region key of the currently selected region in the Spinner.
192 public String getRegionCode(int position) {
193 if (adapter.getCount() <= position) {
196 String value = adapter.getItem(position);
197 return getRegionDataKeyForValue(value);
200 // Returns the region key for the region value.
201 public String getRegionDataKeyForValue(String value) {
202 for (RegionData data : currentRegions) {
203 if (data.getDisplayName().endsWith(value)) {
204 return data.getKey();
211 private final ArrayList<AddressSpinnerInfo> spinners = new ArrayList<AddressSpinnerInfo>();
213 private AddressWidgetUiComponentProvider componentProvider;
215 /** TODO: Add region-dependent width types for address fields. */
216 private WidthType getFieldWidthType(AddressUiComponent field) {
217 return field.getId().getDefaulWidthType();
220 private void createView(ViewGroup rootView, AddressUiComponent field, String defaultKey,
222 @SuppressWarnings("deprecation") // FILL_PARENT renamed MATCH_PARENT in API Level 8.
223 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
224 LayoutParams.WRAP_CONTENT);
225 String fieldText = field.getFieldName();
226 WidthType widthType = getFieldWidthType(field);
228 if (fieldText.length() > 0) {
229 TextView textView = componentProvider.createUiLabel(fieldText, widthType);
230 rootView.addView(textView, lp);
232 if (field.getUiType().equals(UiComponent.EDIT)) {
233 EditText editText = componentProvider.createUiTextField(widthType);
234 field.setView(editText);
235 editText.setEnabled(!readOnly);
236 rootView.addView(editText, lp);
237 } else if (field.getUiType().equals(UiComponent.SPINNER)) {
238 ArrayAdapter<String> adapter = componentProvider.createUiPickerAdapter(widthType);
239 Spinner spinner = componentProvider.createUiPickerSpinner(widthType);
241 field.setView(spinner);
242 rootView.addView(spinner, lp);
243 spinner.setAdapter(adapter);
244 AddressSpinnerInfo spinnerInfo =
245 new AddressSpinnerInfo(spinner, field.getId(), field.getParentId());
246 spinnerInfo.setSpinnerList(field.getCandidatesList(), defaultKey);
248 if (fieldText.length() > 0) {
249 spinner.setPrompt(fieldText);
251 spinner.setOnItemSelectedListener(this);
252 spinners.add(spinnerInfo);
257 * Associates each field with its corresponding AddressUiComponent.
259 private void buildFieldWidgets() {
260 AddressData data = new AddressData.Builder().setCountry(currentRegion).build();
261 LookupKey key = new LookupKey.Builder(LookupKey.KeyType.DATA).setAddressData(data).build();
262 AddressVerificationNodeData countryNode =
263 (new ClientData(cacheData)).getDefaultData(key.toString());
265 // Set up AddressField.ADMIN_AREA
266 AddressUiComponent adminAreaUi = new AddressUiComponent(AddressField.ADMIN_AREA);
267 adminAreaUi.setFieldName(getAdminAreaFieldName(countryNode));
268 inputWidgets.put(AddressField.ADMIN_AREA, adminAreaUi);
270 // Set up AddressField.LOCALITY
271 AddressUiComponent localityUi = new AddressUiComponent(AddressField.LOCALITY);
272 localityUi.setFieldName(getLocalityFieldName(countryNode));
273 inputWidgets.put(AddressField.LOCALITY, localityUi);
275 // Set up AddressField.DEPENDENT_LOCALITY
276 AddressUiComponent subLocalityUi = new AddressUiComponent(AddressField.DEPENDENT_LOCALITY);
277 subLocalityUi.setFieldName(getSublocalityFieldName(countryNode));
278 inputWidgets.put(AddressField.DEPENDENT_LOCALITY, subLocalityUi);
280 // Set up AddressField.ADDRESS_LINE_1
281 AddressUiComponent addressLine1Ui = new AddressUiComponent(AddressField.ADDRESS_LINE_1);
282 addressLine1Ui.setFieldName(context.getString(R.string.i18n_address_line1_label));
283 inputWidgets.put(AddressField.ADDRESS_LINE_1, addressLine1Ui);
285 // Set up AddressField.ADDRESS_LINE_2
286 AddressUiComponent addressLine2Ui = new AddressUiComponent(AddressField.ADDRESS_LINE_2);
287 addressLine2Ui.setFieldName("");
288 inputWidgets.put(AddressField.ADDRESS_LINE_2, addressLine2Ui);
290 // Set up AddressField.ORGANIZATION
291 AddressUiComponent organizationUi = new AddressUiComponent(AddressField.ORGANIZATION);
292 organizationUi.setFieldName(context.getString(R.string.i18n_organization_label));
293 inputWidgets.put(AddressField.ORGANIZATION, organizationUi);
295 // Set up AddressField.RECIPIENT
296 AddressUiComponent recipientUi = new AddressUiComponent(AddressField.RECIPIENT);
297 recipientUi.setFieldName(context.getString(R.string.i18n_recipient_label));
298 inputWidgets.put(AddressField.RECIPIENT, recipientUi);
300 // Set up AddressField.POSTAL_CODE
301 AddressUiComponent postalCodeUi = new AddressUiComponent(AddressField.POSTAL_CODE);
302 postalCodeUi.setFieldName(getZipFieldName(countryNode));
303 inputWidgets.put(AddressField.POSTAL_CODE, postalCodeUi);
305 // Set up AddressField.SORTING_CODE
306 AddressUiComponent sortingCodeUi = new AddressUiComponent(AddressField.SORTING_CODE);
307 sortingCodeUi.setFieldName("CEDEX");
308 inputWidgets.put(AddressField.SORTING_CODE, sortingCodeUi);
311 private void initializeDropDowns() {
312 AddressUiComponent adminAreaUi = inputWidgets.get(AddressField.ADMIN_AREA);
313 List<RegionData> adminAreaList = getRegionData(AddressField.COUNTRY);
314 adminAreaUi.initializeCandidatesList(adminAreaList);
316 AddressUiComponent localityUi = inputWidgets.get(AddressField.LOCALITY);
317 List<RegionData> localityList = getRegionData(AddressField.ADMIN_AREA);
318 localityUi.initializeCandidatesList(localityList);
321 // ZIP code is called postal code in some countries. This method returns the appropriate name
322 // for the given countryNode.
323 private String getZipFieldName(AddressVerificationNodeData countryNode) {
325 String zipType = countryNode.get(AddressDataKey.ZIP_NAME_TYPE);
326 if (zipType == null) {
327 zipLabel = ZipLabel.POSTAL;
328 zipName = context.getString(R.string.i18n_postal_code_label);
330 zipLabel = ZipLabel.ZIP;
331 zipName = context.getString(R.string.i18n_zip_code_label);
336 private String getLocalityFieldName(AddressVerificationNodeData countryNode) {
337 String localityLabelType = countryNode.get(AddressDataKey.LOCALITY_NAME_TYPE);
338 Integer result = LOCALITY_LABELS.get(localityLabelType);
339 if (result == null) {
341 result = R.string.i18n_locality_label;
343 return context.getString(result);
346 private String getSublocalityFieldName(AddressVerificationNodeData countryNode) {
347 String sublocalityLabelType = countryNode.get(AddressDataKey.SUBLOCALITY_NAME_TYPE);
348 Integer result = SUBLOCALITY_LABELS.get(sublocalityLabelType);
349 if (result == null) {
350 // Fallback to suburb.
351 result = R.string.i18n_suburb;
353 return context.getString(result);
356 private String getAdminAreaFieldName(AddressVerificationNodeData countryNode) {
357 String adminLabelType = countryNode.get(AddressDataKey.STATE_NAME_TYPE);
358 Integer result = ADMIN_LABELS.get(adminLabelType);
359 if (result == null) {
360 // Fallback to province.
361 result = R.string.i18n_province;
363 return context.getString(result);
366 private void buildCountryListBox() {
367 // Set up AddressField.COUNTRY
368 AddressUiComponent countryUi = new AddressUiComponent(AddressField.COUNTRY);
369 countryUi.setFieldName(context.getString(R.string.i18n_country_or_region_label));
370 ArrayList<RegionData> countries = new ArrayList<RegionData>();
371 for (RegionData regionData : formController.getRegionData(new LookupKey.Builder(
372 KeyType.DATA).build())) {
373 String regionKey = regionData.getKey();
374 // ZZ represents an unknown region code.
375 if (!regionKey.equals("ZZ")) {
376 String localCountryName = getLocalCountryName(regionKey);
377 RegionData country = new RegionData.Builder().setKey(regionKey).setName(
378 localCountryName).build();
379 countries.add(country);
382 countryUi.initializeCandidatesList(countries);
383 inputWidgets.put(AddressField.COUNTRY, countryUi);
386 private String getLocalCountryName(String regionCode) {
387 return (new Locale("", regionCode)).getDisplayCountry(Locale.getDefault());
390 private AddressSpinnerInfo findSpinnerByView(View view) {
391 for (AddressSpinnerInfo spinnerInfo : spinners) {
392 if (spinnerInfo.view == view) {
399 private void updateFields() {
400 removePreviousViews();
402 initializeDropDowns();
403 layoutAddressFields();
406 private void removePreviousViews() {
407 if (rootView == null) {
410 int childCount = rootView.getChildCount();
411 if (formOptions.isHidden(AddressField.COUNTRY)) {
412 if (childCount > 0) {
413 rootView.removeAllViews();
415 } else if (childCount > 2) {
416 // Keep the TextView and Spinner for Country and remove everything else.
417 rootView.removeViews(2, rootView.getChildCount() - 2);
421 private void layoutAddressFields() {
422 for (AddressField field : formatInterpreter.getAddressFieldOrder(script,
424 if (!formOptions.isHidden(field)) {
425 createView(rootView, inputWidgets.get(field), "", formOptions.isReadonly(field));
430 private void updateChildNodes(AdapterView<?> parent, int position) {
431 AddressSpinnerInfo spinnerInfo = findSpinnerByView(parent);
432 if (spinnerInfo == null) {
436 // Find all the child spinners, if any, that depend on this one.
437 final AddressField myId = spinnerInfo.id;
438 if (myId != AddressField.COUNTRY && myId != AddressField.ADMIN_AREA
439 && myId != AddressField.LOCALITY) {
440 // Only a change in the three AddressFields above will trigger a change in other
441 // AddressFields. Therefore, for all other AddressFields, we return immediately.
445 String regionCode = spinnerInfo.getRegionCode(position);
446 if (myId == AddressField.COUNTRY) {
447 updateWidgetOnCountryChange(regionCode);
451 formController.requestDataForAddress(getAddressData(), new DataLoadListener() {
453 public void dataLoadingBegin(){
457 public void dataLoadingEnd() {
458 Runnable updateChild = new UpdateRunnable(myId);
459 handler.post(updateChild);
464 public void updateWidgetOnCountryChange(String regionCode) {
465 if (currentRegion.equalsIgnoreCase(regionCode)) {
468 currentRegion = regionCode;
469 formController.setCurrentCountry(currentRegion);
473 private void updateInputWidget(AddressField myId) {
474 for (AddressSpinnerInfo child : spinners) {
475 if (child.parentId == myId) {
476 List<RegionData> candidates = getRegionData(child.parentId);
477 child.setSpinnerList(candidates, "");
482 public void renderForm() {
483 setWidgetLocaleAndScript();
484 AddressData data = new AddressData.Builder().setCountry(currentRegion)
485 .setLanguageCode(widgetLocale).build();
486 formController.requestDataForAddress(data, new DataLoadListener() {
488 public void dataLoadingBegin() {
489 progressDialog = componentProvider.getUiActivityIndicatorView();
490 progressDialog.setMessage(context.getString(R.string.address_data_loading));
491 Log.d(this.toString(), "Progress dialog started.");
494 public void dataLoadingEnd() {
495 Log.d(this.toString(), "Data loading completed.");
496 progressDialog.dismiss();
497 Log.d(this.toString(), "Progress dialog stopped.");
498 handler.post(updateMultipleFields);
503 private void setWidgetLocaleAndScript() {
504 widgetLocale = Util.getWidgetCompatibleLanguageCode(Locale.getDefault(), currentRegion);
505 formController.setLanguageCode(widgetLocale);
506 script = Util.isExplicitLatinScript(widgetLocale)
511 private List<RegionData> getRegionData(AddressField parentField) {
512 AddressData address = getAddressData();
514 // Removes language code from address if it is default. This address is used to build
515 // lookup key, which neglects default language. For example, instead of "data/US--en/CA",
516 // the right lookup key is "data/US/CA".
517 if (formController.isDefaultLanguage(address.getLanguageCode())) {
518 address = new AddressData.Builder(address).setLanguageCode(null).build();
521 LookupKey parentKey = formController.getDataKeyFor(address).getKeyForUpperLevelField(
523 List<RegionData> candidates;
524 // Can't build a key with parent field, quit.
525 if (parentKey == null) {
526 Log.w(this.toString(), "Can't build key with parent field " + parentField + ". One of"
527 + " the ancestor fields might be empty");
529 // Removes candidates that exist from previous settings. For example, data/US has a
530 // list of candidates AB, BC, CA, etc, that list should be cleaned up when user updates
531 // the address by changing country to Channel Islands.
532 candidates = new ArrayList<RegionData>(1);
534 candidates = formController.getRegionData(parentKey);
540 * Creates an AddressWidget to be attached to rootView for the specific context using the
541 * default UI component provider.
543 public AddressWidget(Context context, ViewGroup rootView, FormOptions formOptions,
544 ClientCacheManager cacheManager) {
545 this(context, rootView, formOptions, cacheManager,
546 new AddressWidgetUiComponentProvider(context));
550 * Creates an AddressWidget to be attached to rootView for the specific context using UI
551 * component provided by the provider.
553 public AddressWidget(Context context, ViewGroup rootView, FormOptions formOptions,
554 ClientCacheManager cacheManager, AddressWidgetUiComponentProvider provider) {
555 componentProvider = provider;
557 ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE))
558 .getSimCountryIso().toUpperCase(Locale.US);
559 if (currentRegion.length() == 0) {
560 currentRegion = "US";
562 init(context, rootView, formOptions, cacheManager);
567 * Creates an AddressWidget to be attached to rootView for the specific context using the
568 * default UI component provider, and fill out the address form with savedAddress.
570 public AddressWidget(Context context, ViewGroup rootView, FormOptions formOptions,
571 ClientCacheManager cacheManager, AddressData savedAddress) {
572 this(context, rootView, formOptions, cacheManager, savedAddress,
573 new AddressWidgetUiComponentProvider(context));
577 * Creates an AddressWidget to be attached to rootView for the specific context using UI
578 * component provided by the provider, and fill out the address form with savedAddress.
580 public AddressWidget(Context context, ViewGroup rootView, FormOptions formOptions,
581 ClientCacheManager cacheManager, AddressData savedAddress,
582 AddressWidgetUiComponentProvider provider) {
583 componentProvider = provider;
584 currentRegion = savedAddress.getPostalCountry();
585 // Postal country must be 2 letter country code. Otherwise default to US.
586 if (currentRegion == null || currentRegion.length() != 2) {
587 currentRegion = "US";
589 init(context, rootView, formOptions, cacheManager);
590 renderFormWithSavedAddress(savedAddress);
593 public void renderFormWithSavedAddress(AddressData savedAddress) {
594 setWidgetLocaleAndScript();
595 removePreviousViews();
597 layoutAddressFields();
598 initializeFieldsWithAddress(savedAddress);
601 private void initializeFieldsWithAddress(AddressData savedAddress) {
602 for (AddressField field : formatInterpreter.getAddressFieldOrder(script, currentRegion)) {
603 String value = savedAddress.getFieldValue(field);
607 AddressUiComponent uiComponent = inputWidgets.get(field);
608 EditText view = (EditText) uiComponent.getView();
615 private void init(Context context, ViewGroup rootView, FormOptions formOptions,
616 ClientCacheManager cacheManager) {
617 this.context = context;
618 this.rootView = rootView;
619 this.formOptions = formOptions;
620 this.cacheData = new CacheData(cacheManager);
621 this.inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
622 this.formController =
623 new FormController(new ClientData(cacheData), widgetLocale, currentRegion);
624 this.formatInterpreter = new FormatInterpreter(formOptions);
625 this.verifier = new StandardAddressVerifier(new FieldVerifier(new ClientData(cacheData)));
626 if (!formOptions.isHidden(AddressField.COUNTRY)) {
627 buildCountryListBox();
628 createView(rootView, inputWidgets.get(AddressField.COUNTRY),
629 getLocalCountryName(currentRegion),
630 formOptions.isReadonly(AddressField.COUNTRY));
635 * Sets address data server URL. Input URL cannot be null.
637 * @param url The service URL.
639 public void setUrl(String url) {
640 cacheData.setUrl(url);
644 * Gets user input address in AddressData format.
646 public AddressData getAddressData() {
647 AddressData.Builder builder = new AddressData.Builder();
648 builder.setCountry(currentRegion);
649 for (AddressField field : formatInterpreter.getAddressFieldOrder(script,
651 AddressUiComponent addressUiComponent = inputWidgets.get(field);
652 if (addressUiComponent != null) {
653 String value = addressUiComponent.getValue();
654 if (addressUiComponent.getUiType() == UiComponent.SPINNER) {
655 // For drop-downs, return the key of the region selected instead of the value.
656 View view = getViewForField(field);
657 AddressSpinnerInfo spinnerInfo = findSpinnerByView(view);
658 if (spinnerInfo != null) {
659 value = spinnerInfo.getRegionDataKeyForValue(value);
662 builder.set(field, value);
665 builder.setLanguageCode(widgetLocale);
666 return builder.build();
670 * Gets the formatted address.
672 * This method does not validate addresses. Also, it will "normalize" the result strings by
673 * removing redundant spaces and empty lines.
675 * @return the formatted address
677 public List<String> getEnvelopeAddress() {
678 return formatInterpreter.getEnvelopeAddress(getAddressData());
682 * Gets the formatted address based on the AddressData passed in.
684 public List<String> getEnvelopeAddress(AddressData address) {
685 return formatInterpreter.getEnvelopeAddress(address);
689 * Gets the formatted address based on the AddressData passed in with none of the relevant
692 public static List<String> getFullEnvelopeAddress(AddressData address) {
693 return new FormatInterpreter(SHOW_ALL_FIELDS).getEnvelopeAddress(address);
697 * Get problems found in the address data entered by the user.
699 public AddressProblems getAddressProblems() {
700 AddressProblems problems = new AddressProblems();
701 AddressData addressData = getAddressData();
702 verifier.verify(addressData, problems);
707 * Displays an appropriate error message for an AddressField with a problem.
709 * @return the View object representing the AddressField.
711 public View displayErrorMessageForField(AddressData address,
712 AddressField field, AddressProblemType problem) {
713 Log.d(this.toString(), "Display error message for the field: " + field.toString());
714 AddressUiComponent addressUiComponent = inputWidgets.get(field);
715 if (addressUiComponent != null && addressUiComponent.getUiType() == UiComponent.EDIT) {
716 EditText view = (EditText) addressUiComponent.getView();
717 view.setError(getErrorMessageForInvalidEntry(address, field, problem));
723 private String getErrorMessageForInvalidEntry(AddressData address, AddressField field,
724 AddressProblemType problem) {
726 case MISSING_REQUIRED_FIELD:
727 return context.getString(R.string.i18n_missing_required_field);
729 String currentValue = address.getFieldValue(field);
730 return String.format(context.getString(R.string.unknown_entry), currentValue);
731 case UNRECOGNIZED_FORMAT:
732 // We only support this error type for the Postal Code field.
733 return (zipLabel == ZipLabel.POSTAL
734 ? context.getString(R.string.unrecognized_format_postal_code)
735 : context.getString(R.string.unrecognized_format_zip_code));
736 case MISMATCHING_VALUE:
737 // We only support this error type for the Postal Code field.
738 return (zipLabel == ZipLabel.POSTAL
739 ? context.getString(R.string.mismatching_value_postal_code)
740 : context.getString(R.string.mismatching_value_zip_code));
746 * Clears all error messages in the UI.
748 public void clearErrorMessage() {
749 for (AddressField field : formatInterpreter.getAddressFieldOrder(script,
751 AddressUiComponent addressUiComponent = inputWidgets.get(field);
753 if (addressUiComponent != null && addressUiComponent.getUiType() == UiComponent.EDIT) {
754 EditText view = (EditText) addressUiComponent.getView();
762 public View getViewForField(AddressField field) {
763 AddressUiComponent component = inputWidgets.get(field);
764 if (component == null) {
767 return component.getView();
771 public void onNothingSelected(AdapterView<?> arg0) {
775 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
776 updateChildNodes(parent, position);