X-Git-Url: http://git.maemo.org/git/?p=gc-dialer;a=blobdiff_plain;f=src%2Fsession.py;h=af7af7d67d2bb00d402bd3a3550319d1aa28d6d9;hp=e6cd68f0a5c5649f9cbefcbd48bed54890067c78;hb=110a5c76f83613ab5f980a83763cf23034086fcd;hpb=233c6c9b5fc53935cac2319f68a1ea052a39d52c diff --git a/src/session.py b/src/session.py index e6cd68f..af7af7d 100644 --- a/src/session.py +++ b/src/session.py @@ -12,7 +12,8 @@ try: except ImportError: import pickle -from PyQt4 import QtCore +import util.qt_compat as qt_compat +QtCore = qt_compat.QtCore from util import qore_utils from util import qui_utils @@ -27,7 +28,8 @@ _moduleLogger = logging.getLogger(__name__) class _DraftContact(object): - def __init__(self, title, description, numbersWithDescriptions): + def __init__(self, messageId, title, description, numbersWithDescriptions): + self.messageId = messageId self.title = title self.description = description self.numbers = numbersWithDescriptions @@ -36,21 +38,21 @@ class _DraftContact(object): class Draft(QtCore.QObject): - sendingMessage = QtCore.pyqtSignal() - sentMessage = QtCore.pyqtSignal() - calling = QtCore.pyqtSignal() - called = QtCore.pyqtSignal() - cancelling = QtCore.pyqtSignal() - cancelled = QtCore.pyqtSignal() - error = QtCore.pyqtSignal(str) + sendingMessage = qt_compat.Signal() + sentMessage = qt_compat.Signal() + calling = qt_compat.Signal() + called = qt_compat.Signal() + cancelling = qt_compat.Signal() + cancelled = qt_compat.Signal() + error = qt_compat.Signal(str) - recipientsChanged = QtCore.pyqtSignal() + recipientsChanged = qt_compat.Signal() - def __init__(self, pool, backend, errorLog): + def __init__(self, asyncQueue, backend, errorLog): QtCore.QObject.__init__(self) self._errorLog = errorLog self._contacts = {} - self._pool = pool + self._asyncQueue = asyncQueue self._backend = backend self._busyReason = None self._message = "" @@ -59,7 +61,7 @@ class Draft(QtCore.QObject): assert 0 < len(self._contacts), "No contacts selected" assert 0 < len(self._message), "No message to send" numbers = [misc_utils.make_ugly(contact.selectedNumber) for contact in self._contacts.itervalues()] - le = concurrent.AsyncLinearExecution(self._pool, self._send) + le = self._asyncQueue.add_async(self._send) le.start(numbers, self._message) def call(self): @@ -67,11 +69,11 @@ class Draft(QtCore.QObject): assert len(self._message) == 0, "Cannot send message with call" (contact, ) = self._contacts.itervalues() number = misc_utils.make_ugly(contact.selectedNumber) - le = concurrent.AsyncLinearExecution(self._pool, self._call) + le = self._asyncQueue.add_async(self._call) le.start(number) def cancel(self): - le = concurrent.AsyncLinearExecution(self._pool, self._cancel) + le = self._asyncQueue.add_async(self._cancel) le.start() def _get_message(self): @@ -82,11 +84,11 @@ class Draft(QtCore.QObject): message = property(_get_message, _set_message) - def add_contact(self, contactId, title, description, numbersWithDescriptions): + def add_contact(self, contactId, messageId, title, description, numbersWithDescriptions): if self._busyReason is not None: raise RuntimeError("Please wait for %r" % self._busyReason) # Allow overwriting of contacts so that the message can be updated and the SMS dialog popped back up - contactDetails = _DraftContact(title, description, numbersWithDescriptions) + contactDetails = _DraftContact(messageId, title, description, numbersWithDescriptions) self._contacts[contactId] = contactDetails self.recipientsChanged.emit() @@ -103,6 +105,9 @@ class Draft(QtCore.QObject): def get_num_contacts(self): return len(self._contacts) + def get_message_id(self, cid): + return self._contacts[cid].messageId + def get_title(self, cid): return self._contacts[cid].title @@ -194,38 +199,55 @@ class Session(QtCore.QObject): # @todo Somehow add support for csv contacts - stateChange = QtCore.pyqtSignal(str) - loggedOut = QtCore.pyqtSignal() - loggedIn = QtCore.pyqtSignal() - callbackNumberChanged = QtCore.pyqtSignal(str) + stateChange = qt_compat.Signal(str) + loggedOut = qt_compat.Signal() + loggedIn = qt_compat.Signal() + callbackNumberChanged = qt_compat.Signal(str) - accountUpdated = QtCore.pyqtSignal() - messagesUpdated = QtCore.pyqtSignal() - newMessages = QtCore.pyqtSignal() - historyUpdated = QtCore.pyqtSignal() - dndStateChange = QtCore.pyqtSignal(bool) + accountUpdated = qt_compat.Signal() + messagesUpdated = qt_compat.Signal() + newMessages = qt_compat.Signal() + historyUpdated = qt_compat.Signal() + dndStateChange = qt_compat.Signal(bool) + voicemailAvailable = qt_compat.Signal(str, str) - error = QtCore.pyqtSignal(str) + error = qt_compat.Signal(str) LOGGEDOUT_STATE = "logged out" LOGGINGIN_STATE = "logging in" LOGGEDIN_STATE = "logged in" + MESSAGE_TEXTS = "Text" + MESSAGE_VOICEMAILS = "Voicemail" + MESSAGE_ALL = "All" + + HISTORY_RECEIVED = "Received" + HISTORY_MISSED = "Missed" + HISTORY_PLACED = "Placed" + HISTORY_ALL = "All" + _OLDEST_COMPATIBLE_FORMAT_VERSION = misc_utils.parse_version("1.3.0") _LOGGEDOUT_TIME = -1 _LOGGINGIN_TIME = 0 - def __init__(self, errorLog, cachePath = None): + def __init__(self, errorLog, cachePath): QtCore.QObject.__init__(self) self._errorLog = errorLog - self._pool = qore_utils.AsyncPool() + self._pool = qore_utils.FutureThread() + self._asyncQueue = concurrent.AsyncTaskQueue(self._pool) self._backend = [] self._loggedInTime = self._LOGGEDOUT_TIME self._loginOps = [] self._cachePath = cachePath + self._voicemailCachePath = None self._username = None - self._draft = Draft(self._pool, self._backend, self._errorLog) + self._password = None + self._draft = Draft(self._asyncQueue, self._backend, self._errorLog) + self._delayedRelogin = QtCore.QTimer() + self._delayedRelogin.setInterval(0) + self._delayedRelogin.setSingleShot(True) + self._delayedRelogin.timeout.connect(self._on_delayed_relogin) self._contacts = {} self._accountUpdateTime = datetime.datetime(1971, 1, 1) @@ -262,7 +284,7 @@ class Session(QtCore.QObject): self._backend[0:0] = [gv_backend.GVDialer(cookiePath)] self._pool.start() - le = concurrent.AsyncLinearExecution(self._pool, self._login) + le = self._asyncQueue.add_async(self._login) le.start(username, password) def logout(self): @@ -272,6 +294,7 @@ class Session(QtCore.QObject): self._loggedInTime = self._LOGGEDOUT_TIME self._backend[0].persist() self._save_to_cache() + self._clear_voicemail_cache() self.stateChange.emit(self.LOGGEDOUT_STATE) self.loggedOut.emit() @@ -294,19 +317,23 @@ class Session(QtCore.QObject): def update_account(self, force = True): if not force and self._contacts: return - le = concurrent.AsyncLinearExecution(self._pool, self._update_account) + le = self._asyncQueue.add_async(self._update_account), (), {} self._perform_op_while_loggedin(le) + def refresh_connection(self): + le = self._asyncQueue.add_async(self._refresh_authentication) + le.start() + def get_contacts(self): return self._contacts def get_when_contacts_updated(self): return self._accountUpdateTime - def update_messages(self, force = True): + def update_messages(self, messageType, force = True): if not force and self._messages: return - le = concurrent.AsyncLinearExecution(self._pool, self._update_messages) + le = self._asyncQueue.add_async(self._update_messages), (messageType, ), {} self._perform_op_while_loggedin(le) def get_messages(self): @@ -315,10 +342,10 @@ class Session(QtCore.QObject): def get_when_messages_updated(self): return self._messageUpdateTime - def update_history(self, force = True): + def update_history(self, historyType, force = True): if not force and self._history: return - le = concurrent.AsyncLinearExecution(self._pool, self._update_history) + le = self._asyncQueue.add_async(self._update_history), (historyType, ), {} self._perform_op_while_loggedin(le) def get_history(self): @@ -328,13 +355,27 @@ class Session(QtCore.QObject): return self._historyUpdateTime def update_dnd(self): - le = concurrent.AsyncLinearExecution(self._pool, self._update_dnd) + le = self._asyncQueue.add_async(self._update_dnd), (), {} self._perform_op_while_loggedin(le) def set_dnd(self, dnd): - le = concurrent.AsyncLinearExecution(self._pool, self._set_dnd) + le = self._asyncQueue.add_async(self._set_dnd) le.start(dnd) + def is_available(self, messageId): + actualPath = os.path.join(self._voicemailCachePath, "%s.mp3" % messageId) + return os.path.exists(actualPath) + + def voicemail_path(self, messageId): + actualPath = os.path.join(self._voicemailCachePath, "%s.mp3" % messageId) + if not os.path.exists(actualPath): + raise RuntimeError("Voicemail not available") + return actualPath + + def download_voicemail(self, messageId): + le = self._asyncQueue.add_async(self._download_voicemail) + le.start(messageId) + def _set_dnd(self, dnd): oldDnd = self._dnd try: @@ -370,7 +411,7 @@ class Session(QtCore.QObject): return self._callback def set_callback_number(self, callback): - le = concurrent.AsyncLinearExecution(self._pool, self._set_callback_number) + le = self._asyncQueue.add_async(self._set_callback_number) le.start(callback) def _set_callback_number(self, callback): @@ -426,12 +467,20 @@ class Session(QtCore.QObject): self._loggedInTime = int(time.time()) oldUsername = self._username self._username = username + self._password = password finalState = self.LOGGEDIN_STATE if oldUsername != self._username: needOps = not self._load() else: needOps = True + self._voicemailCachePath = os.path.join(self._cachePath, "%s.voicemail.cache" % self._username) + try: + os.makedirs(self._voicemailCachePath) + except OSError, e: + if e.errno != 17: + raise + self.loggedIn.emit() self.stateChange.emit(finalState) finalState = None # Mark it as already set @@ -442,8 +491,8 @@ class Session(QtCore.QObject): else: loginOps = [] del self._loginOps[:] - for asyncOp in loginOps: - asyncOp.start() + for asyncOp, args, kwds in loginOps: + asyncOp.start(*args, **kwds) else: self._loggedInTime = self._LOGGEDOUT_TIME self.error.emit("Error logging in") @@ -458,6 +507,43 @@ class Session(QtCore.QObject): if accountData is not None and self._callback: self.set_callback_number(self._callback) + def _update_account(self): + try: + with qui_utils.notify_busy(self._errorLog, "Updating Account"): + accountData = yield ( + self._backend[0].refresh_account_info, + (), + {}, + ) + except Exception, e: + _moduleLogger.exception("Reporting error to user") + self.error.emit(str(e)) + return + self._loggedInTime = int(time.time()) + self._process_account_data(accountData) + + def _refresh_authentication(self): + try: + with qui_utils.notify_busy(self._errorLog, "Updating Account"): + accountData = yield ( + self._backend[0].refresh_account_info, + (), + {}, + ) + accountData = None + except Exception, e: + _moduleLogger.exception("Passing to user") + self.error.emit(str(e)) + # refresh_account_info does not normally throw, so it is fine if we + # just quit early because something seriously wrong is going on + return + + if accountData is not None: + self._loggedInTime = int(time.time()) + self._process_account_data(accountData) + else: + self._delayedRelogin.start() + def _load(self): updateMessages = len(self._messages) != 0 updateHistory = len(self._history) != 0 @@ -587,29 +673,19 @@ class Session(QtCore.QObject): self.callbackNumberChanged.emit(self._callback) self._save_to_cache() + self._clear_voicemail_cache() - def _update_account(self): - try: - assert self.state == self.LOGGEDIN_STATE, "Contacts requires being logged in (currently %s)" % self.state - with qui_utils.notify_busy(self._errorLog, "Updating Account"): - accountData = yield ( - self._backend[0].refresh_account_info, - (), - {}, - ) - except Exception, e: - _moduleLogger.exception("Reporting error to user") - self.error.emit(str(e)) - return - self._process_account_data(accountData) + def _clear_voicemail_cache(self): + import shutil + shutil.rmtree(self._voicemailCachePath, True) - def _update_messages(self): + def _update_messages(self, messageType): try: assert self.state == self.LOGGEDIN_STATE, "Messages requires being logged in (currently %s" % self.state - with qui_utils.notify_busy(self._errorLog, "Updating Messages"): + with qui_utils.notify_busy(self._errorLog, "Updating %s Messages" % messageType): self._messages = yield ( self._backend[0].get_messages, - (), + (messageType, ), {}, ) except Exception, e: @@ -620,13 +696,13 @@ class Session(QtCore.QObject): self.messagesUpdated.emit() self._alert_on_messages(self._messages) - def _update_history(self): + def _update_history(self, historyType): try: assert self.state == self.LOGGEDIN_STATE, "History requires being logged in (currently %s" % self.state - with qui_utils.notify_busy(self._errorLog, "Updating History"): + with qui_utils.notify_busy(self._errorLog, "Updating '%s' History" % historyType): self._history = yield ( - self._backend[0].get_recent, - (), + self._backend[0].get_call_history, + (historyType, ), {}, ) except Exception, e: @@ -637,24 +713,55 @@ class Session(QtCore.QObject): self.historyUpdated.emit() def _update_dnd(self): - oldDnd = self._dnd - try: - assert self.state == self.LOGGEDIN_STATE, "DND requires being logged in (currently %s" % self.state - self._dnd = yield ( - self._backend[0].is_dnd, - (), - {}, - ) - except Exception, e: - _moduleLogger.exception("Reporting error to user") - self.error.emit(str(e)) + with qui_utils.notify_busy(self._errorLog, "Updating Do-Not-Disturb Status"): + oldDnd = self._dnd + try: + assert self.state == self.LOGGEDIN_STATE, "DND requires being logged in (currently %s" % self.state + self._dnd = yield ( + self._backend[0].is_dnd, + (), + {}, + ) + except Exception, e: + _moduleLogger.exception("Reporting error to user") + self.error.emit(str(e)) + return + if oldDnd != self._dnd: + self.dndStateChange(self._dnd) + + def _download_voicemail(self, messageId): + actualPath = os.path.join(self._voicemailCachePath, "%s.mp3" % messageId) + targetPath = "%s.%s.part" % (actualPath, time.time()) + if os.path.exists(actualPath): + self.voicemailAvailable.emit(messageId, actualPath) return - if oldDnd != self._dnd: - self.dndStateChange(self._dnd) + with qui_utils.notify_busy(self._errorLog, "Downloading Voicemail"): + try: + yield ( + self._backend[0].download, + (messageId, targetPath), + {}, + ) + except Exception, e: + _moduleLogger.exception("Passing to user") + self.error.emit(str(e)) + return + + if os.path.exists(actualPath): + try: + os.remove(targetPath) + except: + _moduleLogger.exception("Ignoring file problems with cache") + self.voicemailAvailable.emit(messageId, actualPath) + return + else: + os.rename(targetPath, actualPath) + self.voicemailAvailable.emit(messageId, actualPath) def _perform_op_while_loggedin(self, op): if self.state == self.LOGGEDIN_STATE: - op.start() + op, args, kwds = op + op.start(*args, **kwds) else: self._push_login_op(op) @@ -708,3 +815,15 @@ class Session(QtCore.QObject): continue yield cleaned + + @misc_utils.log_exception(_moduleLogger) + def _on_delayed_relogin(self): + try: + username = self._username + password = self._password + self.logout() + self.login(username, password) + except Exception, e: + _moduleLogger.exception("Passing to user") + self.error.emit(str(e)) + return