diff options
author | Sascha 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) |
commit | 9d07abf0cf03f24f59ba3456b4b6acbaa82888de (patch) | |
tree | b66cabae43b75cd8127ee10debb7e905da506130 | |
parent | d135159e5eb43ad6b4e456eb4fee31579d8806c9 (diff) |
add HAL support, some fixes
-rw-r--r-- | activity/activity.info | 2 | ||||
-rw-r--r-- | backup.py | 152 |
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 @@ -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!", |