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/signin/easy_unlock_screenlock_state_handler.h"
6
7#include "base/bind.h"
8#include "base/strings/string16.h"
9#include "base/strings/utf_string_conversions.h"
10#include "chrome/browser/browser_process.h"
11#include "chrome/browser/chromeos/chromeos_utils.h"
12#include "chrome/grit/generated_resources.h"
13#include "ui/base/l10n/l10n_util.h"
14
15namespace {
16
17ScreenlockBridge::UserPodCustomIcon GetIconForState(
18    EasyUnlockScreenlockStateHandler::State state) {
19  switch (state) {
20    case EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH:
21    case EasyUnlockScreenlockStateHandler::STATE_NO_PHONE:
22    case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_AUTHENTICATED:
23    case EasyUnlockScreenlockStateHandler::STATE_PHONE_LOCKED:
24    case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY:
25    case EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE:
26    case EasyUnlockScreenlockStateHandler::STATE_PHONE_UNSUPPORTED:
27      return ScreenlockBridge::USER_POD_CUSTOM_ICON_LOCKED;
28    case EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING:
29      return ScreenlockBridge::USER_POD_CUSTOM_ICON_SPINNER;
30    case EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED:
31      return ScreenlockBridge::USER_POD_CUSTOM_ICON_UNLOCKED;
32    default:
33      return ScreenlockBridge::USER_POD_CUSTOM_ICON_NONE;
34  }
35}
36
37bool HardlockOnClick(EasyUnlockScreenlockStateHandler::State state) {
38  return state != EasyUnlockScreenlockStateHandler::STATE_INACTIVE;
39}
40
41size_t GetTooltipResourceId(EasyUnlockScreenlockStateHandler::State state) {
42  switch (state) {
43    case EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH:
44      return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_NO_BLUETOOTH;
45    case EasyUnlockScreenlockStateHandler::STATE_NO_PHONE:
46      return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_NO_PHONE;
47    case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_AUTHENTICATED:
48      return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_NOT_AUTHENTICATED;
49    case EasyUnlockScreenlockStateHandler::STATE_PHONE_LOCKED:
50      return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_LOCKED;
51    case EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE:
52      return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_UNLOCKABLE;
53    case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY:
54      return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_NOT_NEARBY;
55    case EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED:
56      return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_INSTRUCTIONS;
57    case EasyUnlockScreenlockStateHandler::STATE_PHONE_UNSUPPORTED:
58      return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_UNSUPPORTED_ANDROID_VERSION;
59    default:
60      return 0;
61  }
62}
63
64bool TooltipContainsDeviceType(EasyUnlockScreenlockStateHandler::State state) {
65  return state == EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED ||
66         state == EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE ||
67         state == EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH ||
68         state == EasyUnlockScreenlockStateHandler::STATE_PHONE_UNSUPPORTED;
69}
70
71bool IsLocaleEnUS() {
72  return g_browser_process->GetApplicationLocale() == "en-US";
73}
74
75}  // namespace
76
77
78EasyUnlockScreenlockStateHandler::EasyUnlockScreenlockStateHandler(
79    const std::string& user_email,
80    HardlockState initial_hardlock_state,
81    ScreenlockBridge* screenlock_bridge)
82    : state_(STATE_INACTIVE),
83      user_email_(user_email),
84      screenlock_bridge_(screenlock_bridge),
85      hardlock_state_(initial_hardlock_state),
86      hardlock_ui_shown_(false),
87      is_trial_run_(false) {
88  DCHECK(screenlock_bridge_);
89  screenlock_bridge_->AddObserver(this);
90}
91
92EasyUnlockScreenlockStateHandler::~EasyUnlockScreenlockStateHandler() {
93  screenlock_bridge_->RemoveObserver(this);
94  // Make sure the screenlock state set by this gets cleared.
95  ChangeState(STATE_INACTIVE);
96}
97
98bool EasyUnlockScreenlockStateHandler::IsActive() const {
99  return state_ != STATE_INACTIVE;
100}
101
102void EasyUnlockScreenlockStateHandler::ChangeState(State new_state) {
103  if (state_ == new_state)
104    return;
105
106  state_ = new_state;
107
108  // If lock screen is not active or it forces offline password, just cache the
109  // current state. The screenlock state will get refreshed in |ScreenDidLock|.
110  if (!screenlock_bridge_->IsLocked())
111    return;
112
113  // No hardlock UI for trial run.
114  if (!is_trial_run_ && hardlock_state_ != NO_HARDLOCK) {
115    ShowHardlockUI();
116    return;
117  }
118
119  UpdateScreenlockAuthType();
120
121  ScreenlockBridge::UserPodCustomIcon icon = GetIconForState(state_);
122
123  if (icon == ScreenlockBridge::USER_POD_CUSTOM_ICON_NONE) {
124    screenlock_bridge_->lock_handler()->HideUserPodCustomIcon(user_email_);
125    return;
126  }
127
128  ScreenlockBridge::UserPodCustomIconOptions icon_options;
129  icon_options.SetIcon(icon);
130
131  // Don't hardlock on trial run.
132  if (!is_trial_run_ && HardlockOnClick(state_))
133    icon_options.SetHardlockOnClick();
134
135  UpdateTooltipOptions(is_trial_run_, &icon_options);
136
137  // For states without tooltips, we still need to set an accessibility label.
138  if (state_ == EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING) {
139    icon_options.SetAriaLabel(
140        l10n_util::GetStringUTF16(IDS_SMART_LOCK_SPINNER_ACCESSIBILITY_LABEL));
141  }
142
143  screenlock_bridge_->lock_handler()->ShowUserPodCustomIcon(user_email_,
144                                                            icon_options);
145}
146
147void EasyUnlockScreenlockStateHandler::SetHardlockState(
148    HardlockState new_state) {
149  if (hardlock_state_ == new_state)
150    return;
151
152  hardlock_state_ = new_state;
153
154  // If hardlock_state_ was set to NO_HARDLOCK, this means the screen is about
155  // to get unlocked. No need to update it in this case.
156  if (hardlock_state_ != NO_HARDLOCK) {
157    hardlock_ui_shown_ = false;
158
159    RefreshScreenlockState();
160  }
161}
162
163void EasyUnlockScreenlockStateHandler::MaybeShowHardlockUI() {
164  if (hardlock_state_ != NO_HARDLOCK)
165    ShowHardlockUI();
166}
167
168void EasyUnlockScreenlockStateHandler::SetTrialRun() {
169  if (is_trial_run_)
170    return;
171  is_trial_run_ = true;
172  RefreshScreenlockState();
173}
174
175void EasyUnlockScreenlockStateHandler::OnScreenDidLock() {
176  RefreshScreenlockState();
177}
178
179void EasyUnlockScreenlockStateHandler::OnScreenDidUnlock() {
180  hardlock_ui_shown_ = false;
181  is_trial_run_ = false;
182}
183
184void EasyUnlockScreenlockStateHandler::OnFocusedUserChanged(
185    const std::string& user_id) {
186}
187
188void EasyUnlockScreenlockStateHandler::RefreshScreenlockState() {
189  State last_state = state_;
190  // This should force updating screenlock state.
191  state_ = STATE_INACTIVE;
192  ChangeState(last_state);
193}
194
195void EasyUnlockScreenlockStateHandler::ShowHardlockUI() {
196  DCHECK(hardlock_state_ != NO_HARDLOCK);
197
198  if (!screenlock_bridge_->IsLocked())
199    return;
200
201  // Do not override online signin.
202  const ScreenlockBridge::LockHandler::AuthType existing_auth_type =
203      screenlock_bridge_->lock_handler()->GetAuthType(user_email_);
204  if (existing_auth_type == ScreenlockBridge::LockHandler::ONLINE_SIGN_IN)
205    return;
206
207  if (existing_auth_type != ScreenlockBridge::LockHandler::OFFLINE_PASSWORD) {
208    screenlock_bridge_->lock_handler()->SetAuthType(
209        user_email_,
210        ScreenlockBridge::LockHandler::OFFLINE_PASSWORD,
211        base::string16());
212  }
213
214  if (hardlock_state_ == NO_PAIRING) {
215    screenlock_bridge_->lock_handler()->HideUserPodCustomIcon(user_email_);
216    hardlock_ui_shown_ = false;
217    return;
218  }
219
220  if (hardlock_ui_shown_)
221    return;
222
223  ScreenlockBridge::UserPodCustomIconOptions icon_options;
224  icon_options.SetIcon(ScreenlockBridge::USER_POD_CUSTOM_ICON_HARDLOCKED);
225
226  // TODO(tbarzic): Remove this condition for M-40.
227  if (IsLocaleEnUS()) {
228    base::string16 tooltip;
229    if (hardlock_state_ == USER_HARDLOCK) {
230      tooltip = l10n_util::GetStringFUTF16(
231          IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_USER, GetDeviceName());
232    } else if (hardlock_state_ == PAIRING_CHANGED) {
233      tooltip = l10n_util::GetStringUTF16(
234          IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_PAIRING_CHANGED);
235    } else if (hardlock_state_ == PAIRING_ADDED) {
236      tooltip = l10n_util::GetStringUTF16(
237          IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_PAIRING_ADDED);
238    } else {
239      LOG(ERROR) << "Unknown hardlock state " << hardlock_state_;
240    }
241    icon_options.SetTooltip(tooltip, true /* autoshow */);
242  }
243
244  screenlock_bridge_->lock_handler()->ShowUserPodCustomIcon(user_email_,
245                                                            icon_options);
246  hardlock_ui_shown_ = true;
247}
248
249void EasyUnlockScreenlockStateHandler::UpdateTooltipOptions(
250    bool trial_run,
251    ScreenlockBridge::UserPodCustomIconOptions* icon_options) {
252  size_t resource_id = 0;
253  base::string16 device_name;
254  if (trial_run && state_ == STATE_AUTHENTICATED) {
255    // TODO(tbarzic): Remove this condition for M-40 branch.
256    if (IsLocaleEnUS())
257      resource_id = IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_INITIAL_AUTHENTICATED;
258    else
259      resource_id = IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_TUTORIAL;
260  } else {
261    resource_id = GetTooltipResourceId(state_);
262    if (TooltipContainsDeviceType(state_))
263      device_name = GetDeviceName();
264  }
265
266  if (!resource_id)
267    return;
268
269  base::string16 tooltip;
270  if (device_name.empty()) {
271    tooltip = l10n_util::GetStringUTF16(resource_id);
272  } else {
273    tooltip = l10n_util::GetStringFUTF16(resource_id, device_name);
274  }
275
276  if (tooltip.empty())
277    return;
278
279  icon_options->SetTooltip(tooltip, trial_run /* autoshow tooltip */);
280}
281
282base::string16 EasyUnlockScreenlockStateHandler::GetDeviceName() {
283#if defined(OS_CHROMEOS)
284  return chromeos::GetChromeDeviceType();
285#else
286  // TODO(tbarzic): Figure out the name for non Chrome OS case.
287  return base::ASCIIToUTF16("Chrome");
288#endif
289}
290
291void EasyUnlockScreenlockStateHandler::UpdateScreenlockAuthType() {
292  if (!is_trial_run_ && hardlock_state_ != NO_HARDLOCK)
293    return;
294
295  // Do not override online signin.
296  const ScreenlockBridge::LockHandler::AuthType existing_auth_type =
297      screenlock_bridge_->lock_handler()->GetAuthType(user_email_);
298  if (existing_auth_type == ScreenlockBridge::LockHandler::ONLINE_SIGN_IN)
299    return;
300
301  if (state_ == STATE_AUTHENTICATED) {
302    if (existing_auth_type != ScreenlockBridge::LockHandler::USER_CLICK) {
303      screenlock_bridge_->lock_handler()->SetAuthType(
304          user_email_,
305          ScreenlockBridge::LockHandler::USER_CLICK,
306          l10n_util::GetStringUTF16(
307              IDS_EASY_UNLOCK_SCREENLOCK_USER_POD_AUTH_VALUE));
308    }
309  } else if (existing_auth_type !=
310             ScreenlockBridge::LockHandler::OFFLINE_PASSWORD) {
311    screenlock_bridge_->lock_handler()->SetAuthType(
312        user_email_,
313        ScreenlockBridge::LockHandler::OFFLINE_PASSWORD,
314        base::string16());
315  }
316}
317