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/webui/app_list/start_page_handler.h" 6 7 #include <string> 8 9 #include "base/bind.h" 10 #include "base/memory/scoped_ptr.h" 11 #include "base/values.h" 12 #include "base/version.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/search/hotword_service.h" 15 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h" 16 #include "chrome/browser/ui/app_list/app_list_service.h" 17 #include "chrome/browser/ui/app_list/recommended_apps.h" 18 #include "chrome/browser/ui/app_list/start_page_service.h" 19 #include "chrome/browser/ui/host_desktop.h" 20 #include "chrome/browser/ui/webui/extensions/extension_icon_source.h" 21 #include "chrome/common/pref_names.h" 22 #include "components/omaha_query_params/omaha_query_params.h" 23 #include "content/public/browser/web_ui.h" 24 #include "extensions/browser/extension_registry.h" 25 #include "extensions/browser/extension_system.h" 26 #include "extensions/common/constants.h" 27 #include "extensions/common/extension.h" 28 #include "extensions/common/extension_icon_set.h" 29 #include "ui/app_list/app_list_switches.h" 30 #include "ui/app_list/speech_ui_model_observer.h" 31 #include "ui/events/event_constants.h" 32 33 namespace app_list { 34 35 namespace { 36 37 #if defined(OS_CHROMEOS) 38 const char kOldHotwordExtensionVersionString[] = "0.1.1.5023"; 39 #endif 40 41 scoped_ptr<base::DictionaryValue> CreateAppInfo( 42 const extensions::Extension* app) { 43 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue); 44 dict->SetString("appId", app->id()); 45 dict->SetString("textTitle", app->short_name()); 46 dict->SetString("title", app->name()); 47 48 const bool grayscale = false; 49 bool icon_exists = true; 50 GURL icon_url = extensions::ExtensionIconSource::GetIconURL( 51 app, 52 extension_misc::EXTENSION_ICON_MEDIUM, 53 ExtensionIconSet::MATCH_BIGGER, 54 grayscale, 55 &icon_exists); 56 dict->SetString("iconUrl", icon_url.spec()); 57 58 return dict.Pass(); 59 } 60 61 } // namespace 62 63 StartPageHandler::StartPageHandler() 64 : recommended_apps_(NULL), 65 extension_registry_observer_(this) { 66 } 67 68 StartPageHandler::~StartPageHandler() { 69 if (recommended_apps_) 70 recommended_apps_->RemoveObserver(this); 71 } 72 73 void StartPageHandler::RegisterMessages() { 74 web_ui()->RegisterMessageCallback( 75 "initialize", 76 base::Bind(&StartPageHandler::HandleInitialize, base::Unretained(this))); 77 web_ui()->RegisterMessageCallback( 78 "launchApp", 79 base::Bind(&StartPageHandler::HandleLaunchApp, base::Unretained(this))); 80 web_ui()->RegisterMessageCallback( 81 "speechResult", 82 base::Bind(&StartPageHandler::HandleSpeechResult, 83 base::Unretained(this))); 84 web_ui()->RegisterMessageCallback( 85 "speechSoundLevel", 86 base::Bind(&StartPageHandler::HandleSpeechSoundLevel, 87 base::Unretained(this))); 88 web_ui()->RegisterMessageCallback( 89 "setSpeechRecognitionState", 90 base::Bind(&StartPageHandler::HandleSpeechRecognition, 91 base::Unretained(this))); 92 } 93 94 void StartPageHandler::OnExtensionLoaded( 95 content::BrowserContext* browser_context, 96 const extensions::Extension* extension) { 97 #if defined(OS_CHROMEOS) 98 DCHECK_EQ(Profile::FromWebUI(web_ui()), 99 Profile::FromBrowserContext(browser_context)); 100 if (extension->id() == extension_misc::kHotwordExtensionId) 101 OnHotwordEnabledChanged(); 102 #endif 103 } 104 105 void StartPageHandler::OnExtensionUnloaded( 106 content::BrowserContext* browser_context, 107 const extensions::Extension* extension, 108 extensions::UnloadedExtensionInfo::Reason reason) { 109 #if defined(OS_CHROMEOS) 110 DCHECK_EQ(Profile::FromWebUI(web_ui()), 111 Profile::FromBrowserContext(browser_context)); 112 if (extension->id() == extension_misc::kHotwordExtensionId) 113 OnHotwordEnabledChanged(); 114 #endif 115 } 116 117 void StartPageHandler::OnRecommendedAppsChanged() { 118 SendRecommendedApps(); 119 } 120 121 void StartPageHandler::SendRecommendedApps() { 122 const RecommendedApps::Apps& recommends = recommended_apps_->apps(); 123 124 base::ListValue recommended_list; 125 for (size_t i = 0; i < recommends.size(); ++i) { 126 recommended_list.Append(CreateAppInfo(recommends[i].get()).release()); 127 } 128 129 web_ui()->CallJavascriptFunction("appList.startPage.setRecommendedApps", 130 recommended_list); 131 } 132 133 #if defined(OS_CHROMEOS) 134 void StartPageHandler::OnHotwordEnabledChanged() { 135 // If the hotword extension is new enough, we should use the new 136 // hotwordPrivate API to provide the feature. 137 // TODO(mukai): remove this after everything gets stable. 138 Profile* profile = Profile::FromWebUI(web_ui()); 139 140 extensions::ExtensionRegistry* registry = 141 extensions::ExtensionRegistry::Get(profile); 142 const extensions::Extension* hotword_extension = 143 registry->GetExtensionById(extension_misc::kHotwordExtensionId, 144 extensions::ExtensionRegistry::ENABLED); 145 if (hotword_extension && 146 hotword_extension->version()->CompareTo( 147 base::Version(kOldHotwordExtensionVersionString)) <= 0 && 148 !HotwordService::IsExperimentalHotwordingEnabled()) { 149 StartPageService* service = StartPageService::Get(profile); 150 web_ui()->CallJavascriptFunction( 151 "appList.startPage.setHotwordEnabled", 152 base::FundamentalValue(service->HotwordEnabled())); 153 } 154 } 155 #endif 156 157 void StartPageHandler::HandleInitialize(const base::ListValue* args) { 158 Profile* profile = Profile::FromWebUI(web_ui()); 159 StartPageService* service = StartPageService::Get(profile); 160 if (!service) 161 return; 162 163 recommended_apps_ = service->recommended_apps(); 164 recommended_apps_->AddObserver(this); 165 166 SendRecommendedApps(); 167 168 #if defined(OS_CHROMEOS) 169 if (app_list::switches::IsVoiceSearchEnabled() && 170 HotwordService::DoesHotwordSupportLanguage(profile)) { 171 OnHotwordEnabledChanged(); 172 pref_change_registrar_.Init(profile->GetPrefs()); 173 pref_change_registrar_.Add( 174 prefs::kHotwordSearchEnabled, 175 base::Bind(&StartPageHandler::OnHotwordEnabledChanged, 176 base::Unretained(this))); 177 178 extension_registry_observer_.Add( 179 extensions::ExtensionRegistry::Get(profile)); 180 } 181 182 extensions::ExtensionRegistry* registry = 183 extensions::ExtensionRegistry::Get(profile); 184 const extensions::Extension* hotword_extension = 185 registry->GetExtensionById(extension_misc::kHotwordExtensionId, 186 extensions::ExtensionRegistry::ENABLED); 187 if (hotword_extension && 188 hotword_extension->version()->CompareTo( 189 base::Version(kOldHotwordExtensionVersionString)) <= 0) { 190 web_ui()->CallJavascriptFunction( 191 "appList.startPage.setNaclArch", 192 base::StringValue(omaha_query_params::OmahaQueryParams::GetNaclArch())); 193 } 194 #endif 195 196 if (!app_list::switches::IsExperimentalAppListEnabled()) { 197 // If experimental hotwording is enabled, don't enable hotwording in the 198 // start page, since the hotword extension is taking care of this. 199 bool hotword_enabled = service->HotwordEnabled() && 200 !HotwordService::IsExperimentalHotwordingEnabled(); 201 web_ui()->CallJavascriptFunction( 202 "appList.startPage.onAppListShown", 203 base::FundamentalValue(hotword_enabled)); 204 } 205 } 206 207 void StartPageHandler::HandleLaunchApp(const base::ListValue* args) { 208 std::string app_id; 209 CHECK(args->GetString(0, &app_id)); 210 211 Profile* profile = Profile::FromWebUI(web_ui()); 212 const extensions::Extension* app = 213 extensions::ExtensionRegistry::Get(profile) 214 ->GetExtensionById(app_id, extensions::ExtensionRegistry::EVERYTHING); 215 if (!app) { 216 NOTREACHED(); 217 return; 218 } 219 220 AppListControllerDelegate* controller = AppListService::Get( 221 chrome::GetHostDesktopTypeForNativeView( 222 web_ui()->GetWebContents()->GetNativeView()))-> 223 GetControllerDelegate(); 224 controller->ActivateApp(profile, 225 app, 226 AppListControllerDelegate::LAUNCH_FROM_APP_LIST, 227 ui::EF_NONE); 228 } 229 230 void StartPageHandler::HandleSpeechResult(const base::ListValue* args) { 231 base::string16 query; 232 bool is_final = false; 233 CHECK(args->GetString(0, &query)); 234 CHECK(args->GetBoolean(1, &is_final)); 235 236 StartPageService::Get(Profile::FromWebUI(web_ui()))->OnSpeechResult( 237 query, is_final); 238 } 239 240 void StartPageHandler::HandleSpeechSoundLevel(const base::ListValue* args) { 241 double level; 242 CHECK(args->GetDouble(0, &level)); 243 244 StartPageService* service = 245 StartPageService::Get(Profile::FromWebUI(web_ui())); 246 if (service) 247 service->OnSpeechSoundLevelChanged(static_cast<int16>(level)); 248 } 249 250 void StartPageHandler::HandleSpeechRecognition(const base::ListValue* args) { 251 std::string state_string; 252 CHECK(args->GetString(0, &state_string)); 253 254 SpeechRecognitionState new_state = SPEECH_RECOGNITION_OFF; 255 if (state_string == "READY") 256 new_state = SPEECH_RECOGNITION_READY; 257 else if (state_string == "HOTWORD_RECOGNIZING") 258 new_state = SPEECH_RECOGNITION_HOTWORD_LISTENING; 259 else if (state_string == "RECOGNIZING") 260 new_state = SPEECH_RECOGNITION_RECOGNIZING; 261 else if (state_string == "IN_SPEECH") 262 new_state = SPEECH_RECOGNITION_IN_SPEECH; 263 else if (state_string == "STOPPING") 264 new_state = SPEECH_RECOGNITION_STOPPING; 265 else if (state_string == "NETWORK_ERROR") 266 new_state = SPEECH_RECOGNITION_NETWORK_ERROR; 267 268 StartPageService* service = 269 StartPageService::Get(Profile::FromWebUI(web_ui())); 270 if (service) 271 service->OnSpeechRecognitionStateChanged(new_state); 272 } 273 274 } // namespace app_list 275