diff --git a/data/org.gnome.hamster.gschema.xml b/data/org.gnome.hamster.gschema.xml
index dce4c1611..e8cc5f7f7 100644
--- a/data/org.gnome.hamster.gschema.xml
+++ b/data/org.gnome.hamster.gschema.xml
@@ -18,5 +18,20 @@
then the activity belongs to the previous hamster day.
+
+
+ 15
+ After how many minutes to notify the user
+
+
+
+ false
+ Enable notifications
+
+
+
+ false
+ Send Notifications when no activity is set
+
diff --git a/data/preferences.ui b/data/preferences.ui
index 37c81ecb3..213d78f0a 100644
--- a/data/preferences.ui
+++ b/data/preferences.ui
@@ -2,6 +2,11 @@
+
+ 100
+ 1
+ 10
+
False
@@ -40,6 +45,89 @@
start
vertical
8
+
+
+ False
+ vertical
+
+
+ Send notifications
+ True
+ True
+ False
+ True
+
+
+ False
+ True
+ -1
+
+
+
+
+ True
+ False
+ 20
+ vertical
+ 2
+
+
+ True
+ False
+ Remind of current activity every:
+
+
+ False
+ True
+ 0
+
+
+
+
+ True
+ True
+ adjustment1
+ 120
+ 0
+ 0
+ right
+
+
+ False
+ True
+ 1
+
+
+
+
+ Also remind when no activity is set
+ True
+ True
+ False
+ True
+
+
+
+ False
+ True
+ 2
+
+
+
+
+ False
+ True
+ 2
+ 4
+
+
+
+
+ False
+ True
+ 0
+
+
True
diff --git a/src/hamster-cli.py b/src/hamster-cli.py
index 9c6786787..a0176fab9 100644
--- a/src/hamster-cli.py
+++ b/src/hamster-cli.py
@@ -46,7 +46,7 @@
from hamster.lib import default_logger, stuff
from hamster.lib import datetime as dt
from hamster.lib.fact import Fact
-
+from hamster.lib.notifsmanager import notifs_mgr
logger = default_logger(__file__)
diff --git a/src/hamster/lib/configuration.py b/src/hamster/lib/configuration.py
index 24b42c668..c477531cc 100644
--- a/src/hamster/lib/configuration.py
+++ b/src/hamster/lib/configuration.py
@@ -181,5 +181,19 @@ def day_start(self):
hours, minutes = divmod(day_start_minutes, 60)
return dt.time(hours, minutes)
+ @property
+ def notify_interval(self):
+ """Notifications every X minutes"""
+ return self.get("notify-interval-minutes")
+
+ @property
+ def notifications_enabled(self):
+ """Enable/Disable notifications"""
+ return self.get("notifications-enabled")
+
+ @property
+ def notify_on_idle(self) -> bool:
+ """Enable/Disable notifications when no activity is set"""
+ return self.get("notify-on-idle-enabled")
conf = GSettingsStore()
diff --git a/src/hamster/lib/notifsmanager.py b/src/hamster/lib/notifsmanager.py
new file mode 100644
index 000000000..30b402c55
--- /dev/null
+++ b/src/hamster/lib/notifsmanager.py
@@ -0,0 +1,118 @@
+# - coding: utf-8 -
+
+# Copyright (C) 2020 Sharaf Zaman
+
+# This file is part of Project Hamster.
+
+# Project Hamster is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# Project Hamster is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with Project Hamster. If not, see .
+
+import logging
+logger = logging.getLogger(__name__) # noqa: E402
+import datetime
+import dbus
+import hamster.client
+
+from gi.repository import Gtk
+from gi.repository import GObject as gobject
+from hamster.lib.configuration import conf
+
+
+class Notification(object):
+ def __init__(self):
+ self.bus = dbus.SessionBus()
+ self.appname = "Hamster Time Tracker"
+ self.replace_id = 0
+ # FIXME: icon location?
+ self.icon = "/usr/share/hamster-time-tracker/art/hamster-time-tracker.png"
+ self.summary = "Hamster Time Tracker"
+ self.hints = {}
+ self.actions = []
+ self.data = {}
+ self.timeout = -1
+
+ def show(self, message):
+ """
+ Show notitification
+ returns: True if successful
+ """
+ try:
+ self.server = dbus.Interface(self.bus.get_object("org.freedesktop.Notifications",
+ "/org/freedesktop/Notifications"),
+ dbus_interface="org.freedesktop.Notifications")
+ except dbus.exceptions.DBusException as e:
+ # TODO: Log?
+ logger.error(e)
+ logger.warning("Notifications will be disabled")
+ return False
+
+ try:
+ self.server.Notify(self.appname,
+ self.replace_id,
+ self.icon,
+ self.summary,
+ message,
+ self.actions,
+ self.hints,
+ self.timeout
+ )
+ except:
+ return False
+
+ return True
+
+
+class NotificationsManager(gobject.GObject):
+ def __init__(self):
+ self.notify_interval = conf.notify_interval
+ self.minutes_passed = 0
+ self.notification = Notification()
+ gobject.timeout_add_seconds(60, self.check_interval)
+
+ def notify_interval_changed(self, value):
+ self.minutes_passed = 0
+ self.notify_interval = value
+
+ def check_interval(self):
+ if not conf.notifications_enabled:
+ self.minutes_passed = 0
+ return True
+
+ self.minutes_passed += 1
+
+ storage = hamster.client.Storage()
+ facts = storage.get_todays_facts()
+
+ if self.minutes_passed == self.notify_interval:
+ # if the activity is still active
+ if len(facts) > 0 and facts[-1].end_time is None:
+ timedelta_secs = (datetime.datetime.now() - facts[-1].start_time).seconds
+ hours, rem = divmod(timedelta_secs, 60 * 60)
+ minutes, seconds = divmod(rem, 60)
+ if hours != 0:
+ msg = str.format("Working on {} for {} hours and {} minutes", facts[-1].activity, hours, minutes)
+ else:
+ msg = str.format("Working on {} for {} minutes", facts[-1].activity, minutes)
+ self.notification.show(msg)
+ elif conf.notify_on_idle:
+ self.notification.show("No Activity")
+
+ self.minutes_passed = 0
+
+ return True
+
+ def send_test(self):
+ return self.notification.show("This is a test notification!")
+
+
+notifs_mgr = NotificationsManager()
diff --git a/src/hamster/preferences.py b/src/hamster/preferences.py
index c41b1d35c..f66e5a0fb 100644
--- a/src/hamster/preferences.py
+++ b/src/hamster/preferences.py
@@ -25,6 +25,7 @@
from hamster.lib import datetime as dt
from hamster.lib import stuff
from hamster.lib.configuration import Controller, runtime, conf
+from hamster.lib.notifsmanager import notifs_mgr
def get_prev(selection, model):
@@ -133,11 +134,21 @@ def __init__(self):
(selection, selection.connect('changed', self.category_changed_cb, self.category_store))
])
+ # Tracking tab
self.day_start = widgets.TimeInput(dt.time(5,30))
self.get_widget("day_start_placeholder").add(self.day_start)
+ self.notify_scale = self.get_widget("notify-interval-scale")
+ self.notify_scale.set_range(1, 120)
+
+ self.notifs_enabled_toggle = self.get_widget("notifs-enabled-toggle")
+ self.notification_box = self.get_widget("notification-box")
+
self.load_config()
+ self.notify_scale.connect("value-changed", self._notify_interval_value_changed)
+ self.notifs_enabled_toggle.connect("toggled", self._on_notifications_toggled)
+
# Allow enable drag and drop of rows including row move
self.activity_tree.enable_model_drag_source(gdk.ModifierType.BUTTON1_MASK,
self.TARGETS,
@@ -174,6 +185,14 @@ def load_config(self, *args):
self.tags = [tag["name"] for tag in runtime.storage.get_tags(only_autocomplete=True)]
self.get_widget("autocomplete_tags").set_text(", ".join(self.tags))
+ # enable/disable notification related settings
+ self.notifs_enabled_toggle.set_active(conf.notifications_enabled)
+ self.notification_box.set_sensitive(conf.notifications_enabled)
+
+ self.notify_scale.set_value(conf.notify_interval)
+ self.get_widget("notify-on-idle").set_active(conf.notify_on_idle)
+
+
def on_autocomplete_tags_view_focus_out_event(self, view, event):
buf = self.get_widget("autocomplete_tags")
updated_tags = buf.get_text(buf.get_start_iter(), buf.get_end_iter(), 0)
@@ -509,3 +528,24 @@ def on_day_start_changed(self, widget):
conf.set("day-start-minutes", day_start)
def on_close_button_clicked(self, button):
self.close_window()
+
+ def _on_notifications_toggled(self, checkbox):
+ # TODO: Show error message next to the widget
+ # Test only if activated
+ if checkbox.get_active():
+ if notifs_mgr.send_test():
+ self.notification_box.set_sensitive(checkbox.get_active())
+ conf.set("notifications-enabled", checkbox.get_active())
+ else:
+ checkbox.set_active(False)
+
+ self.notification_box.set_sensitive(checkbox.get_active())
+ conf.set("notifications-enabled", checkbox.get_active())
+
+ def _notify_on_idle_toggled(self, checkbox):
+ conf.set("notify-on-idle-enabled", checkbox.get_active())
+
+ def _notify_interval_value_changed(self, range):
+ notify_interval = range.get_value()
+ conf.set("notify-interval-minutes", notify_interval)
+ notifs_mgr.notify_interval_changed(notify_interval)