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