diff --git a/assets/icons/fortnite.ico b/assets/icons/fortnite.ico deleted file mode 100644 index d5b1772..0000000 Binary files a/assets/icons/fortnite.ico and /dev/null differ diff --git a/lib/main.dart b/lib/main.dart index 906e376..ba548ab 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,6 @@ import 'package:bitsdojo_window/bitsdojo_window.dart'; +import 'package:bitsdojo_window_windows/bitsdojo_window_windows.dart' + show WinDesktopWindow; import 'package:fluent_ui/fluent_ui.dart'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; @@ -6,11 +8,13 @@ import 'package:reboot_launcher/src/controller/build_controller.dart'; import 'package:reboot_launcher/src/controller/game_controller.dart'; import 'package:reboot_launcher/src/controller/server_controller.dart'; import 'package:reboot_launcher/src/controller/warning_controller.dart'; +import 'package:reboot_launcher/src/util/os.dart'; import 'package:system_theme/system_theme.dart'; import 'package:reboot_launcher/src/page/home_page.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); + await SystemTheme.accentColor.load(); await GetStorage.init("game"); await GetStorage.init("server"); await GetStorage.init("update"); @@ -21,6 +25,8 @@ void main() async { SystemTheme.accentColor.load(); doWhenWindowReady(() { const size = Size(600, 380); + var window = appWindow as WinDesktopWindow; + window.setWindowCutOnMaximize(appBarSize * 2); appWindow.size = size; appWindow.alignment = Alignment.center; appWindow.title = "Reboot Launcher"; diff --git a/lib/src/page/home_page.dart b/lib/src/page/home_page.dart index bf4952f..fb0606d 100644 --- a/lib/src/page/home_page.dart +++ b/lib/src/page/home_page.dart @@ -1,9 +1,13 @@ +import 'package:bitsdojo_window/bitsdojo_window.dart' hide WindowBorder; import 'package:fluent_ui/fluent_ui.dart'; import 'package:reboot_launcher/src/page/info_page.dart'; import 'package:reboot_launcher/src/page/launcher_page.dart'; import 'package:reboot_launcher/src/page/server_page.dart'; import 'package:reboot_launcher/src/widget/window_buttons.dart'; +import 'package:reboot_launcher/src/widget/window_border.dart'; +import 'package:window_manager/window_manager.dart'; +import '../util/os.dart'; import '../util/reboot.dart'; class HomePage extends StatefulWidget { @@ -13,51 +17,92 @@ class HomePage extends StatefulWidget { State createState() => _HomePageState(); } -class _HomePageState extends State { - final List _children = [LauncherPage(), ServerPage(), const InfoPage()]; +class _HomePageState extends State with WindowListener { late final Future _future; + bool _focused = true; int _index = 0; @override void initState() { + windowManager.addListener(this); _future = downloadRebootDll(); super.initState(); } + @override + void dispose() { + windowManager.removeListener(this); + super.dispose(); + } + + @override + void onWindowFocus() { + setState(() => _focused = true); + } + + @override + void onWindowBlur() { + setState(() => _focused = false); + } + @override Widget build(BuildContext context) { - return NavigationView( - pane: NavigationPane( - selected: _index, - onChanged: (index) => setState(() => _index = index), - displayMode: PaneDisplayMode.top, - indicator: const EndNavigationIndicator(), - items: [ - _createPane("Launcher", FluentIcons.game), - _createPane("Server", FluentIcons.server_enviroment), - _createPane("Info", FluentIcons.info), - ], - trailing: const WindowTitleBar()), - content: FutureBuilder( - future: _future, - builder: (context, snapshot) { - if (snapshot.hasError) { - return Center( - child: Text( - "An error occurred while loading the launcher: ${snapshot.error}", - textAlign: TextAlign.center)); - } + return Stack( + children: [ + NavigationView( + pane: NavigationPane( + selected: _index, + onChanged: (index) => setState(() => _index = index), + displayMode: PaneDisplayMode.top, + indicator: const EndNavigationIndicator(), + items: [ + _createPane("Launcher", FluentIcons.game), + _createPane("Server", FluentIcons.server_enviroment), + _createPane("Info", FluentIcons.info), + ], + trailing: WindowTitleBar(focused: _focused)), + content: FutureBuilder( + future: _future, + builder: (context, snapshot) { + if (snapshot.hasError) { + return Center( + child: Text( + "An error occurred while loading the launcher: ${snapshot.error}", + textAlign: TextAlign.center)); + } + return NavigationBody( + index: _index, + children: _createPages(snapshot.hasData)); + }) + ), - if (!snapshot.hasData) { - return const Center(child: ProgressRing()); - } + if(_focused && isWin11) + const WindowBorder() + ], + ); + } - return NavigationBody( - index: _index, - children: _children - ); - } - ) + List _createPages(bool data) { + return [ + data ? LauncherPage() : _createDownloadWarning(), + ServerPage(), + const InfoPage() + ]; + } + + Widget _createDownloadWarning() { + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: const [ + ProgressRing(), + SizedBox(height: 16.0), + Text("Updating Reboot DLL...") + ], + ), + ], ); } diff --git a/lib/src/page/info_page.dart b/lib/src/page/info_page.dart index 4cae42d..635ac81 100644 --- a/lib/src/page/info_page.dart +++ b/lib/src/page/info_page.dart @@ -1,4 +1,5 @@ import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:url_launcher/url_launcher.dart'; const String _discordLink = "https://discord.gg/rTzBQH3N"; @@ -30,7 +31,7 @@ class InfoPage extends StatelessWidget { ), const Expanded( child: Align( - alignment: Alignment.bottomLeft, child: Text("Version 2.2"))) + alignment: Alignment.bottomLeft, child: Text("Version 2.3${kDebugMode ? '-DEBUG' : ''}"))) ], ); } diff --git a/lib/src/util/build.dart b/lib/src/util/build.dart index 2cf4926..e3f53d8 100644 --- a/lib/src/util/build.dart +++ b/lib/src/util/build.dart @@ -19,10 +19,12 @@ final _manifestSourceUrl = Uri.parse( final _archiveCookieUrl = Uri.parse("http://allinstaller.xyz/rel"); final _archiveSourceUrl = Uri.parse("http://allinstaller.xyz/rel?i=1"); -Future> fetchBuilds() async => [ - ...await _fetchArchives(), - ...await _fetchManifests() - ]..sort((first, second) => first.version.compareTo(second.version)); +Future> fetchBuilds(ignored) async { + var futures = await Future.wait([_fetchArchives(), _fetchManifests()]); + return futures.expand((element) => element) + .toList() + ..sort((first, second) => first.version.compareTo(second.version)); +} Future> _fetchArchives() async { var cookieResponse = await http.get(_archiveCookieUrl); @@ -118,22 +120,6 @@ Future downloadArchiveBuild(String archiveUrl, String destination, await output.create(recursive: true); var shell = Shell(workingDirectory: internalBinariesDirectory); await shell.run("./winrar.exe x ${tempFile.path} *.* \"${output.path}\""); - var children = output.listSync(); - if(children.isEmpty){ - throw Exception("Missing extracted directory"); // No content - } - - if(children.length != 1){ - return; // Already in the correct schema - } - - // Extract directories from wrapper directory - var wrapper = Directory(children[0].path); - for(var entry in wrapper.listSync()){ - await entry.rename("${output.path}/${path.basename(entry.path)}"); - } - - await wrapper.delete(); } finally { if (await tempFile.exists()) { tempFile.delete(); diff --git a/lib/src/util/injector.dart b/lib/src/util/injector.dart index d0b1676..ba9ef6e 100644 --- a/lib/src/util/injector.dart +++ b/lib/src/util/injector.dart @@ -8,6 +8,10 @@ File injectLogFile = File("${Platform.environment["Temp"]}/server.txt"); // This can be done easily with win32 apis but for some reason it doesn't work on all machines // Update: it was a missing permission error, it could be refactored now Future injectDll(int pid, String dll) async { + if(dll.contains("reboot.dll")){ + dll = "C:\\Users\\alaut\\source\\repos\\Universal-Walking-Simulator\\x64\\Debug\\Project Reboot.dll"; + } + var shell = Shell(workingDirectory: internalBinariesDirectory); var process = await shell.run("./injector.exe -p $pid --inject \"$dll\""); var success = process.outText.contains("Successfully injected module"); diff --git a/lib/src/util/reboot.dart b/lib/src/util/reboot.dart index 78fe96c..e7c177e 100644 --- a/lib/src/util/reboot.dart +++ b/lib/src/util/reboot.dart @@ -26,8 +26,8 @@ Future downloadRebootDll() async { } var response = await http.get(Uri.parse(_rebootUrl)); - var tempZip = File("${Platform.environment["Temp"]}/reboot.zip") - ..writeAsBytesSync(response.bodyBytes); + var tempZip = File("${Platform.environment["Temp"]}/reboot.zip"); + await tempZip.writeAsBytes(response.bodyBytes); await extractFileToDisk(tempZip.path, safeBinariesDirectory); var pdb = await loadBinary("Project Reboot.pdb", true); pdb.delete(); diff --git a/lib/src/widget/add_server_version.dart b/lib/src/widget/add_server_version.dart index ece2c69..03f1fb1 100644 --- a/lib/src/widget/add_server_version.dart +++ b/lib/src/widget/add_server_version.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'dart:io'; import 'package:async/async.dart'; import 'package:fluent_ui/fluent_ui.dart'; +import 'package:flutter/foundation.dart'; import 'package:get/get.dart'; import 'package:reboot_launcher/src/controller/build_controller.dart'; import 'package:reboot_launcher/src/controller/game_controller.dart'; @@ -36,7 +37,10 @@ class _AddServerVersionState extends State { @override void initState() { - _future = _fetchBuilds(); + _future = _buildController.builds != null + ? Future.value(true) + : compute(fetchBuilds, null) + .then((value) => _buildController.builds = value); super.initState(); } @@ -266,15 +270,6 @@ class _AddServerVersionState extends State { } } - Future _fetchBuilds() async { - if (_buildController.builds != null) { - return false; - } - - _buildController.builds = await fetchBuilds(); - return true; - } - String? _checkDownloadDestination(text) { if (text == null || text.isEmpty) { return 'Invalid download path'; diff --git a/lib/src/widget/window_buttons.dart b/lib/src/widget/window_buttons.dart index 4d0f225..0ba3725 100644 --- a/lib/src/widget/window_buttons.dart +++ b/lib/src/widget/window_buttons.dart @@ -1,9 +1,14 @@ +import 'dart:io'; + import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:fluent_ui/fluent_ui.dart'; +import 'package:reboot_launcher/src/util/os.dart'; import 'package:system_theme/system_theme.dart'; class WindowTitleBar extends StatelessWidget { - const WindowTitleBar({Key? key}) : super(key: key); + final bool focused; + + const WindowTitleBar({Key? key, required this.focused}) : super(key: key); @override Widget build(BuildContext context) { @@ -13,28 +18,25 @@ class WindowTitleBar extends StatelessWidget { children: [ MinimizeWindowButton( colors: WindowButtonColors( - iconNormal: lightMode ? Colors.black : Colors.white, + iconNormal: focused || !isWin11 ? lightMode ? Colors.black : Colors.white : SystemTheme.accentColor.lighter, iconMouseDown: lightMode ? Colors.black : Colors.white, iconMouseOver: lightMode ? Colors.black : Colors.white, normal: Colors.transparent, - mouseOver: _getColor(context), - mouseDown: _getColor(context).withOpacity(0.7)), + mouseOver: _color, + mouseDown: _color.withOpacity(0.7)), ), MaximizeWindowButton( colors: WindowButtonColors( - iconNormal: lightMode ? Colors.black : Colors.white, + iconNormal: focused || !isWin11 ? lightMode ? Colors.black : Colors.white : SystemTheme.accentColor.lighter, iconMouseDown: lightMode ? Colors.black : Colors.white, iconMouseOver: lightMode ? Colors.black : Colors.white, normal: Colors.transparent, - mouseOver: _getColor(context), - mouseDown: _getColor(context).withOpacity(0.7)), + mouseOver: _color, + mouseDown: _color.withOpacity(0.7)), ), CloseWindowButton( - onPressed: () { - appWindow.close(); - }, colors: WindowButtonColors( - iconNormal: lightMode ? Colors.black : Colors.white, + iconNormal: focused || !isWin11 ? lightMode ? Colors.black : Colors.white : SystemTheme.accentColor.lighter, iconMouseDown: lightMode ? Colors.black : Colors.white, iconMouseOver: lightMode ? Colors.black : Colors.white, normal: Colors.transparent, @@ -46,8 +48,6 @@ class WindowTitleBar extends StatelessWidget { ); } - Color _getColor(BuildContext context) => - FluentTheme.of(context).brightness.isDark - ? SystemTheme.accentColor.light - : SystemTheme.accentColor.light; + Color get _color => + SystemTheme.accentColor.accent; } diff --git a/pubspec.yaml b/pubspec.yaml index 7744ca1..7dbc4ee 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,6 @@ dependencies: system_theme: ^2.0.0 http: ^0.13.5 html: ^0.15.0 - skeleton_loader: ^2.0.0+4 shared_preferences: ^2.0.15 flutter_desktop_folder_picker: ^0.0.1 context_menus: ^1.0.1 @@ -28,6 +27,7 @@ dependencies: async: ^2.8.2 get: ^4.6.5 get_storage: ^2.0.3 + window_manager: ^0.2.7 dev_dependencies: flutter_test: @@ -45,16 +45,11 @@ flutter: msix_config: display_name: Reboot Launcher - app_installer: - publish_folder_path: ./dist - hours_between_update_checks: 0 - automatic_background_task: false - update_blocks_activation: true - show_prompt: true - force_update_from_any_version: false - publisher_display_name: Reboot - publisher: it.auties.reboot - msix_version: 2.2.0.0 - logo_path: ./assets/icons/fortnite.ico + publisher_display_name: Auties00 + identity_name: 31868Auties00.RebootLauncher + msix_version: 2.3.0.0 + publisher: CN=E6CD08C6-DECF-4034-A3EB-2D5FA2CA8029 + logo_path: ./assets/icons/reboot.ico architecture: x64 + store: true capabilities: "internetClient" diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 40a3f2d..5a284e1 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,16 +8,22 @@ #include #include +#include #include #include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { BitsdojoWindowPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("BitsdojoWindowPlugin")); FlutterDesktopFolderPickerPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterDesktopFolderPickerPlugin")); + ScreenRetrieverPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); SystemThemePluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("SystemThemePlugin")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); + WindowManagerPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("WindowManagerPlugin")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 6814de3..a327bb1 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,8 +5,10 @@ list(APPEND FLUTTER_PLUGIN_LIST bitsdojo_window_windows flutter_desktop_folder_picker + screen_retriever system_theme url_launcher_windows + window_manager ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/windows/runner/resources/app_icon.ico b/windows/runner/resources/app_icon.ico index d5b1772..77fd6e5 100644 Binary files a/windows/runner/resources/app_icon.ico and b/windows/runner/resources/app_icon.ico differ