diff --git a/services/presence2/activity.py b/services/presence2/activity.py index b79d2b06..656bb18f 100644 --- a/services/presence2/activity.py +++ b/services/presence2/activity.py @@ -15,33 +15,118 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +import gobject import dbus, dbus.service +from sugar import util from telepathy.interfaces import (CHANNEL_INTERFACE) _ACTIVITY_PATH = "/org/laptop/Sugar/Presence/Activities/" _ACTIVITY_INTERFACE = "org.laptop.Sugar.Presence.Activity" -class Activity(dbus.service.Object): - def __init__(self, bus_name, object_id, activity_id, tp): - self._buddies = [] - self._color = None - self._valid = False - self._name = None - self._activity_id = activity_id - self._type None +class DBusGObjectMetaclass(gobject.GObjectMeta, dbus.service.InterfaceType): pass +class DBusGObject(dbus.service.Object, gobject.GObject): __metaclass__ = DBusGObjectMetaclass + + +class Activity(DBusGObject): + __gtype_name__ = "Activity" + + __gsignals__ = { + 'validity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_BOOLEAN])) + } + + __gproperties__ = { + 'id' : (str, None, None, None, + gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY), + 'name' : (str, None, None, None, gobject.PARAM_READWRITE), + 'color' : (str, None, None, None, gobject.PARAM_READWRITE), + 'type' : (str, None, None, None, gobject.PARAM_READWRITE), + 'valid' : (bool, None, None, False, gobject.PARAM_READABLE), + 'local' : (bool, None, None, False, + gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY), + 'joined' : (bool, None, None, False, gobject.PARAM_READABLE) + } + + def __init__(self, bus_name, object_id, tp, **kwargs): + if not bus_name: + raise ValueError("DBus bus name must be valid") + if not object_id or not isinstance(object_id, int): + raise ValueError("object id must be a valid number") + if not tp: + raise ValueError("telepathy CM must be valid") self._object_id = object_id - self._object_path = "/org/laptop/Presence/Activities/%d" % self._object_id - + self._object_path = _ACTIVITY_PATH + str(self._object_id) + dbus.service.Object.__init__(self, bus_name, self._object_path) + + self._buddies = [] + self._joined = False + # the telepathy client self._tp = tp self._activity_text_channel = None - self._joined = False + self._valid = False + self._id = None + self._local = False + self._type = None - dbus.service.Object.__init__(self, bus_name, self._object_path) - + if not kwargs.get("id"): + raise ValueError("activity id is required") + if not util.validate_activity_id(kwargs['id']): + raise ValueError("Invalid activity id '%s'" % kwargs['id']) + + gobject.GObject.__init__(self, **kwargs) + if self.props.local and not self.props.valid: + raise RuntimeError("local activities require color, type, and name") + + def do_get_property(self, pspec): + if pspec.name == "id": + return self._id + elif pspec.name == "name": + return self._name + elif pspec.name == "color": + return self._color + elif pspec.name == "type": + return self._type + elif pspec.name == "valid": + return self._valid + elif pspec.name == "joined": + return self._joined + elif pspec.name == "local": + return self._local + + def do_set_property(self, pspec, value): + if pspec.name == "id": + self._id = value + elif pspec.name == "name": + self._name = value + elif pspec.name == "color": + self._color = value + elif pspec.name == "type": + if self._type: + raise RuntimeError("activity type is already set") + self._type = value + elif pspec.name == "joined": + self._joined = value + elif pspec.name == "local": + self._local = value + + self._update_validity() + + def _update_validity(self): + try: + old_valid = self._valid + if self._color and self._name and self._id and self._type: + self._valid = True + else: + self._valid = False + + if old_valid != self._valid: + self.emit("validity-changed", self._valid) + except AttributeError: + self._valid = False # dbus signals @dbus.service.signal(_ACTIVITY_INTERFACE, @@ -63,17 +148,17 @@ class Activity(dbus.service.Object): @dbus.service.method(_ACTIVITY_INTERFACE, in_signature="", out_signature="s") def GetId(self): - return self.get_id() + return self.props.id @dbus.service.method(_ACTIVITY_INTERFACE, in_signature="", out_signature="s") def GetColor(self): - return self.get_color() + return self.props.color @dbus.service.method(_ACTIVITY_INTERFACE, in_signature="", out_signature="s") def GetType(self): - return self.get_type() + return self.props.type @dbus.service.method(_ACTIVITY_INTERFACE, in_signature="", out_signature="") @@ -83,8 +168,10 @@ class Activity(dbus.service.Object): @dbus.service.method(_ACTIVITY_INTERFACE, in_signature="", out_signature="ao") def GetJoinedBuddies(self): + ret = [] for buddy in self._buddies: - ret.append(buddy.object_path()) + if buddy.props.valid: + ret.append(buddy.object_path()) return ret @dbus.service.method(_ACTIVITY_INTERFACE, @@ -95,44 +182,34 @@ class Activity(dbus.service.Object): @dbus.service.method(_ACTIVITY_INTERFACE, in_signature="", out_signature="s") def GetName(self): - return self.get_name() + return self.props.name # methods def object_path(self): return dbus.ObjectPath(self._object_path) - def is_valid(self): - """An activity is only valid when it's color is available.""" - return self._valid - - def get_id(self): - return self._activity_id - - def get_color(self): - return self._color - def get_joined_buddies(self): - return self._buddies - - def get_name(self): - return self._name - - def get_type(self): - return self._type + ret = [] + for buddy in self._buddies: + if buddy.props.valid: + ret.append(buddy) + return ret def buddy_joined(self, buddy): if buddy not in self._buddies: self._buddies.append(buddy) - self.BuddyJoined(buddy.object_path()) + if self.props.valid: + self.BuddyJoined(buddy.object_path()) def buddy_left(self, buddy): if buddy in self._buddies: self._buddies.remove(buddy) - self.BuddyLeft(buddy.object_path()) + if self.props.valid: + self.BuddyLeft(buddy.object_path()) def join(self): if not self._joined: - self._activity_text_channel = self._tp.join_activity(self._activity_id) + self._activity_text_channel = self._tp.join_activity(self.props.id) self._activity_text_channel[CHANNEL_INTERFACE].connect_to_signal('Closed', self._activity_text_channel_closed_cb) self._joined = True diff --git a/services/presence2/buddy.py b/services/presence2/buddy.py index 458d1247..756b3085 100644 --- a/services/presence2/buddy.py +++ b/services/presence2/buddy.py @@ -73,9 +73,10 @@ class Buddy(DBusGObject): self._key = None self._icon = '' + if not kwargs.get("key"): + raise ValueError("key required") + gobject.GObject.__init__(self, **kwargs) - if not self._key: - raise RuntimeError("public key required") def do_get_property(self, pspec): if pspec.name == "key": @@ -109,8 +110,6 @@ class Buddy(DBusGObject): elif pspec.name == "current-activity": self._current_activity = value elif pspec.name == "key": - if self._key: - raise RuntimeError("key already set") self._key = value self._update_validity() @@ -167,25 +166,25 @@ class Buddy(DBusGObject): return dbus.ObjectPath(self._object_path) def add_activity(self, activity): - actid = activity.get_id() + actid = activity.props.id if self._activities.has_key(actid): return self._activities[actid] = activity - if activity.is_valid(): + if activity.props.valid: self.JoinedActivity(activity.object_path()) def remove_activity(self, activity): - actid = activity.get_id() + actid = activity.props.id if not self._activities.has_key(actid): return del self._activities[actid] - if activity.is_valid(): + if activity.props.valid: self.LeftActivity(activity.object_path()) def get_joined_activities(self): acts = [] for act in self._activities.values(): - if act.is_valid(): + if act.props.valid: acts.append(act) return acts diff --git a/services/presence2/presenceservice.py b/services/presence2/presenceservice.py index 4ad780d9..2018ec1c 100644 --- a/services/presence2/presenceservice.py +++ b/services/presence2/presenceservice.py @@ -137,22 +137,35 @@ class PresenceService(dbus.service.Object): #print "Buddy %s properties updated" % buddy.props.key def _new_activity(self, activity_id, tp): - objid = self._get_next_object_id() - activity = Activity(self._bus_name, objid, activity_id, tp) - # FIXME : don't do that shit ! - activity._valid = True + try: + objid = self._get_next_object_id() + activity = Activity(self._bus_name, objid, tp, id=activity_id) + except Exception, e: + print "Invalid activity: %s" % e + return None + + activity.connect("validity-changed", self._activity_validity_changed_cb) + self._activities[activity_id] = activity - print "new activity", activity_id - self.ActivityAppeared(activity.object_path()) + # FIXME + # Use values from the network + import random + names = ["Tommy", "Susie", "Jill", "Bryan", "Nathan", "Sophia", "Haley", "Jimmy"] + name = names[random.randint(0, len(names) - 1)] + activity.props.name = "Chat with %s" % name + activity.props.type = "org.laptop.Sugar.Chat" + from sugar.graphics import xocolor + color = xocolor.XoColor().to_string() + activity.props.color = color return activity def _remove_activity(self, activity): - print "remove activity", activity.get_id() + print "remove activity", activity.props.id self.ActivityDisappeared(activity.object_path()) - del self._activities[activity.get_id()] + del self._activities[activity.props.id] def _contact_activities_changed(self, tp, contact_handle, activities): print "------------activities changed-------------" @@ -168,7 +181,7 @@ class PresenceService(dbus.service.Object): old_activities = set() for activity in buddy.get_joined_activities(): - old_activities.add(activity.get_id()) + old_activities.add(activity.props.id) new_activities = set(activities) @@ -177,11 +190,12 @@ class PresenceService(dbus.service.Object): print "buddy", contact_handle, "joined", act activity = self._activities.get(act) if not activity: - # new activity + # new activity, can fail activity = self._new_activity(act, tp) - activity.buddy_joined(buddy) - buddy.add_activity(activity) + if activity: + activity.buddy_joined(buddy) + buddy.add_activity(activity) activities_left = old_activities - new_activities for act in activities_left: @@ -278,18 +292,24 @@ class PresenceService(dbus.service.Object): def _share_activity(self, actid, atype, name, properties): objid = self._get_next_object_id() # FIXME check which tp client we should use to share the activity - activity = Activity(self._bus_name, objid, actid, self._server_plugin) - # FIXME : don't do that shit ! - activity._valid = True + color = self._owner.props.color + activity = Activity(self._bus_name, objid, self._server_plugin, + id=actid, type=atype, name=name, color=color, local=True) + activity.connect("validity-changed", self._activity_validity_changed_cb) self._activities[actid] = activity - # FIXME set the type, name, properties... - print "new activity", actid activity.join() - self.ActivityAppeared(activity.object_path()) return activity + def _activity_validity_changed_cb(self, activity, valid): + if valid: + self.ActivityAppeared(activity.object_path()) + print "New Activity: %s (%s)" % (activity.props.name, activity.props.id) + else: + self.ActivityDisappeared(activity.object_path()) + print "Activity disappeared: %s (%s)" % (activity.props.name, activity.props.id) + def main(): loop = gobject.MainLoop()