1 // Copyright (c) 2012 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/app_list_view_delegate.h"
9 #include "apps/custom_launcher_page_contents.h"
10 #include "base/callback.h"
11 #include "base/command_line.h"
12 #include "base/files/file_path.h"
13 #include "base/metrics/user_metrics.h"
14 #include "base/stl_util.h"
15 #include "chrome/browser/apps/scoped_keep_alive.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/chrome_notification_types.h"
18 #include "chrome/browser/profiles/profile_info_cache.h"
19 #include "chrome/browser/profiles/profile_manager.h"
20 #include "chrome/browser/search/hotword_service.h"
21 #include "chrome/browser/search/hotword_service_factory.h"
22 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
23 #include "chrome/browser/ui/app_list/app_list_service.h"
24 #include "chrome/browser/ui/app_list/app_list_syncable_service.h"
25 #include "chrome/browser/ui/app_list/app_list_syncable_service_factory.h"
26 #include "chrome/browser/ui/app_list/search/search_controller_factory.h"
27 #include "chrome/browser/ui/app_list/search/search_resource_manager.h"
28 #include "chrome/browser/ui/app_list/start_page_service.h"
29 #include "chrome/browser/ui/apps/chrome_app_delegate.h"
30 #include "chrome/browser/ui/browser_finder.h"
31 #include "chrome/browser/ui/chrome_pages.h"
32 #include "chrome/browser/ui/host_desktop.h"
33 #include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
34 #include "chrome/browser/web_applications/web_app.h"
35 #include "chrome/common/chrome_switches.h"
36 #include "chrome/common/extensions/extension_constants.h"
37 #include "chrome/common/url_constants.h"
38 #include "components/signin/core/browser/signin_manager.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "content/public/browser/notification_service.h"
41 #include "content/public/browser/page_navigator.h"
42 #include "content/public/browser/user_metrics.h"
43 #include "content/public/browser/web_contents.h"
44 #include "extensions/browser/extension_registry.h"
45 #include "extensions/common/constants.h"
46 #include "extensions/common/extension_set.h"
47 #include "extensions/common/manifest_constants.h"
48 #include "extensions/common/manifest_handlers/launcher_page_info.h"
49 #include "grit/theme_resources.h"
50 #include "ui/app_list/app_list_switches.h"
51 #include "ui/app_list/app_list_view_delegate_observer.h"
52 #include "ui/app_list/search_box_model.h"
53 #include "ui/app_list/search_controller.h"
54 #include "ui/app_list/speech_ui_model.h"
55 #include "ui/base/resource/resource_bundle.h"
56 #include "ui/views/controls/webview/webview.h"
58 #if defined(TOOLKIT_VIEWS)
59 #include "ui/views/controls/webview/webview.h"
63 #include "ui/keyboard/keyboard_util.h"
67 #include "ash/shell.h"
68 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
69 #include "chrome/browser/ui/ash/app_list/app_sync_ui_state_watcher.h"
73 #include "chrome/browser/web_applications/web_app_win.h"
78 const char kAppLauncherCategoryTag
[] = "AppLauncher";
83 const int kAutoLaunchDefaultTimeoutMilliSec
= 50;
86 void CreateShortcutInWebAppDir(
87 const base::FilePath
& app_data_dir
,
88 base::Callback
<void(const base::FilePath
&)> callback
,
89 const web_app::ShortcutInfo
& info
) {
90 content::BrowserThread::PostTaskAndReplyWithResult(
91 content::BrowserThread::FILE,
93 base::Bind(web_app::CreateShortcutInWebAppDir
, app_data_dir
, info
),
98 void PopulateUsers(const ProfileInfoCache
& profile_info
,
99 const base::FilePath
& active_profile_path
,
100 app_list::AppListViewDelegate::Users
* users
) {
102 const size_t count
= profile_info
.GetNumberOfProfiles();
103 for (size_t i
= 0; i
< count
; ++i
) {
104 app_list::AppListViewDelegate::User user
;
105 user
.name
= profile_info
.GetNameOfProfileAtIndex(i
);
106 user
.email
= profile_info
.GetUserNameOfProfileAtIndex(i
);
107 user
.profile_path
= profile_info
.GetPathOfProfileAtIndex(i
);
108 user
.active
= active_profile_path
== user
.profile_path
;
109 users
->push_back(user
);
113 // Gets a list of URLs of the custom launcher pages to show in the launcher.
114 // Returns a URL for each installed launcher page. If --custom-launcher-page is
115 // specified and valid, also includes that URL.
116 void GetCustomLauncherPageUrls(content::BrowserContext
* browser_context
,
117 std::vector
<GURL
>* urls
) {
118 // First, check the command line.
119 base::CommandLine
* command_line
= base::CommandLine::ForCurrentProcess();
120 if (app_list::switches::IsExperimentalAppListEnabled() &&
121 command_line
->HasSwitch(app_list::switches::kCustomLauncherPage
)) {
122 GURL
custom_launcher_page_url(command_line
->GetSwitchValueASCII(
123 app_list::switches::kCustomLauncherPage
));
125 if (custom_launcher_page_url
.SchemeIs(extensions::kExtensionScheme
)) {
126 urls
->push_back(custom_launcher_page_url
);
128 LOG(ERROR
) << "Invalid custom launcher page URL: "
129 << custom_launcher_page_url
.possibly_invalid_spec();
133 // Search the list of installed extensions for ones with 'launcher_page'.
134 extensions::ExtensionRegistry
* extension_registry
=
135 extensions::ExtensionRegistry::Get(browser_context
);
136 const extensions::ExtensionSet
& enabled_extensions
=
137 extension_registry
->enabled_extensions();
138 for (extensions::ExtensionSet::const_iterator it
= enabled_extensions
.begin();
139 it
!= enabled_extensions
.end();
141 const extensions::Extension
* extension
= it
->get();
142 extensions::LauncherPageInfo
* info
=
143 extensions::LauncherPageHandler::GetInfo(extension
);
147 urls
->push_back(extension
->GetResourceURL(info
->page
));
153 AppListViewDelegate::AppListViewDelegate(AppListControllerDelegate
* controller
)
154 : controller_(controller
),
157 scoped_observer_(this) {
159 // The SigninManagerFactor and the SigninManagers are observed to keep the
160 // profile switcher menu up to date, with the correct list of profiles and the
161 // correct email address (or none for signed out users) for each.
162 SigninManagerFactory::GetInstance()->AddObserver(this);
164 // Start observing all already-created SigninManagers.
165 ProfileManager
* profile_manager
= g_browser_process
->profile_manager();
166 std::vector
<Profile
*> profiles
= profile_manager
->GetLoadedProfiles();
167 for (std::vector
<Profile
*>::iterator i
= profiles
.begin();
170 SigninManagerBase
* manager
=
171 SigninManagerFactory::GetForProfileIfExists(*i
);
173 DCHECK(!scoped_observer_
.IsObserving(manager
));
174 scoped_observer_
.Add(manager
);
178 profile_manager
->GetProfileInfoCache().AddObserver(this);
179 speech_ui_
.reset(new app_list::SpeechUIModel
);
181 #if defined(GOOGLE_CHROME_BUILD)
182 speech_ui_
->set_logo(
183 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
184 IDR_APP_LIST_GOOGLE_LOGO_VOICE_SEARCH
));
188 chrome::NOTIFICATION_APP_TERMINATING
,
189 content::NotificationService::AllSources());
192 AppListViewDelegate::~AppListViewDelegate() {
193 // Note that the destructor is not always called. E.g. on Mac, this is owned
194 // by a leaky singleton. Essential shutdown work must be done by observing
195 // chrome::NOTIFICATION_APP_TERMINATING.
197 g_browser_process
->profile_manager()->GetProfileInfoCache().RemoveObserver(
200 SigninManagerFactory
* factory
= SigninManagerFactory::GetInstance();
202 factory
->RemoveObserver(this);
205 void AppListViewDelegate::SetProfile(Profile
* new_profile
) {
206 if (profile_
== new_profile
)
210 // Note: |search_resource_manager_| has a reference to |speech_ui_| so must
211 // be destroyed first.
212 search_resource_manager_
.reset();
213 search_controller_
.reset();
214 custom_page_contents_
.clear();
215 app_list::StartPageService
* start_page_service
=
216 app_list::StartPageService::Get(profile_
);
217 if (start_page_service
)
218 start_page_service
->RemoveObserver(this);
220 app_sync_ui_state_watcher_
.reset();
225 profile_
= new_profile
;
227 speech_ui_
->SetSpeechRecognitionState(app_list::SPEECH_RECOGNITION_OFF
);
232 app_list::AppListSyncableServiceFactory::GetForProfile(profile_
)->model();
235 app_sync_ui_state_watcher_
.reset(new AppSyncUIStateWatcher(profile_
, model_
));
239 SetUpProfileSwitcher();
240 SetUpCustomLauncherPages();
242 // Clear search query.
243 model_
->search_box()->SetText(base::string16());
246 void AppListViewDelegate::SetUpSearchUI() {
247 app_list::StartPageService
* start_page_service
=
248 app_list::StartPageService::Get(profile_
);
249 if (start_page_service
)
250 start_page_service
->AddObserver(this);
252 speech_ui_
->SetSpeechRecognitionState(start_page_service
253 ? start_page_service
->state()
254 : app_list::SPEECH_RECOGNITION_OFF
);
256 search_resource_manager_
.reset(new app_list::SearchResourceManager(
258 model_
->search_box(),
261 search_controller_
= CreateSearchController(
262 profile_
, model_
->search_box(), model_
->results(), controller_
);
265 void AppListViewDelegate::SetUpProfileSwitcher() {
266 // If a profile change is observed when there is no app list, there is nothing
267 // to update until SetProfile() calls this function again.
271 // Don't populate the app list users if we are on the ash desktop.
272 chrome::HostDesktopType desktop
= chrome::GetHostDesktopTypeForNativeWindow(
273 controller_
->GetAppListWindow());
274 if (desktop
== chrome::HOST_DESKTOP_TYPE_ASH
)
277 // Populate the app list users.
278 PopulateUsers(g_browser_process
->profile_manager()->GetProfileInfoCache(),
283 app_list::AppListViewDelegateObserver
, observers_
, OnProfilesChanged());
286 void AppListViewDelegate::SetUpCustomLauncherPages() {
287 std::vector
<GURL
> custom_launcher_page_urls
;
288 GetCustomLauncherPageUrls(profile_
, &custom_launcher_page_urls
);
289 for (std::vector
<GURL
>::const_iterator it
= custom_launcher_page_urls
.begin();
290 it
!= custom_launcher_page_urls
.end();
292 std::string extension_id
= it
->host();
293 apps::CustomLauncherPageContents
* page_contents
=
294 new apps::CustomLauncherPageContents(
295 scoped_ptr
<extensions::AppDelegate
>(
296 new ChromeAppDelegate(scoped_ptr
<ScopedKeepAlive
>())),
298 page_contents
->Initialize(profile_
, *it
);
299 custom_page_contents_
.push_back(page_contents
);
303 void AppListViewDelegate::OnHotwordStateChanged(bool started
) {
305 if (speech_ui_
->state() == app_list::SPEECH_RECOGNITION_READY
) {
306 OnSpeechRecognitionStateChanged(
307 app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING
);
310 if (speech_ui_
->state() == app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING
)
311 OnSpeechRecognitionStateChanged(app_list::SPEECH_RECOGNITION_READY
);
315 void AppListViewDelegate::OnHotwordRecognized() {
316 DCHECK_EQ(app_list::SPEECH_RECOGNITION_HOTWORD_LISTENING
,
317 speech_ui_
->state());
318 ToggleSpeechRecognition();
321 void AppListViewDelegate::SigninManagerCreated(SigninManagerBase
* manager
) {
322 scoped_observer_
.Add(manager
);
325 void AppListViewDelegate::SigninManagerShutdown(SigninManagerBase
* manager
) {
326 if (scoped_observer_
.IsObserving(manager
))
327 scoped_observer_
.Remove(manager
);
330 void AppListViewDelegate::GoogleSigninFailed(
331 const GoogleServiceAuthError
& error
) {
332 SetUpProfileSwitcher();
335 void AppListViewDelegate::GoogleSigninSucceeded(const std::string
& account_id
,
336 const std::string
& username
,
337 const std::string
& password
) {
338 SetUpProfileSwitcher();
341 void AppListViewDelegate::GoogleSignedOut(const std::string
& account_id
,
342 const std::string
& username
) {
343 SetUpProfileSwitcher();
346 void AppListViewDelegate::OnProfileAdded(const base::FilePath
& profile_path
) {
347 SetUpProfileSwitcher();
350 void AppListViewDelegate::OnProfileWasRemoved(
351 const base::FilePath
& profile_path
,
352 const base::string16
& profile_name
) {
353 SetUpProfileSwitcher();
356 void AppListViewDelegate::OnProfileNameChanged(
357 const base::FilePath
& profile_path
,
358 const base::string16
& old_profile_name
) {
359 SetUpProfileSwitcher();
362 bool AppListViewDelegate::ForceNativeDesktop() const {
363 return controller_
->ForceNativeDesktop();
366 void AppListViewDelegate::SetProfileByPath(const base::FilePath
& profile_path
) {
368 // The profile must be loaded before this is called.
370 g_browser_process
->profile_manager()->GetProfileByPath(profile_path
));
373 app_list::AppListModel
* AppListViewDelegate::GetModel() {
377 app_list::SpeechUIModel
* AppListViewDelegate::GetSpeechUI() {
378 return speech_ui_
.get();
381 void AppListViewDelegate::GetShortcutPathForApp(
382 const std::string
& app_id
,
383 const base::Callback
<void(const base::FilePath
&)>& callback
) {
385 const extensions::Extension
* extension
=
386 extensions::ExtensionRegistry::Get(profile_
)->GetExtensionById(
387 app_id
, extensions::ExtensionRegistry::EVERYTHING
);
389 callback
.Run(base::FilePath());
393 base::FilePath
app_data_dir(
394 web_app::GetWebAppDataDirectory(profile_
->GetPath(),
398 web_app::GetShortcutInfoForApp(
401 base::Bind(CreateShortcutInWebAppDir
, app_data_dir
, callback
));
403 callback
.Run(base::FilePath());
407 void AppListViewDelegate::StartSearch() {
408 if (search_controller_
) {
409 search_controller_
->Start();
410 controller_
->OnSearchStarted();
414 void AppListViewDelegate::StopSearch() {
415 if (search_controller_
)
416 search_controller_
->Stop();
419 void AppListViewDelegate::OpenSearchResult(
420 app_list::SearchResult
* result
,
424 base::RecordAction(base::UserMetricsAction("AppList_AutoLaunched"));
425 search_controller_
->OpenResult(result
, event_flags
);
428 void AppListViewDelegate::InvokeSearchResultAction(
429 app_list::SearchResult
* result
,
432 search_controller_
->InvokeResultAction(result
, action_index
, event_flags
);
435 base::TimeDelta
AppListViewDelegate::GetAutoLaunchTimeout() {
436 return auto_launch_timeout_
;
439 void AppListViewDelegate::AutoLaunchCanceled() {
440 base::RecordAction(base::UserMetricsAction("AppList_AutoLaunchCanceled"));
441 auto_launch_timeout_
= base::TimeDelta();
444 void AppListViewDelegate::ViewInitialized() {
445 app_list::StartPageService
* service
=
446 app_list::StartPageService::Get(profile_
);
448 service
->AppListShown();
449 if (service
->HotwordEnabled()) {
450 HotwordService
* hotword_service
=
451 HotwordServiceFactory::GetForProfile(profile_
);
453 hotword_service
->RequestHotwordSession(this);
458 void AppListViewDelegate::Dismiss() {
459 controller_
->DismissView();
462 void AppListViewDelegate::ViewClosing() {
463 controller_
->ViewClosing();
468 app_list::StartPageService
* service
=
469 app_list::StartPageService::Get(profile_
);
471 service
->AppListHidden();
472 if (service
->HotwordEnabled()) {
473 HotwordService
* hotword_service
=
474 HotwordServiceFactory::GetForProfile(profile_
);
475 if (hotword_service
) {
476 hotword_service
->StopHotwordSession(this);
478 // If we're in always-on mode, we always want to restart hotwording
479 // after closing the launcher window. So, in always-on mode, hotwording
480 // is stopped, and then started again right away. Note that hotwording
481 // may already be stopped. The call to StopHotwordSession() above both
482 // explicitly stops hotwording, if it's running, and clears the
483 // association between the hotword service and |this|. When starting up
484 // hotwording, pass nullptr as the client so that hotword triggers cause
485 // the launcher to open.
486 // TODO(amistry): This only works on ChromeOS since Chrome hides the
487 // launcher instead of destroying it. Make this work on Chrome.
488 if (hotword_service
->IsAlwaysOnEnabled())
489 hotword_service
->RequestHotwordSession(nullptr);
495 gfx::ImageSkia
AppListViewDelegate::GetWindowIcon() {
496 return controller_
->GetWindowIcon();
499 void AppListViewDelegate::OpenSettings() {
500 const extensions::Extension
* extension
=
501 extensions::ExtensionRegistry::Get(profile_
)->GetExtensionById(
502 extension_misc::kSettingsAppId
,
503 extensions::ExtensionRegistry::EVERYTHING
);
505 controller_
->ActivateApp(profile_
,
507 AppListControllerDelegate::LAUNCH_FROM_UNKNOWN
,
511 void AppListViewDelegate::OpenHelp() {
512 chrome::HostDesktopType desktop
= chrome::GetHostDesktopTypeForNativeWindow(
513 controller_
->GetAppListWindow());
514 chrome::ScopedTabbedBrowserDisplayer
displayer(profile_
, desktop
);
515 content::OpenURLParams
params(GURL(chrome::kAppLauncherHelpURL
),
518 ui::PAGE_TRANSITION_LINK
,
520 displayer
.browser()->OpenURL(params
);
523 void AppListViewDelegate::OpenFeedback() {
524 chrome::HostDesktopType desktop
= chrome::GetHostDesktopTypeForNativeWindow(
525 controller_
->GetAppListWindow());
526 Browser
* browser
= chrome::FindTabbedBrowser(profile_
, false, desktop
);
527 chrome::ShowFeedbackPage(browser
, std::string(),
528 chrome::kAppLauncherCategoryTag
);
531 void AppListViewDelegate::ToggleSpeechRecognition() {
532 app_list::StartPageService
* service
=
533 app_list::StartPageService::Get(profile_
);
535 service
->ToggleSpeechRecognition();
537 // With the new hotword extension, stop the hotword session. With the launcher
538 // and NTP, this is unnecessary since the hotwording is implicitly stopped.
539 // However, for always on, hotword triggering launches the launcher which
540 // starts a session and hence starts the hotword detector. This results in the
541 // hotword detector and the speech-to-text engine running in parallel, which
542 // will conflict with each other (i.e. saying 'Ok Google' twice in a row
543 // should cause a search to happen for 'Ok Google', not two hotword triggers).
544 // To get around this, always stop the session when switching to speech
546 if (HotwordService::IsExperimentalHotwordingEnabled() &&
547 service
&& service
->HotwordEnabled()) {
548 HotwordService
* hotword_service
=
549 HotwordServiceFactory::GetForProfile(profile_
);
551 hotword_service
->StopHotwordSession(this);
555 void AppListViewDelegate::ShowForProfileByPath(
556 const base::FilePath
& profile_path
) {
557 controller_
->ShowForProfileByPath(profile_path
);
560 void AppListViewDelegate::OnSpeechResult(const base::string16
& result
,
562 speech_ui_
->SetSpeechResult(result
, is_final
);
564 auto_launch_timeout_
= base::TimeDelta::FromMilliseconds(
565 kAutoLaunchDefaultTimeoutMilliSec
);
566 model_
->search_box()->SetText(result
);
570 void AppListViewDelegate::OnSpeechSoundLevelChanged(int16 level
) {
571 speech_ui_
->UpdateSoundLevel(level
);
574 void AppListViewDelegate::OnSpeechRecognitionStateChanged(
575 app_list::SpeechRecognitionState new_state
) {
576 speech_ui_
->SetSpeechRecognitionState(new_state
);
578 app_list::StartPageService
* service
=
579 app_list::StartPageService::Get(profile_
);
580 // With the new hotword extension, we need to re-request hotwording after
581 // speech recognition has stopped. Do not request hotwording after the app
582 // list has already closed.
583 if (new_state
== app_list::SPEECH_RECOGNITION_READY
&&
584 HotwordService::IsExperimentalHotwordingEnabled() &&
585 service
&& service
->HotwordEnabled() &&
586 controller_
->GetAppListWindow()) {
587 HotwordService
* hotword_service
=
588 HotwordServiceFactory::GetForProfile(profile_
);
589 if (hotword_service
) {
590 hotword_service
->RequestHotwordSession(this);
595 #if defined(TOOLKIT_VIEWS)
596 views::View
* AppListViewDelegate::CreateStartPageWebView(
597 const gfx::Size
& size
) {
598 app_list::StartPageService
* service
=
599 app_list::StartPageService::Get(profile_
);
603 content::WebContents
* web_contents
= service
->GetStartPageContents();
607 DCHECK_EQ(profile_
, web_contents
->GetBrowserContext());
608 views::WebView
* web_view
= new views::WebView(
609 web_contents
->GetBrowserContext());
610 web_view
->SetPreferredSize(size
);
611 web_view
->SetWebContents(web_contents
);
615 std::vector
<views::View
*> AppListViewDelegate::CreateCustomPageWebViews(
616 const gfx::Size
& size
) {
617 std::vector
<views::View
*> web_views
;
619 for (ScopedVector
<apps::CustomLauncherPageContents
>::const_iterator it
=
620 custom_page_contents_
.begin();
621 it
!= custom_page_contents_
.end();
623 content::WebContents
* web_contents
= (*it
)->web_contents();
624 // TODO(mgiuca): DCHECK_EQ(profile_, web_contents->GetBrowserContext())
625 // after http://crbug.com/392763 resolved.
626 views::WebView
* web_view
=
627 new views::WebView(web_contents
->GetBrowserContext());
628 web_view
->SetPreferredSize(size
);
629 web_view
->SetWebContents(web_contents
);
630 web_views
.push_back(web_view
);
637 bool AppListViewDelegate::IsSpeechRecognitionEnabled() {
638 app_list::StartPageService
* service
=
639 app_list::StartPageService::Get(profile_
);
640 return service
&& service
->GetSpeechRecognitionContents();
643 const app_list::AppListViewDelegate::Users
&
644 AppListViewDelegate::GetUsers() const {
648 bool AppListViewDelegate::ShouldCenterWindow() const {
649 if (app_list::switches::IsCenteredAppListEnabled())
652 // keyboard depends upon Aura.
653 #if defined(USE_AURA)
654 // If the virtual keyboard is enabled, use the new app list position. The old
655 // position is too tall, and doesn't fit in the left-over screen space.
656 if (keyboard::IsKeyboardEnabled())
661 // If it is at all possible to enter maximize mode in this configuration
662 // (which has a virtual keyboard), we should use the experimental position.
663 // This avoids having the app list change shape and position as the user
664 // enters and exits maximize mode.
665 if (ash::Shell::HasInstance() &&
666 ash::Shell::GetInstance()
667 ->maximize_mode_controller()
668 ->CanEnterMaximizeMode()) {
676 void AppListViewDelegate::AddObserver(
677 app_list::AppListViewDelegateObserver
* observer
) {
678 observers_
.AddObserver(observer
);
681 void AppListViewDelegate::RemoveObserver(
682 app_list::AppListViewDelegateObserver
* observer
) {
683 observers_
.RemoveObserver(observer
);
686 void AppListViewDelegate::Observe(int type
,
687 const content::NotificationSource
& source
,
688 const content::NotificationDetails
& details
) {
690 case chrome::NOTIFICATION_APP_TERMINATING
:
692 app_list::AppListViewDelegateObserver
, observers_
, OnShutdown());
694 SetProfile(NULL
); // Ensures launcher page web contents are torn down.
696 // SigninManagerFactory is not a leaky singleton (unlike this class), and
697 // its destructor will check that it has no remaining observers.
698 scoped_observer_
.RemoveAll();
699 SigninManagerFactory::GetInstance()->RemoveObserver(this);