summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortbarzic@chromium.org <tbarzic@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-15 04:40:54 +0000
committertbarzic@chromium.org <tbarzic@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-15 04:40:54 +0000
commit7f8aa2a1eaf6808d64ee5785a20ee6706673fb5d (patch)
tree344553c549adce05ec46ebdb05a95479e92bcc4c
parent251a91806256dca9ef6bcdf1f651ba7984566e93 (diff)
downloadchromium_src-7f8aa2a1eaf6808d64ee5785a20ee6706673fb5d.zip
chromium_src-7f8aa2a1eaf6808d64ee5785a20ee6706673fb5d.tar.gz
chromium_src-7f8aa2a1eaf6808d64ee5785a20ee6706673fb5d.tar.bz2
Hook up new easyUnlockPrivate API function for updating lock screen
This will replace screenlockPrivate methods for updating screenlock icon, auth type, and message. The cl starts migration of the Easy Unlocking logic from Chrome app to native code. The new easyUnlockPrivate function should be removed once the migration is complete (probably in M39). It moves resources used on screen lock from Easy Unlock app to Chrome, and simplifies generating icon URL to be used in the screenlock web ui. Adds support for setting icon tooltip and animating the icon directly in screenlock web ui (instead of calling showCustomIcon for each animation frame, which can produce somewhat laggy animation). BUG=393792,397366,397360,385719, 402106 Review URL: https://codereview.chromium.org/446743003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289784 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc56
-rw-r--r--chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h25
-rw-r--r--chrome/browser/extensions/api/screenlock_private/screenlock_private_api.cc15
-rw-r--r--chrome/browser/signin/easy_unlock_screenlock_state_handler.cc211
-rw-r--r--chrome/browser/signin/easy_unlock_screenlock_state_handler.h91
-rw-r--r--chrome/browser/signin/easy_unlock_service.cc29
-rw-r--r--chrome/browser/signin/easy_unlock_service.h9
-rw-r--r--chrome/browser/signin/screenlock_bridge.cc104
-rw-r--r--chrome/browser/signin/screenlock_bridge.h75
-rw-r--r--chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc43
-rw-r--r--chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h11
-rw-r--r--chrome/browser/ui/webui/signin/user_manager_screen_handler.cc20
-rw-r--r--chrome/browser/ui/webui/signin/user_manager_screen_handler.h9
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chrome/common/extensions/api/easy_unlock_private.idl30
-rw-r--r--extensions/browser/extension_function_histogram_value.h1
-rw-r--r--tools/metrics/histograms/histograms.xml1
-rw-r--r--ui/login/account_picker/screen_account_picker.js18
-rw-r--r--ui/login/account_picker/user_pod_row.css13
-rw-r--r--ui/login/account_picker/user_pod_row.js386
-rw-r--r--ui/login/account_picker/user_pod_template.html2
21 files changed, 1039 insertions, 112 deletions
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
index a7f1ce6..1b7f87e 100644
--- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
+++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.cc
@@ -10,6 +10,9 @@
#include "base/values.h"
#include "chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_bluetooth_util.h"
#include "chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_crypto_delegate.h"
+#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/easy_unlock_screenlock_state_handler.h"
+#include "chrome/browser/signin/easy_unlock_service.h"
#include "chrome/common/extensions/api/easy_unlock_private.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "grit/generated_resources.h"
@@ -34,6 +37,36 @@ EasyUnlockPrivateCryptoDelegate* GetCryptoDelegate(
->crypto_delegate();
}
+EasyUnlockScreenlockStateHandler* GetScreenlockStateHandler(
+ content::BrowserContext* context) {
+ return EasyUnlockService::Get(Profile::FromBrowserContext(context))
+ ->GetScreenlockStateHandler();
+}
+
+EasyUnlockScreenlockStateHandler::State ToScreenlockStateHandlerState(
+ easy_unlock_private::State state) {
+ switch (state) {
+ case easy_unlock_private::STATE_NO_BLUETOOTH:
+ return EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH;
+ case easy_unlock_private::STATE_BLUETOOTH_CONNECTING:
+ return EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING;
+ case easy_unlock_private::STATE_NO_PHONE:
+ return EasyUnlockScreenlockStateHandler::STATE_NO_PHONE;
+ case easy_unlock_private::STATE_PHONE_NOT_AUTHENTICATED:
+ return EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_AUTHENTICATED;
+ case easy_unlock_private::STATE_PHONE_LOCKED:
+ return EasyUnlockScreenlockStateHandler::STATE_PHONE_LOCKED;
+ case easy_unlock_private::STATE_PHONE_UNLOCKABLE:
+ return EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE;
+ case easy_unlock_private::STATE_PHONE_NOT_NEARBY:
+ return EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY;
+ case easy_unlock_private::STATE_AUTHENTICATED:
+ return EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED;
+ default:
+ return EasyUnlockScreenlockStateHandler::STATE_INACTIVE;
+ }
+}
+
} // namespace
// static
@@ -368,5 +401,28 @@ void EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction::OnSeekCompleted(
}
}
+EasyUnlockPrivateUpdateScreenlockStateFunction::
+ EasyUnlockPrivateUpdateScreenlockStateFunction() {}
+
+EasyUnlockPrivateUpdateScreenlockStateFunction::
+ ~EasyUnlockPrivateUpdateScreenlockStateFunction() {}
+
+bool EasyUnlockPrivateUpdateScreenlockStateFunction::RunSync() {
+ scoped_ptr<easy_unlock_private::UpdateScreenlockState::Params> params(
+ easy_unlock_private::UpdateScreenlockState::Params::Create(*args_));
+ EXTENSION_FUNCTION_VALIDATE(params.get());
+
+ EasyUnlockScreenlockStateHandler* screenlock_state_handler =
+ GetScreenlockStateHandler(browser_context());
+ if (screenlock_state_handler) {
+ screenlock_state_handler->ChangeState(
+ ToScreenlockStateHandlerState(params->state));
+ return true;
+ }
+
+ SetError("Not allowed");
+ return false;
+}
+
} // namespace api
} // namespace extensions
diff --git a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h
index de47c5e..3802861 100644
--- a/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h
+++ b/chrome/browser/extensions/api/easy_unlock_private/easy_unlock_private_api.h
@@ -81,6 +81,8 @@ class EasyUnlockPrivatePerformECDHKeyAgreementFunction
DECLARE_EXTENSION_FUNCTION("easyUnlockPrivate.performECDHKeyAgreement",
EASYUNLOCKPRIVATE_PERFORMECDHKEYAGREEMENT)
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivatePerformECDHKeyAgreementFunction);
};
class EasyUnlockPrivateGenerateEcP256KeyPairFunction
@@ -99,6 +101,8 @@ class EasyUnlockPrivateGenerateEcP256KeyPairFunction
DECLARE_EXTENSION_FUNCTION("easyUnlockPrivate.generateEcP256KeyPair",
EASYUNLOCKPRIVATE_GENERATEECP256KEYPAIR)
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivateGenerateEcP256KeyPairFunction);
};
class EasyUnlockPrivateCreateSecureMessageFunction
@@ -116,6 +120,8 @@ class EasyUnlockPrivateCreateSecureMessageFunction
DECLARE_EXTENSION_FUNCTION("easyUnlockPrivate.createSecureMessage",
EASYUNLOCKPRIVATE_CREATESECUREMESSAGE)
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivateCreateSecureMessageFunction);
};
class EasyUnlockPrivateUnwrapSecureMessageFunction
@@ -133,6 +139,8 @@ class EasyUnlockPrivateUnwrapSecureMessageFunction
DECLARE_EXTENSION_FUNCTION("easyUnlockPrivate.unwrapSecureMessage",
EASYUNLOCKPRIVATE_UNWRAPSECUREMESSAGE)
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivateUnwrapSecureMessageFunction);
};
class EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction
@@ -155,6 +163,23 @@ class EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction
EasyUnlockPrivateSeekBluetoothDeviceByAddressFunction);
};
+class EasyUnlockPrivateUpdateScreenlockStateFunction
+ : public SyncExtensionFunction {
+ public:
+ EasyUnlockPrivateUpdateScreenlockStateFunction();
+
+ protected:
+ virtual ~EasyUnlockPrivateUpdateScreenlockStateFunction();
+
+ virtual bool RunSync() OVERRIDE;
+
+ private:
+ DECLARE_EXTENSION_FUNCTION("easyUnlockPrivate.updateScreenlockState",
+ EASYUNLOCKPRIVATE_UPDATESCREENLOCKSTATE)
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockPrivateUpdateScreenlockStateFunction);
+};
+
} // namespace api
} // namespace extensions
diff --git a/chrome/browser/extensions/api/screenlock_private/screenlock_private_api.cc b/chrome/browser/extensions/api/screenlock_private/screenlock_private_api.cc
index a51aca6..eb7b6e2 100644
--- a/chrome/browser/extensions/api/screenlock_private/screenlock_private_api.cc
+++ b/chrome/browser/extensions/api/screenlock_private/screenlock_private_api.cc
@@ -7,6 +7,7 @@
#include <vector>
#include "base/lazy_instance.h"
+#include "base/strings/utf_string_conversions.h"
#include "base/values.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/extensions/api/screenlock_private.h"
@@ -100,7 +101,7 @@ bool ScreenlockPrivateShowMessageFunction::RunAsync() {
ScreenlockBridge::LockHandler* locker =
ScreenlockBridge::Get()->lock_handler();
if (locker)
- locker->ShowBannerMessage(params->message);
+ locker->ShowBannerMessage(base::UTF8ToUTF16(params->message));
SendResponse(error_.empty());
return true;
}
@@ -168,9 +169,17 @@ void ScreenlockPrivateShowCustomIconFunction::OnImageLoaded(
const gfx::Image& image) {
ScreenlockBridge::LockHandler* locker =
ScreenlockBridge::Get()->lock_handler();
+ if (!locker) {
+ SetError(kNotLockedError);
+ SendResponse(false);
+ return;
+ }
+
+ ScreenlockBridge::UserPodCustomIconOptions icon;
+ icon.SetIconAsImage(image);
locker->ShowUserPodCustomIcon(
ScreenlockBridge::GetAuthenticatedUserEmail(GetProfile()),
- image);
+ icon);
SendResponse(error_.empty());
}
@@ -212,7 +221,7 @@ bool ScreenlockPrivateSetAuthTypeFunction::RunAsync() {
locker->SetAuthType(
ScreenlockBridge::GetAuthenticatedUserEmail(GetProfile()),
ToLockHandlerAuthType(params->auth_type),
- initial_value);
+ base::UTF8ToUTF16(initial_value));
} else {
SetError(kNotLockedError);
}
diff --git a/chrome/browser/signin/easy_unlock_screenlock_state_handler.cc b/chrome/browser/signin/easy_unlock_screenlock_state_handler.cc
new file mode 100644
index 0000000..13816fc
--- /dev/null
+++ b/chrome/browser/signin/easy_unlock_screenlock_state_handler.cc
@@ -0,0 +1,211 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/signin/easy_unlock_screenlock_state_handler.h"
+
+#include "base/bind.h"
+#include "base/prefs/pref_service.h"
+#include "base/strings/string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "chrome/browser/chromeos/chromeos_utils.h"
+#include "chrome/common/pref_names.h"
+#include "grit/generated_resources.h"
+#include "ui/base/l10n/l10n_util.h"
+
+namespace {
+
+size_t kIconSize = 27u;
+size_t kOpaqueIconOpacity = 50u;
+size_t kSpinnerResourceWidth = 1215u;
+size_t kSpinnerIntervalMs = 50u;
+
+std::string GetIconURLForState(EasyUnlockScreenlockStateHandler::State state) {
+ switch (state) {
+ case EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH:
+ case EasyUnlockScreenlockStateHandler::STATE_NO_PHONE:
+ case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_AUTHENTICATED:
+ case EasyUnlockScreenlockStateHandler::STATE_PHONE_LOCKED:
+ case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY:
+ case EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE:
+ return "chrome://theme/IDR_EASY_UNLOCK_LOCKED";
+ case EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING:
+ return "chrome://theme/IDR_EASY_UNLOCK_SPINNER";
+ case EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED:
+ return "chrome://theme/IDR_EASY_UNLOCK_UNLOCKED";
+ default:
+ return "";
+ }
+}
+
+bool UseOpaqueIcon(EasyUnlockScreenlockStateHandler::State state) {
+ return state == EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH ||
+ state == EasyUnlockScreenlockStateHandler::STATE_NO_PHONE ||
+ state == EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY ||
+ state == EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE;
+}
+
+bool HasAnimation(EasyUnlockScreenlockStateHandler::State state) {
+ return state == EasyUnlockScreenlockStateHandler::STATE_BLUETOOTH_CONNECTING;
+}
+
+size_t GetTooltipResourceId(EasyUnlockScreenlockStateHandler::State state) {
+ switch (state) {
+ case EasyUnlockScreenlockStateHandler::STATE_NO_BLUETOOTH:
+ return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_NO_BLUETOOTH;
+ case EasyUnlockScreenlockStateHandler::STATE_NO_PHONE:
+ return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_NO_PHONE;
+ case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_AUTHENTICATED:
+ return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_NOT_AUTHENTICATED;
+ case EasyUnlockScreenlockStateHandler::STATE_PHONE_LOCKED:
+ return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_LOCKED;
+ case EasyUnlockScreenlockStateHandler::STATE_PHONE_UNLOCKABLE:
+ return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_UNLOCKABLE;
+ case EasyUnlockScreenlockStateHandler::STATE_PHONE_NOT_NEARBY:
+ return IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_PHONE_NOT_NEARBY;
+ case EasyUnlockScreenlockStateHandler::STATE_AUTHENTICATED:
+ // TODO(tbarzic): When hard lock is enabled change this to
+ // IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_HARDLOCK_INSTRUCTIONS.
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+} // namespace
+
+
+EasyUnlockScreenlockStateHandler::EasyUnlockScreenlockStateHandler(
+ const std::string& user_email,
+ PrefService* pref_service,
+ ScreenlockBridge* screenlock_bridge)
+ : state_(STATE_INACTIVE),
+ user_email_(user_email),
+ pref_service_(pref_service),
+ screenlock_bridge_(screenlock_bridge) {
+ DCHECK(screenlock_bridge_);
+ screenlock_bridge_->AddObserver(this);
+}
+
+EasyUnlockScreenlockStateHandler::~EasyUnlockScreenlockStateHandler() {
+ screenlock_bridge_->RemoveObserver(this);
+ // Make sure the screenlock state set by this gets cleared.
+ ChangeState(STATE_INACTIVE);
+}
+
+void EasyUnlockScreenlockStateHandler::ChangeState(State new_state) {
+ if (state_ == new_state)
+ return;
+
+ state_ = new_state;
+
+ // If lock screen is not active, just cache the current state.
+ // The screenlock state will get refreshed in |ScreenDidLock|.
+ if (!screenlock_bridge_->IsLocked())
+ return;
+
+ UpdateScreenlockAuthType();
+
+ ScreenlockBridge::UserPodCustomIconOptions icon_options;
+
+ std::string icon_url = GetIconURLForState(state_);
+ if (icon_url.empty()) {
+ screenlock_bridge_->lock_handler()->HideUserPodCustomIcon(user_email_);
+ return;
+ }
+ icon_options.SetIconAsResourceURL(icon_url);
+
+ UpdateTooltipOptions(&icon_options);
+
+ if (UseOpaqueIcon(state_))
+ icon_options.SetOpacity(kOpaqueIconOpacity);
+
+ icon_options.SetSize(kIconSize, kIconSize);
+
+ if (HasAnimation(state_))
+ icon_options.SetAnimation(kSpinnerResourceWidth, kSpinnerIntervalMs);
+
+ screenlock_bridge_->lock_handler()->ShowUserPodCustomIcon(user_email_,
+ icon_options);
+}
+
+void EasyUnlockScreenlockStateHandler::OnScreenDidLock() {
+ State last_state = state_;
+ // This should force updating screenlock state.
+ state_ = STATE_INACTIVE;
+ ChangeState(last_state);
+}
+
+void EasyUnlockScreenlockStateHandler::OnScreenDidUnlock() {
+}
+
+void EasyUnlockScreenlockStateHandler::UpdateTooltipOptions(
+ ScreenlockBridge::UserPodCustomIconOptions* icon_options) {
+ bool show_tutorial = ShouldShowTutorial();
+
+ size_t resource_id = 0;
+ base::string16 device_name;
+ if (show_tutorial) {
+ resource_id = IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_TUTORIAL;
+ } else {
+ resource_id = GetTooltipResourceId(state_);
+ if (state_ == STATE_AUTHENTICATED || state_ == STATE_PHONE_UNLOCKABLE)
+ device_name = GetDeviceName();
+ }
+
+ if (!resource_id)
+ return;
+
+ base::string16 tooltip;
+ if (device_name.empty()) {
+ tooltip = l10n_util::GetStringUTF16(resource_id);
+ } else {
+ tooltip = l10n_util::GetStringFUTF16(resource_id, device_name);
+ }
+
+ if (tooltip.empty())
+ return;
+
+ if (show_tutorial)
+ MarkTutorialShown();
+
+ icon_options->SetTooltip(tooltip, show_tutorial /* autoshow tooltip */);
+}
+
+bool EasyUnlockScreenlockStateHandler::ShouldShowTutorial() {
+ if (state_ != STATE_AUTHENTICATED)
+ return false;
+ return pref_service_ &&
+ pref_service_->GetBoolean(prefs::kEasyUnlockShowTutorial);
+}
+
+void EasyUnlockScreenlockStateHandler::MarkTutorialShown() {
+ if (!pref_service_)
+ return;
+ pref_service_->SetBoolean(prefs::kEasyUnlockShowTutorial, false);
+}
+
+base::string16 EasyUnlockScreenlockStateHandler::GetDeviceName() {
+#if defined(OS_CHROMEOS)
+ return chromeos::GetChromeDeviceType();
+#else
+ // TODO(tbarzic): Figure out the name for non Chrome OS case.
+ return base::ASCIIToUTF16("Chrome");
+#endif
+}
+
+void EasyUnlockScreenlockStateHandler::UpdateScreenlockAuthType() {
+ if (state_ == STATE_AUTHENTICATED) {
+ screenlock_bridge_->lock_handler()->SetAuthType(
+ user_email_,
+ ScreenlockBridge::LockHandler::USER_CLICK,
+ l10n_util::GetStringUTF16(
+ IDS_EASY_UNLOCK_SCREENLOCK_USER_POD_AUTH_VALUE));
+ } else if (screenlock_bridge_->lock_handler()->GetAuthType(user_email_) !=
+ ScreenlockBridge::LockHandler::OFFLINE_PASSWORD) {
+ screenlock_bridge_->lock_handler()->SetAuthType(
+ user_email_,
+ ScreenlockBridge::LockHandler::OFFLINE_PASSWORD,
+ base::string16());
+ }
+}
diff --git a/chrome/browser/signin/easy_unlock_screenlock_state_handler.h b/chrome/browser/signin/easy_unlock_screenlock_state_handler.h
new file mode 100644
index 0000000..dc5c2cc
--- /dev/null
+++ b/chrome/browser/signin/easy_unlock_screenlock_state_handler.h
@@ -0,0 +1,91 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SCREENLOCK_STATE_HANDLER_H_
+#define CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SCREENLOCK_STATE_HANDLER_H_
+
+#include <string>
+
+#include "base/strings/string16.h"
+#include "base/timer/timer.h"
+#include "chrome/browser/signin/screenlock_bridge.h"
+
+class PrefService;
+
+// Profile specific class responsible for updating screenlock UI for the user
+// associated with the profile when their Easy Unlock state changes.
+class EasyUnlockScreenlockStateHandler : public ScreenlockBridge::Observer {
+ public:
+ // Available Easy Unlock states.
+ enum State {
+ // Easy Unlock is not enabled, or the screen is not locked.
+ STATE_INACTIVE,
+ // Bluetooth is not on.
+ STATE_NO_BLUETOOTH,
+ // Easy Unlock is in process of turning on Bluetooth.
+ STATE_BLUETOOTH_CONNECTING,
+ // No phones eligible to unlock the device can be found.
+ STATE_NO_PHONE,
+ // A phone eligible to unlock the device is found, but cannot be
+ // authenticated.
+ STATE_PHONE_NOT_AUTHENTICATED,
+ // A phone eligible to unlock the device is found, but it's locked.
+ STATE_PHONE_LOCKED,
+ // A phone eligible to unlock the device is found, but does not have lock
+ // screen enabled.
+ STATE_PHONE_UNLOCKABLE,
+ // A phone eligible to unlock the device is found, but it's not close enough
+ // to be allowed to unlock the device.
+ STATE_PHONE_NOT_NEARBY,
+ // The device can be unlocked using Easy Unlock.
+ STATE_AUTHENTICATED
+ };
+
+ // |user_email|: The email for the user associated with the profile to which
+ // this class is attached.
+ // |pref_service|: The profile preferences.
+ // |screenlock_bridge|: The screenlock bridge used to update the screen lock
+ // state.
+ EasyUnlockScreenlockStateHandler(const std::string& user_email,
+ PrefService* pref_service,
+ ScreenlockBridge* screenlock_bridge);
+ virtual ~EasyUnlockScreenlockStateHandler();
+
+ // Changes internal state to |new_state| and updates the user's screenlock
+ // accordingly.
+ void ChangeState(State new_state);
+
+ private:
+ // ScreenlockBridge::Observer:
+ virtual void OnScreenDidLock() OVERRIDE;
+ virtual void OnScreenDidUnlock() OVERRIDE;
+
+ void UpdateTooltipOptions(
+ ScreenlockBridge::UserPodCustomIconOptions* icon_options);
+
+ // Whether the tutorial message should be shown to the user. The message is
+ // shown only once, when the user encounters STATE_AUTHENTICATED for the first
+ // time (across sessions). After the tutorial message is shown,
+ // |MarkTutorialShown| should be called to prevent further tutorial message.
+ bool ShouldShowTutorial();
+
+ // Sets user preference that prevents showing of tutorial messages.
+ void MarkTutorialShown();
+
+ // Gets the name to be used for the device. The name depends on the device
+ // type (example values: Chromebook and Chromebox).
+ base::string16 GetDeviceName();
+
+ // Updates the screenlock auth type if it has to be changed.
+ void UpdateScreenlockAuthType();
+
+ State state_;
+ std::string user_email_;
+ PrefService* pref_service_;
+ ScreenlockBridge* screenlock_bridge_;
+
+ DISALLOW_COPY_AND_ASSIGN(EasyUnlockScreenlockStateHandler);
+};
+
+#endif // CHROME_BROWSER_SIGNIN_EASY_UNLOCK_SCREENLOCK_STATE_HANDLER_H_
diff --git a/chrome/browser/signin/easy_unlock_service.cc b/chrome/browser/signin/easy_unlock_service.cc
index 1afcbc9..81d9f0e 100644
--- a/chrome/browser/signin/easy_unlock_service.cc
+++ b/chrome/browser/signin/easy_unlock_service.cc
@@ -13,7 +13,9 @@
#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
+#include "chrome/browser/signin/easy_unlock_screenlock_state_handler.h"
#include "chrome/browser/signin/easy_unlock_service_factory.h"
+#include "chrome/browser/signin/screenlock_bridge.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
@@ -45,7 +47,8 @@ EasyUnlockService* EasyUnlockService::Get(Profile* profile) {
}
EasyUnlockService::EasyUnlockService(Profile* profile)
- : profile_(profile), weak_ptr_factory_(this) {
+ : profile_(profile),
+ weak_ptr_factory_(this) {
extensions::ExtensionSystem::Get(profile_)->ready().Post(
FROM_HERE,
base::Bind(&EasyUnlockService::Initialize,
@@ -88,7 +91,7 @@ void EasyUnlockService::LaunchSetup() {
bool EasyUnlockService::IsAllowed() {
#if defined(OS_CHROMEOS)
- if (chromeos::UserManager::Get()->IsLoggedInAsGuest())
+ if (!chromeos::UserManager::Get()->IsLoggedInAsRegularUser())
return false;
if (!chromeos::ProfileHelper::IsPrimaryProfile(profile_))
@@ -105,6 +108,20 @@ bool EasyUnlockService::IsAllowed() {
#endif
}
+
+EasyUnlockScreenlockStateHandler*
+ EasyUnlockService::GetScreenlockStateHandler() {
+ if (!IsAllowed())
+ return NULL;
+ if (!screenlock_state_handler_) {
+ screenlock_state_handler_.reset(new EasyUnlockScreenlockStateHandler(
+ ScreenlockBridge::GetAuthenticatedUserEmail(profile_),
+ profile_->GetPrefs(),
+ ScreenlockBridge::Get()));
+ }
+ return screenlock_state_handler_.get();
+}
+
void EasyUnlockService::Initialize() {
registrar_.Init(profile_->GetPrefs());
registrar_.Add(
@@ -143,8 +160,12 @@ void EasyUnlockService::UnloadApp() {
}
void EasyUnlockService::OnPrefsChanged() {
- if (IsAllowed())
+ if (IsAllowed()) {
LoadApp();
- else
+ } else {
UnloadApp();
+ // Reset the screenlock state handler to make sure Screenlock state set
+ // by Easy Unlock app is reset.
+ screenlock_state_handler_.reset();
+ }
}
diff --git a/chrome/browser/signin/easy_unlock_service.h b/chrome/browser/signin/easy_unlock_service.h
index dda962f..a8379f7 100644
--- a/chrome/browser/signin/easy_unlock_service.h
+++ b/chrome/browser/signin/easy_unlock_service.h
@@ -14,6 +14,7 @@ namespace user_prefs {
class PrefRegistrySyncable;
}
+class EasyUnlockScreenlockStateHandler;
class Profile;
class EasyUnlockService : public KeyedService {
@@ -35,6 +36,12 @@ class EasyUnlockService : public KeyedService {
// permitted either the flag is enabled or its field trial is enabled.
bool IsAllowed();
+ // Gets |screenlock_state_handler_|. Returns NULL if Easy Unlock is not
+ // allowed. Otherwise, if |screenlock_state_handler_| is not set, an instance
+ // is created. Do not cache the returned value, as it may go away if Easy
+ // Unlock gets disabled.
+ EasyUnlockScreenlockStateHandler* GetScreenlockStateHandler();
+
private:
void Initialize();
void LoadApp();
@@ -43,6 +50,8 @@ class EasyUnlockService : public KeyedService {
Profile* profile_;
PrefChangeRegistrar registrar_;
+ // Created lazily in |GetScreenlockStateHandler|.
+ scoped_ptr<EasyUnlockScreenlockStateHandler> screenlock_state_handler_;
base::WeakPtrFactory<EasyUnlockService> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(EasyUnlockService);
diff --git a/chrome/browser/signin/screenlock_bridge.cc b/chrome/browser/signin/screenlock_bridge.cc
index 995b1d9..56c2910 100644
--- a/chrome/browser/signin/screenlock_bridge.cc
+++ b/chrome/browser/signin/screenlock_bridge.cc
@@ -5,9 +5,13 @@
#include "chrome/browser/signin/screenlock_bridge.h"
#include "base/logging.h"
+#include "base/strings/string16.h"
#include "chrome/browser/profiles/profile_window.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "components/signin/core/browser/signin_manager.h"
+#include "ui/base/webui/web_ui_util.h"
+#include "ui/gfx/image/image.h"
+#include "ui/gfx/image/image_skia.h"
#if defined(OS_CHROMEOS)
#include "chromeos/dbus/dbus_thread_manager.h"
@@ -26,6 +30,106 @@ ScreenlockBridge* ScreenlockBridge::Get() {
return g_screenlock_bridge_bridge_instance.Pointer();
}
+ScreenlockBridge::UserPodCustomIconOptions::UserPodCustomIconOptions()
+ : width_(0u),
+ height_(0u),
+ animation_set_(false),
+ animation_resource_width_(0u),
+ animation_frame_length_ms_(0u),
+ opacity_(100u),
+ autoshow_tooltip_(false) {
+}
+
+ScreenlockBridge::UserPodCustomIconOptions::~UserPodCustomIconOptions() {}
+
+scoped_ptr<base::DictionaryValue>
+ScreenlockBridge::UserPodCustomIconOptions::ToDictionaryValue() const {
+ scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
+ if (!icon_image_ && icon_resource_url_.empty())
+ return result.Pass();
+
+ if (icon_image_) {
+ gfx::ImageSkia icon_skia = icon_image_->AsImageSkia();
+ base::DictionaryValue* icon_representations = new base::DictionaryValue();
+ icon_representations->SetString(
+ "scale1x",
+ webui::GetBitmapDataUrl(
+ icon_skia.GetRepresentation(1.0f).sk_bitmap()));
+ icon_representations->SetString(
+ "scale2x",
+ webui::GetBitmapDataUrl(
+ icon_skia.GetRepresentation(2.0f).sk_bitmap()));
+ result->Set("data", icon_representations);
+ } else {
+ result->SetString("resourceUrl", icon_resource_url_);
+ }
+
+ if (!tooltip_.empty()) {
+ base::DictionaryValue* tooltip_options = new base::DictionaryValue();
+ tooltip_options->SetString("text", tooltip_);
+ tooltip_options->SetBoolean("autoshow", autoshow_tooltip_);
+ result->Set("tooltip", tooltip_options);
+ }
+
+ base::DictionaryValue* size = new base::DictionaryValue();
+ size->SetInteger("height", height_);
+ size->SetInteger("width", width_);
+ result->Set("size", size);
+
+ result->SetInteger("opacity", opacity_);
+
+ if (animation_set_) {
+ base::DictionaryValue* animation = new base::DictionaryValue();
+ animation->SetInteger("resourceWidth",
+ animation_resource_width_);
+ animation->SetInteger("frameLengthMs",
+ animation_frame_length_ms_);
+ result->Set("animation", animation);
+ }
+ return result.Pass();
+}
+
+void ScreenlockBridge::UserPodCustomIconOptions::SetIconAsResourceURL(
+ const std::string& url) {
+ DCHECK(!icon_image_);
+
+ icon_resource_url_ = url;
+}
+
+void ScreenlockBridge::UserPodCustomIconOptions::SetIconAsImage(
+ const gfx::Image& image) {
+ DCHECK(icon_resource_url_.empty());
+
+ icon_image_.reset(new gfx::Image(image));
+ SetSize(image.Width(), image.Height());
+}
+
+void ScreenlockBridge::UserPodCustomIconOptions::SetSize(size_t icon_width,
+ size_t icon_height) {
+ width_ = icon_width;
+ height_ = icon_height;
+}
+
+void ScreenlockBridge::UserPodCustomIconOptions::SetAnimation(
+ size_t resource_width,
+ size_t frame_length_ms) {
+ animation_set_ = true;
+ animation_resource_width_ = resource_width;
+ animation_frame_length_ms_ = frame_length_ms;
+}
+
+void ScreenlockBridge::UserPodCustomIconOptions::SetOpacity(size_t opacity) {
+ DCHECK_LE(opacity, 100u);
+ opacity_ = opacity;
+}
+
+void ScreenlockBridge::UserPodCustomIconOptions::SetTooltip(
+ const base::string16& tooltip,
+ bool autoshow) {
+ tooltip_ = tooltip;
+ autoshow_tooltip_ = autoshow;
+}
+
// static
std::string ScreenlockBridge::GetAuthenticatedUserEmail(Profile* profile) {
// |profile| has to be a signed-in profile with SigninManager already
diff --git a/chrome/browser/signin/screenlock_bridge.h b/chrome/browser/signin/screenlock_bridge.h
index 1d75e30..72cec43 100644
--- a/chrome/browser/signin/screenlock_bridge.h
+++ b/chrome/browser/signin/screenlock_bridge.h
@@ -7,9 +7,13 @@
#include <string>
+#include "base/basictypes.h"
#include "base/lazy_instance.h"
#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
#include "base/observer_list.h"
+#include "base/strings/string16.h"
+#include "base/values.h"
namespace gfx {
class Image;
@@ -32,6 +36,68 @@ class ScreenlockBridge {
virtual ~Observer() {}
};
+ // Class containing parameters describing the custom icon that should be
+ // shown on a user's screen lock pod next to the input field.
+ class UserPodCustomIconOptions {
+ public:
+ UserPodCustomIconOptions();
+ ~UserPodCustomIconOptions();
+
+ // Converts parameters to a dictionary values that can be sent to the
+ // screenlock web UI.
+ scoped_ptr<base::DictionaryValue> ToDictionaryValue() const;
+
+ // Sets the icon as chrome://theme resource URL.
+ void SetIconAsResourceURL(const std::string& url);
+
+ // Sets the icon as a gfx::Image. The image will be converted to set of data
+ // URLs for each icon representation. Use |SetIconAsResourceURL| instead of
+ // this.
+ // TODO(tbarzic): Remove this one once easy unlock app stops using
+ // screenlockPrivate.showCustomIcon.
+ void SetIconAsImage(const gfx::Image& image);
+
+ // Sets the icon size. Has to be called if |SetIconAsResourceURL| was used
+ // to set the icon. For animated icon, this should be set to a single frame
+ // size, not the animation resource size.
+ void SetSize(size_t icon_width, size_t icon_height);
+
+ // If the icon is supposed to be animated, sets the animation parameters.
+ // If set, it expects that the resource set using |SetIcon*| methods
+ // contains horizontally arranged ordered list of animation frames.
+ // Note that the icon size set in |SetSize| should be a single frame size.
+ // |resource_width|: Total animation resource width.
+ // |frame_length_ms|: Time for which a single animation frame is shown.
+ void SetAnimation(size_t resource_width, size_t frame_length_ms);
+
+ // Sets the icon opacity. The values should be in <0, 100] interval, which
+ // will get scaled into <0, 1] interval. The default value is 100.
+ void SetOpacity(size_t opacity);
+
+ // Sets the icon tooltip. If |autoshow| is set the tooltip is automatically
+ // shown with the icon.
+ void SetTooltip(const base::string16& tooltip, bool autoshow);
+
+ private:
+ std::string icon_resource_url_;
+ scoped_ptr<gfx::Image> icon_image_;
+
+ size_t width_;
+ size_t height_;
+
+ bool animation_set_;
+ size_t animation_resource_width_;
+ size_t animation_frame_length_ms_;
+
+ // The opacity should be in <0, 100] range.
+ size_t opacity_;
+
+ base::string16 tooltip_;
+ bool autoshow_tooltip_;
+
+ DISALLOW_COPY_AND_ASSIGN(UserPodCustomIconOptions);
+ };
+
class LockHandler {
public:
// Supported authentication types. Keep in sync with the enum in
@@ -45,11 +111,12 @@ class ScreenlockBridge {
};
// Displays |message| in a banner on the lock screen.
- virtual void ShowBannerMessage(const std::string& message) = 0;
+ virtual void ShowBannerMessage(const base::string16& message) = 0;
// Shows a custom icon in the user pod on the lock screen.
- virtual void ShowUserPodCustomIcon(const std::string& user_email,
- const gfx::Image& icon) = 0;
+ virtual void ShowUserPodCustomIcon(
+ const std::string& user_email,
+ const UserPodCustomIconOptions& icon) = 0;
// Hides the custom icon in user pod for a user.
virtual void HideUserPodCustomIcon(const std::string& user_email) = 0;
@@ -60,7 +127,7 @@ class ScreenlockBridge {
// Set the authentication type to be used on the lock screen.
virtual void SetAuthType(const std::string& user_email,
AuthType auth_type,
- const std::string& auth_value) = 0;
+ const base::string16& auth_value) = 0;
// Returns the authentication type used for a user.
virtual AuthType GetAuthType(const std::string& user_email) const = 0;
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
index 0a2c84f..b88861d 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.cc
@@ -5,6 +5,7 @@
#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
#include <algorithm>
+#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
@@ -74,8 +75,6 @@
#include "net/url_request/url_request_context_getter.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "ui/base/webui/web_ui_util.h"
-#include "ui/gfx/image/image.h"
-#include "ui/gfx/image/image_skia.h"
#if defined(USE_AURA)
#include "ash/shell.h"
@@ -411,8 +410,6 @@ void SigninScreenHandler::DeclareLocalizedValues(
builder->Add("confirmPasswordText", IDS_LOGIN_CONFIRM_PASSWORD_TEXT);
builder->Add("confirmPasswordErrorText",
IDS_LOGIN_CONFIRM_PASSWORD_ERROR_TEXT);
- builder->Add("easyUnlockTooltip",
- IDS_LOGIN_EASY_UNLOCK_TOOLTIP);
builder->Add("fatalEnrollmentError",
IDS_ENTERPRISE_ENROLLMENT_AUTH_FATAL_ERROR);
@@ -513,8 +510,8 @@ void SigninScreenHandler::UpdateUIState(UIState ui_state,
}
}
-// TODO (ygorshenin@): split this method into small parts.
-// TODO (ygorshenin@): move this logic to GaiaScreenHandler.
+// TODO(ygorshenin@): split this method into small parts.
+// TODO(ygorshenin@): move this logic to GaiaScreenHandler.
void SigninScreenHandler::UpdateStateInternal(
ErrorScreenActor::ErrorReason reason,
bool force_update) {
@@ -923,37 +920,17 @@ void SigninScreenHandler::Observe(int type,
}
}
-void SigninScreenHandler::ShowBannerMessage(const std::string& message) {
+void SigninScreenHandler::ShowBannerMessage(const base::string16& message) {
CallJS("login.AccountPickerScreen.showBannerMessage", message);
}
void SigninScreenHandler::ShowUserPodCustomIcon(
const std::string& username,
- const gfx::Image& icon) {
- gfx::ImageSkia icon_skia = icon.AsImageSkia();
- base::DictionaryValue icon_representations;
- icon_representations.SetString(
- "scale1x",
- webui::GetBitmapDataUrl(icon_skia.GetRepresentation(1.0f).sk_bitmap()));
- icon_representations.SetString(
- "scale2x",
- webui::GetBitmapDataUrl(icon_skia.GetRepresentation(2.0f).sk_bitmap()));
- CallJS("login.AccountPickerScreen.showUserPodCustomIcon",
- username, icon_representations);
-
- // TODO(tengs): Move this code once we move unlocking to native code.
- if (ScreenLocker::default_screen_locker()) {
- UserManager* user_manager = UserManager::Get();
- const user_manager::User* user = user_manager->FindUser(username);
- if (!user)
- return;
- PrefService* profile_prefs =
- ProfileHelper::Get()->GetProfileByUserUnsafe(user)->GetPrefs();
- if (profile_prefs->GetBoolean(prefs::kEasyUnlockShowTutorial)) {
- CallJS("login.AccountPickerScreen.showEasyUnlockBubble");
- profile_prefs->SetBoolean(prefs::kEasyUnlockShowTutorial, false);
- }
- }
+ const ScreenlockBridge::UserPodCustomIconOptions& icon_options) {
+ scoped_ptr<base::DictionaryValue> icon = icon_options.ToDictionaryValue();
+ if (!icon || icon->empty())
+ return;
+ CallJS("login.AccountPickerScreen.showUserPodCustomIcon", username, *icon);
}
void SigninScreenHandler::HideUserPodCustomIcon(const std::string& username) {
@@ -968,7 +945,7 @@ void SigninScreenHandler::EnableInput() {
void SigninScreenHandler::SetAuthType(
const std::string& username,
ScreenlockBridge::LockHandler::AuthType auth_type,
- const std::string& initial_value) {
+ const base::string16& initial_value) {
delegate_->SetAuthType(username, auth_type);
CallJS("login.AccountPickerScreen.setAuthType",
diff --git a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
index 00804c9..2d8ffab 100644
--- a/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
+++ b/chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h
@@ -312,14 +312,15 @@ class SigninScreenHandler
const content::NotificationDetails& details) OVERRIDE;
// ScreenlockBridge::LockHandler implementation:
- virtual void ShowBannerMessage(const std::string& message) OVERRIDE;
- virtual void ShowUserPodCustomIcon(const std::string& username,
- const gfx::Image& icon) OVERRIDE;
+ virtual void ShowBannerMessage(const base::string16& message) OVERRIDE;
+ virtual void ShowUserPodCustomIcon(
+ const std::string& username,
+ const ScreenlockBridge::UserPodCustomIconOptions& icon) OVERRIDE;
virtual void HideUserPodCustomIcon(const std::string& username) OVERRIDE;
virtual void EnableInput() OVERRIDE;
virtual void SetAuthType(const std::string& username,
ScreenlockBridge::LockHandler::AuthType auth_type,
- const std::string& initial_value) OVERRIDE;
+ const base::string16& initial_value) OVERRIDE;
virtual ScreenlockBridge::LockHandler::AuthType GetAuthType(
const std::string& username) const OVERRIDE;
virtual void Unlock(const std::string& user_email) OVERRIDE;
@@ -480,7 +481,7 @@ class SigninScreenHandler
base::Closure kiosk_enable_flow_aborted_callback_for_test_;
// Non-owning ptr.
- // TODO (ygorshenin@): remove this dependency.
+ // TODO(ygorshenin@): remove this dependency.
GaiaScreenHandler* gaia_screen_handler_;
// Helper that retrieves the authenticated user's e-mail address.
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
index b88e631..4850162 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.cc
@@ -217,7 +217,8 @@ UserManagerScreenHandler::~UserManagerScreenHandler() {
ScreenlockBridge::Get()->SetLockHandler(NULL);
}
-void UserManagerScreenHandler::ShowBannerMessage(const std::string& message) {
+void UserManagerScreenHandler::ShowBannerMessage(
+ const base::string16& message) {
web_ui()->CallJavascriptFunction(
"login.AccountPickerScreen.showBannerMessage",
base::StringValue(message));
@@ -225,19 +226,14 @@ void UserManagerScreenHandler::ShowBannerMessage(const std::string& message) {
void UserManagerScreenHandler::ShowUserPodCustomIcon(
const std::string& user_email,
- const gfx::Image& icon) {
- gfx::ImageSkia icon_skia = icon.AsImageSkia();
- base::DictionaryValue icon_representations;
- icon_representations.SetString(
- "scale1x",
- webui::GetBitmapDataUrl(icon_skia.GetRepresentation(1.0f).sk_bitmap()));
- icon_representations.SetString(
- "scale2x",
- webui::GetBitmapDataUrl(icon_skia.GetRepresentation(2.0f).sk_bitmap()));
+ const ScreenlockBridge::UserPodCustomIconOptions& icon_options) {
+ scoped_ptr<base::DictionaryValue> icon = icon_options.ToDictionaryValue();
+ if (!icon || icon->empty())
+ return;
web_ui()->CallJavascriptFunction(
"login.AccountPickerScreen.showUserPodCustomIcon",
base::StringValue(user_email),
- icon_representations);
+ *icon);
}
void UserManagerScreenHandler::HideUserPodCustomIcon(
@@ -254,7 +250,7 @@ void UserManagerScreenHandler::EnableInput() {
void UserManagerScreenHandler::SetAuthType(
const std::string& user_email,
ScreenlockBridge::LockHandler::AuthType auth_type,
- const std::string& auth_value) {
+ const base::string16& auth_value) {
user_auth_type_map_[user_email] = auth_type;
web_ui()->CallJavascriptFunction(
"login.AccountPickerScreen.setAuthType",
diff --git a/chrome/browser/ui/webui/signin/user_manager_screen_handler.h b/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
index afc801a..8339d61f 100644
--- a/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
+++ b/chrome/browser/ui/webui/signin/user_manager_screen_handler.h
@@ -38,14 +38,15 @@ class UserManagerScreenHandler : public content::WebUIMessageHandler,
void GetLocalizedValues(base::DictionaryValue* localized_strings);
// ScreenlockBridge::LockHandler implementation.
- virtual void ShowBannerMessage(const std::string& message) OVERRIDE;
- virtual void ShowUserPodCustomIcon(const std::string& user_email,
- const gfx::Image& icon) OVERRIDE;
+ virtual void ShowBannerMessage(const base::string16& message) OVERRIDE;
+ virtual void ShowUserPodCustomIcon(
+ const std::string& user_email,
+ const ScreenlockBridge::UserPodCustomIconOptions& icon_options) OVERRIDE;
virtual void HideUserPodCustomIcon(const std::string& user_email) OVERRIDE;
virtual void EnableInput() OVERRIDE;
virtual void SetAuthType(const std::string& user_email,
ScreenlockBridge::LockHandler::AuthType auth_type,
- const std::string& auth_value) OVERRIDE;
+ const base::string16& auth_value) OVERRIDE;
virtual ScreenlockBridge::LockHandler::AuthType GetAuthType(
const std::string& user_email) const OVERRIDE;
virtual void Unlock(const std::string& user_email) OVERRIDE;
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index b8e67b7..bc6f200 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -1536,6 +1536,8 @@
'browser/search/hotword_service.h',
'browser/search/hotword_service_factory.cc',
'browser/search/hotword_service_factory.h',
+ 'browser/signin/easy_unlock_screenlock_state_handler.cc',
+ 'browser/signin/easy_unlock_screenlock_state_handler.h',
'browser/signin/easy_unlock_service.cc',
'browser/signin/easy_unlock_service.h',
'browser/signin/easy_unlock_service_factory.cc',
diff --git a/chrome/common/extensions/api/easy_unlock_private.idl b/chrome/common/extensions/api/easy_unlock_private.idl
index 9f43c2f..32e6410 100644
--- a/chrome/common/extensions/api/easy_unlock_private.idl
+++ b/chrome/common/extensions/api/easy_unlock_private.idl
@@ -18,6 +18,32 @@
AES_256_CBC
};
+ // Available states for the Easy Unlock app.
+ enum State {
+ // Screen is either not locked, or the Easy Unlock is not enabled.
+ INACTIVE,
+ // The Bluetooth is not enabled.
+ NO_BLUETOOTH,
+ // Bluetooth is being activated.
+ BLUETOOTH_CONNECTING,
+ // There are no phones eligible to unlock the device.
+ NO_PHONE,
+ // A phone eligible to unlock the device is detected, but can't be
+ // authenticated.
+ PHONE_NOT_AUTHENTICATED,
+ // A phone eligible to unlock the device is detected, but it's locked and
+ // thus unable to unlock the device.
+ PHONE_LOCKED,
+ // A phone eligible to unlock the device is detected, but it is not allowed
+ // to unlock the device because it doesn't have lock screen enabled.
+ PHONE_UNLOCKABLE,
+ // A phone eligible to unlock the device is detected, but it's not close
+ // enough to be allowed to unlock the device.
+ PHONE_NOT_NEARBY,
+ // The devie can be unlocked using Easy Unlock.
+ AUTHENTICATED
+ };
+
// Options that can be passed to |unwrapSecureMessage| method.
dictionary UnwrapSecureMessageOptions {
// The data associated with the message. For the message to be succesfully
@@ -145,5 +171,9 @@
// |callback|: Called to indicate success or failure.
static void seekBluetoothDeviceByAddress(DOMString deviceAddress,
optional EmptyCallback callback);
+
+ // Updates the screenlock state to reflect the Easy Unlock app state.
+ static void updateScreenlockState(State state,
+ optional EmptyCallback callback);
};
};
diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h
index d980632..a46a700 100644
--- a/extensions/browser/extension_function_histogram_value.h
+++ b/extensions/browser/extension_function_histogram_value.h
@@ -934,6 +934,7 @@ enum HistogramValue {
BROWSINGDATA_REMOVESERVICEWORKERS,
USBPRIVATE_GETDEVICES,
USBPRIVATE_GETDEVICEINFO,
+ EASYUNLOCKPRIVATE_UPDATESCREENLOCKSTATE,
// Last entry: Add new entries above and ensure to update
// tools/metrics/histograms/histograms/histograms.xml.
ENUM_BOUNDARY
diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml
index 5965a50..8fd4b113 100644
--- a/tools/metrics/histograms/histograms.xml
+++ b/tools/metrics/histograms/histograms.xml
@@ -40594,6 +40594,7 @@ Therefore, the affected-histogram name has to have at least one dot in it.
<int value="873" label="BROWSINGDATA_REMOVESERVICEWORKERS"/>
<int value="874" label="USBPRIVATE_GETDEVICES"/>
<int value="875" label="USBPRIVATE_GETDEVICEINFO"/>
+ <int value="876" label="EASYUNLOCKPRIVATE_UPDATESCREENLOCKSTATE"/>
</enum>
<enum name="ExtensionInstallCause" type="int">
diff --git a/ui/login/account_picker/screen_account_picker.js b/ui/login/account_picker/screen_account_picker.js
index d4af56b..69e922a 100644
--- a/ui/login/account_picker/screen_account_picker.js
+++ b/ui/login/account_picker/screen_account_picker.js
@@ -29,7 +29,6 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() {
'showUserPodCustomIcon',
'hideUserPodCustomIcon',
'setAuthType',
- 'showEasyUnlockBubble',
'setPublicSessionDisplayName',
'setPublicSessionLocales',
'setPublicSessionKeyboardLayouts',
@@ -279,8 +278,14 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() {
* Shows a custom icon in the user pod of |username|. This function
* is used by the chrome.screenlockPrivate API.
* @param {string} username Username of pod to add button
- * @param {{scale1x: string, scale2x: string}} icon Dictionary of URLs of
- * the custom icon's representations for 1x and 2x scale factors.
+ * @param {!{resourceUrl: (string | undefined),
+ * data: ({scale1x: string, scale2x: string} | undefined),
+ * size: ({width: number, height: number} | undefined),
+ * animation: ({resourceWidth: number, frameLength: number} |
+ * undefined),
+ * opacity: (number | undefined),
+ * tooltip: ({text: string, autoshow: boolean} | undefined)}} icon
+ * The icon parameters.
*/
showUserPodCustomIcon: function(username, icon) {
$('pod-row').showUserPodCustomIcon(username, icon);
@@ -308,13 +313,6 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() {
},
/**
- * Shows a tooltip bubble explaining Easy Unlock.
- */
- showEasyUnlockBubble: function() {
- $('pod-row').showEasyUnlockBubble();
- },
-
- /**
* Updates the display name shown on a public session pod.
* @param {string} userID The user ID of the public session
* @param {string} displayName The new display name
diff --git a/ui/login/account_picker/user_pod_row.css b/ui/login/account_picker/user_pod_row.css
index 5927b25..01a3b7a 100644
--- a/ui/login/account_picker/user_pod_row.css
+++ b/ui/login/account_picker/user_pod_row.css
@@ -219,10 +219,19 @@ html[dir=rtl] .main-pane {
}
.custom-icon {
+ -webkit-margin-end: 0;
+ -webkit-margin-start: auto;
background-position: center;
background-repeat: no-repeat;
flex: none;
+}
+
+.custom-icon-container {
+ display: flex;
+ flex: none;
+ flex-direction: column;
height: 40px;
+ justify-content: center;
width: 40px;
}
@@ -423,10 +432,6 @@ html[dir=rtl] .user-type-bubble {
margin-bottom: 14px;
}
-.easy-unlock-button-content {
- width: 145px;
-}
-
/**** Public account user pod rules *******************************************/
.pod.public-account.expanded {
diff --git a/ui/login/account_picker/user_pod_row.js b/ui/login/account_picker/user_pod_row.js
index 194a3c6..39b5c7d 100644
--- a/ui/login/account_picker/user_pod_row.js
+++ b/ui/login/account_picker/user_pod_row.js
@@ -55,6 +55,7 @@ cr.define('login', function() {
var DESKTOP_POD_HEIGHT = 226;
var POD_ROW_PADDING = 10;
var DESKTOP_ROW_PADDING = 15;
+ var CUSTOM_ICON_CONTAINER_SIZE = 40;
/**
* Minimal padding between user pod and virtual keyboard.
@@ -159,6 +160,330 @@ cr.define('login', function() {
}
/**
+ * Creates an element for custom icon shown in a user pod next to the input
+ * field.
+ * @constructor
+ * @extends {HTMLDivElement}
+ */
+ var UserPodCustomIcon = cr.ui.define(function() {
+ var node = document.createElement('div');
+ node.classList.add('custom-icon-container');
+ node.hidden = true;
+
+ // Create the actual icon element and add it as a child to the container.
+ var iconNode = document.createElement('div');
+ iconNode.classList.add('custom-icon');
+ node.appendChild(iconNode);
+ return node;
+ });
+
+ UserPodCustomIcon.prototype = {
+ __proto__: HTMLDivElement.prototype,
+
+ /**
+ * The icon height.
+ * @type {number}
+ * @private
+ */
+ height_: 0,
+
+ /**
+ * The icon width.
+ * @type {number}
+ * @private
+ */
+ width_: 0,
+
+ /**
+ * Tooltip to be shown when the user hovers over the icon. The icon
+ * properties may be set so the tooltip is shown automatically when the icon
+ * is updated. The tooltip is shown in a bubble attached to the icon
+ * element.
+ * @type {string}
+ * @private
+ */
+ tooltip_: '',
+
+ /**
+ * Whether the tooltip is shown and the user is hovering over the icon.
+ * @type {boolean}
+ * @private
+ */
+ tooltipActive_: false,
+
+ /**
+ * Whether the icon has been shown as a result of |autoshow| parameter begin
+ * set rather than user hovering over the icon.
+ * If this is set, the tooltip will not be removed when the mouse leaves the
+ * icon.
+ * @type {boolean}
+ * @private
+ */
+ tooltipAutoshown_: false,
+
+ /**
+ * A reference to the timeout for showing tooltip after mouse enters the
+ * icon.
+ * @type {?number}
+ * @private
+ */
+ showTooltipTimeout_: null,
+
+ /**
+ * If animation is set, the current horizontal background offset for the
+ * icon resource.
+ * @type {number}
+ * @private
+ */
+ lastAnimationOffset_: 0,
+
+ /**
+ * The reference to interval for progressing the animation.
+ * @type {?number}
+ * @private
+ */
+ animationInterval_: null,
+
+ /**
+ * The width of the resource that contains representations for different
+ * animation stages.
+ * @type {number}
+ * @private
+ */
+ animationResourceSize_: 0,
+
+ /** @override */
+ decorate: function() {
+ this.iconElement.addEventListener('mouseover',
+ this.showTooltipSoon_.bind(this));
+ this.iconElement.addEventListener('mouseout',
+ this.hideTooltip_.bind(this, false));
+ this.iconElement.addEventListener('mousedown',
+ this.hideTooltip_.bind(this, false));
+ },
+
+ /**
+ * Getter for the icon element's div.
+ * @return {HTMLDivElement}
+ */
+ get iconElement() {
+ return this.querySelector('.custom-icon');
+ },
+
+ /**
+ * Set the icon's background image as image set with different
+ * representations for available screen scale factors.
+ * @param {!{scale1x: string, scale2x: string}} icon The icon
+ * representations.
+ */
+ setIconAsImageSet: function(icon) {
+ this.iconElement.style.backgroundImage =
+ '-webkit-image-set(' +
+ 'url(' + icon.scale1x + ') 1x,' +
+ 'url(' + icon.scale2x + ') 2x)';
+ },
+
+ /**
+ * Sets the icon background image to a chrome://theme URL.
+ * @param {!string} iconUrl The icon's background image URL.
+ */
+ setIconAsResourceUrl: function(iconUrl) {
+ this.iconElement.style.backgroundImage =
+ '-webkit-image-set(' +
+ 'url(' + iconUrl + '@1x) 1x,' +
+ 'url(' + iconUrl + '@2x) 2x)';
+ },
+
+ /**
+ * Shows the icon.
+ */
+ show: function() {
+ this.hidden = false;
+ },
+
+ /**
+ * Hides the icon. Makes sure the tooltip is hidden and animation reset.
+ */
+ hide: function() {
+ this.hideTooltip_(true);
+ this.setAnimation(null);
+ this.hidden = true;
+ },
+
+ /**
+ * Sets the icon size element size.
+ * @param {!{width: number, height: number}} size The icon size.
+ */
+ setSize: function(size) {
+ this.height_ = size.height < CUSTOM_ICON_CONTAINER_SIZE ?
+ size.height : CUSTOM_ICON_COTAINER_SIZE;
+ this.iconElement.style.height = this.height_ + 'px';
+
+ this.width_ = size.width < CUSTOM_ICON_CONTAINER_SIZE ?
+ size.width : CUSTOM_ICON_COTAINER_SIZE;
+ this.iconElement.style.width = this.width_ + 'px';
+ this.style.width = this.width_ + 'px';
+ },
+
+ /**
+ * Sets the icon opacity.
+ * @param {number} opacity The icon opacity in [0-100] scale.
+ */
+ setOpacity: function(opacity) {
+ if (opacity > 100) {
+ this.style.opacity = 1;
+ } else if (opacity < 0) {
+ this.style.opacity = 0;
+ } else {
+ this.style.opacity = opacity / 100;
+ }
+ },
+
+ /**
+ * Updates the icon tooltip. If {@code autoshow} parameter is set the
+ * tooltip is immediatelly shown. If tooltip text is not set, the method
+ * ensures the tooltip gets hidden. If tooltip is shown prior to this call,
+ * it remains shown, but the tooltip text is updated.
+ * @param {!{text: string, autoshow: boolean}} tooltip The tooltip
+ * parameters.
+ */
+ setTooltip: function(tooltip) {
+ if (this.tooltip_ == tooltip.text && !tooltip.autoshow)
+ return;
+ this.tooltip_ = tooltip.text;
+
+ // If tooltip is already shown, just update the text.
+ if (tooltip.text && this.tooltipActive_ && !$('bubble').hidden) {
+ // If both previous and the new tooltip are supposed to be shown
+ // automatically, keep the autoshown flag.
+ var markAutoshown = this.tooltipAutoshown_ && tooltip.autoshow;
+ this.hideTooltip_(true);
+ this.showTooltip_();
+ this.tooltipAutoshown_ = markAutoshown;
+ return;
+ }
+
+ // If autoshow flag is set, make sure the tooltip gets shown.
+ if (tooltip.text && tooltip.autoshow) {
+ this.hideTooltip_(true);
+ this.showTooltip_();
+ this.tooltipAutoshown_ = true;
+ // Reset the tooltip active flag, which gets set in |showTooltip_|.
+ this.tooltipActive_ = false;
+ return;
+ }
+
+ this.hideTooltip_(true);
+
+ if (this.tooltip_)
+ this.iconElement.setAttribute('aria-lablel', this.tooltip_);
+ },
+
+ /**
+ * Sets the icon animation parameter and starts the animation.
+ * The animation is set using the resource containing all animation frames
+ * concatenated horizontally. The animator offsets the background image in
+ * regural intervals.
+ * @param {?{resourceWidth: number, frameLengthMs: number}} animation
+ * |resourceWidth|: Total width for the resource containing the
+ * animation frames.
+ * |frameLengthMs|: Time interval for which a single animation frame is
+ * shown.
+ */
+ setAnimation: function(animation) {
+ if (this.animationInterval_)
+ clearInterval(this.animationInterval_);
+ this.iconElement.style.backgroundPosition = 'center';
+ if (!animation)
+ return;
+ this.lastAnimationOffset_ = 0;
+ this.animationResourceSize_ = animation.resourceWidth;
+ this.animationInterval_ = setInterval(this.progressAnimation_.bind(this),
+ animation.frameLengthMs);
+ },
+
+ /**
+ * Called when mouse enters the icon. It sets timeout for showing the
+ * tooltip.
+ * @private
+ */
+ showTooltipSoon_: function() {
+ if (this.showTooltipTimeout_ || this.tooltipActive_)
+ return;
+ this.showTooltipTimeout_ =
+ setTimeout(this.showTooltip_.bind(this), 1000);
+ },
+
+ /**
+ * Shows the current tooltip, if one is set.
+ * @private
+ */
+ showTooltip_: function() {
+ if (this.hidden || !this.tooltip_ || this.tooltipActive_)
+ return;
+
+ // If autoshown bubble got hidden, clear the autoshown flag.
+ if ($('bubble').hidden && this.tooltipAutoshown_)
+ this.tooltipAutoshown_ = false;
+
+ // Show the tooltip bubble.
+ var bubbleContent = document.createElement('div');
+ bubbleContent.textContent = this.tooltip_;
+
+ /** @const */ var BUBBLE_OFFSET = CUSTOM_ICON_CONTAINER_SIZE / 2;
+ /** @const */ var BUBBLE_PADDING = 8;
+ $('bubble').showContentForElement(this,
+ cr.ui.Bubble.Attachment.RIGHT,
+ bubbleContent,
+ BUBBLE_OFFSET,
+ BUBBLE_PADDING);
+ this.ensureTooltipTimeoutCleared_();
+ this.tooltipActive_ = true;
+ },
+
+ /**
+ * Hides the tooltip. If the current tooltip was automatically shown, it
+ * will be hidden only if |force| is set.
+ * @param {boolean} Whether the tooltip should be hidden if it got shown
+ * because autoshow flag was set.
+ * @private
+ */
+ hideTooltip_: function(force) {
+ this.tooltipActive_ = false;
+ this.ensureTooltipTimeoutCleared_();
+ if (!force && this.tooltipAutoshown_)
+ return;
+ $('bubble').hideForElement(this);
+ this.tooltipAutoshown_ = false;
+ this.iconElement.removeAttribute('aria-label');
+ },
+
+ /**
+ * Clears timaout for showing the tooltip if it's set.
+ * @private
+ */
+ ensureTooltipTimeoutCleared_: function() {
+ if (this.showTooltipTimeout_) {
+ clearTimeout(this.showTooltipTimeout_);
+ this.showTooltipTimeout_ = null;
+ }
+ },
+
+ /**
+ * Horizontally offsets the animated icon's background for a single icon
+ * size width.
+ * @private
+ */
+ progressAnimation_: function() {
+ this.lastAnimationOffset_ += this.width_;
+ if (this.lastAnimationOffset_ >= this.animationResourceSize_)
+ this.lastAnimationOffset_ = 0;
+ this.iconElement.style.backgroundPosition =
+ '-' + this.lastAnimationOffset_ + 'px center';
+ }
+ };
+
+ /**
* Unique salt added to user image URLs to prevent caching. Dictionary with
* user names as keys.
* @type {Object}
@@ -198,6 +523,9 @@ cr.define('login', function() {
this.actionBoxRemoveUserWarningButtonElement.addEventListener(
'keydown',
this.handleRemoveUserConfirmationKeyDown_.bind(this));
+
+ var customIcon = this.customIconElement;
+ customIcon.parentNode.replaceChild(new UserPodCustomIcon(), customIcon);
},
/**
@@ -445,7 +773,7 @@ cr.define('login', function() {
* @type {!HTMLDivElement}
*/
get customIconElement() {
- return this.querySelector('.custom-icon');
+ return this.querySelector('.custom-icon-container');
},
/**
@@ -1843,8 +2171,14 @@ cr.define('login', function() {
/**
* Shows a custom icon on a user pod besides the input field.
* @param {string} username Username of pod to add button
- * @param {{scale1x: string, scale2x: string}} icon Dictionary of URLs of
- * the custom icon's representations for 1x and 2x scale factors.
+ * @param {!{resourceUrl: (string | undefined),
+ * data: ({scale1x: string, scale2x: string} | undefined),
+ * size: ({width: number, height: number} | undefined),
+ * animation: ({resourceWidth: number, frameLength: number} |
+ * undefined),
+ * opacity: (number | undefined),
+ * tooltip: ({text: string, autoshow: boolean} | undefined)}} icon
+ * The icon parameters.
*/
showUserPodCustomIcon: function(username, icon) {
var pod = this.getPodWithUsername_(username);
@@ -1854,11 +2188,22 @@ cr.define('login', function() {
return;
}
- pod.customIconElement.hidden = false;
- pod.customIconElement.style.backgroundImage =
- '-webkit-image-set(' +
- 'url(' + icon.scale1x + ') 1x,' +
- 'url(' + icon.scale2x + ') 2x)';
+ if (icon.resourceUrl) {
+ pod.customIconElement.setIconAsResourceUrl(icon.resourceUrl);
+ } else if (icon.data) {
+ pod.customIconElement.setIconAsImageSet(icon.data);
+ } else {
+ return;
+ }
+
+ pod.customIconElement.setSize(icon.size || {width: 0, height: 0});
+ pod.customIconElement.setAnimation(icon.animation || null);
+ pod.customIconElement.setOpacity(icon.opacity || 100);
+ pod.customIconElement.show();
+ // This has to be called after |show| in case the tooltip should be shown
+ // immediatelly.
+ pod.customIconElement.setTooltip(
+ icon.tooltip || {text: '', autoshow: false});
},
/**
@@ -1873,7 +2218,7 @@ cr.define('login', function() {
return;
}
- pod.customIconElement.hidden = true;
+ pod.customIconElement.hide();
},
/**
@@ -1894,29 +2239,6 @@ cr.define('login', function() {
},
/**
- * Shows a tooltip bubble explaining Easy Unlock for the focused pod.
- */
- showEasyUnlockBubble: function() {
- if (!this.focusedPod_) {
- console.error('No focused pod to show Easy Unlock bubble.');
- return;
- }
-
- var bubbleContent = document.createElement('div');
- bubbleContent.classList.add('easy-unlock-button-content');
- bubbleContent.textContent = loadTimeData.getString('easyUnlockTooltip');
-
- var attachElement = this.focusedPod_.customIconElement;
- /** @const */ var BUBBLE_OFFSET = 20;
- /** @const */ var BUBBLE_PADDING = 8;
- $('bubble').showContentForElement(attachElement,
- cr.ui.Bubble.Attachment.RIGHT,
- bubbleContent,
- BUBBLE_OFFSET,
- BUBBLE_PADDING);
- },
-
- /**
* Updates the display name shown on a public session pod.
* @param {string} userID The user ID of the public session
* @param {string} displayName The new display name
diff --git a/ui/login/account_picker/user_pod_template.html b/ui/login/account_picker/user_pod_template.html
index b61f42a..555c56c 100644
--- a/ui/login/account_picker/user_pod_template.html
+++ b/ui/login/account_picker/user_pod_template.html
@@ -28,7 +28,7 @@
</div>
<!-- User Click Authentication -->
<div class="password-label"></div>
- <div class="custom-icon" hidden></div>
+ <div class="custom-icon-container" hidden></div>
<div class="signin-button-container">
<button class="signin-button" i18n-content="signinButton"></button>
</div>