Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Silbe <sascha-pgp@silbe.org>2010-08-29 14:43:35 (GMT)
committer Sascha Silbe <sascha-pgp@silbe.org>2010-08-29 14:43:35 (GMT)
commit9d07abf0cf03f24f59ba3456b4b6acbaa82888de (patch)
treeb66cabae43b75cd8127ee10debb7e905da506130
parentd135159e5eb43ad6b4e456eb4fee31579d8806c9 (diff)
add HAL support, some fixes
-rw-r--r--activity/activity.info2
-rw-r--r--backup.py152
2 files changed, 141 insertions, 13 deletions
diff --git a/activity/activity.info b/activity/activity.info
index d22d395..21e76f1 100644
--- a/activity/activity.info
+++ b/activity/activity.info
@@ -1,6 +1,6 @@
[Activity]
name = Backup
-activity_version = 1
+activity_version = 2
bundle_id = org.sugarlabs.Backup
exec = sugar-activity backup.BackupActivity
icon = Backup_icon
diff --git a/backup.py b/backup.py
index 2fcc402..4e88762 100644
--- a/backup.py
+++ b/backup.py
@@ -56,6 +56,12 @@ DS_DBUS_PATH1 = "/org/laptop/sugar/DataStore"
DS_DBUS_INTERFACE2 = "org.laptop.sugar.DataStore2"
DS_DBUS_PATH2 = "/org/laptop/sugar/DataStore2"
+HAL_SERVICE_NAME = 'org.freedesktop.Hal'
+HAL_MANAGER_PATH = '/org/freedesktop/Hal/Manager'
+HAL_MANAGER_IFACE = 'org.freedesktop.Hal.Manager'
+HAL_DEVICE_IFACE = 'org.freedesktop.Hal.Device'
+HAL_VOLUME_IFACE = 'org.freedesktop.Hal.Device.Volume'
+
def format_size(size):
if not size:
@@ -260,6 +266,9 @@ class AsyncBackup(gobject.GObject):
def _create_bundle(self):
"""Create / open bundle (zip file) with unique file name."""
date = time.strftime('%x')
+ if '/' in date:
+ date = time.strftime('%Y-%m-%d')
+
prefix = _('Journal backup of %s (%s) on %s') % (self._user_name,
self._key_hash, date)
bundle_fd, path = self._create_file(self._mount_point, prefix, '.xoj')
@@ -331,6 +340,8 @@ class AsyncBackup(gobject.GObject):
try:
self._data_store = dbus.Interface(bus.get_object(DS_DBUS_SERVICE,
DS_DBUS_PATH2), DS_DBUS_INTERFACE2)
+ self._data_store.find({'tree_id': 'invalid'},
+ {'metadata': ['tree_id']})
logging.info('Data store with version support found')
return
@@ -339,6 +350,7 @@ class AsyncBackup(gobject.GObject):
self._data_store = dbus.Interface(bus.get_object(DS_DBUS_SERVICE,
DS_DBUS_PATH1), DS_DBUS_INTERFACE1)
+ self._data_store.find({'uid': 'invalid'}, ['uid'])
logging.info('Data store without version support found')
def _find_entries(self):
@@ -385,12 +397,13 @@ class BackupActivity(activity.Activity):
self._message_box = None
self._media_combo_model = None
self._media_combo = None
+ self._backup_button = None
self._backup = None
+ self._hal_devices = {}
client = gconf.client_get_default()
self._color = XoColor(client.get_string('/desktop/sugar/user/color'))
self._setup_widgets()
self._find_media()
- self._media_combo.set_active(0)
def read_file(self, file_path):
"""We don't have any state to save in the Journal."""
@@ -445,9 +458,10 @@ class BackupActivity(activity.Activity):
tool_item.set_tooltip_text(tooltip_text.encode('utf-8'))
toolbar_box.toolbar.insert(tool_item, -1)
- backup_button = BackupButton()
- backup_button.connect('clicked', self.__backup_cb)
- toolbar_box.toolbar.insert(backup_button, -1)
+ self._backup_button = BackupButton()
+ self._backup_button.connect('clicked', self._backup_cb)
+ self._backup_button.set_sensitive(False)
+ toolbar_box.toolbar.insert(self._backup_button, -1)
separator = gtk.SeparatorToolItem()
separator.props.draw = False
@@ -460,8 +474,11 @@ class BackupActivity(activity.Activity):
self.set_toolbar_box(toolbar_box)
toolbar_box.show_all()
- def __backup_cb(self, button):
+ def _backup_cb(self, button):
"""Callback for Backup button."""
+ if not len(self._media_combo_model):
+ return
+
row = self._media_combo_model[self._media_combo.get_active()]
mount_point = row[self._MEDIA_COMBO_PATH_COLUMN]
self._setup_backup_view(mount_point)
@@ -548,24 +565,122 @@ class BackupActivity(activity.Activity):
volume_monitor.connect('mount-added', self._gio_mount_added_cb)
volume_monitor.connect('mount-removed', self._gio_mount_removed_cb)
for mount in volume_monitor.get_mounts():
- self._gio_mount_added_cb(mount)
+ self._gio_mount_added_cb(volume_monitor, mount)
- def _find_media_hal(self):
- # FIXME: implement HAL support
- pass
-
- def _gio_mount_added_cb(self, mount):
+ def _gio_mount_added_cb(self, volume_monitor, mount):
"""Handle notification from GIO that a medium was mounted."""
icon_name = self._choose_icon_name(mount.get_icon().props.names)
path = mount.get_root().get_path()
name = mount.get_name()
self._add_medium(path, name, icon_name)
- def _gio_mount_removed_cb(self, mount):
+ def _gio_mount_removed_cb(self, volume_monitor, mount):
"""Handle notification from GIO that a medium was unmounted."""
path = mount.get_root().get_path()
self._remove_medium(path)
+ def _find_media_hal(self):
+ """Use HAL to fill in the available storage media."""
+ bus = dbus.SystemBus()
+ proxy = bus.get_object(HAL_SERVICE_NAME, HAL_MANAGER_PATH)
+ hal_manager = dbus.Interface(proxy, HAL_MANAGER_IFACE)
+ hal_manager.connect_to_signal('DeviceAdded',
+ self._hal_device_added_cb)
+
+ for udi in hal_manager.FindDeviceByCapability('volume'):
+ self._hal_device_added_cb(udi)
+
+ def _hal_device_added_cb(self, udi):
+ """Handle notification from GIO that a device was added."""
+ bus = dbus.SystemBus()
+ device_object = bus.get_object(HAL_SERVICE_NAME, udi)
+ device = dbus.Interface(device_object, HAL_DEVICE_IFACE)
+
+ # A just-added device might lack one of the attributes we're
+ # looking for, so we need to listen for changes on ALL devices.
+ device.connect_to_signal('PropertyModified',
+ lambda *args: self._hal_property_modified_cb(udi, *args))
+
+ try:
+ if device.GetProperty('volume.fsusage') != 'filesystem':
+ logging.debug('Ignoring non-filesystem UDI %s', udi)
+ return
+ except dbus.DBusException:
+ logging.debug('Ignoring UDI %s without volume.fsusage', udi)
+ return
+
+ self._hal_try_device(device, udi)
+
+ def _hal_try_device(self, device, udi):
+ """Possibly add device to UI."""
+ if not device.GetProperty('volume.is_mounted'):
+ return
+
+ path = device.GetProperty('volume.mount_point')
+ if path == '/':
+ return
+
+ name = device.GetProperty('volume.label')
+ if not name:
+ name = device.GetProperty('volume.uuid')
+
+ self._hal_devices[udi] = path
+ bus = dbus.SystemBus()
+ bus.add_signal_receiver(self._hal_device_removed_cb,
+ 'DeviceRemoved',
+ HAL_MANAGER_IFACE, HAL_SERVICE_NAME,
+ HAL_MANAGER_PATH, arg0=udi)
+
+ self._add_medium(path, name,
+ self._choose_icon_name(self._hal_get_icons_for_volume(device)))
+
+ def _hal_device_removed_cb(self, udi):
+ """Handle notification from GIO that a device was removed."""
+ path = self._hal_devices.pop(udi, None)
+ if not path:
+ return
+
+ self._remove_medium(path)
+
+ def _hal_property_modified_cb(self, udi, count, changes):
+ """Handle notification from HAL that a property has changed."""
+ if 'volume.is_mounted' in [change[0] for change in changes]:
+ logging.debug('volume.is_mounted changed for UDI %s', udi)
+
+ bus = dbus.SystemBus()
+ device_object = bus.get_object(HAL_SERVICE_NAME, udi)
+ device = dbus.Interface(device_object, HAL_DEVICE_IFACE)
+
+ # We can get multiple notifications, so need to figure out
+ # the current state and ignore non-changes.
+ if device.GetProperty('volume.is_mounted'):
+ if udi not in self._hal_devices:
+ self._hal_try_device(device, udi)
+ else:
+ logging.debug('Ignoring duplicate notification')
+ else:
+ if udi in self._hal_devices:
+ self._hal_device_removed_cb(udi)
+ else:
+ logging.debug('Ignoring duplicate notification')
+ else:
+ logging.debug('Ignoring change for UDI %s', udi)
+
+ def _hal_get_icons_for_volume(self, device):
+ bus = dbus.SystemBus()
+ storage_udi = device.GetProperty('block.storage_device')
+ obj = bus.get_object(HAL_SERVICE_NAME, storage_udi)
+ storage_device = dbus.Interface(obj, HAL_DEVICE_IFACE)
+
+ storage_drive_type = storage_device.GetProperty('storage.drive_type')
+ bus_type = storage_device.GetProperty('storage.bus')
+ if storage_drive_type == 'sd_mmc':
+ return ['media-flash-sd', 'media-flash-sd-mmc', 'media']
+ elif bus_type == 'usb':
+ return ['media-flash-usb', 'media', 'media-disk']
+ else:
+ return ['media', 'media-disk', 'media-flash-usb']
+
def _add_medium(self, path, medium_name, icon_name):
"""Make storage medium selectable in the UI."""
# FIXME: update space information periodically or at least after
@@ -575,12 +690,25 @@ class BackupActivity(activity.Activity):
# total_space = stat[statvfs.F_BSIZE] * stat[statvfs.F_BLOCKS]
self._media_combo_model.append([icon_name, medium_name, path,
_('%s Free') % (format_size(free_space), )])
+ self._backup_button.set_sensitive(True)
+ if self._media_combo.get_active() == -1:
+ self._media_combo.set_active(0)
def _remove_medium(self, path):
"""Remove storage medium from UI."""
+ active = self._media_combo.get_active()
for position, row in enumerate(self._media_combo_model):
if path == row[self._MEDIA_COMBO_PATH_COLUMN]:
del self._media_combo_model[position]
+
+ if not len(self._media_combo_model):
+ self._backup_button.set_sensitive(False)
+ return
+
+ if position != active:
+ return
+
+ self._media_combo.set_active(max(0, position-1))
return
logging.warning("Asked to remove %s from UI, but didn't find it!",