1 // Copyright 2013 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.
5 #include "chrome/browser/ui/app_list/search/people/people_provider.h"
10 #include "base/callback.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/search/search.h"
17 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
18 #include "chrome/browser/signin/signin_manager_factory.h"
19 #include "chrome/browser/ui/app_list/search/common/json_response_fetcher.h"
20 #include "chrome/browser/ui/app_list/search/people/people_result.h"
21 #include "chrome/browser/ui/app_list/search/people/person.h"
22 #include "components/signin/core/browser/profile_oauth2_token_service.h"
23 #include "components/signin/core/browser/signin_manager.h"
24 #include "google_apis/gaia/gaia_constants.h"
25 #include "net/base/url_util.h"
32 const char kKeyItems
[] = "items";
34 const char kAccessTokenField
[] = "access_token";
35 const char kQueryField
[] = "query";
36 const char kPeopleSearchUrl
[] =
37 "https://www.googleapis.com/plus/v2whitelisted/people/autocomplete";
39 // OAuth2 scope for access to the Google+ People Search API.
40 const char kPeopleSearchOAuth2Scope
[] =
41 "https://www.googleapis.com/auth/plus.peopleapi.readwrite";
45 PeopleProvider::PeopleProvider(Profile
* profile
,
46 AppListControllerDelegate
* controller
)
47 : WebserviceSearchProvider(profile
),
48 OAuth2TokenService::Consumer("people_provider"),
49 controller_(controller
),
50 people_search_url_(kPeopleSearchUrl
),
51 skip_request_token_for_test_(false) {
52 oauth2_scope_
.insert(kPeopleSearchOAuth2Scope
);
55 PeopleProvider::~PeopleProvider() {}
57 void PeopleProvider::Start(bool /*is_voice_query*/,
58 const base::string16
& query
) {
60 if (!IsValidQuery(query
)) {
65 query_
= base::UTF16ToUTF8(query
);
67 const CacheResult result
= cache_
->Get(WebserviceCache::PEOPLE
, query_
);
69 ProcessPeopleSearchResults(result
.second
);
70 if (!people_search_fetched_callback_
.is_null())
71 people_search_fetched_callback_
.Run();
72 if (result
.first
== FRESH
)
77 if (!people_search_
) {
78 people_search_
.reset(new JSONResponseFetcher(
79 base::Bind(&PeopleProvider::OnPeopleSearchFetched
,
80 base::Unretained(this)),
81 profile_
->GetRequestContext()));
84 if (!skip_request_token_for_test_
) {
85 // We start with reqesting the access token. Once the token is fetched,
86 // we'll create the full query URL and fetch it.
87 StartThrottledQuery(base::Bind(&PeopleProvider::RequestAccessToken
,
88 base::Unretained(this)));
90 // Running in a test, skip requesting the access token, straight away
92 StartThrottledQuery(base::Bind(&PeopleProvider::StartQuery
,
93 base::Unretained(this)));
97 void PeopleProvider::Stop() {
99 people_search_
->Stop();
102 void PeopleProvider::OnGetTokenSuccess(
103 const OAuth2TokenService::Request
* request
,
104 const std::string
& access_token
,
105 const base::Time
& expiration_time
) {
106 DCHECK_EQ(access_token_request_
, request
);
107 access_token_request_
.reset();
108 access_token_
= access_token
;
112 void PeopleProvider::OnGetTokenFailure(
113 const OAuth2TokenService::Request
* request
,
114 const GoogleServiceAuthError
& error
) {
115 DCHECK_EQ(access_token_request_
, request
);
116 access_token_request_
.reset();
119 void PeopleProvider::RequestAccessToken() {
120 // Only one active request at a time.
121 if (access_token_request_
!= NULL
)
124 SigninManagerBase
* signin_manager
=
125 SigninManagerFactory::GetInstance()->GetForProfile(profile_
);
126 if (signin_manager
->IsAuthenticated()) {
127 ProfileOAuth2TokenService
* token_service
=
128 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_
);
129 access_token_request_
= token_service
->StartRequest(
130 signin_manager
->GetAuthenticatedAccountId(), oauth2_scope_
, this);
134 GURL
PeopleProvider::GetQueryUrl(const std::string
& query
) {
135 GURL people_search_url
= people_search_url_
;
136 people_search_url
= net::AppendQueryParameter(people_search_url
,
139 people_search_url
= net::AppendQueryParameter(people_search_url
,
143 return people_search_url
;
146 void PeopleProvider::StartQuery() {
147 // |query_| can be NULL when the query is scheduled but then canceled.
148 if (!people_search_
|| query_
.empty())
151 GURL url
= GetQueryUrl(query_
);
152 people_search_
->Start(url
);
155 void PeopleProvider::OnPeopleSearchFetched(
156 scoped_ptr
<base::DictionaryValue
> json
) {
157 ProcessPeopleSearchResults(json
.get());
158 cache_
->Put(WebserviceCache::PEOPLE
, query_
, json
.Pass());
160 if (!people_search_fetched_callback_
.is_null())
161 people_search_fetched_callback_
.Run();
164 void PeopleProvider::ProcessPeopleSearchResults(
165 const base::DictionaryValue
* json
) {
166 const base::ListValue
* item_list
= NULL
;
168 !json
->GetList(kKeyItems
, &item_list
) ||
170 item_list
->empty()) {
175 for (base::ListValue::const_iterator it
= item_list
->begin();
176 it
!= item_list
->end();
178 const base::DictionaryValue
* dict
;
179 if (!(*it
)->GetAsDictionary(&dict
))
182 scoped_ptr
<SearchResult
> result(CreateResult(*dict
));
190 scoped_ptr
<SearchResult
> PeopleProvider::CreateResult(
191 const base::DictionaryValue
& dict
) {
192 scoped_ptr
<SearchResult
> result
;
194 scoped_ptr
<Person
> person
= Person::Create(dict
);
196 return result
.Pass();
198 result
.reset(new PeopleResult(profile_
, controller_
, person
.Pass()));
199 return result
.Pass();
202 void PeopleProvider::SetupForTest(
203 const base::Closure
& people_search_fetched_callback
,
204 const GURL
& people_search_url
) {
205 people_search_fetched_callback_
= people_search_fetched_callback
;
206 people_search_url_
= people_search_url
;
207 skip_request_token_for_test_
= true;
210 } // namespace app_list