Generalize activity publishing code for > 1 activity per process

This commit is contained in:
Dan Williams 2006-06-15 11:20:18 -04:00
parent f2ae132410
commit ecf81ba35f
9 changed files with 70 additions and 29 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,25 +1,24 @@
from xml.sax import saxutils
import dbus import dbus
import pygtk import pygtk
pygtk.require('2.0') pygtk.require('2.0')
import gtk import gtk
import geckoembed import geckoembed
import urllib
from sugar.shell import activity from sugar.shell import activity
from sugar.browser import NotificationBar from sugar.browser import NotificationBar
from sugar.browser import NavigationToolbar from sugar.browser import NavigationToolbar
_BROWSER_ACTIVITY_TYPE = "_web_browser_olpc._udp"
class BrowserActivity(activity.Activity): class BrowserActivity(activity.Activity):
SOLO = 1 SOLO = 1
FOLLOWING = 2 FOLLOWING = 2
LEADING = 3 LEADING = 3
def __init__(self, group, uri): def __init__(self, uri):
activity.Activity.__init__(self) activity.Activity.__init__(self)
self.uri = uri self.uri = uri
self._group = group
self._mode = BrowserActivity.SOLO self._mode = BrowserActivity.SOLO
def _update_shared_location(self): def _update_shared_location(self):
@ -85,14 +84,14 @@ class BrowserActivity(activity.Activity):
self._setup_shared(self.uri) self._setup_shared(self.uri)
def publish(self): def publish(self):
print 'Publish %s' % self.activity_get_id() print 'Publish %s' % self.get_id()
def get_embed(self): def get_embed(self):
return self.embed return self.embed
def share(self): def share(self):
address = self.embed.get_address() url = self.embed.get_address()
self._model = self._group.get_store().create_model(address) self._model = self._group.get_store().create_model(url)
self._model.set_value('owner', self._group.get_owner().get_nick_name()) self._model.set_value('owner', self._group.get_owner().get_nick_name())
self._update_shared_location() self._update_shared_location()
self.set_mode(BrowserActivity.LEADING) self.set_mode(BrowserActivity.LEADING)
@ -101,8 +100,8 @@ class BrowserActivity(activity.Activity):
proxy_obj = bus.get_object('com.redhat.Sugar.Chat', '/com/redhat/Sugar/Chat') proxy_obj = bus.get_object('com.redhat.Sugar.Chat', '/com/redhat/Sugar/Chat')
chat_shell = dbus.Interface(proxy_obj, 'com.redhat.Sugar.ChatShell') chat_shell = dbus.Interface(proxy_obj, 'com.redhat.Sugar.ChatShell')
escaped_title = saxutils.escape(self.embed.get_title()) escaped_title = urllib.quote(self.embed.get_title())
escaped_address = saxutils.escape(address) escaped_url = urllib.quote(url)
chat_shell.send_text_message('<richtext><link href="' + escaped_address + chat_shell.send_text_message('<richtext><link href="' + escaped_address +
'">' + escaped_title + '</link></richtext>') '">' + escaped_title + '</link></richtext>')

View File

@ -30,6 +30,6 @@ class BrowserShell(dbus.service.Object):
@dbus.service.method('com.redhat.Sugar.BrowserShell') @dbus.service.method('com.redhat.Sugar.BrowserShell')
def open_browser(self, uri): def open_browser(self, uri):
browser = BrowserActivity(None, uri) browser = BrowserActivity(uri)
self.__browsers.append(browser) self.__browsers.append(browser)
browser.connect_to_shell() browser.connect_to_shell()

View File

@ -13,7 +13,6 @@ pygtk.require('2.0')
import gtk, gobject, pango import gtk, gobject, pango
from sugar.shell import activity from sugar.shell import activity
from sugar.presence.Group import Group
from sugar.presence import Buddy from sugar.presence import Buddy
from sugar.presence.Service import Service from sugar.presence.Service import Service
from sugar.p2p.Stream import Stream from sugar.p2p.Stream import Stream

View File

@ -111,13 +111,13 @@ class PresenceService(gobject.GObject):
self._service_advs = [] self._service_advs = []
# Main activity UID to filter services on # Main activity UID to filter services on
self._activity_uid = None self._activity_uids = []
self._bus = dbus.SystemBus() self._bus = dbus.SystemBus()
self._server = dbus.Interface(self._bus.get_object(avahi.DBUS_NAME, self._server = dbus.Interface(self._bus.get_object(avahi.DBUS_NAME,
avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER) avahi.DBUS_PATH_SERVER), avahi.DBUS_INTERFACE_SERVER)
def start(self, activity_uid=None): def start(self):
"""Start the presence service by kicking off service discovery.""" """Start the presence service by kicking off service discovery."""
self._lock.acquire() self._lock.acquire()
if self._started: if self._started:
@ -126,10 +126,6 @@ class PresenceService(gobject.GObject):
self._started = True self._started = True
self._lock.release() self._lock.release()
if activity_uid and not util.validate_activity_uid(activity_uid):
raise ValueError("activity uid must be a valid UID string.")
self._activity_uid = activity_uid
# Always browse .local # Always browse .local
self._new_domain_cb(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, "local") self._new_domain_cb(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, "local")
@ -284,11 +280,9 @@ class PresenceService(gobject.GObject):
# If we care about the service right now, resolve it # If we care about the service right now, resolve it
resolve = False resolve = False
if self._activity_uid and self._activity_uid == uid: if uid in self._activity_uids:
if stype in self._allowed_service_types: if stype in self._allowed_service_types:
resolve = True resolve = True
elif not self._activity_uid:
resolve = True
if self._is_special_service_type(stype): if self._is_special_service_type(stype):
resolve = True resolve = True
if resolve: if resolve:
@ -389,6 +383,11 @@ class PresenceService(gobject.GObject):
if stype in self._allowed_service_types: if stype in self._allowed_service_types:
return return
# Decompose service type if we can
(uid, stype) = Service._decompose_service_type(stype)
if uid and util.validate_activity_uid(uid):
if uid not in self._activity_uids:
self._activity_uids.append(uid)
self._allowed_service_types.append(stype) self._allowed_service_types.append(stype)
# Find unresolved services that match the service type # Find unresolved services that match the service type
@ -404,24 +403,53 @@ class PresenceService(gobject.GObject):
raise RuntimeError("presence service must be started first.") raise RuntimeError("presence service must be started first.")
if not type(stype) == type(""): if not type(stype) == type(""):
raise ValueError("service type must be a string.") raise ValueError("service type must be a string.")
if name in self._allowed_service_types:
# Decompose service type if we can
(uid, stype) = Service._decompose_service_type(stype)
if uid and util.validate_activity_uid(uid):
if uid in self._activity_uids:
self._activity_uids.remove(uid)
if stype in self._allowed_service_types:
self._allowed_service_types.remove(stype) self._allowed_service_types.remove(stype)
def join_group(self, group): def join_shared_activity(self, service):
"""Convenience function to join a group and notify other buddies """Convenience function to join a group and notify other buddies
that you are a member of it.""" that you are a member of it."""
if not isinstance(group, Group.Group): if not isinstance(service, Service.Service):
raise ValueError("group was not a valid group.") raise ValueError("service was not a valid service object.")
gservice = group.get_service()
self.register_service(service) self.register_service(service)
def share_activity(self, activity, stype, properties={}, address=None, port=None):
"""Convenience function to share an activity with other buddies."""
uid = activity.get_id()
owner_nick = self._owner.get_nick_name()
real_stype = "_%s_%s" % (uid, stype)
if address and type(address) != type(""):
raise ValueError("address must be a valid string.")
if not address:
# Use random currently unassigned multicast address
address = "232.%d.%d.%d" % (random.randint(0, 254), random.randint(1, 254),
random.randint(1, 254))
if port and (type(port) != type(1) or port <= 1024 or port >= 65535):
raise ValueError("port must be a number between 1024 and 65535")
if not port:
# random port #
port = random.randint(5000, 65535)
service = Service.Service(name=owner_nick, stype=real_stype, domain="local",
address=address, port=port, properties=properties)
# Publish it to the world
self.register_service(service)
return service
def register_service(self, service): def register_service(self, service):
"""Register a new service, advertising it to other Buddies on the network.""" """Register a new service, advertising it to other Buddies on the network."""
if not self._started: if not self._started:
raise RuntimeError("presence service must be started first.") raise RuntimeError("presence service must be started first.")
rs_name = service.get_name() rs_name = service.get_name()
rs_stype = service.get_type() rs_stype = service.get_network_type()
rs_port = service.get_port() rs_port = service.get_port()
if type(rs_port) != type(1) and (rs_port <= 1024 or rs_port > 65536): if type(rs_port) != type(1) and (rs_port <= 1024 or rs_port > 65536):
raise ValueError("invalid service port.") raise ValueError("invalid service port.")
@ -444,7 +472,7 @@ class PresenceService(gobject.GObject):
# should un-register it an re-register with the correct info # should un-register it an re-register with the correct info
if str(exc) == "Local name collision": if str(exc) == "Local name collision":
pass pass
self.track_service_type(rs_stype) self.track_service_type(service.get_network_type())
return group return group
def get_buddy_by_nick_name(self, nick_name): def get_buddy_by_nick_name(self, nick_name):

View File

@ -69,6 +69,8 @@ def is_multicast_address(address):
return False return False
_ACTIVITY_UID_TAG = "ActivityUID"
class Service(object): class Service(object):
"""Encapsulates information about a specific ZeroConf/mDNS """Encapsulates information about a specific ZeroConf/mDNS
service as advertised on the network.""" service as advertised on the network."""
@ -101,7 +103,16 @@ class Service(object):
self.set_port(port) self.set_port(port)
self._properties = {} self._properties = {}
self.set_properties(properties) self.set_properties(properties)
# Ensure that an ActivityUID tag, if given, matches
# what we expect from the service type
if self._properties.has_key(_ACTIVITY_UID_TAG):
prop_uid = self._properties[_ACTIVITY_UID_TAG]
if (prop_uid and not uid) or (prop_uid != uid):
raise ValueError("ActivityUID property specified, but the service type's activity UID didn't match it.")
self._activity_uid = uid self._activity_uid = uid
if uid and not self._properties.has_key(_ACTIVITY_UID_TAG):
self._properties[_ACTIVITY_UID_TAG] = uid
def get_name(self): def get_name(self):
"""Return the service's name, usually that of the """Return the service's name, usually that of the
@ -141,6 +152,10 @@ class Service(object):
"""Return the service's service type.""" """Return the service's service type."""
return self._stype return self._stype
def get_network_type(self):
"""Return the full service type, including activity UID."""
return self._real_stype
def get_port(self): def get_port(self):
return self._port return self._port

View File

@ -268,7 +268,7 @@ class Activity(object):
else: else:
self._activity_object.set_has_changes(False) self._activity_object.set_has_changes(False)
def activity_get_id(self): def get_id(self):
return self._activity_id return self._activity_id
def shutdown(self): def shutdown(self):