Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix notifications #521

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions data/org.gnome.hamster.gschema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,20 @@
then the activity belongs to the previous hamster day.
</description>
</key>

<key type="u" name="notify-interval-minutes">
<default>15</default>
<summary>After how many minutes to notify the user</summary>
</key>

<key type="b" name="notifications-enabled">
<default>false</default>
<summary>Enable notifications</summary>
</key>

<key type="b" name="notify-on-idle-enabled">
<default>false</default>
<summary>Send Notifications when no activity is set</summary>
</key>
</schema>
</schemalist>
88 changes: 88 additions & 0 deletions data/preferences.ui
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
<!-- Generated with glade 3.20.4 -->
<interface>
<requires lib="gtk+" version="3.10"/>
<object class="GtkAdjustment" id="adjustment1">
<property name="upper">100</property>
<property name="step_increment">1</property>
<property name="page_increment">10</property>
</object>
<object class="GtkTextBuffer" id="autocomplete_tags"/>
<object class="GtkWindow" id="window">
<property name="can_focus">False</property>
Expand Down Expand Up @@ -40,6 +45,89 @@
<property name="valign">start</property>
<property name="orientation">vertical</property>
<property name="spacing">8</property>
<child>
<object class="GtkBox" id="box1">
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
<child>
<object class="GtkCheckButton" id="notifs-enabled-toggle">
<property name="label" translatable="yes">Send notifications</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">-1</property>
</packing>
</child>
<child>
<object class="GtkBox" id="notification-box">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="margin_left">20</property>
<property name="orientation">vertical</property>
<property name="spacing">2</property>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="label" translatable="yes">Remind of current activity every: </property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkScale" id="notify-interval-scale">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="adjustment">adjustment1</property>
<property name="fill_level">120</property>
<property name="round_digits">0</property>
<property name="digits">0</property>
<property name="value_pos">right</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkCheckButton" id="notify-on-idle">
<property name="label" translatable="yes">Also remind when no activity is set</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="draw_indicator">True</property>
<signal name="toggled" handler="_notify_on_idle_toggled" swapped="no"/>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">2</property>
<property name="position">4</property>
</packing>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkBox" id="day-start box">
<property name="visible">True</property>
Expand Down
2 changes: 1 addition & 1 deletion src/hamster-cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

notifs_mgr seems to be referenced nowhere in this file. What am I missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Must be remnants of my debugging session. I'll remove it, thanks!


logger = default_logger(__file__)

Expand Down
14 changes: 14 additions & 0 deletions src/hamster/lib/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
126 changes: 126 additions & 0 deletions src/hamster/lib/notifsmanager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
# - coding: utf-8 -

# Copyright (C) 2020 Sharaf Zaman <[email protected]>

# 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 <http://www.gnu.org/licenses/>.

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
self.summary = "Hamster Time Tracker"
self.hints = {}
self.actions = []
self.data = {}
self.timeout = -1

info = Gtk.IconTheme().lookup_icon("hamster-time-tracker", 0, 0)
self.icon = info.get_filename()

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.notif_id = self.server.Notify(
self.appname,
self.replace_id,
self.icon,
self.summary,
message,
self.actions,
self.hints,
self.timeout)
except:
return False

return True

def close(self):
try: self.server.CloseNotification(self.notif_id)
except: pass



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.notification.close()

self.minutes_passed = 0

return True

def send_test(self):
return self.notification.show("This is a test notification!")


notifs_mgr = NotificationsManager()
40 changes: 40 additions & 0 deletions src/hamster/preferences.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)