mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-08-07 09:01:38 -04:00
OS X/Linux: Show an informational popup message when an Android device is plugged in that needs the user to tap Allow for the connection to work.
This commit is contained in:
parent
c4931a2fcd
commit
6a82169373
@ -1,4 +1,4 @@
|
|||||||
__license__ = 'GPL v3'
|
__license__ = 'GPL v3'
|
||||||
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
__copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||||
"""
|
"""
|
||||||
Defines the errors that the device drivers generate.
|
Defines the errors that the device drivers generate.
|
||||||
@ -18,8 +18,11 @@ class TimeoutError(ProtocolError):
|
|||||||
""" There was a timeout during communication """
|
""" There was a timeout during communication """
|
||||||
|
|
||||||
def __init__(self, func_name):
|
def __init__(self, func_name):
|
||||||
ProtocolError.__init__(self,
|
ProtocolError.__init__(
|
||||||
"There was a timeout while communicating with the device in function: " +func_name)
|
self,
|
||||||
|
"There was a timeout while communicating with the device in function: " +
|
||||||
|
func_name
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceError(ProtocolError):
|
class DeviceError(ProtocolError):
|
||||||
@ -54,7 +57,17 @@ class OpenFeedback(DeviceError):
|
|||||||
If you need to show the user a custom dialog, instead of just
|
If you need to show the user a custom dialog, instead of just
|
||||||
displaying the feedback_msg, create and return it here.
|
displaying the feedback_msg, create and return it here.
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class OpenActionNeeded(DeviceError):
|
||||||
|
|
||||||
|
def __init__(self, device_name, msg, only_once_id):
|
||||||
|
self.device_name, self.feedback_msg, self.only_once_id = device_name, msg, only_once_id
|
||||||
|
DeviceError.__init__(self, msg)
|
||||||
|
|
||||||
|
def custom_dialog(self, parent):
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
class InitialConnectionError(OpenFeedback):
|
class InitialConnectionError(OpenFeedback):
|
||||||
@ -76,8 +89,10 @@ class DeviceBusy(ProtocolError):
|
|||||||
""" Raised when device is busy """
|
""" Raised when device is busy """
|
||||||
|
|
||||||
def __init__(self, uerr=""):
|
def __init__(self, uerr=""):
|
||||||
ProtocolError.__init__(self, "Device is in use by another application:"
|
ProtocolError.__init__(
|
||||||
"\nUnderlying error:" + str(uerr))
|
self, "Device is in use by another application:"
|
||||||
|
"\nUnderlying error:" + str(uerr)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DeviceLocked(ProtocolError):
|
class DeviceLocked(ProtocolError):
|
||||||
@ -138,4 +153,3 @@ class BlacklistedDevice(OpenFailed):
|
|||||||
blacklisted by the user. Only used in drivers that manage device presence,
|
blacklisted by the user. Only used in drivers that manage device presence,
|
||||||
like the MTP driver. '''
|
like the MTP driver. '''
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from functools import partial
|
|||||||
from calibre import prints, as_unicode
|
from calibre import prints, as_unicode
|
||||||
from calibre.constants import plugins, islinux, isosx
|
from calibre.constants import plugins, islinux, isosx
|
||||||
from calibre.ptempfile import SpooledTemporaryFile
|
from calibre.ptempfile import SpooledTemporaryFile
|
||||||
from calibre.devices.errors import OpenFailed, DeviceError, BlacklistedDevice
|
from calibre.devices.errors import OpenFailed, DeviceError, BlacklistedDevice, OpenActionNeeded
|
||||||
from calibre.devices.mtp.base import MTPDeviceBase, synchronous, debug
|
from calibre.devices.mtp.base import MTPDeviceBase, synchronous, debug
|
||||||
|
|
||||||
MTPDevice = namedtuple('MTPDevice', 'busnum devnum vendor_id product_id '
|
MTPDevice = namedtuple('MTPDevice', 'busnum devnum vendor_id product_id '
|
||||||
@ -28,6 +28,7 @@ def fingerprint(d):
|
|||||||
return MTPDevice(d.busnum, d.devnum, d.vendor_id, d.product_id, d.bcd,
|
return MTPDevice(d.busnum, d.devnum, d.vendor_id, d.product_id, d.bcd,
|
||||||
d.serial, d.manufacturer, d.product)
|
d.serial, d.manufacturer, d.product)
|
||||||
|
|
||||||
|
|
||||||
APPLE = 0x05ac
|
APPLE = 0x05ac
|
||||||
|
|
||||||
|
|
||||||
@ -211,6 +212,7 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
@synchronous
|
@synchronous
|
||||||
def open(self, connected_device, library_uuid):
|
def open(self, connected_device, library_uuid):
|
||||||
self.dev = self._filesystem_cache = None
|
self.dev = self._filesystem_cache = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.dev = self.create_device(connected_device)
|
self.dev = self.create_device(connected_device)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@ -218,7 +220,23 @@ class MTP_DEVICE(MTPDeviceBase):
|
|||||||
raise OpenFailed('Failed to open %s: Error: %s'%(
|
raise OpenFailed('Failed to open %s: Error: %s'%(
|
||||||
connected_device, as_unicode(e)))
|
connected_device, as_unicode(e)))
|
||||||
|
|
||||||
storage = sorted(self.dev.storage_info, key=operator.itemgetter('id'))
|
try:
|
||||||
|
storage = sorted(self.dev.storage_info, key=operator.itemgetter('id'))
|
||||||
|
except self.libmtp.MTPError as e:
|
||||||
|
if "The device has no storage information." in str(e):
|
||||||
|
# This happens on newer Android devices while waiting for
|
||||||
|
# the user to allow access. Apparently what happens is
|
||||||
|
# that when the user clicks allow, the device disconnects
|
||||||
|
# and re-connects as a new device.
|
||||||
|
raise OpenActionNeeded(self.dev.friendly_name, _(
|
||||||
|
'The device {0} is not allowing connections.'
|
||||||
|
' Unlock the screen on the {0}, tap "Allow" on any connection popup message you see,'
|
||||||
|
' then either wait a minute or restart calibre. You might'
|
||||||
|
' also have to change the mode of the USB connection on the {0}'
|
||||||
|
' to "Media Transfer mode (MTP)" or similar.'
|
||||||
|
).format(self.dev.friendly_name), (self.dev.friendly_name, self.dev.serial_number))
|
||||||
|
raise
|
||||||
|
|
||||||
storage = [x for x in storage if x.get('rw', False)]
|
storage = [x for x in storage if x.get('rw', False)]
|
||||||
if not storage:
|
if not storage:
|
||||||
self.blacklisted_devices.add(connected_device)
|
self.blacklisted_devices.add(connected_device)
|
||||||
@ -432,6 +450,7 @@ def develop():
|
|||||||
finally:
|
finally:
|
||||||
dev.shutdown()
|
dev.shutdown()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
dev = MTP_DEVICE(None)
|
dev = MTP_DEVICE(None)
|
||||||
dev.startup()
|
dev.startup()
|
||||||
@ -442,4 +461,3 @@ if __name__ == '__main__':
|
|||||||
dev.debug_managed_device_detection(devs, sys.stdout)
|
dev.debug_managed_device_detection(devs, sys.stdout)
|
||||||
dev.set_debug_level(dev.LIBMTP_DEBUG_ALL)
|
dev.set_debug_level(dev.LIBMTP_DEBUG_ALL)
|
||||||
dev.shutdown()
|
dev.shutdown()
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ from PyQt5.Qt import (
|
|||||||
from calibre.customize.ui import (available_input_formats, available_output_formats,
|
from calibre.customize.ui import (available_input_formats, available_output_formats,
|
||||||
device_plugins, disabled_device_plugins)
|
device_plugins, disabled_device_plugins)
|
||||||
from calibre.devices.interface import DevicePlugin, currently_connected_device
|
from calibre.devices.interface import DevicePlugin, currently_connected_device
|
||||||
from calibre.devices.errors import (UserFeedback, OpenFeedback, OpenFailed,
|
from calibre.devices.errors import (UserFeedback, OpenFeedback, OpenFailed, OpenActionNeeded,
|
||||||
InitialConnectionError)
|
InitialConnectionError)
|
||||||
from calibre.ebooks.covers import cprefs, override_prefs, scale_cover, generate_cover
|
from calibre.ebooks.covers import cprefs, override_prefs, scale_cover, generate_cover
|
||||||
from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog
|
from calibre.gui2.dialogs.choose_format_device import ChooseFormatDeviceDialog
|
||||||
@ -165,6 +165,7 @@ class DeviceManager(Thread): # {{{
|
|||||||
self.ejected_devices = set([])
|
self.ejected_devices = set([])
|
||||||
self.mount_connection_requests = Queue.Queue(0)
|
self.mount_connection_requests = Queue.Queue(0)
|
||||||
self.open_feedback_slot = open_feedback_slot
|
self.open_feedback_slot = open_feedback_slot
|
||||||
|
self.open_feedback_only_once_seen = set()
|
||||||
self.after_callback_feedback_slot = after_callback_feedback_slot
|
self.after_callback_feedback_slot = after_callback_feedback_slot
|
||||||
self.open_feedback_msg = open_feedback_msg
|
self.open_feedback_msg = open_feedback_msg
|
||||||
self._device_information = None
|
self._device_information = None
|
||||||
@ -296,6 +297,10 @@ class DeviceManager(Thread): # {{{
|
|||||||
except BlacklistedDevice as e:
|
except BlacklistedDevice as e:
|
||||||
prints('Ignoring blacklisted device: %s'%
|
prints('Ignoring blacklisted device: %s'%
|
||||||
as_unicode(e))
|
as_unicode(e))
|
||||||
|
except OpenActionNeeded as e:
|
||||||
|
if e.only_once_id not in self.open_feedback_only_once_seen:
|
||||||
|
self.open_feedback_only_once_seen.add(e.only_once_id)
|
||||||
|
self.open_feedback_msg(e.device_name, e)
|
||||||
except:
|
except:
|
||||||
prints('Error while trying to open %s (Driver: %s)'%
|
prints('Error while trying to open %s (Driver: %s)'%
|
||||||
(cd, dev))
|
(cd, dev))
|
||||||
@ -875,6 +880,7 @@ class DeviceSignals(QObject): # {{{
|
|||||||
#: otherwise a disconnection.
|
#: otherwise a disconnection.
|
||||||
device_connection_changed = pyqtSignal(object)
|
device_connection_changed = pyqtSignal(object)
|
||||||
|
|
||||||
|
|
||||||
device_signals = DeviceSignals()
|
device_signals = DeviceSignals()
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user