From 6783c2ce632cd1cc949e74395e0139c882758e67 Mon Sep 17 00:00:00 2001 From: Mohamed Ibrahim Date: Thu, 26 Jun 2014 13:31:43 -0700 Subject: [PATCH] Start notifying of new mail at session startup: Bug #714644 Geary can now be configured to notify of new mail at startup. When the user logs in, Geary will autostart with a hidden window and notify of new mail as usual. When Geary is formally executed by the user the Geary window simply appears. In this mode, if the user closes the window Geary will return to its hidden state. Quit must be used to close the process. --- THANKS | 1 + cmake/FindIntltool.cmake | 7 ++ desktop/CMakeLists.txt | 2 + desktop/geary-autostart.desktop.in | 15 ++++ desktop/org.yorba.geary.gschema.xml | 5 ++ po/POTFILES.in | 1 + src/CMakeLists.txt | 1 + src/client/application/autostart-manager.vala | 90 +++++++++++++++++++ src/client/application/geary-application.vala | 6 +- src/client/application/geary-args.vala | 2 + src/client/application/geary-config.vala | 6 ++ src/client/application/geary-controller.vala | 7 +- src/client/components/main-window.vala | 3 + src/client/dialogs/preferences-dialog.vala | 3 + ui/preferences.glade | 21 +++++ 15 files changed, 168 insertions(+), 2 deletions(-) create mode 100644 desktop/geary-autostart.desktop.in create mode 100644 src/client/application/autostart-manager.vala diff --git a/THANKS b/THANKS index 10288858..007ab814 100644 --- a/THANKS +++ b/THANKS @@ -17,6 +17,7 @@ Michael George Michael Gratton Sven Hagemann Mathias Hasselmann +Mohamed Ibrahim Timo Kluck Charles Lehner Avi Levy diff --git a/cmake/FindIntltool.cmake b/cmake/FindIntltool.cmake index 700a9bcc..ac45ce99 100644 --- a/cmake/FindIntltool.cmake +++ b/cmake/FindIntltool.cmake @@ -19,5 +19,12 @@ if (INTLTOOL_MERGE_FOUND) ) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/geary.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) endmacro (INTLTOOL_MERGE_DESKTOP desktop_id po_dir) + macro (INTLTOOL_MERGE_AUTOSTART_DESKTOP desktop_id po_dir) + add_custom_target (geary-autostart.desktop ALL + ${INTLTOOL_MERGE_EXECUTABLE} --desktop-style ${CMAKE_SOURCE_DIR}/${po_dir} + ${CMAKE_CURRENT_SOURCE_DIR}/${desktop_id}.desktop.in ${desktop_id}.desktop + ) + install (FILES ${CMAKE_CURRENT_BINARY_DIR}/geary-autostart.desktop DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) + endmacro (INTLTOOL_MERGE_AUTOSTART_DESKTOP desktop_id po_dir) endif (INTLTOOL_MERGE_FOUND) diff --git a/desktop/CMakeLists.txt b/desktop/CMakeLists.txt index e0214b41..e37988fe 100644 --- a/desktop/CMakeLists.txt +++ b/desktop/CMakeLists.txt @@ -8,10 +8,12 @@ include (FindIntltool) include (FindDesktopFileValidate) if (INTLTOOL_MERGE_FOUND) INTLTOOL_MERGE_DESKTOP (geary po) + INTLTOOL_MERGE_AUTOSTART_DESKTOP (geary-autostart po) if (DESKTOP_VALIDATE) if (DESKTOP_FILE_VALIDATE_FOUND) VALIDATE_DESKTOP_FILE (geary) + VALIDATE_DESKTOP_FILE (geary-autostart) else (DESKTOP_FILE_VALIDATE_FOUND) message (FATAL_ERROR "desktop-file-validate must be installed to validate generated .desktop file") endif (DESKTOP_FILE_VALIDATE_FOUND) diff --git a/desktop/geary-autostart.desktop.in b/desktop/geary-autostart.desktop.in new file mode 100644 index 00000000..7ded47f4 --- /dev/null +++ b/desktop/geary-autostart.desktop.in @@ -0,0 +1,15 @@ +[Desktop Entry] +_Name=Geary +_GenericName=Mail Client +_X-GNOME-FullName=Geary Mail +_Comment=Send and receive email +_Keywords=Email;E-mail;Mail; +Icon=geary +TryExec=geary +Exec=geary --hidden +Type=Application +Terminal=false +Categories=GNOME;GTK;Network;Email; +MimeType=x-scheme-handler/mailto; +StartupNotify=true +NoDisplay=true diff --git a/desktop/org.yorba.geary.gschema.xml b/desktop/org.yorba.geary.gschema.xml index 022a094e..b619d137 100644 --- a/desktop/org.yorba.geary.gschema.xml +++ b/desktop/org.yorba.geary.gschema.xml @@ -59,6 +59,11 @@ show notifications for new mail True to show notification bubbles. + + false + notify of new mail at startup + True to notify of new mail at startup. + true ask when opening an attachment diff --git a/po/POTFILES.in b/po/POTFILES.in index 4e11a577..8679e353 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -1,5 +1,6 @@ [encoding: UTF-8] desktop/geary.desktop.in +desktop/geary-autostart.desktop.in src/client/accounts/account-dialog-account-list-pane.vala src/client/accounts/account-dialog-add-edit-pane.vala src/client/accounts/account-dialog-pane.vala diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d08f30a6..54e10da1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -291,6 +291,7 @@ engine/util/util-trillian.vala ) set(CLIENT_SRC +client/application/autostart-manager.vala client/application/geary-action-adapter.vala client/application/geary-application.vala client/application/geary-args.vala diff --git a/src/client/application/autostart-manager.vala b/src/client/application/autostart-manager.vala new file mode 100644 index 00000000..466b56cf --- /dev/null +++ b/src/client/application/autostart-manager.vala @@ -0,0 +1,90 @@ +/* Copyright 2014 Yorba Foundation + * + * This software is licensed under the GNU Lesser General Public License + * (version 2.1 or later). See the COPYING file in this distribution. + */ + +/* + * A simple class for manipulating autostarting Geary as a hidden application through a simple + * desktop file at $HOME/.config/autostart/geary.desktop + */ +public class AutostartManager : Object { + private const string AUTOSTART_FOLDER = "autostart"; + private const string AUTOSTART_DESKTOP_FILE = "geary-autostart.desktop"; + + private File startup_file; // Startup '.desktop' file + + public AutostartManager() { + startup_file = File.new_for_path(Environment.get_user_config_dir()).get_child(AUTOSTART_FOLDER) + .get_child(AUTOSTART_DESKTOP_FILE); + + // Connect startup-notifications option callback + GearyApplication.instance.config.settings.changed[Configuration.STARTUP_NOTIFICATIONS_KEY].connect( + on_startup_notification_change); + } + + /** + * Returns the system-wide autostart desktop file + */ + public File? get_autostart_desktop_file() { + File? install_dir = GearyApplication.instance.get_install_dir(); + File desktop_file = (install_dir != null) + ? install_dir.get_child("share").get_child("applications").get_child(AUTOSTART_DESKTOP_FILE) + : File.new_for_path(GearyApplication.SOURCE_ROOT_DIR).get_child("build").get_child("desktop").get_child(AUTOSTART_DESKTOP_FILE); + + return desktop_file.query_exists() ? desktop_file : null; + } + + /** + * Deletes the desktop file from autostart directory. + */ + public void delete_startup_file() { + if (startup_file.query_exists()) { + try { + startup_file.delete(); + } catch (Error err) { + message("Failed to delete startup file: %s", err.message); + } + } + } + + /** + * Creates .desktop file in autostart directory (usually '$HOME/.config/autostart/') if no one exists. + */ + public void create_startup_file() { + if (startup_file.query_exists()) + return; + + try { + File autostart_dir = startup_file.get_parent(); + if (!autostart_dir.query_exists()) + autostart_dir.make_directory_with_parents(); + File? autostart = get_autostart_desktop_file(); + if (autostart == null) { + message("Autostart file is not installed!"); + } else { + autostart.copy(startup_file, 0); + } + } catch (Error err) { + message("Failed to create startup file: %s", err.message); + } + } + + /** + * Callback for startup notification option changes. + */ + public void on_startup_notification_change() { + if (GearyApplication.instance.config.startup_notifications) + create_startup_file(); + else + delete_startup_file(); + } + + /* + * A convenience method. The purpose of this method is to synchronize the state of startup notifications setting + * with the actual state of the file, so it's not misleading for the user (the option is checked while the file doesn't exist) + */ + public void sync_with_config() { + GearyApplication.instance.config.startup_notifications = startup_file.query_exists(); + } +} diff --git a/src/client/application/geary-application.vala b/src/client/application/geary-application.vala index 5c9d827d..ee10326f 100644 --- a/src/client/application/geary-application.vala +++ b/src/client/application/geary-application.vala @@ -154,7 +154,11 @@ public class GearyApplication : Gtk.Application { if (controller == null || controller.main_window == null) return false; - controller.main_window.present(); + if (!controller.main_window.get_realized()) + controller.main_window.show_all(); + else + controller.main_window.present(); + return true; } diff --git a/src/client/application/geary-args.vala b/src/client/application/geary-args.vala index 1cbbea57..a26a9c8b 100644 --- a/src/client/application/geary-args.vala +++ b/src/client/application/geary-args.vala @@ -7,6 +7,7 @@ namespace Args { private const OptionEntry[] options = { + { "hidden", 0, 0, OptionArg.NONE, ref hidden_startup, N_("Start Geary with hidden main window"), null }, { "debug", 'd', 0, OptionArg.NONE, ref log_debug, N_("Output debugging information"), null }, { "log-conversations", 0, 0, OptionArg.NONE, ref log_conversations, N_("Log conversation monitoring"), null }, { "log-deserializer", 0, 0, OptionArg.NONE, ref log_deserializer, N_("Log network deserialization"), null }, @@ -26,6 +27,7 @@ private const OptionEntry[] options = { { null } }; +public bool hidden_startup = false; public bool log_debug = false; public bool log_network = false; public bool log_serializer = false; diff --git a/src/client/application/geary-config.vala b/src/client/application/geary-config.vala index ad42a212..2e843042 100644 --- a/src/client/application/geary-config.vala +++ b/src/client/application/geary-config.vala @@ -16,6 +16,7 @@ public class Configuration { public const string SPELL_CHECK_KEY = "spell-check"; public const string PLAY_SOUNDS_KEY = "play-sounds"; public const string SHOW_NOTIFICATIONS_KEY = "show-notifications"; + public const string STARTUP_NOTIFICATIONS_KEY = "startup-notifications"; public const string ASK_OPEN_ATTACHMENT_KEY = "ask-open-attachment"; public const string COMPOSE_AS_HTML_KEY = "compose-as-html"; @@ -63,6 +64,11 @@ public class Configuration { public bool show_notifications { get { return settings.get_boolean(SHOW_NOTIFICATIONS_KEY); } } + + public bool startup_notifications { + get { return settings.get_boolean(STARTUP_NOTIFICATIONS_KEY); } + set { set_boolean(STARTUP_NOTIFICATIONS_KEY, value); } + } private const string CLOCK_FORMAT_KEY = "clock-format"; private const string TIME_FORMAT_KEY = "time-format"; diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala index aa9c01cd..2c4b4f8e 100644 --- a/src/client/application/geary-controller.vala +++ b/src/client/application/geary-controller.vala @@ -79,6 +79,8 @@ public class GearyController : Geary.BaseObject { public Geary.App.ConversationMonitor? current_conversations { get; private set; default = null; } + public AutostartManager? autostart_manager { get; private set; default = null; } + private Geary.Account? current_account = null; private Gee.HashMap email_stores = new Gee.HashMap(); @@ -218,6 +220,9 @@ public class GearyController : Geary.BaseObject { main_window.conversation_list_view.grab_focus(); + // instantiate here to ensure that Config is initialized and ready + autostart_manager = new AutostartManager(); + // Start Geary. try { yield Geary.Engine.instance.open_async(GearyApplication.instance.get_user_data_directory(), @@ -916,7 +921,7 @@ public class GearyController : Geary.BaseObject { */ private void display_main_window_if_ready() { if (did_attempt_open_all_accounts() && !upgrade_dialog.visible && - !cancellable_open_account.is_cancelled()) + !cancellable_open_account.is_cancelled() && !Args.hidden_startup) main_window.show_all(); } diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala index cd50bce6..7809dab6 100644 --- a/src/client/components/main-window.vala +++ b/src/client/components/main-window.vala @@ -96,6 +96,9 @@ public class MainWindow : Gtk.ApplicationWindow { } private bool on_delete_event() { + if (Args.hidden_startup || GearyApplication.instance.config.startup_notifications) + return hide_on_delete(); + GearyApplication.instance.exit(); return true; diff --git a/src/client/dialogs/preferences-dialog.vala b/src/client/dialogs/preferences-dialog.vala index 4df1d9dc..30fb0d7f 100644 --- a/src/client/dialogs/preferences-dialog.vala +++ b/src/client/dialogs/preferences-dialog.vala @@ -21,9 +21,12 @@ public class PreferencesDialog : Object { config.bind(Configuration.SPELL_CHECK_KEY, builder.get_object("spell_check"), "active"); config.bind(Configuration.PLAY_SOUNDS_KEY, builder.get_object("play_sounds"), "active"); config.bind(Configuration.SHOW_NOTIFICATIONS_KEY, builder.get_object("show_notifications"), "active"); + config.bind(Configuration.STARTUP_NOTIFICATIONS_KEY, builder.get_object("startup_notifications"), "active"); } public void run() { + // Sync startup notification option with file state + GearyApplication.instance.controller.autostart_manager.sync_with_config(); dialog.show_all(); dialog.run(); dialog.destroy(); diff --git a/ui/preferences.glade b/ui/preferences.glade index c6ccab45..2342747c 100644 --- a/ui/preferences.glade +++ b/ui/preferences.glade @@ -210,6 +210,27 @@ 1 + + + Notify of new mail at start_up + True + True + False + 12 + 5 + 5 + 5 + True + 0 + True + + + 0 + 9 + 1 + 1 + +