Пример #1
0
    def _on_dbus_name_appeared(self, _connection, name, owner):
        logging.info(f"{name} {owner}")

        self._manager = Manager()
        self._handlerids.append(self._manager.connect("transfer-started", self._on_transfer_started))
        self._handlerids.append(self._manager.connect("transfer-completed", self._on_transfer_completed))
        self._handlerids.append(self._manager.connect('session-removed', self._on_session_removed))

        self._register_agent()
Пример #2
0
    def _on_dbus_name_appeared(self, _connection, name, owner):
        logging.info("%s %s" % (name, owner))

        self._manager = Manager()
        self._manager.connect("transfer-started", self._on_transfer_started)
        self._manager.connect("transfer-completed",
                              self._on_transfer_completed)
        self._manager.connect('session-removed', self._on_session_removed)

        self._register_agent()
Пример #3
0
    def on_load(self) -> None:
        def on_reset(_action: str) -> None:
            self._notification = None
            self._config.reset('shared-path')
            logging.info('Reset share path')

        self._config = Config("org.blueman.transfer")

        share_path, invalid_share_path = self._make_share_path()

        if invalid_share_path:
            text = _('Configured directory for incoming files does not exist')
            secondary_text = _(
                'Please make sure that directory "<b>%s</b>" exists or '
                'configure it with blueman-services. Until then the default "%s" will be used'
            )
            self._notification = Notification(
                text,
                secondary_text % (self._config["shared-path"], share_path),
                icon_name='blueman',
                timeout=30000,
                actions=[('reset', 'Reset to default')],
                actions_cb=on_reset)
            self._notification.show()

        self._watch = Manager.watch_name_owner(self._on_dbus_name_appeared,
                                               self._on_dbus_name_vanished)
Пример #4
0
class TransferService(AppletPlugin):
    __author__ = "cschramm"
    __description__ = _("Provides OBEX file transfer capabilities")
    __icon__ = "blueman-send-symbolic"

    _silent_transfers = 0
    _normal_transfers = 0

    _manager = None
    _agent = None
    _watch = None
    _notification = None
    _handlerids: List[int] = []

    def on_load(self) -> None:
        def on_reset(_action: str) -> None:
            self._notification = None
            self._config.reset('shared-path')
            logging.info('Reset share path')

        self._config = Config("org.blueman.transfer")

        share_path, invalid_share_path = self._make_share_path()

        if invalid_share_path:
            text = _('Configured directory for incoming files does not exist')
            secondary_text = _(
                'Please make sure that directory "<b>%s</b>" exists or '
                'configure it with blueman-services. Until then the default "%s" will be used'
            )
            self._notification = Notification(
                text,
                secondary_text % (self._config["shared-path"], share_path),
                icon_name='blueman',
                timeout=30000,
                actions=[('reset', 'Reset to default')],
                actions_cb=on_reset)
            self._notification.show()

        self._watch = Manager.watch_name_owner(self._on_dbus_name_appeared,
                                               self._on_dbus_name_vanished)

    def on_unload(self) -> None:
        if self._watch:
            Gio.bus_unwatch_name(self._watch)

        self._unregister_agent()

    def _make_share_path(self) -> Tuple[str, bool]:
        config_path = self._config["shared-path"]
        default_path = GLib.get_user_special_dir(
            GLib.UserDirectory.DIRECTORY_DOWNLOAD)
        path = None
        error = False

        if config_path == '':
            path = default_path
        elif not os.path.isdir(config_path):
            path = default_path
            error = True
            logging.warning('Invalid shared-path %s' % config_path)
        else:
            path = config_path

        if not path:
            path = os.path.expanduser("~")
            logging.warning('Failed to get Download dir from XDG')

        # We used to always store the full path which caused problems
        if config_path == default_path:
            logging.info('Reset stored path, identical to default path.')
            self._config["shared-path"] = ''

        return path, error

    def _register_agent(self) -> None:
        if not self._agent:
            self._agent = Agent(self.parent)
        self._agent.register_at_manager()

    def _unregister_agent(self) -> None:
        if self._agent:
            self._agent.unregister_from_manager()
            self._agent.unregister()
            self._agent = None

    def _on_dbus_name_appeared(self, _connection: Gio.DBusConnection,
                               name: str, owner: str) -> None:
        logging.info(f"{name} {owner}")

        self._manager = Manager()
        self._handlerids.append(
            self._manager.connect("transfer-started",
                                  self._on_transfer_started))
        self._handlerids.append(
            self._manager.connect("transfer-completed",
                                  self._on_transfer_completed))
        self._handlerids.append(
            self._manager.connect('session-removed', self._on_session_removed))

        self._register_agent()

    def _on_dbus_name_vanished(self, _connection: Gio.DBusConnection,
                               name: str) -> None:
        logging.info(f"{name} not running or was stopped")

        if self._manager:
            for sigid in self._handlerids:
                self._manager.disconnect(sigid)
            self._manager = None
            self._handlerids = []

        if self._agent:
            self._agent.unregister()
            self._agent = None

    def _on_transfer_started(self, _manager: Manager,
                             transfer_path: str) -> None:
        if not self._agent or transfer_path not in self._agent.transfers:
            # This is not an incoming transfer we authorized
            return

        size = self._agent.transfers[transfer_path]['size']
        assert size is not None
        if size > 350000:
            self._normal_transfers += 1
        else:
            self._silent_transfers += 1

    def _add_open(self, n: NotificationType, name: str, path: str) -> None:
        if n.actions_supported:
            logging.info("adding action")

            def on_open(_action: str) -> None:
                self._notification = None
                logging.info("open")
                launch("xdg-open", paths=[path], system=True)

            n.add_action("open", name, on_open)

    def _on_transfer_completed(self, _manager: Manager, transfer_path: str,
                               success: bool) -> None:
        if not self._agent or transfer_path not in self._agent.transfers:
            logging.info(
                "This is probably not an incoming transfer we authorized")
            return

        attributes = self._agent.transfers[transfer_path]

        src = attributes['path']
        dest_dir, ignored = self._make_share_path()
        filename = os.path.basename(src)

        dest = os.path.join(dest_dir, filename)
        if os.path.exists(dest):
            now = datetime.now()
            filename = f"{now.strftime('%Y%m%d%H%M%S')}_{filename}"
            logging.info(f"Destination file exists, renaming to: {filename}")

        try:
            shutil.move(src, dest)
        except (OSError, PermissionError):
            logging.error("Failed to move files", exc_info=True)
            success = False

        if success:
            self._notification = Notification(
                _("File received"),
                _("File %(0)s from %(1)s successfully received") % {
                    "0": "<b>" + escape(filename) + "</b>",
                    "1": "<b>" + escape(attributes['name']) + "</b>"
                },
                icon_name="blueman")
            self._add_open(self._notification, _("Open"), dest)
            self._notification.show()
        elif not success:
            n = Notification(
                _("Transfer failed"),
                _("Transfer of file %(0)s failed") % {
                    "0": "<b>" + escape(filename) + "</b>",
                    "1": "<b>" + escape(attributes['name']) + "</b>"
                },
                icon_name="blueman")
            n.show()
            assert attributes['size'] is not None
            if attributes['size'] > 350000:
                self._normal_transfers -= 1
            else:
                self._silent_transfers -= 1

        del self._agent.transfers[transfer_path]

    def _on_session_removed(self, _manager: Manager,
                            _session_path: str) -> None:
        if self._silent_transfers == 0:
            return

        share_path, ignored = self._make_share_path()
        if self._normal_transfers == 0:
            self._notification = Notification(
                _("Files received"),
                ngettext("Received %(files)d file in the background",
                         "Received %(files)d files in the background",
                         self._silent_transfers) %
                {"files": self._silent_transfers},
                icon_name="blueman")

            self._add_open(self._notification, _("Open Location"), share_path)
            self._notification.show()
        else:
            self._notification = Notification(
                _("Files received"),
                ngettext("Received %(files)d more file in the background",
                         "Received %(files)d more files in the background",
                         self._silent_transfers) %
                {"files": self._silent_transfers},
                icon_name="blueman")
            self._add_open(self._notification, _("Open Location"), share_path)
            self._notification.show()
Пример #5
0
    def __init__(self, device, adapter_path, files):
        super().__init__(
            title=_("Bluetooth File Transfer"),
            name="BluemanSendTo",
            icon_name="blueman",
            border_width=5,
            default_width=400,
            window_position=Gtk.WindowPosition.CENTER,
            type_hint=Gdk.WindowTypeHint.DIALOG
        )

        self.b_cancel = self.add_button("_Stop", Gtk.ResponseType.CLOSE)
        self.b_cancel.props.receives_default = True
        self.b_cancel.props.use_underline = True
        self.b_cancel.connect("clicked", self.on_cancel)

        self.Builder = Gtk.Builder(translation_domain="blueman")
        bind_textdomain_codeset("blueman", "UTF-8")
        self.Builder.add_from_file(UI_PATH + "/send-dialog.ui")

        grid = self.Builder.get_object("sendto")
        content_area = self.get_content_area()
        content_area.add(grid)

        self.l_dest = self.Builder.get_object("l_dest")
        self.l_file = self.Builder.get_object("l_file")

        self.pb = self.Builder.get_object("pb")
        self.pb.props.text = _("Connecting")

        self.device = device
        self.adapter = Adapter(adapter_path)
        self.manager = Manager()
        self.files: List[Gio.File] = []
        self.num_files = 0
        self.object_push = None
        self.transfer = None

        self.total_bytes = 0
        self.total_transferred = 0

        self._last_bytes = 0
        self._last_update = 0.0

        self.error_dialog = None
        self.cancelling = False

        # bytes transferred on a current transfer
        self.transferred = 0

        self.speed = SpeedCalc(6)

        for file_name in files:
            parsed_file = Gio.File.parse_name(file_name)

            if not parsed_file.query_exists():
                logging.info("Skipping non existing file %s" % parsed_file.get_path())
                continue

            file_info = parsed_file.query_info("standard::*", Gio.FileQueryInfoFlags.NONE)

            if file_info.get_file_type() == Gio.FileType.DIRECTORY:
                logging.info("Skipping directory %s" % parsed_file.get_path())
                continue

            self.files.append(parsed_file)
            self.num_files += 1
            self.total_bytes += file_info.get_size()

        if len(self.files) == 0:
            self.emit("result", False)

        try:
            self.client = Client()
            self.manager.connect_signal('session-added', self.on_session_added)
            self.manager.connect_signal('session-removed', self.on_session_removed)
        except GLib.Error as e:
            if 'StartServiceByName' in e.message:
                logging.debug(e.message)
                d = ErrorDialog(_("obexd not available"), _("Failed to autostart obex service. Make sure the obex "
                                                            "daemon is running"), parent=self.get_toplevel())
                d.run()
                d.destroy()
                self.emit("result", False)
            else:
                # Fail on anything else
                raise

        self.l_file.props.label = self.files[-1].get_basename()

        self.client.connect('session-failed', self.on_session_failed)

        logging.info("Sending to %s" % device['Address'])
        self.l_dest.props.label = device['Alias']

        # Stop discovery if discovering and let adapter settle for a second
        if self.adapter["Discovering"]:
            self.adapter.stop_discovery()
            time.sleep(1)

        self.create_session()

        self.show()
Пример #6
0
class Sender(Gtk.Dialog):
    __gsignals__: GSignals = {
        'result': (GObject.SignalFlags.RUN_FIRST, None, (GObject.TYPE_BOOLEAN,)),
    }

    def __init__(self, device, adapter_path, files):
        super().__init__(
            title=_("Bluetooth File Transfer"),
            name="BluemanSendTo",
            icon_name="blueman",
            border_width=5,
            default_width=400,
            window_position=Gtk.WindowPosition.CENTER,
            type_hint=Gdk.WindowTypeHint.DIALOG
        )

        self.b_cancel = self.add_button("_Stop", Gtk.ResponseType.CLOSE)
        self.b_cancel.props.receives_default = True
        self.b_cancel.props.use_underline = True
        self.b_cancel.connect("clicked", self.on_cancel)

        self.Builder = Gtk.Builder(translation_domain="blueman")
        bind_textdomain_codeset("blueman", "UTF-8")
        self.Builder.add_from_file(UI_PATH + "/send-dialog.ui")

        grid = self.Builder.get_object("sendto")
        content_area = self.get_content_area()
        content_area.add(grid)

        self.l_dest = self.Builder.get_object("l_dest")
        self.l_file = self.Builder.get_object("l_file")

        self.pb = self.Builder.get_object("pb")
        self.pb.props.text = _("Connecting")

        self.device = device
        self.adapter = Adapter(adapter_path)
        self.manager = Manager()
        self.files: List[Gio.File] = []
        self.num_files = 0
        self.object_push = None
        self.transfer = None

        self.total_bytes = 0
        self.total_transferred = 0

        self._last_bytes = 0
        self._last_update = 0.0

        self.error_dialog = None
        self.cancelling = False

        # bytes transferred on a current transfer
        self.transferred = 0

        self.speed = SpeedCalc(6)

        for file_name in files:
            parsed_file = Gio.File.parse_name(file_name)

            if not parsed_file.query_exists():
                logging.info("Skipping non existing file %s" % parsed_file.get_path())
                continue

            file_info = parsed_file.query_info("standard::*", Gio.FileQueryInfoFlags.NONE)

            if file_info.get_file_type() == Gio.FileType.DIRECTORY:
                logging.info("Skipping directory %s" % parsed_file.get_path())
                continue

            self.files.append(parsed_file)
            self.num_files += 1
            self.total_bytes += file_info.get_size()

        if len(self.files) == 0:
            self.emit("result", False)

        try:
            self.client = Client()
            self.manager.connect_signal('session-added', self.on_session_added)
            self.manager.connect_signal('session-removed', self.on_session_removed)
        except GLib.Error as e:
            if 'StartServiceByName' in e.message:
                logging.debug(e.message)
                d = ErrorDialog(_("obexd not available"), _("Failed to autostart obex service. Make sure the obex "
                                                            "daemon is running"), parent=self.get_toplevel())
                d.run()
                d.destroy()
                self.emit("result", False)
            else:
                # Fail on anything else
                raise

        self.l_file.props.label = self.files[-1].get_basename()

        self.client.connect('session-failed', self.on_session_failed)

        logging.info("Sending to %s" % device['Address'])
        self.l_dest.props.label = device['Alias']

        # Stop discovery if discovering and let adapter settle for a second
        if self.adapter["Discovering"]:
            self.adapter.stop_discovery()
            time.sleep(1)

        self.create_session()

        self.show()

    def create_session(self):
        self.client.create_session(self.device['Address'], self.adapter["Address"])

    def on_cancel(self, button):
        self.pb.props.text = _("Cancelling")
        if button:
            button.props.sensitive = False

        if self.object_push:
            self.client.remove_session(self.object_push.get_session_path())

        self.emit("result", False)

    def on_transfer_started(self, _object_push, transfer_path, filename):
        if self.total_transferred == 0:
            self.pb.props.text = _("Sending File") + (" %(0)s/%(1)s (%(2).2f %(3)s/s) " + _("ETA:") + " %(4)s") % {
                "1": self.num_files,
                "0": (self.num_files - len(self.files) + 1),
                "2": 0.0,
                "3": "B/s",
                "4": "∞"}

        self.l_file.props.label = filename
        self._last_bytes = 0
        self.transferred = 0

        self.transfer = Transfer(transfer_path)
        self.transfer.connect("error", self.on_transfer_error)
        self.transfer.connect("progress", self.on_transfer_progress)
        self.transfer.connect("completed", self.on_transfer_completed)

    def on_transfer_failed(self, _object_push, error):
        self.on_transfer_error(None, str(error))

    def on_transfer_progress(self, _transfer, progress):
        self.transferred = progress
        if self._last_bytes == 0:
            self.total_transferred += progress
        else:
            self.total_transferred += (progress - self._last_bytes)

        self._last_bytes = progress

        tm = time.time()
        if tm - self._last_update > 0.5:
            spd = self.speed.calc(self.total_transferred)
            (size, units) = format_bytes(spd)
            try:
                x = ((self.total_bytes - self.total_transferred) / spd) + 1
                if x > 60:
                    x /= 60
                    eta = ngettext("%.0f Minute", "%.0f Minutes", round(x)) % x
                else:
                    eta = ngettext("%.0f Second", "%.0f Seconds", round(x)) % x
            except ZeroDivisionError:
                eta = "∞"

            self.pb.props.text = _("Sending File") + (" %(0)s/%(1)s (%(2).2f %(3)s/s) " + _("ETA:") + " %(4)s") % {
                "1": self.num_files,
                "0": (self.num_files - len(self.files) + 1),
                "2": size,
                "3": units,
                "4": eta}
            self._last_update = tm

        self.pb.props.fraction = float(self.total_transferred) / self.total_bytes

    def on_transfer_completed(self, _transfer):
        del self.files[-1]
        self.transfer = None

        self.process_queue()

    def process_queue(self):
        if len(self.files) > 0:
            self.send_file(self.files[-1].get_path())
        else:
            self.emit("result", True)

    def send_file(self, file_path):
        logging.info(file_path)
        if self.object_push:
            self.object_push.send_file(file_path)

    def on_transfer_error(self, _transfer, msg=""):
        if not self.error_dialog:
            self.speed.reset()
            d = ErrorDialog(msg, _("Error occurred while sending file %s") % self.files[-1].get_basename(),
                            modal=True, icon_name="blueman", parent=self.get_toplevel(), buttons=[])

            if len(self.files) > 1:
                d.add_button(_("Skip"), Gtk.ResponseType.NO)
            d.add_button(_("Retry"), Gtk.ResponseType.YES)
            d.add_button("_Cancel", Gtk.ResponseType.CANCEL)

            if self.object_push:
                self.client.remove_session(self.object_push.get_object_path())

            def on_response(dialog, resp):
                dialog.destroy()
                self.error_dialog = None

                if resp == Gtk.ResponseType.CANCEL:
                    self.on_cancel(None)
                elif resp == Gtk.ResponseType.NO:
                    finfo = self.files[-1].query_info('standard::*', Gio.FileQueryInfoFlags.NONE)
                    self.total_bytes -= finfo.get_size()
                    self.total_transferred -= self.transferred
                    self.transferred = 0
                    del self.files[-1]
                    if not self.object_push:
                        self.create_session()
                    self.process_queue()
                elif resp == Gtk.ResponseType.YES:
                    self.total_transferred -= self.transferred
                    self.transferred = 0
                    if not self.object_push:
                        self.create_session()

                    self.process_queue()
                else:
                    self.on_cancel(None)

            d.connect("response", on_response)
            d.show()
            self.error_dialog = d

    def on_session_added(self, _manager, session_path):
        self.object_push = ObjectPush(session_path)
        self.object_push.connect("transfer-started", self.on_transfer_started)
        self.object_push.connect("transfer-failed", self.on_transfer_failed)
        self.process_queue()

    def on_session_removed(self, _manager, session_path):
        logging.debug('Session removed: %s' % session_path)
        if self.object_push:
            self.object_push.disconnect_by_func(self.on_transfer_started)
            self.object_push.disconnect_by_func(self.on_transfer_failed)
            self.object_push = None

    def on_session_failed(self, _client, msg):
        d = ErrorDialog(_("Error occurred"), msg.reason.split(None, 1)[1], icon_name="blueman",
                        parent=self.get_toplevel())

        d.run()
        d.destroy()
        self.emit("result", False)
Пример #7
0
class TransferService(AppletPlugin):
    __author__ = "cschramm"
    __description__ = _("Provides OBEX file transfer capabilities")
    __icon__ = "blueman-send-file"

    _silent_transfers = 0
    _normal_transfers = 0

    _manager = None
    _agent = None
    _watch = None
    _notification = None

    def on_load(self):
        def on_reset(*_args):
            self._notification = None
            self._config.reset('shared-path')
            logging.info('Reset share path')

        self._config = Config("org.blueman.transfer")

        share_path, invalid_share_path = self._make_share_path()

        if invalid_share_path:
            text = _('Configured directory for incoming files does not exist')
            secondary_text = _(
                'Please make sure that directory "<b>%s</b>" exists or '
                'configure it with blueman-services. Until then the default "%s" will be used'
            )
            self._notification = Notification(
                text,
                secondary_text % (self._config["shared-path"], share_path),
                icon_name='blueman',
                timeout=30000,
                actions=[['reset', 'Reset to default', 'blueman']],
                actions_cb=on_reset)
            self._notification.show()

        self._watch = Manager.watch_name_owner(self._on_dbus_name_appeared,
                                               self._on_dbus_name_vanished)

    def on_unload(self):
        if self._watch:
            Gio.bus_unwatch_name(self._watch)

        self._unregister_agent()

    def _make_share_path(self):
        config_path = self._config["shared-path"]
        default_path = GLib.get_user_special_dir(
            GLib.UserDirectory.DIRECTORY_DOWNLOAD)
        path = None
        error = False

        if config_path == '':
            path = default_path
        elif not os.path.isdir(config_path):
            path = default_path
            error = True
            logging.warning('Invalid shared-path %s' % config_path)
        else:
            path = config_path

        if not path:
            path = os.path.expanduser("~")
            logging.warning('Failed to get Download dir from XDG')

        # We used to always store the full path which caused problems
        if config_path == default_path:
            logging.info('Reset stored path, identical to default path.')
            self._config["shared-path"] = ''

        return path, error

    def _register_agent(self):
        if not self._agent:
            self._agent = Agent(self.parent)
        self._agent.register_at_manager()

    def _unregister_agent(self):
        if self._agent:
            self._agent.unregister_from_manager()
            self._agent.unregister()
            self._agent = None

    def _on_dbus_name_appeared(self, _connection, name, owner):
        logging.info("%s %s" % (name, owner))

        self._manager = Manager()
        self._manager.connect("transfer-started", self._on_transfer_started)
        self._manager.connect("transfer-completed",
                              self._on_transfer_completed)
        self._manager.connect('session-removed', self._on_session_removed)

        self._register_agent()

    def _on_dbus_name_vanished(self, _connection, name):
        logging.info("%s not running or was stopped" % name)

        if self._manager:
            self._manager.disconnect_by_func(self._on_transfer_started)
            self._manager.disconnect_by_func(self._on_transfer_completed)
            self._manager.disconnect_by_func(self._on_session_removed)
            self._manager = None

        if self._agent:
            self._agent.unregister()
            self._agent = None

    def _on_transfer_started(self, _manager, transfer_path):
        if not self._agent or transfer_path not in self._agent.transfers:
            # This is not an incoming transfer we authorized
            return

        if self._agent.transfers[transfer_path]['size'] > 350000:
            self._normal_transfers += 1
        else:
            self._silent_transfers += 1

    def _add_open(self, n, name, path):
        if n.actions_supported:
            logging.info("adding action")

            def on_open(*_args):
                self._notification = None
                logging.info("open")
                launch("xdg-open", [path], True)

            n.add_action("open", name, on_open)

    @property
    def _notify_kwargs(self):
        kwargs = {"icon_name": "blueman"}
        try:
            kwargs["pos_hint"] = self.parent.Plugins.StatusIcon.geometry
        except AttributeError:
            logging.error("No statusicon found")

        return kwargs

    def _on_transfer_completed(self, _manager, transfer_path, success):
        if not self._agent or transfer_path not in self._agent.transfers:
            logging.info(
                "This is probably not an incoming transfer we authorized")
            return

        attributes = self._agent.transfers[transfer_path]

        src = attributes['path']
        dest_dir, ignored = self._make_share_path()
        filename = os.path.basename(src)

        dest = os.path.join(dest_dir, filename)
        if os.path.exists(dest):
            now = datetime.now()
            filename = "%s_%s" % (now.strftime("%Y%m%d%H%M%S"), filename)
            logging.info("Destination file exists, renaming to: %s" % filename)

        try:
            shutil.move(src, dest)
        except (OSError, PermissionError):
            logging.error("Failed to move files", exc_info=True)
            success = False

        if success:
            self._notification = Notification(
                _("File received"),
                _("File %(0)s from %(1)s successfully received") % {
                    "0": "<b>" + escape(filename) + "</b>",
                    "1": "<b>" + escape(attributes['name']) + "</b>"
                }, **self._notify_kwargs)
            self._add_open(self._notification, "Open", dest)
            self._notification.show()
        elif not success:
            n = Notification(
                _("Transfer failed"),
                _("Transfer of file %(0)s failed") % {
                    "0": "<b>" + escape(filename) + "</b>",
                    "1": "<b>" + escape(attributes['name']) + "</b>"
                }, **self._notify_kwargs)
            n.show()
            if attributes['size'] > 350000:
                self._normal_transfers -= 1
            else:
                self._silent_transfers -= 1

        del self._agent.transfers[transfer_path]

    def _on_session_removed(self, _manager, _session_path):
        if self._silent_transfers == 0:
            return

        share_path, ignored = self._make_share_path()
        if self._normal_transfers == 0:
            self._notification = Notification(
                _("Files received"),
                ngettext("Received %d file in the background",
                         "Received %d files in the background",
                         self._silent_transfers) % self._silent_transfers,
                **self._notify_kwargs)

            self._add_open(self._notification, "Open Location", share_path)
            self._notification.show()
        else:
            self._notification = Notification(
                _("Files received"),
                ngettext("Received %d more file in the background",
                         "Received %d more files in the background",
                         self._silent_transfers) % self._silent_transfers,
                **self._notify_kwargs)
            self._add_open(self._notification, "Open Location", share_path)
            self._notification.show()