[geary/mjog/plugin-support: 6/6] Convert Notification.Desktop to a plugin
- From: Michael Gratton <mjog src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [geary/mjog/plugin-support: 6/6] Convert Notification.Desktop to a plugin
- Date: Fri, 27 Sep 2019 01:00:51 +0000 (UTC)
commit e9a130566b57be119e9dda7818f27efb4f7ab830
Author: Michael Gratton <mike vee net>
Date: Fri Sep 27 02:51:05 2019 +1000
Convert Notification.Desktop to a plugin
Rename to DesktopNotifications and use Plugins.Notification as the base
class. Add a plugin config file. Add build config to build the plugin.
Update Plugins.Notification class to suit the requirements of this
plugin better.
po/POTFILES.in | 3 +-
src/client/application/application-controller.vala | 15 +-
.../application/application-plugin-manager.vala | 25 ++-
src/client/application/geary-application.vala | 31 ++-
src/client/meson.build | 3 +-
src/client/notification/notification-desktop.vala | 221 ---------------------
.../desktop-notifications.plugin.in | 5 +
.../desktop-notifications.vala | 213 ++++++++++++++++++++
.../plugin/desktop-notifications/meson.build | 24 +++
src/client/plugin/meson.build | 25 +++
src/client/plugin/plugin-notification.vala | 9 +-
src/meson.build | 1 +
12 files changed, 331 insertions(+), 244 deletions(-)
---
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 4cb50a03..dc81032e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -80,9 +80,10 @@ src/client/folder-list/folder-list-tree.vala
src/client/notification/in-app-notification.vala
src/client/notification/libmessagingmenu.vala
src/client/notification/new-messages-indicator.vala
-src/client/notification/notification-desktop.vala
src/client/notification/null-indicator.vala
src/client/notification/unity-launcher.vala
+src/client/plugin/desktop-notifications/desktop-notifications.vala
+src/client/plugin/desktop-notifications/desktop-notifications.desktop.in
src/client/plugin/plugin-notification.vala
src/client/sidebar/sidebar-branch.vala
src/client/sidebar/sidebar-common.vala
diff --git a/src/client/application/application-controller.vala
b/src/client/application/application-controller.vala
index 2a489cb6..03d23bf9 100644
--- a/src/client/application/application-controller.vala
+++ b/src/client/application/application-controller.vala
@@ -127,9 +127,6 @@ public class Application.Controller : Geary.BaseObject {
get; private set;
}
- /** Desktop notifications for the application. */
- public Notification.Desktop notifications { get; private set; }
-
/** Avatar store for the application. */
public Application.AvatarStore avatars {
get; private set; default = new Application.AvatarStore();
@@ -272,9 +269,7 @@ public class Application.Controller : Geary.BaseObject {
}
- this.plugin_manager = new PluginManager(
- application.get_app_plugins_dir()
- );
+ this.plugin_manager = new PluginManager(application);
this.plugin_manager.notifications = new NotificationContext(
this.avatars,
this.get_contact_store_for_account,
@@ -321,12 +316,6 @@ public class Application.Controller : Geary.BaseObject {
this.unity_launcher = new UnityLauncher(this.plugin_manager.notifications);
- this.notifications = new Notification.Desktop(
- this.plugin_manager.notifications,
- this.application,
- cancellable
- );
-
this.main_window.conversation_list_view.grab_focus();
// initialize revokable
@@ -553,7 +542,7 @@ public class Application.Controller : Geary.BaseObject {
Geary.ServiceProblemReport? service_report =
report as Geary.ServiceProblemReport;
if (service_report != null && service_report.service.protocol == SMTP) {
- this.notifications.set_error_notification(
+ this.application.send_error_notification(
/// Notification title.
_("A problem occurred sending email for %s").printf(
service_report.account.display_name
diff --git a/src/client/application/application-plugin-manager.vala
b/src/client/application/application-plugin-manager.vala
index 85421982..89fa686b 100644
--- a/src/client/application/application-plugin-manager.vala
+++ b/src/client/application/application-plugin-manager.vala
@@ -13,32 +13,47 @@ public class Application.PluginManager : GLib.Object {
public NotificationContext notifications { get; set; }
+ private GearyApplication application;
private Peas.Engine engine;
private Peas.ExtensionSet? notification_extensions = null;
+ private bool is_shutdown = false;
- public PluginManager(GLib.File app_plugin_dir) {
+ public PluginManager(GearyApplication application) {
+ this.application = application;
this.engine = Peas.Engine.get_default();
- this.engine.add_search_path(app_plugin_dir.get_path(), null);
+ this.engine.add_search_path(
+ application.get_app_plugins_dir().get_path(), null
+ );
}
public void load() {
this.notification_extensions = new Peas.ExtensionSet(
this.engine,
typeof(Plugin.Notification),
+ "application", this.application,
"context", this.notifications
);
this.notification_extensions.extension_added.connect((info, extension) => {
(extension as Plugin.Notification).activate();
});
this.notification_extensions.extension_removed.connect((info, extension) => {
- (extension as Plugin.Notification).deactivate();
+ (extension as Plugin.Notification).deactivate(this.is_shutdown);
});
// Load built-in plugins by default
foreach (Peas.PluginInfo info in this.engine.get_plugin_list()) {
- if (info.is_builtin()) {
- this.engine.load_plugin(info);
+ try {
+ info.is_available();
+ if (info.is_builtin()) {
+ debug("Loading built-in plugin: %s", info.get_name());
+ this.engine.load_plugin(info);
+ } else {
+ debug("Not loading plugin: %s", info.get_name());
+ }
+ } catch (GLib.Error err) {
+ warning("Plugin %s not available: %s",
+ info.get_name(), err.message);
}
}
}
diff --git a/src/client/application/geary-application.vala b/src/client/application/geary-application.vala
index f09da89d..5746ad0c 100644
--- a/src/client/application/geary-application.vala
+++ b/src/client/application/geary-application.vala
@@ -161,6 +161,9 @@ public class GearyApplication : Gtk.Application {
private const int64 USEC_PER_SEC = 1000000;
private const int64 FORCE_SHUTDOWN_USEC = 5 * USEC_PER_SEC;
+ private const string ERROR_NOTIFICATION_ID = "error";
+
+
/** Object returned by {@link get_runtime_information}. */
public struct RuntimeDetail {
@@ -265,6 +268,7 @@ public class GearyApplication : Gtk.Application {
private GLib.Cancellable controller_cancellable = new GLib.Cancellable();
private Components.Inspector? inspector = null;
private Geary.Nonblocking.Mutex controller_mutex = new Geary.Nonblocking.Mutex();
+ private GLib.Notification? error_notification = null;
/**
@@ -625,7 +629,8 @@ public class GearyApplication : Gtk.Application {
public GLib.File get_app_plugins_dir() {
return (is_installed)
? GLib.File.new_for_path(_PLUGINS_DIR)
- : GLib.File.new_for_path(BUILD_ROOT_DIR).get_child("src");
+ : GLib.File.new_for_path(BUILD_ROOT_DIR)
+ .get_child("src").get_child("client").get_child("plugin");
}
/** Displays a URI on the current active window, if any. */
@@ -701,6 +706,30 @@ public class GearyApplication : Gtk.Application {
Posix.exit(1);
}
+ /**
+ * Displays an error notification.
+ *
+ * Use _very_ sparingly.
+ */
+ internal void send_error_notification(string summary, string body) {
+ if (this.error_notification != null) {
+ clear_error_notification();
+ }
+
+ GLib.Notification error = new GLib.Notification(summary);
+ error.set_body(body);
+ error.set_icon(
+ new GLib.ThemedIcon("%s-symbolic".printf(GearyApplication.APP_ID))
+ );
+ send_notification(ERROR_NOTIFICATION_ID, error);
+ this.error_notification = error;
+ }
+
+ internal void clear_error_notification() {
+ this.error_notification = null;
+ withdraw_notification(ERROR_NOTIFICATION_ID);
+ }
+
// Presents a main window. If the controller is not open, opens it
// first.
private async void present() {
diff --git a/src/client/meson.build b/src/client/meson.build
index 6e0e3d7f..015d28eb 100644
--- a/src/client/meson.build
+++ b/src/client/meson.build
@@ -84,7 +84,6 @@ geary_client_vala_sources = files(
'folder-list/folder-list-search-branch.vala',
'folder-list/folder-list-special-grouping.vala',
- 'notification/notification-desktop.vala',
'notification/in-app-notification.vala',
'notification/libmessagingmenu.vala',
'notification/new-messages-indicator.vala',
@@ -175,3 +174,5 @@ geary_client_dep = declare_dependency(
link_with: geary_client_lib,
include_directories: include_directories('.'),
)
+
+subdir('plugin')
diff --git a/src/client/plugin/desktop-notifications/desktop-notifications.plugin.in
b/src/client/plugin/desktop-notifications/desktop-notifications.plugin.in
new file mode 100644
index 00000000..837778b9
--- /dev/null
+++ b/src/client/plugin/desktop-notifications/desktop-notifications.plugin.in
@@ -0,0 +1,5 @@
+[Plugin]
+Module=libdesktop-notifications.so
+Name=Desktop Notifications
+Description=Displays desktop notifications when new email is delivered
+Builtin=true
diff --git a/src/client/plugin/desktop-notifications/desktop-notifications.vala
b/src/client/plugin/desktop-notifications/desktop-notifications.vala
new file mode 100644
index 00000000..506fa3c3
--- /dev/null
+++ b/src/client/plugin/desktop-notifications/desktop-notifications.vala
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2016 Software Freedom Conservancy Inc.
+ * Copyright 2019 Michael Gratton <mike vee net>.
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+[ModuleInit]
+public void peas_register_types(TypeModule module) {
+ Peas.ObjectModule obj = module as Peas.ObjectModule;
+ obj.register_extension_type(
+ typeof(Plugin.Notification),
+ typeof(Plugin.DesktopNotifications)
+ );
+}
+
+/**
+ * Manages standard desktop application notifications.
+ */
+public class Plugin.DesktopNotifications : Notification {
+
+
+ public const Geary.Email.Field REQUIRED_FIELDS =
+ Geary.Email.Field.ORIGINATORS | Geary.Email.Field.SUBJECT;
+
+ public override GearyApplication application {
+ get; construct set;
+ }
+
+ public override Application.NotificationContext context {
+ get; construct set;
+ }
+
+ private const string ARRIVED_ID = "email-arrived";
+
+ private GLib.Notification? arrived_notification = null;
+ private GLib.Cancellable? cancellable = null;
+
+
+ public override void activate() {
+ this.context.add_required_fields(REQUIRED_FIELDS);
+ this.context.new_messages_arrived.connect(on_new_messages_arrived);
+ this.cancellable = new GLib.Cancellable();
+ }
+
+ public override void deactivate(bool is_shutdown) {
+ this.cancellable.cancel();
+ this.context.new_messages_arrived.disconnect(on_new_messages_arrived);
+ this.context.remove_required_fields(REQUIRED_FIELDS);
+
+ // Keep existing notifications if shutting down since they are
+ // persistent, but revoke if the plugin is being disabled.
+ if (!is_shutdown) {
+ clear_arrived_notification();
+ }
+ }
+
+ private void clear_arrived_notification() {
+ this.application.withdraw_notification(ARRIVED_ID);
+ this.arrived_notification = null;
+ }
+
+ private void notify_new_mail(Geary.Folder folder, int added) {
+ string body = ngettext(
+ /// Notification body text for new email when no other
+ /// new messages are already awaiting.
+ "%d new message", "%d new messages", added
+ ).printf(added);
+
+ int total = 0;
+ try {
+ total = this.context.get_new_message_count(folder);
+ } catch (Geary.EngineError err) {
+ // All good
+ }
+
+ if (total > added) {
+ body = ngettext(
+ /// Notification body text for new email when
+ /// other new messages have already been notified
+ /// about
+ "%s, %d new message total", "%s, %d new messages total",
+ total
+ ).printf(body, total);
+ }
+
+ issue_arrived_notification(
+ folder.account.information.display_name, body, folder, null
+ );
+ }
+
+ private async void notify_one_message(Geary.Folder folder,
+ Geary.Email email,
+ GLib.Cancellable? cancellable)
+ throws GLib.Error {
+ Geary.RFC822.MailboxAddress? originator =
+ Util.Email.get_primary_originator(email);
+ if (originator != null) {
+ Application.ContactStore contacts =
+ this.context.get_contact_store(folder.account);
+ Application.Contact contact = yield contacts.load(
+ originator, cancellable
+ );
+
+ int count = 1;
+ try {
+ count = this.context.get_new_message_count(folder);
+ } catch (Geary.EngineError.NOT_FOUND err) {
+ // All good
+ }
+
+ string body = "";
+ if (count <= 1) {
+ body = Util.Email.strip_subject_prefixes(email);
+ } else {
+ body = ngettext(
+ "%s\n(%d other new message for %s)",
+ "%s\n(%d other new messages for %s)", count - 1).printf(
+ Util.Email.strip_subject_prefixes(email),
+ count - 1,
+ folder.account.information.display_name
+ );
+ }
+
+ issue_arrived_notification(
+ contact.is_trusted
+ ? contact.display_name : originator.to_short_display(),
+ body,
+ folder,
+ email.id
+ );
+ } else {
+ notify_new_mail(folder, 1);
+ }
+ }
+
+ private void issue_arrived_notification(string summary,
+ string body,
+ Geary.Folder folder,
+ Geary.EmailIdentifier? id) {
+ // only one outstanding notification at a time
+ clear_arrived_notification();
+
+ string? action = null;
+ GLib.Variant[] target_param = new GLib.Variant[] {
+ folder.account.information.id,
+ new GLib.Variant.variant(folder.path.to_variant())
+ };
+
+ if (id == null) {
+ action = GearyApplication.ACTION_SHOW_FOLDER;
+ } else {
+ action = GearyApplication.ACTION_SHOW_EMAIL;
+ target_param += new GLib.Variant.variant(id.to_variant());
+ }
+
+ this.arrived_notification = issue_notification(
+ ARRIVED_ID,
+ summary,
+ body,
+ "app." + action,
+ new GLib.Variant.tuple(target_param)
+ );
+ }
+
+ private GLib.Notification issue_notification(string id,
+ string summary,
+ string body,
+ string? action,
+ GLib.Variant? action_target) {
+ GLib.Notification notification = new GLib.Notification(summary);
+ notification.set_body(body);
+ notification.set_icon(
+ new GLib.ThemedIcon("%s-symbolic".printf(GearyApplication.APP_ID))
+ );
+
+ /* We do not show notification action under Unity */
+
+ if (this.application.config.desktop_environment == Configuration.DesktopEnvironment.UNITY) {
+ this.application.send_notification(id, notification);
+ return notification;
+ } else {
+ if (action != null) {
+ notification.set_default_action_and_target_value(
+ action, action_target
+ );
+ }
+
+ this.application.send_notification(id, notification);
+ return notification;
+ }
+ }
+
+ private void on_new_messages_arrived(Geary.Folder folder,
+ int total,
+ int added) {
+ if (this.context.should_notify_new_messages(folder)) {
+ if (added == 1 &&
+ this.context.last_new_message_folder != null &&
+ this.context.last_new_message != null) {
+ this.notify_one_message.begin(
+ this.context.last_new_message_folder,
+ this.context.last_new_message,
+ this.cancellable
+ );
+ } else if (added > 0) {
+ notify_new_mail(folder, added);
+ }
+ }
+ }
+
+}
diff --git a/src/client/plugin/desktop-notifications/meson.build
b/src/client/plugin/desktop-notifications/meson.build
new file mode 100644
index 00000000..06b9b8e8
--- /dev/null
+++ b/src/client/plugin/desktop-notifications/meson.build
@@ -0,0 +1,24 @@
+
+plugin_name = 'desktop-notifications'
+
+plugin_src = join_paths(plugin_name + '.vala')
+plugin_data = join_paths(plugin_name + '.plugin')
+plugin_dest = join_paths(plugins_dir, plugin_name)
+
+shared_module(
+ plugin_name,
+ sources: plugin_src,
+ dependencies: plugin_dependencies,
+ c_args: plugin_cflags,
+ install: true,
+ install_dir: plugin_dest
+)
+
+i18n.merge_file(
+ input: plugin_data + '.in',
+ output: plugin_data,
+ type: 'desktop',
+ po_dir: po_dir,
+ install: true,
+ install_dir: plugin_dest
+)
diff --git a/src/client/plugin/meson.build b/src/client/plugin/meson.build
new file mode 100644
index 00000000..4826545b
--- /dev/null
+++ b/src/client/plugin/meson.build
@@ -0,0 +1,25 @@
+#
+# Builds individual plugins. The client's plugin classes themselves
+# are built at the next directory back up the tree.
+#
+
+plugin_dependencies = [
+ folks,
+ gdk,
+ geary_client_dep,
+ geary_engine_dep,
+ gee,
+ gmime,
+ goa,
+ gtk,
+ javascriptcoregtk,
+ libhandy,
+ libmath,
+ libpeas,
+ libsoup,
+ webkit2gtk,
+]
+
+plugin_cflags = geary_c_options
+
+subdir('desktop-notifications')
diff --git a/src/client/plugin/plugin-notification.vala b/src/client/plugin/plugin-notification.vala
index 92e825fc..48ab11fa 100644
--- a/src/client/plugin/plugin-notification.vala
+++ b/src/client/plugin/plugin-notification.vala
@@ -10,7 +10,12 @@
*/
public abstract class Plugin.Notification : Geary.BaseObject {
- /** Context object for notification information. */
+ /** The application instance containing the plugin. */
+ public abstract GearyApplication application {
+ get; construct set;
+ }
+
+ /** Context object for notifications. */
public abstract Application.NotificationContext context {
get; construct set;
}
@@ -19,6 +24,6 @@ public abstract class Plugin.Notification : Geary.BaseObject {
public abstract void activate();
/* Invoked to deactivate the plugin, prior to unloading */
- public abstract void deactivate();
+ public abstract void deactivate(bool is_shutdown);
}
diff --git a/src/meson.build b/src/meson.build
index 050e2368..4f8c4fa3 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -72,6 +72,7 @@ geary_bin_dependencies = [
javascriptcoregtk,
libhandy,
libmath,
+ libpeas,
libsoup,
webkit2gtk,
]
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]