Commit fb08c7a4 authored by bartfab@chromium.org's avatar bartfab@chromium.org

Add language and keyboard layout pickers to public session pods

This CL adds language and keyboard pickers to public session pods. The
CL focuses on the necessary changes to the login UI. Follow-up CLs will:
- wire up the pickers so that they actually influence the language and
  keyboard layout in the public session, and
- customize the contents of the language picker based on policy
  (languages recommended by policy go first; the top recommended language
  is pre-selected)

BUG=214904,241790
TEST=Manual, including RTL

Review URL: https://codereview.chromium.org/398543002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284983 0039d316-1c4b-4281-b951-d872f2087c98
parent 586cfb15
......@@ -4647,6 +4647,9 @@ Battery full
<message name="IDS_LOGIN_PUBLIC_ACCOUNT_SIGNOUT_REMINDER" desc="Text shown in the public account user pod, reminding the user to log out.">
Your information will be removed when you sign out.
</message>
<message name="IDS_LOGIN_PUBLIC_SESSION_LANGUAGE_AND_INPUT" desc="Link in public session pod that shows a section which allows the user to change the UI language and keyboard layout.">
Language and input
</message>
<message name="IDS_LOGIN_PUBLIC_ACCOUNT_ENTER" meaning="Label text for signing in." desc="Label text for the sign-in button in the public account user pod.">
Enter
</message>
......
......@@ -4,6 +4,8 @@
#include "chrome/browser/chromeos/login/screens/user_selection_screen.h"
#include <vector>
#include "ash/shell.h"
#include "base/logging.h"
#include "base/prefs/pref_service.h"
......@@ -12,6 +14,7 @@
#include "chrome/browser/chromeos/login/users/multi_profile_user_controller.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/signin/screenlock_bridge.h"
#include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
#include "chrome/common/pref_names.h"
#include "components/user_manager/user_type.h"
......@@ -34,6 +37,8 @@ const char kKeyIsOwner[] = "isOwner";
const char kKeyInitialAuthType[] = "initialAuthType";
const char kKeyMultiProfilesAllowed[] = "isMultiProfilesAllowed";
const char kKeyMultiProfilesPolicy[] = "multiProfilesPolicy";
const char kKeyInitialLocales[] = "initialLocales";
const char kKeyInitialKeyboardLayouts[] = "initialKeyboardLayouts";
// Max number of users to show.
// Please keep synced with one in signin_userlist_unittest.cc.
......@@ -97,6 +102,16 @@ void UserSelectionScreen::FillUserDictionary(
user_dict->SetString(kKeyEnterpriseDomain,
policy_connector->GetEnterpriseDomain());
}
// TODO(bartfab): Initialize |locale| and |most_relevant_languages| based on
// policy.
const std::string locale;
std::vector<std::string> most_relevant_languages;
user_dict->Set(
kKeyInitialLocales,
GetUILanguageList(&most_relevant_languages, locale).release());
user_dict->Set(kKeyInitialKeyboardLayouts,
GetKeyboardLayoutsForLocale(locale).release());
}
}
......
......@@ -36,6 +36,7 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() {
'hideUserPodCustomIcon',
'setAuthType',
'showEasyUnlockBubble',
'setPublicSessionKeyboardLayouts',
],
preferredWidth_: 0,
......@@ -306,6 +307,15 @@ login.createScreen('AccountPickerScreen', 'account-picker', function() {
*/
showEasyUnlockBubble: function() {
$('pod-row').showEasyUnlockBubble();
},
/**
* Updates the list of available keyboard layouts for a public session pod.
* @param {string} userID The user ID of the public session
* @param {!Object} list List of available keyboard layouts
*/
setPublicSessionKeyboardLayouts: function(userID, list) {
$('pod-row').setPublicSessionKeyboardLayouts(userID, list);
}
};
});
......
......@@ -415,6 +415,10 @@ html[dir=rtl] .user-type-bubble {
width: 500px;
}
.pod.public-account.expanded.advanced {
width: 610px;
}
.pod.public-account.focused .name-container {
display: flex;
}
......@@ -443,6 +447,7 @@ html[dir=rtl] .user-type-bubble {
.pod.public-account.animating .expanded-pane,
.pod.public-account.expanded .expanded-pane {
display: block;
font-size: 12px;
margin: 10px;
overflow: hidden;
z-index: 1;
......@@ -456,13 +461,24 @@ html[dir=rtl] .user-type-bubble {
width: 490px;
}
.pod.public-account.transitioning-to-advanced .expanded-pane-contents {
transition: width 180ms;
}
.pod.public-account.expanded.advanced .expanded-pane-contents {
width: 600px;
}
html[dir=rtl] .expanded-pane-contents {
float: left;
}
.expanded-pane-name {
.side-container {
-webkit-margin-start: 200px;
flex: none;
flex: auto;
}
.expanded-pane-name {
font-size: 19px;
margin-bottom: 11px;
margin-top: -2px;
......@@ -472,16 +488,53 @@ html[dir=rtl] .expanded-pane-contents {
}
.reminder {
-webkit-margin-start: 200px;
flex: auto;
font-size: 12px;
font-weight: bold;
}
.language-and-input-section {
display: none;
}
.pod.public-account.transitioning-to-advanced .language-and-input-section {
display: block;
opacity: 0;
transition: opacity 180ms ease 180ms;
}
.pod.public-account.expanded.advanced .language-and-input-section {
display: block;
opacity: 1;
}
.select-with-label {
display: flex;
margin-top: 20px;
}
.language-select-label,
.keyboard-select-label {
flex: none;
margin-top: 3px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
width: 170px;
}
.select-container {
flex: auto;
}
.language-select,
.keyboard-select {
width: 100%;
}
.bottom-container {
-webkit-margin-start: 10px;
display: flex;
flex: none;
font-size: 13px;
}
.expanded-pane-learn-more-container,
......@@ -499,9 +552,28 @@ html[dir=rtl] .expanded-pane-contents {
.info {
flex: auto;
font-size: 13px;
margin: 5px 10px 0 10px;
margin: 5px 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.language-and-input-container {
-webkit-margin-end: 25px;
flex: none;
}
.language-and-input {
color: rgb(49, 106, 197);
position: relative;
text-decoration: none;
top: 4px;
}
.pod.public-account.expanded.advanced .language-and-input-container {
display: none;
}
.enter-button {
font-size: 14px;
}
......@@ -50,6 +50,7 @@ cr.define('login', function() {
*/
var POD_WIDTH = 180;
var PUBLIC_EXPANDED_BASIC_WIDTH = 500;
var PUBLIC_EXPANDED_ADVANCED_WIDTH = 610;
var CROS_POD_HEIGHT = 213;
var DESKTOP_POD_HEIGHT = 226;
var POD_ROW_PADDING = 10;
......@@ -996,21 +997,10 @@ cr.define('login', function() {
this.resetTabOrder();
this.classList.toggle('expanded', expanded);
if (expanded) {
var isDesktopUserManager = Oobe.getInstance().displayType ==
DISPLAY_TYPE.DESKTOP_USER_MANAGER;
var rowPadding = isDesktopUserManager ? DESKTOP_ROW_PADDING :
POD_ROW_PADDING;
this.usualLeft = this.left;
this.usualTop = this.top;
if (this.left + PUBLIC_EXPANDED_BASIC_WIDTH >
$('pod-row').offsetWidth - rowPadding)
this.left = $('pod-row').offsetWidth - rowPadding -
PUBLIC_EXPANDED_BASIC_WIDTH;
} else {
if (typeof(this.usualLeft) != 'undefined')
this.left = this.usualLeft;
if (typeof(this.usualTop) != 'undefined')
this.top = this.usualTop;
this.makeSpaceForExpandedPod_();
} else if (typeof(this.usualLeft) != 'undefined') {
this.left = this.usualLeft;
}
var self = this;
......@@ -1025,6 +1015,8 @@ cr.define('login', function() {
if (document.activeElement)
document.activeElement.dispatchEvent(new Event('focus'));
});
// Guard timer set to animation duration + 20ms.
ensureTransitionEndEvent(this, 200);
},
/** @override */
......@@ -1061,12 +1053,57 @@ cr.define('login', function() {
learnMore.addEventListener('click', this.handleLearnMoreEvent);
learnMore.addEventListener('keydown', this.handleLearnMoreEvent);
var languageSelect = this.querySelector('.language-select');
languageSelect.tabIndex = UserPodTabOrder.POD_INPUT;
languageSelect.addEventListener('change', function() {
chrome.send('getPublicSessionKeyboardLayouts', [
this.user.username,
languageSelect.options[languageSelect.selectedIndex].value]);
}.bind(this));
this.querySelector('.keyboard-select').tabIndex =
UserPodTabOrder.POD_INPUT;
var languageAndInput = this.querySelector('.language-and-input');
languageAndInput.tabIndex = UserPodTabOrder.POD_INPUT;
languageAndInput.addEventListener('click',
this.transitionToAdvanced_.bind(this));
this.enterButtonElement.addEventListener('click', (function(e) {
this.enterButtonElement.disabled = true;
chrome.send('launchPublicAccount', [this.user.username]);
}).bind(this));
},
/** @override **/
initialize: function() {
UserPod.prototype.initialize.call(this);
var id = this.user.username + '-language';
this.querySelector('.language-select-label').htmlFor = id;
var languageSelect = this.querySelector('.language-select');
languageSelect.setAttribute('id', id);
var list = this.user.initialLocales;
languageSelect.innerHTML = '';
var group = languageSelect;
for (var i = 0; i < list.length; ++i) {
var item = list[i];
if (item.optionGroupName) {
group = document.createElement('optgroup');
group.label = item.optionGroupName;
languageSelect.appendChild(group);
} else {
group.appendChild(
new Option(item.title, item.value, item.selected, item.selected));
}
}
id = this.user.username + '-keyboard';
this.querySelector('.keyboard-select-label').htmlFor = id;
this.querySelector('.keyboard-select').setAttribute('id', id);
this.populateKeyboardSelect_(this.user.initialKeyboardLayouts);
},
/** @override **/
update: function() {
UserPod.prototype.update.call(this);
......@@ -1095,8 +1132,10 @@ cr.define('login', function() {
/** @override */
activate: function(e) {
this.expanded = true;
this.focusInput();
if (!this.expanded) {
this.expanded = true;
this.focusInput();
}
return true;
},
......@@ -1138,6 +1177,54 @@ cr.define('login', function() {
chrome.send('launchHelpApp', [HELP_TOPIC_PUBLIC_SESSION]);
stopEventPropagation(event);
},
makeSpaceForExpandedPod_: function() {
var width = this.classList.contains('advanced') ?
PUBLIC_EXPANDED_ADVANCED_WIDTH : PUBLIC_EXPANDED_BASIC_WIDTH;
var isDesktopUserManager = Oobe.getInstance().displayType ==
DISPLAY_TYPE.DESKTOP_USER_MANAGER;
var rowPadding = isDesktopUserManager ? DESKTOP_ROW_PADDING :
POD_ROW_PADDING;
if (this.left + width > $('pod-row').offsetWidth - rowPadding)
this.left = $('pod-row').offsetWidth - rowPadding - width;
},
/**
* Transition the expanded pod from the basic to the advanced view.
*/
transitionToAdvanced_: function() {
var pod = this;
var languageAndInputSection =
this.querySelector('.language-and-input-section');
this.classList.add('transitioning-to-advanced');
setTimeout(function() {
pod.classList.add('advanced');
pod.makeSpaceForExpandedPod_();
languageAndInputSection.addEventListener('webkitTransitionEnd',
function observer() {
languageAndInputSection.removeEventListener('webkitTransitionEnd',
observer);
pod.classList.remove('transitioning-to-advanced');
pod.querySelector('.language-select').focus();
});
// Guard timer set to animation duration + 20ms.
ensureTransitionEndEvent(languageAndInputSection, 380);
}, 0);
},
/**
* Populates the keyboard layout "select" element with a list of layouts.
* @param {!Object} list List of available keyboard layouts
*/
populateKeyboardSelect_: function(list) {
var keyboardSelect = this.querySelector('.keyboard-select');
keyboardSelect.innerHTML = '';
for (var i = 0; i < list.length; ++i) {
var item = list[i];
keyboardSelect.appendChild(
new Option(item.title, item.value, item.selected, item.selected));
}
}
};
/**
......@@ -1732,6 +1819,17 @@ cr.define('login', function() {
BUBBLE_PADDING);
},
/**
* Updates the list of available keyboard layouts for a public session pod.
* @param {string} userID The user ID of the public session
* @param {!Object} list List of available keyboard layouts
*/
setPublicSessionKeyboardLayouts: function(userID, list) {
var pod = this.getPodWithUsername_(userID);
if (pod != null)
pod.populateKeyboardSelect_(list);
},
/**
* Called when window was resized.
*/
......@@ -2087,11 +2185,10 @@ cr.define('login', function() {
pod.isActionBoxMenuHovered = true;
// Return focus back to single pod.
if (this.alwaysFocusSinglePod) {
if (this.alwaysFocusSinglePod && !pod) {
this.focusPod(this.focusedPod_, true /* force */);
this.focusedPod_.userTypeBubbleElement.classList.remove('bubble-shown');
if (!pod)
this.focusedPod_.isActionBoxMenuHovered = false;
this.focusedPod_.isActionBoxMenuHovered = false;
}
},
......
......@@ -84,13 +84,37 @@
<div id="public-account-user-pod-extras-template" hidden>
<div class="expanded-pane">
<div class="expanded-pane-contents">
<div class="expanded-pane-name"></div>
<div class="reminder" i18n-content="publicAccountReminder"></div>
<div class="side-container">
<div class="expanded-pane-name"></div>
<div class="reminder" i18n-content="publicAccountReminder"></div>
<div class="language-and-input-section">
<div class="select-with-label">
<label class="language-select-label"
i18n-content="publicSessionSelectLanguage">
</label>
<div class="select-container">
<select class="language-select"></select>
</div>
</div>
<div class="select-with-label">
<label class="keyboard-select-label"
i18n-content="publicSessionSelectKeyboard">
</label>
<div class="select-container">
<select class="keyboard-select"></select>
</div>
</div>
</div>
</div>
<div class="bottom-container">
<div class="expanded-pane-learn-more-container">
<div class="expanded-pane-learn-more"></div>
</div>
<div class="info"></div>
<div class="language-and-input-container">
<a class="language-and-input" href="#" role="button"
i18n-content="publicSessionLanguageAndInput"></a>
</div>
<button class="enter-button"
i18n-content="publicAccountEnter"
i18n-values="aria-label:publicAccountEnterAccessibleName">
......
......@@ -404,4 +404,36 @@ scoped_ptr<base::ListValue> GetLoginKeyboardLayouts(
return input_methods_list.Pass();
}
scoped_ptr<base::ListValue> GetKeyboardLayoutsForLocale(
const std::string& locale) {
input_method::InputMethodUtil* util =
input_method::InputMethodManager::Get()->GetInputMethodUtil();
std::vector<std::string> layouts = util->GetHardwareInputMethodIds();
std::vector<std::string> layouts_from_locale;
util->GetInputMethodIdsFromLanguageCode(
l10n_util::GetApplicationLocale(locale),
input_method::kKeyboardLayoutsOnly,
&layouts_from_locale);
layouts.insert(layouts.end(), layouts_from_locale.begin(),
layouts_from_locale.end());
std::string selected;
if (!layouts_from_locale.empty()) {
selected =
util->GetInputMethodDescriptorFromId(layouts_from_locale[0])->id();
}
scoped_ptr<base::ListValue> input_methods_list(new base::ListValue);
std::set<std::string> input_methods_added;
for (std::vector<std::string>::const_iterator it = layouts.begin();
it != layouts.end(); ++it) {
const input_method::InputMethodDescriptor* ime =
util->GetInputMethodDescriptorFromId(*it);
if (!InsertString(ime->id(), input_methods_added))
continue;
input_methods_list->Append(CreateInputMethodsEntry(*ime, selected));
}
return input_methods_list.Pass();
}
} // namespace chromeos
......@@ -55,6 +55,15 @@ scoped_ptr<base::ListValue> GetLoginKeyboardLayouts(
const std::string& locale,
const std::string& selected);
// Return a list of keyboard layouts that can be used for |locale|. Each list
// entry is a dictionary that contains data such as an ID and a display name.
// The list will consist of the device's hardware layouts, followed by a divider
// and locale-specific keyboard layouts, if any. All layouts supported for
// |locale| are returned, including those that produce non-Latin characters by
// default.
scoped_ptr<base::ListValue> GetKeyboardLayoutsForLocale(
const std::string& locale);
} // namespace chromeos
#endif // CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_L10N_UTIL_H_
......@@ -38,6 +38,7 @@
#include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/device_local_account.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/extensions/api/screenlock_private/screenlock_private_api.h"
......@@ -45,6 +46,7 @@
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/webui/chromeos/login/authenticated_user_email_retriever.h"
#include "chrome/browser/ui/webui/chromeos/login/error_screen_handler.h"
#include "chrome/browser/ui/webui/chromeos/login/l10n_util.h"
#include "chrome/browser/ui/webui/chromeos/login/native_window_delegate.h"
#include "chrome/browser/ui/webui/chromeos/login/network_state_informer.h"
#include "chrome/browser/ui/webui/chromeos/login/oobe_ui.h"
......@@ -53,6 +55,7 @@
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/power_manager_client.h"
#include "chromeos/ime/ime_keyboard.h"
#include "chromeos/ime/input_method_descriptor.h"
#include "chromeos/ime/input_method_manager.h"
#include "chromeos/login/auth/key.h"
#include "chromeos/login/auth/user_context.h"
......@@ -376,9 +379,13 @@ void SigninScreenHandler::DeclareLocalizedValues(
builder->Add("publicAccountInfoFormat", IDS_LOGIN_PUBLIC_ACCOUNT_INFO_FORMAT);
builder->Add("publicAccountReminder",
IDS_LOGIN_PUBLIC_ACCOUNT_SIGNOUT_REMINDER);
builder->Add("publicSessionLanguageAndInput",
IDS_LOGIN_PUBLIC_SESSION_LANGUAGE_AND_INPUT);
builder->Add("publicAccountEnter", IDS_LOGIN_PUBLIC_ACCOUNT_ENTER);
builder->Add("publicAccountEnterAccessibleName",
IDS_LOGIN_PUBLIC_ACCOUNT_ENTER_ACCESSIBLE_NAME);
builder->Add("publicSessionSelectLanguage", IDS_LANGUAGE_SELECTION_SELECT);
builder->Add("publicSessionSelectKeyboard", IDS_KEYBOARD_SELECTION_SELECT);
builder->Add("removeUserWarningText",
base::string16());
builder->AddF("removeSupervisedUserWarningText",
......@@ -742,6 +749,9 @@ void SigninScreenHandler::RegisterMessages() {
AddCallback("focusPod", &SigninScreenHandler::HandleFocusPod);
AddCallback("retrieveAuthenticatedUserEmail",
&SigninScreenHandler::HandleRetrieveAuthenticatedUserEmail);
AddCallback("getPublicSessionKeyboardLayouts",
&SigninScreenHandler::HandleGetPublicSessionKeyboardLayouts);
// This message is sent by the kiosk app menu, but is handled here
// so we can tell the delegate to launch the app.
......@@ -1280,6 +1290,15 @@ void SigninScreenHandler::HandleRetrieveAuthenticatedUserEmail(
Profile::FromWebUI(web_ui())->GetRequestContext()));
}
void SigninScreenHandler::HandleGetPublicSessionKeyboardLayouts(
const std::string& user_id,
const std::string& locale) {
web_ui()->CallJavascriptFunction(
"login.AccountPickerScreen.setPublicSessionKeyboardLayouts",
base::StringValue(user_id),
*GetKeyboardLayoutsForLocale(locale).release());
}
void SigninScreenHandler::HandleLaunchKioskApp(const std::string& app_id,
bool diagnostic_mode) {
UserContext context(user_manager::USER_TYPE_KIOSK_APP, app_id);
......
......@@ -355,7 +355,8 @@ class SigninScreenHandler
void HandleFocusPod(const std::string& user_id);
void HandleLaunchKioskApp(const std::string& app_id, bool diagnostic_mode);
void HandleRetrieveAuthenticatedUserEmail(double attempt_token);
void HandleGetPublicSessionKeyboardLayouts(const std::string& user_id,
const std::string& locale);
// Returns true iff
// (i) log in is restricted to some user list,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment