Commit e518bda4 authored by Toshiki Kikuchi's avatar Toshiki Kikuchi Committed by Commit Bot

exo/wayland: add buttons and clicked event to notification-shell-unstable protocol

This CL allows to show the notification with buttons in create_notification
request, and allows Exosphere to handle this request.
Also, this CL adds clicked event for that protocol.

BUG=chromium:838531
TEST=NotificationTest.*, manual verification

Change-Id: Ib7d0693695afac5c732a8c0808a9b3250fa7a327
Reviewed-on: https://chromium-review.googlesource.com/c/1276045
Commit-Queue: Toshiki Kikuchi <toshikikikuchi@google.com>
Reviewed-by: default avatarDavid Reveman <reveman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602213}
parent 6d2ed424
......@@ -20,8 +20,10 @@ namespace {
class NotificationDelegate : public message_center::NotificationDelegate {
public:
NotificationDelegate(
const base::RepeatingCallback<void(bool)>& close_callback)
: close_callback_(close_callback) {}
const base::RepeatingCallback<void(bool)>& close_callback,
const base::RepeatingCallback<void(const base::Optional<int>&)>&
click_callback)
: close_callback_(close_callback), click_callback_(click_callback) {}
// message_center::NotificationDelegate:
void Close(bool by_user) override {
......@@ -30,11 +32,20 @@ class NotificationDelegate : public message_center::NotificationDelegate {
close_callback_.Run(by_user);
}
void Click(const base::Optional<int>& button_index,
const base::Optional<base::string16>& reply) override {
if (!click_callback_)
return;
click_callback_.Run(button_index);
}
private:
// The destructor is private since this class is ref-counted.
~NotificationDelegate() override = default;
const base::RepeatingCallback<void(bool)> close_callback_;
const base::RepeatingCallback<void(const base::Optional<int>&)>
click_callback_;
DISALLOW_COPY_AND_ASSIGN(NotificationDelegate);
};
......@@ -47,7 +58,10 @@ Notification::Notification(
const std::string& display_source,
const std::string& notification_id,
const std::string& notifier_id,
const base::RepeatingCallback<void(bool)>& close_callback)
const std::vector<std::string>& buttons,
const base::RepeatingCallback<void(bool)>& close_callback,
const base::RepeatingCallback<void(const base::Optional<int>&)>&
click_callback)
: notification_id_(notification_id) {
auto notifier = message_center::NotifierId(
message_center::NotifierId::APPLICATION, notifier_id);
......@@ -56,12 +70,16 @@ Notification::Notification(
->GetPrimaryUserSession()
->user_info->account_id.GetUserEmail();
message_center::RichNotificationData data;
for (const auto& button : buttons)
data.buttons.emplace_back(base::UTF8ToUTF16(button));
auto notification = std::make_unique<message_center::Notification>(
message_center::NOTIFICATION_TYPE_SIMPLE, notification_id,
base::UTF8ToUTF16(title), base::UTF8ToUTF16(message), gfx::Image(),
base::UTF8ToUTF16(display_source), GURL(), notifier,
message_center::RichNotificationData(),
base::MakeRefCounted<NotificationDelegate>(close_callback));
base::UTF8ToUTF16(display_source), GURL(), notifier, data,
base::MakeRefCounted<NotificationDelegate>(close_callback,
click_callback));
message_center::MessageCenter::Get()->AddNotification(
std::move(notification));
......
......@@ -6,8 +6,10 @@
#define COMPONENTS_EXO_NOTIFICATION_H_
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/optional.h"
namespace exo {
......@@ -19,7 +21,10 @@ class Notification {
const std::string& display_source,
const std::string& notification_id,
const std::string& notifier_id,
const base::RepeatingCallback<void(bool)>& close_callback);
const std::vector<std::string>& buttons,
const base::RepeatingCallback<void(bool)>& close_callback,
const base::RepeatingCallback<void(const base::Optional<int>&)>&
click_callback);
// Closes this notification.
void Close();
......
......@@ -21,6 +21,13 @@ void Close(int* close_call_count, bool by_user) {
(*close_call_count)++;
}
void Click(int* click_call_count,
base::Optional<int>* passed_button_index,
const base::Optional<int>& button_index) {
(*click_call_count)++;
*passed_button_index = button_index;
}
TEST_F(NotificationTest, CloseCallback) {
auto* message_center = message_center::MessageCenter::Get();
......@@ -34,13 +41,15 @@ TEST_F(NotificationTest, CloseCallback) {
const std::string display_source = "TEST display_source";
const std::string notification_id = "exo-notification.test";
const std::string notifier_id = "exo-notification-test";
const std::vector<std::string> buttons = {"TEST button0", "TEST button1"};
// For the close callback.
int close_call_count = 0;
Notification notification(
title, message, display_source, notification_id, notifier_id,
base::BindRepeating(&Close, base::Unretained(&close_call_count)));
title, message, display_source, notification_id, notifier_id, buttons,
base::BindRepeating(&Close, base::Unretained(&close_call_count)),
base::DoNothing());
EXPECT_EQ(close_call_count, 0);
......@@ -61,5 +70,54 @@ TEST_F(NotificationTest, CloseCallback) {
false /* by_user */, message_center::MessageCenter::RemoveType::ALL);
}
TEST_F(NotificationTest, ClickCallback) {
auto* message_center = message_center::MessageCenter::Get();
// Clear all notifications.
message_center->RemoveAllNotifications(
false /* by_user */, message_center::MessageCenter::RemoveType::ALL);
// Params for test notification.
const std::string title = "TEST title";
const std::string message = "TEST message";
const std::string display_source = "TEST display_source";
const std::string notification_id = "exo-notification.test";
const std::string notifier_id = "exo-notification-test";
const std::vector<std::string> buttons = {"TEST button0", "TEST button1"};
// For the click callback.
int click_call_count = 0;
base::Optional<int> passed_button_index;
Notification notification(
title, message, display_source, notification_id, notifier_id, buttons,
base::DoNothing(),
base::BindRepeating(&Click, base::Unretained(&click_call_count),
base::Unretained(&passed_button_index)));
EXPECT_EQ(click_call_count, 0);
EXPECT_NE(nullptr,
message_center->FindVisibleNotificationById(notification_id));
// Clicks on notification.
message_center->ClickOnNotification(notification_id);
// Expected to be called once without button index
EXPECT_EQ(click_call_count, 1);
EXPECT_EQ(passed_button_index, base::nullopt);
// Clicks on button.
message_center->ClickOnNotificationButton(notification_id, 0);
// Expected to be called once with button index
EXPECT_EQ(click_call_count, 2);
EXPECT_EQ(*passed_button_index, 0);
// Clear all notifications.
message_center->RemoveAllNotifications(
false /* by_user */, message_center::MessageCenter::RemoveType::ALL);
}
} // namespace
} // namespace exo
......@@ -5693,12 +5693,15 @@ class WaylandNotificationShellNotification {
const std::string& message,
const std::string& display_source,
const std::string& notification_id,
const std::vector<std::string>& buttons,
wl_resource* resource)
: resource_(resource), weak_ptr_factory_(this) {
notification_ = std::make_unique<Notification>(
title, message, display_source, notification_id,
kNotificationShellNotifierId,
kNotificationShellNotifierId, buttons,
base::BindRepeating(&WaylandNotificationShellNotification::OnClose,
weak_ptr_factory_.GetWeakPtr()),
base::BindRepeating(&WaylandNotificationShellNotification::OnClick,
weak_ptr_factory_.GetWeakPtr()));
}
......@@ -5710,6 +5713,12 @@ class WaylandNotificationShellNotification {
wl_client_flush(wl_resource_get_client(resource_));
}
void OnClick(const base::Optional<int>& button_index) {
int32_t index = button_index ? *button_index : -1;
zcr_notification_shell_notification_v1_send_clicked(resource_, index);
wl_client_flush(wl_resource_get_client(resource_));
}
wl_resource* const resource_;
std::unique_ptr<Notification> notification_;
......@@ -5745,12 +5754,14 @@ class WaylandNotificationShell {
const std::string& message,
const std::string& display_source,
const std::string& notification_key,
const std::vector<std::string>& buttons,
wl_resource* notification_resource) {
auto notification_id = base::StringPrintf(
kNotificationShellNotificationIdFormat, id_, notification_key.c_str());
return std::make_unique<WaylandNotificationShellNotification>(
title, message, display_source, notification_id, notification_resource);
title, message, display_source, notification_id, buttons,
notification_resource);
}
private:
......@@ -5766,14 +5777,27 @@ void notification_shell_create_notification(wl_client* client,
const char* title,
const char* message,
const char* display_source,
const char* notification_key) {
const char* notification_key,
wl_array* buttons) {
wl_resource* notification_resource = wl_resource_create(
client, &zcr_notification_shell_notification_v1_interface,
wl_resource_get_version(resource), id);
// Converts wl_array of strings into std::vector<std::string>. All elements
// are 0-terminated so we use it as the mark of the element's end.
std::vector<std::string> button_strings;
const char* data = static_cast<const char*>(buttons->data);
int len = 0;
for (const char *pos = data; pos < data + buttons->size; ++pos, ++len) {
if (*pos == '\0') {
button_strings.emplace_back(std::string(pos - len, len));
len = 0;
}
}
std::unique_ptr<WaylandNotificationShellNotification> notification =
GetUserDataAs<WaylandNotificationShell>(resource)->CreateNotification(
title, message, display_source, notification_key,
title, message, display_source, notification_key, button_strings,
notification_resource);
SetImplementation(notification_resource, &notification_implementation,
......
......@@ -163,12 +163,12 @@ zcr_notification_shell_v1_destroy(struct zcr_notification_shell_v1 *zcr_notifica
* Creates a desktop notification from plain text information.
*/
static inline struct zcr_notification_shell_notification_v1 *
zcr_notification_shell_v1_create_notification(struct zcr_notification_shell_v1 *zcr_notification_shell_v1, const char *title, const char *message, const char *display_source, const char *notification_key)
zcr_notification_shell_v1_create_notification(struct zcr_notification_shell_v1 *zcr_notification_shell_v1, const char *title, const char *message, const char *display_source, const char *notification_key, struct wl_array *buttons)
{
struct wl_proxy *id;
id = wl_proxy_marshal_constructor((struct wl_proxy *) zcr_notification_shell_v1,
ZCR_NOTIFICATION_SHELL_V1_CREATE_NOTIFICATION, &zcr_notification_shell_notification_v1_interface, NULL, title, message, display_source, notification_key);
ZCR_NOTIFICATION_SHELL_V1_CREATE_NOTIFICATION, &zcr_notification_shell_notification_v1_interface, NULL, title, message, display_source, notification_key, buttons);
return (struct zcr_notification_shell_notification_v1 *) id;
}
......@@ -264,6 +264,16 @@ struct zcr_notification_shell_notification_v1_listener {
void (*closed)(void *data,
struct zcr_notification_shell_notification_v1 *zcr_notification_shell_notification_v1,
uint32_t by_user);
/**
* Notification is clicked
*
* Notifies the notification object that the notification or its
* button is clicked.
* @param button_index -1 if the body of the notification is cliked as opposed to a button
*/
void (*clicked)(void *data,
struct zcr_notification_shell_notification_v1 *zcr_notification_shell_notification_v1,
int32_t button_index);
};
/**
......@@ -284,6 +294,10 @@ zcr_notification_shell_notification_v1_add_listener(struct zcr_notification_shel
* @ingroup iface_zcr_notification_shell_notification_v1
*/
#define ZCR_NOTIFICATION_SHELL_NOTIFICATION_V1_CLOSED_SINCE_VERSION 1
/**
* @ingroup iface_zcr_notification_shell_notification_v1
*/
#define ZCR_NOTIFICATION_SHELL_NOTIFICATION_V1_CLICKED_SINCE_VERSION 1
/**
* @ingroup iface_zcr_notification_shell_notification_v1
......
......@@ -130,6 +130,7 @@ struct zcr_notification_shell_v1_interface {
*
* Creates a desktop notification from plain text information.
* @param display_source textual representation of who's shown the notification
* @param buttons array of 0-terminated button title names
*/
void (*create_notification)(struct wl_client *client,
struct wl_resource *resource,
......@@ -137,7 +138,8 @@ struct zcr_notification_shell_v1_interface {
const char *title,
const char *message,
const char *display_source,
const char *notification_key);
const char *notification_key,
struct wl_array *buttons);
/**
* create a notification surface from a surface
*
......@@ -217,11 +219,16 @@ struct zcr_notification_shell_notification_v1_interface {
};
#define ZCR_NOTIFICATION_SHELL_NOTIFICATION_V1_CLOSED 0
#define ZCR_NOTIFICATION_SHELL_NOTIFICATION_V1_CLICKED 1
/**
* @ingroup iface_zcr_notification_shell_notification_v1
*/
#define ZCR_NOTIFICATION_SHELL_NOTIFICATION_V1_CLOSED_SINCE_VERSION 1
/**
* @ingroup iface_zcr_notification_shell_notification_v1
*/
#define ZCR_NOTIFICATION_SHELL_NOTIFICATION_V1_CLICKED_SINCE_VERSION 1
/**
* @ingroup iface_zcr_notification_shell_notification_v1
......@@ -244,6 +251,18 @@ zcr_notification_shell_notification_v1_send_closed(struct wl_resource *resource_
wl_resource_post_event(resource_, ZCR_NOTIFICATION_SHELL_NOTIFICATION_V1_CLOSED, by_user);
}
/**
* @ingroup iface_zcr_notification_shell_notification_v1
* Sends an clicked event to the client owning the resource.
* @param resource_ The client's resource
* @param button_index -1 if the body of the notification is cliked as opposed to a button
*/
static inline void
zcr_notification_shell_notification_v1_send_clicked(struct wl_resource *resource_, int32_t button_index)
{
wl_resource_post_event(resource_, ZCR_NOTIFICATION_SHELL_NOTIFICATION_V1_CLICKED, button_index);
}
#ifdef __cplusplus
}
#endif
......
......@@ -38,14 +38,15 @@ static const struct wl_interface *types[] = {
NULL,
NULL,
NULL,
NULL,
&zcr_notification_shell_surface_v1_interface,
&wl_surface_interface,
NULL,
};
static const struct wl_message zcr_notification_shell_v1_requests[] = {
{ "create_notification", "nssss", types + 1 },
{ "get_notification_surface", "nos", types + 6 },
{ "create_notification", "nssssa", types + 1 },
{ "get_notification_surface", "nos", types + 7 },
};
WL_EXPORT const struct wl_interface zcr_notification_shell_v1_interface = {
......@@ -72,11 +73,12 @@ static const struct wl_message zcr_notification_shell_notification_v1_requests[]
static const struct wl_message zcr_notification_shell_notification_v1_events[] = {
{ "closed", "u", types + 0 },
{ "clicked", "i", types + 0 },
};
WL_EXPORT const struct wl_interface zcr_notification_shell_notification_v1_interface = {
"zcr_notification_shell_notification_v1", 1,
2, zcr_notification_shell_notification_v1_requests,
1, zcr_notification_shell_notification_v1_events,
2, zcr_notification_shell_notification_v1_events,
};
......@@ -56,6 +56,7 @@
<arg name="message" type="string" />
<arg name="display_source" type="string" summary="textual representation of who's shown the notification"/>
<arg name="notification_key" type="string" />
<arg name="buttons" type="array" summary="array of 0-terminated button title names"/>
</request>
<request name="get_notification_surface">
......@@ -112,5 +113,12 @@
</description>
<arg name="by_user" type="uint" summary="1 if notification is closed by a user"/>
</event>
<event name="clicked">
<description summary="Notification is clicked">
Notifies the notification object that the notification or its button is clicked.
</description>
<arg name="button_index" type="int" summary="-1 if the body of the notification is cliked as opposed to a button"/>
</event>
</interface>
</protocol>
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