import os
import time
import datetime
+import ConfigParser
+import logging
+import util.qt_compat as qt_compat
+QtCore = qt_compat.QtCore
import dbus
-import osso.alarmd as alarmd
-class AlarmHandler(object):
+_FREMANTLE_ALARM = "Fremantle"
+_DIABLO_ALARM = "Diablo"
+_NO_ALARM = "None"
+
+
+try:
+ import alarm
+ ALARM_TYPE = _FREMANTLE_ALARM
+except (ImportError, OSError):
+ try:
+ import osso.alarmd as alarmd
+ ALARM_TYPE = _DIABLO_ALARM
+ except (ImportError, OSError):
+ ALARM_TYPE = _NO_ALARM
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+def _get_start_time(recurrence):
+ now = datetime.datetime.now()
+ startTimeMinute = now.minute + max(recurrence, 5) # being safe
+ startTimeHour = now.hour + int(startTimeMinute / 60)
+ startTimeMinute = startTimeMinute % 59
+ now.replace(minute=startTimeMinute)
+ timestamp = int(time.mktime(now.timetuple()))
+ return timestamp
+
+
+def _create_recurrence_mask(recurrence, base):
+ """
+ >>> bin(_create_recurrence_mask(60, 60))
+ '0b1'
+ >>> bin(_create_recurrence_mask(30, 60))
+ '0b1000000000000000000000000000001'
+ >>> bin(_create_recurrence_mask(2, 60))
+ '0b10101010101010101010101010101010101010101010101010101010101'
+ >>> bin(_create_recurrence_mask(1, 60))
+ '0b111111111111111111111111111111111111111111111111111111111111'
+ """
+ mask = 0
+ for i in xrange(base / recurrence):
+ mask |= 1 << (recurrence * i)
+ return mask
+
+
+def _unpack_minutes(recurrence):
+ """
+ >>> _unpack_minutes(0)
+ (0, 0, 0)
+ >>> _unpack_minutes(1)
+ (0, 0, 1)
+ >>> _unpack_minutes(59)
+ (0, 0, 59)
+ >>> _unpack_minutes(60)
+ (0, 1, 0)
+ >>> _unpack_minutes(129)
+ (0, 2, 9)
+ >>> _unpack_minutes(5 * 60 * 24 + 3 * 60 + 2)
+ (5, 3, 2)
+ >>> _unpack_minutes(12 * 60 * 24 + 3 * 60 + 2)
+ (5, 3, 2)
+ """
+ minutesInAnHour = 60
+ minutesInDay = 24 * minutesInAnHour
+ minutesInAWeek = minutesInDay * 7
+
+ days = recurrence / minutesInDay
+ daysOfWeek = days % 7
+ recurrence -= days * minutesInDay
+ hours = recurrence / minutesInAnHour
+ recurrence -= hours * minutesInAnHour
+ mins = recurrence % minutesInAnHour
+ recurrence -= mins
+ assert recurrence == 0, "Recurrence %d" % recurrence
+ return daysOfWeek, hours, mins
+
+
+class _FremantleAlarmHandler(object):
+
+ _INVALID_COOKIE = -1
+ _REPEAT_FOREVER = -1
+ _TITLE = "Dialcentral Notifications"
+ _LAUNCHER = os.path.abspath(os.path.join(os.path.dirname(__file__), "alarm_notify.py"))
+
+ def __init__(self):
+ self._recurrence = 5
+
+ self._alarmCookie = self._INVALID_COOKIE
+ self._launcher = self._LAUNCHER
+
+ def load_settings(self, config, sectionName):
+ try:
+ self._recurrence = config.getint(sectionName, "recurrence")
+ self._alarmCookie = config.getint(sectionName, "alarmCookie")
+ launcher = config.get(sectionName, "notifier")
+ if launcher:
+ self._launcher = launcher
+ except ConfigParser.NoOptionError:
+ pass
+ except ConfigParser.NoSectionError:
+ pass
+
+ def save_settings(self, config, sectionName):
+ try:
+ config.set(sectionName, "recurrence", str(self._recurrence))
+ config.set(sectionName, "alarmCookie", str(self._alarmCookie))
+ launcher = self._launcher if self._launcher != self._LAUNCHER else ""
+ config.set(sectionName, "notifier", launcher)
+ except ConfigParser.NoOptionError:
+ pass
+ except ConfigParser.NoSectionError:
+ pass
+
+ def apply_settings(self, enabled, recurrence):
+ if recurrence != self._recurrence or enabled != self.isEnabled:
+ if self.isEnabled:
+ self._clear_alarm()
+ if enabled:
+ self._set_alarm(recurrence)
+ self._recurrence = int(recurrence)
+
+ @property
+ def recurrence(self):
+ return self._recurrence
+
+ @property
+ def isEnabled(self):
+ return self._alarmCookie != self._INVALID_COOKIE
+
+ def _set_alarm(self, recurrenceMins):
+ assert 1 <= recurrenceMins, "Notifications set to occur too frequently: %d" % recurrenceMins
+ alarmTime = _get_start_time(recurrenceMins)
+
+ event = alarm.Event()
+ event.appid = self._TITLE
+ event.alarm_time = alarmTime
+ event.recurrences_left = self._REPEAT_FOREVER
+
+ action = event.add_actions(1)[0]
+ action.flags |= alarm.ACTION_TYPE_EXEC | alarm.ACTION_WHEN_TRIGGERED
+ action.command = self._launcher
+
+ recurrence = event.add_recurrences(1)[0]
+ recurrence.mask_min |= _create_recurrence_mask(recurrenceMins, 60)
+ recurrence.mask_hour |= alarm.RECUR_HOUR_DONTCARE
+ recurrence.mask_mday |= alarm.RECUR_MDAY_DONTCARE
+ recurrence.mask_wday |= alarm.RECUR_WDAY_DONTCARE
+ recurrence.mask_mon |= alarm.RECUR_MON_DONTCARE
+ recurrence.special |= alarm.RECUR_SPECIAL_NONE
+
+ assert event.is_sane()
+ self._alarmCookie = alarm.add_event(event)
+
+ def _clear_alarm(self):
+ if self._alarmCookie == self._INVALID_COOKIE:
+ return
+ alarm.delete_event(self._alarmCookie)
+ self._alarmCookie = self._INVALID_COOKIE
+
+
+class _DiabloAlarmHandler(object):
_INVALID_COOKIE = -1
_TITLE = "Dialcentral Notifications"
_LAUNCHER = os.path.abspath(os.path.join(os.path.dirname(__file__), "alarm_notify.py"))
_REPEAT_FOREVER = -1
- _DEFAULT_FLAGS = (
- alarmd.ALARM_EVENT_NO_DIALOG |
- alarmd.ALARM_EVENT_NO_SNOOZE |
- alarmd.ALARM_EVENT_CONNECTED
- )
def __init__(self):
self._recurrence = 5
bus = dbus.SystemBus()
self._alarmdDBus = bus.get_object("com.nokia.alarmd", "/com/nokia/alarmd");
self._alarmCookie = self._INVALID_COOKIE
+ self._launcher = self._LAUNCHER
def load_settings(self, config, sectionName):
- self._recurrence = config.getint(sectionName, "recurrence")
- self._alarmCookie = config.getint(sectionName, "alarmCookie")
+ try:
+ self._recurrence = config.getint(sectionName, "recurrence")
+ self._alarmCookie = config.getint(sectionName, "alarmCookie")
+ launcher = config.get(sectionName, "notifier")
+ if launcher:
+ self._launcher = launcher
+ except ConfigParser.NoOptionError:
+ pass
+ except ConfigParser.NoSectionError:
+ pass
def save_settings(self, config, sectionName):
config.set(sectionName, "recurrence", str(self._recurrence))
config.set(sectionName, "alarmCookie", str(self._alarmCookie))
+ launcher = self._launcher if self._launcher != self._LAUNCHER else ""
+ config.set(sectionName, "notifier", launcher)
def apply_settings(self, enabled, recurrence):
if recurrence != self._recurrence or enabled != self.isEnabled:
if self.isEnabled:
- self.delete_alarm()
+ self._clear_alarm()
if enabled:
self._set_alarm(recurrence)
self._recurrence = int(recurrence)
- def delete_alarm(self):
- if self._alarmCookie == self._INVALID_COOKIE:
- return
- deleteResult = self._alarmdDBus.del_event(dbus.Int32(self._alarmCookie))
- self._alarmCookie = self._INVALID_COOKIE
- assert deleteResult != -1, "Deleting of alarm event failed"
-
@property
def recurrence(self):
return self._recurrence
def isEnabled(self):
return self._alarmCookie != self._INVALID_COOKIE
- def _get_start_time(self, recurrence):
- now = datetime.datetime.now()
- startTimeMinute = now.minute + recurrence
- now.replace(minute=startTimeMinute)
- timestamp = int(time.mktime(now.timetuple()))
- return timestamp
-
def _set_alarm(self, recurrence):
- alarmTime = self._get_start_time(recurrence)
+ assert 1 <= recurrence, "Notifications set to occur too frequently: %d" % recurrence
+ alarmTime = _get_start_time(recurrence)
#Setup the alarm arguments so that they can be passed to the D-Bus add_event method
+ _DEFAULT_FLAGS = (
+ alarmd.ALARM_EVENT_NO_DIALOG |
+ alarmd.ALARM_EVENT_NO_SNOOZE |
+ alarmd.ALARM_EVENT_CONNECTED
+ )
action = []
- action.extend(['flags', self._DEFAULT_FLAGS])
+ action.extend(['flags', _DEFAULT_FLAGS])
action.extend(['title', self._TITLE])
- action.extend(['path', self._LAUNCHER])
+ action.extend(['path', self._launcher])
action.extend([
'arguments',
dbus.Array(
self._alarmCookie = self._alarmdDBus.add_event(*event);
+ def _clear_alarm(self):
+ if self._alarmCookie == self._INVALID_COOKIE:
+ return
+ deleteResult = self._alarmdDBus.del_event(dbus.Int32(self._alarmCookie))
+ self._alarmCookie = self._INVALID_COOKIE
+ assert deleteResult != -1, "Deleting of alarm event failed"
+
+
+class _ApplicationAlarmHandler(object):
+
+ _REPEAT_FOREVER = -1
+ _MIN_TO_MS_FACTORY = 1000 * 60
+
+ def __init__(self):
+ self._timer = QtCore.QTimer()
+ self._timer.setSingleShot(False)
+ self._timer.setInterval(5 * self._MIN_TO_MS_FACTORY)
+
+ def load_settings(self, config, sectionName):
+ try:
+ self._timer.setInterval(config.getint(sectionName, "recurrence") * self._MIN_TO_MS_FACTORY)
+ except ConfigParser.NoOptionError:
+ pass
+ except ConfigParser.NoSectionError:
+ pass
+ self._timer.start()
+
+ def save_settings(self, config, sectionName):
+ config.set(sectionName, "recurrence", str(self.recurrence))
+
+ def apply_settings(self, enabled, recurrence):
+ self._timer.setInterval(recurrence * self._MIN_TO_MS_FACTORY)
+ if enabled:
+ self._timer.start()
+ else:
+ self._timer.stop()
+
+ @property
+ def notifySignal(self):
+ return self._timer.timeout
+
+ @property
+ def recurrence(self):
+ return int(self._timer.interval() / self._MIN_TO_MS_FACTORY)
+
+ @property
+ def isEnabled(self):
+ return self._timer.isActive()
+
+
+class _NoneAlarmHandler(object):
+
+ def __init__(self):
+ self._enabled = False
+ self._recurrence = 5
+
+ def load_settings(self, config, sectionName):
+ try:
+ self._recurrence = config.getint(sectionName, "recurrence")
+ self._enabled = True
+ except ConfigParser.NoOptionError:
+ pass
+ except ConfigParser.NoSectionError:
+ pass
+
+ def save_settings(self, config, sectionName):
+ config.set(sectionName, "recurrence", str(self.recurrence))
+
+ def apply_settings(self, enabled, recurrence):
+ self._enabled = enabled
+
+ @property
+ def recurrence(self):
+ return self._recurrence
+
+ @property
+ def isEnabled(self):
+ return self._enabled
+
+
+_BACKGROUND_ALARM_FACTORY = {
+ _FREMANTLE_ALARM: _FremantleAlarmHandler,
+ _DIABLO_ALARM: _DiabloAlarmHandler,
+ _NO_ALARM: None,
+}[ALARM_TYPE]
+
+
+class AlarmHandler(object):
+
+ ALARM_NONE = "No Alert"
+ ALARM_BACKGROUND = "Background Alert"
+ ALARM_APPLICATION = "Application Alert"
+ ALARM_TYPES = [ALARM_NONE, ALARM_BACKGROUND, ALARM_APPLICATION]
+
+ ALARM_FACTORY = {
+ ALARM_NONE: _NoneAlarmHandler,
+ ALARM_BACKGROUND: _BACKGROUND_ALARM_FACTORY,
+ ALARM_APPLICATION: _ApplicationAlarmHandler,
+ }
+
+ def __init__(self):
+ self._alarms = {self.ALARM_NONE: _NoneAlarmHandler()}
+ self._currentAlarmType = self.ALARM_NONE
+
+ def load_settings(self, config, sectionName):
+ try:
+ self._currentAlarmType = config.get(sectionName, "alarm")
+ except (ConfigParser.NoOptionError, ConfigParser.NoSectionError):
+ _moduleLogger.exception("Falling back to old style")
+ self._currentAlarmType = self.ALARM_BACKGROUND
+ if self._currentAlarmType not in self.ALARM_TYPES:
+ self._currentAlarmType = self.ALARM_NONE
+
+ self._init_alarm(self._currentAlarmType)
+ if self._currentAlarmType in self._alarms:
+ self._alarms[self._currentAlarmType].load_settings(config, sectionName)
+ if not self._alarms[self._currentAlarmType].isEnabled:
+ _moduleLogger.info("Config file lied, not actually enabled")
+ self._currentAlarmType = self.ALARM_NONE
+ else:
+ _moduleLogger.info("Background alerts not supported")
+ self._currentAlarmType = self.ALARM_NONE
+
+ def save_settings(self, config, sectionName):
+ config.set(sectionName, "alarm", self._currentAlarmType)
+ self._alarms[self._currentAlarmType].save_settings(config, sectionName)
+
+ def apply_settings(self, t, recurrence):
+ self._init_alarm(t)
+ newHandler = self._alarms[t]
+ oldHandler = self._alarms[self._currentAlarmType]
+ if newHandler != oldHandler:
+ oldHandler.apply_settings(False, 0)
+ newHandler.apply_settings(True, recurrence)
+ self._currentAlarmType = t
+
+ @property
+ def alarmType(self):
+ return self._currentAlarmType
+
+ @property
+ def backgroundNotificationsSupported(self):
+ return self.ALARM_FACTORY[self.ALARM_BACKGROUND] is not None
+
+ @property
+ def applicationNotifySignal(self):
+ self._init_alarm(self.ALARM_APPLICATION)
+ return self._alarms[self.ALARM_APPLICATION].notifySignal
+
+ @property
+ def recurrence(self):
+ return self._alarms[self._currentAlarmType].recurrence
+
+ @property
+ def isEnabled(self):
+ return self._currentAlarmType != self.ALARM_NONE
+
+ def _init_alarm(self, t):
+ if t not in self._alarms and self.ALARM_FACTORY[t] is not None:
+ self._alarms[t] = self.ALARM_FACTORY[t]()
+
def main():
- import ConfigParser
+ logFormat = '(%(relativeCreated)5d) %(levelname)-5s %(threadName)s.%(name)s.%(funcName)s: %(message)s'
+ logging.basicConfig(level=logging.DEBUG, format=logFormat)
import constants
try:
import optparse