OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/ui/webui/options/chromeos/system_settings_provider.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/i18n/rtl.h" | |
10 #include "base/lazy_instance.h" | |
11 #include "base/memory/scoped_ptr.h" | |
12 #include "base/stl_util.h" | |
13 #include "base/string_util.h" | |
14 #include "base/stringprintf.h" | |
15 #include "base/synchronization/lock.h" | |
16 #include "base/time.h" | |
17 #include "base/utf_string_conversions.h" | |
18 #include "base/values.h" | |
19 #include "chrome/browser/chromeos/cros/cros_library.h" | |
20 #include "chrome/browser/chromeos/cros_settings.h" | |
21 #include "chrome/browser/chromeos/cros_settings_names.h" | |
22 #include "chrome/browser/chromeos/login/user_manager.h" | |
23 #include "grit/generated_resources.h" | |
24 #include "ui/base/l10n/l10n_util.h" | |
25 #include "unicode/calendar.h" | |
26 #include "unicode/timezone.h" | |
27 #include "unicode/ures.h" | |
28 | |
29 namespace { | |
30 | |
31 // TODO(jungshik): Using Enumerate method in ICU gives 600+ timezones. | |
32 // Even after filtering out duplicate entries with a strict identity check, | |
33 // we still have 400+ zones. Relaxing the criteria for the timezone | |
34 // identity is likely to cut down the number to < 100. Until we | |
35 // come up with a better list, we hard-code the following list as used by | |
36 // Android. | |
37 static const char* kTimeZones[] = { | |
38 "Pacific/Majuro", | |
39 "Pacific/Midway", | |
40 "Pacific/Honolulu", | |
41 "America/Anchorage", | |
42 "America/Los_Angeles", | |
43 "America/Tijuana", | |
44 "America/Denver", | |
45 "America/Phoenix", | |
46 "America/Chihuahua", | |
47 "America/Chicago", | |
48 "America/Mexico_City", | |
49 "America/Costa_Rica", | |
50 "America/Regina", | |
51 "America/New_York", | |
52 "America/Bogota", | |
53 "America/Caracas", | |
54 "America/Barbados", | |
55 "America/Manaus", | |
56 "America/Santiago", | |
57 "America/St_Johns", | |
58 "America/Sao_Paulo", | |
59 "America/Araguaina", | |
60 "America/Argentina/Buenos_Aires", | |
61 "America/Godthab", | |
62 "America/Montevideo", | |
63 "Atlantic/South_Georgia", | |
64 "Atlantic/Azores", | |
65 "Atlantic/Cape_Verde", | |
66 "Africa/Casablanca", | |
67 "Europe/London", | |
68 "Europe/Amsterdam", | |
69 "Europe/Belgrade", | |
70 "Europe/Brussels", | |
71 "Europe/Sarajevo", | |
72 "Africa/Windhoek", | |
73 "Africa/Brazzaville", | |
74 "Asia/Amman", | |
75 "Europe/Athens", | |
76 "Asia/Beirut", | |
77 "Africa/Cairo", | |
78 "Europe/Helsinki", | |
79 "Asia/Jerusalem", | |
80 "Europe/Minsk", | |
81 "Africa/Harare", | |
82 "Asia/Baghdad", | |
83 "Europe/Moscow", | |
84 "Asia/Kuwait", | |
85 "Africa/Nairobi", | |
86 "Asia/Tehran", | |
87 "Asia/Baku", | |
88 "Asia/Tbilisi", | |
89 "Asia/Yerevan", | |
90 "Asia/Dubai", | |
91 "Asia/Kabul", | |
92 "Asia/Karachi", | |
93 "Asia/Oral", | |
94 "Asia/Yekaterinburg", | |
95 "Asia/Calcutta", | |
96 "Asia/Colombo", | |
97 "Asia/Katmandu", | |
98 "Asia/Almaty", | |
99 "Asia/Rangoon", | |
100 "Asia/Krasnoyarsk", | |
101 "Asia/Bangkok", | |
102 "Asia/Shanghai", | |
103 "Asia/Hong_Kong", | |
104 "Asia/Irkutsk", | |
105 "Asia/Kuala_Lumpur", | |
106 "Australia/Perth", | |
107 "Asia/Taipei", | |
108 "Asia/Seoul", | |
109 "Asia/Tokyo", | |
110 "Asia/Yakutsk", | |
111 "Australia/Adelaide", | |
112 "Australia/Darwin", | |
113 "Australia/Brisbane", | |
114 "Australia/Hobart", | |
115 "Australia/Sydney", | |
116 "Asia/Vladivostok", | |
117 "Pacific/Guam", | |
118 "Asia/Magadan", | |
119 "Pacific/Auckland", | |
120 "Pacific/Fiji", | |
121 "Pacific/Tongatapu", | |
122 }; | |
123 | |
124 static base::LazyInstance<base::Lock>::Leaky | |
125 g_timezone_bundle_lock = LAZY_INSTANCE_INITIALIZER; | |
126 | |
127 struct UResClose { | |
128 inline void operator() (UResourceBundle* b) const { | |
129 ures_close(b); | |
130 } | |
131 }; | |
132 | |
133 string16 GetExemplarCity(const icu::TimeZone& zone) { | |
134 // TODO(jungshik): After upgrading to ICU 4.6, use U_ICUDATA_ZONE | |
135 static const char* zone_bundle_name = NULL; | |
136 | |
137 // These will be leaked at the end. | |
138 static UResourceBundle *zone_bundle = NULL; | |
139 static UResourceBundle *zone_strings = NULL; | |
140 | |
141 UErrorCode status = U_ZERO_ERROR; | |
142 { | |
143 base::AutoLock lock(g_timezone_bundle_lock.Get()); | |
144 if (zone_bundle == NULL) | |
145 zone_bundle = ures_open(zone_bundle_name, uloc_getDefault(), &status); | |
146 | |
147 if (zone_strings == NULL) | |
148 zone_strings = ures_getByKey(zone_bundle, "zone_strings", NULL, &status); | |
149 } | |
150 | |
151 icu::UnicodeString zone_id; | |
152 zone.getID(zone_id); | |
153 std::string zone_id_str; | |
154 zone_id.toUTF8String(zone_id_str); | |
155 | |
156 // resource keys for timezones use ':' in place of '/'. | |
157 ReplaceSubstringsAfterOffset(&zone_id_str, 0, "/", ":"); | |
158 scoped_ptr_malloc<UResourceBundle, UResClose> zone_item( | |
159 ures_getByKey(zone_strings, zone_id_str.c_str(), NULL, &status)); | |
160 icu::UnicodeString city; | |
161 if (!U_FAILURE(status)) { | |
162 city = icu::ures_getUnicodeStringByKey(zone_item.get(), "ec", &status); | |
163 if (U_SUCCESS(status)) | |
164 return string16(city.getBuffer(), city.length()); | |
165 } | |
166 | |
167 // Fallback case in case of failure. | |
168 ReplaceSubstringsAfterOffset(&zone_id_str, 0, ":", "/"); | |
169 // Take the last component of a timezone id (e.g. 'Baz' in 'Foo/Bar/Baz'). | |
170 // Depending on timezones, keeping all but the 1st component | |
171 // (e.g. Bar/Baz) may be better, but our current list does not have | |
172 // any timezone for which that's the case. | |
173 std::string::size_type slash_pos = zone_id_str.rfind('/'); | |
174 if (slash_pos != std::string::npos && slash_pos < zone_id_str.size()) | |
175 zone_id_str.erase(0, slash_pos + 1); | |
176 // zone id has '_' in place of ' '. | |
177 ReplaceSubstringsAfterOffset(&zone_id_str, 0, "_", " "); | |
178 return ASCIIToUTF16(zone_id_str); | |
179 } | |
180 | |
181 } // namespace anonymous | |
182 | |
183 namespace chromeos { | |
184 | |
185 SystemSettingsProvider::SystemSettingsProvider( | |
186 const NotifyObserversCallback& notify_cb) | |
187 : CrosSettingsProvider(notify_cb) { | |
188 for (size_t i = 0; i < arraysize(kTimeZones); i++) { | |
189 timezones_.push_back(icu::TimeZone::createTimeZone( | |
190 icu::UnicodeString(kTimeZones[i], -1, US_INV))); | |
191 } | |
192 system::TimezoneSettings::GetInstance()->AddObserver(this); | |
193 timezone_value_.reset(base::Value::CreateStringValue(GetKnownTimezoneID( | |
194 system::TimezoneSettings::GetInstance()->GetTimezone()))); | |
195 } | |
196 | |
197 SystemSettingsProvider::~SystemSettingsProvider() { | |
198 system::TimezoneSettings::GetInstance()->RemoveObserver(this); | |
199 STLDeleteElements(&timezones_); | |
200 } | |
201 | |
202 void SystemSettingsProvider::DoSet(const std::string& path, | |
203 const base::Value& in_value) { | |
204 // Non-guest users can change the time zone. | |
205 if (UserManager::Get()->IsLoggedInAsGuest()) | |
206 return; | |
207 | |
208 if (path == kSystemTimezone) { | |
209 string16 value; | |
210 if (!in_value.IsType(Value::TYPE_STRING) || !in_value.GetAsString(&value)) | |
211 return; | |
212 const icu::TimeZone* timezone = GetTimezone(value); | |
213 if (!timezone) | |
214 return; | |
215 system::TimezoneSettings::GetInstance()->SetTimezone(*timezone); | |
216 timezone_value_.reset( | |
217 base::Value::CreateStringValue(GetKnownTimezoneID(*timezone))); | |
218 } | |
219 } | |
220 | |
221 const base::Value* SystemSettingsProvider::Get(const std::string& path) const { | |
222 if (path == kSystemTimezone) | |
223 return timezone_value_.get(); | |
224 return NULL; | |
225 } | |
226 | |
227 // The timezone is always trusted. | |
228 bool SystemSettingsProvider::PrepareTrustedValues(const base::Closure& cb) { | |
229 return true; | |
230 } | |
231 | |
232 bool SystemSettingsProvider::HandlesSetting(const std::string& path) const { | |
233 return path == kSystemTimezone; | |
234 } | |
235 | |
236 void SystemSettingsProvider::Reload() { | |
237 // TODO(pastarmovj): We can actually cache the timezone here to make returning | |
238 // it faster. | |
239 } | |
240 | |
241 void SystemSettingsProvider::TimezoneChanged(const icu::TimeZone& timezone) { | |
242 // Fires system setting change notification. | |
243 timezone_value_.reset( | |
244 base::Value::CreateStringValue(GetKnownTimezoneID(timezone))); | |
245 NotifyObservers(kSystemTimezone); | |
246 } | |
247 | |
248 ListValue* SystemSettingsProvider::GetTimezoneList() { | |
249 ListValue* timezoneList = new ListValue(); | |
250 for (std::vector<icu::TimeZone*>::iterator iter = timezones_.begin(); | |
251 iter != timezones_.end(); ++iter) { | |
252 const icu::TimeZone* timezone = *iter; | |
253 ListValue* option = new ListValue(); | |
254 option->Append(Value::CreateStringValue(GetTimezoneID(*timezone))); | |
255 option->Append(Value::CreateStringValue(GetTimezoneName(*timezone))); | |
256 timezoneList->Append(option); | |
257 } | |
258 return timezoneList; | |
259 } | |
260 | |
261 string16 SystemSettingsProvider::GetTimezoneName( | |
262 const icu::TimeZone& timezone) { | |
263 // Instead of using the raw_offset, use the offset in effect now. | |
264 // For instance, US Pacific Time, the offset shown will be -7 in summer | |
265 // while it'll be -8 in winter. | |
266 int raw_offset, dst_offset; | |
267 UDate now = icu::Calendar::getNow(); | |
268 UErrorCode status = U_ZERO_ERROR; | |
269 timezone.getOffset(now, false, raw_offset, dst_offset, status); | |
270 DCHECK(U_SUCCESS(status)); | |
271 int offset = raw_offset + dst_offset; | |
272 // offset is in msec. | |
273 int minute_offset = std::abs(offset) / 60000; | |
274 int hour_offset = minute_offset / 60; | |
275 int min_remainder = minute_offset % 60; | |
276 // Some timezones have a non-integral hour offset. So, we need to | |
277 // use hh:mm form. | |
278 std::string offset_str = base::StringPrintf(offset >= 0 ? | |
279 "UTC+%d:%02d" : "UTC-%d:%02d", hour_offset, min_remainder); | |
280 | |
281 // TODO(jungshik): When coming up with a better list of timezones, we also | |
282 // have to come up with better 'display' names. One possibility is to list | |
283 // multiple cities (e.g. "Los Angeles, Vancouver .." in the order of | |
284 // the population of a country the city belongs to.). | |
285 // We can also think of using LONG_GENERIC or LOCATION once we upgrade | |
286 // to ICU 4.6. | |
287 // In the meantime, we use "LONG" name with "Exemplar City" to distinguish | |
288 // multiple timezones with the same "LONG" name but with different | |
289 // rules (e.g. US Mountain Time in Denver vs Phoenix). | |
290 icu::UnicodeString name; | |
291 timezone.getDisplayName(dst_offset != 0, icu::TimeZone::LONG, name); | |
292 string16 result(l10n_util::GetStringFUTF16( | |
293 IDS_OPTIONS_SETTINGS_TIMEZONE_DISPLAY_TEMPLATE, ASCIIToUTF16(offset_str), | |
294 string16(name.getBuffer(), name.length()), GetExemplarCity(timezone))); | |
295 base::i18n::AdjustStringForLocaleDirection(&result); | |
296 return result; | |
297 } | |
298 | |
299 string16 SystemSettingsProvider::GetTimezoneID( | |
300 const icu::TimeZone& timezone) { | |
301 icu::UnicodeString id; | |
302 timezone.getID(id); | |
303 return string16(id.getBuffer(), id.length()); | |
304 } | |
305 | |
306 const icu::TimeZone* SystemSettingsProvider::GetTimezone( | |
307 const string16& timezone_id) { | |
308 for (std::vector<icu::TimeZone*>::iterator iter = timezones_.begin(); | |
309 iter != timezones_.end(); ++iter) { | |
310 const icu::TimeZone* timezone = *iter; | |
311 if (GetTimezoneID(*timezone) == timezone_id) { | |
312 return timezone; | |
313 } | |
314 } | |
315 return NULL; | |
316 } | |
317 | |
318 string16 SystemSettingsProvider::GetKnownTimezoneID( | |
319 const icu::TimeZone& timezone) const { | |
320 for (std::vector<icu::TimeZone*>::const_iterator iter = timezones_.begin(); | |
321 iter != timezones_.end(); ++iter) { | |
322 const icu::TimeZone* known_timezone = *iter; | |
323 if (known_timezone->hasSameRules(timezone)) | |
324 return GetTimezoneID(*known_timezone); | |
325 } | |
326 | |
327 // Not able to find a matching timezone in our list. | |
328 return string16(); | |
329 } | |
330 | |
331 } // namespace chromeos | |
OLD | NEW |