I extended the Invites to handle connections for 1-1 connections. This should also handle incoming streaming media connections for VideoChat (when that activity has the necessary bits in place).
It depends on Guillaume's patches in http://dev.laptop.org/ticket/6298 for presence-service, sugar, sugar-toolkit and chat-activity. I will get this patch up in my git repo, as soon as I can figure out why it won't allow me to push (non-fast forward). It works in jhbuild with incoming XMPP chat giving an invite for Chat, which launches Chat. The only thing not working yet is removing the invite when you click on it. Review please... Morgan
commit a856fc40557fb1efc6e029088d49f74564d4675c Author: Morgan Collett <[EMAIL PROTECTED]> Date: Fri May 23 16:47:58 2008 +0200 #6298: Refactor invites to handle 1-1 XMPP connections diff --git a/src/model/Invites.py b/src/model/Invites.py index 9ffab44..2a4c9f2 100644 --- a/src/model/Invites.py +++ b/src/model/Invites.py @@ -18,10 +18,11 @@ import gobject from sugar.presence import presenceservice class Invite: - def __init__(self, issuer, bundle_id, activity_id): - self._issuer = issuer + def __init__(self, bundle_id, activity_id=None, + private_connection=None): self._activity_id = activity_id self._bundle_id = bundle_id + self._private_connection = private_connection def get_activity_id(self): return self._activity_id @@ -29,6 +30,11 @@ class Invite: def get_bundle_id(self): return self._bundle_id + def get_private_connection(self): + """Connection info from private invitation""" + return self._private_connection + + class Invites(gobject.GObject): __gsignals__ = { 'invite-added': (gobject.SIGNAL_RUN_FIRST, @@ -41,30 +47,52 @@ class Invites(gobject.GObject): gobject.GObject.__init__(self) self._dict = {} + self._private_invites = {} ps = presenceservice.get_instance() owner = ps.get_owner() owner.connect('joined-activity', self._owner_joined_cb) + # FIXME need equivalent for ^ for private invite - def add_invite(self, issuer, bundle_id, activity_id): + def add_invite(self, bundle_id, activity_id): if activity_id in self._dict: # there is no point to add more than one time # an invite for the same activity return - invite = Invite(issuer, bundle_id, activity_id) + invite = Invite(bundle_id, activity_id=activity_id) self._dict[activity_id] = invite self.emit('invite-added', invite) + def add_private_invite(self, private_connection, bundle_id): + if private_connection in self._dict: + # there is no point to add more than one invite for the + # same incoming connection + return + + invite = Invite(bundle_id, + private_connection=private_connection) + self._dict[private_connection] = invite + self.emit('invite-added', invite) + def remove_invite(self, invite): self._dict.pop(invite.get_activity_id()) self.emit('invite-removed', invite) + def remove_private_invite(self, invite): + self._dict.pop(invite.get_private_connection()) + self.emit('invite-removed', invite) + def remove_activity(self, activity_id): invite = self._dict.get(activity_id) if invite is not None: self.remove_invite(invite) + def remove_private_connection(self, private_connection): + invite = self._dict.get(private_connection) + if invite is not None: + self.remove_private_invite(invite) + def _owner_joined_cb(self, owner, activity): self.remove_activity(activity.props.id) diff --git a/src/model/Owner.py b/src/model/Owner.py index 7affb83..1643c6f 100644 --- a/src/model/Owner.py +++ b/src/model/Owner.py @@ -17,6 +17,7 @@ import gobject import os +import json from telepathy.interfaces import CHANNEL_TYPE_TEXT @@ -81,7 +82,7 @@ class ShellOwner(gobject.GObject): return self._nick def _activity_invitation_cb(self, pservice, activity, buddy, message): - self._invites.add_invite(buddy, activity.props.type, + self._invites.add_invite(activity.props.type, activity.props.id) def _private_invitation_cb(self, pservice, bus_name, connection, @@ -91,14 +92,13 @@ class ShellOwner(gobject.GObject): This is a connection by a non-Sugar XMPP client, so launch Chat with the Telepathy connection and channel. """ - import json - from sugar import activity - from sugar.activity import activityfactory + if channel_type == CHANNEL_TYPE_TEXT: + bundle_id = 'org.laptop.Chat' + else: + bundle_id = 'org.laptop.VideoChat' tp_channel = json.write([str(bus_name), str(connection), str(channel)]) - registry = activity.get_registry() - if registry.get_activity('org.laptop.Chat') and channel_type == CHANNEL_TYPE_TEXT: - activityfactory.create_with_uri('org.laptop.Chat', tp_channel) + self._invites.add_private_invite(tp_channel, bundle_id) def _activity_disappeared_cb(self, pservice, activity): self._invites.remove_activity(activity.props.id) diff --git a/src/view/Shell.py b/src/view/Shell.py index f45923d..6dddda9 100644 --- a/src/view/Shell.py +++ b/src/view/Shell.py @@ -169,6 +169,14 @@ class Shell(gobject.GObject): self._activities_starting.add(activity_type) activityfactory.create(activity_type) + def start_activity_with_uri(self, activity_type, uri): + if activity_type in self._activities_starting: + logging.debug("This activity is still launching.") + return + + self._activities_starting.add(activity_type) + activityfactory.create_with_uri(activity_type, uri) + def take_activity_screenshot(self): if self._model.get_zoom_level() != shellmodel.ShellModel.ZOOM_ACTIVITY: return diff --git a/src/view/frame/activitiestray.py b/src/view/frame/activitiestray.py index 3958f20..50b6f69 100644 --- a/src/view/frame/activitiestray.py +++ b/src/view/frame/activitiestray.py @@ -26,7 +26,7 @@ from sugar.graphics.toolbutton import ToolButton from sugar.graphics.icon import Icon from sugar.graphics.palette import Palette, WidgetInvoker from sugar.graphics.menuitem import MenuItem -from sugar import activity +from sugar import activity, profile from model import shellmodel from view.palettes import JournalPalette, CurrentActivityPalette @@ -88,21 +88,43 @@ class ActivityButton(RadioToolButton): home_activity.disconnect(self._notify_launching_hid) class InviteButton(ToolButton): - def __init__(self, activity_model): + """Invite to shared activity""" + def __init__(self, invite): ToolButton.__init__(self) - self._activity_model = activity_model + self._invite = invite + self._activity_model = activity_model = None + self._private_connection = None + if invite.get_activity_id(): + # shared activity + mesh = shellmodel.get_instance().get_mesh() + activity_model = mesh.get_activity(invite.get_activity_id()) + self._activity_model = activity_model + self._bundle_id = activity_model.get_bundle_id() + else: + # private invite to 1-1 connection + self._private_connection = invite.get_private_connection() + self._bundle_id = invite.get_bundle_id() self._icon = Icon() - self._icon.props.xo_color = activity_model.get_color() - if activity_model.get_icon_name(): - self._icon.props.file = activity_model.get_icon_name() + if activity_model: + self._icon.props.xo_color = activity_model.get_color() + if activity_model.get_icon_name(): + self._icon.props.file = activity_model.get_icon_name() + else: + self._icon.props.icon_name = 'image-missing' else: - self._icon.props.icon_name = 'image-missing' + self._icon.props.xo_color = profile.get_color() + registry = activity.get_registry() + activity_info = registry.get_activity(self._bundle_id) + if activity_info: + self._icon.props.file = activity_info.icon + else: + self._icon.props.icon_name = 'image-missing' self.set_icon_widget(self._icon) self._icon.show() - palette = InvitePalette(activity_model) + palette = InvitePalette(invite) palette.props.invoker = FrameWidgetInvoker(self) palette.set_group_id('frame') self.set_palette(palette) @@ -111,16 +133,25 @@ class InviteButton(ToolButton): self.connect('destroy', self.__destroy_cb) self._notif_icon = NotificationIcon() - self._notif_icon.props.xo_color = activity_model.get_color() - if activity_model.get_icon_name(): - icon_name = activity_model.get_icon_name() - self._notif_icon.props.icon_filename = icon_name + if activity_model: + self._notif_icon.props.xo_color = activity_model.get_color() + if activity_model.get_icon_name(): + icon_name = activity_model.get_icon_name() + self._notif_icon.props.icon_filename = icon_name + else: + self._notif_icon.props.icon_name = 'image-missing' else: - self._notif_icon.props.icon_name = 'image-missing' + self._notif_icon.props.xo_color = profile.get_color() + registry = activity.get_registry() + activity_info = registry.get_activity(self._bundle_id) + if activity_info: + self._notif_icon.props.icon_filename = activity_info.icon + else: + self._notif_icon.props.icon_name = 'image-missing' self._notif_icon.connect('button-release-event', self.__button_release_event_cb) - palette = InvitePalette(activity_model) + palette = InvitePalette(invite) palette.props.invoker = WidgetInvoker(self._notif_icon) palette.set_group_id('frame') self._notif_icon.palette = palette @@ -137,25 +168,42 @@ class InviteButton(ToolButton): self._notif_icon = None shell = view.Shell.get_instance() - shell.join_activity(self._activity_model.get_bundle_id(), - self._activity_model.get_id()) + if self._activity_model: + shell.join_activity(self._activity_model.get_bundle_id(), + self._activity_model.get_id()) + else: + shell.start_activity_with_uri(self._bundle_id, + self._private_connection) def __destroy_cb(self, button): frame = view.frame.frame.get_instance() frame.remove_notification(self._notif_icon) class InvitePalette(Palette): - def __init__(self, activity_model): - self._activity_model = activity_model + def __init__(self, invite): + + self._invite = invite + self._activity_model = activity_model = None + self._private_connection = None + if invite.get_activity_id(): + # shared activity + mesh = shellmodel.get_instance().get_mesh() + activity_model = mesh.get_activity(invite.get_activity_id()) + self._activity_model = activity_model + self._bundle_id = activity_model.get_bundle_id() + else: + # private invite to 1-1 connection + self._private_connection = invite.get_private_connection() + self._bundle_id = invite.get_bundle_id() Palette.__init__(self, '') registry = activity.get_registry() - activity_info = registry.get_activity(activity_model.get_bundle_id()) + activity_info = registry.get_activity(self._bundle_id) if activity_info: self.set_primary_text(activity_info.name) else: - self.set_primary_text(activity_model.get_bundle_id()) + self.set_primary_text(self._bundle_id) menu_item = MenuItem(_('Join'), icon_name='dialog-ok') menu_item.connect('activate', self.__join_activate_cb) @@ -169,15 +217,21 @@ class InvitePalette(Palette): def __join_activate_cb(self, menu_item): shell = view.Shell.get_instance() - shell.join_activity(self._activity_model.get_bundle_id(), - self._activity_model.get_id()) + if self._activity_model: + shell.join_activity(self._activity_model.get_bundle_id(), + self._activity_model.get_id()) + else: + shell.start_activity_with_uri(self._bundle_id, + self._private_connection) def __decline_activate_cb(self, menu_item): invites = shellmodel.get_instance().get_invites() - for invite in invites: - if invite.get_activity_id() == self._activity_model.get_id(): - invites.remove_invite(invite) - return + if self._activity_model: + activity_id = self._activity_model.get_id() + invites.remove_activity(activity_id) + else: + invites.remove_private_connection(self._private_connection) + class ActivitiesTray(HTray): def __init__(self): @@ -237,15 +291,17 @@ class ActivitiesTray(HTray): self._remove_invite(invite) def _add_invite(self, invite): - mesh = shellmodel.get_instance().get_mesh() - activity_model = mesh.get_activity(invite.get_activity_id()) - if activity_model: - item = InviteButton(activity_model) - item.connect('clicked', self.__invite_clicked_cb, invite) - self.add_item(item) - item.show() - - self._invite_to_item[invite] = item + """Add an invite (SugarInvite or PrivateInvite)""" + if invite.get_activity_id(): + mesh = shellmodel.get_instance().get_mesh() + activity_model = mesh.get_activity(invite.get_activity_id()) + if not activity_model: + return + item = InviteButton(invite) + item.connect('clicked', self.__invite_clicked_cb, invite) + self.add_item(item) + item.show() + self._invite_to_item[invite] = item def _remove_invite(self, invite): self.remove_item(self._invite_to_item[invite])
_______________________________________________ Sugar mailing list Sugar@lists.laptop.org http://lists.laptop.org/listinfo/sugar