summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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>