From: Ed Page Date: Sat, 26 Feb 2011 01:24:10 +0000 (-0600) Subject: Adding in application alarm support X-Git-Url: http://git.maemo.org/git/?p=gc-dialer;a=commitdiff_plain;h=0b0fee7fba5f0bcba82ac289aaf1d2627905c25a Adding in application alarm support --- diff --git a/src/alarm_handler.py b/src/alarm_handler.py index 70854ff..52a580e 100644 --- a/src/alarm_handler.py +++ b/src/alarm_handler.py @@ -6,6 +6,7 @@ import datetime import ConfigParser import logging +from PyQt4 import QtCore import dbus @@ -113,10 +114,15 @@ class _FremantleAlarmHandler(object): 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) + 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: @@ -255,39 +261,67 @@ class _DiabloAlarmHandler(object): assert deleteResult != -1, "Deleting of alarm event failed" -class _NoneAlarmHandler(object): +class _ApplicationAlarmHandler(object): - _INVALID_COOKIE = -1 _REPEAT_FOREVER = -1 - _LAUNCHER = os.path.abspath(os.path.join(os.path.dirname(__file__), "alarm_notify.py")) + _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._alarmCookie = 0 + self._enabled = False 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): - 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) + config.set(sectionName, "recurrence", str(self.recurrence)) def apply_settings(self, enabled, recurrence): - self._alarmCookie = 0 if enabled else self._INVALID_COOKIE - self._recurrence = recurrence + self._enabled = enabled @property def recurrence(self): @@ -295,16 +329,91 @@ class _NoneAlarmHandler(object): @property def isEnabled(self): - return self._alarmCookie != self._INVALID_COOKIE + return self._enabled -AlarmHandler = { +_BACKGROUND_ALARM_FACTORY = { _FREMANTLE_ALARM: _FremantleAlarmHandler, _DIABLO_ALARM: _DiabloAlarmHandler, - _NO_ALARM: _NoneAlarmHandler, + _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(): logFormat = '(%(relativeCreated)5d) %(levelname)-5s %(threadName)s.%(name)s.%(funcName)s: %(message)s' logging.basicConfig(level=logging.DEBUG, format=logFormat) diff --git a/src/dialcentral_qt.py b/src/dialcentral_qt.py index b99d32a..0dbf286 100755 --- a/src/dialcentral_qt.py +++ b/src/dialcentral_qt.py @@ -13,6 +13,8 @@ from PyQt4 import QtGui from PyQt4 import QtCore import constants +import alarm_handler +import led_handler from util import qtpie from util import qwrappers from util import qui_utils @@ -24,29 +26,6 @@ import session _moduleLogger = logging.getLogger(__name__) -class LedWrapper(object): - - def __init__(self): - self._ledHandler = None - self._init = False - - def off(self): - self._lazy_init() - if self._ledHandler is not None: - self._ledHandler.off() - - def _lazy_init(self): - if self._init: - return - self._init = True - try: - import led_handler - self._ledHandler = led_handler.LedHandler() - except Exception, e: - _moduleLogger.exception('Unable to initialize LED Handling: "%s"' % str(e)) - self._ledHandler = None - - class Dialcentral(qwrappers.ApplicationWrapper): _DATA_PATHS = [ @@ -57,24 +36,12 @@ class Dialcentral(qwrappers.ApplicationWrapper): def __init__(self, app): self._dataPath = None self._aboutDialog = None - self._ledHandler = LedWrapper() self.notifyOnMissed = False self.notifyOnVoicemail = False self.notifyOnSms = False - try: - import alarm_handler - if alarm_handler.AlarmHandler is not alarm_handler._NoneAlarmHandler: - self._alarmHandler = alarm_handler.AlarmHandler() - else: - self._alarmHandler = None - except (ImportError, OSError): - self._alarmHandler = None - except Exception: - _moduleLogger.exception("Notification failure") - self._alarmHandler = None - if self._alarmHandler is None: - _moduleLogger.info("No notification support") + self._ledHandler = led_handler.LedHandler() + self._alarmHandler = alarm_handler.AlarmHandler() qwrappers.ApplicationWrapper.__init__(self, app, constants) @@ -123,28 +90,27 @@ class Dialcentral(qwrappers.ApplicationWrapper): except Exception: _moduleLogger.exception("Unknown loading error") - if self._alarmHandler is not None: - try: - self._alarmHandler.load_settings(config, "alarm") - self.notifyOnMissed = config.getboolean("2 - Account Info", "notifyOnMissed") - self.notifyOnVoicemail = config.getboolean("2 - Account Info", "notifyOnVoicemail") - self.notifyOnSms = config.getboolean("2 - Account Info", "notifyOnSms") - except ConfigParser.NoOptionError, e: - _moduleLogger.info( - "Settings file %s is missing option %s" % ( - constants._user_settings_, - e.option, - ), - ) - except ConfigParser.NoSectionError, e: - _moduleLogger.info( - "Settings file %s is missing section %s" % ( - constants._user_settings_, - e.section, - ), - ) - except Exception: - _moduleLogger.exception("Unknown loading error") + try: + self._alarmHandler.load_settings(config, "alarm") + self.notifyOnMissed = config.getboolean("2 - Account Info", "notifyOnMissed") + self.notifyOnVoicemail = config.getboolean("2 - Account Info", "notifyOnVoicemail") + self.notifyOnSms = config.getboolean("2 - Account Info", "notifyOnSms") + except ConfigParser.NoOptionError, e: + _moduleLogger.info( + "Settings file %s is missing option %s" % ( + constants._user_settings_, + e.option, + ), + ) + except ConfigParser.NoSectionError, e: + _moduleLogger.info( + "Settings file %s is missing section %s" % ( + constants._user_settings_, + e.section, + ), + ) + except Exception: + _moduleLogger.exception("Unknown loading error") creds = ( base64.b64decode(blob) @@ -168,9 +134,8 @@ class Dialcentral(qwrappers.ApplicationWrapper): blob = base64.b64encode(value) config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob) - if self._alarmHandler is not None: - config.add_section("alarm") - self._alarmHandler.save_settings(config, "alarm") + config.add_section("alarm") + self._alarmHandler.save_settings(config, "alarm") config.add_section("2 - Account Info") config.set("2 - Account Info", "notifyOnMissed", repr(self.notifyOnMissed)) config.set("2 - Account Info", "notifyOnVoicemail", repr(self.notifyOnVoicemail)) @@ -361,6 +326,7 @@ class MainWindow(qwrappers.WindowWrapper): self._session.loggedOut.connect(self._on_logout) self._session.draft.recipientsChanged.connect(self._on_recipients_changed) self._session.newMessages.connect(self._on_new_message_alert) + self._app.alarmHandler.applicationNotifySignal.connect(self._on_app_alert) self._defaultCredentials = "", "" self._curentCredentials = "", "" self._currentTab = 0 @@ -551,14 +517,12 @@ class MainWindow(qwrappers.WindowWrapper): if self._accountDialog is None: import dialogs self._accountDialog = dialogs.AccountDialog(self._app) - if self._app.alarmHandler is None: - self._accountDialog.setIfNotificationsSupported(False) - if self._app.alarmHandler is not None: - self._accountDialog.notifications = self._app.alarmHandler.isEnabled - self._accountDialog.notificationTime = self._app.alarmHandler.recurrence - self._accountDialog.notifyOnMissed = self._app.notifyOnMissed - self._accountDialog.notifyOnVoicemail = self._app.notifyOnVoicemail - self._accountDialog.notifyOnSms = self._app.notifyOnSms + self._accountDialog.setIfNotificationsSupported(self._app.alarmHandler.backgroundNotificationsSupported) + self._accountDialog.notifications = self._app.alarmHandler.alarmType + self._accountDialog.notificationTime = self._app.alarmHandler.recurrence + self._accountDialog.notifyOnMissed = self._app.notifyOnMissed + self._accountDialog.notifyOnVoicemail = self._app.notifyOnVoicemail + self._accountDialog.notifyOnSms = self._app.notifyOnSms self._accountDialog.set_callbacks( self._session.get_callback_numbers(), self._session.get_callback_number() ) @@ -577,12 +541,11 @@ class MainWindow(qwrappers.WindowWrapper): else: callbackNumber = self._accountDialog.selectedCallback self._session.set_callback_number(callbackNumber) - if self._app.alarmHandler is not None: - self._app.alarmHandler.apply_settings(self._accountDialog.notifications, self._accountDialog.notificationTime) - self._app.notifyOnMissed = self._accountDialog.notifyOnMissed - self._app.notifyOnVoicemail = self._accountDialog.notifyOnVoicemail - self._app.notifyOnSms = self._accountDialog.notifyOnSms - self._app.save_settings() + self._app.alarmHandler.apply_settings(self._accountDialog.notifications, self._accountDialog.notificationTime) + self._app.notifyOnMissed = self._accountDialog.notifyOnMissed + self._app.notifyOnVoicemail = self._accountDialog.notifyOnVoicemail + self._app.notifyOnSms = self._accountDialog.notifyOnSms + self._app.save_settings() elif response == QtGui.QDialog.Rejected: _moduleLogger.info("Cancelled") else: @@ -592,7 +555,11 @@ class MainWindow(qwrappers.WindowWrapper): @misc_utils.log_exception(_moduleLogger) def _on_new_message_alert(self): with qui_utils.notify_error(self._errorLog): - self._errorLog.push_message("New messages available") + if self._app.alarmHandler.alarmType == self._app.alarmHandler.ALARM_APPLICATION: + if self._currentTab == self.MESSAGES_TAB or not self._app.ledHandler.isReal: + self._errorLog.push_message("New messages available") + else: + self._app.ledHandler.on() @QtCore.pyqtSlot(str) @misc_utils.log_exception(_moduleLogger) @@ -624,6 +591,13 @@ class MainWindow(qwrappers.WindowWrapper): @QtCore.pyqtSlot() @misc_utils.log_exception(_moduleLogger) + def _on_app_alert(self): + with qui_utils.notify_error(self._errorLog): + if self._session.state == self._session.LOGGEDIN_STATE: + self._session.update_messages(force=True) + + @QtCore.pyqtSlot() + @misc_utils.log_exception(_moduleLogger) def _on_recipients_changed(self): with qui_utils.notify_error(self._errorLog): if self._session.draft.get_num_contacts() == 0: @@ -653,6 +627,8 @@ class MainWindow(qwrappers.WindowWrapper): with qui_utils.notify_error(self._errorLog): self._currentTab = index self._initialize_tab(index) + if self._app.alarmHandler.alarmType == self._app.alarmHandler.ALARM_APPLICATION: + self._app.ledHandler.off() @QtCore.pyqtSlot() @QtCore.pyqtSlot(bool) diff --git a/src/dialogs.py b/src/dialogs.py index 18d97e6..4cbcd84 100644 --- a/src/dialogs.py +++ b/src/dialogs.py @@ -169,13 +169,17 @@ class AccountDialog(object): (12*60, "12 hours"), ] + ALARM_NONE = "No Alert" + ALARM_BACKGROUND = "Background Alert" + ALARM_APPLICATION = "Application Alert" + def __init__(self, app): self._app = app self._doClear = False self._accountNumberLabel = QtGui.QLabel("NUMBER NOT SET") - self._notificationButton = QtGui.QCheckBox("Notifications") - self._notificationButton.stateChanged.connect(self._on_notification_change) + self._notificationSelecter = QtGui.QComboBox() + self._notificationSelecter.currentIndexChanged.connect(self._on_notification_change) self._notificationTimeSelector = QtGui.QComboBox() #self._notificationTimeSelector.setEditable(True) self._notificationTimeSelector.setInsertPolicy(QtGui.QComboBox.InsertAtTop) @@ -197,7 +201,7 @@ class AccountDialog(object): self._credLayout.addWidget(self._accountNumberLabel, 0, 1) self._credLayout.addWidget(QtGui.QLabel("Callback"), 1, 0) self._credLayout.addWidget(self._callbackSelector, 1, 1) - self._credLayout.addWidget(self._notificationButton, 2, 0) + self._credLayout.addWidget(self._notificationSelecter, 2, 0) self._credLayout.addWidget(self._notificationTimeSelector, 2, 1) self._credLayout.addWidget(QtGui.QLabel(""), 3, 0) self._credLayout.addWidget(self._missedCallsNotificationButton, 3, 1) @@ -239,24 +243,34 @@ class AccountDialog(object): def setIfNotificationsSupported(self, isSupported): if isSupported: - self._notificationButton.setVisible(True) - self._notificationTimeSelector.setVisible(True) - self._missedCallsNotificationButton.setVisible(True) - self._voicemailNotificationButton.setVisible(True) - self._smsNotificationButton.setVisible(True) + self._notificationSelecter.clear() + self._notificationSelecter.addItems([self.ALARM_NONE, self.ALARM_APPLICATION, self.ALARM_BACKGROUND]) + self._notificationTimeSelector.setEnabled(False) + self._missedCallsNotificationButton.setEnabled(False) + self._voicemailNotificationButton.setEnabled(False) + self._smsNotificationButton.setEnabled(False) else: - self._notificationButton.setVisible(False) - self._notificationTimeSelector.setVisible(False) - self._missedCallsNotificationButton.setVisible(False) - self._voicemailNotificationButton.setVisible(False) - self._smsNotificationButton.setVisible(False) + self._notificationSelecter.clear() + self._notificationSelecter.addItems([self.ALARM_NONE, self.ALARM_APPLICATION]) + self._notificationTimeSelector.setEnabled(False) + self._missedCallsNotificationButton.setEnabled(False) + self._voicemailNotificationButton.setEnabled(False) + self._smsNotificationButton.setEnabled(False) def set_account_number(self, num): self._accountNumberLabel.setText(num) + def _set_notifications(self, enabled): + for i in xrange(self._notificationSelecter.count()): + if self._notificationSelecter.itemText(i) == enabled: + self._notificationSelecter.setCurrentIndex(i) + break + else: + self._notificationSelecter.setCurrentIndex(0) + notifications = property( - lambda self: self._notificationButton.isChecked(), - lambda self, enabled: self._notificationButton.setChecked(enabled), + lambda self: str(self._notificationSelecter.currentText()), + _set_notifications, ) notifyOnMissed = property( @@ -325,20 +339,37 @@ class AccountDialog(object): _moduleLogger.exception("Oh well") def _update_notification_state(self): - if self._notificationButton.isChecked(): + currentText = str(self._notificationSelecter.currentText()) + if currentText == self.ALARM_BACKGROUND: self._notificationTimeSelector.setEnabled(True) + self._missedCallsNotificationButton.setEnabled(True) self._voicemailNotificationButton.setEnabled(True) self._smsNotificationButton.setEnabled(True) + elif currentText == self.ALARM_APPLICATION: + self._notificationTimeSelector.setEnabled(True) + + self._missedCallsNotificationButton.setEnabled(False) + self._voicemailNotificationButton.setEnabled(False) + self._smsNotificationButton.setEnabled(False) + + self._missedCallsNotificationButton.setChecked(False) + self._voicemailNotificationButton.setChecked(True) + self._smsNotificationButton.setChecked(True) else: + self._notificationTimeSelector.setEnabled(False) self._missedCallsNotificationButton.setEnabled(False) self._voicemailNotificationButton.setEnabled(False) self._smsNotificationButton.setEnabled(False) + self._missedCallsNotificationButton.setChecked(False) + self._voicemailNotificationButton.setChecked(False) + self._smsNotificationButton.setChecked(False) + @QtCore.pyqtSlot(int) @misc_utils.log_exception(_moduleLogger) - def _on_notification_change(self, state): + def _on_notification_change(self, index): with qui_utils.notify_error(self._app.errorLog): self._update_notification_state() diff --git a/src/gv_views.py b/src/gv_views.py index 6aba9c3..e1eff94 100644 --- a/src/gv_views.py +++ b/src/gv_views.py @@ -344,7 +344,7 @@ class History(object): def refresh(self, force=True): self._itemView.setFocus(QtCore.Qt.OtherFocusReason) self._session.update_history(force) - if self._app.notifyOnMissed: + if self._app.notifyOnMissed and self._app.alarmHandler.alarmType == self._app.alarmHandler.ALARM_BACKGROUND: self._app.ledHandler.off() def _populate_items(self): @@ -554,7 +554,7 @@ class Messages(object): def refresh(self, force=True): self._itemView.setFocus(QtCore.Qt.OtherFocusReason) self._session.update_messages(force) - if self._app.notifyOnSms or self._app.notifyOnVoicemail: + if self._app.notifyOnSms or self._app.notifyOnVoicemail and self._app.alarmHandler.alarmType == self._app.alarmHandler.ALARM_BACKGROUND: self._app.ledHandler.off() def _populate_items(self): diff --git a/src/led_handler.py b/src/led_handler.py index 211036e..f1b6328 100755 --- a/src/led_handler.py +++ b/src/led_handler.py @@ -3,7 +3,7 @@ import dbus -class LedHandler(object): +class _NokiaLedHandler(object): def __init__(self): self._bus = dbus.SystemBus() @@ -19,6 +19,48 @@ class LedHandler(object): self._mceRequest.req_led_pattern_deactivate(self._ledPattern) +class _NoLedHandler(object): + + def __init__(self): + pass + + def on(self): + pass + + def off(self): + pass + + +class LedHandler(object): + + def __init__(self): + self._actual = None + self._isReal = False + + def on(self): + self._lazy_init() + self._actual.on() + + def off(self): + self._lazy_init() + self._actual.on() + + @property + def isReal(self): + self._lazy_init() + self._isReal + + def _lazy_init(self): + if self._actual is not None: + return + try: + self._actual = _NokiaLedHandler() + self._isReal = True + except dbus.DBusException: + self._actual = _NoLedHandler() + self._isReal = False + + if __name__ == "__main__": - leds = LedHandler() + leds = _NokiaLedHandler() leds.off() diff --git a/src/session.py b/src/session.py index c325d92..8728b68 100644 --- a/src/session.py +++ b/src/session.py @@ -671,6 +671,7 @@ class Session(QtCore.QObject): def _alert_on_messages(self, messages): cleanNewMessages = list(self._clean_messages(messages)) + cleanNewMessages.sort(key=lambda m: m["contactId"]) if self._cleanMessages: if self._cleanMessages != cleanNewMessages: self.newMessages.emit()