start_page_service.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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/start_page_service.h"
6
7#include <string>
8
9#include "base/command_line.h"
10#include "base/memory/singleton.h"
11#include "base/metrics/user_metrics.h"
12#include "chrome/browser/chrome_notification_types.h"
13#include "chrome/browser/media/media_stream_infobar_delegate.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/ui/app_list/app_list_service.h"
16#include "chrome/browser/ui/app_list/recommended_apps.h"
17#include "chrome/browser/ui/app_list/start_page_observer.h"
18#include "chrome/browser/ui/app_list/start_page_service_factory.h"
19#include "chrome/common/chrome_switches.h"
20#include "chrome/common/url_constants.h"
21#include "content/public/browser/notification_details.h"
22#include "content/public/browser/notification_observer.h"
23#include "content/public/browser/notification_registrar.h"
24#include "content/public/browser/notification_service.h"
25#include "content/public/browser/notification_source.h"
26#include "content/public/browser/web_contents.h"
27#include "content/public/browser/web_contents_delegate.h"
28#include "extensions/browser/extension_system_provider.h"
29#include "extensions/browser/extensions_browser_client.h"
30#include "extensions/common/extension.h"
31#include "ui/app_list/app_list_switches.h"
32
33using base::RecordAction;
34using base::UserMetricsAction;
35
36namespace app_list {
37
38namespace {
39
40bool InSpeechRecognition(SpeechRecognitionState state) {
41  return state == SPEECH_RECOGNITION_RECOGNIZING ||
42      state == SPEECH_RECOGNITION_IN_SPEECH;
43}
44
45}
46
47class StartPageService::ProfileDestroyObserver
48    : public content::NotificationObserver {
49 public:
50  explicit ProfileDestroyObserver(StartPageService* service)
51      : service_(service) {
52    registrar_.Add(this,
53                   chrome::NOTIFICATION_PROFILE_DESTROYED,
54                   content::Source<Profile>(service_->profile()));
55  }
56  virtual ~ProfileDestroyObserver() {}
57
58 private:
59  // content::NotificationObserver
60  virtual void Observe(int type,
61                       const content::NotificationSource& source,
62                       const content::NotificationDetails& details) OVERRIDE {
63    DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED, type);
64    DCHECK_EQ(service_->profile(), content::Source<Profile>(source).ptr());
65    service_->Shutdown();
66  }
67
68  StartPageService* service_;  // Owner of this class.
69  content::NotificationRegistrar registrar_;
70
71  DISALLOW_COPY_AND_ASSIGN(ProfileDestroyObserver);
72};
73
74class StartPageService::StartPageWebContentsDelegate
75    : public content::WebContentsDelegate {
76 public:
77  StartPageWebContentsDelegate() {}
78  virtual ~StartPageWebContentsDelegate() {}
79
80  virtual void RequestMediaAccessPermission(
81      content::WebContents* web_contents,
82      const content::MediaStreamRequest& request,
83      const content::MediaResponseCallback& callback) OVERRIDE {
84    if (MediaStreamInfoBarDelegate::Create(web_contents, request, callback))
85      NOTREACHED() << "Media stream not allowed for WebUI";
86  }
87
88 private:
89  DISALLOW_COPY_AND_ASSIGN(StartPageWebContentsDelegate);
90};
91
92// static
93StartPageService* StartPageService::Get(Profile* profile) {
94  return StartPageServiceFactory::GetForProfile(profile);
95}
96
97StartPageService::StartPageService(Profile* profile)
98    : profile_(profile),
99      profile_destroy_observer_(new ProfileDestroyObserver(this)),
100      recommended_apps_(new RecommendedApps(profile)),
101      state_(app_list::SPEECH_RECOGNITION_OFF),
102      speech_button_toggled_manually_(false),
103      speech_result_obtained_(false) {
104#if defined(OS_CHROMEOS)
105  // Updates the default state to hotword listening, because this is
106  // the default behavior. This will be updated when the page is loaded and
107  // the nacl module is loaded.
108  if (app_list::switches::IsVoiceSearchEnabled())
109    state_ = app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING;
110#endif
111
112  contents_.reset(content::WebContents::Create(
113      content::WebContents::CreateParams(profile_)));
114  contents_delegate_.reset(new StartPageWebContentsDelegate());
115  contents_->SetDelegate(contents_delegate_.get());
116
117  GURL url(chrome::kChromeUIAppListStartPageURL);
118  CommandLine* command_line = CommandLine::ForCurrentProcess();
119  if (command_line->HasSwitch(::switches::kAppListStartPageURL)) {
120    url = GURL(
121        command_line->GetSwitchValueASCII(::switches::kAppListStartPageURL));
122  }
123
124  contents_->GetController().LoadURL(
125      url,
126      content::Referrer(),
127      content::PAGE_TRANSITION_AUTO_TOPLEVEL,
128      std::string());
129}
130
131StartPageService::~StartPageService() {}
132
133void StartPageService::AddObserver(StartPageObserver* observer) {
134  observers_.AddObserver(observer);
135}
136
137void StartPageService::RemoveObserver(StartPageObserver* observer) {
138  observers_.RemoveObserver(observer);
139}
140
141void StartPageService::ToggleSpeechRecognition() {
142  speech_button_toggled_manually_ = true;
143  contents_->GetWebUI()->CallJavascriptFunction(
144      "appList.startPage.toggleSpeechRecognition");
145}
146
147content::WebContents* StartPageService::GetStartPageContents() {
148  return app_list::switches::IsExperimentalAppListEnabled() ? contents_.get()
149                                                            : NULL;
150}
151
152content::WebContents* StartPageService::GetSpeechRecognitionContents() {
153  return app_list::switches::IsVoiceSearchEnabled() ? contents_.get() : NULL;
154}
155
156void StartPageService::OnSpeechResult(
157    const base::string16& query, bool is_final) {
158  if (is_final) {
159    speech_result_obtained_ = true;
160    RecordAction(UserMetricsAction("AppList_SearchedBySpeech"));
161  }
162  FOR_EACH_OBSERVER(StartPageObserver,
163                    observers_,
164                    OnSpeechResult(query, is_final));
165}
166
167void StartPageService::OnSpeechSoundLevelChanged(int16 level) {
168  FOR_EACH_OBSERVER(StartPageObserver,
169                    observers_,
170                    OnSpeechSoundLevelChanged(level));
171}
172
173void StartPageService::OnSpeechRecognitionStateChanged(
174    SpeechRecognitionState new_state) {
175  SpeechRecognitionState old_state = state_;
176  state_ = new_state;
177
178  if (!InSpeechRecognition(old_state) && InSpeechRecognition(new_state)) {
179    if (!speech_button_toggled_manually_ &&
180        old_state == SPEECH_RECOGNITION_HOTWORD_LISTENING) {
181      RecordAction(UserMetricsAction("AppList_HotwordRecognized"));
182      AppListService* app_list_service =
183          AppListService::Get(chrome::GetActiveDesktop());
184      if (!app_list_service->IsAppListVisible())
185       app_list_service->Show();
186    } else {
187      RecordAction(UserMetricsAction("AppList_VoiceSearchStartedManually"));
188    }
189  } else if (InSpeechRecognition(old_state) &&
190             !InSpeechRecognition(new_state) &&
191             !speech_result_obtained_) {
192    RecordAction(UserMetricsAction("AppList_VoiceSearchCanceled"));
193  }
194  speech_button_toggled_manually_ = false;
195  speech_result_obtained_ = false;
196
197  FOR_EACH_OBSERVER(StartPageObserver,
198                    observers_,
199                    OnSpeechRecognitionStateChanged(new_state));
200}
201
202void StartPageService::Shutdown() {
203  contents_.reset();
204}
205
206}  // namespace app_list
207