Upon startup, starting geometric polling at the max rather than the min
[theonering] / src / connection.py
index d1ca887..798563d 100644 (file)
@@ -1,15 +1,8 @@
-
-"""
-@todo Add params for different state machines update times
-@todo Get a callback for missed calls to force an update of the voicemail state machine
-@todo Get a callback on an incoming call and if its from GV, auto-pickup
-"""
-
-
 import os
 import weakref
 import logging
 
+import gobject
 import telepathy
 
 try:
@@ -21,6 +14,7 @@ except (ImportError, OSError):
 import constants
 import tp
 import util.coroutines as coroutines
+import util.go_utils as gobject_utils
 import util.misc as util_misc
 import gtk_toolbox
 
@@ -40,6 +34,28 @@ import channel_manager
 _moduleLogger = logging.getLogger("connection")
 
 
+class TheOneRingOptions(object):
+
+       useGVContacts = True
+
+       assert gvoice.session.Session._DEFAULTS["contacts"][1] == "hours"
+       contactsPollPeriodInHours = gvoice.session.Session._DEFAULTS["contacts"][0]
+
+       assert gvoice.session.Session._DEFAULTS["voicemail"][1] == "minutes"
+       voicemailPollPeriodInMinutes = gvoice.session.Session._DEFAULTS["voicemail"][0]
+
+       assert gvoice.session.Session._DEFAULTS["texts"][1] == "minutes"
+       textsPollPeriodInMinutes = gvoice.session.Session._DEFAULTS["texts"][0]
+
+       def __init__(self, parameters = None):
+               if parameters is None:
+                       return
+               self.useGVContacts = parameters["use-gv-contacts"]
+               self.contactsPollPeriodInHours = parameters['contacts-poll-period-in-hours']
+               self.voicemailPollPeriodInMinutes = parameters['voicemail-poll-period-in-minutes']
+               self.textsPollPeriodInMinutes = parameters['texts-poll-period-in-minutes']
+
+
 class TheOneRingConnection(
        tp.Connection,
        requests.RequestsMixin,
@@ -52,15 +68,23 @@ class TheOneRingConnection(
 
        # overiding base class variable
        _mandatory_parameters = {
-               'account' : 's',
-               'password' : 's',
+               'account': 's',
+               'password': 's',
        }
        # overiding base class variable
        _optional_parameters = {
-               'forward' : 's',
+               'forward': 's',
+               'use-gv-contacts': 'b',
+               'contacts-poll-period-in-hours': 'i',
+               'voicemail-poll-period-in-minutes': 'i',
+               'texts-poll-period-in-minutes': 'i',
        }
        _parameter_defaults = {
-               'forward' : '',
+               'forward': '',
+               'use-gv-contacts': TheOneRingOptions.useGVContacts,
+               'contacts-poll-period-in-hours': TheOneRingOptions.contactsPollPeriodInHours,
+               'voicemail-poll-period-in-minutes': TheOneRingOptions.voicemailPollPeriodInMinutes,
+               'texts-poll-period-in-minutes': TheOneRingOptions.textsPollPeriodInMinutes,
        }
        _secret_parameters = set((
                "password",
@@ -78,6 +102,7 @@ class TheOneRingConnection(
 
                # Connection init must come first
                self.__session = gvoice.session.Session(None)
+               self.__options = TheOneRingOptions(parameters)
                tp.Connection.__init__(
                        self,
                        constants._telepathy_protocol_name_,
@@ -101,10 +126,8 @@ class TheOneRingConnection(
 
                if conic is not None:
                        self.__connection = conic.Connection()
-                       self.__connectionEventId = None
                else:
                        self.__connection = None
-                       self.__connectionEventId = None
                self.__cachePath = os.sep.join((constants._data_path_, "cache", self.username))
                try:
                        os.makedirs(self.__cachePath)
@@ -115,6 +138,8 @@ class TheOneRingConnection(
                self.set_self_handle(handle.create_handle(self, 'connection'))
 
                self.__callback = None
+               self.__connectionEventId = None
+               self.__delayedDisconnectEventId = None
                _moduleLogger.info("Connection to the account %s created" % account)
 
        @property
@@ -126,6 +151,10 @@ class TheOneRingConnection(
                return self.__session
 
        @property
+       def options(self):
+               return self.__options
+
+       @property
        def username(self):
                return self.__credentials[0]
 
@@ -136,11 +165,11 @@ class TheOneRingConnection(
        def get_handle_by_name(self, handleType, handleName):
                requestedHandleName = handleName.encode('utf-8')
                if handleType == telepathy.HANDLE_TYPE_CONTACT:
-                       _moduleLogger.info("get_handle_by_name Contact: %s" % requestedHandleName)
+                       _moduleLogger.debug("get_handle_by_name Contact: %s" % requestedHandleName)
                        h = handle.create_handle(self, 'contact', requestedHandleName)
                elif handleType == telepathy.HANDLE_TYPE_LIST:
                        # Support only server side (immutable) lists
-                       _moduleLogger.info("get_handle_by_name List: %s" % requestedHandleName)
+                       _moduleLogger.debug("get_handle_by_name List: %s" % requestedHandleName)
                        h = handle.create_handle(self, 'list', requestedHandleName)
                else:
                        raise telepathy.errors.NotAvailable('Handle type unsupported %d' % handleType)
@@ -216,14 +245,14 @@ class TheOneRingConnection(
                """
                For org.freedesktop.telepathy.Connection
                """
-               self.StatusChanged(
-                       telepathy.CONNECTION_STATUS_DISCONNECTED,
-                       telepathy.CONNECTION_STATUS_REASON_REQUESTED
-               )
                try:
                        self._disconnect()
                except Exception:
                        _moduleLogger.exception("Error durring disconnect")
+               self.StatusChanged(
+                       telepathy.CONNECTION_STATUS_DISCONNECTED,
+                       telepathy.CONNECTION_STATUS_REASON_REQUESTED
+               )
 
        @gtk_toolbox.log_exception(_moduleLogger)
        def RequestChannel(self, type, handleType, handleId, suppressHandler):
@@ -276,12 +305,11 @@ class TheOneRingConnection(
                self.session.save(self.__cachePath)
                self.session.logout()
                self.session.close()
-               self.__session = None
-               if self.__connection is not None:
-                       self.__connection.disconnect(self.__connectionEventId)
-                       self.__connectionEventId = None
 
                self.manager.disconnected(self)
+
+               self._cancel_delayed_disconnect()
+               self.__connection = None
                _moduleLogger.info("Disconnected")
 
        @gtk_toolbox.log_exception(_moduleLogger)
@@ -291,14 +319,26 @@ class TheOneRingConnection(
                        h = self.get_handle_by_name(telepathy.HANDLE_TYPE_CONTACT, phoneNumber)
                        # Just let the TextChannel decide whether it should be reported to the user or not
                        props = self._generate_props(telepathy.CHANNEL_TYPE_TEXT, h, False)
+                       if self.__channelManager.channel_exists(props):
+                               continue
+
+                       # Maemo 4.1's RTComm opens a window for a chat regardless if a
+                       # message is received or not, so we need to do some filtering here
+                       mergedConv = conv.get_conversation(phoneNumber)
+                       unreadConvs = [
+                               conversation
+                               for conversation in mergedConv.conversations
+                               if not conversation.isRead and not conversation.isArchived
+                       ]
+                       if not unreadConvs:
+                               continue
+
                        chan = self.__channelManager.channel_for_props(props, signal=True)
 
        @gtk_toolbox.log_exception(_moduleLogger)
        def _on_connection_change(self, connection, event):
                """
                @note Maemo specific
-
-               @todo Make this delayed to handle background switching of networks.  First I need to verify I receive connected
                """
                status = event.get_status()
                error = event.get_error()
@@ -306,14 +346,35 @@ class TheOneRingConnection(
                bearer = event.get_bearer_type()
 
                if status == conic.STATUS_DISCONNECTED:
-                       _moduleLogger.info("Disconnecting due to loss of network connection")
-                       self.StatusChanged(
-                               telepathy.CONNECTION_STATUS_DISCONNECTED,
-                               telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR
+                       _moduleLogger.info("Disconnected from network, starting countdown to logoff")
+                       self.__delayedDisconnectEventId = gobject_utils.timeout_add_seconds(
+                               5, self._on_delayed_disconnect
                        )
-                       try:
-                               self._disconnect()
-                       except Exception:
-                               _moduleLogger.exception("Error durring disconnect")
+               elif status == conic.STATUS_CONNECTED:
+                       _moduleLogger.info("Connected to network")
+                       self._cancel_delayed_disconnect()
                else:
                        _moduleLogger.info("Other status: %r" % (status, ))
+
+       def _cancel_delayed_disconnect(self):
+               if self.__delayedDisconnectEventId is None:
+                       return
+               _moduleLogger.info("Cancelling auto-log off")
+               gobject.source_reove(self.__delayedDisconnectEventId)
+               self.__delayedDisconnectEventId = None
+
+       @gtk_toolbox.log_exception(_moduleLogger)
+       def _on_delayed_disconnect(self):
+               if not self.session.is_logged_in():
+                       _moduleLogger.info("Received connection change event when not logged in")
+                       return
+               try:
+                       self._disconnect()
+               except Exception:
+                       _moduleLogger.exception("Error durring disconnect")
+               self.StatusChanged(
+                       telepathy.CONNECTION_STATUS_DISCONNECTED,
+                       telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR
+               )
+               self.__delayedDisconnectEventId = None
+               return False