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/start_page_service.h"
9 #include "base/command_line.h"
10 #include "base/memory/singleton.h"
11 #include "base/metrics/user_metrics.h"
12 #include "base/prefs/pref_service.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/media/media_stream_infobar_delegate.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/search/hotword_service.h"
17 #include "chrome/browser/search/hotword_service_factory.h"
18 #include "chrome/browser/ui/app_list/recommended_apps.h"
19 #include "chrome/browser/ui/app_list/start_page_observer.h"
20 #include "chrome/browser/ui/app_list/start_page_service_factory.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/common/pref_names.h"
23 #include "chrome/common/url_constants.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_observer.h"
26 #include "content/public/browser/notification_registrar.h"
27 #include "content/public/browser/notification_service.h"
28 #include "content/public/browser/notification_source.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/browser/web_contents_delegate.h"
31 #include "extensions/browser/extension_system_provider.h"
32 #include "extensions/browser/extensions_browser_client.h"
33 #include "extensions/common/extension.h"
34 #include "ui/app_list/app_list_switches.h"
36 using base::RecordAction
;
37 using base::UserMetricsAction
;
43 bool InSpeechRecognition(SpeechRecognitionState state
) {
44 return state
== SPEECH_RECOGNITION_RECOGNIZING
||
45 state
== SPEECH_RECOGNITION_IN_SPEECH
;
50 class StartPageService::ProfileDestroyObserver
51 : public content::NotificationObserver
{
53 explicit ProfileDestroyObserver(StartPageService
* service
)
56 chrome::NOTIFICATION_PROFILE_DESTROYED
,
57 content::Source
<Profile
>(service_
->profile()));
59 virtual ~ProfileDestroyObserver() {}
62 // content::NotificationObserver
63 virtual void Observe(int type
,
64 const content::NotificationSource
& source
,
65 const content::NotificationDetails
& details
) OVERRIDE
{
66 DCHECK_EQ(chrome::NOTIFICATION_PROFILE_DESTROYED
, type
);
67 DCHECK_EQ(service_
->profile(), content::Source
<Profile
>(source
).ptr());
71 StartPageService
* service_
; // Owner of this class.
72 content::NotificationRegistrar registrar_
;
74 DISALLOW_COPY_AND_ASSIGN(ProfileDestroyObserver
);
77 class StartPageService::StartPageWebContentsDelegate
78 : public content::WebContentsDelegate
{
80 StartPageWebContentsDelegate() {}
81 virtual ~StartPageWebContentsDelegate() {}
83 virtual void RequestMediaAccessPermission(
84 content::WebContents
* web_contents
,
85 const content::MediaStreamRequest
& request
,
86 const content::MediaResponseCallback
& callback
) OVERRIDE
{
87 if (MediaStreamInfoBarDelegate::Create(web_contents
, request
, callback
))
88 NOTREACHED() << "Media stream not allowed for WebUI";
92 DISALLOW_COPY_AND_ASSIGN(StartPageWebContentsDelegate
);
96 StartPageService
* StartPageService::Get(Profile
* profile
) {
97 return StartPageServiceFactory::GetForProfile(profile
);
100 StartPageService::StartPageService(Profile
* profile
)
102 profile_destroy_observer_(new ProfileDestroyObserver(this)),
103 recommended_apps_(new RecommendedApps(profile
)),
104 state_(app_list::SPEECH_RECOGNITION_OFF
),
105 speech_button_toggled_manually_(false),
106 speech_result_obtained_(false) {
107 // If experimental hotwording is enabled, then we're always "ready".
108 // Transitioning into the "hotword recognizing" state is handled by the
109 // hotword extension.
110 if (HotwordService::IsExperimentalHotwordingEnabled())
111 state_
= app_list::SPEECH_RECOGNITION_READY
;
113 if (app_list::switches::IsExperimentalAppListEnabled())
117 StartPageService::~StartPageService() {}
119 void StartPageService::AddObserver(StartPageObserver
* observer
) {
120 observers_
.AddObserver(observer
);
123 void StartPageService::RemoveObserver(StartPageObserver
* observer
) {
124 observers_
.RemoveObserver(observer
);
127 void StartPageService::AppListShown() {
131 // If experimental hotwording is enabled, don't enable hotwording in the
132 // start page, since the hotword extension is taking care of this.
133 bool hotword_enabled
= HotwordEnabled() &&
134 !HotwordService::IsExperimentalHotwordingEnabled();
135 contents_
->GetWebUI()->CallJavascriptFunction(
136 "appList.startPage.onAppListShown",
137 base::FundamentalValue(hotword_enabled
));
141 void StartPageService::AppListHidden() {
142 contents_
->GetWebUI()->CallJavascriptFunction(
143 "appList.startPage.onAppListHidden");
144 if (!app_list::switches::IsExperimentalAppListEnabled())
148 void StartPageService::ToggleSpeechRecognition() {
149 speech_button_toggled_manually_
= true;
150 contents_
->GetWebUI()->CallJavascriptFunction(
151 "appList.startPage.toggleSpeechRecognition");
154 bool StartPageService::HotwordEnabled() {
155 if (HotwordService::IsExperimentalHotwordingEnabled()) {
156 return HotwordServiceFactory::IsServiceAvailable(profile_
) &&
157 profile_
->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled
);
159 #if defined(OS_CHROMEOS)
160 return HotwordServiceFactory::IsServiceAvailable(profile_
) &&
161 profile_
->GetPrefs()->GetBoolean(prefs::kHotwordSearchEnabled
);
167 content::WebContents
* StartPageService::GetStartPageContents() {
168 return app_list::switches::IsExperimentalAppListEnabled() ? contents_
.get()
172 content::WebContents
* StartPageService::GetSpeechRecognitionContents() {
173 if (app_list::switches::IsVoiceSearchEnabled()) {
176 return contents_
.get();
181 void StartPageService::OnSpeechResult(
182 const base::string16
& query
, bool is_final
) {
184 speech_result_obtained_
= true;
185 RecordAction(UserMetricsAction("AppList_SearchedBySpeech"));
187 FOR_EACH_OBSERVER(StartPageObserver
,
189 OnSpeechResult(query
, is_final
));
192 void StartPageService::OnSpeechSoundLevelChanged(int16 level
) {
193 FOR_EACH_OBSERVER(StartPageObserver
,
195 OnSpeechSoundLevelChanged(level
));
198 void StartPageService::OnSpeechRecognitionStateChanged(
199 SpeechRecognitionState new_state
) {
200 if (!InSpeechRecognition(state_
) && InSpeechRecognition(new_state
)) {
201 if (!speech_button_toggled_manually_
&&
202 state_
== SPEECH_RECOGNITION_HOTWORD_LISTENING
) {
203 RecordAction(UserMetricsAction("AppList_HotwordRecognized"));
205 RecordAction(UserMetricsAction("AppList_VoiceSearchStartedManually"));
207 } else if (InSpeechRecognition(state_
) && !InSpeechRecognition(new_state
) &&
208 !speech_result_obtained_
) {
209 RecordAction(UserMetricsAction("AppList_VoiceSearchCanceled"));
211 speech_button_toggled_manually_
= false;
212 speech_result_obtained_
= false;
214 FOR_EACH_OBSERVER(StartPageObserver
,
216 OnSpeechRecognitionStateChanged(new_state
));
219 void StartPageService::Shutdown() {
223 void StartPageService::LoadContents() {
224 contents_
.reset(content::WebContents::Create(
225 content::WebContents::CreateParams(profile_
)));
226 contents_delegate_
.reset(new StartPageWebContentsDelegate());
227 contents_
->SetDelegate(contents_delegate_
.get());
229 GURL
url(chrome::kChromeUIAppListStartPageURL
);
230 CommandLine
* command_line
= CommandLine::ForCurrentProcess();
231 if (command_line
->HasSwitch(::switches::kAppListStartPageURL
)) {
233 command_line
->GetSwitchValueASCII(::switches::kAppListStartPageURL
));
236 contents_
->GetController().LoadURL(
239 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
243 void StartPageService::UnloadContents() {
247 } // namespace app_list