| OLD | NEW |
| 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/ui/google_now/google_now_service.h" | 5 #include "chrome/browser/ui/google_now/google_now_service.h" |
| 6 | 6 |
| 7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
| 8 #include "base/json/json_reader.h" |
| 9 #include "base/memory/scoped_vector.h" |
| 10 #include "base/utf_string_conversions.h" |
| 11 #include "base/values.h" |
| 12 #include "chrome/browser/browser_process.h" |
| 13 #include "chrome/browser/notifications/notification.h" |
| 14 #include "chrome/browser/notifications/notification_ui_manager.h" |
| 8 #include "chrome/browser/profiles/profile.h" | 15 #include "chrome/browser/profiles/profile.h" |
| 16 #include "chrome/browser/ui/google_now/google_now_notification_delegate.h" |
| 9 #include "chrome/common/chrome_switches.h" | 17 #include "chrome/common/chrome_switches.h" |
| 18 #include "chrome/common/extensions/api/experimental_notification.h" |
| 10 #include "content/public/browser/geolocation.h" | 19 #include "content/public/browser/geolocation.h" |
| 11 #include "content/public/common/geoposition.h" | 20 #include "content/public/common/geoposition.h" |
| 12 #include "net/http/http_status_code.h" | 21 #include "net/http/http_status_code.h" |
| 13 #include "net/url_request/url_fetcher.h" | 22 #include "net/url_request/url_fetcher.h" |
| 14 #include "net/url_request/url_request_context.h" | 23 #include "net/url_request/url_request_context.h" |
| 15 | 24 |
| 16 using base::Bind; | 25 using base::Bind; |
| 26 using base::JSONReader; |
| 17 using base::TimeDelta; | 27 using base::TimeDelta; |
| 28 using base::Value; |
| 18 using content::Geoposition; | 29 using content::Geoposition; |
| 30 using extensions::api::experimental_notification::ShowOptions; |
| 19 using net::HTTP_OK; | 31 using net::HTTP_OK; |
| 20 using net::URLFetcher; | 32 using net::URLFetcher; |
| 21 using net::URLRequestStatus; | 33 using net::URLRequestStatus; |
| 22 | 34 |
| 23 namespace { | 35 namespace { |
| 24 // TODO(vadimt): Figure out the values of the constants. | 36 // TODO(vadimt): Figure out the values of the constants. |
| 25 | 37 |
| 26 // Period for polling for Google Now cards to use when the period from the | 38 // Period for polling for Google Now cards to use when the period from the |
| 27 // server is not available. | 39 // server is not available. |
| 28 // TODO(vadimt): Figure out the consequences for LBS and battery. | 40 // TODO(vadimt): Figure out the consequences for LBS and battery. |
| 29 // TODO(vadimt): Consider triggers other than the timer for refreshing the | 41 // TODO(vadimt): Consider triggers other than the timer for refreshing the |
| 30 // position, such as waking from sleep. | 42 // position, such as waking from sleep. |
| 31 const int kDefaultPollingPeriodMs = 300000; // 5 minutes | 43 const int kDefaultPollingPeriodMs = 300000; // 5 minutes |
| 32 // Time allocated to a geolocation request. | 44 // Time allocated to a geolocation request. |
| 33 const int kGeolocationRequestTimeoutMs = 60000; // 1 minute | 45 const int kGeolocationRequestTimeoutMs = 60000; // 1 minute |
| 46 // JSON key for expiration timestamp. |
| 47 const char kExpirationTimestampKey[] = "expiration_timestamp_seconds"; |
| 48 // JSON key for cards. |
| 49 const char kCardsKey[] = "cards"; |
| 34 } // namespace | 50 } // namespace |
| 35 | 51 |
| 52 // Google Now cards in the server response. |
| 53 typedef ScopedVector<ShowOptions> ParsedCards; |
| 54 |
| 36 struct GoogleNowService::ServerResponse { | 55 struct GoogleNowService::ServerResponse { |
| 37 // TODO(vadimt): Populate this structure with real fields. | 56 ParsedCards cards; |
| 38 TimeDelta next_request_delay; | 57 TimeDelta next_request_delay; |
| 39 }; | 58 }; |
| 40 | 59 |
| 41 GoogleNowService::GoogleNowService(Profile* profile) | 60 GoogleNowService::GoogleNowService(Profile* profile) |
| 42 : profile_(profile), | 61 : profile_(profile), |
| 43 ALLOW_THIS_IN_INITIALIZER_LIST(geolocation_request_weak_factory_(this)) { | 62 ALLOW_THIS_IN_INITIALIZER_LIST(geolocation_request_weak_factory_(this)) { |
| 44 DCHECK(profile_); | 63 DCHECK(profile_); |
| 45 } | 64 } |
| 46 | 65 |
| 47 GoogleNowService::~GoogleNowService() { | 66 GoogleNowService::~GoogleNowService() { |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 151 fetcher_->SetRequestContext(profile_->GetRequestContext()); | 170 fetcher_->SetRequestContext(profile_->GetRequestContext()); |
| 152 fetcher_->SetLoadFlags( | 171 fetcher_->SetLoadFlags( |
| 153 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | | 172 net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE | |
| 154 net::LOAD_DO_NOT_SAVE_COOKIES); | 173 net::LOAD_DO_NOT_SAVE_COOKIES); |
| 155 | 174 |
| 156 fetcher_->Start(); | 175 fetcher_->Start(); |
| 157 } | 176 } |
| 158 | 177 |
| 159 bool GoogleNowService::ParseServerResponse(const std::string& response_string, | 178 bool GoogleNowService::ParseServerResponse(const std::string& response_string, |
| 160 ServerResponse* server_response) { | 179 ServerResponse* server_response) { |
| 161 // TODO(vadimt): Do real parsing. | 180 // Server response is in JSON format. |
| 181 scoped_ptr<Value> value(JSONReader::Read(response_string)); |
| 182 |
| 183 if (value == NULL) |
| 184 return false; |
| 185 |
| 186 const DictionaryValue* root; |
| 187 if (!value->GetAsDictionary(&root)) |
| 188 return false; |
| 189 |
| 190 int expiration_timestamp_seconds; |
| 191 if (!root->GetInteger(kExpirationTimestampKey, |
| 192 &expiration_timestamp_seconds)) { |
| 193 return false; |
| 194 } |
| 195 |
| 162 server_response->next_request_delay = | 196 server_response->next_request_delay = |
| 163 TimeDelta::FromMilliseconds(kDefaultPollingPeriodMs); | 197 TimeDelta::FromSeconds(expiration_timestamp_seconds); |
| 198 |
| 199 const ListValue* cards; |
| 200 if (!root->GetList(kCardsKey, &cards)) |
| 201 return false; |
| 202 |
| 203 for (size_t index = 0; index < cards->GetSize(); ++index) { |
| 204 const base::DictionaryValue* card; |
| 205 if (!cards->GetDictionary(index, &card)) |
| 206 return false; |
| 207 |
| 208 scoped_ptr<ShowOptions> show_options(new ShowOptions()); |
| 209 |
| 210 if (!ShowOptions::Populate(*card, show_options.get())) |
| 211 return false; |
| 212 |
| 213 server_response->cards.push_back(show_options.release()); |
| 214 } |
| 215 |
| 164 return true; | 216 return true; |
| 165 } | 217 } |
| 166 | 218 |
| 167 void GoogleNowService::ShowNotifications( | 219 void GoogleNowService::ShowNotifications( |
| 168 const ServerResponse& server_response) { | 220 const ServerResponse& server_response) { |
| 169 // TODO(vadimt): Implement using Chrome Notifications. | 221 // TODO(vadimt): Most of the code below duplicates the code in |
| 222 // NotificationShowFunction::RunImpl. Once we finally decide the format of the |
| 223 // server response, remove the duplication. Before this, keep this code in |
| 224 // sync with its origin. |
| 225 for (ParsedCards::const_iterator it = server_response.cards.begin(); |
| 226 it != server_response.cards.end(); |
| 227 ++it) { |
| 228 ShowOptions* show_options = *it; |
| 229 |
| 230 ui::notifications::NotificationType type = |
| 231 ui::notifications::StringToNotificationType(show_options->type); |
| 232 GURL icon_url(UTF8ToUTF16(show_options->icon_url)); |
| 233 string16 title(UTF8ToUTF16(show_options->title)); |
| 234 string16 message(UTF8ToUTF16(show_options->message)); |
| 235 |
| 236 scoped_ptr<DictionaryValue> optional_fields(new DictionaryValue()); |
| 237 |
| 238 // For all notification types. |
| 239 if (show_options->priority.get()) |
| 240 optional_fields->SetInteger(ui::notifications::kPriorityKey, |
| 241 *show_options->priority); |
| 242 if (show_options->timestamp.get()) |
| 243 optional_fields->SetString(ui::notifications::kTimestampKey, |
| 244 *show_options->timestamp); |
| 245 if (show_options->second_icon_url.get()) |
| 246 optional_fields->SetString(ui::notifications::kSecondIconUrlKey, |
| 247 UTF8ToUTF16(*show_options->second_icon_url)); |
| 248 if (show_options->unread_count.get()) |
| 249 optional_fields->SetInteger(ui::notifications::kUnreadCountKey, |
| 250 *show_options->unread_count); |
| 251 if (show_options->button_one_title.get()) |
| 252 optional_fields->SetString(ui::notifications::kButtonOneTitleKey, |
| 253 UTF8ToUTF16(*show_options->button_one_title)); |
| 254 if (show_options->button_two_title.get()) |
| 255 optional_fields->SetString(ui::notifications::kButtonTwoTitleKey, |
| 256 UTF8ToUTF16(*show_options->button_two_title)); |
| 257 if (show_options->expanded_message.get()) |
| 258 optional_fields->SetString(ui::notifications::kExpandedMessageKey, |
| 259 UTF8ToUTF16(*show_options->expanded_message)); |
| 260 |
| 261 // For image notifications (type == 'image'). |
| 262 if (show_options->image_url.get()) |
| 263 optional_fields->SetString(ui::notifications::kImageUrlKey, |
| 264 UTF8ToUTF16(*show_options->image_url)); |
| 265 |
| 266 // For multiple-item notifications (type == 'multiple'). |
| 267 if (show_options->items.get()) { |
| 268 base::ListValue* items = new base::ListValue(); |
| 269 std::vector< |
| 270 linked_ptr< |
| 271 extensions::api::experimental_notification::NotificationItem> >:: |
| 272 iterator i; |
| 273 for (i = show_options->items->begin(); |
| 274 i != show_options->items->end(); |
| 275 ++i) { |
| 276 base::DictionaryValue* item = new base::DictionaryValue(); |
| 277 item->SetString(ui::notifications::kItemTitleKey, |
| 278 UTF8ToUTF16(i->get()->title)); |
| 279 item->SetString(ui::notifications::kItemMessageKey, |
| 280 UTF8ToUTF16(i->get()->message)); |
| 281 items->Append(item); |
| 282 } |
| 283 optional_fields->Set(ui::notifications::kItemsKey, items); |
| 284 } |
| 285 |
| 286 string16 replace_id(UTF8ToUTF16(show_options->replace_id)); |
| 287 |
| 288 Notification notification(type, icon_url, title, message, |
| 289 WebKit::WebTextDirectionDefault, |
| 290 string16(), replace_id, |
| 291 optional_fields.get(), |
| 292 new GoogleNowNotificationDelegate()); |
| 293 g_browser_process->notification_ui_manager()->Add(notification, profile_); |
| 294 } |
| 170 } | 295 } |
| 171 | 296 |
| 172 void GoogleNowService::OnURLFetchComplete(const net::URLFetcher* source) { | 297 void GoogleNowService::OnURLFetchComplete(const net::URLFetcher* source) { |
| 173 DCHECK(source != NULL); | 298 DCHECK(source != NULL); |
| 174 DCHECK(IsGoogleNowEnabled()); | 299 DCHECK(IsGoogleNowEnabled()); |
| 175 DCHECK(!next_update_timer_.IsRunning()); | 300 DCHECK(!next_update_timer_.IsRunning()); |
| 176 DCHECK(!geolocation_request_timer_.IsRunning()); | 301 DCHECK(!geolocation_request_timer_.IsRunning()); |
| 177 DCHECK(!geolocation_request_weak_factory_.HasWeakPtrs()); | 302 DCHECK(!geolocation_request_weak_factory_.HasWeakPtrs()); |
| 178 DCHECK(fetcher_.get() == source); | 303 DCHECK(fetcher_.get() == source); |
| 179 | 304 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 191 if (ParseServerResponse(response_string, &server_response)) { | 316 if (ParseServerResponse(response_string, &server_response)) { |
| 192 ShowNotifications(server_response); | 317 ShowNotifications(server_response); |
| 193 next_request_delay = server_response.next_request_delay; | 318 next_request_delay = server_response.next_request_delay; |
| 194 } | 319 } |
| 195 } | 320 } |
| 196 } | 321 } |
| 197 | 322 |
| 198 fetcher_.reset(NULL); | 323 fetcher_.reset(NULL); |
| 199 StartWaitingForNextUpdate(next_request_delay); | 324 StartWaitingForNextUpdate(next_request_delay); |
| 200 } | 325 } |
| OLD | NEW |