blob: ec74485d3b9bc25e97227416c2c1dc3cd48e27c2 [file] [log] [blame]
// Copyright 2020 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.
import 'dart:convert';
import 'package:flutter/foundation.dart';
import '../primitives/auto_dispose.dart';
import '../primitives/utils.dart';
import '../screens/inspector/inspector_service.dart';
import '../service/vm_service_wrapper.dart';
import 'globals.dart';
/// A controller for global application preferences.
class PreferencesController extends DisposableController
with AutoDisposeControllerMixin {
final ValueNotifier<bool> _darkModeTheme = ValueNotifier(true);
final ValueNotifier<bool> _vmDeveloperMode = ValueNotifier(false);
final ValueNotifier<bool> _denseMode = ValueNotifier(false);
ValueListenable<bool> get darkModeTheme => _darkModeTheme;
ValueListenable<bool> get vmDeveloperModeEnabled => _vmDeveloperMode;
ValueListenable<bool> get denseModeEnabled => _denseMode;
InspectorPreferencesController get inspector => _inspector;
final _inspector = InspectorPreferencesController();
Future<void> init() async {
// Get the current values and listen for and write back changes.
String? value = await storage.getValue('ui.darkMode');
toggleDarkModeTheme(value == null || value == 'true');
addAutoDisposeListener(_darkModeTheme, () {
storage.setValue('ui.darkMode', '${_darkModeTheme.value}');
});
value = await storage.getValue('ui.vmDeveloperMode');
toggleVmDeveloperMode(value == 'true');
addAutoDisposeListener(_vmDeveloperMode, () {
storage.setValue('ui.vmDeveloperMode', '${_vmDeveloperMode.value}');
});
value = await storage.getValue('ui.denseMode');
toggleDenseMode(value == 'true');
addAutoDisposeListener(_denseMode, () {
storage.setValue('ui.denseMode', '${_denseMode.value}');
});
await _inspector.init();
setGlobal(PreferencesController, this);
}
@override
void dispose() {
inspector.dispose();
super.dispose();
}
/// Change the value for the dark mode setting.
void toggleDarkModeTheme(bool useDarkMode) {
_darkModeTheme.value = useDarkMode;
}
/// Change the value for the VM developer mode setting.
void toggleVmDeveloperMode(bool enableVmDeveloperMode) {
_vmDeveloperMode.value = enableVmDeveloperMode;
VmServicePrivate.enablePrivateRpcs = enableVmDeveloperMode;
}
/// Change the value for the dense mode setting.
void toggleDenseMode(bool enableDenseMode) {
_denseMode.value = enableDenseMode;
}
}
class InspectorPreferencesController extends DisposableController
with AutoDisposeControllerMixin {
ValueListenable<bool> get hoverEvalModeEnabled => _hoverEvalMode;
ListValueNotifier<String> get customPubRootDirectories =>
_customPubRootDirectories;
ValueListenable<bool> get isRefreshingCustomPubRootDirectories =>
_customPubRootDirectoriesAreBusy;
InspectorService get inspectorService =>
serviceManager.inspectorService as InspectorService;
final _hoverEvalMode = ValueNotifier<bool>(false);
final _customPubRootDirectories = ListValueNotifier<String>([]);
final _customPubRootDirectoriesAreBusy = ValueNotifier<bool>(false);
final _busyCounter = ValueNotifier<int>(0);
static const _hoverEvalModeStorageId = 'inspector.hoverEvalMode';
static const _customPubRootDirectoriesStorageId =
'inspector.customPubRootDirectories';
Future<void> init() async {
await initHoverEvalMode();
_initCustomPubRootListeners();
}
Future<void> initHoverEvalMode() async {
String? hoverEvalModeEnabledValue =
await storage.getValue(_hoverEvalModeStorageId);
// When embedded, default hoverEvalMode to off
hoverEvalModeEnabledValue ??= (!ideTheme.embed).toString();
setHoverEvalMode(hoverEvalModeEnabledValue == 'true');
addAutoDisposeListener(_hoverEvalMode, () {
storage.setValue(
_hoverEvalModeStorageId,
_hoverEvalMode.value.toString(),
);
});
}
void _initCustomPubRootListeners() {
// TODO(CoderDake): add _customPubRootDirectories listener back when
// finalizing https://github.com/flutter/devtools/issues/3941
/*
addAutoDisposeListener(_customPubRootDirectories, () {
storage.setValue(
_customPubRootDirectoriesStorageId,
jsonEncode(_customPubRootDirectories.value),
);
});
*/
addAutoDisposeListener(_busyCounter, () {
_customPubRootDirectoriesAreBusy.value = _busyCounter.value != 0;
});
}
Future<void> addPubRootDirectories(
List<String> pubRootDirectories,
) async {
await _customPubRootDirectoryBusyTracker(() async {
await inspectorService.addPubRootDirectories(pubRootDirectories);
await _refreshPubRootDirectoriesFromService();
});
}
Future<void> removePubRootDirectories(
List<String> pubRootDirectories,
) async {
await _customPubRootDirectoryBusyTracker(() async {
await inspectorService.removePubRootDirectories(pubRootDirectories);
await _refreshPubRootDirectoriesFromService();
});
}
Future<void> _refreshPubRootDirectoriesFromService() async {
await _customPubRootDirectoryBusyTracker(() async {
final freshPubRootDirectories =
await inspectorService.getPubRootDirectories();
if (freshPubRootDirectories != null) {
final newSet = Set<String>.from(freshPubRootDirectories);
final oldSet = Set<String>.from(_customPubRootDirectories.value);
final directoriesToAdd = newSet.difference(oldSet);
final directoriesToRemove = oldSet.difference(newSet);
_customPubRootDirectories.removeAll(directoriesToRemove);
_customPubRootDirectories.addAll(directoriesToAdd);
}
});
}
Future<void> loadCustomPubRootDirectories() async {
await _customPubRootDirectoryBusyTracker(() async {
final storedCustomPubRootDirectories =
await storage.getValue(_customPubRootDirectoriesStorageId);
if (storedCustomPubRootDirectories != null) {
await addPubRootDirectories(
List<String>.from(
jsonDecode(storedCustomPubRootDirectories),
),
);
}
});
}
Future<void> _customPubRootDirectoryBusyTracker(
Future<void> Function() callback,
) async {
try {
_busyCounter.value++;
await callback();
} finally {
_busyCounter.value--;
}
}
/// Change the value for the hover eval mode setting.
void setHoverEvalMode(bool enableHoverEvalMode) {
_hoverEvalMode.value = enableHoverEvalMode;
}
}