| // Copyright 2017 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/lock_debug_view.h" |
| |
| #include <algorithm> |
| #include <map> |
| #include <memory> |
| #include <utility> |
| |
| #include "ash/detachable_base/detachable_base_pairing_status.h" |
| #include "ash/ime/ime_controller.h" |
| #include "ash/login/login_screen_controller.h" |
| #include "ash/login/ui/layout_util.h" |
| #include "ash/login/ui/lock_contents_view.h" |
| #include "ash/login/ui/lock_screen.h" |
| #include "ash/login/ui/login_data_dispatcher.h" |
| #include "ash/login/ui/login_detachable_base_model.h" |
| #include "ash/login/ui/non_accessible_view.h" |
| #include "ash/shell.h" |
| #include "base/optional.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "mojo/common/values_struct_traits.h" |
| #include "ui/base/ime/chromeos/ime_keyboard.h" |
| #include "ui/views/controls/button/md_text_button.h" |
| #include "ui/views/layout/box_layout.h" |
| #include "ui/views/view.h" |
| |
| namespace ash { |
| namespace { |
| |
| constexpr const char* kDebugUserNames[] = { |
| "Angelina Johnson", "Marcus Cohen", "Chris Wallace", |
| "Debbie Craig", "Stella Wong", "Stephanie Wade", |
| }; |
| |
| constexpr const char* kDebugDetachableBases[] = {"Base A", "Base B", "Base C"}; |
| |
| constexpr const char kDebugOsVersion[] = |
| "Chromium 64.0.3279.0 (Platform 10146.0.0 dev-channel peppy test)"; |
| constexpr const char kDebugEnterpriseInfo[] = "Asset ID: 1111"; |
| constexpr const char kDebugBluetoothName[] = "Bluetooth adapter"; |
| |
| // Additional state for a user that the debug UI needs to reference. |
| struct UserMetadata { |
| explicit UserMetadata(const mojom::UserInfoPtr& user_info) |
| : account_id(user_info->account_id) {} |
| |
| AccountId account_id; |
| bool enable_pin = false; |
| bool enable_click_to_unlock = false; |
| mojom::EasyUnlockIconId easy_unlock_id = mojom::EasyUnlockIconId::NONE; |
| |
| views::View* view = nullptr; |
| }; |
| |
| std::string DetachableBasePairingStatusToString( |
| DetachableBasePairingStatus pairing_status) { |
| switch (pairing_status) { |
| case DetachableBasePairingStatus::kNone: |
| return "No device"; |
| case DetachableBasePairingStatus::kAuthenticated: |
| return "Authenticated"; |
| case DetachableBasePairingStatus::kNotAuthenticated: |
| return "Not authenticated"; |
| case DetachableBasePairingStatus::kInvalidDevice: |
| return "Invalid device"; |
| } |
| return "Unknown"; |
| } |
| |
| } // namespace |
| |
| // Applies a series of user-defined transformations to a |LoginDataDispatcher| |
| // instance; this is used for debugging and development. The debug overlay uses |
| // this class to change what data is exposed to the UI. |
| class LockDebugView::DebugDataDispatcherTransformer |
| : public LoginDataDispatcher::Observer { |
| public: |
| DebugDataDispatcherTransformer( |
| mojom::TrayActionState initial_lock_screen_note_state, |
| LoginDataDispatcher* dispatcher) |
| : root_dispatcher_(dispatcher), |
| lock_screen_note_state_(initial_lock_screen_note_state) { |
| root_dispatcher_->AddObserver(this); |
| } |
| ~DebugDataDispatcherTransformer() override { |
| root_dispatcher_->RemoveObserver(this); |
| } |
| |
| LoginDataDispatcher* debug_dispatcher() { return &debug_dispatcher_; } |
| |
| // Changes the number of displayed users to |count|. |
| void SetUserCount(int count) { |
| DCHECK(!root_users_.empty()); |
| |
| count = std::max(count, 1); |
| |
| // Trim any extra debug users. |
| if (debug_users_.size() > size_t{count}) |
| debug_users_.erase(debug_users_.begin() + count, debug_users_.end()); |
| |
| // Build |users|, add any new users to |debug_users|. |
| std::vector<mojom::LoginUserInfoPtr> users; |
| for (size_t i = 0; i < size_t{count}; ++i) { |
| const mojom::LoginUserInfoPtr& root_user = |
| root_users_[i % root_users_.size()]; |
| users.push_back(root_user->Clone()); |
| if (i >= root_users_.size()) { |
| users[i]->basic_user_info->account_id = AccountId::FromUserEmailGaiaId( |
| users[i]->basic_user_info->account_id.GetUserEmail() + |
| std::to_string(i), |
| users[i]->basic_user_info->account_id.GetGaiaId() + |
| std::to_string(i)); |
| } |
| if (i >= debug_users_.size()) |
| debug_users_.push_back(UserMetadata(users[i]->basic_user_info)); |
| } |
| |
| // Set debug user names. Useful for the stub user, which does not have a |
| // name set. |
| for (size_t i = 0; i < users.size(); ++i) |
| users[i]->basic_user_info->display_name = |
| kDebugUserNames[i % arraysize(kDebugUserNames)]; |
| |
| // User notification resets PIN state. |
| for (UserMetadata& user : debug_users_) |
| user.enable_pin = false; |
| |
| debug_dispatcher_.NotifyUsers(users); |
| } |
| |
| const AccountId& GetAccountIdForUserIndex(size_t user_index) { |
| DCHECK(user_index >= 0 && user_index < debug_users_.size()); |
| UserMetadata* debug_user = &debug_users_[user_index]; |
| return debug_user->account_id; |
| } |
| |
| // Activates or deactivates PIN for the user at |user_index|. |
| void TogglePinStateForUserIndex(size_t user_index) { |
| DCHECK(user_index >= 0 && user_index < debug_users_.size()); |
| UserMetadata* debug_user = &debug_users_[user_index]; |
| debug_user->enable_pin = !debug_user->enable_pin; |
| debug_dispatcher_.SetPinEnabledForUser(debug_user->account_id, |
| debug_user->enable_pin); |
| } |
| |
| // Enables click to auth for the user at |user_index|. |
| void CycleEasyUnlockForUserIndex(size_t user_index) { |
| DCHECK(user_index >= 0 && user_index < debug_users_.size()); |
| UserMetadata* debug_user = &debug_users_[user_index]; |
| |
| // EasyUnlockIconId state transition. |
| auto get_next_id = [](mojom::EasyUnlockIconId id) { |
| switch (id) { |
| case mojom::EasyUnlockIconId::NONE: |
| return mojom::EasyUnlockIconId::SPINNER; |
| case mojom::EasyUnlockIconId::SPINNER: |
| return mojom::EasyUnlockIconId::LOCKED; |
| case mojom::EasyUnlockIconId::LOCKED: |
| return mojom::EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED; |
| case mojom::EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED: |
| return mojom::EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT; |
| case mojom::EasyUnlockIconId::LOCKED_WITH_PROXIMITY_HINT: |
| return mojom::EasyUnlockIconId::HARDLOCKED; |
| case mojom::EasyUnlockIconId::HARDLOCKED: |
| return mojom::EasyUnlockIconId::UNLOCKED; |
| case mojom::EasyUnlockIconId::UNLOCKED: |
| return mojom::EasyUnlockIconId::NONE; |
| } |
| return mojom::EasyUnlockIconId::NONE; |
| }; |
| debug_user->easy_unlock_id = get_next_id(debug_user->easy_unlock_id); |
| |
| // Enable/disable click to unlock. |
| debug_user->enable_click_to_unlock = |
| debug_user->easy_unlock_id == mojom::EasyUnlockIconId::UNLOCKED; |
| |
| // Prepare icon that we will show. |
| auto icon = mojom::EasyUnlockIconOptions::New(); |
| icon->icon = debug_user->easy_unlock_id; |
| if (icon->icon == mojom::EasyUnlockIconId::SPINNER) { |
| icon->aria_label = base::ASCIIToUTF16("Icon is spinning"); |
| } else if (icon->icon == mojom::EasyUnlockIconId::LOCKED || |
| icon->icon == mojom::EasyUnlockIconId::LOCKED_TO_BE_ACTIVATED) { |
| icon->autoshow_tooltip = true; |
| icon->tooltip = base::ASCIIToUTF16( |
| "This is a long message to trigger overflow. This should show up " |
| "automatically. icon_id=" + |
| std::to_string(static_cast<int>(icon->icon))); |
| } else { |
| icon->tooltip = |
| base::ASCIIToUTF16("This should not show up automatically."); |
| } |
| |
| // Show icon and enable/disable click to unlock. |
| debug_dispatcher_.ShowEasyUnlockIcon(debug_user->account_id, icon); |
| debug_dispatcher_.SetClickToUnlockEnabledForUser( |
| debug_user->account_id, debug_user->enable_click_to_unlock); |
| } |
| |
| void ToggleLockScreenNoteButton() { |
| if (lock_screen_note_state_ == mojom::TrayActionState::kAvailable) { |
| lock_screen_note_state_ = mojom::TrayActionState::kNotAvailable; |
| } else { |
| lock_screen_note_state_ = mojom::TrayActionState::kAvailable; |
| } |
| |
| debug_dispatcher_.SetLockScreenNoteState(lock_screen_note_state_); |
| } |
| |
| void AddLockScreenDevChannelInfo(const std::string& os_version, |
| const std::string& enterprise_info, |
| const std::string& bluetooth_name) { |
| debug_dispatcher_.SetDevChannelInfo(os_version, enterprise_info, |
| bluetooth_name); |
| } |
| |
| // LoginDataDispatcher::Observer: |
| void OnUsersChanged( |
| const std::vector<mojom::LoginUserInfoPtr>& users) override { |
| // Update root_users_ to new source data. |
| root_users_.clear(); |
| for (auto& user : users) |
| root_users_.push_back(user->Clone()); |
| |
| // Rebuild debug users using new source data. |
| SetUserCount(debug_users_.size()); |
| } |
| void OnPinEnabledForUserChanged(const AccountId& user, |
| bool enabled) override { |
| // Forward notification only if the user is currently being shown. |
| for (size_t i = 0u; i < debug_users_.size(); ++i) { |
| if (debug_users_[i].account_id == user) { |
| debug_users_[i].enable_pin = enabled; |
| debug_dispatcher_.SetPinEnabledForUser(user, enabled); |
| break; |
| } |
| } |
| } |
| void OnClickToUnlockEnabledForUserChanged(const AccountId& user, |
| bool enabled) override { |
| // Forward notification only if the user is currently being shown. |
| for (size_t i = 0u; i < debug_users_.size(); ++i) { |
| if (debug_users_[i].account_id == user) { |
| debug_users_[i].enable_click_to_unlock = enabled; |
| debug_dispatcher_.SetClickToUnlockEnabledForUser(user, enabled); |
| break; |
| } |
| } |
| } |
| void OnLockScreenNoteStateChanged(mojom::TrayActionState state) override { |
| lock_screen_note_state_ = state; |
| debug_dispatcher_.SetLockScreenNoteState(state); |
| } |
| void OnShowEasyUnlockIcon( |
| const AccountId& user, |
| const mojom::EasyUnlockIconOptionsPtr& icon) override { |
| debug_dispatcher_.ShowEasyUnlockIcon(user, icon); |
| } |
| void OnDetachableBasePairingStatusChanged( |
| DetachableBasePairingStatus pairing_status) override { |
| debug_dispatcher_.SetDetachableBasePairingStatus(pairing_status); |
| } |
| |
| private: |
| // The debug overlay UI takes ground-truth data from |root_dispatcher_|, |
| // applies a series of transformations to it, and exposes it to the UI via |
| // |debug_dispatcher_|. |
| LoginDataDispatcher* root_dispatcher_; // Unowned. |
| LoginDataDispatcher debug_dispatcher_; |
| |
| // Original set of users from |root_dispatcher_|. |
| std::vector<mojom::LoginUserInfoPtr> root_users_; |
| |
| // Metadata for users that the UI is displaying. |
| std::vector<UserMetadata> debug_users_; |
| |
| // The current lock screen note action state. |
| mojom::TrayActionState lock_screen_note_state_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DebugDataDispatcherTransformer); |
| }; |
| |
| // In-memory wrapper around LoginDetachableBaseModel used by lock UI. |
| // It provides, methods to override the detachable base pairing state seen by |
| // the UI. |
| class LockDebugView::DebugLoginDetachableBaseModel |
| : public LoginDetachableBaseModel { |
| public: |
| static constexpr int kNullBaseId = -1; |
| |
| explicit DebugLoginDetachableBaseModel(LoginDataDispatcher* data_dispatcher) |
| : data_dispatcher_(data_dispatcher) {} |
| ~DebugLoginDetachableBaseModel() override = default; |
| |
| bool debugging_pairing_state() const { return pairing_status_.has_value(); } |
| |
| // Calculates the pairing status to which the model should be changed when |
| // button for cycling detachable base pairing statuses is clicked. |
| DetachableBasePairingStatus NextPairingStatus() const { |
| if (!pairing_status_.has_value()) |
| return DetachableBasePairingStatus::kNone; |
| |
| switch (*pairing_status_) { |
| case DetachableBasePairingStatus::kNone: |
| return DetachableBasePairingStatus::kAuthenticated; |
| case DetachableBasePairingStatus::kAuthenticated: |
| return DetachableBasePairingStatus::kNotAuthenticated; |
| case DetachableBasePairingStatus::kNotAuthenticated: |
| return DetachableBasePairingStatus::kInvalidDevice; |
| case DetachableBasePairingStatus::kInvalidDevice: |
| return DetachableBasePairingStatus::kNone; |
| } |
| |
| return DetachableBasePairingStatus::kNone; |
| } |
| |
| // Calculates the debugging detachable base ID that should become the paired |
| // base in the model when the button for cycling paired bases is clicked. |
| int NextBaseId() const { |
| return (base_id_ + 1) % arraysize(kDebugDetachableBases); |
| } |
| |
| // Gets the descripting text for currently paired base, if any. |
| std::string BaseButtonText() const { |
| if (base_id_ < 0) |
| return "No base"; |
| return kDebugDetachableBases[base_id_]; |
| } |
| |
| // Sets the model's pairing state - base pairing status, and the currently |
| // paired base ID. ID should be an index in |kDebugDetachableBases| array, and |
| // it should be set if pairing status is kAuthenticated. The base ID is |
| // ignored if pairing state is different than kAuthenticated. |
| void SetPairingState(DetachableBasePairingStatus pairing_status, |
| int base_id) { |
| pairing_status_ = pairing_status; |
| if (pairing_status == DetachableBasePairingStatus::kAuthenticated) { |
| CHECK_GE(base_id, 0); |
| CHECK_LT(base_id, static_cast<int>(arraysize(kDebugDetachableBases))); |
| base_id_ = base_id; |
| } else { |
| base_id_ = kNullBaseId; |
| } |
| |
| data_dispatcher_->SetDetachableBasePairingStatus(pairing_status); |
| } |
| |
| // Marks the paired base (as seen by the model) as the user's last used base. |
| // No-op if the current pairing status is different than kAuthenticated. |
| void SetBaseLastUsedForUser(const AccountId& account_id) { |
| if (GetPairingStatus() != DetachableBasePairingStatus::kAuthenticated) |
| return; |
| DCHECK_GE(base_id_, 0); |
| |
| last_used_bases_[account_id] = base_id_; |
| data_dispatcher_->SetDetachableBasePairingStatus(*pairing_status_); |
| } |
| |
| // Clears all in-memory pairing state. |
| void ClearDebugPairingState() { |
| pairing_status_ = base::nullopt; |
| base_id_ = kNullBaseId; |
| last_used_bases_.clear(); |
| |
| data_dispatcher_->SetDetachableBasePairingStatus( |
| DetachableBasePairingStatus::kNone); |
| } |
| |
| // LoginDetachableBaseModel: |
| DetachableBasePairingStatus GetPairingStatus() override { |
| if (!pairing_status_.has_value()) |
| return DetachableBasePairingStatus::kNone; |
| return *pairing_status_; |
| } |
| bool PairedBaseMatchesLastUsedByUser( |
| const mojom::UserInfo& user_info) override { |
| if (GetPairingStatus() != DetachableBasePairingStatus::kAuthenticated) |
| return false; |
| |
| if (last_used_bases_.count(user_info.account_id) == 0) |
| return true; |
| return last_used_bases_[user_info.account_id] == base_id_; |
| } |
| bool SetPairedBaseAsLastUsedByUser( |
| const mojom::UserInfo& user_info) override { |
| if (GetPairingStatus() != DetachableBasePairingStatus::kAuthenticated) |
| return false; |
| |
| last_used_bases_[user_info.account_id] = base_id_; |
| return true; |
| } |
| |
| private: |
| LoginDataDispatcher* data_dispatcher_; |
| |
| // In-memory detachable base pairing state. |
| base::Optional<DetachableBasePairingStatus> pairing_status_; |
| int base_id_ = kNullBaseId; |
| // Maps user account to the last used detachable base ID (base ID being the |
| // base's index in kDebugDetachableBases array). |
| std::map<AccountId, int> last_used_bases_; |
| |
| DISALLOW_COPY_AND_ASSIGN(DebugLoginDetachableBaseModel); |
| }; |
| |
| LockDebugView::LockDebugView(mojom::TrayActionState initial_note_action_state, |
| LoginDataDispatcher* data_dispatcher) |
| : debug_data_dispatcher_(std::make_unique<DebugDataDispatcherTransformer>( |
| initial_note_action_state, |
| data_dispatcher)) { |
| SetLayoutManager( |
| std::make_unique<views::BoxLayout>(views::BoxLayout::kHorizontal)); |
| |
| auto debug_detachable_base_model = |
| std::make_unique<DebugLoginDetachableBaseModel>(data_dispatcher); |
| debug_detachable_base_model_ = debug_detachable_base_model.get(); |
| |
| lock_ = new LockContentsView(initial_note_action_state, |
| debug_data_dispatcher_->debug_dispatcher(), |
| std::move(debug_detachable_base_model)); |
| AddChildView(lock_); |
| |
| debug_row_ = new NonAccessibleView(); |
| debug_row_->SetLayoutManager( |
| std::make_unique<views::BoxLayout>(views::BoxLayout::kHorizontal)); |
| AddChildView(debug_row_); |
| |
| per_user_action_column_ = new NonAccessibleView(); |
| per_user_action_column_->SetLayoutManager( |
| std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical)); |
| debug_row_->AddChildView(per_user_action_column_); |
| |
| auto* margin = new NonAccessibleView(); |
| margin->SetPreferredSize(gfx::Size(10, 10)); |
| debug_row_->AddChildView(margin); |
| |
| toggle_blur_ = AddButton("Blur"); |
| toggle_note_action_ = AddButton("Toggle note action"); |
| toggle_caps_lock_ = AddButton("Toggle caps lock"); |
| add_dev_channel_info_ = AddButton("Add dev channel info"); |
| add_user_ = AddButton("Add user"); |
| remove_user_ = AddButton("Remove user"); |
| toggle_auth_ = AddButton("Auth (allowed)"); |
| |
| RebuildDebugUserColumn(); |
| BuildDetachableBaseColumn(); |
| } |
| |
| LockDebugView::~LockDebugView() { |
| // Make sure debug_data_dispatcher_ lives longer than LockContentsView so |
| // pointer debug_dispatcher_ is always valid for LockContentsView. |
| RemoveChildView(lock_); |
| } |
| |
| void LockDebugView::Layout() { |
| views::View::Layout(); |
| lock_->SetBoundsRect(GetLocalBounds()); |
| debug_row_->SetPosition(gfx::Point()); |
| debug_row_->SizeToPreferredSize(); |
| } |
| |
| void LockDebugView::ButtonPressed(views::Button* sender, |
| const ui::Event& event) { |
| // Enable or disable wallpaper blur. |
| if (sender == toggle_blur_) { |
| LockScreen::Get()->ToggleBlurForDebug(); |
| return; |
| } |
| |
| // Enable or disable note action. |
| if (sender == toggle_note_action_) { |
| debug_data_dispatcher_->ToggleLockScreenNoteButton(); |
| return; |
| } |
| |
| // Enable or disable caps lock. |
| if (sender == toggle_caps_lock_) { |
| ImeController* ime_controller = Shell::Get()->ime_controller(); |
| ime_controller->SetCapsLockEnabled(!ime_controller->IsCapsLockEnabled()); |
| return; |
| } |
| |
| // Iteratively adds more info to the dev channel labels to test 7 permutations |
| // and then disables the button. |
| if (sender == add_dev_channel_info_) { |
| DCHECK_LT(num_dev_channel_info_clicks_, 7u); |
| ++num_dev_channel_info_clicks_; |
| if (num_dev_channel_info_clicks_ == 7u) |
| add_dev_channel_info_->SetEnabled(false); |
| |
| std::string os_version = |
| num_dev_channel_info_clicks_ / 4 ? kDebugOsVersion : ""; |
| std::string enterprise_info = |
| (num_dev_channel_info_clicks_ % 4) / 2 ? kDebugEnterpriseInfo : ""; |
| std::string bluetooth_name = |
| num_dev_channel_info_clicks_ % 2 ? kDebugBluetoothName : ""; |
| debug_data_dispatcher_->AddLockScreenDevChannelInfo( |
| os_version, enterprise_info, bluetooth_name); |
| return; |
| } |
| |
| // Add or remove a user. |
| if (sender == add_user_ || sender == remove_user_) { |
| if (sender == add_user_) |
| ++num_users_; |
| else if (sender == remove_user_) |
| --num_users_; |
| if (num_users_ < 1u) |
| num_users_ = 1u; |
| debug_data_dispatcher_->SetUserCount(num_users_); |
| RebuildDebugUserColumn(); |
| Layout(); |
| return; |
| } |
| |
| // Enable/disable auth. This is useful for testing auth failure scenarios on |
| // Linux Desktop builds, where the cryptohome dbus stub accepts all passwords |
| // as valid. |
| if (sender == toggle_auth_) { |
| auto get_next_auth_state = [](LoginScreenController::ForceFailAuth auth) { |
| switch (auth) { |
| case LoginScreenController::ForceFailAuth::kOff: |
| return LoginScreenController::ForceFailAuth::kImmediate; |
| case LoginScreenController::ForceFailAuth::kImmediate: |
| return LoginScreenController::ForceFailAuth::kDelayed; |
| case LoginScreenController::ForceFailAuth::kDelayed: |
| return LoginScreenController::ForceFailAuth::kOff; |
| } |
| NOTREACHED(); |
| return LoginScreenController::ForceFailAuth::kOff; |
| }; |
| auto get_auth_label = [](LoginScreenController::ForceFailAuth auth) { |
| switch (auth) { |
| case LoginScreenController::ForceFailAuth::kOff: |
| return "Auth (allowed)"; |
| case LoginScreenController::ForceFailAuth::kImmediate: |
| return "Auth (immediate fail)"; |
| case LoginScreenController::ForceFailAuth::kDelayed: |
| return "Auth (delayed fail)"; |
| } |
| NOTREACHED(); |
| return "Auth (allowed)"; |
| }; |
| force_fail_auth_ = get_next_auth_state(force_fail_auth_); |
| toggle_auth_->SetText(base::ASCIIToUTF16(get_auth_label(force_fail_auth_))); |
| Shell::Get() |
| ->login_screen_controller() |
| ->set_force_fail_auth_for_debug_overlay(force_fail_auth_); |
| return; |
| } |
| |
| if (sender == toggle_debug_detachable_base_) { |
| if (debug_detachable_base_model_->debugging_pairing_state()) { |
| debug_detachable_base_model_->ClearDebugPairingState(); |
| // In authenticated state, per user column has a button to mark the |
| // current base as last used for the user - ut should get removed when the |
| // detachable base debugging gets disabled. |
| RebuildDebugUserColumn(); |
| } else { |
| debug_detachable_base_model_->SetPairingState( |
| DetachableBasePairingStatus::kNone, |
| DebugLoginDetachableBaseModel::kNullBaseId); |
| } |
| UpdateDetachableBaseColumn(); |
| Layout(); |
| return; |
| } |
| |
| if (sender == cycle_detachable_base_status_) { |
| debug_detachable_base_model_->SetPairingState( |
| debug_detachable_base_model_->NextPairingStatus(), |
| debug_detachable_base_model_->NextBaseId()); |
| RebuildDebugUserColumn(); |
| UpdateDetachableBaseColumn(); |
| Layout(); |
| return; |
| } |
| |
| if (sender == cycle_detachable_base_id_) { |
| debug_detachable_base_model_->SetPairingState( |
| DetachableBasePairingStatus::kAuthenticated, |
| debug_detachable_base_model_->NextBaseId()); |
| UpdateDetachableBaseColumn(); |
| Layout(); |
| return; |
| } |
| |
| for (size_t i = 0u; i < per_user_action_column_use_detachable_base_.size(); |
| ++i) { |
| if (per_user_action_column_use_detachable_base_[i] == sender) { |
| debug_detachable_base_model_->SetBaseLastUsedForUser( |
| debug_data_dispatcher_->GetAccountIdForUserIndex(i)); |
| return; |
| } |
| } |
| |
| // Enable or disable PIN. |
| for (size_t i = 0u; i < per_user_action_column_toggle_pin_.size(); ++i) { |
| if (per_user_action_column_toggle_pin_[i] == sender) |
| debug_data_dispatcher_->TogglePinStateForUserIndex(i); |
| } |
| |
| // Cycle easy unlock. |
| for (size_t i = 0u; |
| i < per_user_action_column_cycle_easy_unlock_state_.size(); ++i) { |
| if (per_user_action_column_cycle_easy_unlock_state_[i] == sender) |
| debug_data_dispatcher_->CycleEasyUnlockForUserIndex(i); |
| } |
| } |
| |
| void LockDebugView::RebuildDebugUserColumn() { |
| per_user_action_column_->RemoveAllChildViews(true /*delete_children*/); |
| per_user_action_column_toggle_pin_.clear(); |
| per_user_action_column_cycle_easy_unlock_state_.clear(); |
| per_user_action_column_use_detachable_base_.clear(); |
| |
| for (size_t i = 0u; i < num_users_; ++i) { |
| auto* row = new NonAccessibleView(); |
| row->SetLayoutManager( |
| std::make_unique<views::BoxLayout>(views::BoxLayout::kHorizontal)); |
| |
| views::View* toggle_pin = |
| AddButton("Toggle PIN", false /*add_to_debug_row*/); |
| per_user_action_column_toggle_pin_.push_back(toggle_pin); |
| row->AddChildView(toggle_pin); |
| |
| views::View* toggle_click_auth = |
| AddButton("Cycle easy unlock", false /*add_to_debug_row*/); |
| per_user_action_column_cycle_easy_unlock_state_.push_back( |
| toggle_click_auth); |
| row->AddChildView(toggle_click_auth); |
| |
| if (debug_detachable_base_model_->debugging_pairing_state() && |
| debug_detachable_base_model_->GetPairingStatus() == |
| DetachableBasePairingStatus::kAuthenticated) { |
| views::View* use_detachable_base = AddButton("Set base used", false); |
| per_user_action_column_use_detachable_base_.push_back( |
| use_detachable_base); |
| row->AddChildView(use_detachable_base); |
| } |
| |
| per_user_action_column_->AddChildView(row); |
| } |
| } |
| |
| void LockDebugView::BuildDetachableBaseColumn() { |
| detachable_base_column_ = new NonAccessibleView(); |
| detachable_base_column_->SetLayoutManager( |
| std::make_unique<views::BoxLayout>(views::BoxLayout::kVertical)); |
| debug_row_->AddChildView(detachable_base_column_); |
| |
| UpdateDetachableBaseColumn(); |
| } |
| |
| void LockDebugView::UpdateDetachableBaseColumn() { |
| detachable_base_column_->RemoveAllChildViews(true /*delete_children*/); |
| |
| toggle_debug_detachable_base_ = AddButton("Detachable base debugging", false); |
| detachable_base_column_->AddChildView( |
| login_layout_util::WrapViewForPreferredSize( |
| toggle_debug_detachable_base_)); |
| |
| if (!debug_detachable_base_model_->debugging_pairing_state()) |
| return; |
| |
| const std::string kPairingStatusText = |
| std::string("Pairing status : ") + |
| DetachableBasePairingStatusToString( |
| debug_detachable_base_model_->GetPairingStatus()); |
| cycle_detachable_base_status_ = AddButton(kPairingStatusText, false); |
| |
| detachable_base_column_->AddChildView( |
| login_layout_util::WrapViewForPreferredSize( |
| cycle_detachable_base_status_)); |
| |
| cycle_detachable_base_id_ = |
| AddButton(debug_detachable_base_model_->BaseButtonText(), false); |
| bool base_authenticated = debug_detachable_base_model_->GetPairingStatus() == |
| DetachableBasePairingStatus::kAuthenticated; |
| cycle_detachable_base_id_->SetEnabled(base_authenticated); |
| |
| detachable_base_column_->AddChildView( |
| login_layout_util::WrapViewForPreferredSize(cycle_detachable_base_id_)); |
| } |
| |
| views::MdTextButton* LockDebugView::AddButton(const std::string& text, |
| bool add_to_debug_row) { |
| // Creates a button with |text| that cannot be focused. |
| auto* button = views::MdTextButton::Create(this, base::ASCIIToUTF16(text)); |
| button->SetFocusBehavior(views::View::FocusBehavior::NEVER); |
| if (add_to_debug_row) |
| debug_row_->AddChildView( |
| login_layout_util::WrapViewForPreferredSize(button)); |
| return button; |
| } |
| |
| } // namespace ash |