Continued work on getting contacts ready to use
authorEd Page <eopage@byu.net>
Wed, 30 Sep 2009 02:59:58 +0000 (21:59 -0500)
committerEd Page <eopage@byu.net>
Wed, 30 Sep 2009 02:59:58 +0000 (21:59 -0500)
src/channel/contact_list.py
src/connection.py
src/gvoice/addressbook.py
src/handle.py

index 0d20dc0..3fbd722 100644 (file)
@@ -3,28 +3,14 @@ import logging
 
 import telepathy
 
+import util.go_utils as gobject_utils
+import util.coroutines as coroutines
 import handle
 
 
 _moduleLogger = logging.getLogger("channel.contact_list")
 
 
-def create_contact_list_channel(connection, h):
-       if h.get_name() == 'subscribe':
-               channel_class = SubscribeListChannel
-       elif h.get_name() == 'publish':
-               channel_class = PublishListChannel
-       elif h.get_name() == 'hide':
-               _moduleLogger.warn("Unsuported type %s" % h.get_name())
-       elif h.get_name() == 'allow':
-               _moduleLogger.warn("Unsuported type %s" % h.get_name())
-       elif h.get_name() == 'deny':
-               _moduleLogger.warn("Unsuported type %s" % h.get_name())
-       else:
-               raise TypeError("Unknown list type : " + h.get_name())
-       return channel_class(connection, h)
-
-
 class AbstractListChannel(
                telepathy.server.ChannelTypeContactList,
                telepathy.server.ChannelInterfaceGroup,
@@ -36,141 +22,71 @@ class AbstractListChannel(
                telepathy.server.ChannelInterfaceGroup.__init__(self)
 
                self._conn_ref = weakref.ref(connection)
+               self._addessbook = connection.addressbook
 
-       def GetLocalPendingMembersWithInfo(self):
-               return []
 
-
-class SubscribeListChannel(AbstractListChannel):
+class AllContactsListChannel(AbstractListChannel):
        """
-       Subscribe List channel.
-
-       This channel contains the list of contact to whom the current used is
-       'subscribed', basically this list contains the contact for whom you are
-       supposed to receive presence notification.
+       The group of contacts for whom you receive presence
        """
 
        def __init__(self, connection, h):
                AbstractListChannel.__init__(self, connection, h)
-               self.GroupFlagsChanged(
-                       telepathy.CHANNEL_GROUP_FLAG_CAN_ADD |
-                       telepathy.CHANNEL_GROUP_FLAG_CAN_REMOVE,
-                       0,
+               self._addressbook.updateSignalHandle.register_sink(
+                       self._on_contacts_refreshed
                )
 
-       def AddMembers(self, contacts, message):
-               addressBook = self._conn.gvoice_client
-               for h in contacts:
-                       h = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, h)
-                       contact = h.contact
-                       if contact is None:
-                               account = h.account
-                       else:
-                               account = contact.account
-                       groups = list(h.pending_groups)
-                       h.pending_groups = set()
-                       addressBook.add_messenger_contact(account,
-                                       invite_message=message.encode('utf-8'),
-                                       groups=groups)
-
-       def RemoveMembers(self, contacts, message):
-               addressBook = self._conn.gvoice_client
-               for h in contacts:
-                       h = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, h)
-                       contact = h.contact
-                       addressBook.delete_contact(contact)
-
-
-class PublishListChannel(AbstractListChannel):
+       @coroutines.func_sink
+       @coroutines.expand_positional
+       @gobject_utils.async
+       def _on_contacts_refreshed(self, addressbook, added, removed, changed):
+               connection = self._conn_ref()
+               handlesAdded = [
+                       handle.create_handle(connection, "contact", contactId)
+                       for contactId in added
+               ]
+               handlesRemoved = [
+                       handle.create_handle(connection, "contact", contactId)
+                       for contactId in removed
+               ]
+               message = ""
+               actor = 0
+               reason = telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE
+               self.MembersChanged(
+                       message,
+                       handlesAdded, handlesRemoved,
+                       (), (),
+                       actor,
+                       reason,
+               )
 
-       def __init__(self, connection, h):
-               AbstractListChannel.__init__(self, connection, h)
-               self.GroupFlagsChanged(0, 0)
-
-       def AddMembers(self, contacts, message):
-               addressBook = self._conn.gvoice_client
-               for contactHandleId in contacts:
-                       contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT,
-                                               contactHandleId)
-                       contact = contactHandle.contact
-                       addressBook.accept_contact_invitation(contact, False)
-
-       def RemoveMembers(self, contacts, message):
-               addressBook = self._conn.gvoice_client
-               for contactHandleId in contacts:
-                       contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT,
-                                               contactHandleId)
-                       contact = contactHandle.contact
-
-       def GetLocalPendingMembersWithInfo(self):
-               addressBook = self._conn.gvoice_client
-               result = []
-               for contact in addressBook.contacts:
-                       h = handle.create_handle(self._conn_ref(), 'contact',
-                                       contact.account, contact.network_id)
-                       result.append((h, h,
-                                       telepathy.CHANNEL_GROUP_CHANGE_REASON_INVITED,
-                                       contact.attributes.get('invite_message', '')))
-               return result
-
-
-class GroupChannel(AbstractListChannel):
 
-       def __init__(self, connection, h):
-               self.__pending_add = []
-               self.__pending_remove = []
-               AbstractListChannel.__init__(self, connection, h)
-               self.GroupFlagsChanged(
-                       telepathy.CHANNEL_GROUP_FLAG_CAN_ADD | telepathy.CHANNEL_GROUP_FLAG_CAN_REMOVE,
-                       0,
-               )
+def create_contact_list_channel(connection, h):
+       if h.get_name() == 'subscribe':
+               # The group of contacts for whom you receive presence
+               ChannelClass = AllContactsListChannel
+       elif h.get_name() == 'publish':
+               # The group of contacts who may receive your presence
+               ChannelClass = AllContactsListChannel
+       elif h.get_name() == 'hide':
+               # A group of contacts who are on the publish list but are temporarily
+               # disallowed from receiving your presence
+               # This doesn't make sense to support
+               _moduleLogger.warn("Unsuported type %s" % h.get_name())
+       elif h.get_name() == 'allow':
+               # A group of contacts who may send you messages
+               # @todo This would be cool to support
+               _moduleLogger.warn("Unsuported type %s" % h.get_name())
+       elif h.get_name() == 'deny':
+               # A group of contacts who may not send you messages
+               # @todo This would be cool to support
+               _moduleLogger.warn("Unsuported type %s" % h.get_name())
+       elif h.get_name() == 'stored':
+               # On protocols where the user's contacts are stored, this contact list
+               # contains all stored contacts regardless of subscription status.
+               ChannelClass = AllContactsListChannel
+       else:
+               raise TypeError("Unknown list type : " + h.get_name())
+       return ChannelClass(connection, h)
+
 
-       def AddMembers(self, contacts, message):
-               addressBook = self._conn.gvoice_client
-               if self._handle.group is None:
-                       for contactHandleId in contacts:
-                               contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, contactHandleId)
-                               _moduleLogger.info("Adding contact %r to pending group %r" % (contactHandle, self._handle))
-                               if contactHandleId in self.__pending_remove:
-                                       self.__pending_remove.remove(contactHandleId)
-                               else:
-                                       self.__pending_add.append(contactHandleId)
-                       return
-               else:
-                       for contactHandleId in contacts:
-                               contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, contactHandleId)
-                               _moduleLogger.info("Adding contact %r to group %r" % (contactHandle, self._handle))
-                               contact = contactHandle.contact
-                               group = self._handle.group
-                               if contact is not None:
-                                       addressBook.add_contact_to_group(group, contact)
-                               else:
-                                       contactHandle.pending_groups.add(group)
-
-       def RemoveMembers(self, contacts, message):
-               addressBook = self._conn.gvoice_client
-               if self._handle.group is None:
-                       for contactHandleId in contacts:
-                               contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, contactHandleId)
-                               _moduleLogger.info("Adding contact %r to pending group %r" % (contactHandle, self._handle))
-                               if contactHandleId in self.__pending_add:
-                                       self.__pending_add.remove(contactHandleId)
-                               else:
-                                       self.__pending_remove.append(contactHandleId)
-                       return
-               else:
-                       for contactHandleId in contacts:
-                               contactHandle = self._conn.handle(telepathy.HANDLE_TYPE_CONTACT, contactHandleId)
-                               _moduleLogger.info("Removing contact %r from pending group %r" % (contactHandle, self._handle))
-                               contact = contactHandle.contact
-                               group = self._handle.group
-                               if contact is not None:
-                                       addressBook.delete_contact_from_group(group, contact)
-                               else:
-                                       contactHandle.pending_groups.discard(group)
-
-       def Close(self):
-               _moduleLogger.debug("Deleting group %s" % self._handle.name)
-               addressBook = self._conn.gvoice_client
-               group = self._handle.group
-               addressBook.delete_group(group)
index da96e44..2fd3632 100644 (file)
@@ -47,6 +47,7 @@ class TheOneRingConnection(telepathy.server.Connection, simple_presence.SimplePr
 
                        cookieFilePath = "%s/cookies.txt" % constants._data_path_
                        self._backend = gvoice.dialer.GVDialer(cookieFilePath)
+                       self._addressbook = gvoice.addressbook.Addressbook(self._backend)
 
                        self.set_self_handle(handle.create_handle(self, 'connection'))
 
@@ -64,6 +65,10 @@ class TheOneRingConnection(telepathy.server.Connection, simple_presence.SimplePr
                return self._backend
 
        @property
+       def addressbook(self):
+               return self._addressbook
+
+       @property
        def username(self):
                self._credentials[0]
 
@@ -155,9 +160,8 @@ class TheOneRingConnection(telepathy.server.Connection, simple_presence.SimplePr
                        if handleType == telepathy.HANDLE_TYPE_CONTACT:
                                h = self._create_contact_handle(name)
                        elif handleType == telepathy.HANDLE_TYPE_LIST:
+                               # Support only server side (immutable) lists
                                h = handle.create_handle(self, 'list', name)
-                       elif handleType == telepathy.HANDLE_TYPE_GROUP:
-                               h = handle.create_handle(self, 'group', name)
                        else:
                                raise telepathy.NotAvailable('Handle type unsupported %d' % handleType)
                        handles.append(h.id)
@@ -167,16 +171,16 @@ class TheOneRingConnection(telepathy.server.Connection, simple_presence.SimplePr
        def _create_contact_handle(self, name):
                requestedContactId = name
 
-               contacts = self._backend.get_contacts()
+               contacts = self._addressbook.get_contacts()
                contactsFound = [
-                       (contactId, contactName) for (contactId, contactName) in contacts
+                       contactId for contactId in contacts
                        if contactId == requestedContactId
                ]
 
                if 0 < len(contactsFound):
-                       contactId, contactName = contactsFound[0]
+                       contactId = contactsFound[0]
                        if len(contactsFound) != 1:
-                               _moduleLogger.error("Contact ID was not unique: %s for %s" % (contactId, contactName))
+                               _moduleLogger.error("Contact ID was not unique: %s for %s" % (contactId, ))
                else:
-                       contactId, contactName = requestedContactId, ""
-               h = handle.create_handle(self, 'contact', contactId, contactName)
+                       contactId = requestedContactId
+               h = handle.create_handle(self, 'contact', contactId)
index e796583..aeb8de5 100644 (file)
@@ -3,6 +3,8 @@
 
 import logging
 
+import util.coroutines as coroutines
+
 
 _moduleLogger = logging.getLogger("gvoice.addressbook")
 
@@ -12,27 +14,65 @@ class Addressbook(object):
        def __init__(self, backend):
                self._backend = backend
                self._contacts = {}
+               self._addedContacts = set()
+               self._removedContacts = set()
+               self._changedContacts = set()
 
-       def clear_cache(self):
-               self._contacts.clear()
+               self.updateSignalHandler = coroutines.CoTee()
 
-       def get_contacts(self):
+       def update(self):
+               oldContacts = self._contacts
+               oldContactIds = set(self.get_contacts())
+
+               self._contacts = {}
                self._populate_contacts()
+               newContactIds = set(self.get_contacts())
+
+               self._addedContacts = newContactIds - oldContactIds
+               self._removedContacts = oldContactIds - newContactIds
+               self._changedContacts = set(
+                       contactId
+                       for contactId in newContactIds.intersection(oldContactIds)
+                       if self._has_contact_changed(contactId, oldContacts)
+               )
+
+               message = self, self._addedContacts, self._removedContacts, self._changedContacts
+               self.updateSignalHandler.send(message)
+
+       def get_contacts(self):
                return self._contacts.iterkeys()
 
+       def get_contact_name(self, contactId):
+               return self._contacts[contactId][0]
+
        def get_contact_details(self, contactId):
-               self._populate_contacts()
                self._populate_contact_details(contactId)
-               return self._contacts[contactId]
+               return self._get_contact_details(contactId)
 
        def _populate_contacts(self):
                if self._contacts:
                        return
                contacts = self._backend.get_contacts()
                for contactId, contactName in contacts:
-                       self._contacts[contactId] = None
+                       self._contacts[contactId] = (contactName, {})
 
        def _populate_contact_details(self, contactId):
-               if self._contacts[contactId] is not None:
+               if self._get_contact_details(contactId):
                        return
-               self._contacts[contactId] = self._backend.get_contact_details(contactId)
+               self._get_contact_details(contactId).update(
+                       self._backend.get_contact_details(contactId)
+               )
+
+       def _get_contact_details(self, contactId):
+               return self._contacts[contactId][1]
+
+       def _has_contact_changed(self, contactId, oldContacts):
+               oldContact = oldContacts[contactId]
+               oldContactName = oldContact[0]
+               oldContactDetails = oldContact[1]
+               if oldContactName != self.get_contact_name(contactId):
+                       return True
+               if not oldContactDetails[1]:
+                       return False
+               # if its already in the old cache, purposefully add it into the new cache
+               return oldContactDetails != self.get_contact_details(contactId)
index 702e35a..057ddf3 100644 (file)
@@ -66,12 +66,11 @@ class ConnectionHandle(TheOneRingHandle):
 
 class ContactHandle(TheOneRingHandle):
 
-       def __init__(self, connection, id, contactId, contactAccount):
+       def __init__(self, connection, id, contactId):
                handleType = telepathy.HANDLE_TYPE_CONTACT
                handleName = contactId
                TheOneRingHandle.__init__(self, connection, id, handleType, handleName)
 
-               self._account = contactAccount
                self._id = contactId
 
        @property
@@ -79,12 +78,8 @@ class ContactHandle(TheOneRingHandle):
                return self._id
 
        @property
-       def contactName(self):
-               return self._account
-
-       @property
        def contactDetails(self):
-               return self._conn.gvoice_client.get_contact_details(self._id)
+               return self._conn.addressbook.get_contact_details(self._id)
 
 
 class ListHandle(TheOneRingHandle):
@@ -95,19 +90,10 @@ class ListHandle(TheOneRingHandle):
                TheOneRingHandle.__init__(self, connection, id, handleType, handleName)
 
 
-class GroupHandle(TheOneRingHandle):
-
-       def __init__(self, connection, id, groupName):
-               handleType = telepathy.HANDLE_TYPE_GROUP
-               handleName = groupName
-               TheOneRingHandle.__init__(self, connection, id, handleType, handleName)
-
-
 _HANDLE_TYPE_MAPPING = {
        'connection': ConnectionHandle,
        'contact': ContactHandle,
        'list': ListHandle,
-       'group': GroupHandle,
 }