[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:

committed by
Chromium LUCI CQ

parent
d685286338
commit
74f3cac016
ash
BUILD.gn
accessibility
ash_strings.grdash_strings_grd
@ -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
|
Reference in New Issue
Block a user