Revert "Stop showing long names for local timezones when it is ambiguous"

This reverts commit cc4d2c22d4c444e877830b77a1e4246ca292e662.

Change-Id: I04773f3dd0f2d5756e6901185fe24b5310c5041e
diff --git a/src/com/android/settings/DateTimeSettings.java b/src/com/android/settings/DateTimeSettings.java
index c7d3810..6f41fd5 100644
--- a/src/com/android/settings/DateTimeSettings.java
+++ b/src/com/android/settings/DateTimeSettings.java
@@ -29,6 +29,7 @@
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
 import android.os.Bundle;
+import android.preference.ListPreference;
 import android.preference.Preference;
 import android.preference.PreferenceScreen;
 import android.preference.SwitchPreference;
@@ -163,7 +164,7 @@
         Date dummyDate = mDummyDate.getTime();
         mDatePref.setSummary(DateFormat.getLongDateFormat(context).format(now.getTime()));
         mTimePref.setSummary(DateFormat.getTimeFormat(getActivity()).format(now.getTime()));
-        mTimeZone.setSummary(getTimeZoneText(now.getTimeZone(), now.getTime()));
+        mTimeZone.setSummary(getTimeZoneText(now.getTimeZone(), true));
         mTime24Pref.setSummary(DateFormat.getTimeFormat(getActivity()).format(dummyDate));
     }
 
@@ -343,37 +344,32 @@
         }
     }
 
-    private static String getTimeZoneText(TimeZone tz, Date now) {
-        Locale locale = Locale.getDefault();
-        String gmtString = getGmtOffsetString(locale, tz);
-        String zoneNameString = getZoneLongName(locale, tz, now);
-        if (zoneNameString == null) {
-            return gmtString;
-        }
+    public static String getTimeZoneText(TimeZone tz, boolean includeName) {
+        Date now = new Date();
 
-        // We don't use punctuation here to avoid having to worry about localizing that too!
-        return gmtString + " " + zoneNameString;
-    }
-
-    public static String getZoneLongName(Locale locale, TimeZone tz, Date now) {
-        boolean daylight = tz.inDaylightTime(now);
-        // This returns a name if it can, or will fall back to GMT+0X:00 format.
-        return tz.getDisplayName(daylight, TimeZone.LONG, locale);
-    }
-
-    public static String getGmtOffsetString(Locale locale, TimeZone tz) {
         // Use SimpleDateFormat to format the GMT+00:00 string.
         SimpleDateFormat gmtFormatter = new SimpleDateFormat("ZZZZ");
         gmtFormatter.setTimeZone(tz);
-        Date now = new Date();
         String gmtString = gmtFormatter.format(now);
 
         // Ensure that the "GMT+" stays with the "00:00" even if the digits are RTL.
         BidiFormatter bidiFormatter = BidiFormatter.getInstance();
-        boolean isRtl = TextUtils.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL;
+        Locale l = Locale.getDefault();
+        boolean isRtl = TextUtils.getLayoutDirectionFromLocale(l) == View.LAYOUT_DIRECTION_RTL;
         gmtString = bidiFormatter.unicodeWrap(gmtString,
                 isRtl ? TextDirectionHeuristics.RTL : TextDirectionHeuristics.LTR);
-        return gmtString;
+
+        if (!includeName) {
+            return gmtString;
+        }
+
+        // Optionally append the time zone name.
+        SimpleDateFormat zoneNameFormatter = new SimpleDateFormat("zzzz");
+        zoneNameFormatter.setTimeZone(tz);
+        String zoneNameString = zoneNameFormatter.format(now);
+
+        // We don't use punctuation here to avoid having to worry about localizing that too!
+        return gmtString + " " + zoneNameString;
     }
 
     private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
diff --git a/src/com/android/settings/ZonePicker.java b/src/com/android/settings/ZonePicker.java
index fec84b5..0a9173f 100644
--- a/src/com/android/settings/ZonePicker.java
+++ b/src/com/android/settings/ZonePicker.java
@@ -34,17 +34,19 @@
 
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
+import java.util.Calendar;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 import java.util.TimeZone;
-import java.util.TreeSet;
+import libcore.icu.ICU;
 import libcore.icu.TimeZoneNames;
 
 /**
@@ -102,7 +104,8 @@
 
         final String sortKey = (sortedByName ? KEY_DISPLAYNAME : KEY_OFFSET);
         final MyComparator comparator = new MyComparator(sortKey);
-        final List<Map<String, Object>> sortedList = getZones(context);
+        ZoneGetter zoneGetter = new ZoneGetter();
+        final List<HashMap<String, Object>> sortedList = zoneGetter.getZones(context);
         Collections.sort(sortedList, comparator);
         final SimpleAdapter adapter = new SimpleAdapter(context,
                 sortedList,
@@ -221,132 +224,74 @@
         }
     }
 
-    private static List<Map<String, Object>> getZones(Context context) {
-        final Locale locale = Locale.getDefault();
-        final Date now = new Date();
+    static class ZoneGetter {
+        private final List<HashMap<String, Object>> mZones =
+                new ArrayList<HashMap<String, Object>>();
+        private final HashSet<String> mLocalZones = new HashSet<String>();
+        private final Date mNow = Calendar.getInstance().getTime();
+        private final SimpleDateFormat mZoneNameFormatter = new SimpleDateFormat("zzzz");
 
-        // The display name chosen for each zone entry depends on whether the zone is one associated
-        // with the country of the user's chosen locale. For "local" zones we prefer the "long name"
-        // (e.g. "Europe/London" -> "British Summer Time" for people in the UK). For "non-local"
-        // zones we prefer the exemplar location (e.g. "Europe/London" -> "London" for English
-        // speakers from outside the UK). This heuristic is based on the fact that people are
-        // typically familiar with their local timezones and exemplar locations don't always match
-        // modern-day expectations for people living in the country covered. Large countries like
-        // China that mostly use a single timezone (olson id: "Asia/Shanghai") may not live near
-        // "Shanghai" and prefer the long name over the exemplar location. The only time we don't
-        // follow this policy for local zones is when Android supplies multiple olson IDs to choose
-        // from and the use of a zone's long name leads to ambiguity. For example, at the time of
-        // writing Android lists 5 olson ids for Australia which collapse to 2 different zone names
-        // in winter but 4 different zone names in summer. The ambiguity leads to the users
-        // selecting the wrong olson ids.
-
-        // Get the list of olson ids to display to the user.
-        List<String> olsonIdsToDisplay = readTimezonesToDisplay(context);
-
-        // Create a lookup of local zone IDs.
-        Set<String> localZoneIds = new TreeSet<String>();
-        for (String olsonId : TimeZoneNames.forLocale(locale)) {
-            localZoneIds.add(olsonId);
-        }
-
-        // Work out whether the long names for the local entries that we would show by default would
-        // be ambiguous.
-        Set<String> localZoneNames = new TreeSet<String>();
-        boolean localLongNamesAreAmbiguous = false;
-        for (String olsonId : olsonIdsToDisplay) {
-            if (localZoneIds.contains(olsonId)) {
-                TimeZone tz = TimeZone.getTimeZone(olsonId);
-                String zoneLongName = DateTimeSettings.getZoneLongName(locale, tz, now);
-                boolean longNameIsUnique = localZoneNames.add(zoneLongName);
-                if (!longNameIsUnique) {
-                    localLongNamesAreAmbiguous = true;
-                    break;
+        private List<HashMap<String, Object>> getZones(Context context) {
+            for (String olsonId : TimeZoneNames.forLocale(Locale.getDefault())) {
+                mLocalZones.add(olsonId);
+            }
+            try {
+                XmlResourceParser xrp = context.getResources().getXml(R.xml.timezones);
+                while (xrp.next() != XmlResourceParser.START_TAG) {
+                    continue;
                 }
-            }
-        }
-
-        // Generate the list of zone entries to return.
-        List<Map<String, Object>> zones = new ArrayList<Map<String, Object>>();
-        for (String olsonId : olsonIdsToDisplay) {
-            final TimeZone tz = TimeZone.getTimeZone(olsonId);
-            // Exemplar location display is the default. The only time we intend to display the long
-            // name is when the olsonId is local AND long names are not ambiguous.
-            boolean isLocalZoneId = localZoneIds.contains(olsonId);
-            boolean preferLongName = isLocalZoneId && !localLongNamesAreAmbiguous;
-            String displayName = getZoneDisplayName(locale, tz, now, preferLongName);
-
-            String gmtOffsetString = DateTimeSettings.getGmtOffsetString(locale, tz);
-            int offsetMillis = tz.getOffset(now.getTime());
-            Map<String, Object> displayEntry =
-                    createDisplayEntry(tz, gmtOffsetString, displayName, offsetMillis);
-            zones.add(displayEntry);
-        }
-        return zones;
-    }
-
-    private static Map<String, Object> createDisplayEntry(
-            TimeZone tz, String gmtOffsetString, String displayName, int offsetMillis) {
-        Map<String, Object> map = new HashMap<String, Object>();
-        map.put(KEY_ID, tz.getID());
-        map.put(KEY_DISPLAYNAME, displayName);
-        map.put(KEY_GMT, gmtOffsetString);
-        map.put(KEY_OFFSET, offsetMillis);
-        return map;
-    }
-
-    private static List<String> readTimezonesToDisplay(Context context) {
-        List<String> olsonIds = new ArrayList<String>();
-        try (XmlResourceParser xrp = context.getResources().getXml(R.xml.timezones)) {
-            while (xrp.next() != XmlResourceParser.START_TAG) {
-                continue;
-            }
-            xrp.next();
-            while (xrp.getEventType() != XmlResourceParser.END_TAG) {
-                while (xrp.getEventType() != XmlResourceParser.START_TAG) {
-                    if (xrp.getEventType() == XmlResourceParser.END_DOCUMENT) {
-                        return olsonIds;
+                xrp.next();
+                while (xrp.getEventType() != XmlResourceParser.END_TAG) {
+                    while (xrp.getEventType() != XmlResourceParser.START_TAG) {
+                        if (xrp.getEventType() == XmlResourceParser.END_DOCUMENT) {
+                            return mZones;
+                        }
+                        xrp.next();
+                    }
+                    if (xrp.getName().equals(XMLTAG_TIMEZONE)) {
+                        String olsonId = xrp.getAttributeValue(0);
+                        addTimeZone(olsonId);
+                    }
+                    while (xrp.getEventType() != XmlResourceParser.END_TAG) {
+                        xrp.next();
                     }
                     xrp.next();
                 }
-                if (xrp.getName().equals(XMLTAG_TIMEZONE)) {
-                    String olsonId = xrp.getAttributeValue(0);
-                    olsonIds.add(olsonId);
-                }
-                while (xrp.getEventType() != XmlResourceParser.END_TAG) {
-                    xrp.next();
-                }
-                xrp.next();
+                xrp.close();
+            } catch (XmlPullParserException xppe) {
+                Log.e(TAG, "Ill-formatted timezones.xml file");
+            } catch (java.io.IOException ioe) {
+                Log.e(TAG, "Unable to read timezones.xml file");
             }
-        } catch (XmlPullParserException xppe) {
-            Log.e(TAG, "Ill-formatted timezones.xml file");
-        } catch (java.io.IOException ioe) {
-            Log.e(TAG, "Unable to read timezones.xml file");
+            return mZones;
         }
-        return olsonIds;
-    }
 
-    /**
-     * Returns a name for the specific zone. If {@code preferLongName} is {@code true} then the
-     * long display name for the timezone will be used, otherwise the exemplar location will be
-     * preferred.
-     */
-    private static String getZoneDisplayName(Locale locale, TimeZone tz, Date now,
-            boolean preferLongName) {
-        String zoneNameString;
-        if (preferLongName) {
-            zoneNameString = DateTimeSettings.getZoneLongName(locale, tz, now);
-        } else {
-            zoneNameString = getZoneExemplarLocation(locale, tz);
-            if (zoneNameString == null || zoneNameString.isEmpty()) {
-                // getZoneExemplarLocation can return null.
-                zoneNameString = DateTimeSettings.getZoneLongName(locale, tz, now);
+        private void addTimeZone(String olsonId) {
+            // We always need the "GMT-07:00" string.
+            final TimeZone tz = TimeZone.getTimeZone(olsonId);
+
+            // For the display name, we treat time zones within the country differently
+            // from other countries' time zones. So in en_US you'd get "Pacific Daylight Time"
+            // but in de_DE you'd get "Los Angeles" for the same time zone.
+            String displayName;
+            if (mLocalZones.contains(olsonId)) {
+                // Within a country, we just use the local name for the time zone.
+                mZoneNameFormatter.setTimeZone(tz);
+                displayName = mZoneNameFormatter.format(mNow);
+            } else {
+                // For other countries' time zones, we use the exemplar location.
+                final String localeName = Locale.getDefault().toString();
+                displayName = TimeZoneNames.getExemplarLocation(localeName, olsonId);
             }
-        }
-        return zoneNameString;
-    }
 
-    private static String getZoneExemplarLocation(Locale locale, TimeZone tz) {
-        return TimeZoneNames.getExemplarLocation(locale.toString(), tz.getID());
+            final HashMap<String, Object> map = new HashMap<String, Object>();
+            map.put(KEY_ID, olsonId);
+            map.put(KEY_DISPLAYNAME, displayName);
+            map.put(KEY_GMT, DateTimeSettings.getTimeZoneText(tz, false));
+            map.put(KEY_OFFSET, tz.getOffset(mNow.getTime()));
+
+            mZones.add(map);
+        }
     }
 
     @Override
@@ -368,7 +313,7 @@
         }
     }
 
-    private static class MyComparator implements Comparator<Map<?, ?>> {
+    private static class MyComparator implements Comparator<HashMap<?, ?>> {
         private String mSortingKey;
 
         public MyComparator(String sortingKey) {
@@ -379,7 +324,7 @@
             mSortingKey = sortingKey;
         }
 
-        public int compare(Map<?, ?> map1, Map<?, ?> map2) {
+        public int compare(HashMap<?, ?> map1, HashMap<?, ?> map2) {
             Object value1 = map1.get(mSortingKey);
             Object value2 = map2.get(mSortingKey);