Skip to content

Commit 2a73ce9

Browse files
authored
Refactor DeviceManager.findTargetDevices() and FlutterCommand.findAllTargetDevices(), and add a flag to not show prompt. (#112223)
1 parent eefd5d4 commit 2a73ce9

File tree

12 files changed

+475
-391
lines changed

12 files changed

+475
-391
lines changed

packages/flutter_tools/lib/src/context_runner.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,6 @@ Future<T> runInContext<T>(
205205
),
206206
fuchsiaSdk: globals.fuchsiaSdk!,
207207
operatingSystemUtils: globals.os,
208-
terminal: globals.terminal,
209208
customDevicesConfig: globals.customDevicesConfig,
210209
),
211210
DevtoolsLauncher: () => DevtoolsServerLauncher(

packages/flutter_tools/lib/src/device.dart

Lines changed: 45 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@ import 'package:meta/meta.dart';
99

1010
import 'application_package.dart';
1111
import 'artifacts.dart';
12-
import 'base/common.dart';
1312
import 'base/context.dart';
1413
import 'base/dds.dart';
1514
import 'base/file_system.dart';
1615
import 'base/logger.dart';
17-
import 'base/terminal.dart';
18-
import 'base/user_messages.dart' hide userMessages;
1916
import 'base/utils.dart';
2017
import 'build_info.dart';
2118
import 'devfs.dart';
@@ -83,15 +80,9 @@ class PlatformType {
8380
abstract class DeviceManager {
8481
DeviceManager({
8582
required Logger logger,
86-
required Terminal terminal,
87-
required UserMessages userMessages,
88-
}) : _logger = logger,
89-
_terminal = terminal,
90-
_userMessages = userMessages;
83+
}) : _logger = logger;
9184

9285
final Logger _logger;
93-
final Terminal _terminal;
94-
final UserMessages _userMessages;
9586

9687
/// Constructing DeviceManagers is cheap; they only do expensive work if some
9788
/// of their methods are called.
@@ -219,7 +210,12 @@ abstract class DeviceManager {
219210
];
220211
}
221212

222-
/// Find and return a list of devices based on the current project and environment.
213+
/// Find and return all target [Device]s based upon currently connected
214+
/// devices, the current project, and criteria entered by the user on
215+
/// the command line.
216+
///
217+
/// If no device can be found that meets specified criteria,
218+
/// then print an error message and return null.
223219
///
224220
/// Returns a list of devices specified by the user.
225221
///
@@ -233,9 +229,13 @@ abstract class DeviceManager {
233229
/// device connected, then filter out unsupported devices and prioritize
234230
/// ephemeral devices.
235231
///
236-
/// * If [flutterProject] is null, then assume the project supports all
237-
/// device types.
238-
Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, { Duration? timeout }) async {
232+
/// * If [promptUserToChooseDevice] is true, and there are more than one
233+
/// device after the aforementioned filters, and the user is connected to a
234+
/// terminal, then show a prompt asking the user to choose one.
235+
Future<List<Device>> findTargetDevices(
236+
FlutterProject? flutterProject, {
237+
Duration? timeout,
238+
}) async {
239239
if (timeout != null) {
240240
// Reset the cache with the specified timeout.
241241
await refreshAllConnectedDevices(timeout: timeout);
@@ -244,95 +244,54 @@ abstract class DeviceManager {
244244
List<Device> devices = (await getDevices())
245245
.where((Device device) => device.isSupported()).toList();
246246

247-
// Always remove web and fuchsia devices from `--all`. This setting
248-
// currently requires devices to share a frontend_server and resident
249-
// runner instance. Both web and fuchsia require differently configured
250-
// compilers, and web requires an entirely different resident runner.
251247
if (hasSpecifiedAllDevices) {
248+
// User has specified `--device all`.
249+
//
250+
// Always remove web and fuchsia devices from `--all`. This setting
251+
// currently requires devices to share a frontend_server and resident
252+
// runner instance. Both web and fuchsia require differently configured
253+
// compilers, and web requires an entirely different resident runner.
252254
devices = <Device>[
253255
for (final Device device in devices)
254256
if (await device.targetPlatform != TargetPlatform.fuchsia_arm64 &&
255257
await device.targetPlatform != TargetPlatform.fuchsia_x64 &&
256-
await device.targetPlatform != TargetPlatform.web_javascript)
258+
await device.targetPlatform != TargetPlatform.web_javascript &&
259+
isDeviceSupportedForProject(device, flutterProject))
257260
device,
258261
];
259-
}
262+
} else if (!hasSpecifiedDeviceId) {
263+
// User did not specify the device.
260264

261-
// If there is no specified device, the remove all devices which are not
262-
// supported by the current application. For example, if there was no
263-
// 'android' folder then don't attempt to launch with an Android device.
264-
if (devices.length > 1 && !hasSpecifiedDeviceId) {
265+
// Remove all devices which are not supported by the current application.
266+
// For example, if there was no 'android' folder then don't attempt to
267+
// launch with an Android device.
265268
devices = <Device>[
266269
for (final Device device in devices)
267270
if (isDeviceSupportedForProject(device, flutterProject))
268271
device,
269272
];
270-
} else if (devices.length == 1 &&
271-
!hasSpecifiedDeviceId &&
272-
!isDeviceSupportedForProject(devices.single, flutterProject)) {
273-
// If there is only a single device but it is not supported, then return
274-
// early.
275-
return <Device>[];
276-
}
277273

278-
// If there are still multiple devices and the user did not specify to run
279-
// all, then attempt to prioritize ephemeral devices. For example, if the
280-
// user only typed 'flutter run' and both an Android device and desktop
281-
// device are available, choose the Android device.
282-
if (devices.length > 1 && !hasSpecifiedAllDevices) {
283-
// Note: ephemeral is nullable for device types where this is not well
284-
// defined.
285-
if (devices.any((Device device) => device.ephemeral == true)) {
286-
// if there is only one ephemeral device, get it
287-
final List<Device> ephemeralDevices = devices
288-
.where((Device device) => device.ephemeral == true)
289-
.toList();
290-
291-
if (ephemeralDevices.length == 1) {
292-
devices = ephemeralDevices;
293-
}
274+
if (devices.length > 1) {
275+
// If there are still multiple devices and the user did not specify to run
276+
// all, then attempt to prioritize ephemeral devices. For example, if the
277+
// user only typed 'flutter run' and both an Android device and desktop
278+
// device are available, choose the Android device.
279+
280+
// Note: ephemeral is nullable for device types where this is not well
281+
// defined.
282+
final List<Device> ephemeralDevices = <Device>[
283+
for (final Device device in devices)
284+
if (device.ephemeral == true)
285+
device,
286+
];
287+
288+
if (ephemeralDevices.length == 1) {
289+
devices = ephemeralDevices;
290+
}
294291
}
295-
// If it was not able to prioritize a device. For example, if the user
296-
// has two active Android devices running, then we request the user to
297-
// choose one. If the user has two nonEphemeral devices running, we also
298-
// request input to choose one.
299-
if (devices.length > 1 && _terminal.stdinHasTerminal) {
300-
_logger.printStatus(_userMessages.flutterMultipleDevicesFound);
301-
await Device.printDevices(devices, _logger);
302-
final Device chosenDevice = await _chooseOneOfAvailableDevices(devices);
303-
specifiedDeviceId = chosenDevice.id;
304-
devices = <Device>[chosenDevice];
305-
}
306-
}
307-
return devices;
308-
}
309-
310-
Future<Device> _chooseOneOfAvailableDevices(List<Device> devices) async {
311-
_displayDeviceOptions(devices);
312-
final String userInput = await _readUserInput(devices.length);
313-
if (userInput.toLowerCase() == 'q') {
314-
throwToolExit('');
315-
}
316-
return devices[int.parse(userInput) - 1];
317-
}
318-
319-
void _displayDeviceOptions(List<Device> devices) {
320-
int count = 1;
321-
for (final Device device in devices) {
322-
_logger.printStatus(_userMessages.flutterChooseDevice(count, device.name, device.id));
323-
count++;
324292
}
325-
}
326293

327-
Future<String> _readUserInput(int deviceCount) async {
328-
_terminal.usesTerminalUi = true;
329-
final String result = await _terminal.promptForCharInput(
330-
<String>[ for (int i = 0; i < deviceCount; i++) '${i + 1}', 'q', 'Q'],
331-
displayAcceptedCharacters: false,
332-
logger: _logger,
333-
prompt: _userMessages.flutterChooseOne,
334-
);
335-
return result;
294+
return devices;
336295
}
337296

338297
/// Returns whether the device is supported for the project.

packages/flutter_tools/lib/src/flutter_device_manager.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'artifacts.dart';
1111
import 'base/file_system.dart';
1212
import 'base/os.dart';
1313
import 'base/platform.dart';
14+
import 'base/user_messages.dart';
1415
import 'custom_devices/custom_device.dart';
1516
import 'custom_devices/custom_devices_config.dart';
1617
import 'device.dart';
@@ -51,10 +52,9 @@ class FlutterDeviceManager extends DeviceManager {
5152
required Artifacts artifacts,
5253
required MacOSWorkflow macOSWorkflow,
5354
required FuchsiaSdk fuchsiaSdk,
54-
required super.userMessages,
55+
required UserMessages userMessages,
5556
required OperatingSystemUtils operatingSystemUtils,
5657
required WindowsWorkflow windowsWorkflow,
57-
required super.terminal,
5858
required CustomDevicesConfig customDevicesConfig,
5959
}) : deviceDiscoverers = <DeviceDiscovery>[
6060
AndroidDevices(

packages/flutter_tools/lib/src/runner/flutter_command.dart

Lines changed: 91 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1413,52 +1413,105 @@ abstract class FlutterCommand extends Command<void> {
14131413
timeout: deviceDiscoveryTimeout,
14141414
);
14151415

1416-
if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) {
1417-
globals.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId!));
1418-
final List<Device> allDevices = await deviceManager.getAllConnectedDevices();
1419-
if (allDevices.isNotEmpty) {
1420-
globals.printStatus('');
1421-
globals.printStatus('The following devices were found:');
1422-
await Device.printDevices(allDevices, globals.logger);
1423-
}
1424-
return null;
1425-
} else if (devices.isEmpty) {
1426-
if (deviceManager.hasSpecifiedAllDevices) {
1427-
globals.printStatus(userMessages.flutterNoDevicesFound);
1416+
if (devices.isEmpty) {
1417+
if (deviceManager.hasSpecifiedDeviceId) {
1418+
globals.logger.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId!));
1419+
final List<Device> allDevices = await deviceManager.getAllConnectedDevices();
1420+
if (allDevices.isNotEmpty) {
1421+
globals.logger.printStatus('');
1422+
globals.logger.printStatus('The following devices were found:');
1423+
await Device.printDevices(allDevices, globals.logger);
1424+
}
1425+
return null;
1426+
} else if (deviceManager.hasSpecifiedAllDevices) {
1427+
globals.logger.printStatus(userMessages.flutterNoDevicesFound);
1428+
await _printUnsupportedDevice(deviceManager);
1429+
return null;
14281430
} else {
1429-
globals.printStatus(userMessages.flutterNoSupportedDevices);
1430-
}
1431-
final List<Device> unsupportedDevices = await deviceManager.getDevices();
1432-
if (unsupportedDevices.isNotEmpty) {
1433-
final StringBuffer result = StringBuffer();
1434-
result.writeln(userMessages.flutterFoundButUnsupportedDevices);
1435-
result.writeAll(
1436-
(await Device.descriptions(unsupportedDevices))
1437-
.map((String desc) => desc)
1438-
.toList(),
1439-
'\n',
1440-
);
1441-
result.writeln();
1442-
result.writeln(userMessages.flutterMissPlatformProjects(
1443-
Device.devicesPlatformTypes(unsupportedDevices),
1444-
));
1445-
globals.printStatus(result.toString());
1431+
globals.logger.printStatus(userMessages.flutterNoSupportedDevices);
1432+
await _printUnsupportedDevice(deviceManager);
1433+
return null;
14461434
}
1447-
return null;
1448-
} else if (devices.length > 1 && !deviceManager.hasSpecifiedAllDevices) {
1435+
} else if (devices.length > 1) {
14491436
if (deviceManager.hasSpecifiedDeviceId) {
1450-
globals.printStatus(userMessages.flutterFoundSpecifiedDevices(devices.length, deviceManager.specifiedDeviceId!));
1451-
} else {
1452-
globals.printStatus(userMessages.flutterSpecifyDeviceWithAllOption);
1453-
devices = await deviceManager.getAllConnectedDevices();
1437+
globals.logger.printStatus(userMessages.flutterFoundSpecifiedDevices(devices.length, deviceManager.specifiedDeviceId!));
1438+
return null;
1439+
} else if (!deviceManager.hasSpecifiedAllDevices) {
1440+
if (globals.terminal.stdinHasTerminal) {
1441+
// If DeviceManager was not able to prioritize a device. For example, if the user
1442+
// has two active Android devices running, then we request the user to
1443+
// choose one. If the user has two nonEphemeral devices running, we also
1444+
// request input to choose one.
1445+
globals.logger.printStatus(userMessages.flutterMultipleDevicesFound);
1446+
await Device.printDevices(devices, globals.logger);
1447+
final Device chosenDevice = await _chooseOneOfAvailableDevices(devices);
1448+
1449+
// Update the [DeviceManager.specifiedDeviceId] so that we will not be prompted again.
1450+
deviceManager.specifiedDeviceId = chosenDevice.id;
1451+
1452+
devices = <Device>[chosenDevice];
1453+
} else {
1454+
// Show an error message asking the user to specify `-d all` if they
1455+
// want to run on multiple devices.
1456+
final List<Device> allDevices = await deviceManager.getAllConnectedDevices();
1457+
globals.logger.printStatus(userMessages.flutterSpecifyDeviceWithAllOption);
1458+
globals.logger.printStatus('');
1459+
await Device.printDevices(allDevices, globals.logger);
1460+
return null;
1461+
}
14541462
}
1455-
globals.printStatus('');
1456-
await Device.printDevices(devices, globals.logger);
1457-
return null;
14581463
}
1464+
14591465
return devices;
14601466
}
14611467

1468+
Future<void> _printUnsupportedDevice(DeviceManager deviceManager) async {
1469+
final List<Device> unsupportedDevices = await deviceManager.getDevices();
1470+
if (unsupportedDevices.isNotEmpty) {
1471+
final StringBuffer result = StringBuffer();
1472+
result.writeln(userMessages.flutterFoundButUnsupportedDevices);
1473+
result.writeAll(
1474+
(await Device.descriptions(unsupportedDevices))
1475+
.map((String desc) => desc)
1476+
.toList(),
1477+
'\n',
1478+
);
1479+
result.writeln();
1480+
result.writeln(userMessages.flutterMissPlatformProjects(
1481+
Device.devicesPlatformTypes(unsupportedDevices),
1482+
));
1483+
globals.logger.printStatus(result.toString());
1484+
}
1485+
}
1486+
1487+
Future<Device> _chooseOneOfAvailableDevices(List<Device> devices) async {
1488+
_displayDeviceOptions(devices);
1489+
final String userInput = await _readUserInput(devices.length);
1490+
if (userInput.toLowerCase() == 'q') {
1491+
throwToolExit('');
1492+
}
1493+
return devices[int.parse(userInput) - 1];
1494+
}
1495+
1496+
void _displayDeviceOptions(List<Device> devices) {
1497+
int count = 1;
1498+
for (final Device device in devices) {
1499+
globals.logger.printStatus(userMessages.flutterChooseDevice(count, device.name, device.id));
1500+
count++;
1501+
}
1502+
}
1503+
1504+
Future<String> _readUserInput(int deviceCount) async {
1505+
globals.terminal.usesTerminalUi = true;
1506+
final String result = await globals.terminal.promptForCharInput(
1507+
<String>[ for (int i = 0; i < deviceCount; i++) '${i + 1}', 'q', 'Q'],
1508+
displayAcceptedCharacters: false,
1509+
logger: globals.logger,
1510+
prompt: userMessages.flutterChooseOne,
1511+
);
1512+
return result;
1513+
}
1514+
14621515
/// Find and return the target [Device] based upon currently connected
14631516
/// devices and criteria entered by the user on the command line.
14641517
/// If a device cannot be found that meets specified criteria,

0 commit comments

Comments
 (0)