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/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 28 void RecordCompletion(chromeos::first_run::TutorialCompletion type) { 29 UMA_HISTOGRAM_ENUMERATION("CrosFirstRun.TutorialCompletion", 30 type, 31 chromeos::first_run::kTutorialCompletionSize); 32 } 33 34 } // namespace 35 36 namespace chromeos { 37 38 FirstRunController::~FirstRunController() {} 39 40 // static 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 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 61 FirstRunController* FirstRunController::GetInstanceForTest() { 62 return g_instance; 63 } 64 65 FirstRunController::FirstRunController() 66 : actor_(NULL), 67 current_step_index_(NONE_STEP_INDEX), 68 user_profile_(NULL) { 69 } 70 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 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 111 void FirstRunController::OnActorInitialized() { 112 RegisterSteps(); 113 ShowNextStep(); 114 } 115 116 void FirstRunController::OnNextButtonClicked(const std::string& step_name) { 117 DCHECK(GetCurrentStep() && GetCurrentStep()->name() == step_name); 118 GetCurrentStep()->OnBeforeHide(); 119 actor_->HideCurrentStep(); 120 } 121 122 void FirstRunController::OnHelpButtonClicked() { 123 RecordCompletion(first_run::kTutorialCompletedWithKeepExploring); 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 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 138 void FirstRunController::OnStepShown(const std::string& step_name) { 139 DCHECK(GetCurrentStep() && GetCurrentStep()->name() == step_name); 140 } 141 142 void FirstRunController::OnActorFinalized() { 143 if (!on_actor_finalized_.is_null()) 144 on_actor_finalized_.Run(); 145 Stop(); 146 } 147 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 155 void FirstRunController::OnCancelled() { 156 RecordCompletion(first_run::kTutorialNotFinished); 157 Stop(); 158 } 159 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 169 void FirstRunController::ShowNextStep() { 170 AdvanceStep(); 171 if (!GetCurrentStep()) { 172 actor_->Finalize(); 173 RecordCompletion(first_run::kTutorialCompletedWithGotIt); 174 return; 175 } 176 GetCurrentStep()->Show(); 177 } 178 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 188 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