0

Reland "Reland "[CrOS] Introduce Kiosk app default message for Kiosk

SKU""

This is a reland of commit 91bed44fa3

Original change's description:
> Reland "[CrOS] Introduce Kiosk app default message for Kiosk SKU"
>
> This is a reland of commit f831febf00
>
> Revert CL is crrev.com/c/3586949.
>
> Original change's description:
> > [CrOS] Introduce Kiosk app default message for Kiosk SKU
> >
> > For device with kiosk SKU, a kiosk app default message would be shown
> > if kiosk apps are not set up. (go/kiosk-sku-prd)
> >
> > Implementation screenshots
> > (https://screenshot.googleplex.com/7d298sSr6JkqhaJ)
> >
> > Bug: 1307303
> > Change-Id: Ibd32bbfd1bd07ffcf06bd6240726d039025237cb
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3575382
> > Reviewed-by: Denis Kuznetsov <antrim@chromium.org>
> > Commit-Queue: Sherri Lin <sherrilin@google.com>
> > Cr-Commit-Position: refs/heads/main@{#992770}
>
> Bug: 1307303
> Change-Id: I27857e41c9003dc3205c9b5e88bac8c069ffce16
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3599039
> Reviewed-by: Denis Kuznetsov <antrim@chromium.org>
> Commit-Queue: Sherri Lin <sherrilin@google.com>
> Cr-Commit-Position: refs/heads/main@{#995310}

Bug: 1307303
Change-Id: I0e667fec7888c23239f4f779354af859d146c7bb
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3603301
Reviewed-by: Denis Kuznetsov <antrim@chromium.org>
Commit-Queue: Sherri Lin <sherrilin@google.com>
Cr-Commit-Position: refs/heads/main@{#996051}
This commit is contained in:
Sherri Lin
2022-04-26 08:43:56 +00:00
committed by Chromium LUCI CQ
parent 37c36e0d2b
commit f098fd8e4d
11 changed files with 372 additions and 7 deletions

@ -635,6 +635,8 @@ component("ash") {
"login/ui/hover_notifier.h",
"login/ui/image_parser.cc",
"login/ui/image_parser.h",
"login/ui/kiosk_app_default_message.cc",
"login/ui/kiosk_app_default_message.h",
"login/ui/lock_contents_view.cc",
"login/ui/lock_contents_view.h",
"login/ui/lock_debug_view.cc",

@ -4191,6 +4191,9 @@ Here are some things you can try to get started.
<message name="IDS_SHELF_KIOSK_APP_INSTRUCTION" desc="Instructions to show users where to start using kiosk apps.">
Open an app to get started
</message>
<message name="IDS_SHELF_KIOSK_APP_SETUP" desc="Notifying users to ask their admins to setup the kiosk app.">
Ask your administrator to set up this device in the Google Admin console
</message>
<message name="IDS_APP_ACCESSIBILITY_BLOCKED_INSTALLED_APP_ANNOUNCEMENT" desc="Accessibility text to specify a search result is a blocked installed app.">
<ph name="APP_NAME">$1<ex>Files</ex></ph>, Installed App, Blocked
</message>

@ -0,0 +1 @@
a8c2ee402f4660daac50ec1a3a0e57900a56b9a3

@ -0,0 +1,106 @@
// Copyright 2022 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 "ash/login/ui/kiosk_app_default_message.h"
#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/tray/tray_popup_utils.h"
#include "ash/system/tray/tray_utils.h"
#include "components/vector_icons/vector_icons.h"
#include "third_party/skia/include/core/SkColor.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/models/image_model.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/layout/layout_provider.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
// The icon size of the kiosk app message.
constexpr int kIconSize = 16;
// The line height of the kiosk app message title.
constexpr int kTitleLineHeight = 20;
} // namespace
KioskAppDefaultMessage::KioskAppDefaultMessage()
: BubbleDialogDelegateView(nullptr, views::BubbleBorder::NONE),
background_animator_(
/* Don't pass the Shelf so the translucent color is always used. */
nullptr,
Shell::Get()->wallpaper_controller()) {
auto* layout_provider = views::LayoutProvider::Get();
set_margins(gfx::Insets(layout_provider->GetDistanceMetric(
views::DISTANCE_DIALOG_CONTENT_MARGIN_TOP_CONTROL)));
SetShowCloseButton(false);
SetButtons(ui::DIALOG_BUTTON_NONE);
// Bubbles that use transparent colors should not paint their ClientViews to a
// layer as doing so could result in visual artifacts.
SetPaintClientToLayer(false);
background_animator_.Init(ShelfBackgroundType::kDefaultBg);
background_animator_observation_.Observe(&background_animator_);
views::FlexLayout* layout =
SetLayoutManager(std::make_unique<views::FlexLayout>());
layout->SetOrientation(views::LayoutOrientation::kHorizontal);
layout->SetMainAxisAlignment(views::LayoutAlignment::kCenter);
layout->SetCrossAxisAlignment(views::LayoutAlignment::kCenter);
// Set up the icon.
icon_ = AddChildView(std::make_unique<views::ImageView>());
icon_->SetProperty(
views::kMarginsKey,
gfx::Insets::TLBR(/*top=*/0, /*left=*/0, /*bottom=*/0,
layout_provider->GetDistanceMetric(
views::DISTANCE_RELATED_CONTROL_HORIZONTAL)));
// Set up the title view.
title_ = AddChildView(std::make_unique<views::Label>());
title_->SetText(l10n_util::GetStringUTF16(IDS_SHELF_KIOSK_APP_SETUP));
title_->SetLineHeight(kTitleLineHeight);
title_->SetMultiLine(true);
TrayPopupUtils::SetLabelFontList(title_,
TrayPopupUtils::FontStyle::kSmallTitle);
views::DialogDelegate::CreateDialogWidget(
this, nullptr /* context */,
Shell::GetContainer(ash::Shell::GetRootWindowForNewWindows(),
kShellWindowId_SettingBubbleContainer) /* parent */);
GetBubbleFrameView()->SetCornerRadius(
views::LayoutProvider::Get()->GetCornerRadiusMetric(
views::Emphasis::kHigh));
GetBubbleFrameView()->SetBackgroundColor(
AshColorProvider::Get()->GetBaseLayerColor(
AshColorProvider::BaseLayerType::kTransparent90));
}
KioskAppDefaultMessage::~KioskAppDefaultMessage() = default;
void KioskAppDefaultMessage::OnThemeChanged() {
views::View::OnThemeChanged();
auto* color_provider = AshColorProvider::Get();
SkColor icon_color = color_provider->GetContentLayerColor(
AshColorProvider::ContentLayerType::kButtonIconColor);
icon_->SetImage(gfx::CreateVectorIcon(vector_icons::kErrorOutlineIcon,
kIconSize, icon_color));
SkColor label_color = color_provider->GetContentLayerColor(
AshColorProvider::ContentLayerType::kTextColorPrimary);
title_->SetEnabledColor(label_color);
}
} // namespace ash

@ -0,0 +1,48 @@
// Copyright 2022 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 ASH_LOGIN_UI_KIOSK_APP_DEFAULT_MESSAGE_H_
#define ASH_LOGIN_UI_KIOSK_APP_DEFAULT_MESSAGE_H_
#include "ash/ash_export.h"
#include "ash/shelf/shelf_background_animator.h"
#include "ash/shelf/shelf_background_animator_observer.h"
#include "base/scoped_observation.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
namespace views {
class ImageView;
class Label;
} // namespace views
namespace ash {
// The implementation of kiosk app default message for the shelf.
// KioskAppDefaultMessage is owned by itself and would be destroyed when its
// widget got destroyed, which happened when the widget's window got destroyed.
class ASH_EXPORT KioskAppDefaultMessage
: public views::BubbleDialogDelegateView,
public ShelfBackgroundAnimatorObserver {
public:
KioskAppDefaultMessage();
KioskAppDefaultMessage(const KioskAppDefaultMessage&) = delete;
KioskAppDefaultMessage& operator=(const KioskAppDefaultMessage&) = delete;
~KioskAppDefaultMessage() override;
// views::View:
void OnThemeChanged() override;
private:
views::ImageView* icon_ = nullptr;
views::Label* title_ = nullptr;
ShelfBackgroundAnimator background_animator_;
base::ScopedObservation<ShelfBackgroundAnimator,
ShelfBackgroundAnimatorObserver>
background_animator_observation_{this};
};
} // namespace ash
#endif // ASH_LOGIN_UI_KIOSK_APP_DEFAULT_MESSAGE_H_

@ -18,6 +18,7 @@
#include "ash/ime/ime_controller_impl.h"
#include "ash/login/login_screen_controller.h"
#include "ash/login/ui/bottom_status_indicator.h"
#include "ash/login/ui/kiosk_app_default_message.h"
#include "ash/login/ui/lock_screen.h"
#include "ash/login/ui/lock_screen_media_controls_view.h"
#include "ash/login/ui/login_auth_user_view.h"
@ -327,7 +328,7 @@ class UserAddingScreenIndicator : public views::View {
layout_manager->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kStart);
info_icon_ = AddChildView(new views::ImageView());
info_icon_ = AddChildView(std::make_unique<views::ImageView>());
info_icon_->SetPreferredSize(gfx::Size(kInfoIconSizeDp, kInfoIconSizeDp));
std::u16string message =
@ -434,6 +435,11 @@ LockContentsView::TestApi::TestApi(LockContentsView* view) : view_(view) {}
LockContentsView::TestApi::~TestApi() = default;
KioskAppDefaultMessage* LockContentsView::TestApi::kiosk_default_message()
const {
return view_->kiosk_default_message_;
}
LoginBigUserView* LockContentsView::TestApi::primary_big_view() const {
return view_->primary_big_view_;
}
@ -671,12 +677,11 @@ LockContentsView::LockContentsView(
tooltip_bubble_->SetPadding(kHorizontalPaddingLoginTooltipViewDp,
kVerticalPaddingLoginTooltipViewDp);
management_bubble_ = new ManagementBubble(
management_bubble_ = AddChildView(std::make_unique<ManagementBubble>(
l10n_util::GetStringFUTF16(IDS_ASH_LOGIN_ENTERPRISE_MANAGED_POP_UP,
ui::GetChromeOSDeviceName(),
base::UTF8ToUTF16(enterprise_domain_manager)),
bottom_status_indicator_);
AddChildView(management_bubble_);
bottom_status_indicator_));
warning_banner_bubble_ = AddChildView(std::make_unique<LoginErrorBubble>());
warning_banner_bubble_->set_persistent(true);
@ -688,7 +693,6 @@ LockContentsView::LockContentsView(
user_adding_screen_indicator_ =
AddChildView(std::make_unique<UserAddingScreenIndicator>());
}
OnLockScreenNoteStateChanged(initial_note_action_state);
chromeos::PowerManagerClient::Get()->AddObserver(this);
RegisterAccelerators();
@ -833,6 +837,26 @@ void LockContentsView::ShowParentAccessDialog() {
Shell::Get()->login_screen_controller()->ShowParentAccessButton(false);
}
void LockContentsView::SetKioskAppsButtonPresence(
bool is_kiosk_apps_button_present) {
if (!kiosk_license_mode_)
return;
// check if the kiosk app button is visible
if (is_kiosk_apps_button_present) {
if (kiosk_default_message_)
kiosk_default_message_->GetWidget()->Hide();
} else {
if (!kiosk_default_message_) {
// KioskAppDefaultMessage is owned by itself and would be destroyed when
// its widget got destroyed, which happened when the widget's window got
// destroyed.
kiosk_default_message_ = new KioskAppDefaultMessage();
}
kiosk_default_message_->GetWidget()->Show();
}
}
void LockContentsView::Layout() {
View::Layout();
LayoutTopHeader();
@ -939,8 +963,7 @@ void LockContentsView::OnUsersChanged(const std::vector<LoginUserInfo>& users) {
views::BoxLayout::CrossAxisAlignment::kCenter);
// TODO(crbug.com/1307303): Determine if this is a kiosk license device.
bool kiosk_license_mode = false;
if (kiosk_license_mode)
if (kiosk_license_mode_)
return;
// If there are no users, show GAIA signin if login.
@ -2563,6 +2586,11 @@ void LockContentsView::OnBackToSigninButtonTapped() {
/*prefilled_account=*/EmptyAccountId());
}
void LockContentsView::SetKioskLicenseModeForTesting(
bool is_kiosk_license_mode) {
kiosk_license_mode_ = is_kiosk_license_mode;
}
BEGIN_METADATA(LockContentsView, NonAccessibleView)
END_METADATA

@ -46,6 +46,7 @@ class BoxLayout;
namespace ash {
class KioskAppDefaultMessage;
class LockScreenMediaControlsView;
class LoginAuthUserView;
class LoginBigUserView;
@ -89,6 +90,7 @@ class ASH_EXPORT LockContentsView
explicit TestApi(LockContentsView* view);
~TestApi();
KioskAppDefaultMessage* kiosk_default_message() const;
LoginBigUserView* primary_big_view() const;
LoginBigUserView* opt_secondary_big_view() const;
AccountId focused_user() const;
@ -152,6 +154,7 @@ class ASH_EXPORT LockContentsView
void ShowAdbEnabled();
void ToggleSystemInfo();
void ShowParentAccessDialog();
void SetKioskAppsButtonPresence(bool is_kiosk_apps_button_present);
// views::View:
void Layout() override;
@ -253,6 +256,9 @@ class ASH_EXPORT LockContentsView
// Called for debugging to remove forced online sign-in form |user|.
void UndoForceOnlineSignInForUserForDebug(const AccountId& user);
// Test API. Set device to have kiosk license.
void SetKioskLicenseModeForTesting(bool is_kiosk_license_mode);
// Called by LockScreenMediaControlsView.
void CreateMediaControlsLayout();
void HideMediaControlsLayout();
@ -487,6 +493,10 @@ class ASH_EXPORT LockContentsView
// Contains authentication user and the additional user views.
NonAccessibleView* main_view_ = nullptr;
// If the kiosk app button is not visible, the kiosk app default message would
// be shown.
raw_ptr<KioskAppDefaultMessage> kiosk_default_message_ = nullptr;
// Actions that should be executed before a new layout happens caused by a
// display change (eg. screen rotation). A full layout pass is performed after
// all actions are executed.
@ -532,6 +542,10 @@ class ASH_EXPORT LockContentsView
// screen note state.
bool disable_lock_screen_note_ = false;
// TODO(1307303): Change it to the real function that checks if device is with
// Kiosk License
bool kiosk_license_mode_ = false;
// Whether the system information should be displayed or not be displayed
// forcedly according to policy settings.
absl::optional<bool> enable_system_info_enforced_ = absl::nullopt;

@ -17,6 +17,7 @@
#include "ash/login/mock_login_screen_client.h"
#include "ash/login/ui/arrow_button_view.h"
#include "ash/login/ui/fake_login_detachable_base_model.h"
#include "ash/login/ui/kiosk_app_default_message.h"
#include "ash/login/ui/lock_screen.h"
#include "ash/login/ui/lock_screen_media_controls_view.h"
#include "ash/login/ui/login_auth_user_view.h"
@ -35,6 +36,7 @@
#include "ash/public/mojom/tray_action.mojom.h"
#include "ash/root_window_controller.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/login_shelf_view.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_navigation_widget.h"
#include "ash/shelf/shelf_widget.h"
@ -3228,4 +3230,156 @@ TEST_F(LockContentsViewUnitTest, SmartLockStateHidesAuthErrorMessage) {
EXPECT_FALSE(test_api.auth_error_bubble()->GetVisible());
}
class LockContentsViewWithKioskLicenseTest : public LoginTestBase {
protected:
LockContentsViewWithKioskLicenseTest() = default;
LockContentsViewWithKioskLicenseTest(LockContentsViewWithKioskLicenseTest&) =
delete;
LockContentsViewWithKioskLicenseTest& operator=(
LockContentsViewWithKioskLicenseTest&) = delete;
~LockContentsViewWithKioskLicenseTest() override = default;
void SetUp() override {
set_start_session(false);
LoginTestBase::SetUp();
login_shelf_view_ = GetPrimaryShelf()->shelf_widget()->login_shelf_view();
// Set initial states.
NotifySessionStateChanged(session_manager::SessionState::OOBE);
}
void SetNumberOfKioskApps(int number_apps) {
std::vector<KioskAppMenuEntry> kiosk_apps(number_apps);
login_shelf_view_->SetKioskApps(kiosk_apps);
}
void NotifySessionStateChanged(session_manager::SessionState state) {
GetSessionControllerClient()->SetSessionState(state);
GetSessionControllerClient()->FlushForTest();
}
LoginShelfView* login_shelf_view_ = nullptr; // Unowned.
};
// Checks default message hides if device is with kiosk license but with apps.
TEST_F(LockContentsViewWithKioskLicenseTest,
ShouldNotShowKioskDefaultMessageWithApps) {
// Set up
const bool is_kiosk_license_mode = true;
login_shelf_view_->SetKioskLicenseModeForTesting(is_kiosk_license_mode);
// Show login screen with no user.
ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
LockContentsView* lock_contents_view =
LockScreen::TestApi(LockScreen::Get()).contents_view();
lock_contents_view->SetKioskLicenseModeForTesting(is_kiosk_license_mode);
LockContentsView::TestApi test_api(lock_contents_view);
SetUserCount(0);
std::unique_ptr<views::Widget> widget =
CreateWidgetWithContent(lock_contents_view);
NotifySessionStateChanged(session_manager::SessionState::LOGIN_PRIMARY);
SetNumberOfKioskApps(1);
EXPECT_FALSE(test_api.kiosk_default_message());
}
// Checks default message hidden if device is not with kiosk license and has
// no apps.
TEST_F(LockContentsViewWithKioskLicenseTest, ShouldHideKioskDefaultMessage) {
// Set up
const bool is_kiosk_license_mode = false;
login_shelf_view_->SetKioskLicenseModeForTesting(is_kiosk_license_mode);
// Show login screen with no user.
ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
LockContentsView* lock_contents_view =
LockScreen::TestApi(LockScreen::Get()).contents_view();
lock_contents_view->SetKioskLicenseModeForTesting(is_kiosk_license_mode);
LockContentsView::TestApi test_api(lock_contents_view);
SetUserCount(0);
std::unique_ptr<views::Widget> widget =
CreateWidgetWithContent(lock_contents_view);
NotifySessionStateChanged(session_manager::SessionState::LOGIN_PRIMARY);
SetNumberOfKioskApps(0);
EXPECT_FALSE(test_api.kiosk_default_message());
}
// Checks default message appeared if device is with kiosk license and no
// kiosk app is set up.
TEST_F(LockContentsViewWithKioskLicenseTest,
ShouldShowKioskDefaultMessageWithoutApps) {
// Set up
const bool is_kiosk_license_mode = true;
login_shelf_view_->SetKioskLicenseModeForTesting(is_kiosk_license_mode);
// Show login screen with no user.
ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
LockContentsView* lock_contents_view =
LockScreen::TestApi(LockScreen::Get()).contents_view();
lock_contents_view->SetKioskLicenseModeForTesting(is_kiosk_license_mode);
LockContentsView::TestApi test_api(lock_contents_view);
SetUserCount(0);
std::unique_ptr<views::Widget> widget =
CreateWidgetWithContent(lock_contents_view);
NotifySessionStateChanged(session_manager::SessionState::LOGIN_PRIMARY);
SetNumberOfKioskApps(0);
EXPECT_TRUE(test_api.kiosk_default_message());
EXPECT_TRUE(test_api.kiosk_default_message()->GetWidget()->IsVisible());
}
// Checks default message appeared if device is with kiosk license, no
// kiosk app is set up and has users.
TEST_F(LockContentsViewWithKioskLicenseTest,
ShouldShowKioskDefaultMessageWithoutAppsWithUsers) {
// Set up
const bool is_kiosk_license_mode = true;
login_shelf_view_->SetKioskLicenseModeForTesting(is_kiosk_license_mode);
// Show login screen with one user.
ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
LockContentsView* lock_contents_view =
LockScreen::TestApi(LockScreen::Get()).contents_view();
lock_contents_view->SetKioskLicenseModeForTesting(is_kiosk_license_mode);
LockContentsView::TestApi test_api(lock_contents_view);
SetUserCount(1);
std::unique_ptr<views::Widget> widget =
CreateWidgetWithContent(lock_contents_view);
NotifySessionStateChanged(session_manager::SessionState::LOGIN_PRIMARY);
SetNumberOfKioskApps(0);
EXPECT_TRUE(test_api.kiosk_default_message());
EXPECT_TRUE(test_api.kiosk_default_message()->GetWidget()->IsVisible());
}
// Checks default message appeared if device is with kiosk license and no
// kiosk app is set up. After some kiosk app is set up, the default message
// shall disappear.
TEST_F(LockContentsViewWithKioskLicenseTest,
ShouldShowAndHideKioskDefaultMessageWithAppChanges) {
// Set up
const bool is_kiosk_license_mode = true;
login_shelf_view_->SetKioskLicenseModeForTesting(is_kiosk_license_mode);
// Show login screen with no user.
ASSERT_NO_FATAL_FAILURE(ShowLoginScreen());
LockContentsView* lock_contents_view =
LockScreen::TestApi(LockScreen::Get()).contents_view();
lock_contents_view->SetKioskLicenseModeForTesting(is_kiosk_license_mode);
LockContentsView::TestApi test_api(lock_contents_view);
SetUserCount(0);
std::unique_ptr<views::Widget> widget =
CreateWidgetWithContent(lock_contents_view);
NotifySessionStateChanged(session_manager::SessionState::LOGIN_PRIMARY);
SetNumberOfKioskApps(0);
EXPECT_TRUE(test_api.kiosk_default_message());
EXPECT_TRUE(test_api.kiosk_default_message()->GetWidget()->IsVisible());
SetNumberOfKioskApps(1);
EXPECT_TRUE(test_api.kiosk_default_message());
EXPECT_FALSE(test_api.kiosk_default_message()->GetWidget()->IsVisible());
}
} // namespace ash

@ -165,6 +165,10 @@ void LockScreen::ShowParentAccessDialog() {
contents_view_->ShowParentAccessDialog();
}
void LockScreen::SetKioskAppsButtonPresence(bool is_kiosk_apps_button_present) {
contents_view_->SetKioskAppsButtonPresence(is_kiosk_apps_button_present);
}
void LockScreen::OnLockScreenNoteStateChanged(mojom::TrayActionState state) {
Shell::Get()
->login_screen_controller()

@ -72,6 +72,7 @@ class ASH_EXPORT LockScreen : public TrayActionObserver,
void FocusNextUser();
void FocusPreviousUser();
void ShowParentAccessDialog();
void SetKioskAppsButtonPresence(bool has_kiosk_apps);
// TrayActionObserver:
void OnLockScreenNoteStateChanged(mojom::TrayActionState state) override;

@ -731,6 +731,10 @@ void LoginShelfView::SetKioskApps(
const std::vector<KioskAppMenuEntry>& kiosk_apps) {
kiosk_apps_button_->SetApps(kiosk_apps);
UpdateUi();
if (LockScreen::HasInstance()) {
LockScreen::Get()->SetKioskAppsButtonPresence(
kiosk_apps_button_->GetVisible());
}
}
void LoginShelfView::ConfigureKioskCallbacks(