1// Copyright 2014 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/login/screens/controller_pairing_screen.h"
6
7#include "base/command_line.h"
8#include "base/values.h"
9#include "chrome/browser/chromeos/login/wizard_controller.h"
10#include "google_apis/gaia/gaia_auth_util.h"
11
12using namespace chromeos::controller_pairing;
13using namespace pairing_chromeos;
14
15namespace chromeos {
16
17ControllerPairingScreen::ControllerPairingScreen(
18    ScreenObserver* observer,
19    ControllerPairingScreenActor* actor,
20    ControllerPairingController* controller)
21    : WizardScreen(observer),
22      actor_(actor),
23      controller_(controller),
24      current_stage_(ControllerPairingController::STAGE_NONE),
25      device_preselected_(false) {
26  actor_->SetDelegate(this);
27  controller_->AddObserver(this);
28}
29
30ControllerPairingScreen::~ControllerPairingScreen() {
31  if (actor_)
32    actor_->SetDelegate(NULL);
33  controller_->RemoveObserver(this);
34}
35
36void ControllerPairingScreen::CommitContextChanges() {
37  if (!context_.HasChanges())
38    return;
39  base::DictionaryValue diff;
40  context_.GetChangesAndReset(&diff);
41  if (actor_)
42    actor_->OnContextChanged(diff);
43}
44
45bool ControllerPairingScreen::ExpectStageIs(Stage stage) const {
46  DCHECK(stage == current_stage_);
47  if (current_stage_ != stage)
48    LOG(ERROR) << "Incorrect stage. Expected: " << stage
49               << ", current stage: " << current_stage_;
50  return stage == current_stage_;
51}
52
53void ControllerPairingScreen::PrepareToShow() {
54}
55
56void ControllerPairingScreen::Show() {
57  if (actor_)
58    actor_->Show();
59  controller_->StartPairing();
60}
61
62void ControllerPairingScreen::Hide() {
63  if (actor_)
64    actor_->Hide();
65}
66
67std::string ControllerPairingScreen::GetName() const {
68  return WizardController::kControllerPairingScreenName;
69}
70
71// Overridden from ControllerPairingController::Observer:
72void ControllerPairingScreen::PairingStageChanged(Stage new_stage) {
73  DCHECK(new_stage != current_stage_);
74
75  std::string desired_page;
76  switch (new_stage) {
77    case ControllerPairingController::STAGE_DEVICES_DISCOVERY: {
78      desired_page = kPageDevicesDiscovery;
79      context_.SetStringList(kContextKeyDevices, StringList());
80      context_.SetString(kContextKeySelectedDevice, std::string());
81      device_preselected_ = false;
82      break;
83    }
84    case ControllerPairingController::STAGE_DEVICE_NOT_FOUND: {
85      desired_page = kPageDeviceNotFound;
86      break;
87    }
88    case ControllerPairingController::STAGE_ESTABLISHING_CONNECTION: {
89      desired_page = kPageEstablishingConnection;
90      break;
91    }
92    case ControllerPairingController::STAGE_ESTABLISHING_CONNECTION_ERROR: {
93      desired_page = kPageEstablishingConnectionError;
94      break;
95    }
96    case ControllerPairingController::STAGE_WAITING_FOR_CODE_CONFIRMATION: {
97      desired_page = kPageCodeConfirmation;
98      context_.SetString(kContextKeyConfirmationCode,
99                         controller_->GetConfirmationCode());
100      break;
101    }
102    case ControllerPairingController::STAGE_HOST_UPDATE_IN_PROGRESS: {
103      desired_page = kPageHostUpdate;
104      break;
105    }
106    case ControllerPairingController::STAGE_HOST_CONNECTION_LOST: {
107      desired_page = kPageHostConnectionLost;
108      break;
109    }
110    case ControllerPairingController::STAGE_WAITING_FOR_CREDENTIALS: {
111      controller_->RemoveObserver(this);
112      get_screen_observer()->OnExit(
113          WizardController::CONTROLLER_PAIRING_FINISHED);
114      // TODO: Move the rest of the stages to the proper location.
115      desired_page = kPageEnrollmentIntroduction;
116      break;
117    }
118    case ControllerPairingController::STAGE_HOST_ENROLLMENT_IN_PROGRESS: {
119      desired_page = kPageHostEnrollment;
120      break;
121    }
122    case ControllerPairingController::STAGE_HOST_ENROLLMENT_ERROR: {
123      desired_page = kPageHostEnrollmentError;
124      break;
125    }
126    case ControllerPairingController::STAGE_PAIRING_DONE: {
127      desired_page = kPagePairingDone;
128      break;
129    }
130    case ControllerPairingController::STAGE_FINISHED: {
131      get_screen_observer()->OnExit(
132          WizardController::CONTROLLER_PAIRING_FINISHED);
133      break;
134    }
135    default:
136      NOTREACHED();
137  }
138  current_stage_ = new_stage;
139  context_.SetString(kContextKeyPage, desired_page);
140  context_.SetBoolean(kContextKeyControlsDisabled, false);
141  CommitContextChanges();
142}
143
144void ControllerPairingScreen::DiscoveredDevicesListChanged() {
145  if (!ExpectStageIs(ControllerPairingController::STAGE_DEVICES_DISCOVERY))
146    return;
147  ControllerPairingController::DeviceIdList devices =
148      controller_->GetDiscoveredDevices();
149  std::sort(devices.begin(), devices.end());
150  context_.SetStringList(kContextKeyDevices, devices);
151  context_.SetString(
152      kContextKeyPage,
153      devices.empty() ? kPageDevicesDiscovery : kPageDeviceSelect);
154  std::string selected_device = context_.GetString(kContextKeySelectedDevice);
155  if (std::find(devices.begin(), devices.end(), selected_device) ==
156      devices.end()) {
157    selected_device.clear();
158  }
159  if (devices.empty()) {
160    device_preselected_ = false;
161  } else if (!device_preselected_) {
162    selected_device = devices.front();
163    device_preselected_ = true;
164  }
165  context_.SetString(kContextKeySelectedDevice, selected_device);
166  context_.SetBoolean(kContextKeyControlsDisabled, selected_device.empty());
167  CommitContextChanges();
168}
169
170void ControllerPairingScreen::OnActorDestroyed(
171    ControllerPairingScreenActor* actor) {
172  if (actor_ == actor)
173    actor_ = NULL;
174}
175
176// Overridden from ControllerPairingView::Delegate:
177void ControllerPairingScreen::OnUserActed(const std::string& action) {
178  if (context_.GetBoolean(kContextKeyControlsDisabled)) {
179    LOG(WARNING) << "User acted, but controls are disabled. Ignoring.";
180    return;
181  }
182  bool disable_controls = true;
183  if (action == kActionChooseDevice) {
184    std::string selectedDevice = context_.GetString(kContextKeySelectedDevice);
185    if (selectedDevice.empty())
186      LOG(ERROR) << "Device was not selected.";
187    else
188      controller_->ChooseDeviceForPairing(selectedDevice);
189  } else if (action == kActionRepeatDiscovery) {
190    controller_->RepeatDiscovery();
191  } else if (action == kActionAcceptCode) {
192    controller_->SetConfirmationCodeIsCorrect(true);
193  } else if (action == kActionRejectCode) {
194    controller_->SetConfirmationCodeIsCorrect(false);
195  } else if (action == kActionProceedToAuthentication) {
196    context_.SetString(kContextKeyPage, kPageAuthentication);
197    disable_controls = false;
198  } else if (action == kActionEnroll) {
199    const std::string account_id =
200        gaia::SanitizeEmail(context_.GetString(kContextKeyAccountId));
201    const std::string domain(gaia::ExtractDomainName(account_id));
202    context_.SetString(kContextKeyEnrollmentDomain, domain);
203  } else if (action == kActionStartSession) {
204    controller_->StartSession();
205  }
206  context_.SetBoolean(kContextKeyControlsDisabled, disable_controls);
207  CommitContextChanges();
208}
209
210void ControllerPairingScreen::OnScreenContextChanged(
211    const base::DictionaryValue& diff) {
212  std::vector<std::string> changedKeys;
213  context_.ApplyChanges(diff, &changedKeys);
214  for (std::vector<std::string>::const_iterator key = changedKeys.begin();
215       key != changedKeys.end();
216       ++key) {
217    if (*key == kContextKeySelectedDevice) {
218      context_.SetBoolean(kContextKeyControlsDisabled,
219                          context_.GetString(*key).empty());
220      CommitContextChanges();
221    }
222  }
223}
224
225}  // namespace chromeos
226