1
0

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:
Neil Booth
2016-01-24 13:01:04 +09:00
parent a0ef42d572
commit f8ed7b058d
4 changed files with 71 additions and 62 deletions

View File

@@ -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.