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/chromeos/first_run/first_run_controller.h"
6
7 #include "ash/shell.h"
8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram.h"
11 #include "chrome/browser/chromeos/first_run/first_run_view.h"
12 #include "chrome/browser/chromeos/first_run/metrics.h"
13 #include "chrome/browser/chromeos/first_run/steps/app_list_step.h"
14 #include "chrome/browser/chromeos/first_run/steps/help_step.h"
15 #include "chrome/browser/chromeos/first_run/steps/tray_step.h"
16 #include "chrome/browser/chromeos/login/users/user_manager.h"
17 #include "chrome/browser/ui/chrome_pages.h"
18 #include "ui/views/widget/widget.h"
19
20 namespace {
21
22 size_t NONE_STEP_INDEX = std::numeric_limits<size_t>::max();
23
24 // Instance of currently running controller, or NULL if controller is not
25 // running now.
26 chromeos::FirstRunController* g_instance;
27
RecordCompletion(chromeos::first_run::TutorialCompletion type)28 void RecordCompletion(chromeos::first_run::TutorialCompletion type) {
29 UMA_HISTOGRAM_ENUMERATION("CrosFirstRun.TutorialCompletion",
30 type,
31 chromeos::first_run::TUTORIAL_COMPLETION_SIZE);
32 }
33
34 } // namespace
35
36 namespace chromeos {
37
~FirstRunController()38 FirstRunController::~FirstRunController() {}
39
40 // static
Start()41 void FirstRunController::Start() {
42 if (g_instance) {
43 LOG(WARNING) << "First-run tutorial is running already.";
44 return;
45 }
46 g_instance = new FirstRunController();
47 g_instance->Init();
48 }
49
50 // static
Stop()51 void FirstRunController::Stop() {
52 if (!g_instance) {
53 LOG(WARNING) << "First-run tutorial is not running.";
54 return;
55 }
56 g_instance->Finalize();
57 base::MessageLoop::current()->DeleteSoon(FROM_HERE, g_instance);
58 g_instance = NULL;
59 }
60
GetInstanceForTest()61 FirstRunController* FirstRunController::GetInstanceForTest() {
62 return g_instance;
63 }
64
FirstRunController()65 FirstRunController::FirstRunController()
66 : actor_(NULL),
67 current_step_index_(NONE_STEP_INDEX),
68 user_profile_(NULL) {
69 }
70
Init()71 void FirstRunController::Init() {
72 start_time_ = base::Time::Now();
73 UserManager* user_manager = UserManager::Get();
74 user_profile_ = user_manager->GetProfileByUser(user_manager->GetActiveUser());
75
76 shell_helper_.reset(ash::Shell::GetInstance()->CreateFirstRunHelper());
77 shell_helper_->AddObserver(this);
78
79 FirstRunView* view = new FirstRunView();
80 view->Init(user_profile_);
81 shell_helper_->GetOverlayWidget()->SetContentsView(view);
82 actor_ = view->GetActor();
83 actor_->set_delegate(this);
84 shell_helper_->GetOverlayWidget()->Show();
85 view->RequestFocus();
86 web_contents_for_tests_ = view->GetWebContents();
87
88 if (actor_->IsInitialized())
89 OnActorInitialized();
90 }
91
Finalize()92 void FirstRunController::Finalize() {
93 int furthest_step = current_step_index_ == NONE_STEP_INDEX
94 ? steps_.size() - 1
95 : current_step_index_;
96 UMA_HISTOGRAM_ENUMERATION("CrosFirstRun.FurthestStep",
97 furthest_step,
98 steps_.size());
99 UMA_HISTOGRAM_MEDIUM_TIMES("CrosFirstRun.TimeSpent",
100 base::Time::Now() - start_time_);
101 if (GetCurrentStep())
102 GetCurrentStep()->OnBeforeHide();
103 steps_.clear();
104 if (actor_)
105 actor_->set_delegate(NULL);
106 actor_ = NULL;
107 shell_helper_->RemoveObserver(this);
108 shell_helper_.reset();
109 }
110
OnActorInitialized()111 void FirstRunController::OnActorInitialized() {
112 RegisterSteps();
113 ShowNextStep();
114 }
115
OnNextButtonClicked(const std::string & step_name)116 void FirstRunController::OnNextButtonClicked(const std::string& step_name) {
117 DCHECK(GetCurrentStep() && GetCurrentStep()->name() == step_name);
118 GetCurrentStep()->OnBeforeHide();
119 actor_->HideCurrentStep();
120 }
121
OnHelpButtonClicked()122 void FirstRunController::OnHelpButtonClicked() {
123 RecordCompletion(first_run::TUTORIAL_COMPLETED_WITH_KEEP_EXPLORING);
124 on_actor_finalized_ = base::Bind(chrome::ShowHelpForProfile,
125 user_profile_,
126 chrome::HOST_DESKTOP_TYPE_ASH,
127 chrome::HELP_SOURCE_MENU);
128 actor_->Finalize();
129 }
130
OnStepHidden(const std::string & step_name)131 void FirstRunController::OnStepHidden(const std::string& step_name) {
132 DCHECK(GetCurrentStep() && GetCurrentStep()->name() == step_name);
133 GetCurrentStep()->OnAfterHide();
134 if (!actor_->IsFinalizing())
135 ShowNextStep();
136 }
137
OnStepShown(const std::string & step_name)138 void FirstRunController::OnStepShown(const std::string& step_name) {
139 DCHECK(GetCurrentStep() && GetCurrentStep()->name() == step_name);
140 }
141
OnActorFinalized()142 void FirstRunController::OnActorFinalized() {
143 if (!on_actor_finalized_.is_null())
144 on_actor_finalized_.Run();
145 Stop();
146 }
147
OnActorDestroyed()148 void FirstRunController::OnActorDestroyed() {
149 // Normally this shouldn't happen because we are implicitly controlling
150 // actor's lifetime.
151 NOTREACHED() <<
152 "FirstRunActor destroyed before FirstRunController::Finalize.";
153 }
154
OnCancelled()155 void FirstRunController::OnCancelled() {
156 RecordCompletion(first_run::TUTORIAL_NOT_FINISHED);
157 Stop();
158 }
159
RegisterSteps()160 void FirstRunController::RegisterSteps() {
161 steps_.push_back(make_linked_ptr(
162 new first_run::AppListStep(shell_helper_.get(), actor_)));
163 steps_.push_back(make_linked_ptr(
164 new first_run::TrayStep(shell_helper_.get(), actor_)));
165 steps_.push_back(make_linked_ptr(
166 new first_run::HelpStep(shell_helper_.get(), actor_)));
167 }
168
ShowNextStep()169 void FirstRunController::ShowNextStep() {
170 AdvanceStep();
171 if (!GetCurrentStep()) {
172 actor_->Finalize();
173 RecordCompletion(first_run::TUTORIAL_COMPLETED_WITH_GOT_IT);
174 return;
175 }
176 GetCurrentStep()->Show();
177 }
178
AdvanceStep()179 void FirstRunController::AdvanceStep() {
180 if (current_step_index_ == NONE_STEP_INDEX)
181 current_step_index_ = 0;
182 else
183 ++current_step_index_;
184 if (current_step_index_ >= steps_.size())
185 current_step_index_ = NONE_STEP_INDEX;
186 }
187
GetCurrentStep() const188 first_run::Step* FirstRunController::GetCurrentStep() const {
189 return current_step_index_ != NONE_STEP_INDEX ?
190 steps_[current_step_index_].get() : NULL;
191 }
192
193 } // namespace chromeos
194
195