Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(63)

Side by Side Diff: chrome/browser/chromeos/system_settings_provider.cc

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

Powered by Google App Engine
This is Rietveld 408576698