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

Side by Side Diff: chrome/browser/chromeos/contacts/google_contact_source.cc

Issue 10542076: ABANDONED: chromeos: Download contacts (work in progress). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: minor changes 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 | Annotate | Revision Log
OLDNEW
(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/chromeos/contacts/google_contact_source.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/file_path.h"
11 #include "base/logging.h"
12 #include "base/stringprintf.h"
13 #include "base/tracked_objects.h"
14 #include "chrome/browser/chromeos/contacts/contact.h"
15 #include "chrome/browser/chromeos/contacts/contact_database.h"
16 #include "chrome/browser/chromeos/contacts/contact_source_observer.h"
17 #include "chrome/browser/chromeos/gdata/gdata_contacts_service.h"
18 #include "chrome/browser/chromeos/gdata/gdata_system_service.h"
19 #include "chrome/browser/chromeos/gdata/gdata_util.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "content/public/browser/browser_thread.h"
22
23 using content::BrowserThread;
24
25 namespace contacts {
26
27 namespace {
28
29 // Name of the file within the profile directory where contacts are written.
30 const FilePath::CharType kDatabaseFileName[] =
31 FILE_PATH_LITERAL("Google Contacts");
32
33 // We wait this long after the last update has completed before updating again.
34 // FIXME: set this to a non-insane value
35 const int kUpdateIntervalSec = 60;
36
37 } // namespace
38
39 GoogleContactSource::TestAPI::TestAPI(GoogleContactSource* source)
40 : source_(source) {
41 DCHECK(source);
42 }
43
44 GoogleContactSource::TestAPI::~TestAPI() {
45 source_ = NULL;
46 }
47
48 void GoogleContactSource::TestAPI::SetDatabase(ContactDatabaseInterface* db) {
49 source_->db_.reset(db);
50 }
51
52 void GoogleContactSource::TestAPI::SetGDataService(
53 gdata::GDataContactsServiceInterface* service) {
54 source_->gdata_service_for_testing_.reset(service);
55 }
56
57 void GoogleContactSource::TestAPI::DoUpdate() {
58 source_->UpdateContacts();
59 }
60
61 GoogleContactSource::GoogleContactSource(Profile* profile)
62 : profile_(profile),
63 contacts_deleter_(&contacts_),
64 db_(new ContactDatabase) {
65 }
66
67 GoogleContactSource::~GoogleContactSource() {
68 }
69
70 void GoogleContactSource::Init() {
71 VLOG(1) << "Initializing contact database for " << profile_->GetProfileName();
72 db_->Init(profile_->GetPath().Append(kDatabaseFileName),
73 base::Bind(&GoogleContactSource::OnDatabaseInitialized,
74 base::Unretained(this)));
75 }
76
77 void GoogleContactSource::AppendContacts(ContactPointers* contacts_out) {
78 DCHECK(contacts_out);
79 for (ContactMap::const_iterator it = contacts_.begin();
80 it != contacts_.end(); ++it) {
81 if (!it->second->deleted)
82 contacts_out->push_back(it->second);
83 }
84 }
85
86 const Contact* GoogleContactSource::GetContactByProviderId(
87 const std::string& provider_id) {
88 ContactMap::const_iterator it = contacts_.find(provider_id);
89 return it != contacts_.end() ? it->second : NULL;
90 }
91
92 void GoogleContactSource::AddObserver(ContactSourceObserver* observer) {
93 DCHECK(observer);
94 observers_.AddObserver(observer);
95 }
96
97 void GoogleContactSource::RemoveObserver(ContactSourceObserver* observer) {
98 DCHECK(observer);
99 observers_.RemoveObserver(observer);
100 }
101
102 void GoogleContactSource::UpdateContacts() {
103 // TODO(derat): Need to do a full sync when it's been more than 30 days
104 // since the last update?
105 // https://developers.google.com/google-apps/contacts/v3/index says that
106 // deleted contact (groups?) will only be returned for 30 days after
107 // deletion when the "showdeleted" parameter is set.
108 base::Time min_update_time;
109 if (!last_update_time_.is_null()) {
110 // TODO(derat): Adding one millisecond to the last update time here as I
111 // don't want to re-download the same most-recently-updated contact each
112 // time, but what happens if within the same millisecond, contact A is
113 // updated, we do a sync, and then contact B is updated? I'm probably
114 // being overly paranoid about this.
115 min_update_time =
116 last_update_time_ + base::TimeDelta::FromMilliseconds(1);
117 }
118 if (min_update_time.is_null()) {
119 VLOG(1) << "Downloading all contacts for " << profile_->GetProfileName();
120 } else {
121 VLOG(1) << "Downloading contacts for " << profile_->GetProfileName()
122 << " modified since "
123 << gdata::util::FormatTimeAsString(min_update_time);
124 }
125
126 gdata::GDataContactsServiceInterface* service =
127 gdata_service_for_testing_.get() ?
128 gdata_service_for_testing_.get() :
129 gdata::GDataSystemServiceFactory::GetForProfile(profile_)->
130 contacts_service();
131 DCHECK(service);
132 service->DownloadContacts(
133 base::Bind(&GoogleContactSource::OnDownloadSuccess,
134 base::Unretained(this),
135 min_update_time.is_null()),
136 base::Bind(&GoogleContactSource::OnDownloadFailure,
137 base::Unretained(this)),
138 min_update_time);
139 }
140
141 void GoogleContactSource::ScheduleUpdate() {
142 update_timer_.Start(
143 FROM_HERE, base::TimeDelta::FromSeconds(kUpdateIntervalSec),
144 this, &GoogleContactSource::UpdateContacts);
145 }
146
147 void GoogleContactSource::MergeContacts(
148 bool is_full_update,
149 ScopedVector<Contact>* updated_contacts) {
150 if (is_full_update)
151 contacts_.clear();
152
153 size_t num_updated_contacts = updated_contacts->size();
154 for (ScopedVector<Contact>::iterator it = updated_contacts->begin();
155 it != updated_contacts->end(); ++it) {
156 Contact* contact = *it;
157 VLOG(1) << "Updating " << contact->provider_id;
158 ContactMap::iterator map_it = contacts_.find(contact->provider_id);
159 if (map_it == contacts_.end()) {
160 contacts_[contact->provider_id] = contact;
161 } else {
162 delete map_it->second;
163 map_it->second = contact;
164 }
165 }
166 updated_contacts->weak_clear();
167
168 if (is_full_update || num_updated_contacts > 0) {
169 // Find the latest update time.
170 last_update_time_ = base::Time();
171 for (ContactMap::const_iterator it = contacts_.begin();
172 it != contacts_.end(); ++it) {
173 const Contact* contact = it->second;
174 if (!contact->update_time.is_null() &&
175 (last_update_time_.is_null() ||
176 last_update_time_ < contact->update_time)) {
177 last_update_time_ = contact->update_time;
178 }
179 }
180 }
181 VLOG(1) << "Last update time is "
182 << (last_update_time_.is_null() ?
183 std::string("null") :
184 gdata::util::FormatTimeAsString(last_update_time_));
185 }
186
187 void GoogleContactSource::OnDownloadSuccess(
188 bool is_full_update,
189 scoped_ptr<ScopedVector<Contact> > updated_contacts) {
190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
191 VLOG(1) << "Got " << updated_contacts->size() << " contact(s) for "
192 << profile_->GetProfileName();
193 size_t num_updated_contacts = updated_contacts->size();
194 MergeContacts(is_full_update, updated_contacts.get());
195
196 bool changed = is_full_update || num_updated_contacts > 0;
197 if (changed) {
198 FOR_EACH_OBSERVER(ContactSourceObserver,
199 observers_,
200 OnContactsUpdated(this));
201 }
202
203 if (changed && db_.get()) {
204 scoped_ptr<ContactPointers> contacts_to_save(new ContactPointers);
205 for (ContactMap::const_iterator it = contacts_.begin();
206 it != contacts_.end(); ++it) {
207 contacts_to_save->push_back(it->second);
208 }
209 db_->SaveContacts(
210 contacts_to_save.Pass(),
211 is_full_update,
212 base::Bind(&GoogleContactSource::OnDatabaseContactsSaved,
213 base::Unretained(this)));
214 // We'll schedule an update from OnDatabaseContactsSaved() after we're doing
215 // writing to the database -- we don't want to modify the contacts while
216 // they're being used by the database.
217 } else {
218 ScheduleUpdate();
219 }
220 }
221
222 void GoogleContactSource::OnDownloadFailure() {
223 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
224 LOG(WARNING) << "Contacts download failed for " << profile_->GetProfileName();
225 ScheduleUpdate();
226 }
227
228 void GoogleContactSource::OnDatabaseInitialized(bool success) {
229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
230 if (success) {
231 VLOG(1) << "Contact database initialized for "
232 << profile_->GetProfileName();
233 db_->LoadContacts(
234 base::Bind(&GoogleContactSource::OnDatabaseContactsLoaded,
235 base::Unretained(this)));
236 } else {
237 LOG(WARNING) << "Failed to initialize contact database for "
238 << profile_->GetProfileName();
239 // Limp along as best as we can: throw away the database and do an update,
240 // which will schedule further updates.
241 db_.reset();
242 UpdateContacts();
243 }
244 }
245
246 void GoogleContactSource::OnDatabaseContactsLoaded(
247 bool success, scoped_ptr<ScopedVector<Contact> > contacts) {
248 if (success) {
249 VLOG(1) << "Loaded " << contacts->size() << " contact(s) from database";
250 MergeContacts(true, contacts.get());
251 if (!contacts_.empty()) {
252 FOR_EACH_OBSERVER(ContactSourceObserver,
253 observers_,
254 OnContactsUpdated(this));
255 }
256 } else {
257 LOG(WARNING) << "Failed to load contacts from database";
258 }
259 UpdateContacts();
260 }
261
262 void GoogleContactSource::OnDatabaseContactsSaved(bool success) {
263 if (!success)
264 LOG(WARNING) << "Failed to save contacts to database";
265 ScheduleUpdate();
266 }
267
268 } // namespace contacts
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698