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.
This commit is contained in:
Mohamed Ibrahim 2014-06-26 13:31:43 -07:00 committed by Jim Nelson
parent e07f2ad1ed
commit 6783c2ce63
15 changed files with 168 additions and 2 deletions

1
THANKS
View file

@ -17,6 +17,7 @@ Michael George <mdgeorge@cs.cornell.edu>
Michael Gratton <mike@vee.net>
Sven Hagemann <sven@rednose.nl>
Mathias Hasselmann <mathias@openismus.com>
Mohamed Ibrahim <mohamed.ib.md@gmail.com>
Timo Kluck <tkluck@infty.nl>
Charles Lehner <rdm.cel@celehner.com>
Avi Levy <avi.w.levy@gmail.com>

View file

@ -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)

View file

@ -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)

View file

@ -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

View file

@ -59,6 +59,11 @@
<summary>show notifications for new mail</summary>
<description>True to show notification bubbles.</description>
</key>
<key name="startup-notifications" type="b">
<default>false</default>
<summary>notify of new mail at startup</summary>
<description>True to notify of new mail at startup.</description>
</key>
<key name="ask-open-attachment" type="b">
<default>true</default>
<summary>ask when opening an attachment</summary>

View file

@ -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

View file

@ -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

View file

@ -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();
}
}

View file

@ -154,7 +154,11 @@ public class GearyApplication : Gtk.Application {
if (controller == null || controller.main_window == null)
return false;
if (!controller.main_window.get_realized())
controller.main_window.show_all();
else
controller.main_window.present();
return true;
}

View file

@ -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;

View file

@ -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";
@ -64,6 +65,11 @@ public class Configuration {
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";
public Date.ClockFormat clock_format {

View file

@ -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<Geary.Account, Geary.App.EmailStore> email_stores
= new Gee.HashMap<Geary.Account, Geary.App.EmailStore>();
@ -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();
}

View file

@ -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;

View file

@ -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();

View file

@ -210,6 +210,27 @@
<property name="height">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="startup_notifications">
<property name="label" translatable="yes">Notify of new mail at start_up</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="margin_left">12</property>
<property name="margin_right">5</property>
<property name="margin_top">5</property>
<property name="margin_bottom">5</property>
<property name="use_underline">True</property>
<property name="xalign">0</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="left_attach">0</property>
<property name="top_attach">9</property>
<property name="width">1</property>
<property name="height">1</property>
</packing>
</child>
<child>
<placeholder/>
</child>