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. 4 5 #include "chrome/browser/ui/app_list/search/people/people_result.h" 6 7 #include <vector> 8 9 #include "base/bind.h" 10 #include "base/memory/ref_counted.h" 11 #include "base/strings/utf_string_conversions.h" 12 #include "chrome/browser/profiles/profile.h" 13 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 14 #include "chrome/browser/signin/signin_manager_factory.h" 15 #include "chrome/browser/ui/app_list/search/common/url_icon_source.h" 16 #include "chrome/browser/ui/app_list/search/people/person.h" 17 #include "chrome/browser/ui/browser_navigator.h" 18 #include "chrome/common/extensions/api/hangouts_private.h" 19 #include "components/signin/core/browser/profile_oauth2_token_service.h" 20 #include "components/signin/core/browser/signin_manager.h" 21 #include "content/public/browser/user_metrics.h" 22 #include "extensions/browser/event_router.h" 23 #include "grit/generated_resources.h" 24 #include "grit/theme_resources.h" 25 #include "ui/base/l10n/l10n_util.h" 26 #include "ui/base/resource/resource_bundle.h" 27 28 namespace OnHangoutRequested = 29 extensions::api::hangouts_private::OnHangoutRequested; 30 31 using extensions::api::hangouts_private::User; 32 using extensions::api::hangouts_private::HangoutRequest; 33 34 namespace { 35 36 const int kIconSize = 32; 37 const char kImageSizePath[] = "s64-p/"; 38 const char kEmailUrlPrefix[] = "mailto:"; 39 40 const char* const kHangoutsExtensionIds[] = { 41 "nckgahadagoaajjgafhacjanaoiihapd", 42 "ljclpkphhpbpinifbeabbhlfddcpfdde", 43 "ppleadejekpmccmnpjdimmlfljlkdfej", 44 "eggnbpckecmjlblplehfpjjdhhidfdoj", 45 "jfjjdfefebklmdbmenmlehlopoocnoeh", 46 "knipolnnllmklapflnccelgolnpehhpl" 47 }; 48 49 // Add a query parameter to specify the size to fetch the image in. The 50 // original profile image can be of an arbitrary size, we ask the server to 51 // crop it to a square 64x64 using its smart cropping algorithm. 52 GURL GetImageUrl(const GURL& url) { 53 std::string image_filename = url.ExtractFileName(); 54 if (image_filename.empty()) 55 return url; 56 57 return url.Resolve(kImageSizePath + image_filename); 58 } 59 60 } // namespace 61 62 namespace app_list { 63 64 PeopleResult::PeopleResult(Profile* profile, scoped_ptr<Person> person) 65 : profile_(profile), person_(person.Pass()), weak_factory_(this) { 66 set_id(person_->id); 67 set_title(base::UTF8ToUTF16(person_->display_name)); 68 set_relevance(person_->interaction_rank); 69 set_details(base::UTF8ToUTF16(person_->email)); 70 71 RefreshHangoutsExtensionId(); 72 SetDefaultActions(); 73 74 image_ = gfx::ImageSkia( 75 new UrlIconSource(base::Bind(&PeopleResult::OnIconLoaded, 76 weak_factory_.GetWeakPtr()), 77 profile_->GetRequestContext(), 78 GetImageUrl(person_->image_url), 79 kIconSize, 80 IDR_PROFILE_PICTURE_LOADING), 81 gfx::Size(kIconSize, kIconSize)); 82 SetIcon(image_); 83 } 84 85 PeopleResult::~PeopleResult() { 86 } 87 88 void PeopleResult::Open(int event_flags) { 89 // Action 0 will always be our default action. 90 InvokeAction(0, event_flags); 91 } 92 93 void PeopleResult::InvokeAction(int action_index, int event_flags) { 94 if (hangouts_extension_id_.empty()) { 95 // If the hangouts app is not available, the only option we are showing 96 // to the user is 'Send Email'. 97 SendEmail(); 98 } else { 99 switch (action_index) { 100 case 0: 101 OpenChat(); 102 break; 103 case 1: 104 SendEmail(); 105 break; 106 default: 107 LOG(ERROR) << "Invalid people search action: " << action_index; 108 } 109 } 110 } 111 112 scoped_ptr<ChromeSearchResult> PeopleResult::Duplicate() { 113 return scoped_ptr<ChromeSearchResult>( 114 new PeopleResult(profile_, person_->Duplicate().Pass())).Pass(); 115 } 116 117 void PeopleResult::OnIconLoaded() { 118 // Remove the existing image reps since the icon data is loaded and they 119 // need to be re-created. 120 const std::vector<gfx::ImageSkiaRep>& image_reps = image_.image_reps(); 121 for (size_t i = 0; i < image_reps.size(); ++i) 122 image_.RemoveRepresentation(image_reps[i].scale()); 123 124 SetIcon(image_); 125 } 126 127 void PeopleResult::SetDefaultActions() { 128 Actions actions; 129 130 ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); 131 if (!hangouts_extension_id_.empty()) { 132 actions.push_back(Action( 133 *bundle.GetImageSkiaNamed(IDR_PEOPLE_SEARCH_ACTION_CHAT), 134 *bundle.GetImageSkiaNamed(IDR_PEOPLE_SEARCH_ACTION_CHAT_HOVER), 135 *bundle.GetImageSkiaNamed(IDR_PEOPLE_SEARCH_ACTION_CHAT_PRESSED), 136 l10n_util::GetStringUTF16(IDS_PEOPLE_SEARCH_ACTION_CHAT_TOOLTIP))); 137 } 138 actions.push_back(Action( 139 *bundle.GetImageSkiaNamed(IDR_PEOPLE_SEARCH_ACTION_EMAIL), 140 *bundle.GetImageSkiaNamed(IDR_PEOPLE_SEARCH_ACTION_EMAIL_HOVER), 141 *bundle.GetImageSkiaNamed(IDR_PEOPLE_SEARCH_ACTION_EMAIL_PRESSED), 142 l10n_util::GetStringUTF16(IDS_PEOPLE_SEARCH_ACTION_EMAIL_TOOLTIP))); 143 SetActions(actions); 144 } 145 146 void PeopleResult::OpenChat() { 147 HangoutRequest request; 148 149 request.type = extensions::api::hangouts_private::HANGOUT_TYPE_CHAT; 150 151 // from: the user this chat request is originating from. 152 SigninManagerBase* signin_manager = 153 SigninManagerFactory::GetInstance()->GetForProfile(profile_); 154 DCHECK(signin_manager); 155 request.from = signin_manager->GetAuthenticatedAccountId(); 156 157 // to: list of users with whom to start this hangout is with. 158 linked_ptr<User> target(new User()); 159 target->id = person_->owner_id; 160 request.to.push_back(target); 161 162 scoped_ptr<extensions::Event> event( 163 new extensions::Event(OnHangoutRequested::kEventName, 164 OnHangoutRequested::Create(request))); 165 166 // TODO(rkc): Change this once we remove the hangoutsPrivate API. 167 // See crbug.com/306672 168 extensions::EventRouter::Get(profile_) 169 ->DispatchEventToExtension(hangouts_extension_id_, event.Pass()); 170 171 content::RecordAction(base::UserMetricsAction("PeopleSearch_OpenChat")); 172 } 173 174 void PeopleResult::SendEmail() { 175 chrome::NavigateParams params(profile_, 176 GURL(kEmailUrlPrefix + person_->email), 177 content::PAGE_TRANSITION_LINK); 178 // If no window exists, this will open a new window this one tab. 179 params.disposition = NEW_FOREGROUND_TAB; 180 chrome::Navigate(¶ms); 181 content::RecordAction(base::UserMetricsAction("PeopleSearch_SendEmail")); 182 } 183 184 void PeopleResult::RefreshHangoutsExtensionId() { 185 // TODO(rkc): Change this once we remove the hangoutsPrivate API. 186 // See crbug.com/306672 187 for (size_t i = 0; i < arraysize(kHangoutsExtensionIds); ++i) { 188 if (extensions::EventRouter::Get(profile_)->ExtensionHasEventListener( 189 kHangoutsExtensionIds[i], OnHangoutRequested::kEventName)) { 190 hangouts_extension_id_ = kHangoutsExtensionIds[i]; 191 return; 192 } 193 } 194 hangouts_extension_id_.clear(); 195 } 196 197 ChromeSearchResultType PeopleResult::GetType() { 198 return SEARCH_PEOPLE_SEARCH_RESULT; 199 } 200 201 } // namespace app_list 202