0

[DisableTrackpad] Implement 30 second timer for confirmation dialog

Bug: 368059319
Change-Id: Ie749db3f43df040136b54b98a2ad6230ec517765
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5933784
Commit-Queue: Jesulayomi Kupoluyi <lkupo@google.com>
Reviewed-by: Akihiro Ota <akihiroota@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1372322}
This commit is contained in:
LK Kupoluyi
2024-10-22 21:19:26 +00:00
committed by Chromium LUCI CQ
parent d685286338
commit 74f3cac016
9 changed files with 162 additions and 31 deletions

@ -3716,6 +3716,8 @@ test("ash_unittests") {
"accessibility/switch_access/point_scan_controller_unittest.cc",
"accessibility/test_event_recorder.cc",
"accessibility/test_event_recorder.h",
"accessibility/ui/accessibility_confirmation_dialog.cc",
"accessibility/ui/accessibility_confirmation_dialog.h",
"accessibility/ui/accessibility_focus_ring_controller_unittest.cc",
"accessibility/ui/accessibility_focus_ring_group_unittest.cc",
"accessibility/ui/accessibility_highlight_controller_unittest.cc",

@ -27,6 +27,7 @@
#include "ash/accessibility/mouse_keys/mouse_keys_controller.h"
#include "ash/accessibility/sticky_keys/sticky_keys_controller.h"
#include "ash/accessibility/switch_access/point_scan_controller.h"
#include "ash/accessibility/ui/accessibility_confirmation_dialog.h"
#include "ash/accessibility/ui/accessibility_highlight_controller.h"
#include "ash/accessibility/ui/accessibility_panel_layout_manager.h"
#include "ash/color_enhancement/color_enhancement_controller.h"
@ -117,6 +118,8 @@ using session_manager::SessionState;
namespace ash {
namespace {
// How much time before the time out dialog should close.
const int kDialogTimeoutSeconds = 30;
// How much distance to travel with each generated scroll event.
const int kScrollDelta = 40;
@ -3016,12 +3019,20 @@ void AccessibilityController::ShowDisableTouchpadDialog() {
prefs::kAccessibilityDisableTrackpadMode));
const std::u16string title =
l10n_util::GetStringUTF16(IDS_ASH_DISABLE_TOUCHPAD_DIALOG_TITLE);
// Construct the timeout message, leaving a placeholder for the countdown
// timer so that the string does not need to be completely rebuilt every
// timer tick.
constexpr char16_t kTimeoutPlaceHolder[] = u"$1";
const std::u16string description =
disable_touchpad_mode == DisableTouchpadMode::kAlways
? l10n_util::GetStringUTF16(
IDS_ASH_DISABLE_TOUCHPAD_DIALOG_DESCRIPTION)
: l10n_util::GetStringUTF16(
IDS_ASH_DISABLE_TOUCHPAD_DIALOG_EXTERNAL_MOUSE_DESCRIPTION);
? l10n_util::GetStringFUTF16(
IDS_ASH_DISABLE_TOUCHPAD_DIALOG_DESCRIPTION,
kTimeoutPlaceHolder)
: l10n_util::GetStringFUTF16(
IDS_ASH_DISABLE_TOUCHPAD_DIALOG_EXTERNAL_MOUSE_DESCRIPTION,
kTimeoutPlaceHolder);
ShowConfirmationDialog(
title, description, l10n_util::GetStringUTF16(IDS_ASH_CONFIRM_BUTTON),
@ -3031,7 +3042,8 @@ void AccessibilityController::ShowDisableTouchpadDialog() {
base::BindOnce(&AccessibilityController::OnDisableTouchpadDialogDismissed,
GetWeakPtr()),
base::BindOnce(&AccessibilityController::OnDisableTouchpadDialogDismissed,
GetWeakPtr()));
GetWeakPtr()),
kDialogTimeoutSeconds);
}
void AccessibilityController::OnDisableTouchpadDialogAccepted() {
@ -3455,7 +3467,8 @@ void AccessibilityController::ShowConfirmationDialog(
const std::u16string& cancel_name,
base::OnceClosure on_accept_callback,
base::OnceClosure on_cancel_callback,
base::OnceClosure on_close_callback) {
base::OnceClosure on_close_callback,
std::optional<int> timeout_seconds) {
if (confirmation_dialog_) {
// If a dialog is already being shown we do not show a new one.
// Instead, run the on_close_callback on the new dialog to indicate
@ -3467,7 +3480,7 @@ void AccessibilityController::ShowConfirmationDialog(
auto* dialog = new AccessibilityConfirmationDialog(
title, description, confirm_name, cancel_name,
std::move(on_accept_callback), std::move(on_cancel_callback),
std::move(on_close_callback));
std::move(on_close_callback), timeout_seconds);
// Save the dialog so it doesn't go out of scope before it is
// used and closed.
confirmation_dialog_ = dialog->GetWeakPtr();

@ -629,13 +629,15 @@ class ASH_EXPORT AccessibilityController
// pointer in the variable |confirmation_dialog_| below.
// This is also used to show the dialog for Select to Speak's enhanced network
// voices.
void ShowConfirmationDialog(const std::u16string& title,
const std::u16string& description,
const std::u16string& confirm_name,
const std::u16string& cancel_name,
base::OnceClosure on_accept_callback,
base::OnceClosure on_cancel_callback,
base::OnceClosure on_close_callback);
void ShowConfirmationDialog(
const std::u16string& title,
const std::u16string& description,
const std::u16string& confirm_name,
const std::u16string& cancel_name,
base::OnceClosure on_accept_callback,
base::OnceClosure on_cancel_callback,
base::OnceClosure on_close_callback,
std::optional<int> timeout_seconds = std::nullopt);
gfx::Rect GetConfirmationDialogBoundsInScreen();
void PreviewFlashNotification() const;

@ -16,6 +16,7 @@
#include "ash/accessibility/magnifier/docked_magnifier_controller.h"
#include "ash/accessibility/sticky_keys/sticky_keys_controller.h"
#include "ash/accessibility/test_accessibility_controller_client.h"
#include "ash/accessibility/ui/accessibility_confirmation_dialog.h"
#include "ash/constants/ash_constants.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
@ -64,8 +65,9 @@ namespace {
constexpr char kDictationLanguageUpgradedNudgeId[] =
"dictation_language_upgraded.nudge_id";
const int kUsbMouseDeviceId = 20;
const int kDialogTimeoutSeconds = 30;
const int kInternalTouchpadDeviceId = 30;
const int kUsbMouseDeviceId = 20;
ui::InputDevice GetSampleMouseUsb() {
return {kUsbMouseDeviceId, ui::INPUT_DEVICE_USB, "SampleMouseUsb"};
@ -2390,7 +2392,8 @@ TEST_F(AccessibilityControllerSelectToSpeakKeyboardShortcutTest,
class AccessibilityControllerDisableTouchpadTest : public AshTestBase {
protected:
AccessibilityControllerDisableTouchpadTest() = default;
AccessibilityControllerDisableTouchpadTest()
: AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
AccessibilityControllerDisableTouchpadTest(
const AccessibilityControllerDisableTouchpadTest&) = delete;
AccessibilityControllerDisableTouchpadTest& operator=(
@ -2416,6 +2419,13 @@ class AccessibilityControllerDisableTouchpadTest : public AshTestBase {
return Shell::Get()->session_controller()->GetLastActiveUserPrefService();
}
base::RepeatingTimer& confirmation_dialog_timer() {
return Shell::Get()
->accessibility_controller()
->GetConfirmationDialogForTest()
->timer_;
}
DisableTouchpadEventRewriter* disable_touchpad_event_rewriter() {
return Shell::Get()
->accessibility_controller()
@ -2597,4 +2607,43 @@ TEST_F(AccessibilityControllerDisableTouchpadTest,
ASSERT_TRUE(disable_touchpad_event_rewriter()->IsEnabled());
}
TEST_F(AccessibilityControllerDisableTouchpadTest, DialogClosesAfter30Seconds) {
SimulateOnlyInternalTouchpadConnected();
// Ensure confirmation dialog closes after 30 seconds when trackpad mode
// is set to "on mouse connected".
prefs()->SetInteger(
prefs::kAccessibilityDisableTrackpadMode,
static_cast<int>(DisableTouchpadMode::kOnExternalMouseConnected));
ASSERT_FALSE(disable_touchpad_event_rewriter()->IsEnabled());
SimulateExternalMouseConnected();
ASSERT_TRUE(disable_touchpad_event_rewriter()->IsEnabled());
ASSERT_NE(nullptr, controller()->GetConfirmationDialogForTest());
ASSERT_TRUE(confirmation_dialog_timer().IsRunning());
for (int i = 0; i < kDialogTimeoutSeconds; ++i) {
ASSERT_NE(nullptr, controller()->GetConfirmationDialogForTest());
task_environment()->FastForwardBy(base::Seconds(1));
}
ASSERT_FALSE(disable_touchpad_event_rewriter()->IsEnabled());
ASSERT_EQ(nullptr, controller()->GetConfirmationDialogForTest());
// Ensure confirmation dialog closes after 30 seconds when trackpad mode
// is set to "always".
prefs()->SetInteger(prefs::kAccessibilityDisableTrackpadMode,
static_cast<int>(DisableTouchpadMode::kAlways));
ASSERT_TRUE(disable_touchpad_event_rewriter()->IsEnabled());
ASSERT_TRUE(confirmation_dialog_timer().IsRunning());
for (int i = 0; i < kDialogTimeoutSeconds; ++i) {
ASSERT_NE(nullptr, controller()->GetConfirmationDialogForTest());
task_environment()->FastForwardBy(base::Seconds(1));
}
ASSERT_FALSE(disable_touchpad_event_rewriter()->IsEnabled());
ASSERT_EQ(nullptr, controller()->GetConfirmationDialogForTest());
}
} // namespace ash

@ -12,7 +12,10 @@
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "base/functional/bind.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/time_format.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/base/ui_base_types.h"
@ -24,6 +27,20 @@
namespace ash {
namespace {
std::u16string GetMessageWithTimeout(
const std::u16string& timeout_message_with_placeholder,
int time_remaining) {
const std::u16string remaining_time = ui::TimeFormat::Simple(
ui::TimeFormat::FORMAT_DURATION, ui::TimeFormat::LENGTH_LONG,
base::Seconds(time_remaining));
return base::ReplaceStringPlaceholders(timeout_message_with_placeholder,
remaining_time, /*offset=*/nullptr);
}
} // namespace
AccessibilityConfirmationDialog::AccessibilityConfirmationDialog(
const std::u16string& window_title_text,
const std::u16string& dialog_text,
@ -31,7 +48,10 @@ AccessibilityConfirmationDialog::AccessibilityConfirmationDialog(
const std::u16string& cancel_text,
base::OnceClosure on_accept_callback,
base::OnceClosure on_cancel_callback,
base::OnceClosure on_close_callback) {
base::OnceClosure on_close_callback,
std::optional<int> timeout_seconds)
: timeout_seconds_(timeout_seconds),
timeout_message_with_placeholder_(dialog_text) {
SetModalType(ui::mojom::ModalType::kSystem);
SetTitle(window_title_text);
SetButtonLabel(ui::mojom::DialogButton::kOk, confirm_text);
@ -46,11 +66,16 @@ AccessibilityConfirmationDialog::AccessibilityConfirmationDialog(
SetBorder(views::CreateEmptyBorder(
views::LayoutProvider::Get()->GetDialogInsetsForContentType(
views::DialogContentType::kText, views::DialogContentType::kText)));
std::unique_ptr<views::Label> label =
std::make_unique<views::Label>(dialog_text);
std::u16string dialog_string =
timeout_seconds.has_value()
? GetMessageWithTimeout(timeout_message_with_placeholder_,
time_remaining_)
: dialog_text;
label = AddChildView(std::make_unique<views::Label>(dialog_string));
label->SetMultiLine(true);
label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
AddChildView(std::move(label));
// Parent the dialog widget to the LockSystemModalContainer to ensure that it
// will get displayed on respective lock/signin or OOBE screen.
@ -67,6 +92,14 @@ AccessibilityConfirmationDialog::AccessibilityConfirmationDialog(
this, nullptr,
Shell::GetContainer(Shell::GetPrimaryRootWindow(), container_id));
widget->Show();
// If a timeout is specified, start the timer.
if (timeout_seconds_.has_value()) {
time_remaining_ = timeout_seconds_.value();
timer_.Start(
FROM_HERE, base::Seconds(1),
base::BindRepeating(&AccessibilityConfirmationDialog::OnTimerTick,
GetWeakPtr()));
}
}
AccessibilityConfirmationDialog::~AccessibilityConfirmationDialog() = default;
@ -75,6 +108,16 @@ bool AccessibilityConfirmationDialog::ShouldShowCloseButton() const {
return false;
}
void AccessibilityConfirmationDialog::OnTimerTick() {
if (--time_remaining_ == 0) {
CancelDialog();
return;
}
label->SetText(GetMessageWithTimeout(timeout_message_with_placeholder_,
time_remaining_));
}
base::WeakPtr<AccessibilityConfirmationDialog>
AccessibilityConfirmationDialog::GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();

@ -9,21 +9,28 @@
#include "base/functional/callback_forward.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "ui/views/window/dialog_delegate.h"
namespace ash {
namespace {
inline constexpr int kDefaultTimeoutInSeconds = 30;
}
// Defines a dialog for accessibility that require confirmation from users prior
// to performing some action.
class AccessibilityConfirmationDialog : public views::DialogDelegateView {
public:
AccessibilityConfirmationDialog(const std::u16string& window_title_text,
const std::u16string& dialog_text,
const std::u16string& confirm_text,
const std::u16string& cancel_text,
base::OnceClosure on_accept_callback,
base::OnceClosure on_cancel_callback,
base::OnceClosure on_close_callback);
AccessibilityConfirmationDialog(
const std::u16string& window_title_text,
const std::u16string& dialog_text,
const std::u16string& confirm_text,
const std::u16string& cancel_text,
base::OnceClosure on_accept_callback,
base::OnceClosure on_cancel_callback,
base::OnceClosure on_close_callback,
std::optional<int> timeout_seconds = std::nullopt);
~AccessibilityConfirmationDialog() override;
AccessibilityConfirmationDialog(const AccessibilityConfirmationDialog&) =
delete;
@ -36,6 +43,21 @@ class AccessibilityConfirmationDialog : public views::DialogDelegateView {
base::WeakPtr<AccessibilityConfirmationDialog> GetWeakPtr();
private:
friend class AccessibilityControllerDisableTouchpadTest;
void OnTimerTick();
// How much time before the time out dialog should close.
std::optional<int> timeout_seconds_;
// Amount of time left until the dialog closes.
int time_remaining_ = kDefaultTimeoutInSeconds;
// The timer to invoke OnTimerTick() every second. This cannot be
// OneShotTimer since the message contains text that has to be
// updated every second.
base::RepeatingTimer timer_;
// Placeholder string that shows the time left before the dialog closes.
const std::u16string timeout_message_with_placeholder_;
raw_ptr<views::Label> label = nullptr;
base::WeakPtrFactory<AccessibilityConfirmationDialog> weak_ptr_factory_{this};
};

@ -7963,12 +7963,12 @@ New install
<message name="IDS_ASH_DISABLE_TOUCHPAD_DIALOG_DESCRIPTION" desc="Content of the dialog for disabling the internal touchpad.">
The built-in touchpad is disabled. Use your keyboard or another pointer device to confirm the change.
Otherwise the built-in touchpad will be re-enabled in 30 seconds.
Otherwise the built-in touchpad will be re-enabled in <ph name="TIMEOUT_SECONDS">$1<ex>30 seconds</ex></ph>.
</message>
<message name="IDS_ASH_DISABLE_TOUCHPAD_DIALOG_EXTERNAL_MOUSE_DESCRIPTION" desc="Sign out button.">
The built-in touchpad is disabled because a mouse is connected. Use the mouse or keyboard to confirm the change.
Otherwise the built-in touchpad will be re-enabled in 30 seconds.
Otherwise the built-in touchpad will be re-enabled in <ph name="TIMEOUT_SECONDS">$1<ex>30 seconds</ex></ph>.
</message>
<!-- Guest session confirmation dialog -->

@ -1 +1 @@
c978c212d711cfbc5d9918b0dd1217de74a292f1
ce5298f6ee9c2256048580326bb10584c8988b76

@ -1 +1 @@
1f7588e1d96d9c819091dbcc264908cd940646e3
69e438c76d72b4a401a7faa22284ed88c303535d