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