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

Unified Diff: chrome/browser/autocomplete/contact_provider_chromeos.cc

Issue 10542076: ABANDONED: chromeos: Download contacts (work in progress). (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: ContactProvider cleanup and tests Created 8 years, 4 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/autocomplete/contact_provider_chromeos.cc
diff --git a/chrome/browser/autocomplete/contact_provider_chromeos.cc b/chrome/browser/autocomplete/contact_provider_chromeos.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0eb0c214c8b683674cf4877b340d2b7cb7777c5f
--- /dev/null
+++ b/chrome/browser/autocomplete/contact_provider_chromeos.cc
@@ -0,0 +1,313 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/autocomplete/contact_provider_chromeos.h"
+
+#include <algorithm>
+
+#include "base/string_split.h"
+#include "base/utf_string_conversions.h"
+#include "chrome/browser/autocomplete/autocomplete_input.h"
+#include "chrome/browser/chromeos/contacts/contact.pb.h"
+#include "chrome/browser/chromeos/contacts/contact_manager.h"
+#include "chrome/browser/profiles/profile.h"
+#include "unicode/usearch.h"
+
+namespace {
+
+// Searches for the first occurrence of |substring| within |string|. Case and
+// accent differences are ignored -- see "primary level" at
+// http://userguide.icu-project.org/collation/concepts.
+//
+// false is returned if |substring| is not found or if an error occurs. If
+// non-NULL, |string_start_index| is set to the 0-based index where the match
+// begins and |string_match_length| is set to the length of the matched text.
+bool FindFirstSubstring(const string16& string,
+ const string16& substring,
+ size_t* string_start_index,
+ size_t* string_match_length) {
+ if (string.empty() || substring.empty())
+ return false;
+
+ UErrorCode status = U_ZERO_ERROR;
+ UStringSearch* search = usearch_open(substring.data(), -1,
+ string.data(), -1,
+ uloc_getDefault(),
+ NULL, // breakiter
+ &status);
+ if (!U_SUCCESS(status))
+ return false;
+
+ UCollator* collator = usearch_getCollator(search);
+ ucol_setStrength(collator, UCOL_PRIMARY);
+ usearch_reset(search);
+
+ int32_t start_index = usearch_first(search, &status);
+ int32_t match_length = usearch_getMatchedLength(search);
+ usearch_close(search);
+
+ if (!U_SUCCESS(status) || start_index == USEARCH_DONE)
+ return false;
+
+ DCHECK_GE(start_index, 0);
+ DCHECK_GE(match_length, 0);
+ DCHECK_LE(start_index + match_length, static_cast<int32_t>(string.size()));
+
+ if (string_start_index)
+ *string_start_index = static_cast<size_t>(start_index);
+ if (string_match_length)
+ *string_match_length = static_cast<size_t>(match_length);
+ return true;
+}
+
+// Fills |classifications| based on |match_spans|, which consists of sorted
+// [index, length] pairs for matching text. Overlapping or adjacent spans are
+// collapsed where possible. Returns false on invalid input.
+bool FillACMatchClassifications(
+ const ContactProvider::MatchSpans& match_spans,
+ size_t text_length,
+ AutocompleteMatch::ACMatchClassifications* classifications) {
+ DCHECK(classifications);
+ classifications->clear();
+
+ // Index where the previous span started.
+ size_t last_span_start = 0;
+
+ // One beyond the final character in the previous span (e.g. if there's a
+ // span at offset 0 with length 1, this will be set to 1).
+ size_t last_span_end = 0;
+
+ for (ContactProvider::MatchSpans::const_iterator it = match_spans.begin();
+ it != match_spans.end(); ++it) {
+ size_t span_start = it->first;
+ size_t span_length = it->second;
+
+ if (span_start < last_span_start || span_start + span_length > text_length)
+ return false;
+
+ if (span_length == 0)
+ continue;
+
+ if (span_start > last_span_end || last_span_end == 0) {
+ if (span_start != 0) {
+ // If we're not at the beginning of the string, add a preceding unstyled
+ // classification.
+ classifications->push_back(
+ ACMatchClassification(
+ last_span_end,
+ AutocompleteMatch::ACMatchClassification::NONE));
+ }
+ classifications->push_back(
+ ACMatchClassification(
+ span_start, AutocompleteMatch::ACMatchClassification::MATCH));
+ last_span_end = span_start + span_length;
+ } else {
+ // This span starts at or before the point where the last span ended, so
+ // don't bother adding an additional classification.
+ last_span_end = std::max(last_span_end, span_start + span_length);
+ }
+
+ last_span_start = span_start;
+ }
+
+ if (last_span_end < text_length) {
+ classifications->push_back(
+ ACMatchClassification(
+ last_span_end, AutocompleteMatch::ACMatchClassification::NONE));
+ }
+
+ return true;
+}
+
+} // namespace
+
+// static
+const char ContactProvider::kMatchContactIdKey[] = "contact_id";
+
+// Cached information about a contact.
+struct ContactProvider::ContactData {
+ ContactData(const string16& full_name,
+ const string16& given_name,
+ const string16& family_name,
+ const std::string& contact_id)
+ : full_name(full_name),
+ given_name(given_name),
+ family_name(family_name),
+ given_name_index(0),
+ family_name_index(0),
+ given_name_index_valid(false),
+ family_name_index_valid(false),
+ contact_id(contact_id) {
+ given_name_index_valid =
+ FindFirstSubstring(full_name, given_name, &given_name_index, NULL);
+ family_name_index_valid =
+ FindFirstSubstring(full_name, family_name, &family_name_index, NULL);
+ }
+
+ string16 full_name;
+ string16 given_name;
+ string16 family_name;
+
+ // Indices into |full_name| where |given_name| and |family_name| first appear.
+ // Only valid when the corresponding |*_index_valid| field is true.
+ size_t given_name_index;
+ size_t family_name_index;
+
+ // Do |given_name| or |family_name| appear in |full_name|?
+ bool given_name_index_valid;
+ bool family_name_index_valid;
+
+ // Unique ID used to look up additional contact information.
+ std::string contact_id;
+};
+
+ContactProvider::TestAPI::TestAPI(ContactProvider* provider)
+ : provider_(provider) {
+}
+
+ContactProvider::TestAPI::~TestAPI() {
+ provider_ = NULL;
+}
+
+// static
+AutocompleteMatch::ACMatchClassifications
+ContactProvider::TestAPI::RunFillACMatchClassifications(
+ const MatchSpans& match_spans,
+ size_t text_length) {
+ ACMatchClassifications classifications;
+ if (FillACMatchClassifications(match_spans, text_length, &classifications))
+ return classifications;
+ else
+ return ACMatchClassifications();
+}
+
+ContactProvider::ContactProvider(
+ AutocompleteProviderListener* listener,
+ Profile* profile,
+ base::WeakPtr<contacts::ContactManagerInterface> contact_manager)
+ : AutocompleteProvider(listener, profile, "Contacts"),
+ contact_manager_(contact_manager) {
+ contact_manager_->AddObserver(this, profile);
+ RefreshContacts();
+}
+
+void ContactProvider::Start(const AutocompleteInput& input,
+ bool minimal_changes) {
+ matches_.clear();
+
+ if (input.type() != AutocompleteInput::UNKNOWN &&
+ input.type() != AutocompleteInput::QUERY &&
+ input.type() != AutocompleteInput::FORCED_QUERY) {
+ return;
+ }
+
+ std::vector<string16> input_parts;
+ base::SplitStringAlongWhitespace(input.text(), &input_parts);
+
+ for (ContactDataVector::const_iterator it = contacts_.begin();
+ it != contacts_.end(); ++it) {
+ const ContactData& contact = *it;
+
+ // [index, length] spans within |contact.full_name| that are matched by the
+ // input.
+ MatchSpans match_spans;
+
+ bool is_matched = false;
+ size_t match_index = 0, match_length = 0;
+
+ // First, check if the whole input string is a prefix of the full name.
+ if (FindFirstSubstring(
+ contact.full_name, input.text(), &match_index, &match_length) &&
+ match_index == 0) {
+ is_matched = true;
+ match_spans.push_back(std::make_pair(0, match_length));
+ } else {
+ // If not, check whether every search term is a prefix of the given name
+ // or the family name.
+ for (std::vector<string16>::const_iterator it = input_parts.begin();
+ it != input_parts.end(); ++it) {
+ if (FindFirstSubstring(
+ contact.given_name, *it, &match_index, &match_length) &&
+ match_index == 0) {
+ is_matched = true;
+ if (contact.given_name_index_valid) {
+ match_spans.push_back(
+ std::make_pair(contact.given_name_index, match_length));
+ }
+ } else if (
+ FindFirstSubstring(
+ contact.family_name, *it, &match_index, &match_length) &&
+ match_index == 0) {
+ is_matched = true;
+ if (contact.family_name_index_valid) {
+ match_spans.push_back(
+ std::make_pair(contact.family_name_index, match_length));
+ }
+ } else {
+ is_matched = false;
+ break;
+ }
+ }
+ }
+
+ if (is_matched) {
+ std::sort(match_spans.begin(), match_spans.end());
+ matches_.push_back(CreateAutocompleteMatch(input, contact, match_spans));
+ }
+ }
+}
+
+void ContactProvider::OnContactsUpdated(Profile* profile) {
+ DCHECK_EQ(profile, profile_);
+ RefreshContacts();
+}
+
+ContactProvider::~ContactProvider() {
+ // Like ContactProvider, ContactManager gets destroyed at profile destruction.
+ // Make sure that this class doesn't try to access ContactManager after
+ // ContactManager is gone.
+ if (contact_manager_.get())
+ contact_manager_->RemoveObserver(this, profile_);
+}
+
+void ContactProvider::RefreshContacts() {
+ typedef std::vector<const contacts::Contact*> ContactPointers;
+ scoped_ptr<ContactPointers> contacts =
+ contact_manager_->GetAllContacts(profile_);
+
+ contacts_.clear();
+ for (ContactPointers::const_iterator it = contacts->begin();
+ it != contacts->end(); ++it) {
+ const contacts::Contact& contact = **it;
+ string16 full_name =
+ AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.full_name()));
+ string16 given_name =
+ AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.given_name()));
+ string16 family_name =
+ AutocompleteMatch::SanitizeString(UTF8ToUTF16(contact.family_name()));
+
+ if (!full_name.empty()) {
+ contacts_.push_back(
+ ContactData(
+ full_name, given_name, family_name, contact.contact_id()));
+ }
+ }
+}
+
+AutocompleteMatch ContactProvider::CreateAutocompleteMatch(
+ const AutocompleteInput& input,
+ const ContactData& contact,
+ const MatchSpans& match_spans) {
+ AutocompleteMatch match(this, 0, false, AutocompleteMatch::CONTACT);
+ match.fill_into_edit = input.text();
+ match.inline_autocomplete_offset = string16::npos;
+ match.contents = contact.full_name;
+ match.relevance = 1000; // FIXME: update autocomplete_provider.h
+ match.RecordAdditionalInfo(kMatchContactIdKey, contact.contact_id);
+ FillACMatchClassifications(
+ match_spans,
+ match.contents.length(),
+ &match.contents_class);
+ return match;
+}

Powered by Google App Engine
This is Rietveld 408576698