blob: 494c3a234a1587eae64239945aa9ebf871847106 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This file contain higher level utils, i.e. utils that depend on
// other libraries in this package.
// Utils, that do not have dependencies, should go to primitives/utils.dart.
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
import 'package:vm_service/vm_service.dart';
import '../../devtools.dart' as devtools;
import '../config_specific/logger/logger.dart' as logger;
import 'connected_app.dart';
import 'globals.dart';
import 'notifications.dart';
/// Attempts to copy a String of `data` to the clipboard.
///
/// Shows a `successMessage` [Notification] on the passed in `context`.
Future<void> copyToClipboard(
String data,
String successMessage,
BuildContext context,
) async {
await Clipboard.setData(
ClipboardData(
text: data,
),
);
Notifications.of(context)?.push(successMessage);
}
/// Logging to debug console only in debug runs.
void debugLogger(String message) {
assert(
() {
logger.log('$message');
return true;
}(),
);
}
double scaleByFontFactor(double original) {
return (original * ideTheme.fontSizeFactor).roundToDouble();
}
bool isDense() {
return preferences.denseModeEnabled.value || isEmbedded();
}
bool isEmbedded() => ideTheme.embed;
mixin CompareMixin implements Comparable {
bool operator <(other) {
return compareTo(other) < 0;
}
bool operator >(other) {
return compareTo(other) > 0;
}
bool operator <=(other) {
return compareTo(other) <= 0;
}
bool operator >=(other) {
return compareTo(other) >= 0;
}
}
extension VmExtension on VM {
List<IsolateRef> isolatesForDevToolsMode() {
final vmDeveloperModeEnabled = preferences.vmDeveloperModeEnabled.value;
final vmIsolates = isolates ?? <IsolateRef>[];
return [
...vmIsolates,
if (vmDeveloperModeEnabled || vmIsolates.isEmpty)
...systemIsolates ?? <IsolateRef>[],
];
}
String get deviceDisplay {
return [
'$targetCPU',
if (architectureBits != null && architectureBits != -1)
'($architectureBits bit)',
operatingSystem,
].join(' ');
}
}
Map<String, String> generateDeviceDescription(
VM vm,
ConnectedApp connectedApp, {
bool includeVmServiceConnection = true,
}) {
var version = vm.version!;
// Convert '2.9.0-13.0.dev (dev) (Fri May ... +0200) on "macos_x64"' to
// '2.9.0-13.0.dev'.
if (version.contains(' ')) {
version = version.substring(0, version.indexOf(' '));
}
final flutterVersion = connectedApp.flutterVersionNow;
return {
'CPU / OS': vm.deviceDisplay,
'Dart Version': version,
if (flutterVersion != null) ...{
'Flutter Version':
'${flutterVersion.version} / ${flutterVersion.channel}',
'Framework / Engine': '${flutterVersion.frameworkRevision} / '
'${flutterVersion.engineRevision}',
},
'Connected app type': connectedApp.display,
if (includeVmServiceConnection && serviceManager.service != null)
'VM Service Connection': serviceManager.service!.connectedUri.toString(),
};
}
List<String> issueLinkDetails() {
final issueDescriptionItems = [
'<-- Please describe your problem here. Be sure to include repro steps. -->',
'___', // This will create a separator in the rendered markdown.
'**DevTools version**: ${devtools.version}',
];
final vm = serviceManager.vm;
final connectedApp = serviceManager.connectedApp;
if (vm != null && connectedApp != null) {
final deviceDescriptionMap = generateDeviceDescription(
vm,
connectedApp,
includeVmServiceConnection: false,
);
final deviceDescription = deviceDescriptionMap.keys
.map((key) => '$key: ${deviceDescriptionMap[key]}');
issueDescriptionItems.addAll([
'**Connected Device**:',
...deviceDescription,
]);
}
return issueDescriptionItems;
}
/// Mixin that provides a [controller] from package:provider for a State class.
///
/// [initController] must be called from [State.didChangeDependencies]. If
/// [initController] returns false, return early from [didChangeDependencies] to
/// avoid calling any initialization code that should only be called once for a
/// controller. See [initController] documentation below for more details.
mixin ProvidedControllerMixin<T, V extends StatefulWidget> on State<V> {
T get controller => _controller!;
T? _controller;
/// Initializes [_controller] from package:provider.
///
/// This method should be called in [didChangeDependencies]. Returns whether
/// or not a new controller was provided upon subsequent calls to
/// [initController].
///
/// This method will commonly be used to return early from
/// [didChangeDependencies] when initialization code should not be run again
/// if the provided controller has not changed.
///
/// E.g. `if (!initController()) return;`
bool initController() {
final newController = Provider.of<T>(context);
if (newController == _controller) return false;
_controller = newController;
return true;
}
}