X-Git-Url: http://git.maemo.org/git/?p=gc-dialer;a=blobdiff_plain;f=src%2Falarm_handler.py;h=a79f992f9b08d5b6c1f76a73e1b92b88cac19a37;hp=d3a9bb93f1eef2a4a43b37d64e47132a0870dd56;hb=b124bd533d432f7e468f2ec9f03c5f19ab5625d1;hpb=70ee2dce6438c82655bb60d4e6a042c762c671a4 diff --git a/src/alarm_handler.py b/src/alarm_handler.py index d3a9bb9..a79f992 100644 --- a/src/alarm_handler.py +++ b/src/alarm_handler.py @@ -4,22 +4,180 @@ 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 @@ -38,6 +196,8 @@ class AlarmHandler(object): self._launcher = launcher except ConfigParser.NoOptionError: pass + except ConfigParser.NoSectionError: + pass def save_settings(self, config, sectionName): config.set(sectionName, "recurrence", str(self._recurrence)) @@ -61,22 +221,18 @@ class AlarmHandler(object): def isEnabled(self): return self._alarmCookie != self._INVALID_COOKIE - def _get_start_time(self, 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 _set_alarm(self, recurrence): assert 1 <= recurrence, "Notifications set to occur too frequently: %d" % recurrence - alarmTime = self._get_start_time(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([ @@ -106,8 +262,163 @@ class AlarmHandler(object): 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