Upstream version 8.36.169.0
[platform/framework/web/crosswalk.git] / src / third_party / libaddressinput / src / java / src / com / android / i18n / addressinput / AddressWidget.java
1 /*
2  * Copyright (C) 2010 Google Inc.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package com.android.i18n.addressinput;
18
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;
34
35 import com.android.i18n.addressinput.AddressUIComponent.UIComponent;
36 import com.android.i18n.addressinput.LookupKey.KeyType;
37 import com.android.i18n.addressinput.LookupKey.ScriptType;
38
39 import java.util.ArrayList;
40 import java.text.Collator;
41 import java.util.Collections;
42 import java.util.EnumMap;
43 import java.util.HashMap;
44 import java.util.List;
45 import java.util.Locale;
46 import java.util.Map;
47
48 /**
49  * Address widget that lays out address fields, validate and format addresses according to local
50  * customs.
51  */
52 public class AddressWidget implements AdapterView.OnItemSelectedListener {
53     private Context mContext;
54
55     private ViewGroup mRootView;
56
57     private LayoutInflater mInflater;
58
59     private CacheData mCacheData;
60
61     // A map for all address fields except for country.
62     private final EnumMap<AddressField, AddressUIComponent> mInputWidgets =
63         new EnumMap<AddressField, AddressUIComponent>(AddressField.class);
64
65     private FormController mFormController;
66
67     private FormatInterpreter mFormatInterpreter;
68
69     private FormOptions mFormOptions;
70
71     private StandardAddressVerifier mVerifier;
72
73     private ProgressDialog mProgressDialog;
74
75     private String mCurrentRegion;
76
77     // The current language the widget use in BCP47 format. It differs from the default locale of
78     // the phone in that it contains information on the script to use.
79     private String mWidgetLocale;
80
81     private ScriptType mScript;
82
83     // The appropriate label that should be applied to the admin area field of the current country.
84     // Examples include "state", "province", "emirate", etc.
85     private String mAdminLabel;
86
87     private static final Map<String, Integer> ADMIN_LABELS;
88     private static final Map<String, Integer> ADMIN_ERROR_MESSAGES;
89
90     private static final FormOptions SHOW_ALL_FIELDS = new FormOptions.Builder().build();
91
92     // The appropriate label that should be applied to the zip code field of the current country.
93     private enum ZipLabel {
94         ZIP,
95         POSTAL
96     }
97
98     private ZipLabel mZipLabel;
99
100     static {
101         Map<String, Integer> adminLabelMap = new HashMap<String, Integer>(15);
102         adminLabelMap.put("area", R.string.i18n_area);
103         adminLabelMap.put("county", R.string.i18n_county_label);
104         adminLabelMap.put("department", R.string.i18n_department);
105         adminLabelMap.put("district", R.string.i18n_dependent_locality_label);
106         adminLabelMap.put("do_si", R.string.i18n_do_si);
107         adminLabelMap.put("emirate", R.string.i18n_emirate);
108         adminLabelMap.put("island", R.string.i18n_island);
109         adminLabelMap.put("parish", R.string.i18n_parish);
110         adminLabelMap.put("prefecture", R.string.i18n_prefecture);
111         adminLabelMap.put("province", R.string.i18n_province);
112         adminLabelMap.put("state", R.string.i18n_state_label);
113         ADMIN_LABELS = Collections.unmodifiableMap(adminLabelMap);
114
115         Map<String, Integer> adminErrorMap = new HashMap<String, Integer>(15);
116         adminErrorMap.put("area", R.string.invalid_area);
117         adminErrorMap.put("county", R.string.invalid_county_label);
118         adminErrorMap.put("department", R.string.invalid_department);
119         adminErrorMap.put("district", R.string.invalid_dependent_locality_label);
120         adminErrorMap.put("do_si", R.string.invalid_do_si);
121         adminErrorMap.put("emirate", R.string.invalid_emirate);
122         adminErrorMap.put("island", R.string.invalid_island);
123         adminErrorMap.put("parish", R.string.invalid_parish);
124         adminErrorMap.put("prefecture", R.string.invalid_prefecture);
125         adminErrorMap.put("province", R.string.invalid_province);
126         adminErrorMap.put("state", R.string.invalid_state_label);
127         ADMIN_ERROR_MESSAGES = Collections.unmodifiableMap(adminErrorMap);
128     }
129
130     // Need handler for callbacks to the UI thread
131     final Handler mHandler = new Handler();
132
133     final Runnable mUpdateMultipleFields = new Runnable() {
134         @Override
135         public void run() {
136             updateFields();
137         }
138     };
139
140     private class UpdateRunnable implements Runnable {
141         private AddressField myId;
142
143         public UpdateRunnable(AddressField id) {
144             myId = id;
145         }
146
147         @Override
148         public void run() {
149             updateInputWidget(myId);
150         }
151     }
152
153     private static class AddressSpinnerInfo {
154         private Spinner mView;
155
156         private AddressField mId;
157
158         private AddressField mParentId;
159
160         private ArrayAdapter<String> mAdapter;
161
162         private List<RegionData> mCurrentRegions;
163
164         public AddressSpinnerInfo(Spinner view, AddressField id, AddressField parentId) {
165             mView = view;
166             mId = id;
167             mParentId = parentId;
168         }
169
170         // Initializes the adapter that manages the Spinner.
171         public void initAdapter(Context context, int textViewId, int dropDownId) {
172             mAdapter = new ArrayAdapter<String>(context, textViewId);
173             mAdapter.setDropDownViewResource(dropDownId);
174         }
175
176         public void setSpinnerList(List<RegionData> list, String defaultKey) {
177             mCurrentRegions = list;
178             mAdapter.clear();
179             for (RegionData item : list) {
180                 mAdapter.add(item.getDisplayName());
181             }
182             mAdapter.sort(Collator.getInstance(Locale.getDefault()));
183             if (defaultKey.length() == 0) {
184                 mView.setSelection(0);
185             } else {
186                 int position = mAdapter.getPosition(defaultKey);
187                 mView.setSelection(position);
188             }
189         }
190
191         // Returns the region key of the currently selected region in the Spinner.
192         public String getRegionCode(int position) {
193             if (mAdapter.getCount() <= position) {
194                 return "";
195             }
196             String value = mAdapter.getItem(position);
197             return getRegionDataKeyForValue(value);
198         }
199
200         // Returns the region key for the region value.
201         public String getRegionDataKeyForValue(String value) {
202             for (RegionData data : mCurrentRegions) {
203                 if (data.getDisplayName().endsWith(value)) {
204                     return data.getKey();
205                 }
206             }
207             return "";
208         }
209     }
210
211     private ArrayList<AddressSpinnerInfo> mSpinners = new ArrayList<AddressSpinnerInfo>();
212
213     private void createView(ViewGroup rootView, AddressUIComponent field, String defaultKey,
214             boolean readOnly) {
215         @SuppressWarnings("deprecation")  // FILL_PARENT renamed MATCH_PARENT in API Level 8.
216         LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
217                 LayoutParams.WRAP_CONTENT);
218         String fieldText = field.getFieldName();
219
220         if (fieldText.length() > 0) {
221             TextView textView = (TextView) mInflater.inflate(R.layout.address_textview, rootView,
222                     false);
223             rootView.addView(textView, lp);
224             textView.setText(fieldText);
225         }
226         if (field.getUIType().equals(UIComponent.EDIT)) {
227             View view = mInflater.inflate(R.layout.address_edittext, rootView, false);
228             field.setView(view);
229             EditText editText = (EditText) view;
230             editText.setEnabled(!readOnly);
231             rootView.addView(editText, lp);
232         } else if (field.getUIType().equals(UIComponent.SPINNER)) {
233             View view = mInflater.inflate(R.layout.address_spinner, rootView, false);
234             field.setView(view);
235             Spinner spinner = (Spinner) view;
236             rootView.addView(spinner, lp);
237             AddressSpinnerInfo spinnerInfo =
238                 new AddressSpinnerInfo(spinner, field.getId(), field.getParentId());
239             spinnerInfo.initAdapter(mContext, android.R.layout.simple_spinner_item,
240                     android.R.layout.simple_spinner_dropdown_item);
241             spinner.setAdapter(spinnerInfo.mAdapter);
242             spinnerInfo.setSpinnerList(field.getCandidatesList(), defaultKey);
243
244             if (fieldText.length() > 0) {
245                 spinner.setPrompt(fieldText);
246             }
247             spinner.setOnItemSelectedListener(this);
248             mSpinners.add(spinnerInfo);
249         }
250     }
251
252     /**
253      *  Associates each field with its corresponding AddressUIComponent.
254      */
255     private void buildFieldWidgets() {
256         AddressData data = new AddressData.Builder().setCountry(mCurrentRegion).build();
257         LookupKey key = new LookupKey.Builder(LookupKey.KeyType.DATA).setAddressData(data).build();
258         AddressVerificationNodeData countryNode =
259             (new ClientData(mCacheData)).getDefaultData(key.toString());
260
261         // Set up AddressField.ADMIN_AREA
262         AddressUIComponent adminAreaUI = new AddressUIComponent(AddressField.ADMIN_AREA);
263         adminAreaUI.setFieldName(getAdminAreaFieldName(countryNode));
264         mInputWidgets.put(AddressField.ADMIN_AREA, adminAreaUI);
265
266         // Set up AddressField.LOCALITY
267         AddressUIComponent localityUI = new AddressUIComponent(AddressField.LOCALITY);
268         localityUI.setFieldName(mContext.getString(R.string.i18n_locality_label));
269         mInputWidgets.put(AddressField.LOCALITY, localityUI);
270
271         // Set up AddressField.DEPENDENT_LOCALITY
272         AddressUIComponent subLocalityUI = new AddressUIComponent(AddressField.DEPENDENT_LOCALITY);
273         subLocalityUI.setFieldName(mContext.getString(R.string.i18n_dependent_locality_label));
274         mInputWidgets.put(AddressField.DEPENDENT_LOCALITY, subLocalityUI);
275
276         // Set up AddressField.ADDRESS_LINE_1
277         AddressUIComponent addressLine1UI = new AddressUIComponent(AddressField.ADDRESS_LINE_1);
278         addressLine1UI.setFieldName(mContext.getString(R.string.i18n_address_line1_label));
279         mInputWidgets.put(AddressField.ADDRESS_LINE_1, addressLine1UI);
280
281         // Set up AddressField.ADDRESS_LINE_2
282         AddressUIComponent addressLine2UI = new AddressUIComponent(AddressField.ADDRESS_LINE_2);
283         addressLine2UI.setFieldName("");
284         mInputWidgets.put(AddressField.ADDRESS_LINE_2, addressLine2UI);
285
286         // Set up AddressField.ORGANIZATION
287         AddressUIComponent organizationUI = new AddressUIComponent(AddressField.ORGANIZATION);
288         organizationUI.setFieldName(mContext.getString(R.string.i18n_organization_label));
289         mInputWidgets.put(AddressField.ORGANIZATION, organizationUI);
290
291         // Set up AddressField.RECIPIENT
292         AddressUIComponent recipientUI = new AddressUIComponent(AddressField.RECIPIENT);
293         recipientUI.setFieldName(mContext.getString(R.string.i18n_recipient_label));
294         mInputWidgets.put(AddressField.RECIPIENT, recipientUI);
295
296         // Set up AddressField.POSTAL_CODE
297         AddressUIComponent postalCodeUI = new AddressUIComponent(AddressField.POSTAL_CODE);
298         postalCodeUI.setFieldName(getZipFieldName(countryNode));
299         mInputWidgets.put(AddressField.POSTAL_CODE, postalCodeUI);
300
301         // Set up AddressField.SORTING_CODE
302         AddressUIComponent sortingCodeUI = new AddressUIComponent(AddressField.SORTING_CODE);
303         sortingCodeUI.setFieldName("CEDEX");
304         mInputWidgets.put(AddressField.SORTING_CODE, sortingCodeUI);
305     }
306
307     private void initializeDropDowns() {
308         AddressUIComponent adminAreaUI = mInputWidgets.get(AddressField.ADMIN_AREA);
309         List<RegionData> adminAreaList = getRegionData(AddressField.COUNTRY);
310         adminAreaUI.initializeCandidatesList(adminAreaList);
311
312         AddressUIComponent localityUI = mInputWidgets.get(AddressField.LOCALITY);
313         List<RegionData> localityList = getRegionData(AddressField.ADMIN_AREA);
314         localityUI.initializeCandidatesList(localityList);
315     }
316
317     // Zip code is called postal code in some countries. This method returns the appropriate name
318     // for the given countryNode.
319     private String getZipFieldName(AddressVerificationNodeData countryNode) {
320         String zipName;
321         String zipType = countryNode.get(AddressDataKey.ZIP_NAME_TYPE);
322         if (zipType == null) {
323             mZipLabel = ZipLabel.POSTAL;
324             zipName = mContext.getString(R.string.i18n_postal_code_label);
325         } else {
326             mZipLabel = ZipLabel.ZIP;
327             zipName = mContext.getString(R.string.i18n_zip_code_label);
328         }
329         return zipName;
330     }
331
332     private String getAdminAreaFieldName(AddressVerificationNodeData countryNode) {
333         String adminLabelType = countryNode.get(AddressDataKey.STATE_NAME_TYPE);
334         mAdminLabel = adminLabelType;
335         Integer result = ADMIN_LABELS.get(adminLabelType);
336         if (result == null) {
337             // Fallback to province.
338             result = R.string.i18n_province;
339         }
340         return mContext.getString(result);
341     }
342
343     private void buildCountryListBox() {
344         // Set up AddressField.COUNTRY
345         AddressUIComponent countryUI = new AddressUIComponent(AddressField.COUNTRY);
346         countryUI.setFieldName(mContext.getString(R.string.i18n_country_label));
347         ArrayList<RegionData> countries = new ArrayList<RegionData>();
348         for (RegionData regionData : mFormController.getRegionData(new LookupKey.Builder(
349                 KeyType.DATA).build())) {
350             String regionKey = regionData.getKey();
351             // ZZ represents an unknown region code.
352             if (!regionKey.equals("ZZ")) {
353                 String localCountryName = getLocalCountryName(regionKey);
354                 RegionData country = new RegionData.Builder().setKey(regionKey).setName(
355                         localCountryName).build();
356                 countries.add(country);
357             }
358         }
359         countryUI.initializeCandidatesList(countries);
360         mInputWidgets.put(AddressField.COUNTRY, countryUI);
361     }
362
363     private String getLocalCountryName(String regionCode) {
364         return (new Locale("", regionCode)).getDisplayCountry(Locale.getDefault());
365     }
366
367     private AddressSpinnerInfo findSpinnerByView(View view) {
368         for (AddressSpinnerInfo spinnerInfo : mSpinners) {
369             if (spinnerInfo.mView == view) {
370                 return spinnerInfo;
371             }
372         }
373         return null;
374     }
375
376     private void updateFields() {
377         removePreviousViews();
378         buildFieldWidgets();
379         initializeDropDowns();
380         layoutAddressFields();
381     }
382
383     private void removePreviousViews() {
384         if (mRootView == null) {
385             return;
386         }
387         int childCount = mRootView.getChildCount();
388         if (mFormOptions.isHidden(AddressField.COUNTRY)) {
389             if (childCount > 0) {
390                 mRootView.removeAllViews();
391             }
392         } else if (childCount > 2) {
393             // Keep the TextView and Spinner for Country and remove everything else.
394             mRootView.removeViews(2, mRootView.getChildCount() - 2);
395         }
396     }
397
398     private void layoutAddressFields() {
399         for (AddressField field : mFormatInterpreter.getAddressFieldOrder(mScript,
400                 mCurrentRegion)) {
401             if (!mFormOptions.isHidden(field)) {
402               createView(mRootView, mInputWidgets.get(field), "", mFormOptions.isReadonly(field));
403             }
404         }
405     }
406
407     private void updateChildNodes(AdapterView<?> parent, int position) {
408         AddressSpinnerInfo spinnerInfo = findSpinnerByView(parent);
409         if (spinnerInfo == null) {
410             return;
411         }
412
413         // Find all the child spinners, if any, that depend on this one.
414         final AddressField myId = spinnerInfo.mId;
415         if (myId != AddressField.COUNTRY && myId != AddressField.ADMIN_AREA
416                 && myId != AddressField.LOCALITY) {
417             // Only a change in the three AddressFields above will trigger a change in other
418             // AddressFields. Therefore, for all other AddressFields, we return immediately.
419             return;
420         }
421
422         String regionCode = spinnerInfo.getRegionCode(position);
423         if (myId == AddressField.COUNTRY) {
424             updateWidgetOnCountryChange(regionCode);
425             return;
426         }
427
428         mFormController.requestDataForAddress(getAddressData(), new DataLoadListener() {
429             @Override
430             public void dataLoadingBegin(){
431             }
432
433             @Override
434             public void dataLoadingEnd() {
435                 Runnable updateChild = new UpdateRunnable(myId);
436                 mHandler.post(updateChild);
437             }
438         });
439     }
440
441     public void updateWidgetOnCountryChange(String regionCode) {
442         if (mCurrentRegion.equalsIgnoreCase(regionCode)) {
443             return;
444         }
445         mCurrentRegion = regionCode;
446         mFormController.setCurrentCountry(mCurrentRegion);
447         renderForm();
448     }
449
450     private void updateInputWidget(AddressField myId) {
451         for (AddressSpinnerInfo child : mSpinners) {
452             if (child.mParentId == myId) {
453                 List<RegionData> candidates = getRegionData(child.mParentId);
454                 child.setSpinnerList(candidates, "");
455             }
456         }
457     }
458
459     public void renderForm() {
460         setWidgetLocaleAndScript();
461         AddressData data = new AddressData.Builder().setCountry(mCurrentRegion)
462                 .setLanguageCode(mWidgetLocale).build();
463         mFormController.requestDataForAddress(data, new DataLoadListener() {
464             @Override
465             public void dataLoadingBegin() {
466                 mProgressDialog = ProgressDialog.show(mContext, "",
467                         mContext.getString(R.string.address_data_loading));
468                 Log.d(this.toString(), "Progress dialog started.");
469             }
470             @Override
471             public void dataLoadingEnd() {
472                 Log.d(this.toString(), "Data loading completed.");
473                 mProgressDialog.dismiss();
474                 Log.d(this.toString(), "Progress dialog stopped.");
475                 mHandler.post(mUpdateMultipleFields);
476             }
477         });
478     }
479
480     private void setWidgetLocaleAndScript() {
481         mWidgetLocale = Util.getWidgetCompatibleLanguageCode(Locale.getDefault(), mCurrentRegion);
482         mFormController.setLanguageCode(mWidgetLocale);
483         mScript = Util.isExplicitLatinScript(mWidgetLocale)
484                 ? ScriptType.LATIN
485                 : ScriptType.LOCAL;
486     }
487
488     private List<RegionData> getRegionData(AddressField parentField) {
489         AddressData address = getAddressData();
490
491         // Removes language code from address if it is default. This address is used to build
492         // lookup key, which neglects default language. For example, instead of "data/US--en/CA",
493         // the right lookup key is "data/US/CA".
494         if (mFormController.isDefaultLanguage(address.getLanguageCode())) {
495             address = new AddressData.Builder(address).setLanguageCode(null).build();
496         }
497
498         LookupKey parentKey = mFormController.getDataKeyFor(address).getKeyForUpperLevelField(
499                 parentField);
500         List<RegionData> candidates;
501         // Can't build a key with parent field, quit.
502         if (parentKey == null) {
503             Log.w(this.toString(), "Can't build key with parent field " + parentField + ". One of"
504                     + " the ancestor fields might be empty");
505
506             // Removes candidates that exist from previous settings. For example, data/US has a
507             // list of candidates AB, BC, CA, etc, that list should be cleaned up when user updates
508             // the address by changing country to Channel Islands.
509             candidates = new ArrayList<RegionData>(1);
510         } else {
511             candidates = mFormController.getRegionData(parentKey);
512         }
513         return candidates;
514     }
515
516     /**
517      * Creates an AddressWidget to be attached to rootView for the specific context.
518      */
519     public AddressWidget(Context context, ViewGroup rootView, FormOptions formOptions,
520             ClientCacheManager cacheManager) {
521         mCurrentRegion =
522             ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE))
523                     .getSimCountryIso().toUpperCase(Locale.US);
524         if (mCurrentRegion.length() == 0) {
525             mCurrentRegion = "US";
526         }
527         init(context, rootView, formOptions, cacheManager);
528         renderForm();
529     }
530
531     /**
532      * Creates an AddressWidget to be attached to rootView for the specific context, and fill out
533      * the address form with savedAddress.
534      */
535     public AddressWidget(Context context, ViewGroup rootView, FormOptions formOptions,
536             ClientCacheManager cacheManager, AddressData savedAddress) {
537         mCurrentRegion = savedAddress.getPostalCountry();
538         // Postal country must be 2 letter country code. Otherwise default to US.
539         if (mCurrentRegion == null || mCurrentRegion.length() != 2) {
540             mCurrentRegion = "US";
541         }
542         init(context, rootView, formOptions, cacheManager);
543         renderFormWithSavedAddress(savedAddress);
544     }
545
546     public void renderFormWithSavedAddress(AddressData savedAddress) {
547         setWidgetLocaleAndScript();
548         removePreviousViews();
549         buildFieldWidgets();
550         layoutAddressFields();
551         initializeFieldsWithAddress(savedAddress);
552     }
553
554     private void initializeFieldsWithAddress(AddressData savedAddress) {
555         for (AddressField field : mFormatInterpreter.getAddressFieldOrder(mScript,
556                 mCurrentRegion)) {
557             String value = savedAddress.getFieldValue(field);
558             if (value == null) {
559                 value = "";
560             }
561             AddressUIComponent uiComponent = mInputWidgets.get(field);
562             EditText view = (EditText) uiComponent.getView();
563             if (view != null) {
564                view.setText(value);
565             }
566         }
567     }
568
569     private void init(Context context, ViewGroup rootView, FormOptions formOptions,
570             ClientCacheManager cacheManager) {
571         mContext = context;
572         mRootView = rootView;
573         mFormOptions = formOptions;
574         mCacheData = new CacheData(cacheManager);
575         mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
576         mFormController =
577             new FormController(new ClientData(mCacheData),
578                                mWidgetLocale, mCurrentRegion);
579         mFormatInterpreter = new FormatInterpreter(mFormOptions);
580         mVerifier = new StandardAddressVerifier(
581                 new FieldVerifier(new ClientData(mCacheData)));
582         if (!formOptions.isHidden(AddressField.COUNTRY)) {
583             buildCountryListBox();
584             createView(mRootView, mInputWidgets.get(AddressField.COUNTRY),
585                     getLocalCountryName(mCurrentRegion),
586                     formOptions.isReadonly(AddressField.COUNTRY));
587         }
588     }
589
590     /**
591      * Sets address data server URL. Input URL cannot be null.
592      *
593      * @param url The service URL.
594      */
595     public void setUrl(String url) {
596         mCacheData.setUrl(url);
597     }
598
599     /**
600      * Gets user input address in AddressData format.
601      */
602     public AddressData getAddressData() {
603         AddressData.Builder builder = new AddressData.Builder();
604         builder.setCountry(mCurrentRegion);
605         for (AddressField field : mFormatInterpreter.getAddressFieldOrder(mScript,
606                 mCurrentRegion)) {
607             AddressUIComponent addressUIComponent = mInputWidgets.get(field);
608             if (addressUIComponent != null) {
609                 String value = addressUIComponent.getValue();
610                 if (addressUIComponent.getUIType() == UIComponent.SPINNER) {
611                      // For drop-downs, return the key of the region selected instead of the value.
612                      View view = getViewForField(field);
613                      AddressSpinnerInfo spinnerInfo = findSpinnerByView(view);
614                      if (spinnerInfo != null) {
615                          value = spinnerInfo.getRegionDataKeyForValue(value);
616                      }
617                 }
618                 builder.set(field, value);
619             }
620         }
621         builder.setLanguageCode(mWidgetLocale);
622         return builder.build();
623     }
624
625     /**
626      * Gets the formatted address.
627      *
628      * This method does not validate addresses. Also, it will "normalize" the result strings by
629      * removing redundant spaces and empty lines.
630      *
631      * @return the formatted address
632      */
633     public List<String> getEnvelopeAddress() {
634         return mFormatInterpreter.getEnvelopeAddress(getAddressData());
635     }
636
637     /**
638      * Gets the formatted address based on the AddressData passed in.
639      */
640     public List<String> getEnvelopeAddress(AddressData address) {
641         return mFormatInterpreter.getEnvelopeAddress(address);
642     }
643
644     /**
645      * Gets the formatted address based on the AddressData passed in with none of the relevant
646      * fields hidden.
647      */
648     public static List<String> getFullEnvelopeAddress(AddressData address) {
649         return new FormatInterpreter(SHOW_ALL_FIELDS).getEnvelopeAddress(address);
650     }
651
652     /**
653      * Get problems found in the address data entered by the user.
654      */
655     public AddressProblems getAddressProblems() {
656         AddressProblems problems = new AddressProblems();
657         AddressData addressData = getAddressData();
658         mVerifier.verify(addressData, problems);
659         return problems;
660     }
661
662     /**
663      * Displays an appropriate error message when the AddressField contains an invalid entry.
664      *
665      * @return the View object representing the AddressField.
666      */
667     public View displayErrorMessageForInvalidEntryIn(AddressField field) {
668         Log.d(this.toString(), "Display error message for the field: " + field.toString());
669         AddressUIComponent addressUIComponent = mInputWidgets.get(field);
670         if (addressUIComponent != null && addressUIComponent.getUIType() == UIComponent.EDIT) {
671             int errorMessageId = getErrorMessageIdForInvalidEntryIn(field);
672             EditText view = (EditText) addressUIComponent.getView();
673             view.setError(mContext.getString(errorMessageId));
674             return view;
675         }
676         return null;
677     }
678
679     private int getErrorMessageIdForInvalidEntryIn(AddressField field) {
680         switch (field) {
681             case ADMIN_AREA:
682                 return ADMIN_ERROR_MESSAGES.get(mAdminLabel);
683             case LOCALITY:
684                 return R.string.invalid_locality_label;
685             case DEPENDENT_LOCALITY:
686                 return R.string.invalid_dependent_locality_label;
687             case POSTAL_CODE:
688                 return (mZipLabel == ZipLabel.POSTAL
689                         ? R.string.invalid_postal_code_label
690                         : R.string.invalid_zip_code_label);
691             default:
692                 return R.string.invalid_entry;
693         }
694     }
695
696     /**
697      * Clears all error messages in the UI.
698      */
699     public void clearErrorMessage() {
700         for (AddressField field : mFormatInterpreter.getAddressFieldOrder(mScript,
701                 mCurrentRegion)) {
702             AddressUIComponent addressUIComponent = mInputWidgets.get(field);
703
704             if (addressUIComponent != null && addressUIComponent.getUIType() == UIComponent.EDIT) {
705                 EditText view = (EditText) addressUIComponent.getView();
706                 if (view != null) {
707                     view.setError(null);
708                 }
709             }
710         }
711     }
712
713     public View getViewForField(AddressField field) {
714       AddressUIComponent component = mInputWidgets.get(field);
715       if (component == null) {
716         return null;
717       }
718       return component.getView();
719     }
720
721     @Override
722     public void onNothingSelected(AdapterView<?> arg0) {
723     }
724
725     @Override
726     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
727         updateChildNodes(parent, position);
728     }
729 }