Commit 3d8ab230 authored by yoshiki iguchi's avatar yoshiki iguchi Committed by Commit Bot

Support toast on lock screen

This CL supports showing toasts on lock screen.

If the device is locked and the toast can be in the lock screen,
ToastOverlay appears in kShellWindowId_LockSystemModalContainer
instead of kShellWindowId_SystemModalContainer.

kShellWindowId_LockSystemModalContainer can be used only during
the lock screen is visible, so we switched the container when the
lock status is changed.

Bug: b/79951342
Test: ash_unittests --gtest_filter="ToastManagerTest.*" passes

Change-Id: I40d62e21b437686fcfc4b119a0c9954207f3533d
Reviewed-on: https://chromium-review.googlesource.com/1154783
Commit-Queue: Yoshiki Iguchi <yoshiki@chromium.org>
Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#579866}
parent 3fb5b87b
......@@ -11,11 +11,13 @@ namespace ash {
ToastData::ToastData(std::string id,
const base::string16& text,
int32_t duration_ms,
const base::Optional<base::string16>& dismiss_text)
const base::Optional<base::string16>& dismiss_text,
bool visible_on_lock_screen)
: id(std::move(id)),
text(text),
duration_ms(duration_ms),
dismiss_text(dismiss_text) {}
dismiss_text(dismiss_text),
visible_on_lock_screen(visible_on_lock_screen) {}
ToastData::ToastData(const ToastData& other) = default;
......
......@@ -21,7 +21,8 @@ struct ASH_EXPORT ToastData {
ToastData(std::string id,
const base::string16& text,
int32_t duration_ms,
const base::Optional<base::string16>& dismiss_text);
const base::Optional<base::string16>& dismiss_text,
bool visible_on_lock_screen = false);
ToastData(const ToastData& other);
~ToastData();
......@@ -29,6 +30,7 @@ struct ASH_EXPORT ToastData {
base::string16 text;
int32_t duration_ms;
base::Optional<base::string16> dismiss_text;
bool visible_on_lock_screen;
};
} // namespace ash
......
......@@ -6,6 +6,8 @@
#include <algorithm>
#include "ash/session/session_controller.h"
#include "ash/shell.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/threading/thread_task_runner_handle.h"
......@@ -19,7 +21,9 @@ const int32_t kMinimumDurationMs = 200;
} // anonymous namespace
ToastManager::ToastManager() : weak_ptr_factory_(this) {}
ToastManager::ToastManager()
: locked_(Shell::Get()->session_controller()->IsScreenLocked()),
weak_ptr_factory_(this) {}
ToastManager::~ToastManager() = default;
......@@ -27,7 +31,7 @@ void ToastManager::Show(const ToastData& data) {
const std::string& id = data.id;
DCHECK(!id.empty());
if (current_toast_id_ == id) {
if (current_toast_data_ && current_toast_data_->id == id) {
// TODO(yoshiki): Replaces the visible toast.
return;
}
......@@ -47,7 +51,7 @@ void ToastManager::Show(const ToastData& data) {
}
void ToastManager::Cancel(const std::string& id) {
if (id == current_toast_id_) {
if (current_toast_data_ && current_toast_data_->id == id) {
overlay_->Show(false);
return;
}
......@@ -61,30 +65,44 @@ void ToastManager::Cancel(const std::string& id) {
void ToastManager::OnClosed() {
overlay_.reset();
current_toast_id_.clear();
current_toast_data_.reset();
// Show the next toast if available.
// Note that don't show during the lock state is changing, since we reshow
// manually after the state is changed. See OnLockStateChanged.
if (!queue_.empty())
ShowLatest();
}
void ToastManager::ShowLatest() {
DCHECK(!overlay_);
DCHECK(!current_toast_data_);
auto it = locked_ ? std::find_if(queue_.begin(), queue_.end(),
[](const auto& data) {
return data.visible_on_lock_screen;
})
: queue_.begin();
if (it == queue_.end())
return;
const ToastData data = std::move(queue_.front());
queue_.pop_front();
current_toast_data_ = *it;
queue_.erase(it);
current_toast_id_ = data.id;
serial_++;
overlay_.reset(new ToastOverlay(this, data.text, data.dismiss_text));
overlay_ = std::make_unique<ToastOverlay>(
this, current_toast_data_->text, current_toast_data_->dismiss_text,
current_toast_data_->visible_on_lock_screen && locked_);
overlay_->Show(true);
if (data.duration_ms != ToastData::kInfiniteDuration) {
int32_t duration_ms = std::max(data.duration_ms, kMinimumDurationMs);
if (current_toast_data_->duration_ms != ToastData::kInfiniteDuration) {
int32_t duration_ms =
std::max(current_toast_data_->duration_ms, kMinimumDurationMs);
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE, base::Bind(&ToastManager::OnDurationPassed,
weak_ptr_factory_.GetWeakPtr(), serial_),
FROM_HERE,
base::BindOnce(&ToastManager::OnDurationPassed,
weak_ptr_factory_.GetWeakPtr(), serial_),
base::TimeDelta::FromMilliseconds(duration_ms));
}
}
......@@ -94,4 +112,33 @@ void ToastManager::OnDurationPassed(int toast_number) {
overlay_->Show(false);
}
void ToastManager::OnSessionStateChanged(session_manager::SessionState state) {
// Note that this method is called before the lock state is changed and
// OnLockStateChanged is called.
const bool locked = state == session_manager::SessionState::LOCKED;
if ((locked != locked_) && current_toast_data_) {
// Re-queue the currently visible toast which is not for lock screen.
queue_.push_front(*current_toast_data_);
current_toast_data_.reset();
// Hide the currently visible toast without any animation.
overlay_.reset();
}
}
void ToastManager::OnLockStateChanged(bool locked) {
if (locked_ == locked)
return;
locked_ = locked;
DCHECK(!overlay_);
if (!overlay_ && !queue_.empty()) {
// Try to (re-)show a queued toast, which may be the queued one in
// OnSessionStateChanged..
ShowLatest();
}
}
} // namespace ash
......@@ -9,6 +9,7 @@
#include <string>
#include "ash/ash_export.h"
#include "ash/session/session_observer.h"
#include "ash/system/toast/toast_data.h"
#include "ash/system/toast/toast_overlay.h"
#include "base/containers/circular_deque.h"
......@@ -17,7 +18,8 @@
namespace ash {
// Class managing toast requests.
class ASH_EXPORT ToastManager : public ToastOverlay::Delegate {
class ASH_EXPORT ToastManager : public ToastOverlay::Delegate,
public SessionObserver {
public:
ToastManager();
~ToastManager() override;
......@@ -31,6 +33,10 @@ class ASH_EXPORT ToastManager : public ToastOverlay::Delegate {
// ToastOverlay::Delegate overrides:
void OnClosed() override;
// SessionObserver:
void OnSessionStateChanged(session_manager::SessionState state) override;
void OnLockStateChanged(bool locked) override;
private:
friend class ToastManagerTest;
......@@ -41,13 +47,15 @@ class ASH_EXPORT ToastManager : public ToastOverlay::Delegate {
int serial_for_testing() const { return serial_; }
void ResetSerialForTesting() { serial_ = 0; }
// ID of the toast which is currently shown. Empty if no toast is visible.
std::string current_toast_id_;
// Data of the toast which is currently shown. Empty if no toast is visible.
base::Optional<ToastData> current_toast_data_;
int serial_ = 0;
bool locked_;
base::circular_deque<ToastData> queue_;
std::unique_ptr<ToastOverlay> overlay_;
ScopedSessionObserver scoped_session_observer_{this};
base::WeakPtrFactory<ToastManager> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ToastManager);
......
......@@ -5,6 +5,7 @@
#include "ash/system/toast/toast_manager.h"
#include "ash/screen_util.h"
#include "ash/session/session_controller.h"
#include "ash/shelf/shelf.h"
#include "ash/shelf/shelf_constants.h"
#include "ash/shell.h"
......@@ -13,6 +14,7 @@
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "components/session_manager/session_manager_types.h"
#include "ui/compositor/scoped_animation_duration_scale_mode.h"
#include "ui/display/manager/display_manager.h"
#include "ui/views/widget/widget.h"
......@@ -76,10 +78,12 @@ class ToastManagerTest : public AshTestBase {
overlay->ClickDismissButtonForTesting(DummyEvent());
}
std::string ShowToast(const std::string& text, int32_t duration) {
std::string ShowToast(const std::string& text,
int32_t duration,
bool visible_on_lock_screen = false) {
std::string id = "TOAST_ID_" + base::UintToString(serial_++);
manager()->Show(
ToastData(id, base::ASCIIToUTF16(text), duration, base::string16()));
manager()->Show(ToastData(id, base::ASCIIToUTF16(text), duration,
base::string16(), visible_on_lock_screen));
return id;
}
......@@ -99,6 +103,13 @@ class ToastManagerTest : public AshTestBase {
void CancelToast(const std::string& id) { manager()->Cancel(id); }
void ChangeLockState(bool lock) {
mojom::SessionInfoPtr info_ptr = mojom::SessionInfo::New();
info_ptr->state = lock ? session_manager::SessionState::LOCKED
: session_manager::SessionState::LOGIN_PRIMARY;
Shell::Get()->session_controller()->SetSessionInfo(std::move(info_ptr));
}
private:
ToastManager* manager_ = nullptr;
unsigned int serial_ = 0;
......@@ -311,4 +322,76 @@ TEST_F(ToastManagerTest, CancelToast) {
EXPECT_EQ(2, GetToastSerial());
}
TEST_F(ToastManagerTest, ShowToastOnLockScreen) {
// Simulate device lock.
ChangeLockState(true);
// Trying to show a toast.
std::string id1 = ShowToast("TEXT1", ToastData::kInfiniteDuration);
// Confirm that it's not visible because it's queued.
EXPECT_EQ(nullptr, GetCurrentOverlay());
// Simulate device unlock.
ChangeLockState(false);
EXPECT_TRUE(GetCurrentOverlay());
EXPECT_EQ(base::ASCIIToUTF16("TEXT1"), GetCurrentText());
}
TEST_F(ToastManagerTest, ShowSupportedToastOnLockScreen) {
// Simulate device lock.
ChangeLockState(true);
// Trying to show a toast.
std::string id1 = ShowToast("TEXT1", ToastData::kInfiniteDuration,
/*visible_on_lock_screen=*/true);
// Confirm it's visible and not queued.
EXPECT_NE(nullptr, GetCurrentOverlay());
EXPECT_EQ(base::ASCIIToUTF16("TEXT1"), GetCurrentText());
// Simulate device unlock.
ChangeLockState(false);
// Confirm that the toast is still visible.
EXPECT_NE(nullptr, GetCurrentOverlay());
EXPECT_EQ(base::ASCIIToUTF16("TEXT1"), GetCurrentText());
}
TEST_F(ToastManagerTest, DeferToastByLockScreen) {
// Show a toast.
std::string id1 = ShowToast("TEXT1", ToastData::kInfiniteDuration,
/*visible_on_lock_screen=*/true);
EXPECT_NE(nullptr, GetCurrentOverlay());
EXPECT_EQ(base::ASCIIToUTF16("TEXT1"), GetCurrentText());
// Simulate device lock.
ChangeLockState(true);
// Confirm that it gets hidden.
EXPECT_NE(nullptr, GetCurrentOverlay());
EXPECT_EQ(base::ASCIIToUTF16("TEXT1"), GetCurrentText());
// Simulate device unlock.
ChangeLockState(false);
// Confirm that it gets visible again.
EXPECT_NE(nullptr, GetCurrentOverlay());
EXPECT_EQ(base::ASCIIToUTF16("TEXT1"), GetCurrentText());
}
TEST_F(ToastManagerTest, NotDeferToastForLockScreen) {
// Show a toast.
std::string id1 = ShowToast("TEXT1", ToastData::kInfiniteDuration,
/*visible_on_lock_screen=*/false);
EXPECT_NE(nullptr, GetCurrentOverlay());
EXPECT_EQ(base::ASCIIToUTF16("TEXT1"), GetCurrentText());
// Simulate device lock.
ChangeLockState(true);
// Confirm that it gets hidden.
EXPECT_EQ(nullptr, GetCurrentOverlay());
// Simulate device unlock.
ChangeLockState(false);
// Confirm that it gets visible again.
EXPECT_NE(nullptr, GetCurrentOverlay());
EXPECT_EQ(base::ASCIIToUTF16("TEXT1"), GetCurrentText());
}
} // namespace ash
......@@ -192,7 +192,8 @@ class ToastOverlayView : public views::View, public views::ButtonListener {
// ToastOverlay
ToastOverlay::ToastOverlay(Delegate* delegate,
const base::string16& text,
base::Optional<base::string16> dismiss_text)
base::Optional<base::string16> dismiss_text,
bool show_on_lock_screen)
: delegate_(delegate),
text_(text),
dismiss_text_(dismiss_text),
......@@ -210,7 +211,8 @@ ToastOverlay::ToastOverlay(Delegate* delegate,
params.bounds = CalculateOverlayBounds();
// Show toasts above the app list and below the lock screen.
params.parent = Shell::GetRootWindowForNewWindows()->GetChildById(
kShellWindowId_SystemModalContainer);
show_on_lock_screen ? kShellWindowId_LockSystemModalContainer
: kShellWindowId_SystemModalContainer);
overlay_widget_->Init(params);
overlay_widget_->SetVisibilityChangedAnimationsEnabled(true);
overlay_widget_->SetContentsView(overlay_view_.get());
......
......@@ -47,7 +47,8 @@ class ASH_EXPORT ToastOverlay : public ui::ImplicitAnimationObserver {
// used.
ToastOverlay(Delegate* delegate,
const base::string16& text,
base::Optional<base::string16> dismiss_text);
base::Optional<base::string16> dismiss_text,
bool show_on_lock_screen = false);
~ToastOverlay() override;
// Shows or hides the overlay.
......
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