X-Git-Url: http://git.maemo.org/git/?p=gc-dialer;a=blobdiff_plain;f=src%2Fdialcentral_qt.py;h=1377bd2f6031fc5c288c197c7e4abc4e3415fcd3;hp=b81939016dc3d6ba7171dd2bad7a996069cad24d;hb=0ad2d5ebd0448d8084e16017d6d69f84ed217dab;hpb=01fd33aaf43c49935b3d55aa5b38ff3db3369a13 diff --git a/src/dialcentral_qt.py b/src/dialcentral_qt.py index b819390..1377bd2 100755 --- a/src/dialcentral_qt.py +++ b/src/dialcentral_qt.py @@ -8,11 +8,13 @@ import base64 import ConfigParser import functools import logging +import logging.handlers from PyQt4 import QtGui from PyQt4 import QtCore import constants +import alarm_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,13 @@ 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._streamHandler = None + self._ledHandler = None + self._alarmHandler = alarm_handler.AlarmHandler() qwrappers.ApplicationWrapper.__init__(self, app, constants) @@ -94,88 +62,12 @@ class Dialcentral(qwrappers.ApplicationWrapper): except Exception: _moduleLogger.exception("Unknown loading error") - blobs = "", "" - isFullscreen = False - isPortrait = qui_utils.screen_orientation() == QtCore.Qt.Vertical - tabIndex = 0 - try: - blobs = [ - config.get(constants.__pretty_app_name__, "bin_blob_%i" % i) - for i in xrange(len(self._mainWindow.get_default_credentials())) - ] - isFullscreen = config.getboolean(constants.__pretty_app_name__, "fullscreen") - tabIndex = config.getint(constants.__pretty_app_name__, "tab") - isPortrait = config.getboolean(constants.__pretty_app_name__, "portrait") - 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") - - 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") - - creds = ( - base64.b64decode(blob) - for blob in blobs - ) - self._mainWindow.set_default_credentials(*creds) - self._fullscreenAction.setChecked(isFullscreen) - self._orientationAction.setChecked(isPortrait) - self._mainWindow.set_current_tab(tabIndex) self._mainWindow.load_settings(config) def save_settings(self): _moduleLogger.info("Saving settings") config = ConfigParser.SafeConfigParser() - config.add_section(constants.__pretty_app_name__) - config.set(constants.__pretty_app_name__, "tab", str(self._mainWindow.get_current_tab())) - config.set(constants.__pretty_app_name__, "fullscreen", str(self._fullscreenAction.isChecked())) - config.set(constants.__pretty_app_name__, "portrait", str(self._orientationAction.isChecked())) - for i, value in enumerate(self._mainWindow.get_default_credentials()): - 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("2 - Account Info") - config.set("2 - Account Info", "notifyOnMissed", repr(self.notifyOnMissed)) - config.set("2 - Account Info", "notifyOnVoicemail", repr(self.notifyOnVoicemail)) - config.set("2 - Account Info", "notifyOnSms", repr(self.notifyOnSms)) - self._mainWindow.save_settings(config) with open(constants._user_settings_, "wb") as configFile: @@ -193,6 +85,17 @@ class Dialcentral(qwrappers.ApplicationWrapper): else: return None + def get_resource(self, name): + if self._dataPath is None: + for path in self._DATA_PATHS: + if os.path.exists(os.path.join(path, name)): + self._dataPath = path + break + if self._dataPath is not None: + return os.path.join(self._dataPath, name) + else: + return None + def _close_windows(self): qwrappers.ApplicationWrapper._close_windows(self) if self._aboutDialog is not None: @@ -203,11 +106,21 @@ class Dialcentral(qwrappers.ApplicationWrapper): return os.path.join(constants._data_path_, "contacts") @property + def streamHandler(self): + if self._streamHandler is None: + import stream_handler + self._streamHandler = stream_handler.StreamHandler() + return self._streamHandler + + @property def alarmHandler(self): return self._alarmHandler @property def ledHandler(self): + if self._ledHandler is None: + import led_handler + self._ledHandler = led_handler.LedHandler() return self._ledHandler def _new_main_window(self): @@ -342,7 +255,6 @@ class MainWindow(qwrappers.WindowWrapper): def __init__(self, parent, app): qwrappers.WindowWrapper.__init__(self, parent, app) self._window.setWindowTitle("%s" % constants.__pretty_app_name__) - #self._freezer = qwrappers.AutoFreezeWindowFeature(self._app, self._window) self._errorLog = self._app.errorLog self._session = session.Session(self._errorLog, constants._data_path_) @@ -350,6 +262,15 @@ class MainWindow(qwrappers.WindowWrapper): self._session.loggedIn.connect(self._on_login) 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._voicemailRefreshDelay = QtCore.QTimer() + self._voicemailRefreshDelay.setInterval(30 * 1000) + self._voicemailRefreshDelay.timeout.connect(self._on_call_missed) + self._voicemailRefreshDelay.setSingleShot(True) + self._callHandler = None + self._updateVoicemailOnMissedCall = False + self._defaultCredentials = "", "" self._curentCredentials = "", "" self._currentTab = 0 @@ -394,30 +315,36 @@ class MainWindow(qwrappers.WindowWrapper): self._layout.addWidget(self._tabWidget) - self._loginTabAction = QtGui.QAction(None) - self._loginTabAction.setText("Login") - self._loginTabAction.triggered.connect(self._on_login_requested) + self._loginAction = QtGui.QAction(None) + self._loginAction.setText("Login") + self._loginAction.triggered.connect(self._on_login_requested) - self._importTabAction = QtGui.QAction(None) - self._importTabAction.setText("Import") - self._importTabAction.triggered.connect(self._on_import) + self._importAction = QtGui.QAction(None) + self._importAction.setText("Import") + self._importAction.triggered.connect(self._on_import) - self._accountTabAction = QtGui.QAction(None) - self._accountTabAction.setText("Account") - self._accountTabAction.triggered.connect(self._on_account) + self._accountAction = QtGui.QAction(None) + self._accountAction.setText("Account") + self._accountAction.triggered.connect(self._on_account) + + self._refreshConnectionAction = QtGui.QAction(None) + self._refreshConnectionAction.setText("Refresh Connection") + self._refreshConnectionAction.setShortcut(QtGui.QKeySequence("CTRL+a")) + self._refreshConnectionAction.triggered.connect(self._on_refresh_connection) self._refreshTabAction = QtGui.QAction(None) - self._refreshTabAction.setText("Refresh") + self._refreshTabAction.setText("Refresh Tab") self._refreshTabAction.setShortcut(QtGui.QKeySequence("CTRL+r")) self._refreshTabAction.triggered.connect(self._on_refresh) fileMenu = self._window.menuBar().addMenu("&File") - fileMenu.addAction(self._loginTabAction) + fileMenu.addAction(self._loginAction) fileMenu.addAction(self._refreshTabAction) + fileMenu.addAction(self._refreshConnectionAction) toolsMenu = self._window.menuBar().addMenu("&Tools") - toolsMenu.addAction(self._accountTabAction) - toolsMenu.addAction(self._importTabAction) + toolsMenu.addAction(self._accountAction) + toolsMenu.addAction(self._importAction) toolsMenu.addAction(self._app.aboutAction) self._initialize_tab(self._tabWidget.currentIndex()) @@ -471,6 +398,67 @@ class MainWindow(qwrappers.WindowWrapper): self._tabWidget.setCurrentIndex(tabIndex) def load_settings(self, config): + blobs = "", "" + isFullscreen = False + isPortrait = qui_utils.screen_orientation() == QtCore.Qt.Vertical + tabIndex = 0 + try: + blobs = [ + config.get(constants.__pretty_app_name__, "bin_blob_%i" % i) + for i in xrange(len(self.get_default_credentials())) + ] + isFullscreen = config.getboolean(constants.__pretty_app_name__, "fullscreen") + tabIndex = config.getint(constants.__pretty_app_name__, "tab") + isPortrait = config.getboolean(constants.__pretty_app_name__, "portrait") + 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._app.alarmHandler.load_settings(config, "alarm") + self._app.notifyOnMissed = config.getboolean("2 - Account Info", "notifyOnMissed") + self._app.notifyOnVoicemail = config.getboolean("2 - Account Info", "notifyOnVoicemail") + self._app.notifyOnSms = config.getboolean("2 - Account Info", "notifyOnSms") + self._updateVoicemailOnMissedCall = config.getboolean("2 - Account Info", "updateVoicemailOnMissedCall") + 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) + for blob in blobs + ) + self.set_default_credentials(*creds) + self._app.fullscreenAction.setChecked(isFullscreen) + self._app.orientationAction.setChecked(isPortrait) + self.set_current_tab(tabIndex) + backendId = 2 # For backwards compatibility for tabIndex, tabTitle in enumerate(self._TAB_TITLES): sectionName = "%s - %s" % (backendId, tabTitle) @@ -501,6 +489,22 @@ class MainWindow(qwrappers.WindowWrapper): self._tabsContents[tabIndex].set_settings(settings) def save_settings(self, config): + config.add_section(constants.__pretty_app_name__) + config.set(constants.__pretty_app_name__, "tab", str(self.get_current_tab())) + config.set(constants.__pretty_app_name__, "fullscreen", str(self._app.fullscreenAction.isChecked())) + config.set(constants.__pretty_app_name__, "portrait", str(self._app.orientationAction.isChecked())) + for i, value in enumerate(self.get_default_credentials()): + blob = base64.b64encode(value) + config.set(constants.__pretty_app_name__, "bin_blob_%i" % i, blob) + + config.add_section("alarm") + self._app.alarmHandler.save_settings(config, "alarm") + config.add_section("2 - Account Info") + config.set("2 - Account Info", "notifyOnMissed", repr(self._app.notifyOnMissed)) + config.set("2 - Account Info", "notifyOnVoicemail", repr(self._app.notifyOnVoicemail)) + config.set("2 - Account Info", "notifyOnSms", repr(self._app.notifyOnSms)) + config.set("2 - Account Info", "updateVoicemailOnMissedCall", repr(self._updateVoicemailOnMissedCall)) + backendId = 2 # For backwards compatibility for tabIndex, tabTitle in enumerate(self._TAB_TITLES): sectionName = "%s - %s" % (backendId, tabTitle) @@ -521,7 +525,7 @@ class MainWindow(qwrappers.WindowWrapper): if not self._tabsContents[index].has_child(): tab = self._TAB_CLASS[index](self._app, self._session, self._errorLog) self._tabsContents[index].set_child(tab) - self._tabsContents[index].refresh(force=False) + self._tabsContents[index].refresh(force=False) def _prompt_for_login(self): if self._credentialsDialog is None: @@ -540,14 +544,19 @@ 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) + + if self._callHandler is None or not self._callHandler.isSupported: + self._accountDialog.updateVMOnMissedCall = self._accountDialog.VOICEMAIL_CHECK_NOT_SUPPORTED + elif self._updateVoicemailOnMissedCall: + self._accountDialog.updateVMOnMissedCall = self._accountDialog.VOICEMAIL_CHECK_ENABLED + else: + self._accountDialog.updateVMOnMissedCall = self._accountDialog.VOICEMAIL_CHECK_DISABLED + 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() ) @@ -566,17 +575,50 @@ 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() + + if self._callHandler is None or self._accountDialog.updateVMOnMissedCall == self._accountDialog.VOICEMAIL_CHECK_DISABLEDD: + pass + elif self._accountDialog.updateVMOnMissedCall == self._accountDialog.VOICEMAIL_CHECK_ENABLED: + self._updateVoicemailOnMissedCall = True + self._callHandler.start() + else: + self._updateVoicemailOnMissedCall = False + self._callHandler.stop() + if ( + self._accountDialog.notifyOnMissed or + self._accountDialog.notifyOnVoicemail or + self._accountDialog.notifyOnSms + ): + notifications = self._accountDialog.notifications + else: + notifications = self._accountDialog.ALARM_NONE + self._app.alarmHandler.apply_settings(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: _moduleLogger.info("Unknown response") + @QtCore.pyqtSlot() + @misc_utils.log_exception(_moduleLogger) + def _on_new_message_alert(self): + with qui_utils.notify_error(self._errorLog): + 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() + @misc_utils.log_exception(_moduleLogger) + def _on_call_missed(self): + with qui_utils.notify_error(self._errorLog): + self._session.update_messages(self._session.MESSAGE_VOICEMAILS, force=True) + @QtCore.pyqtSlot(str) @misc_utils.log_exception(_moduleLogger) def _on_session_error(self, message): @@ -596,6 +638,13 @@ class MainWindow(qwrappers.WindowWrapper): for tab in self._tabsContents: tab.enable() + self._initialize_tab(self._currentTab) + if self._updateVoicemailOnMissedCall: + if self._callHandler is None: + import call_handler + self._callHandler = call_handler.MissedCallWatcher() + self._callHandler.callMissed.connect(self._voicemailRefreshDelay.start) + self._callHandler.start() @QtCore.pyqtSlot() @misc_utils.log_exception(_moduleLogger) @@ -603,6 +652,20 @@ class MainWindow(qwrappers.WindowWrapper): with qui_utils.notify_error(self._errorLog): for tab in self._tabsContents: tab.disable() + if self._callHandler is not None: + self._callHandler.stop() + + @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: + messageType = { + (True, True): self._session.MESSAGE_ALL, + (True, False): self._session.MESSAGE_TEXTS, + (False, True): self._session.MESSAGE_VOICEMAILS, + }[(self._app.notifyOnSms, self._app.notifyOnVoicemail)] + self._session.update_messages(messageType, force=True) @QtCore.pyqtSlot() @misc_utils.log_exception(_moduleLogger) @@ -635,6 +698,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) @@ -646,6 +711,13 @@ class MainWindow(qwrappers.WindowWrapper): @QtCore.pyqtSlot() @QtCore.pyqtSlot(bool) @misc_utils.log_exception(_moduleLogger) + def _on_refresh_connection(self, checked = True): + with qui_utils.notify_error(self._errorLog): + self._session.refresh_connection() + + @QtCore.pyqtSlot() + @QtCore.pyqtSlot(bool) + @misc_utils.log_exception(_moduleLogger) def _on_import(self, checked = True): with qui_utils.notify_error(self._errorLog): csvName = QtGui.QFileDialog.getOpenFileName(self._window, caption="Import", filter="CSV Files (*.csv)") @@ -662,10 +734,49 @@ class MainWindow(qwrappers.WindowWrapper): @misc_utils.log_exception(_moduleLogger) def _on_account(self, checked = True): with qui_utils.notify_error(self._errorLog): + assert self._session.state == self._session.LOGGEDIN_STATE, "Must be logged in for settings" self._show_account_dialog() def run(): + try: + os.makedirs(constants._data_path_) + except OSError, e: + if e.errno != 17: + raise + + logFormat = '(%(relativeCreated)5d) %(levelname)-5s %(threadName)s.%(name)s.%(funcName)s: %(message)s' + logging.basicConfig(level=logging.DEBUG, format=logFormat) + rotating = logging.handlers.RotatingFileHandler(constants._user_logpath_, maxBytes=512*1024, backupCount=1) + rotating.setFormatter(logging.Formatter(logFormat)) + root = logging.getLogger() + root.addHandler(rotating) + _moduleLogger.info("%s %s-%s" % (constants.__app_name__, constants.__version__, constants.__build__)) + _moduleLogger.info("OS: %s" % (os.uname()[0], )) + _moduleLogger.info("Kernel: %s (%s) for %s" % os.uname()[2:]) + _moduleLogger.info("Hostname: %s" % os.uname()[1]) + + try: + import gobject + gobject.threads_init() + except ImportError: + _moduleLogger.info("GObject support not available") + try: + import dbus + try: + from dbus.mainloop.qt import DBusQtMainLoop + DBusQtMainLoop(set_as_default=True) + _moduleLogger.info("Using Qt mainloop") + except ImportError: + try: + from dbus.mainloop.glib import DBusGMainLoop + DBusGMainLoop(set_as_default=True) + _moduleLogger.info("Using GObject mainloop") + except ImportError: + _moduleLogger.info("Mainloop not available") + except ImportError: + _moduleLogger.info("DBus support not available") + app = QtGui.QApplication([]) handle = Dialcentral(app) qtpie.init_pies() @@ -675,13 +786,5 @@ def run(): if __name__ == "__main__": import sys - logFormat = '(%(relativeCreated)5d) %(levelname)-5s %(threadName)s.%(name)s.%(funcName)s: %(message)s' - logging.basicConfig(level=logging.DEBUG, format=logFormat) - try: - os.makedirs(constants._data_path_) - except OSError, e: - if e.errno != 17: - raise - val = run() sys.exit(val)