14c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang/* 24c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * Copyright (C) 2010 The Android Open Source Project 34c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * 44c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * Licensed under the Apache License, Version 2.0 (the "License"); 54c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * you may not use this file except in compliance with the License. 64c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * You may obtain a copy of the License at 74c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * 84c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * http://www.apache.org/licenses/LICENSE-2.0 94c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * 104c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * Unless required by applicable law or agreed to in writing, software 114c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * distributed under the License is distributed on an "AS IS" BASIS, 124c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 134c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * See the License for the specific language governing permissions and 144c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * limitations under the License. 154c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang */ 164c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 174c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangpackage com.android.calendar; 184c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 194c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangimport android.content.Context; 204c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangimport android.content.SharedPreferences; 214c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangimport android.content.res.Resources; 2266a3c9ee58ae73a3a1ad3bdec79aa1566582465cRoboErikimport android.text.TextUtils; 234c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangimport android.text.format.DateUtils; 24e274e47b7073a350dec4924ac7c09ba4211cb06dErikimport android.util.Log; 254c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangimport android.widget.ArrayAdapter; 264c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 27559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chanimport com.android.calendar.TimezoneAdapter.TimezoneRow; 28559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan 294c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangimport java.util.ArrayList; 304c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangimport java.util.Arrays; 314c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangimport java.util.Collections; 32559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chanimport java.util.Date; 332adb0a5ee7e9ff4984e5e3198bc9f44710158418Erikimport java.util.LinkedHashMap; 344c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangimport java.util.LinkedHashSet; 354c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangimport java.util.List; 36559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chanimport java.util.Locale; 374c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangimport java.util.TimeZone; 384c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 394c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang/** 404c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * {@link TimezoneAdapter} is a custom adapter implementation that allows you to 414c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * easily display a list of timezones for users to choose from. In addition, it 424c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * provides a two-stage behavior that initially only loads a small set of 434c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * timezones (one user-provided, the device timezone, and two recent timezones), 444c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * which can later be expanded into the full list with a call to 454c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * {@link #showAllTimezones()}. 464c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang */ 474c8871bf5dee3b3586b375aee98effde31b781a8Mason Tangpublic class TimezoneAdapter extends ArrayAdapter<TimezoneRow> { 48e274e47b7073a350dec4924ac7c09ba4211cb06dErik private static final String TAG = "TimezoneAdapter"; 494c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 504c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** 514c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * {@link TimezoneRow} is an immutable class for representing a timezone. We 524c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * don't use {@link TimeZone} directly, in order to provide a reasonable 534c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * implementation of toString() and to control which display names we use. 544c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang */ 55559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan public class TimezoneRow implements Comparable<TimezoneRow> { 564c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 574c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** The ID of this timezone, e.g. "America/Los_Angeles" */ 584c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang public final String mId; 594c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 604c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** The display name of this timezone, e.g. "Pacific Time" */ 61559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan private final String mDisplayName; 624c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 634c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** The actual offset of this timezone from GMT in milliseconds */ 64559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan private final int mOffset; 65559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan 66559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan /** Whether the TZ observes daylight saving time */ 67559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan private final boolean mUseDaylightTime; 684c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 694c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** 704c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * A one-line representation of this timezone, including both GMT offset 714c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * and display name, e.g. "(GMT-7:00) Pacific Time" 724c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang */ 73559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan private String mGmtDisplayName; 744c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 754c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang public TimezoneRow(String id, String displayName) { 764c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang mId = id; 774c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang mDisplayName = displayName; 784c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang TimeZone tz = TimeZone.getTimeZone(id); 79559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan mUseDaylightTime = tz.useDaylightTime(); 80559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan mOffset = tz.getOffset(TimezoneAdapter.this.mTime); 81559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan } 82559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan 83559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan @Override 84559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan public String toString() { 85559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan if (mGmtDisplayName == null) { 86559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan buildGmtDisplayName(); 87559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan } 88559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan 89559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan return mGmtDisplayName; 90559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan } 91559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan 92559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan /** 93559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan * 94559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan */ 95559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan public void buildGmtDisplayName() { 96559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan if (mGmtDisplayName != null) { 97559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan return; 98559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan } 994c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 100559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan int p = Math.abs(mOffset); 1014c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang StringBuilder name = new StringBuilder(); 1024c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang name.append("GMT"); 1034c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 104559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan if (mOffset < 0) { 1054c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang name.append('-'); 1064c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } else { 1074c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang name.append('+'); 1084c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1094c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 1104c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang name.append(p / (DateUtils.HOUR_IN_MILLIS)); 1114c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang name.append(':'); 1124c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 1134c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang int min = p / 60000; 1144c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang min %= 60; 1154c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 1164c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (min < 10) { 1174c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang name.append('0'); 1184c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1194c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang name.append(min); 1204c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang name.insert(0, "("); 1214c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang name.append(") "); 122559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan name.append(mDisplayName); 123559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan if (mUseDaylightTime) { 124559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan name.append(" \u2600"); // Sun symbol 125559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan } 1264c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang mGmtDisplayName = name.toString(); 1274c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1284c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 1294c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang @Override 1304c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang public int hashCode() { 1314c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang final int prime = 31; 1324c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang int result = 1; 1334c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang result = prime * result + ((mDisplayName == null) ? 0 : mDisplayName.hashCode()); 1344c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang result = prime * result + ((mId == null) ? 0 : mId.hashCode()); 1354c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang result = prime * result + mOffset; 1364c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return result; 1374c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1384c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 1394c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang @Override 1404c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang public boolean equals(Object obj) { 1414c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (this == obj) { 1424c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return true; 1434c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1444c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (obj == null) { 1454c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return false; 1464c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1474c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (getClass() != obj.getClass()) { 1484c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return false; 1494c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1504c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang TimezoneRow other = (TimezoneRow) obj; 1514c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (mDisplayName == null) { 1524c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (other.mDisplayName != null) { 1534c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return false; 1544c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1554c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } else if (!mDisplayName.equals(other.mDisplayName)) { 1564c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return false; 1574c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1584c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (mId == null) { 1594c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (other.mId != null) { 1604c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return false; 1614c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1624c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } else if (!mId.equals(other.mId)) { 1634c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return false; 1644c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1654c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (mOffset != other.mOffset) { 1664c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return false; 1674c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1684c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return true; 1694c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1704c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 1714c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang @Override 1724c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang public int compareTo(TimezoneRow another) { 1734c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (mOffset == another.mOffset) { 1744c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return 0; 1754c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } else { 1764c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return mOffset < another.mOffset ? -1 : 1; 1774c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1784c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1794c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 1804c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 1814c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 1824c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang private static final String KEY_RECENT_TIMEZONES = "preferences_recent_timezones"; 1834c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 1844c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** The delimiter we use when serializing recent timezones to shared preferences */ 1854c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang private static final String RECENT_TIMEZONES_DELIMITER = ","; 1864c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 1874c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** The maximum number of recent timezones to save */ 1884c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang private static final int MAX_RECENT_TIMEZONES = 3; 1894c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 1904c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** 1914c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * Static cache of all known timezones, mapped to their string IDs. This is 192a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik * lazily-loaded on the first call to {@link #loadFromResources(Resources)}. 193a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik * Loading is called in a synchronized block during initialization of this 194a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik * class and is based off the resources available to the calling context. 195a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik * This class should not be used outside of the initial context. 196a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik * LinkedHashMap is used to preserve ordering. 1974c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang */ 198a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik private static LinkedHashMap<String, TimezoneRow> sTimezones; 1994c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 2004c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang private Context mContext; 2014c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 2024c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang private String mCurrentTimezone; 2034c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 2044c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang private boolean mShowingAll = false; 2054c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 206559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan private long mTime; 207559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan 208559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan private Date mDateTime; 209559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan 2104c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** 2114c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * Constructs a timezone adapter that contains an initial set of entries 2124c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * including the current timezone, the device timezone, and two recently 2134c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * used timezones. 2144c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * 2154c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * @param context 2164c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * @param currentTimezone 217559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan * @param time - needed to determine whether DLS is in effect 2184c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang */ 219559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan public TimezoneAdapter(Context context, String currentTimezone, long time) { 2204c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang super(context, android.R.layout.simple_spinner_dropdown_item, android.R.id.text1); 2214c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang mContext = context; 2224c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang mCurrentTimezone = currentTimezone; 223559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan mTime = time; 224559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan mDateTime = new Date(mTime); 2254c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang mShowingAll = false; 2264c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang showInitialTimezones(); 2274c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 2284c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 2294c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** 2304c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * Given the ID of a timezone, returns the position of the timezone in this 2314c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * adapter, or -1 if not found. 2324c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * 2334c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * @param id the ID of the timezone to find 2344c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * @return the row position of the timezone, or -1 if not found 2354c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang */ 2364c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang public int getRowById(String id) { 2374c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang TimezoneRow timezone = sTimezones.get(id); 2384c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (timezone == null) { 2394c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return -1; 2404c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } else { 2414c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang return getPosition(timezone); 2424c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 2434c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 2444c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 2454c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** 2464c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * Populates the adapter with an initial list of timezones (one 2474c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * user-provided, the device timezone, and two recent timezones), which can 2484c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * later be expanded into the full list with a call to 2494c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * {@link #showAllTimezones()}. 2504c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * 2514c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * @param currentTimezone 2524c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang */ 2534c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang public void showInitialTimezones() { 2544c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 2554c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang // we use a linked hash set to guarantee only unique IDs are added, and 2564c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang // also to maintain the insertion order of the timezones 2574c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang LinkedHashSet<String> ids = new LinkedHashSet<String>(); 2584c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 2594c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang // add in the provided (event) timezone 26066a3c9ee58ae73a3a1ad3bdec79aa1566582465cRoboErik if (!TextUtils.isEmpty(mCurrentTimezone)) { 26166a3c9ee58ae73a3a1ad3bdec79aa1566582465cRoboErik ids.add(mCurrentTimezone); 26266a3c9ee58ae73a3a1ad3bdec79aa1566582465cRoboErik } 2634c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 2644c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang // add in the device timezone if it is different 2654c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang ids.add(TimeZone.getDefault().getID()); 2664c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 2674c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang // add in recent timezone selections 2684b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa SharedPreferences prefs = GeneralPreferences.getSharedPreferences(mContext); 2694c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang String recentsString = prefs.getString(KEY_RECENT_TIMEZONES, null); 2704c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (recentsString != null) { 2714c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang String[] recents = recentsString.split(RECENT_TIMEZONES_DELIMITER); 2724c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang for (String recent : recents) { 27366a3c9ee58ae73a3a1ad3bdec79aa1566582465cRoboErik if (!TextUtils.isEmpty(recent)) { 27466a3c9ee58ae73a3a1ad3bdec79aa1566582465cRoboErik ids.add(recent); 27566a3c9ee58ae73a3a1ad3bdec79aa1566582465cRoboErik } 2764c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 2774c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 2784c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 2794c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang clear(); 280a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik 281a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik synchronized (TimezoneAdapter.class) { 282a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik loadFromResources(mContext.getResources()); 283a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik TimeZone gmt = TimeZone.getTimeZone("GMT"); 284a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik for (String id : ids) { 285a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik if (!sTimezones.containsKey(id)) { 286a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik // a timezone we don't know about, so try to add it... 287a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik TimeZone newTz = TimeZone.getTimeZone(id); 288a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik // since TimeZone.getTimeZone actually returns a clone of GMT 289a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik // when it doesn't recognize the ID, this appears to be the only 290a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik // reliable way to check to see if the ID is a valid timezone 291a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik if (!newTz.equals(gmt)) { 292559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan final String tzDisplayName = newTz.getDisplayName( 293559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan newTz.inDaylightTime(mDateTime), TimeZone.LONG, 294559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan Locale.getDefault()); 295559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan sTimezones.put(id, new TimezoneRow(id, tzDisplayName)); 296a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik } else { 297a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik continue; 298a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik } 2994c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 300a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik add(sTimezones.get(id)); 3014c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 3024c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 3034c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang mShowingAll = false; 3044c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 3054c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 3064c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** 3074c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * Populates this adapter with all known timezones. 3084c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang */ 3094c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang public void showAllTimezones() { 3104c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang List<TimezoneRow> timezones = new ArrayList<TimezoneRow>(sTimezones.values()); 3114c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang Collections.sort(timezones); 3124c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang clear(); 3134c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang for (TimezoneRow timezone : timezones) { 314559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan timezone.buildGmtDisplayName(); 3154c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang add(timezone); 3164c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 3174c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang mShowingAll = true; 3184c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 3194c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 3204c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** 3214c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * Sets the current timezone. If the adapter is currently displaying only a 3224c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * subset of views, reload that view since it may have changed. 3234c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * 3244c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * @param currentTimezone the current timezone 3254c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang */ 3264c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang public void setCurrentTimezone(String currentTimezone) { 327559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan if (currentTimezone != null && !currentTimezone.equals(mCurrentTimezone)) { 328559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan mCurrentTimezone = currentTimezone; 329559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan if (!mShowingAll) { 330559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan showInitialTimezones(); 331559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan } 332559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan } 333559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan } 334559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan 335559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan /** 336559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan * Set the time for the adapter and update the display string appropriate 337559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan * for the time of the year e.g. standard time vs daylight time 338559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan * 339559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan * @param time 340559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan */ 341559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan public void setTime(long time) { 342559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan if (time != mTime) { 343559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan mTime = time; 344559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan mDateTime.setTime(mTime); 345559cb86bf1aa43c0a9cba7a4c7297d8d6d3a33f8Michael Chan sTimezones = null; 3464c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang showInitialTimezones(); 3474c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 3484c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 3494c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 3504c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang /** 3514c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * Saves the given timezone ID as a recent timezone under shared 3524c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * preferences. If there are already the maximum number of recent timezones 3534c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * saved, it will remove the oldest and append this one. 3544c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * 3554c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * @param id the ID of the timezone to save 3564c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang * @see {@link #MAX_RECENT_TIMEZONES} 3574c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang */ 3584c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang public void saveRecentTimezone(String id) { 3594b441bd6544fe6d11be75f974a41afd8fa040a4fDaisuke Miyakawa SharedPreferences prefs = GeneralPreferences.getSharedPreferences(mContext); 3604c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang String recentsString = prefs.getString(KEY_RECENT_TIMEZONES, null); 3614c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang List<String> recents; 3624c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (recentsString == null) { 3634c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang recents = new ArrayList<String>(MAX_RECENT_TIMEZONES); 3644c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } else { 3654c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang recents = new ArrayList<String>( 3664c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang Arrays.asList(recentsString.split(RECENT_TIMEZONES_DELIMITER))); 3674c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 3684c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 3694c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang while (recents.size() >= MAX_RECENT_TIMEZONES) { 3704c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang recents.remove(0); 3714c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 3724c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang recents.add(id); 3734c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang recentsString = Utils.join(recents, RECENT_TIMEZONES_DELIMITER); 374f4ad4757de32ace6971cf4c3db7c395aa249001aMason Tang Utils.setSharedPreference(mContext, KEY_RECENT_TIMEZONES, recentsString); 3754c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 3764c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang 377a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik /** 378a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik * Returns an array of ids/time zones. This returns a double indexed array 379a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik * of ids and time zones for Calendar. It is an inefficient method and 380a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik * shouldn't be called often, but can be used for one time generation of 381a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik * this list. 382a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik * 383a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik * @return double array of tz ids and tz names 384a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik */ 385a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik public CharSequence[][] getAllTimezones() { 386a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik CharSequence[][] timeZones = new CharSequence[2][sTimezones.size()]; 387a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik List<String> ids = new ArrayList<String>(sTimezones.keySet()); 388a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik List<TimezoneRow> timezones = new ArrayList<TimezoneRow>(sTimezones.values()); 389a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik int i = 0; 390a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik for (TimezoneRow row : timezones) { 391a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik timeZones[0][i] = ids.get(i); 392a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik timeZones[1][i++] = row.toString(); 393a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik } 394a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik return timeZones; 395a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik } 396a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik 397a48b9d426236d8d26bd99602bf0a84315b3f1b09Erik private void loadFromResources(Resources resources) { 3984c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang if (sTimezones == null) { 399e274e47b7073a350dec4924ac7c09ba4211cb06dErik String[] ids = resources.getStringArray(R.array.timezone_values); 400e274e47b7073a350dec4924ac7c09ba4211cb06dErik String[] labels = resources.getStringArray(R.array.timezone_labels); 401e274e47b7073a350dec4924ac7c09ba4211cb06dErik 402e274e47b7073a350dec4924ac7c09ba4211cb06dErik int length = ids.length; 403e274e47b7073a350dec4924ac7c09ba4211cb06dErik sTimezones = new LinkedHashMap<String, TimezoneRow>(length); 404e274e47b7073a350dec4924ac7c09ba4211cb06dErik 405e274e47b7073a350dec4924ac7c09ba4211cb06dErik if (ids.length != labels.length) { 406e274e47b7073a350dec4924ac7c09ba4211cb06dErik Log.wtf(TAG, "ids length (" + ids.length + ") and labels length(" + labels.length + 407e274e47b7073a350dec4924ac7c09ba4211cb06dErik ") should be equal but aren't."); 408e274e47b7073a350dec4924ac7c09ba4211cb06dErik } 409e274e47b7073a350dec4924ac7c09ba4211cb06dErik for (int i = 0; i < length; i++) { 410e274e47b7073a350dec4924ac7c09ba4211cb06dErik sTimezones.put(ids[i], new TimezoneRow(ids[i], labels[i])); 4114c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 4124c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 4134c8871bf5dee3b3586b375aee98effde31b781a8Mason Tang } 414d7c7f2ab6fdb55451ead2d54819ba8f37af2d0a7Mason Tang} 415