diff --git a/resources/images/devices/tablet.png b/resources/images/devices/tablet.png new file mode 100644 index 0000000000..2084ccc954 Binary files /dev/null and b/resources/images/devices/tablet.png differ diff --git a/src/calibre/devices/interface.py b/src/calibre/devices/interface.py index 74236609ee..10d21ad97e 100644 --- a/src/calibre/devices/interface.py +++ b/src/calibre/devices/interface.py @@ -99,6 +99,12 @@ class DevicePlugin(Plugin): #: after the books lists have been loaded to get the driveinfo. SLOW_DRIVEINFO = False + #: If set to True, calibre will ask the user if they want to manage the + #: device with calibre, the first time it is detected. If you set this to + #: True you must implement :meth:`get_device_uid()` and + #: :meth:`ignore_connected_device()`. + ASK_TO_ALLOW_CONNECT = False + @classmethod def get_gui_name(cls): if hasattr(cls, 'gui_name'): @@ -587,6 +593,24 @@ class DevicePlugin(Plugin): ''' pass + def get_device_uid(self): + ''' + Must return a unique id for the currently connected device (this is + called immediately after a successful call to open()). You must + implement this method if you set ASK_TO_ALLOW_CONNECT = True + ''' + raise NotImplementedError() + + def ignore_connected_device(self, uid): + ''' + Should ignore the device identified by uid (the result of a call to + get_device_uid()) in the future. You must implement this method if you + set ASK_TO_ALLOW_CONNECT = True. Note that this function is called + immediately after open(), so if open() caches some state, the driver + should reset that state. + ''' + raise NotImplementedError() + # Dynamic control interface. # The following methods are probably called on the GUI thread. Any driver # that implements these methods must take pains to be thread safe, because diff --git a/src/calibre/devices/mtp/base.py b/src/calibre/devices/mtp/base.py index 4ada58ecef..893ceebf04 100644 --- a/src/calibre/devices/mtp/base.py +++ b/src/calibre/devices/mtp/base.py @@ -27,7 +27,7 @@ def synchronous(func): class MTPDeviceBase(DevicePlugin): name = 'MTP Device Interface' gui_name = _('MTP Device') - icon = I('devices/galaxy_s3.png') + icon = I('devices/tablet.png') description = _('Communicate with MTP devices') author = 'Kovid Goyal' version = (1, 0, 0) diff --git a/src/calibre/devices/mtp/driver.py b/src/calibre/devices/mtp/driver.py index fa37d33889..b4405695d7 100644 --- a/src/calibre/devices/mtp/driver.py +++ b/src/calibre/devices/mtp/driver.py @@ -38,6 +38,7 @@ class MTP_DEVICE(BASE): FORMATS = ['epub', 'azw3', 'mobi', 'pdf'] DEVICE_PLUGBOARD_NAME = 'MTP_DEVICE' SLOW_DRIVEINFO = True + ASK_TO_ALLOW_CONNECT = True def __init__(self, *args, **kwargs): BASE.__init__(self, *args, **kwargs) @@ -90,6 +91,17 @@ class MTP_DEVICE(BASE): self.current_device_defaults = self.device_defaults(device, self) + def get_device_uid(self): + return self.current_serial_num + + def ignore_connected_device(self, uid): + bl = self.prefs['blacklist'] + if uid not in bl: + bl.append(uid) + self.prefs['blacklist'] = bl + if self.is_mtp_device_connected: + self.eject() + # Device information {{{ def _update_drive_info(self, storage, location_code, name=None): import uuid diff --git a/src/calibre/devices/smart_device_app/driver.py b/src/calibre/devices/smart_device_app/driver.py index f655e2bc4b..558f382dca 100644 --- a/src/calibre/devices/smart_device_app/driver.py +++ b/src/calibre/devices/smart_device_app/driver.py @@ -34,7 +34,7 @@ from calibre.library import current_library_name from calibre.library.server import server_config as content_server_config from calibre.ptempfile import PersistentTemporaryFile from calibre.utils.ipc import eintr_retry_call -from calibre.utils.config import from_json, tweaks, ConfigProxy +from calibre.utils.config import from_json, tweaks from calibre.utils.date import isoformat, now from calibre.utils.filenames import ascii_filename as sanitize, shorten_components_to from calibre.utils.mdns import (publish as publish_zeroconf, unpublish as @@ -171,7 +171,7 @@ class SDBook(Book): class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): name = 'SmartDevice App Interface' - gui_name = _('Wireless Device') + gui_name = gui_name_base = _('Wireless Device') gui_name_template = '%s: %s' icon = I('devices/galaxy_s3.png') @@ -1056,6 +1056,7 @@ class SMART_DEVICE_APP(DeviceConfig, DevicePlugin): def eject(self): self._debug() self._close_device_socket() + self.gui_name = self.gui_name_base @synchronous('sync_lock') def post_yank_cleanup(self): diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 3146436030..69cafebdef 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -294,7 +294,9 @@ def question_dialog(parent, title, msg, det_msg='', show_copy_button=False, # Set skip_dialog_name to a unique name for this dialog # Set skip_dialog_msg to a message displayed to the user skip_dialog_name=None, skip_dialog_msg=_('Show this confirmation again'), - skip_dialog_skipped_value=True, skip_dialog_skip_precheck=True): + skip_dialog_skipped_value=True, skip_dialog_skip_precheck=True, + # Override icon (QIcon to be used as the icon for this dialog) + override_icon=None): from calibre.gui2.dialogs.message_box import MessageBox auto_skip = set(gprefs.get('questions_to_auto_skip', [])) @@ -302,7 +304,8 @@ def question_dialog(parent, title, msg, det_msg='', show_copy_button=False, return bool(skip_dialog_skipped_value) d = MessageBox(MessageBox.QUESTION, title, msg, det_msg, parent=parent, - show_copy_button=show_copy_button, default_yes=default_yes) + show_copy_button=show_copy_button, default_yes=default_yes, + q_icon=override_icon) if skip_dialog_name is not None and skip_dialog_msg: tc = d.toggle_checkbox diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 2db118c8e5..d2f5704c6d 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -20,7 +20,7 @@ from calibre.utils.ipc.job import BaseJob from calibre.devices.scanner import DeviceScanner from calibre.gui2 import (config, error_dialog, Dispatcher, dynamic, warning_dialog, info_dialog, choose_dir, FunctionDispatcher, - show_restart_warning) + show_restart_warning, gprefs, question_dialog) from calibre.ebooks.metadata import authors_to_string from calibre import preferred_encoding, prints, force_unicode, as_unicode from calibre.utils.filenames import ascii_filename @@ -122,7 +122,7 @@ def device_name_for_plugboards(device_class): class DeviceManager(Thread): # {{{ def __init__(self, connected_slot, job_manager, open_feedback_slot, - open_feedback_msg, sleep_time=2): + open_feedback_msg, allow_connect_slot, sleep_time=2): ''' :sleep_time: Time to sleep between device probes in secs ''' @@ -136,6 +136,7 @@ class DeviceManager(Thread): # {{{ x.MANAGES_DEVICE_PRESENCE] self.sleep_time = sleep_time self.connected_slot = connected_slot + self.allow_connect_slot = allow_connect_slot self.jobs = Queue.Queue(0) self.job_steps = Queue.Queue(0) self.keep_going = True @@ -193,6 +194,21 @@ class DeviceManager(Thread): # {{{ return False def after_device_connect(self, dev, device_kind): + allow_connect = True + try: + uid = dev.get_device_uid() + except NotImplementedError: + uid = None + asked = gprefs.get('ask_to_manage_device', []) + if (dev.ASK_TO_ALLOW_CONNECT and uid and uid not in asked): + if not self.allow_connect_slot(dev.get_gui_name(), dev.icon): + allow_connect = False + asked.append(uid) + gprefs.set('ask_to_manage_device', asked) + if not allow_connect: + dev.ignore_connected_device(uid) + return + self.connected_device = dev self.connected_device_kind = device_kind self.connected_slot(True, device_kind) @@ -829,12 +845,19 @@ class DeviceMixin(object): # {{{ self.device_error_dialog.setModal(Qt.NonModal) self.device_manager = DeviceManager(FunctionDispatcher(self.device_detected), self.job_manager, Dispatcher(self.status_bar.show_message), - Dispatcher(self.show_open_feedback)) + Dispatcher(self.show_open_feedback), + FunctionDispatcher(self.allow_connect)) self.device_manager.start() self.device_manager.devices_initialized.wait() if tweaks['auto_connect_to_folder']: self.connect_to_folder_named(tweaks['auto_connect_to_folder']) + def allow_connect(self, name, icon): + return question_dialog(self, _('Mange the %s?')%name, + _('Detected the %s. Do you want calibre to manage it?')% + name, show_copy_button=False, + override_icon=QIcon(icon)) + def debug_detection(self, done): self.debug_detection_callback = weakref.ref(done) self.device_manager.debug_detection(FunctionDispatcher(self.debug_detection_done)) diff --git a/src/calibre/gui2/device_drivers/mtp_config.py b/src/calibre/gui2/device_drivers/mtp_config.py index cd7e495225..9fd59ab124 100644 --- a/src/calibre/gui2/device_drivers/mtp_config.py +++ b/src/calibre/gui2/device_drivers/mtp_config.py @@ -415,6 +415,7 @@ class MTPConfig(QTabWidget): return True def commit(self): + self.device.prefs['blacklist'] = self.igntab.blacklist p = self.device.prefs.get(self.current_device_key, {}) if hasattr(self, 'formats'): @@ -440,7 +441,6 @@ class MTPConfig(QTabWidget): self.device.prefs[self.current_device_key] = p - self.device.prefs['blacklist'] = self.igntab.blacklist if __name__ == '__main__': from calibre.gui2 import Application