Improved multi-device handling
Ask user which device to use when there are many. If there is only one skip the question. We used to just pick the first one we found; user had no way to switch. We have to handle querying from the non-GUI thread.
This commit is contained in:
@@ -228,6 +228,7 @@ class BasePlugin(PrintError):
|
||||
pass
|
||||
|
||||
Device = namedtuple("Device", "path id_ product_key")
|
||||
DeviceInfo = namedtuple("DeviceInfo", "device description initialized")
|
||||
|
||||
class DeviceMgr(PrintError):
|
||||
'''Manages hardware clients. A client communicates over a hardware
|
||||
@@ -328,10 +329,6 @@ class DeviceMgr(PrintError):
|
||||
def paired_wallets(self):
|
||||
return list(self.wallets.keys())
|
||||
|
||||
def unpaired_devices(self, handler):
|
||||
devices = self.scan_devices(handler)
|
||||
return [dev for dev in devices if not self.wallet_by_id(dev.id_)]
|
||||
|
||||
def client_lookup(self, id_):
|
||||
with self.lock:
|
||||
for client, (path, client_id) in self.clients.items():
|
||||
@@ -362,28 +359,56 @@ class DeviceMgr(PrintError):
|
||||
|
||||
if force_pair:
|
||||
first_address, derivation = wallet.first_address()
|
||||
# Wallets don't have a first address in the install wizard
|
||||
# until account creation
|
||||
if not first_address:
|
||||
self.print_error("no first address for ", wallet)
|
||||
return None
|
||||
assert first_address
|
||||
|
||||
# The wallet has not been previously paired, so get the
|
||||
# first address of all unpaired clients and compare.
|
||||
for device in devices:
|
||||
# Skip already-paired devices
|
||||
if self.wallet_by_id(device.id_):
|
||||
continue
|
||||
client = self.create_client(device, wallet.handler, plugin)
|
||||
# The wallet has not been previously paired, so let the user
|
||||
# choose an unpaired device and compare its first address.
|
||||
info = self.select_device(wallet, plugin, devices)
|
||||
if info:
|
||||
client = self.client_lookup(info.device.id_)
|
||||
if client and not client.features.bootloader_mode:
|
||||
# This will trigger a PIN/passphrase entry request
|
||||
client_first_address = client.first_address(derivation)
|
||||
if client_first_address == first_address:
|
||||
self.pair_wallet(wallet, device.id_)
|
||||
self.pair_wallet(wallet, info.device.id_)
|
||||
return client
|
||||
|
||||
return None
|
||||
|
||||
def unpaired_device_infos(self, handler, plugin, devices=None):
|
||||
'''Returns a list of DeviceInfo objects: one for each connected,
|
||||
unpaired device accepted by the plugin.'''
|
||||
if devices is None:
|
||||
devices = self.scan_devices(handler)
|
||||
devices = [dev for dev in devices if not self.wallet_by_id(dev.id_)]
|
||||
|
||||
states = [_("wiped"), _("initialized")]
|
||||
infos = []
|
||||
for device in devices:
|
||||
if not device.product_key in plugin.DEVICE_IDS:
|
||||
continue
|
||||
client = self.create_client(device, handler, plugin)
|
||||
if not client:
|
||||
continue
|
||||
state = states[client.is_initialized()]
|
||||
label = client.label() or _("An unnamed %s") % plugin.device
|
||||
descr = "%s (%s)" % (label, state)
|
||||
infos.append(DeviceInfo(device, descr, client.is_initialized()))
|
||||
|
||||
return infos
|
||||
|
||||
def select_device(self, wallet, plugin, devices=None):
|
||||
'''Ask the user to select a device to use if there is more than one,
|
||||
and return the DeviceInfo for the device.'''
|
||||
infos = self.unpaired_device_infos(wallet.handler, plugin, devices)
|
||||
if not infos:
|
||||
return None
|
||||
if len(infos) == 1:
|
||||
return infos[0]
|
||||
msg = _("Please select which %s device to use:") % plugin.device
|
||||
descriptions = [info.description for info in infos]
|
||||
return infos[wallet.handler.query_choice(msg, descriptions)]
|
||||
|
||||
def scan_devices(self, handler):
|
||||
# All currently supported hardware libraries use hid, so we
|
||||
# assume it here. This can be easily abstracted if necessary.
|
||||
|
||||
Reference in New Issue
Block a user