├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── analysis_options.yaml ├── assets ├── AdbWinApi.dll ├── AdbWinUsbApi.dll ├── WSA_logo.png ├── adb ├── adb.exe ├── adbMac ├── lightningBoltLogo.png └── logo512.png ├── installers ├── macOS │ ├── config.json │ └── logo512.icns └── windows │ └── Inno_Setup_Script.iss ├── lib ├── components │ ├── apk_backup_dialog.dart │ ├── apk_download_dialog.dart │ ├── apk_install_dialog.dart │ ├── console_output.dart │ ├── custom_list_tile.dart │ ├── device_info_list_tile.dart │ ├── file_transfer_progress.dart │ ├── icon_name_material_button.dart │ ├── material_ribbon.dart │ ├── obtainRootDialog.dart │ ├── page_subheading.dart │ ├── preference_toggle.dart │ ├── prompt_dialog.dart │ ├── reinstall_system_app_dialog.dart │ ├── select_compilation_mode_dialog.dart │ ├── set_app_installer_dialog.dart │ ├── simple_file_transfer_progress.dart │ ├── simple_rectangle_icon_material_button.dart │ ├── simple_text_field_alert.dart │ ├── updater_dialog.dart │ └── window_buttons.dart ├── layout_widgets │ └── PackageInfo.dart ├── main.dart ├── models │ ├── device.dart │ ├── file_transfer_job.dart │ ├── item.dart │ └── storage.dart ├── screens │ ├── connection_initiation_screen.dart │ ├── device_info_screen.dart │ ├── file_manager_screen.dart │ ├── home_screen.dart │ ├── package_manager_screen.dart │ ├── power_controls_screen.dart │ ├── settings_screen.dart │ └── theme_mode_service.dart ├── services │ ├── adb_services.dart │ ├── android_api_checks.dart │ ├── date_time_service.dart │ ├── file_services.dart │ ├── platform_services.dart │ ├── shared_prefs.dart │ ├── string_services.dart │ ├── transform_functions.dart │ └── update_services.dart └── utils │ ├── const.dart │ ├── enums.dart │ └── vars.dart ├── license.txt ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist ├── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── 1024.png │ │ │ ├── 128.png │ │ │ ├── 16.png │ │ │ ├── 256.png │ │ │ ├── 32.png │ │ │ ├── 512.png │ │ │ ├── 64.png │ │ │ └── Contents.json │ │ └── Contents.json │ ├── Base.lproj │ │ └── MainMenu.xib │ ├── Configs │ │ ├── AppInfo.xcconfig │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements └── RunnerTests │ └── RunnerTests.swift ├── pubspec.yaml ├── readme_assets ├── lightningBoltLogo.png ├── screenshot_apps.png ├── screenshot_connection_initiation.png ├── screenshot_device_info.png ├── screenshot_file_manager.png └── screenshot_power_controls.png ├── test └── widget_test.dart └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter ├── CMakeLists.txt ├── generated_plugin_registrant.cc ├── generated_plugin_registrant.h └── generated_plugins.cmake └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | # See https://www.dartlang.org/guides/libraries/private-files 48 | 49 | # Files and directories created by pub 50 | # If you're building an application, you may want to check-in your pubspec.lock 51 | pubspec.lock 52 | 53 | # Directory created by dartdoc 54 | # If you don't generate documentation locally you can remove this line. 55 | doc/api/ 56 | 57 | # Avoid committing generated Javascript files: 58 | *.dart.js 59 | *.info.json # Produced by the --dump-info flag. 60 | *.js # When generated by dart2js. Don't specify *.js if your 61 | # project includes source files written in JavaScript. 62 | *.js_ 63 | *.js.deps 64 | *.js.map 65 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "ba393198430278b6595976de84fe170f553cc728" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: ba393198430278b6595976de84fe170f553cc728 17 | base_revision: ba393198430278b6595976de84fe170f553cc728 18 | - platform: ios 19 | create_revision: ba393198430278b6595976de84fe170f553cc728 20 | base_revision: ba393198430278b6595976de84fe170f553cc728 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |
3 | logo 4 |

Android-Toolbox

5 |
6 | 7 | ![Downloads](https://img.shields.io/github/downloads/lightningbolt047/Android-Toolbox/total) ![Watchers](https://img.shields.io/github/watchers/lightningbolt047/Android-Toolbox?label=Watch) ![Stars](https://img.shields.io/github/stars/lightningbolt047/Android-Toolbox?style=social) ![License](https://img.shields.io/github/license/lightningbolt047/Android-Toolbox) 8 | 9 |
10 |

About

11 |

This application is built with Flutter. It uses adb behind the scenes to execute each and every user operation. The application comes bundled with adb, so you need not have adb installed and configured in path. I plan to bring it to linux after adding some functionality.

12 |
13 | 14 |
15 |

What does it do?

16 |

As of now, you can only use it to access your internal storage either on your phone or on WSA.
17 | Current features: 18 |

41 |

42 |

Okay I'm interested. How do I install it?

43 |

As of now only the Windows installer is available (Although I plan to release it on linux and macOS (if I can get my hands on a Mac)). You may download it from this repo's releases which you can find here: [Releases](https://github.com/lightningbolt047/Android-Toolbox/releases). Or if you are very much interested to compile and run in linux, you may as well clone this repo and build it for yourself. Do note that the app might not work as intended in that case.

44 |

Do I have to keep checking this repo for future updates to the app?

45 |

No you don't! The app will notify you when there is an update available, and you may choose to download and install the update from within the app.

46 |

Feature X is awesome, I can't wait to try it out, but it is a prerelease. How do I try it out?

47 |

There is support for updating to prerelease builds from within the app. Beware! Prerelease builds might not work as intended, and may even break updates (which might happen if I screw with the updater) in which case you will have to manually install the next update.

48 |

Why would I use the app's file manager when I can simply use Windows Explorer?

49 |

Gone are the days when we could use USB storage and mount the device as a storage device in windows. Right now MTP is being used and it is painful to use especially when transferring large number of files. ADB pull/push seems to be way faster when working with large number of files (I got an almost 2x improvement using my highly unscientific method of testing speed)

50 |
51 | 52 |
53 |

Screenshots

54 |

Connection Initiation

55 | connection_initiation 56 |

File Manager

57 | file_manager 58 |

Power Controls

59 | power_controls 60 |

Apps

61 | apps 62 |

Device Info

63 | device_info 64 |
65 | 66 |
67 |

Clone and build it yourself

68 | 82 |
-------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /assets/AdbWinApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/assets/AdbWinApi.dll -------------------------------------------------------------------------------- /assets/AdbWinUsbApi.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/assets/AdbWinUsbApi.dll -------------------------------------------------------------------------------- /assets/WSA_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/assets/WSA_logo.png -------------------------------------------------------------------------------- /assets/adb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/assets/adb -------------------------------------------------------------------------------- /assets/adb.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/assets/adb.exe -------------------------------------------------------------------------------- /assets/adbMac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/assets/adbMac -------------------------------------------------------------------------------- /assets/lightningBoltLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/assets/lightningBoltLogo.png -------------------------------------------------------------------------------- /assets/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/assets/logo512.png -------------------------------------------------------------------------------- /installers/macOS/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Android Toolbox", 3 | "icon": "logo512.icns", 4 | "contents": [ 5 | { "x": 448, "y": 344, "type": "link", "path": "/Applications" }, 6 | { "x": 192, "y": 344, "type": "file", "path": "../../build/macos/Build/Products/Release/Android Toolbox.app" } 7 | ] 8 | } -------------------------------------------------------------------------------- /installers/macOS/logo512.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/installers/macOS/logo512.icns -------------------------------------------------------------------------------- /installers/windows/Inno_Setup_Script.iss: -------------------------------------------------------------------------------- 1 | ; Script generated by the Inno Setup Script Wizard. 2 | ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! 3 | 4 | #define MyAppName "Android-Toolbox" 5 | #define MyAppVersion "2.0.0" 6 | #define MyAppPublisher "LightningBolt" 7 | #define MyAppURL "https://lightningbolt047.github.io/" 8 | #define MyAppExeName "adb_gui.exe" 9 | 10 | [Setup] 11 | ; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications. 12 | ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) 13 | AppId={{0A8CB1AC-327F-465C-AAD4-3F02B994CFF0} 14 | AppName={#MyAppName} 15 | AppVersion={#MyAppVersion} 16 | ;AppVerName={#MyAppName} {#MyAppVersion} 17 | AppPublisher={#MyAppPublisher} 18 | AppPublisherURL={#MyAppURL} 19 | AppSupportURL={#MyAppURL} 20 | AppUpdatesURL={#MyAppURL} 21 | DefaultDirName={autopf}\{#MyAppName} 22 | DisableDirPage=yes 23 | DisableProgramGroupPage=yes 24 | LicenseFile=C:\Users\sasha\AndroidStudioProjects\Android-Toolbox\license.txt 25 | ; Uncomment the following line to run in non administrative install mode (install for current user only.) 26 | ;PrivilegesRequired=lowest 27 | OutputDir=C:\Users\sasha\AndroidStudioProjects\Android-Toolbox\build\windows 28 | OutputBaseFilename=Android-Toolbox-windows-x64-2.0.0 29 | SetupIconFile=C:\Users\sasha\AndroidStudioProjects\Android-Toolbox\windows\runner\resources\app_icon.ico 30 | Compression=lzma 31 | SolidCompression=yes 32 | WizardStyle=modern 33 | 34 | [Languages] 35 | Name: "english"; MessagesFile: "compiler:Default.isl" 36 | 37 | [Tasks] 38 | Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked 39 | 40 | [Files] 41 | Source: "C:\Users\sasha\AndroidStudioProjects\Android-Toolbox\build\windows\x64\runner\Release\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion 42 | Source: "C:\Users\sasha\AndroidStudioProjects\Android-Toolbox\build\windows\x64\runner\Release\adb_gui.exp"; DestDir: "{app}"; Flags: ignoreversion 43 | Source: "C:\Users\sasha\AndroidStudioProjects\Android-Toolbox\build\windows\x64\runner\Release\adb_gui.lib"; DestDir: "{app}"; Flags: ignoreversion 44 | Source: "C:\Users\sasha\AndroidStudioProjects\Android-Toolbox\build\windows\x64\runner\Release\flutter_acrylic_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion 45 | Source: "C:\Users\sasha\AndroidStudioProjects\Android-Toolbox\build\windows\x64\runner\Release\system_theme_plugin.dll"; DestDir: "{app}"; Flags: ignoreversion 46 | Source: "C:\Users\sasha\AndroidStudioProjects\Android-Toolbox\build\windows\x64\runner\Release\bitsdojo_window_windows_plugin.lib"; DestDir: "{app}"; Flags: ignoreversion 47 | Source: "C:\Users\sasha\AndroidStudioProjects\Android-Toolbox\build\windows\x64\runner\Release\flutter_windows.dll"; DestDir: "{app}"; Flags: ignoreversion 48 | Source: "C:\Users\sasha\AndroidStudioProjects\Android-Toolbox\build\windows\x64\runner\Release\data\*"; DestDir: "{app}\data"; Flags: ignoreversion recursesubdirs createallsubdirs 49 | ; NOTE: Don't use "Flags: ignoreversion" on any shared system files 50 | 51 | [Icons] 52 | Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" 53 | Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon 54 | 55 | [Run] 56 | Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent 57 | 58 | -------------------------------------------------------------------------------- /lib/components/apk_download_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:adb_gui/utils/enums.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class APKDownloadDialog extends StatefulWidget { 5 | final Future numAPKDownloaded; 6 | const APKDownloadDialog({Key? key,required this.numAPKDownloaded}) : super(key: key); 7 | 8 | @override 9 | _APKDownloadDialogState createState() => _APKDownloadDialogState(numAPKDownloaded); 10 | } 11 | 12 | class _APKDownloadDialogState extends State { 13 | 14 | final Future numAPKDownloaded; 15 | ProcessStatus processStatus=ProcessStatus.working; 16 | 17 | _APKDownloadDialogState(this.numAPKDownloaded); 18 | 19 | void monitorProgress() async{ 20 | if(await numAPKDownloaded>0){ 21 | setState(() { 22 | processStatus=ProcessStatus.success; 23 | }); 24 | }else{ 25 | setState(() { 26 | processStatus=ProcessStatus.fail; 27 | }); 28 | } 29 | } 30 | 31 | @override 32 | void initState() { 33 | monitorProgress(); 34 | super.initState(); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return AlertDialog( 40 | title: processStatus==ProcessStatus.working?const Text("Downloading APK(s)"):processStatus==ProcessStatus.success?const Text("Success"):const Text("Failed to save APK"), 41 | content: processStatus==ProcessStatus.working?const LinearProgressIndicator():processStatus==ProcessStatus.success?const Text("Successfully saved APK(s) to the selected directory"):Text("Exit Code: $numAPKDownloaded"), 42 | actions: [ 43 | if(processStatus!=ProcessStatus.working) 44 | TextButton( 45 | onPressed: (){ 46 | Navigator.pop(context); 47 | }, 48 | child: const Text("OK"), 49 | ), 50 | ], 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/components/console_output.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:google_fonts/google_fonts.dart'; 3 | 4 | class ConsoleOutput extends StatelessWidget { 5 | 6 | final String consoleOutput; 7 | 8 | const ConsoleOutput({Key? key, required this.consoleOutput}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return Expanded( 13 | child: Container( 14 | width: double.infinity, 15 | color: Theme.of(context).brightness==Brightness.light?Colors.grey[300]:Colors.black, 16 | child: SingleChildScrollView( 17 | reverse: true, 18 | scrollDirection: Axis.vertical, 19 | child: Text(consoleOutput,style: GoogleFonts.inconsolata(),), 20 | ), 21 | ), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/components/custom_list_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:system_theme/system_theme.dart'; 3 | 4 | class CustomListTile extends StatelessWidget { 5 | final String title; 6 | final Icon icon; 7 | const CustomListTile({Key? key,required this.title, required this.icon}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Row( 12 | children: [ 13 | icon, 14 | const SizedBox( 15 | width: 4 , 16 | ), 17 | Text(title,style: TextStyle( 18 | color: SystemTheme.accentColor.accent, 19 | ),) 20 | ], 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/components/device_info_list_tile.dart: -------------------------------------------------------------------------------- 1 | import 'package:adb_gui/services/adb_services.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class DeviceInfoListTile extends StatelessWidget { 5 | 6 | final String propertyName; 7 | final String propertyDisplayName; 8 | final ADBService adbService; 9 | final Function? transformFunction; 10 | 11 | const DeviceInfoListTile({Key? key,required this.propertyName,required this.propertyDisplayName,required this.adbService,this.transformFunction}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return ListTile( 16 | title: Text(propertyDisplayName,style: const TextStyle( 17 | fontWeight: FontWeight.w600, 18 | fontSize: 16 19 | ),), 20 | subtitle: FutureBuilder( 21 | future: adbService.getDeviceProperty(propertyName), 22 | builder: (BuildContext context,AsyncSnapshot snapshot){ 23 | if(snapshot.connectionState==ConnectionState.waiting){ 24 | return const LinearProgressIndicator(); 25 | } 26 | if(snapshot.hasError || snapshot.data==""){ 27 | return Row( 28 | children: const [ 29 | Icon(Icons.error), 30 | SizedBox( 31 | width: 12, 32 | ), 33 | Flexible( 34 | child: Text("Error fetching information. Property might not be available on your device"), 35 | ), 36 | ], 37 | ); 38 | } 39 | return SelectableText(transformFunction==null?snapshot.data:transformFunction!(snapshot.data),); 40 | }, 41 | ), 42 | ); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/components/file_transfer_progress.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:adb_gui/components/console_output.dart'; 3 | import 'package:adb_gui/services/file_services.dart'; 4 | import 'package:adb_gui/services/string_services.dart'; 5 | import 'package:adb_gui/utils/enums.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 8 | import 'dart:async'; 9 | import 'dart:math' as math; 10 | import 'package:percent_indicator/linear_percent_indicator.dart'; 11 | 12 | class FileTransferProgress extends StatefulWidget { 13 | final Process process; 14 | final Function getSourceSize; 15 | final Function getDestinationSize; 16 | final String sourcePath; 17 | final String destinationPath; 18 | final FileTransferType fileTransferType; 19 | 20 | const FileTransferProgress({Key? key,required this.process,required this.getSourceSize, required this.getDestinationSize,required this.sourcePath, required this.destinationPath,required this.fileTransferType}) : super(key: key); 21 | 22 | @override 23 | _FileTransferProgressState createState() => _FileTransferProgressState(process,getSourceSize,getDestinationSize,sourcePath,destinationPath,fileTransferType); 24 | } 25 | 26 | class _FileTransferProgressState extends State { 27 | 28 | final Process process; 29 | final Function getSourcePathSize; 30 | final Function getDestinationPathSize; 31 | final FileTransferType fileTransferType; 32 | final String sourcePath; 33 | final String destinationPath; 34 | int exitCode=20000; 35 | String _consoleOutput="Do not panic if it looks stuck\n\n"; 36 | int sourcePathSize=0; 37 | int destinationPathSize=0; 38 | bool _calculatingProgress=false; 39 | 40 | 41 | _FileTransferProgressState(this.process,this.getSourcePathSize,this.getDestinationPathSize,this.sourcePath,this.destinationPath,this.fileTransferType); 42 | 43 | 44 | 45 | void getSourceSizeAndUpdateProgress() async{ 46 | sourcePathSize=await getSourcePathSize(sourcePath); 47 | setState(() {}); 48 | } 49 | 50 | String getSizeAsMegaBytes(int numBytes){ 51 | return (numBytes/(1024*1024)).toStringAsFixed(2); 52 | } 53 | 54 | // String convertKBtoMB(int numBytes){ 55 | // return (numBytes/1024).toStringAsFixed(2); 56 | // } 57 | 58 | 59 | 60 | 61 | void calculateProgress(){ 62 | Timer.periodic(const Duration(seconds: 2), (timer) async{ 63 | if(!_calculatingProgress && exitCode==20000){ 64 | setState(() { 65 | _calculatingProgress=true; 66 | }); 67 | destinationPathSize=math.max(destinationPathSize, await getDestinationPathSize(destinationPath+(fileTransferType==FileTransferType.phoneToPC?getPlatformDelimiter():"")+getLastPathElement(sourcePath))); 68 | setState(() { 69 | _calculatingProgress=false; 70 | }); 71 | } 72 | }); 73 | } 74 | 75 | 76 | 77 | void monitorTransferStatus() async{ 78 | exitCode=await process.exitCode; 79 | setState(() { 80 | if(exitCode==0){ 81 | _consoleOutput+="\nOperation Complete"; 82 | } 83 | }); 84 | } 85 | 86 | @override 87 | void initState() { 88 | getSourceSizeAndUpdateProgress(); 89 | calculateProgress(); 90 | process.stdout.listen((event) { 91 | setState(() { 92 | _consoleOutput+=String.fromCharCodes(event); 93 | }); 94 | }); 95 | monitorTransferStatus(); 96 | super.initState(); 97 | } 98 | 99 | 100 | @override 101 | Widget build(BuildContext context) { 102 | return Dialog( 103 | shape: const RoundedRectangleBorder( 104 | borderRadius: BorderRadius.all(Radius.circular(25)), 105 | ), 106 | elevation: 3, 107 | child: LayoutBuilder( 108 | builder: (context,constraints) { 109 | return SizedBox( 110 | height: constraints.maxHeight*0.75, 111 | width: constraints.maxWidth*0.5, 112 | child: Padding( 113 | padding: const EdgeInsets.all(12.0), 114 | child: Column( 115 | crossAxisAlignment: CrossAxisAlignment.start, 116 | mainAxisSize: MainAxisSize.max, 117 | children: [ 118 | Padding( 119 | padding: const EdgeInsets.only(bottom: 8.0), 120 | child: Row( 121 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 122 | children: [ 123 | const Text("File Transfer Operation",style: TextStyle( 124 | color: Colors.blue, 125 | fontSize: 25, 126 | ),), 127 | if(exitCode==20000) 128 | const CircularProgressIndicator() 129 | else if(exitCode==0) 130 | const Icon(FontAwesomeIcons.check,color: Colors.green,) 131 | else 132 | const Icon(Icons.cancel,color: Colors.red,) 133 | ], 134 | ), 135 | ), 136 | const Divider( 137 | height: 1, 138 | thickness: 1, 139 | ), 140 | ConsoleOutput(consoleOutput: _consoleOutput), 141 | SizedBox.fromSize( 142 | size: const Size(0,4), 143 | ), 144 | if(exitCode!=0) 145 | Column( 146 | children: [ 147 | const SizedBox( 148 | height: 8, 149 | ), 150 | LinearPercentIndicator( 151 | animateFromLastPercent: true, 152 | animation: true, 153 | padding: const EdgeInsets.all(0), 154 | progressColor: Colors.blue, 155 | percent: (sourcePathSize==0 || destinationPathSize==0 || destinationPathSize>=sourcePathSize?0:(destinationPathSize/sourcePathSize)), 156 | ), 157 | if(sourcePathSize!=0 && destinationPathSize!=0 && destinationPathSize createState() => _ObtainRootDialogState(obtainRoot,onCompleted); 13 | } 14 | 15 | class _ObtainRootDialogState extends State { 16 | 17 | Function obtainRoot; 18 | Function onCompleted; 19 | 20 | _ObtainRootDialogState(this.obtainRoot,this.onCompleted); 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return FutureBuilder( 25 | future: obtainRoot(), 26 | builder: (BuildContext context,AsyncSnapshot snapshot){ 27 | if(snapshot.connectionState==ConnectionState.waiting){ 28 | return AlertDialog( 29 | title: Text("Obtaining Root",style: TextStyle( 30 | color: SystemTheme.accentColor.accent, 31 | fontWeight: FontWeight.w600, 32 | ),), 33 | content: const Row( 34 | children: [ 35 | CircularProgressIndicator(), 36 | SizedBox( 37 | width: 12, 38 | ), 39 | Text("Obtaining Root. Grant permission from the Android device if asked.") 40 | ], 41 | ), 42 | ); 43 | } 44 | return AlertDialog( 45 | title: Text(snapshot.data!?"Obtained root successfully":"Failed to obtain root",style: TextStyle( 46 | color: SystemTheme.accentColor.accent, 47 | fontWeight: FontWeight.w600, 48 | ),), 49 | content: Text(snapshot.data!?"You are root. Act responsibly!":"Your device may not have access to root, you may not have enabled rooted debugging or you may not have granted the appropriate permissions"), 50 | actions: [ 51 | TextButton( 52 | child: Text("OK",style: TextStyle(color: SystemTheme.accentColor.accent),), 53 | onPressed: (){ 54 | Navigator.pop(context); 55 | onCompleted(); 56 | }, 57 | ), 58 | ], 59 | ); 60 | 61 | }, 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/components/page_subheading.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class PageSubheading extends StatelessWidget { 4 | final String subheadingName; 5 | const PageSubheading({Key? key,required this.subheadingName}) : super(key: key); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Padding( 10 | padding: const EdgeInsets.all(8.0), 11 | child: Column( 12 | crossAxisAlignment: CrossAxisAlignment.start, 13 | children: [ 14 | Text(subheadingName,style: Theme.of(context).textTheme.headline5,), 15 | SizedBox( 16 | width: MediaQuery.of(context).size.width, 17 | child: const Divider( 18 | thickness: 0.5, 19 | color: Colors.grey, 20 | ), 21 | ), 22 | ], 23 | ), 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/components/preference_toggle.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | 4 | class PreferenceToggle extends StatefulWidget { 5 | 6 | final String titleText; 7 | final String subtitleText; 8 | final Function getPreference; 9 | final Function onChanged; 10 | 11 | const PreferenceToggle({Key? key, required this.titleText, required this.subtitleText, required this.getPreference, required this.onChanged}) : super(key: key); 12 | 13 | @override 14 | _PreferenceToggleState createState() => _PreferenceToggleState(titleText,subtitleText,getPreference,onChanged); 15 | } 16 | 17 | class _PreferenceToggleState extends State { 18 | 19 | final String titleText; 20 | final String subtitleText; 21 | final Function getPreference; 22 | final Function onChanged; 23 | 24 | _PreferenceToggleState(this.titleText,this.subtitleText,this.getPreference,this.onChanged); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return FutureBuilder( 29 | future: getPreference(), 30 | builder: (BuildContext context,AsyncSnapshot snapshot){ 31 | if(!snapshot.hasData){ 32 | return const LinearProgressIndicator(); 33 | } 34 | return SwitchListTile( 35 | title: Text(titleText,style: const TextStyle( 36 | fontSize: 20, 37 | ),), 38 | dense: true, 39 | subtitle: Text(subtitleText), 40 | shape: RoundedRectangleBorder( 41 | borderRadius: BorderRadius.circular(18) 42 | ), 43 | value: snapshot.data!, 44 | onChanged: (value) async { 45 | await onChanged(value); 46 | setState(() {}); 47 | }, 48 | ); 49 | }, 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/components/prompt_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:system_theme/system_theme.dart'; 3 | class PromptDialog extends StatelessWidget { 4 | final String title; 5 | final String contentText; 6 | final VoidCallback onConfirm; 7 | const PromptDialog({Key? key,required this.title,required this.contentText, required this.onConfirm}) : super(key: key); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return AlertDialog( 12 | title: Text(title,style: TextStyle( 13 | color: SystemTheme.accentColor.accent, 14 | fontWeight: FontWeight.w600, 15 | ),), 16 | content: Text(contentText), 17 | actions: [ 18 | TextButton( 19 | child: Text("Cancel",style: TextStyle(color: SystemTheme.accentColor.accent),), 20 | onPressed: (){ 21 | Navigator.pop(context); 22 | }, 23 | ), 24 | TextButton( 25 | onPressed: onConfirm, 26 | child: Text("OK",style: TextStyle(color: SystemTheme.accentColor.accent),), 27 | ), 28 | ], 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/components/reinstall_system_app_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:adb_gui/components/page_subheading.dart'; 3 | import 'package:adb_gui/services/adb_services.dart'; 4 | import 'package:adb_gui/utils/enums.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:shimmer/shimmer.dart'; 7 | import 'package:system_theme/system_theme.dart'; 8 | 9 | class ReinstallSystemAppDialog extends StatefulWidget { 10 | 11 | final ADBService adbService; 12 | 13 | const ReinstallSystemAppDialog({Key? key,required this.adbService}) : super(key: key); 14 | 15 | @override 16 | _ReinstallSystemAppDialogState createState() => _ReinstallSystemAppDialogState(adbService); 17 | } 18 | 19 | class _ReinstallSystemAppDialogState extends State with SingleTickerProviderStateMixin { 20 | 21 | 22 | ProcessStatus processStatus = ProcessStatus.notStarted; 23 | final ADBService adbService; 24 | late AnimationController _animationController; 25 | late Animation _fadeAnimation; 26 | 27 | 28 | _ReinstallSystemAppDialogState(this.adbService); 29 | 30 | 31 | 32 | @override 33 | void initState() { 34 | 35 | _animationController=AnimationController( 36 | vsync: this, 37 | duration: const Duration(milliseconds: 500) 38 | ); 39 | 40 | _fadeAnimation=Tween( 41 | begin: 0, 42 | end: 1 43 | ).animate(CurvedAnimation(parent: _animationController, curve: Curves.decelerate)); 44 | 45 | super.initState(); 46 | } 47 | 48 | @override 49 | void dispose() { 50 | _animationController.dispose(); 51 | super.dispose(); 52 | } 53 | 54 | 55 | @override 56 | Widget build(BuildContext context) { 57 | return Dialog( 58 | shape: const RoundedRectangleBorder( 59 | borderRadius: BorderRadius.all(Radius.circular(25)), 60 | ), 61 | elevation: 3, 62 | child: LayoutBuilder( 63 | builder: (context,constraints){ 64 | return SizedBox( 65 | height: constraints.maxHeight*0.75, 66 | width: constraints.maxWidth*0.5, 67 | child: Padding( 68 | padding: const EdgeInsets.all(12), 69 | child: Column( 70 | children: [ 71 | const PageSubheading(subheadingName: "Reinstall System App"), 72 | const SizedBox( 73 | height: 4, 74 | ), 75 | Expanded( 76 | child: FutureBuilder( 77 | future: adbService.getUninstalledSystemApps(), 78 | builder: (BuildContext context,AsyncSnapshot> snapshot) { 79 | 80 | _animationController.forward(from: 0); 81 | 82 | if(snapshot.connectionState != ConnectionState.done || !snapshot.hasData){ 83 | return FadeTransition( 84 | opacity: _fadeAnimation, 85 | child: Shimmer.fromColors( 86 | baseColor: Theme.of(context).brightness==Brightness.light?const Color(0xFFE0E0E0):Colors.black12, 87 | highlightColor: Theme.of(context).brightness==Brightness.light?const Color(0xFFF5F5F5):Colors.blueGrey, 88 | enabled: true, 89 | child: const GetEmptyListView() 90 | ), 91 | ); 92 | } 93 | 94 | if(snapshot.data!.isEmpty){ 95 | return FadeTransition( 96 | opacity: _fadeAnimation, 97 | child: Column( 98 | mainAxisAlignment: MainAxisAlignment.center, 99 | children: const [ 100 | Icon(Icons.android,color: Colors.grey,size: 100,), 101 | Text( 102 | "No System App to Install", 103 | style: TextStyle(color: Colors.grey, fontSize: 25), 104 | ) 105 | ], 106 | ), 107 | ); 108 | } 109 | 110 | return FadeTransition( 111 | opacity: _fadeAnimation, 112 | child: ListView.builder( 113 | itemCount: snapshot.data!.length, 114 | itemBuilder: (context,index){ 115 | return ListTile( 116 | leading: const Icon(Icons.android,color: Colors.green,size: 35,), 117 | title: Text(snapshot.data![index]), 118 | onTap: () async{ 119 | Process process = await adbService.reinstallSystemAppForUser(packageName: snapshot.data![index]); 120 | await showDialog( 121 | context: context, 122 | builder: (context)=>ReinstallProgressDialog( 123 | process: process, 124 | ), 125 | ); 126 | setState(() {}); 127 | }, 128 | ); 129 | }, 130 | ), 131 | ); 132 | } 133 | ), 134 | ), 135 | const SizedBox( 136 | height: 8, 137 | ), 138 | MaterialButton( 139 | shape: RoundedRectangleBorder( 140 | borderRadius: BorderRadius.circular(20) 141 | ), 142 | color: SystemTheme.accentColor.accent, 143 | disabledColor: Colors.grey, 144 | onPressed: processStatus==ProcessStatus.working?null:(){ 145 | Navigator.pop(context); 146 | }, 147 | child: const Padding( 148 | padding: EdgeInsets.symmetric(horizontal: 20,vertical: 8), 149 | child: Text("Close",style: TextStyle(color: Colors.white),), 150 | ), 151 | ), 152 | ], 153 | ), 154 | ), 155 | ); 156 | }, 157 | ), 158 | ); 159 | } 160 | } 161 | 162 | class GetEmptyListView extends StatelessWidget { 163 | const GetEmptyListView({Key? key}) : super(key: key); 164 | 165 | @override 166 | Widget build(BuildContext context) { 167 | return ListView.builder( 168 | itemCount: 20, 169 | itemBuilder: (context,index){ 170 | return ListTile( 171 | leading: Container( 172 | width: 35, 173 | height: 35, 174 | decoration: const BoxDecoration( 175 | color: Colors.black, 176 | shape: BoxShape.circle 177 | ), 178 | ), 179 | title: Container( 180 | width: 50, 181 | height: 25, 182 | decoration: BoxDecoration( 183 | color: Colors.black, 184 | borderRadius: BorderRadius.circular(25) 185 | ), 186 | ), 187 | ); 188 | }, 189 | ); 190 | } 191 | } 192 | 193 | 194 | class ReinstallProgressDialog extends StatefulWidget { 195 | final Process process; 196 | 197 | const ReinstallProgressDialog({Key? key, required this.process}) : super(key: key); 198 | 199 | @override 200 | _ReinstallProgressDialogState createState() => _ReinstallProgressDialogState(process); 201 | } 202 | 203 | class _ReinstallProgressDialogState extends State { 204 | 205 | int exitCode = 20000; 206 | 207 | final Process process; 208 | 209 | _ReinstallProgressDialogState(this.process); 210 | 211 | void monitorStatus() async{ 212 | exitCode = await process.exitCode; 213 | setState(() {}); 214 | } 215 | 216 | @override 217 | void initState() { 218 | monitorStatus(); 219 | super.initState(); 220 | } 221 | 222 | 223 | @override 224 | Widget build(BuildContext context) { 225 | return AlertDialog( 226 | title: Text(exitCode==20000?"Reinstalling":exitCode==0?"Install success":"Installation failed"), 227 | content: exitCode==20000?Row( 228 | children: const [ 229 | CircularProgressIndicator(), 230 | SizedBox( 231 | width: 16, 232 | ), 233 | Text("Reinstall in progress") 234 | ], 235 | ):Text(exitCode==0?"System app reinstall successful":"Error occurred when reinstalling"), 236 | actions: [ 237 | if(exitCode!=20000) 238 | TextButton( 239 | child: Text("OK",style: TextStyle( 240 | color: SystemTheme.accentColor.accent 241 | ),), 242 | onPressed: (){ 243 | Navigator.pop(context); 244 | }, 245 | ), 246 | ], 247 | ); 248 | } 249 | } 250 | 251 | -------------------------------------------------------------------------------- /lib/components/select_compilation_mode_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:adb_gui/services/adb_services.dart'; 2 | import 'package:adb_gui/utils/enums.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:system_theme/system_theme.dart'; 5 | 6 | 7 | class SelectCompilationModeDialog extends StatefulWidget { 8 | final String packageName; 9 | final ADBService adbService; 10 | const SelectCompilationModeDialog({Key? key, required this.packageName, required this.adbService}) : super(key: key); 11 | 12 | @override 13 | _SelectCompilationModeDialogState createState() => _SelectCompilationModeDialogState(packageName,adbService); 14 | } 15 | 16 | class _SelectCompilationModeDialogState extends State { 17 | 18 | final String packageName; 19 | final ADBService adbService; 20 | 21 | _SelectCompilationModeDialogState(this.packageName,this.adbService); 22 | 23 | CompilationMode selectedCompilationMode = CompilationMode.speed; 24 | ProcessStatus processStatus = ProcessStatus.notStarted; 25 | 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | return AlertDialog( 30 | title: Row( 31 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 32 | children: [ 33 | Text(processStatus==ProcessStatus.working?"Compiling":"Select Compilation Mode"), 34 | const SizedBox( 35 | width: 20, 36 | ), 37 | IconButton( 38 | icon: Icon(Icons.info_outline_rounded,color: SystemTheme.accentColor.accent,), 39 | onPressed: (){ 40 | showDialog( 41 | context: context, 42 | builder: (context)=>AlertDialog( 43 | title: const Text("Info"), 44 | content: const Text("The JIT (Just In Time) compiler compiles an app on launch. This can save space but since it does compilation on launch, some apps may take more time to launch. On the other hand, you can also choose to compile an app beforehand thus improving launch times but this will require more space on your storage. You may choose a compilation mode which suits your needs."), 45 | actions: [ 46 | TextButton( 47 | child: const Text("Close"), 48 | onPressed: (){ 49 | Navigator.pop(context); 50 | }, 51 | ), 52 | ], 53 | ), 54 | ); 55 | }, 56 | ), 57 | ], 58 | ), 59 | content: Builder( 60 | builder: (context) { 61 | if(processStatus==ProcessStatus.working){ 62 | return Row( 63 | children: [ 64 | const CircularProgressIndicator(), 65 | const SizedBox( 66 | width: 12, 67 | ), 68 | Text("Compiling $packageName") 69 | ], 70 | ); 71 | } 72 | if(processStatus==ProcessStatus.success){ 73 | return Text("$packageName compiled successfully"); 74 | } 75 | if(processStatus==ProcessStatus.fail){ 76 | return Text("Failed to compile $packageName"); 77 | } 78 | return SingleChildScrollView( 79 | child: Column( 80 | mainAxisSize: MainAxisSize.min, 81 | children: [ 82 | CompilationModeOption( 83 | value: selectedCompilationMode, 84 | groupValue: CompilationMode.speed, 85 | label: "Speed", 86 | toolTipMessage: "Run DEX code verification and AOT-compile all methods", 87 | onClick: (value){ 88 | setState(() { 89 | selectedCompilationMode=CompilationMode.speed; 90 | }); 91 | }, 92 | ), 93 | CompilationModeOption( 94 | value: selectedCompilationMode, 95 | groupValue: CompilationMode.speedProfile, 96 | label: "Speed-Profile", 97 | toolTipMessage: "Run DEX code verification and AOT-compile methods listed in a profile file", 98 | onClick: (value){ 99 | setState(() { 100 | selectedCompilationMode=CompilationMode.speedProfile; 101 | }); 102 | }, 103 | ), 104 | CompilationModeOption( 105 | value: selectedCompilationMode, 106 | groupValue: CompilationMode.quicken, 107 | label: "Quicken", 108 | toolTipMessage: "Runs DEX code verification and optimizes some DEX instructions to get better interpreter performance", 109 | onClick: (value){ 110 | setState(() { 111 | selectedCompilationMode=CompilationMode.quicken; 112 | }); 113 | }, 114 | ), 115 | CompilationModeOption( 116 | value: selectedCompilationMode, 117 | groupValue: CompilationMode.space, 118 | label: "Space", 119 | toolTipMessage: "Reduces space usage but requires compilation on start", 120 | onClick: (value){ 121 | setState(() { 122 | selectedCompilationMode=CompilationMode.space; 123 | }); 124 | }, 125 | ), 126 | CompilationModeOption( 127 | value: selectedCompilationMode, 128 | groupValue: CompilationMode.spaceProfile, 129 | label: "Space-Profile", 130 | toolTipMessage: "Reduces space usage but requires compilation on start", 131 | onClick: (value){ 132 | setState(() { 133 | selectedCompilationMode=CompilationMode.spaceProfile; 134 | }); 135 | }, 136 | ), 137 | CompilationModeOption( 138 | value: selectedCompilationMode, 139 | groupValue: CompilationMode.everything, 140 | label: "Everything", 141 | toolTipMessage: "Compile everything", 142 | onClick: (value){ 143 | setState(() { 144 | selectedCompilationMode=CompilationMode.everything; 145 | }); 146 | }, 147 | ), 148 | ], 149 | ), 150 | ); 151 | } 152 | ), 153 | actions: [ 154 | if(processStatus==ProcessStatus.notStarted) 155 | TextButton( 156 | child: const Text("Cancel"), 157 | onPressed: (){ 158 | Navigator.pop(context); 159 | }, 160 | ), 161 | if(processStatus!=ProcessStatus.working) 162 | TextButton( 163 | child: const Text("OK"), 164 | onPressed: () async{ 165 | if(processStatus==ProcessStatus.notStarted){ 166 | setState(() { 167 | processStatus=ProcessStatus.working; 168 | }); 169 | if((await adbService.compileApp(packageName, selectedCompilationMode))==0){ 170 | setState(() { 171 | processStatus=ProcessStatus.success; 172 | }); 173 | }else{ 174 | setState(() { 175 | processStatus=ProcessStatus.fail; 176 | }); 177 | } 178 | }else{ 179 | Navigator.pop(context); 180 | } 181 | }, 182 | ), 183 | ], 184 | ); 185 | } 186 | } 187 | 188 | 189 | class CompilationModeOption extends StatelessWidget { 190 | 191 | final CompilationMode value; 192 | final CompilationMode groupValue; 193 | final String label; 194 | final String toolTipMessage; 195 | final Function onClick; 196 | 197 | const CompilationModeOption({Key? key, required this.value, required this.groupValue, required this.label, required this.toolTipMessage, required this.onClick}) : super(key: key); 198 | 199 | @override 200 | Widget build(BuildContext context) { 201 | return MaterialButton( 202 | onPressed: (){ 203 | onClick(groupValue); 204 | }, 205 | child: Tooltip( 206 | message: toolTipMessage, 207 | child: Padding( 208 | padding: const EdgeInsets.all(8.0), 209 | child: Row( 210 | children: [ 211 | Radio( 212 | value: value, 213 | groupValue: groupValue, 214 | onChanged: (value){ 215 | onClick(value); 216 | }, 217 | ), 218 | Text(label), 219 | ], 220 | ), 221 | ), 222 | ), 223 | ); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /lib/components/set_app_installer_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'package:adb_gui/components/page_subheading.dart'; 2 | import 'package:adb_gui/utils/enums.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 5 | 6 | class SetAppInstallerDialog extends StatefulWidget { 7 | const SetAppInstallerDialog({Key? key}) : super(key: key); 8 | 9 | @override 10 | _SetAppInstallerDialogState createState() => _SetAppInstallerDialogState(); 11 | } 12 | 13 | class _SetAppInstallerDialogState extends State { 14 | 15 | 16 | AppInstaller selectedAppInstaller = AppInstaller.googlePlayStore; 17 | 18 | late final TextEditingController appInstallerController; 19 | 20 | 21 | 22 | void setInstaller(){ 23 | 24 | } 25 | 26 | 27 | 28 | @override 29 | void initState() { 30 | appInstallerController = TextEditingController(); 31 | super.initState(); 32 | } 33 | 34 | @override 35 | void dispose() { 36 | appInstallerController.dispose(); 37 | super.dispose(); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return Dialog( 43 | shape: const RoundedRectangleBorder( 44 | borderRadius: BorderRadius.all(Radius.circular(25)), 45 | ), 46 | elevation: 3, 47 | child: LayoutBuilder( 48 | builder: (context,constraints){ 49 | return SizedBox( 50 | height: constraints.maxHeight*0.4, 51 | width: constraints.maxWidth*0.6, 52 | child: Padding( 53 | padding: const EdgeInsets.all(12.0), 54 | child: Column( 55 | children: [ 56 | const PageSubheading(subheadingName: "Select Installer"), 57 | Expanded( 58 | child: SingleChildScrollView( 59 | child: Column( 60 | children: [ 61 | InstallerAppListTile( 62 | installerIcon: const Icon(FontAwesomeIcons.googlePlay), 63 | installerName: "Google Play Store", 64 | value: selectedAppInstaller, 65 | groupValue: AppInstaller.googlePlayStore, 66 | onPressed: (value){ 67 | setState(() { 68 | selectedAppInstaller = AppInstaller.googlePlayStore; 69 | }); 70 | }, 71 | ), 72 | ListTile( 73 | leading: Radio( 74 | value: selectedAppInstaller, 75 | groupValue: AppInstaller.custom, 76 | onChanged: (value){ 77 | setState(() { 78 | selectedAppInstaller=AppInstaller.custom; 79 | }); 80 | }, 81 | ), 82 | title: TextField( 83 | controller: appInstallerController, 84 | decoration: InputDecoration( 85 | enabled: selectedAppInstaller==AppInstaller.custom, 86 | hintText: "com.android.vending", 87 | hintStyle: TextStyle( 88 | color: Colors.grey[500], 89 | ), 90 | ), 91 | ), 92 | ), 93 | ], 94 | ), 95 | ), 96 | ), 97 | Align( 98 | alignment: Alignment.bottomRight, 99 | child: TextButton( 100 | child: const Text("OK",), 101 | onPressed: () async{ 102 | Navigator.pop(context); 103 | }, 104 | ), 105 | ), 106 | ], 107 | ), 108 | ), 109 | ); 110 | }, 111 | ), 112 | ); 113 | } 114 | } 115 | 116 | class InstallerAppListTile extends StatelessWidget { 117 | 118 | final Icon installerIcon; 119 | final String installerName; 120 | final AppInstaller value; 121 | final AppInstaller groupValue; 122 | final Function onPressed; 123 | 124 | const InstallerAppListTile({Key? key,required this.installerIcon, required this.installerName, required this.value, required this.groupValue, required this.onPressed}) : super(key: key); 125 | 126 | @override 127 | Widget build(BuildContext context) { 128 | return ListTile( 129 | leading: Radio( 130 | value: value, 131 | groupValue: groupValue, 132 | onChanged: (value){ 133 | onPressed(value); 134 | }, 135 | ), 136 | title: Row( 137 | children: [ 138 | installerIcon, 139 | const SizedBox( 140 | width: 12, 141 | ), 142 | Text(installerName,style: Theme.of(context).textTheme.headline6), 143 | ], 144 | ), 145 | ); 146 | } 147 | } 148 | 149 | -------------------------------------------------------------------------------- /lib/components/simple_file_transfer_progress.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:adb_gui/utils/enums.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:system_theme/system_theme.dart'; 5 | 6 | 7 | class SimpleFileTransferProgress extends StatefulWidget { 8 | final Process process; 9 | final FileTransferType fileTransferType; 10 | const SimpleFileTransferProgress({Key? key,required this.process,required this.fileTransferType}) : super(key: key); 11 | 12 | @override 13 | _SimpleFileTransferProgressState createState() => _SimpleFileTransferProgressState(process,fileTransferType); 14 | } 15 | 16 | class _SimpleFileTransferProgressState extends State { 17 | 18 | final Process process; 19 | final FileTransferType fileTransferType; 20 | 21 | int exitCode=20000; 22 | 23 | _SimpleFileTransferProgressState(this.process,this.fileTransferType); 24 | 25 | 26 | 27 | void monitorTransferStatus() async{ 28 | exitCode=await process.exitCode; 29 | setState(() {}); 30 | } 31 | 32 | @override 33 | void initState() { 34 | monitorTransferStatus(); 35 | super.initState(); 36 | } 37 | 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | return AlertDialog( 42 | title: Text("File ${fileTransferType==FileTransferType.move?"move":"copy"}",style: TextStyle( 43 | color: SystemTheme.accentColor.accent 44 | ),), 45 | content: exitCode==20000?const LinearProgressIndicator():exitCode==0?Text("File ${fileTransferType==FileTransferType.move?"move":"copy"} complete"):Text("File ${fileTransferType==FileTransferType.move?"move":"copy"} failed"), 46 | actions: [ 47 | TextButton( 48 | onPressed: exitCode==20000?null:(){ 49 | Navigator.pop(context); 50 | }, 51 | child: Text("Close",style: TextStyle( 52 | color: SystemTheme.accentColor.accent 53 | ),), 54 | ) 55 | ], 56 | ); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/components/simple_rectangle_icon_material_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:system_theme/system_theme.dart'; 3 | 4 | class SimpleRectangleIconMaterialButton extends StatelessWidget { 5 | final Icon buttonIcon; 6 | final String buttonText; 7 | final VoidCallback onPressed; 8 | const SimpleRectangleIconMaterialButton({Key? key,required this.buttonIcon,required this.buttonText, required this.onPressed}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | return MaterialButton( 13 | shape: RoundedRectangleBorder( 14 | borderRadius: BorderRadius.circular(18) 15 | ), 16 | onPressed: onPressed, 17 | child: Padding( 18 | padding: const EdgeInsets.all(12.0), 19 | child: Column( 20 | children: [ 21 | buttonIcon, 22 | Text(buttonText,style: TextStyle( 23 | color: SystemTheme.accentColor.accent, 24 | fontWeight: FontWeight.w700, 25 | ),), 26 | ], 27 | ), 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/components/simple_text_field_alert.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:system_theme/system_theme.dart'; 3 | 4 | 5 | class SimpleTextFieldAlert extends StatelessWidget { 6 | final String title; 7 | final TextEditingController textFieldController; 8 | final String hintText; 9 | final VoidCallback action; 10 | const SimpleTextFieldAlert({Key? key,required this.title, required this.textFieldController, required this.hintText, required this.action}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return AlertDialog( 15 | title: Text(title,style: TextStyle( 16 | color: SystemTheme.accentColor.accent 17 | ),), 18 | content: TextField( 19 | controller: textFieldController, 20 | decoration: InputDecoration( 21 | border: OutlineInputBorder( 22 | borderRadius: BorderRadius.circular(10) 23 | ), 24 | focusColor: SystemTheme.accentColor.accent, 25 | hintText: hintText, 26 | ), 27 | ), 28 | actions: [ 29 | TextButton(onPressed: action, child: Text("OK",style: TextStyle(color: SystemTheme.accentColor.accent),)) 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/components/updater_dialog.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:adb_gui/services/update_services.dart'; 3 | import 'package:adb_gui/utils/enums.dart'; 4 | import 'package:bitsdojo_window/bitsdojo_window.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:system_theme/system_theme.dart'; 7 | 8 | class UpdaterDialog extends StatefulWidget { 9 | final Map updateInfo; 10 | const UpdaterDialog({Key? key,required this.updateInfo}) : super(key: key); 11 | 12 | @override 13 | _UpdaterDialogState createState() => _UpdaterDialogState(updateInfo); 14 | } 15 | 16 | class _UpdaterDialogState extends State { 17 | 18 | final Map updateInfo; 19 | 20 | _UpdaterDialogState(this.updateInfo); 21 | 22 | bool _isDownloading=false; 23 | bool _error=false; 24 | 25 | Widget _getDialogTitle(){ 26 | if(_isDownloading){ 27 | return Text("Downloading Update",style: TextStyle( 28 | color: SystemTheme.accentColor.accent 29 | ),); 30 | }else if(_error){ 31 | return const Text("Download failed"); 32 | } 33 | return Text("New update available! ${(updateInfo['preRelease']!=null && updateInfo['preRelease'])?"(Prerelease)":""}",style: TextStyle( 34 | color: SystemTheme.accentColor.accent 35 | ),); 36 | } 37 | 38 | Widget _getDialogContent(){ 39 | if(_isDownloading){ 40 | return const Row( 41 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 42 | children: [ 43 | Text("Update download in progress",style: TextStyle( 44 | ), 45 | ), 46 | SizedBox( 47 | width: 25, 48 | ), 49 | CircularProgressIndicator() 50 | ], 51 | ); 52 | }else if(_error){ 53 | return const Text("Check your internet connection and try again"); 54 | } 55 | return Text("A new update is available to download! Version: ${updateInfo['version']}"); 56 | } 57 | 58 | @override 59 | Widget build(BuildContext context) { 60 | return AlertDialog( 61 | title: _getDialogTitle(), 62 | content: _getDialogContent(), 63 | actions: [ 64 | if(!_isDownloading) 65 | Padding( 66 | padding: const EdgeInsets.all(8.0), 67 | child: TextButton( 68 | child: Text("Close",style: TextStyle( 69 | color: SystemTheme.accentColor.accent 70 | ),), 71 | onPressed: (){ 72 | Navigator.pop(context); 73 | }, 74 | ), 75 | ), 76 | if(!_isDownloading) 77 | Padding( 78 | padding: const EdgeInsets.all(8), 79 | child: UpdateNowButton( 80 | updateFileLink: updateInfo['assetLink'], 81 | beforeExecution: (){ 82 | setState(() { 83 | _isDownloading=true; 84 | }); 85 | }, 86 | onError: (){ 87 | setState(() { 88 | _error=true; 89 | }); 90 | }, 91 | afterExecution: (){ 92 | setState(() { 93 | _isDownloading=false; 94 | }); 95 | }, 96 | ) 97 | ) 98 | ], 99 | ); 100 | } 101 | } 102 | 103 | class BackgroundUpdateUI extends StatefulWidget { 104 | final Map updateInfo; 105 | const BackgroundUpdateUI({Key? key,required this.updateInfo}) : super(key: key); 106 | 107 | @override 108 | _BackgroundUpdateUIState createState() => _BackgroundUpdateUIState(updateInfo); 109 | } 110 | 111 | class _BackgroundUpdateUIState extends State { 112 | 113 | ProcessStatus processStatus = ProcessStatus.notStarted; 114 | final Map updateInfo; 115 | 116 | _BackgroundUpdateUIState(this.updateInfo); 117 | 118 | @override 119 | Widget build(BuildContext context) { 120 | return Row( 121 | children: [ 122 | if(processStatus==ProcessStatus.notStarted)...[ 123 | const Icon(Icons.update_rounded), 124 | const SizedBox( 125 | width: 16, 126 | ), 127 | Text("A new update is available to download! Version: ${updateInfo['version']} ${(updateInfo['preRelease']!=null && updateInfo['preRelease'])?"(Prerelease)":""}"), 128 | const Spacer(), 129 | UpdateNowButton( 130 | updateFileLink: updateInfo['assetLink'], 131 | beforeExecution: (){ 132 | setState(() { 133 | processStatus = ProcessStatus.working; 134 | }); 135 | }, 136 | onError: (){ 137 | setState(() { 138 | processStatus = ProcessStatus.fail; 139 | }); 140 | }, 141 | afterExecution: (){ 142 | setState(() { 143 | processStatus = ProcessStatus.success; 144 | }); 145 | }, 146 | ), 147 | ], 148 | if(processStatus==ProcessStatus.working)...[ 149 | const CircularProgressIndicator(), 150 | const SizedBox( 151 | width: 16, 152 | ), 153 | const Text("Downloading Update"), 154 | ], 155 | if(processStatus==ProcessStatus.fail)...[ 156 | const Icon(Icons.cancel,color: Colors.red,), 157 | const SizedBox( 158 | width: 16, 159 | ), 160 | const Text("Failed to download update"), 161 | ], 162 | if(processStatus==ProcessStatus.success)...[ 163 | const Icon(Icons.check_circle,color: Colors.green,), 164 | const SizedBox( 165 | width: 16, 166 | ), 167 | const Text("Update download successful"), 168 | ], 169 | ], 170 | ); 171 | } 172 | } 173 | 174 | 175 | class UpdateNowButton extends CloseWindowButton{ 176 | final VoidCallback beforeExecution; 177 | final VoidCallback onError; 178 | final VoidCallback afterExecution; 179 | final String updateFileLink; 180 | final bool disabled; 181 | UpdateNowButton({Key? key,required this.updateFileLink,required this.beforeExecution,required this.onError,required this.afterExecution,this.disabled=false}) : super(key: key); 182 | 183 | void installUpdate(String pathToFile) async{ 184 | // await Process.run(adbExecutable, ["kill-server"],runInShell: true); 185 | if(Platform.isWindows) { 186 | Process.run("start",[pathToFile],runInShell: true); 187 | Process.run("taskkill", ["/IM","adb.exe","/F"],runInShell: true); 188 | } else if(Platform.isLinux) { 189 | Process.run("nautilus", [pathToFile],runInShell: true); 190 | } else if(Platform.isMacOS) { 191 | Process.run("open", [pathToFile], runInShell: true); 192 | } 193 | super.onPressed!(); 194 | } 195 | 196 | @override 197 | Widget build(BuildContext context){ 198 | return TextButton( 199 | onPressed: disabled?null:() async{ 200 | beforeExecution(); 201 | try{ 202 | // print(await downloadRelease(updateFileLink)); 203 | installUpdate(await downloadRelease(updateFileLink)); 204 | 205 | }catch(e){ 206 | onError(); 207 | return; 208 | } 209 | afterExecution(); 210 | }, 211 | child: Text("Update Now",style: TextStyle( 212 | color: SystemTheme.accentColor.accent 213 | ),), 214 | ); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /lib/components/window_buttons.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:adb_gui/services/shared_prefs.dart'; 3 | import 'package:bitsdojo_window/bitsdojo_window.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:adb_gui/utils/vars.dart'; 6 | import 'package:system_theme/system_theme.dart'; 7 | 8 | import '../utils/const.dart'; 9 | 10 | class CustomMinimizeWindowButton extends MinimizeWindowButton{ 11 | CustomMinimizeWindowButton({Key? key}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context){ 15 | 16 | return WindowMaterialButton( 17 | // buttonColor: Colors.blue, 18 | hoverColor: SystemTheme.accentColor.accent, 19 | buttonIcon: Icon(Icons.minimize,color: Theme.of(context).brightness == Brightness.dark?Colors.white:kLightModeAppBarTitleColor,), 20 | onPressed: super.onPressed!, 21 | ); 22 | } 23 | 24 | 25 | } 26 | 27 | class CustomMaximizeWindowButton extends MaximizeWindowButton{ 28 | CustomMaximizeWindowButton({Key? key}) : super(key: key); 29 | 30 | @override 31 | Widget build(BuildContext context){ 32 | return WindowMaterialButton( 33 | // buttonColor: Colors.blue, 34 | hoverColor: SystemTheme.accentColor.accent, 35 | buttonIcon: Icon(Icons.check_box_outline_blank,color: Theme.of(context).brightness == Brightness.dark?Colors.white:kLightModeAppBarTitleColor,), 36 | onPressed: super.onPressed!, 37 | ); 38 | } 39 | } 40 | 41 | class CustomCloseWindowButton extends CloseWindowButton{ 42 | CustomCloseWindowButton({Key? key}) : super(key: key); 43 | 44 | @override 45 | Widget build(BuildContext context){ 46 | return WindowMaterialButton( 47 | // buttonColor: Colors.blue, 48 | hoverColor: Colors.redAccent, 49 | darkModeHoverColor: Colors.redAccent, 50 | buttonIcon: Icon(Icons.close,color: Theme.of(context).brightness == Brightness.dark?Colors.white:kLightModeAppBarTitleColor,), 51 | onPressed: () async{ 52 | if((await getKillADBOnExitPreference())!){ 53 | await Process.run(adbExecutable, ["kill-server"],runInShell: true); 54 | if(Platform.isWindows){ 55 | await Process.run("taskkill", ["/IM","adb.exe","/F"],runInShell: true); 56 | } 57 | } 58 | super.onPressed!(); 59 | }, 60 | ); 61 | } 62 | } 63 | 64 | 65 | class WindowMaterialButton extends StatelessWidget { 66 | 67 | // final Color? buttonColor; 68 | final Color? hoverColor; 69 | final Color? darkModeHoverColor; 70 | final Icon buttonIcon; 71 | final Color? buttonColor; 72 | final VoidCallback onPressed; 73 | 74 | const WindowMaterialButton({Key? key,this.hoverColor=Colors.black26, this.darkModeHoverColor=Colors.black26,required this.buttonIcon, this.buttonColor = Colors.transparent,required this.onPressed}) : super(key: key); 75 | 76 | @override 77 | Widget build(BuildContext context) { 78 | return MaterialButton( 79 | shape: const CircleBorder(), 80 | color: buttonColor, 81 | elevation: 0, 82 | minWidth: 8, 83 | hoverColor: Theme.of(context).brightness==Brightness.light?hoverColor:darkModeHoverColor, 84 | height: 150, 85 | materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, 86 | onPressed: onPressed, 87 | child: Padding( 88 | padding: const EdgeInsets.all(8.0), 89 | child: buttonIcon, 90 | ), 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:adb_gui/screens/connection_initiation_screen.dart'; 2 | import 'package:adb_gui/screens/theme_mode_service.dart'; 3 | import 'package:adb_gui/services/shared_prefs.dart'; 4 | import 'package:adb_gui/utils/const.dart'; 5 | import 'package:bitsdojo_window/bitsdojo_window.dart'; 6 | import 'package:flutter/foundation.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter/scheduler.dart'; 9 | import 'package:flutter_acrylic/flutter_acrylic.dart'; 10 | import 'utils/vars.dart'; 11 | import 'dart:io'; 12 | import 'services/platform_services.dart'; 13 | import 'package:google_fonts/google_fonts.dart'; 14 | import 'package:system_theme/system_theme.dart'; 15 | 16 | void main() async { 17 | 18 | if(kDebugMode && (Platform.isWindows)){ 19 | adbExecutable="adb"; 20 | }else{ 21 | if(Platform.isWindows){ 22 | adbExecutable="data/flutter_assets/assets/adb.exe"; 23 | }else if(Platform.isLinux){ 24 | adbExecutable="data/flutter_assets/assets/adb"; 25 | }else if(Platform.isMacOS){ 26 | List pathToAppExecList = Platform.resolvedExecutable.split("/"); 27 | pathToAppExecList = pathToAppExecList.sublist(0, pathToAppExecList.length - 2); 28 | adbExecutable="${pathToAppExecList.join("/")}/Frameworks/App.framework/Versions/A/Resources/flutter_assets/assets/adbMac"; 29 | } 30 | } 31 | 32 | WidgetsFlutterBinding.ensureInitialized(); 33 | 34 | await Window.initialize(); 35 | Window.makeTitlebarTransparent(); 36 | // Window.enableFullSizeContentView(); 37 | 38 | SystemTheme.fallbackColor = const Color(0xFF2196F3); 39 | await SystemTheme.accentColor.load(); 40 | runApp(const TitlebarSafeArea(child: MyApp())); 41 | 42 | doWhenWindowReady(() { 43 | const initialSize = Size(1000, 625); 44 | appWindow.minSize = const Size(850, 525); 45 | appWindow.size = initialSize; 46 | appWindow.alignment = Alignment.center; 47 | appWindow.title="Android Toolbox"; 48 | appWindow.show(); 49 | }); 50 | } 51 | 52 | 53 | class MyApp extends StatefulWidget { 54 | const MyApp({Key? key}) : super(key: key); 55 | 56 | @override 57 | State createState() => _MyAppState(); 58 | } 59 | 60 | class _MyAppState extends State { 61 | 62 | final Color accentColor = SystemTheme.accentColor.accent; 63 | 64 | 65 | void _setWindowTheme(ThemeMode themeModePreference) async{ 66 | if(Platform.isWindows){ 67 | await Window.hideWindowControls(); 68 | if(isWindows11()){ 69 | await Window.setEffect( 70 | effect: WindowEffect.mica, 71 | dark: themeModePreference==ThemeMode.dark?true:themeModePreference==ThemeMode.light?false:SchedulerBinding.instance.window.platformBrightness==Brightness.dark 72 | ); 73 | }else{ 74 | await Window.setEffect( 75 | effect: WindowEffect.acrylic, 76 | color: kDarkModeMenuColor.withOpacity(0.75), 77 | ); 78 | } 79 | }else if(Platform.isLinux){ 80 | await Window.setEffect( 81 | effect: WindowEffect.solid, 82 | color: themeModePreference==ThemeMode.dark?kDarkModeMenuColor:themeModePreference==ThemeMode.light?Colors.white70:SchedulerBinding.instance.window.platformBrightness==Brightness.dark?kDarkModeMenuColor:Colors.white70, 83 | ); 84 | } else if(Platform.isMacOS) { 85 | // Window.setBlurViewState(MacOSBlurViewState.followsWindowActiveState); 86 | await Window.setEffect( 87 | effect: WindowEffect.sidebar, 88 | // color: Colors.black 89 | ); 90 | // if(themeModePreference == ThemeMode.dark) { 91 | // Window.overrideMacOSBrightness(dark: true); 92 | // } else { 93 | // Window.overrideMacOSBrightness(dark: false); 94 | // } 95 | } 96 | } 97 | 98 | @override 99 | void initState() { 100 | themeModeService.addListener(() { 101 | setState(() {}); 102 | }); 103 | SystemTheme.onChange.listen((event) { 104 | setState(() {}); 105 | }); 106 | super.initState(); 107 | } 108 | 109 | @override 110 | Widget build(BuildContext context) { 111 | return FutureBuilder( 112 | future: getThemeModePreference(), 113 | builder: (BuildContext context,AsyncSnapshot snapshot){ 114 | if(snapshot.connectionState==ConnectionState.waiting){ 115 | return const Center(child: CircularProgressIndicator(),); 116 | } 117 | _setWindowTheme(snapshot.data!); 118 | return MaterialApp( 119 | themeMode: snapshot.data!, 120 | theme: ThemeData( 121 | useMaterial3: true, 122 | colorScheme: ColorScheme.fromSeed(seedColor: accentColor, secondary: accentColor), 123 | // primaryColor: kAccentColor, 124 | appBarTheme: const AppBarTheme( 125 | backgroundColor: Colors.transparent, 126 | ), 127 | // toggleableActiveColor: kAccentColor, 128 | progressIndicatorTheme: ProgressIndicatorThemeData( 129 | color: accentColor, 130 | ), 131 | scaffoldBackgroundColor: Platform.isLinux?Colors.white:Colors.transparent, 132 | textTheme: TextTheme( 133 | displayLarge: GoogleFonts.quicksand(), 134 | displayMedium: GoogleFonts.quicksand(), 135 | displaySmall: GoogleFonts.quicksand(color: accentColor,fontSize: 40,), 136 | headlineMedium: GoogleFonts.quicksand(), 137 | headlineSmall: GoogleFonts.quicksand(color: accentColor,fontSize: 25,fontWeight: FontWeight.w600), 138 | titleLarge: GoogleFonts.quicksand(fontSize: 20,fontWeight: FontWeight.w500,), 139 | titleMedium: GoogleFonts.quicksand(fontSize: 15), 140 | titleSmall: GoogleFonts.quicksand(), 141 | bodyLarge: GoogleFonts.quicksand(), 142 | bodyMedium: GoogleFonts.quicksand(), 143 | labelLarge: GoogleFonts.quicksand(), 144 | bodySmall: GoogleFonts.quicksand(), 145 | ), 146 | dialogTheme: DialogTheme( 147 | titleTextStyle: GoogleFonts.quicksand( 148 | fontSize: 20, 149 | color: accentColor, 150 | fontWeight: FontWeight.w500, 151 | ), 152 | ) 153 | ), 154 | darkTheme: ThemeData.dark().copyWith( 155 | colorScheme: ColorScheme.fromSeed(seedColor: accentColor, secondary: accentColor, brightness: Brightness.dark), 156 | scaffoldBackgroundColor: Platform.isLinux?Colors.black:Colors.transparent, 157 | primaryColor: Colors.blueGrey, 158 | // cardColor: Colors.transparent, 159 | cardTheme: const CardTheme( 160 | shadowColor: Colors.transparent, 161 | color: Colors.transparent, 162 | ), 163 | popupMenuTheme: const PopupMenuThemeData( 164 | color: kDarkModeMenuColor 165 | ), 166 | bannerTheme: const MaterialBannerThemeData( 167 | backgroundColor: Colors.transparent 168 | ), 169 | appBarTheme: const AppBarTheme( 170 | shadowColor: Colors.transparent, 171 | backgroundColor: Colors.transparent, 172 | ), 173 | listTileTheme: ListTileThemeData( 174 | textColor: Colors.white, 175 | iconColor: accentColor, 176 | ), 177 | drawerTheme: const DrawerThemeData( 178 | backgroundColor: kDarkModeMenuColor, 179 | ), 180 | dialogBackgroundColor: kDarkModeMenuColor, 181 | snackBarTheme: const SnackBarThemeData( 182 | backgroundColor: kDarkModeMenuColor, 183 | contentTextStyle: TextStyle( 184 | color: Colors.white 185 | ) 186 | ), 187 | textTheme: TextTheme( 188 | displayLarge: GoogleFonts.quicksand(), 189 | displayMedium: GoogleFonts.quicksand(), 190 | displaySmall: GoogleFonts.quicksand(color: Colors.white,fontSize: 40), 191 | headlineMedium: GoogleFonts.quicksand(), 192 | headlineSmall: GoogleFonts.quicksand(color: Colors.white,fontSize: 25,fontWeight: FontWeight.w600), 193 | titleLarge: GoogleFonts.quicksand(color: Colors.white,fontSize: 20,fontWeight: FontWeight.w500), 194 | titleMedium: GoogleFonts.quicksand(color: Colors.white,fontSize: 15), 195 | titleSmall: GoogleFonts.quicksand(), 196 | bodyLarge: GoogleFonts.quicksand(), 197 | bodyMedium: GoogleFonts.quicksand(color: Colors.white70), 198 | bodySmall: GoogleFonts.quicksand(), 199 | labelLarge: GoogleFonts.quicksand(), 200 | ), 201 | radioTheme: RadioThemeData( 202 | fillColor: MaterialStateProperty.resolveWith((states) => Colors.blueGrey), 203 | ), 204 | buttonTheme: const ButtonThemeData( 205 | buttonColor: Colors.black 206 | ), 207 | // toggleableActiveColor: Colors.blueGrey, 208 | ), 209 | home: const ConnectionInitiationScreen(), 210 | ); 211 | }, 212 | ); 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /lib/models/device.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Device{ 4 | String id; 5 | late String model; 6 | late String manufacturer; 7 | late String androidVersion; 8 | late String status; 9 | late int selectedDeviceIndex; 10 | late Function updateSelectionStatus; 11 | late int androidAPILevel; 12 | late int index; 13 | 14 | Device(this.index,this.id,this.status,this.selectedDeviceIndex,this.updateSelectionStatus); 15 | Device.wsaCons(this.id); 16 | 17 | void setOtherDeviceAttributes(String model,String manufacturer,String androidVersion,int androidAPILevel){ 18 | this.model=model; 19 | this.manufacturer=manufacturer; 20 | this.androidVersion=androidVersion; 21 | this.androidAPILevel=androidAPILevel; 22 | } 23 | 24 | DataRow getDeviceInfoAsDataRow(BuildContext context){ 25 | return DataRow( 26 | cells: [ 27 | DataCell( 28 | Radio( 29 | value: index, 30 | onChanged: (value){ 31 | updateSelectionStatus(index); 32 | }, 33 | groupValue: selectedDeviceIndex, 34 | ) 35 | ), 36 | DataCell(Text(id,maxLines: 3,),), 37 | DataCell(Text(model,maxLines: 3,),), 38 | DataCell(Text(manufacturer,maxLines: 3,),), 39 | DataCell(Text(androidVersion,maxLines: 3,),), 40 | DataCell(Text(status,maxLines: 3,),), 41 | ] 42 | ); 43 | } 44 | 45 | 46 | } -------------------------------------------------------------------------------- /lib/models/file_transfer_job.dart: -------------------------------------------------------------------------------- 1 | import 'package:adb_gui/utils/enums.dart'; 2 | 3 | class FileTransferJob { 4 | FileTransferType jobType; 5 | String itemPath, itemName; 6 | FileTransferJob(this.jobType, this.itemPath, this.itemName); 7 | 8 | bool checkSameItem(FileTransferJob fileTransferJob) { 9 | return itemPath == fileTransferJob.itemPath && 10 | itemName == fileTransferJob.itemName; 11 | } 12 | } 13 | 14 | -------------------------------------------------------------------------------- /lib/models/item.dart: -------------------------------------------------------------------------------- 1 | import 'package:adb_gui/utils/enums.dart'; 2 | 3 | class Item{ 4 | String itemName; 5 | Future itemContentType; 6 | Item(this.itemName,this.itemContentType); 7 | } -------------------------------------------------------------------------------- /lib/models/storage.dart: -------------------------------------------------------------------------------- 1 | class Storage{ 2 | final String path; 3 | final String name; 4 | 5 | Storage(this.path,this.name); 6 | } -------------------------------------------------------------------------------- /lib/screens/device_info_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:adb_gui/components/device_info_list_tile.dart'; 2 | import 'package:adb_gui/components/page_subheading.dart'; 3 | import 'package:adb_gui/services/adb_services.dart'; 4 | import 'package:adb_gui/services/transform_functions.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; 7 | import '../models/device.dart'; 8 | 9 | class DeviceInfoScreen extends StatefulWidget { 10 | final Device device; 11 | const DeviceInfoScreen({Key? key, required this.device}) : super(key: key); 12 | 13 | @override 14 | State createState() => _DeviceInfoScreenState(device); 15 | } 16 | 17 | class _DeviceInfoScreenState extends State { 18 | 19 | final Device device; 20 | late final ADBService _adbService; 21 | late final TransformFunctions _transformFunctions; 22 | 23 | _DeviceInfoScreenState(this.device); 24 | 25 | 26 | @override 27 | void initState() { 28 | _adbService=ADBService(device: device); 29 | _transformFunctions=TransformFunctions(); 30 | super.initState(); 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return SingleChildScrollView( 36 | child: StaggeredGrid.count( 37 | crossAxisCount: 3, 38 | mainAxisSpacing: 2, 39 | crossAxisSpacing: 2, 40 | children: [ 41 | Card( 42 | color: Theme.of(context).brightness==Brightness.dark?Colors.white.withOpacity(0.05):null, 43 | child: Column( 44 | children: [ 45 | const PageSubheading(subheadingName: "Android System"), 46 | DeviceInfoListTile( 47 | propertyDisplayName: "Android Version", 48 | propertyName: "ro.system.build.version.release", 49 | adbService: _adbService, 50 | ), 51 | DeviceInfoListTile( 52 | propertyDisplayName: "Android API Level", 53 | propertyName: "ro.system.build.version.sdk", 54 | adbService: _adbService, 55 | ), 56 | DeviceInfoListTile( 57 | propertyDisplayName: "Launch API Level", 58 | propertyName: "ro.product.first_api_level", 59 | adbService: _adbService, 60 | ), 61 | DeviceInfoListTile( 62 | propertyDisplayName: "System Build Fingerprint", 63 | propertyName: "ro.system.build.fingerprint", 64 | adbService: _adbService, 65 | ), 66 | DeviceInfoListTile( 67 | propertyDisplayName: "Vendor Build Fingerprint", 68 | propertyName: "ro.vendor.build.fingerprint", 69 | adbService: _adbService, 70 | ), 71 | DeviceInfoListTile( 72 | propertyDisplayName: "Android Security Patch", 73 | propertyName: "ro.build.version.security_patch", 74 | adbService: _adbService, 75 | transformFunction: _transformFunctions.securityPatchTransform, 76 | ), 77 | DeviceInfoListTile( 78 | propertyDisplayName: "Vendor Security Patch Level", 79 | propertyName: "ro.vendor.build.security_patch", 80 | adbService: _adbService, 81 | transformFunction: _transformFunctions.securityPatchTransform, 82 | ), 83 | DeviceInfoListTile( 84 | propertyDisplayName: "Build Date", 85 | propertyName: "ro.system.build.date", 86 | adbService: _adbService, 87 | ), 88 | ], 89 | ), 90 | ), 91 | Card( 92 | color: Theme.of(context).brightness==Brightness.dark?Colors.white.withOpacity(0.05):null, 93 | child: Column( 94 | children: [ 95 | const PageSubheading(subheadingName: "Hardware"), 96 | DeviceInfoListTile( 97 | propertyDisplayName: "Board", 98 | propertyName: "ro.product.board", 99 | adbService: _adbService, 100 | ), 101 | DeviceInfoListTile( 102 | propertyDisplayName: "Device Model", 103 | propertyName: "ro.product.model", 104 | adbService: _adbService, 105 | ), 106 | DeviceInfoListTile( 107 | propertyDisplayName: "Supported CPU ABIs", 108 | propertyName: "ro.product.cpu.abilist", 109 | adbService: _adbService, 110 | transformFunction: _transformFunctions.addCommaSeparation, 111 | ), 112 | DeviceInfoListTile( 113 | propertyDisplayName: "Vulkan Device", 114 | propertyName: "ro.hardware.vulkan", 115 | adbService: _adbService, 116 | ), 117 | DeviceInfoListTile( 118 | propertyDisplayName: "Serial No.", 119 | propertyName: "ro.boot.serialno", 120 | adbService: _adbService, 121 | ), 122 | DeviceInfoListTile( 123 | propertyDisplayName: "Screen HDR support", 124 | propertyName: "ro.surface_flinger.has_HDR_display", 125 | adbService: _adbService, 126 | transformFunction: _transformFunctions.boolStringTransform, 127 | ), 128 | ], 129 | ), 130 | ), 131 | Card( 132 | color: Theme.of(context).brightness==Brightness.dark?Colors.white.withOpacity(0.05):null, 133 | child: Column( 134 | children: [ 135 | const PageSubheading(subheadingName: "Network Status"), 136 | DeviceInfoListTile( 137 | propertyDisplayName: "WiFi Interface", 138 | propertyName: "wifi.interface", 139 | adbService: _adbService, 140 | transformFunction: _transformFunctions.addCommaSeparation, 141 | ), 142 | DeviceInfoListTile( 143 | propertyDisplayName: "Network Type", 144 | propertyName: "gsm.network.type", 145 | adbService: _adbService, 146 | transformFunction: _transformFunctions.mobileNetworkOperatorsTransform, 147 | ), 148 | DeviceInfoListTile( 149 | propertyDisplayName: "Connected Mobile Networks", 150 | propertyName: "gsm.operator.alpha", 151 | adbService: _adbService, 152 | transformFunction: _transformFunctions.mobileNetworkOperatorsTransform, 153 | ), 154 | DeviceInfoListTile( 155 | propertyDisplayName: "Connected Mobile Network Country", 156 | propertyName: "gsm.operator.iso-country", 157 | adbService: _adbService, 158 | transformFunction: _transformFunctions.mobileNetworkOperatorsTransform, 159 | ), 160 | DeviceInfoListTile( 161 | propertyDisplayName: "SIM Operator", 162 | propertyName: "gsm.sim.operator.alpha", 163 | adbService: _adbService, 164 | transformFunction: _transformFunctions.mobileNetworkOperatorsTransform, 165 | ), 166 | DeviceInfoListTile( 167 | propertyDisplayName: "SIM Operator Country", 168 | propertyName: "gsm.sim.operator.iso-country", 169 | adbService: _adbService, 170 | transformFunction: _transformFunctions.mobileNetworkOperatorsTransform, 171 | ), 172 | DeviceInfoListTile( 173 | propertyDisplayName: "SIM Operator numeric code", 174 | propertyName: "gsm.sim.operator.numeric", 175 | adbService: _adbService, 176 | transformFunction: _transformFunctions.mobileNetworkOperatorsTransform, 177 | ), 178 | DeviceInfoListTile( 179 | propertyDisplayName: "Baseband Version", 180 | propertyName: "gsm.version.baseband", 181 | adbService: _adbService, 182 | ), 183 | ], 184 | ), 185 | ), 186 | ], 187 | ), 188 | ); 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /lib/screens/home_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:adb_gui/components/window_buttons.dart'; 4 | import 'package:adb_gui/screens/device_info_screen.dart'; 5 | import 'package:adb_gui/screens/package_manager_screen.dart'; 6 | import 'package:adb_gui/services/adb_services.dart'; 7 | import 'package:adb_gui/services/platform_services.dart'; 8 | import 'package:adb_gui/utils/const.dart'; 9 | import 'package:adb_gui/utils/enums.dart'; 10 | import 'package:adb_gui/models/device.dart'; 11 | import 'package:adb_gui/screens/file_manager_screen.dart'; 12 | import 'package:adb_gui/screens/power_controls_screen.dart'; 13 | import 'package:adb_gui/services/android_api_checks.dart'; 14 | import 'package:bitsdojo_window/bitsdojo_window.dart'; 15 | import 'package:flutter/material.dart'; 16 | import 'package:system_theme/system_theme.dart'; 17 | 18 | class HomeScreen extends StatefulWidget { 19 | final Device device; 20 | const HomeScreen({Key? key,required this.device}) : super(key: key); 21 | 22 | @override 23 | _HomeScreenState createState() => _HomeScreenState(device); 24 | } 25 | 26 | class _HomeScreenState extends State { 27 | 28 | Screens _currentScreen=Screens.fileManager; 29 | final Device device; 30 | 31 | _HomeScreenState(this.device); 32 | 33 | 34 | String _getScreenName(Screens screenEnum){ 35 | switch(screenEnum){ 36 | case Screens.fileManager: return "Files"; 37 | case Screens.powerControls: return "Power Controls"; 38 | case Screens.packageManager: return "Apps"; 39 | case Screens.deviceInfo: return "Device Info"; 40 | default: return "Wtf??"; 41 | } 42 | } 43 | 44 | @override 45 | void initState() { 46 | super.initState(); 47 | } 48 | 49 | @override 50 | Widget build(BuildContext context) { 51 | return Scaffold( 52 | appBar: PreferredSize( 53 | preferredSize: const Size.fromHeight(75), 54 | child: MoveWindow( 55 | child: AppBar( 56 | toolbarHeight: 75, 57 | title: Row( 58 | mainAxisSize: MainAxisSize.max, 59 | children: [ 60 | Tooltip( 61 | message: "${device.id} - ${device.model}", 62 | child: Text("${_getScreenName(_currentScreen)} (${device.model})",overflow: TextOverflow.ellipsis,style: TextStyle( 63 | color: Theme.of(context).brightness == Brightness.dark?Colors.white:kLightModeAppBarTitleColor, 64 | fontSize: 20 65 | ),), 66 | ), 67 | const Spacer(), 68 | if(isPreMarshmallowAndroid(device.androidAPILevel)) 69 | WindowMaterialButton( 70 | // buttonColor: Colors.blue, 71 | hoverColor: Colors.amber[300], 72 | buttonIcon: Icon(Icons.warning,color: Colors.amber[700],), 73 | onPressed: (){ 74 | showDialog( 75 | context: context, 76 | builder: (context)=>AlertDialog( 77 | title: Text("Stability Alert",style: TextStyle( 78 | color: SystemTheme.accentColor.accent 79 | ),), 80 | content: Text("You may experience degraded performance/glitches since your device runs on Android ${device.androidVersion} .\nThe recommended Android version is 7.0 and above"), 81 | actions: [ 82 | TextButton( 83 | onPressed: (){ 84 | Navigator.pop(context); 85 | }, 86 | child: Padding( 87 | padding: const EdgeInsets.all(8.0), 88 | child: Text("OK",style: TextStyle( 89 | color: SystemTheme.accentColor.accent 90 | ),), 91 | ), 92 | ), 93 | ], 94 | ), 95 | ); 96 | }, 97 | ), 98 | WindowMaterialButton( 99 | // buttonColor: Colors.blue, 100 | buttonIcon: Icon(Icons.exit_to_app_rounded,color: Theme.of(context).brightness == Brightness.dark?Colors.white:kLightModeAppBarTitleColor,), 101 | onPressed: () async{ 102 | await ADBService(device: device).disconnect(); 103 | Navigator.pop(context); 104 | }, 105 | ), 106 | if(!Platform.isMacOS)...[ 107 | CustomMinimizeWindowButton(), 108 | CustomMaximizeWindowButton(), 109 | CustomCloseWindowButton(), 110 | ] 111 | ], 112 | ), 113 | ), 114 | ), 115 | ), 116 | drawer: Drawer( 117 | shape: const RoundedRectangleBorder( 118 | borderRadius: BorderRadius.only( 119 | topRight: Radius.circular(10), 120 | bottomRight: Radius.circular(10))), 121 | child: Builder(builder: (context) { 122 | return ListView( 123 | children: [ 124 | DrawerHeader( 125 | decoration: BoxDecoration( 126 | gradient: Theme.of(context).brightness==Brightness.light?LinearGradient( 127 | colors: [SystemTheme.accentColor.accent.withOpacity(0.6), Colors.transparent], 128 | begin: Alignment.topCenter, 129 | end: Alignment.bottomCenter, 130 | ):null, 131 | ), 132 | child: Column( 133 | mainAxisAlignment: MainAxisAlignment.spaceBetween, 134 | children: [ 135 | Align( 136 | alignment: Alignment.topLeft, 137 | child: IconButton( 138 | icon: const Icon( 139 | Icons.arrow_back_rounded, 140 | color: Colors.white, 141 | ), 142 | onPressed: () { 143 | Navigator.pop(context); 144 | }, 145 | ), 146 | ), 147 | Image.asset("assets/lightningBoltLogo.png",width: 50,height: 50,), 148 | Padding( 149 | padding: const EdgeInsets.only(left: 12.0, bottom: 4), 150 | child: Text( 151 | "Android Toolbox", 152 | style: TextStyle(color: SystemTheme.accentColor.accent, fontSize: 25), 153 | ), 154 | ), 155 | ], 156 | )), 157 | ListTile( 158 | leading: const Icon(Icons.drive_file_move), 159 | title: const Text("File Manager",), 160 | dense: false, 161 | onTap: (){ 162 | setState(() { 163 | _currentScreen=Screens.fileManager; 164 | }); 165 | Navigator.pop(context); 166 | }, 167 | ), 168 | ListTile( 169 | leading: const Icon(Icons.power_settings_new_rounded), 170 | enabled: !isWSA(device.id), 171 | title: Text("Power Controls ${isWSA(device.id)?"(N/A for WSA)":""}"), 172 | dense: false, 173 | onTap: (){ 174 | setState(() { 175 | _currentScreen=Screens.powerControls; 176 | }); 177 | Navigator.pop(context); 178 | }, 179 | ), 180 | ListTile( 181 | leading: const Icon(Icons.apps_rounded), 182 | title: const Text("Applications"), 183 | dense: false, 184 | onTap: (){ 185 | setState(() { 186 | _currentScreen=Screens.packageManager; 187 | }); 188 | Navigator.pop(context); 189 | }, 190 | ), 191 | ListTile( 192 | leading: const Icon(Icons.info_outline_rounded), 193 | title: const Text("Device Info"), 194 | dense: false, 195 | onTap: (){ 196 | setState(() { 197 | _currentScreen=Screens.deviceInfo; 198 | }); 199 | Navigator.pop(context); 200 | }, 201 | ), 202 | ], 203 | ); 204 | }), 205 | ), 206 | body: Column( 207 | children: [ 208 | Expanded( 209 | child: Builder( 210 | builder: (context){ 211 | switch(_currentScreen){ 212 | case Screens.fileManager: return FileManagerScreen(device: device); 213 | case Screens.powerControls: return PowerControlsScreen(device: device); 214 | case Screens.packageManager: return PackageManagerScreen(device: device); 215 | case Screens.deviceInfo: return DeviceInfoScreen(device: device); 216 | default: return Container(); 217 | } 218 | }, 219 | ), 220 | ), 221 | ], 222 | ), 223 | ); 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /lib/screens/power_controls_screen.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'package:adb_gui/components/page_subheading.dart'; 3 | import 'package:adb_gui/models/device.dart'; 4 | import 'package:adb_gui/utils/vars.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 7 | 8 | class PowerControlsScreen extends StatelessWidget { 9 | final Device device; 10 | const PowerControlsScreen({Key? key,required this.device}) : super(key: key); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return ListView( 15 | children: [ 16 | const PageSubheading(subheadingName: "Basic"), 17 | ActionTile( 18 | leadingIcon: const Icon(FontAwesomeIcons.powerOff,color: Colors.red,), 19 | titleText: "Power Off", 20 | subtitleText: "Shuts the device down", 21 | deviceID: device.id, 22 | arguments: const ["shell","reboot","-p"], 23 | ), 24 | ActionTile( 25 | leadingIcon: const Icon(FontAwesomeIcons.android,color: Colors.green,), 26 | titleText: "Reboot (System)", 27 | subtitleText: "Reboot and boot back into Android", 28 | deviceID: device.id, 29 | arguments: const ["reboot"], 30 | ), 31 | const PageSubheading(subheadingName: "Advanced"), 32 | ActionTile( 33 | leadingIcon: const Icon(Icons.settings,color: Colors.blueGrey), 34 | titleText: "Reboot to bootloader", 35 | subtitleText: "Reboot and boot into the bootloader (May vary by device)", 36 | deviceID: device.id, 37 | arguments: const ["reboot","bootloader"], 38 | ), 39 | ActionTile( 40 | leadingIcon: const Icon(Icons.settings_backup_restore_rounded,color: Color(0xFF1592B4),), 41 | titleText: "Reboot to recovery", 42 | subtitleText: "Reboot and boot into the recovery (May vary by device)", 43 | deviceID: device.id, 44 | arguments: const ["reboot","recovery"], 45 | ), 46 | ActionTile( 47 | leadingIcon: const Icon(Icons.system_update_rounded,color: Colors.blueAccent), 48 | titleText: "Reboot to sideload", 49 | subtitleText: "Reboot directly into recovery's sideload mode (May vary by device)", 50 | deviceID: device.id, 51 | arguments: const ["reboot","sideload"], 52 | ), 53 | ActionTile( 54 | leadingIcon: const Icon(Icons.warning,color: Colors.amber), 55 | titleText: "Reboot to fastboot", 56 | subtitleText: "Reboot and boot into fastboot mode (May vary by device)", 57 | deviceID: device.id, 58 | arguments: const ["reboot","fastboot"], 59 | ), 60 | ], 61 | ); 62 | } 63 | } 64 | 65 | 66 | class ActionTile extends StatefulWidget { 67 | final String deviceID; 68 | final List arguments; 69 | final String titleText; 70 | final Icon leadingIcon; 71 | final String subtitleText; 72 | 73 | const ActionTile({Key? key,required this.deviceID,required this.arguments,required this.titleText,required this.leadingIcon,required this.subtitleText}) : super(key: key); 74 | 75 | @override 76 | _ActionTileState createState() => _ActionTileState(deviceID,arguments,titleText,leadingIcon,subtitleText); 77 | } 78 | 79 | 80 | class _ActionTileState extends State { 81 | 82 | final String deviceID; 83 | final List arguments; 84 | final String titleText; 85 | final Icon leadingIcon; 86 | final String subtitleText; 87 | 88 | _ActionTileState(this.deviceID,this.arguments,this.titleText,this.leadingIcon,this.subtitleText); 89 | 90 | 91 | bool _doingWork=false; 92 | bool _doneOnce=false; 93 | bool _error=false; 94 | 95 | void performAction() async{ 96 | List fullArguments=["-s",deviceID]; 97 | fullArguments.addAll(arguments); 98 | setState(() { 99 | _doingWork=true; 100 | }); 101 | ProcessResult processResult=await Process.run(adbExecutable, fullArguments); 102 | 103 | setState(() { 104 | if(processResult.exitCode==0){ 105 | _error=false; 106 | }else{ 107 | _error=true; 108 | } 109 | _doneOnce=true; 110 | _doingWork=false; 111 | }); 112 | 113 | } 114 | 115 | @override 116 | Widget build(BuildContext context) { 117 | return ListTile( 118 | leading: leadingIcon, 119 | title: Text(titleText,style: const TextStyle( 120 | fontSize: 15 121 | ),), 122 | onTap: performAction, 123 | subtitle: Text(subtitleText), 124 | trailing: _doingWork?const CircularProgressIndicator():(_error && _doneOnce)?const Icon(Icons.error,color: Colors.red,):_doneOnce?const Icon(Icons.check_circle,color: Colors.green,):const Icon(Icons.arrow_forward_rounded), 125 | ); 126 | } 127 | } 128 | 129 | -------------------------------------------------------------------------------- /lib/screens/theme_mode_service.dart: -------------------------------------------------------------------------------- 1 | import 'package:adb_gui/services/shared_prefs.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class ThemeModeService extends ChangeNotifier{ 5 | Future setThemeMode(ThemeMode themeMode) async{ 6 | await setThemeModePreference(themeMode); 7 | notifyListeners(); 8 | } 9 | } 10 | 11 | ThemeModeService themeModeService=ThemeModeService(); -------------------------------------------------------------------------------- /lib/services/android_api_checks.dart: -------------------------------------------------------------------------------- 1 | bool isPreMarshmallowAndroid(int apiLevel){ 2 | return apiLevel<24; 3 | } 4 | 5 | bool isPreIceCreamSandwichAndroid(int apiLevel){ 6 | return apiLevel<15; 7 | } 8 | 9 | bool appSuspendSupported(int apiLevel){ 10 | return apiLevel>=29; 11 | } 12 | 13 | bool appCompilationSupported(int apiLevel){ 14 | return apiLevel>=29; 15 | } 16 | 17 | bool newStoragePathSupported(int apiLevel){ 18 | return apiLevel>=23; 19 | } -------------------------------------------------------------------------------- /lib/services/date_time_service.dart: -------------------------------------------------------------------------------- 1 | class DateTimeService{ 2 | 3 | static String getMonthStringFromInt(int monthAsInt){ 4 | switch(monthAsInt){ 5 | case 1: return "January"; 6 | case 2: return "February"; 7 | case 3: return "March"; 8 | case 4: return "April"; 9 | case 5: return "May"; 10 | case 6: return "June"; 11 | case 7: return "July"; 12 | case 8: return "August"; 13 | case 9: return "September"; 14 | case 10: return "October"; 15 | case 11: return "November"; 16 | case 12: return "December"; 17 | default: return "Invalid"; 18 | } 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /lib/services/file_services.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:adb_gui/models/device.dart'; 4 | import 'package:adb_gui/services/adb_services.dart'; 5 | import 'package:adb_gui/utils/enums.dart'; 6 | import 'package:adb_gui/utils/vars.dart'; 7 | import 'package:file_picker/file_picker.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 10 | 11 | import 'android_api_checks.dart'; 12 | 13 | Future getFileType({required Device device,required String currentPath,required String fileName}) async { 14 | bool isLegacyAndroidFile=false; 15 | if(isPreMarshmallowAndroid(device.androidAPILevel)){ 16 | ProcessResult result=await Process.run(adbExecutable,["-s",device.id,"shell","ls","\"${currentPath+fileName}\""]); 17 | // print(result.stdout.toString().trim().contains(_currentPath+fileName)); 18 | if(result.stdout.toString().trim().contains(currentPath+fileName)){ 19 | isLegacyAndroidFile=true; 20 | } 21 | } 22 | String fileExtension = fileName.split(".").last; 23 | if(fileName!="sdcard" && (isLegacyAndroidFile || (!isPreMarshmallowAndroid(device.androidAPILevel) && !fileName.endsWith("/")))){ 24 | if (fileExtension == "pdf") { 25 | return FileContentTypes.pdf; 26 | } else if (fileExtension == "zip" || 27 | fileExtension == "tar" || 28 | fileExtension == "gz" || 29 | fileExtension == "rar" || 30 | fileExtension == "7z") { 31 | return FileContentTypes.archive; 32 | } else if (fileExtension == "apk") { 33 | return FileContentTypes.apk; 34 | } else if (fileExtension == "doc" || fileExtension == "txt" || fileExtension == "docx" || fileExtension == "odt") { 35 | return FileContentTypes.wordDocument; 36 | }else if(fileExtension == "ppt" || fileExtension == "pptx"){ 37 | return FileContentTypes.powerpoint; 38 | }else if(fileExtension == "xls" || fileExtension == "xlsx" || fileExtension == "csv"){ 39 | return FileContentTypes.excel; 40 | }else if (fileExtension == "png" || 41 | fileExtension == "jpg" || 42 | fileExtension == "jpeg" || 43 | fileExtension == "gif" || 44 | fileExtension == "raw") { 45 | return FileContentTypes.image; 46 | } else if (fileExtension == "mp4" || 47 | fileExtension == "mkv" || 48 | fileExtension == "webm" || 49 | fileExtension == "mpeg") { 50 | return FileContentTypes.video; 51 | } else if (fileExtension == "mp3" || 52 | fileExtension == "wma" || 53 | fileExtension == "flac" || 54 | fileExtension == "wav" || 55 | fileExtension == "ogg") { 56 | return FileContentTypes.audio; 57 | } else if (fileExtension == "torrent") { 58 | return FileContentTypes.torrent; 59 | } else if (fileExtension == "cer") { 60 | return FileContentTypes.securityCertificate; 61 | } 62 | return FileContentTypes.file; 63 | } 64 | 65 | return FileContentTypes.directory; 66 | } 67 | 68 | 69 | Future findFileItemType(ADBService adbService, String currentPath, String fileItemName) async { 70 | 71 | ProcessResult result=await adbService.executeLs(currentPath+fileItemName); 72 | 73 | if (result.stdout.split("\r\n")[0] == currentPath + fileItemName) { 74 | return FileContentTypes.file; 75 | } 76 | return FileContentTypes.directory; 77 | } 78 | 79 | 80 | Future pickFileFolderFromDesktop({required FileItemType fileItemType, required String dialogTitle, List allowedExtensions=const []}) async{ 81 | if (fileItemType == FileItemType.file) { 82 | FilePickerResult? filePicker = await FilePicker.platform.pickFiles(dialogTitle: dialogTitle, type: allowedExtensions[0]=="*"?FileType.any:FileType.custom, allowedExtensions: allowedExtensions); 83 | return filePicker?.files.single.path; 84 | } else { 85 | return await FilePicker.platform.getDirectoryPath(dialogTitle: dialogTitle,lockParentWindow: true); 86 | } 87 | } 88 | 89 | Future> pickMultipleFilesFromDesktop({required String dialogTitle,required List allowedExtensions}) async{ 90 | FilePickerResult? filePicker = await FilePicker.platform.pickFiles(dialogTitle: dialogTitle,allowMultiple: true, type: allowedExtensions[0]=="*"?FileType.any:FileType.custom, allowedExtensions: allowedExtensions); 91 | List filePaths=[]; 92 | if(filePicker==null){ 93 | return filePaths; 94 | } 95 | for(int i=0;i elements=path.split("\\"); 106 | if(path.endsWith("\\")){ 107 | return elements[elements.length-2]; 108 | } 109 | return elements[elements.length-1]; 110 | } 111 | List elements=path.split("/"); 112 | if(path.endsWith("/")){ 113 | return elements[elements.length-2]; 114 | } 115 | return elements[elements.length-1]; 116 | } 117 | 118 | String getPlatformDelimiter(){ 119 | if(Platform.isWindows){ 120 | return "\\"; 121 | } 122 | return "/"; 123 | } 124 | 125 | 126 | Future getDesktopFileSize(String filePath) async{ 127 | int size=0; 128 | 129 | File file=File(filePath); 130 | if(await file.exists()){ 131 | return file.length(); 132 | } 133 | Directory root=Directory(filePath); 134 | if(await root.exists()){ 135 | List element=root.listSync(recursive: true,followLinks: false); 136 | for (FileSystemEntity element in element) { 137 | if(element is File){ 138 | size+=await element.length(); 139 | } 140 | } 141 | } 142 | return size; 143 | 144 | 145 | } 146 | 147 | 148 | Future getFileIconByType(Future fileType) async { 149 | switch(await fileType){ 150 | case FileContentTypes.pdf: return FontAwesomeIcons.filePdf; 151 | case FileContentTypes.wordDocument: return FontAwesomeIcons.fileWord; 152 | case FileContentTypes.powerpoint: return FontAwesomeIcons.filePowerpoint; 153 | case FileContentTypes.excel: return FontAwesomeIcons.fileExcel; 154 | case FileContentTypes.image: return FontAwesomeIcons.fileImage; 155 | case FileContentTypes.video: return FontAwesomeIcons.fileVideo; 156 | case FileContentTypes.audio: return FontAwesomeIcons.fileAudio; 157 | case FileContentTypes.apk: return FontAwesomeIcons.android; 158 | case FileContentTypes.archive: return FontAwesomeIcons.fileArchive; 159 | case FileContentTypes.torrent: return FontAwesomeIcons.magnet; 160 | case FileContentTypes.securityCertificate: return FontAwesomeIcons.key; 161 | case FileContentTypes.file: return FontAwesomeIcons.file; 162 | default: return FontAwesomeIcons.folder; 163 | } 164 | } -------------------------------------------------------------------------------- /lib/services/platform_services.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'dart:io'; 3 | 4 | String getPlatformName(){ 5 | if(Platform.isWindows){ 6 | return "windows"; 7 | }else if(Platform.isMacOS){ 8 | return "macOS"; 9 | } 10 | return "linux"; 11 | } 12 | 13 | String getPlatformDelimiter(){ 14 | if(Platform.isWindows){ 15 | return "\\"; 16 | } 17 | return "/"; 18 | } 19 | 20 | bool isWindows11(){ 21 | try{ 22 | String versionInfo=Platform.operatingSystemVersion; 23 | String buildNumber=versionInfo.split(" ").last; 24 | buildNumber=buildNumber.replaceAll(")", ""); 25 | return int.parse(buildNumber)>=22000; 26 | }catch(e){ 27 | return false; 28 | } 29 | } 30 | 31 | bool isWSA(String deviceID){ 32 | return (deviceID=="127.0.0.1:58526" || deviceID=="localhost:58526"); 33 | } -------------------------------------------------------------------------------- /lib/services/shared_prefs.dart: -------------------------------------------------------------------------------- 1 | import 'package:adb_gui/services/platform_services.dart'; 2 | import 'package:shared_preferences/shared_preferences.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'dart:io'; 5 | 6 | Future setAllowPreReleasePreference(bool value) async { 7 | SharedPreferences pref=await SharedPreferences.getInstance(); 8 | pref.setBool("allowPreRelease", value); 9 | } 10 | 11 | Future getAllowPreReleasePreference() async{ 12 | SharedPreferences pref=await SharedPreferences.getInstance(); 13 | if(pref.getBool("allowPreRelease")==null){ 14 | await setAllowPreReleasePreference(false); 15 | return false; 16 | } 17 | return pref.getBool("allowPreRelease"); 18 | } 19 | 20 | Future setThemeModePreference(ThemeMode themeMode) async{ 21 | SharedPreferences pref=await SharedPreferences.getInstance(); 22 | pref.setInt("themeMode", themeMode.index); 23 | } 24 | 25 | Future getThemeModePreference() async{ 26 | SharedPreferences pref=await SharedPreferences.getInstance(); 27 | 28 | if(pref.getInt("themeMode")==null){ 29 | ThemeMode themeMode=(Platform.isWindows && !isWindows11())?ThemeMode.dark:ThemeMode.system; 30 | await setThemeModePreference(themeMode); 31 | return themeMode; 32 | } 33 | return ThemeMode.values[pref.getInt("themeMode")!]; 34 | } 35 | 36 | Future setKillADBDuringStartPreference(bool value) async{ 37 | SharedPreferences pref=await SharedPreferences.getInstance(); 38 | pref.setBool("killADBDuringStart", value); 39 | } 40 | 41 | Future getKillADBDuringStartPreference() async{ 42 | SharedPreferences pref=await SharedPreferences.getInstance(); 43 | if(pref.getBool("killADBDuringStart")==null){ 44 | await setKillADBDuringStartPreference(true); 45 | return true; 46 | } 47 | return pref.getBool("killADBDuringStart"); 48 | } 49 | 50 | Future setKillADBOnExitPreference(bool value) async{ 51 | SharedPreferences pref=await SharedPreferences.getInstance(); 52 | pref.setBool("killADBOnExit", value); 53 | } 54 | 55 | Future getKillADBOnExitPreference() async{ 56 | SharedPreferences pref=await SharedPreferences.getInstance(); 57 | if(pref.getBool("killADBOnExit")==null){ 58 | await setKillADBOnExitPreference(true); 59 | return true; 60 | } 61 | return pref.getBool("killADBOnExit"); 62 | } 63 | 64 | Future getCheckUpdatesDuringStartupPreference() async{ 65 | SharedPreferences pref=await SharedPreferences.getInstance(); 66 | if(pref.getBool("checkUpdatesDuringStartup")==null){ 67 | await setCheckUpdatesDuringStartupPreference(true); 68 | return true; 69 | } 70 | return pref.getBool("checkUpdatesDuringStartup"); 71 | } 72 | 73 | Future setCheckUpdatesDuringStartupPreference(bool value) async{ 74 | SharedPreferences pref=await SharedPreferences.getInstance(); 75 | pref.setBool("checkUpdatesDuringStartup", value); 76 | } 77 | 78 | Future setShowHiddenFilesPreference(bool value) async{ 79 | SharedPreferences pref=await SharedPreferences.getInstance(); 80 | pref.setBool("showHiddenFiles", value); 81 | } 82 | 83 | Future getShowHiddenFilesPreference() async{ 84 | SharedPreferences pref=await SharedPreferences.getInstance(); 85 | if(pref.getBool("showHiddenFiles")==null){ 86 | await setShowHiddenFilesPreference(false); 87 | return false; 88 | } 89 | return pref.getBool("showHiddenFiles"); 90 | } -------------------------------------------------------------------------------- /lib/services/string_services.dart: -------------------------------------------------------------------------------- 1 | import 'package:adb_gui/utils/enums.dart'; 2 | 3 | List getTrimmedStringList(List inputString){ 4 | for(int i=0;i inputString){ 11 | String outputString = ""; 12 | for(int i=0;i units=["B","KB","MB","GB","TB","PB"]; 31 | int selectedUnitIndex=0; 32 | while(size > 1023 && selectedUnitIndex < units.length){ 33 | size/=1024; 34 | selectedUnitIndex++; 35 | } 36 | return "${size.toStringAsFixed(2)} ${units[selectedUnitIndex]}"; 37 | } -------------------------------------------------------------------------------- /lib/services/transform_functions.dart: -------------------------------------------------------------------------------- 1 | import 'date_time_service.dart'; 2 | 3 | class TransformFunctions{ 4 | 5 | String securityPatchTransform(String value){ 6 | DateTime dateTime=DateTime.parse(value); 7 | String patchLevelInfo = ""; 8 | if (dateTime.day == 1) { 9 | patchLevelInfo = "Android Framework patch only"; 10 | } else if(dateTime.day == 5) { 11 | patchLevelInfo = "Android Framework & Vendor/Kernel patches included"; 12 | } 13 | return "${DateTimeService.getMonthStringFromInt(dateTime.month)} ${dateTime.year} ($patchLevelInfo)"; 14 | } 15 | 16 | String mobileNetworkOperatorsTransform(String value){ 17 | if(!value.contains(",")){ 18 | return value; 19 | } 20 | List splitString=value.split(","); 21 | return "SIM 1: ${splitString[0]} SIM 2: ${splitString[1]}"; 22 | } 23 | 24 | String boolStringTransform(String value){ 25 | if(value.isEmpty){ 26 | return "Unknown"; 27 | } 28 | return value=="true"?"Supported":"Not Supported"; 29 | } 30 | 31 | String addCommaSeparation(String value){ 32 | return value.replaceAll(",", ", "); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /lib/services/update_services.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | import 'package:adb_gui/services/platform_services.dart'; 4 | import 'package:adb_gui/services/shared_prefs.dart'; 5 | import 'package:package_info_plus/package_info_plus.dart'; 6 | import 'package:path_provider/path_provider.dart'; 7 | import 'package:version/version.dart'; 8 | import 'package:http/http.dart' as http; 9 | 10 | Future> checkForUpdates() async{ 11 | //Scenario 0 - User does not want pre-release builds: 12 | //Fetches the latest release info using getLatestGithubReleaseInfo() 13 | //User can choose to update the app thereby downloading and installing the latest stable build 14 | //Scenario 1 - User wants pre release builds: 15 | //Fetches all release info finds the latest build irrespective of whether it is marked as pre-release 16 | //Show a dialog to the user saying an update is available and proceed with it 17 | 18 | //An 'Update is available' dialog is showed only if one of the release assets contains the respective platform name (Eg. windows, linux) 19 | //If current version is 1.0.0 and a release version 1.0.1 is available 20 | //Example: For Windows, only if there is an asset containing 'windows' as its name will the "Update is available" show 21 | 22 | PackageInfo currentPackageInfo = await PackageInfo.fromPlatform(); 23 | List currentVersionByType = currentPackageInfo.version.split("."); 24 | Version currentVersion=Version(int.parse(currentVersionByType[0]),int.parse(currentVersionByType[1]),int.parse(currentVersionByType[2])); 25 | try{ 26 | Map latestGithubReleaseInfo=await getLatestGithubReleaseInfo(); 27 | List latestVersionSplitByType=latestGithubReleaseInfo['tag_name'].split("."); 28 | Version latestVersion=Version(int.parse(latestVersionSplitByType[0]), int.parse(latestVersionSplitByType[1]), int.parse(latestVersionSplitByType[2])); 29 | 30 | if(currentVersion < latestVersion && !latestGithubReleaseInfo['prerelease']){ 31 | String assetLink=""; 32 | bool updateAvailable=false; 33 | for(int i=0;i> getLatestGithubPreRelease(Version currentVersion) async{ 60 | http.Response response=await http.get(Uri.parse("https://api.github.com/repos/lightningbolt047/Android-Toolbox/releases")); 61 | Version latestVersion=currentVersion; 62 | Map latestReleaseInfo={ 63 | 'updateAvailable':false 64 | }; 65 | if(response.statusCode==200){ 66 | List allReleases=jsonDecode(response.body); 67 | for(int i=0;i> getLatestGithubReleaseInfo() async{ 87 | //Fetches latest GitHub release info 88 | http.Response response=await http.get(Uri.parse("https://api.github.com/repos/lightningbolt047/Android-Toolbox/releases/latest"),headers: { 89 | "Accept":"application/vnd.github.v3+.json" 90 | }); 91 | 92 | if(response.statusCode==200) { 93 | return jsonDecode(response.body); 94 | } 95 | throw "Something went wrong when checking for updates"; 96 | } 97 | 98 | Future downloadRelease(String url) async{ 99 | //Download the bytes in the TEMP directory defined by the Operating Systems 100 | //Temp directory for Windows: C:\Users\\AppData\Local\Temp 101 | //Creates a file named update.exe for Windows, update.tar.xz for Linux 102 | //Launches the update.exe on Windows 103 | //Launches the TEMP directory and selects the update.tar.xz file 104 | //User has to extract the .tar.xz file to their pwd 105 | Directory saveDirectory=await getTemporaryDirectory(); 106 | String appendSymbol=Platform.isWindows?"\\":"/"; 107 | String executableFileExtension = "exe"; 108 | if(Platform.isMacOS) { 109 | executableFileExtension = "dmg"; 110 | } else if(Platform.isLinux) { 111 | executableFileExtension = "tar.xz"; 112 | } 113 | File latestRelease=File("${saveDirectory.path}${appendSymbol}update.$executableFileExtension"); 114 | if(await latestRelease.exists()){ 115 | await latestRelease.delete(); 116 | } 117 | http.Response response=await http.get(Uri.parse(url)); 118 | if(response.statusCode==200){ 119 | //Save file and return path to file; 120 | await latestRelease.create(); 121 | await latestRelease.writeAsBytes(response.bodyBytes); 122 | return "${saveDirectory.path}${appendSymbol}update.$executableFileExtension"; 123 | } 124 | throw "Error occurred when attempting to download file"; 125 | } -------------------------------------------------------------------------------- /lib/utils/const.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const kDarkModeMenuColor = Color(0xFF212121); //Colors.grey[900] 4 | // const kAccentColor=Color(0xFF2196F3); //Colors.blue 5 | const kLightModeAppBarTitleColor = Color(0xFF242424); -------------------------------------------------------------------------------- /lib/utils/enums.dart: -------------------------------------------------------------------------------- 1 | enum Screens{ 2 | fileManager, 3 | packageManager, 4 | powerControls, 5 | deviceInfo, 6 | other 7 | } 8 | 9 | enum FileItemType{ 10 | file, 11 | directory 12 | } 13 | 14 | enum FileContentTypes{ 15 | file, 16 | directory, 17 | pdf, 18 | wordDocument, 19 | powerpoint, 20 | excel, 21 | image, 22 | audio, 23 | video, 24 | archive, 25 | apk, 26 | torrent, 27 | securityCertificate, 28 | } 29 | 30 | enum FileTransferType{ 31 | move, 32 | copy, 33 | pcToPhone, 34 | phoneToPC 35 | } 36 | 37 | enum AppType{ 38 | system, 39 | user 40 | } 41 | 42 | enum AppInstallType{ 43 | single, 44 | multiApks, 45 | batch, 46 | } 47 | 48 | enum ProcessStatus{ 49 | notStarted, 50 | working, 51 | success, 52 | fail, 53 | } 54 | 55 | enum AppInstaller{ 56 | googlePlayStore, 57 | custom 58 | } 59 | 60 | enum CompilationMode{ 61 | quicken, 62 | space, 63 | spaceProfile, 64 | speed, 65 | speedProfile, 66 | everything, 67 | } -------------------------------------------------------------------------------- /lib/utils/vars.dart: -------------------------------------------------------------------------------- 1 | 2 | import 'package:bitsdojo_window/bitsdojo_window.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | String adbExecutable="adb"; 6 | 7 | final windowButtonColors = WindowButtonColors( 8 | iconNormal: Colors.white, 9 | mouseOver: Colors.lightBlueAccent, 10 | mouseDown: Colors.blue, 11 | iconMouseOver: Colors.white, 12 | iconMouseDown: Colors.white 13 | ); 14 | 15 | final windowCloseButtonColors = WindowButtonColors( 16 | mouseOver: Colors.redAccent, 17 | mouseDown: Colors.blue, 18 | iconNormal: Colors.white, 19 | iconMouseOver: Colors.white 20 | ); 21 | 22 | final List clipboardChipColors=[ 23 | Colors.pink, 24 | Colors.green, 25 | Colors.red, 26 | Colors.blueGrey, 27 | Colors.cyan 28 | ]; -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(runner LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "adb_gui") 5 | set(APPLICATION_ID "com.lightningbolt.adb_gui") 6 | 7 | cmake_policy(SET CMP0063 NEW) 8 | 9 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 10 | 11 | # Root filesystem for cross-building. 12 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 13 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 14 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 18 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 19 | endif() 20 | 21 | # Configure build options. 22 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 23 | set(CMAKE_BUILD_TYPE "Debug" CACHE 24 | STRING "Flutter build mode" FORCE) 25 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 26 | "Debug" "Profile" "Release") 27 | endif() 28 | 29 | # Compilation settings that should be applied to most targets. 30 | function(APPLY_STANDARD_SETTINGS TARGET) 31 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 32 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 33 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 34 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 35 | endfunction() 36 | 37 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 38 | 39 | # Flutter library and tool build rules. 40 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 41 | 42 | # System-level dependencies. 43 | find_package(PkgConfig REQUIRED) 44 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 45 | 46 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 47 | 48 | # Application build 49 | add_executable(${BINARY_NAME} 50 | "main.cc" 51 | "my_application.cc" 52 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 53 | ) 54 | apply_standard_settings(${BINARY_NAME}) 55 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 56 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 57 | add_dependencies(${BINARY_NAME} flutter_assemble) 58 | # Only the install-generated bundle's copy of the executable will launch 59 | # correctly, since the resources must in the right relative locations. To avoid 60 | # people trying to run the unbundled copy, put it in a subdirectory instead of 61 | # the default top-level location. 62 | set_target_properties(${BINARY_NAME} 63 | PROPERTIES 64 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 65 | ) 66 | 67 | # Generated plugin build rules, which manage building the plugins and adding 68 | # them to the application. 69 | include(flutter/generated_plugins.cmake) 70 | 71 | 72 | # === Installation === 73 | # By default, "installing" just makes a relocatable bundle in the build 74 | # directory. 75 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 76 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 77 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 78 | endif() 79 | 80 | # Start with a clean build bundle directory every time. 81 | install(CODE " 82 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 83 | " COMPONENT Runtime) 84 | 85 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 86 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 87 | 88 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 89 | COMPONENT Runtime) 90 | 91 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 92 | COMPONENT Runtime) 93 | 94 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 95 | COMPONENT Runtime) 96 | 97 | if(PLUGIN_BUNDLED_LIBRARIES) 98 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 99 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 100 | COMPONENT Runtime) 101 | endif() 102 | 103 | # Fully re-copy the assets directory on each build to avoid having stale files 104 | # from a previous install. 105 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 106 | install(CODE " 107 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 108 | " COMPONENT Runtime) 109 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 110 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 111 | 112 | # Install the AOT library on non-Debug builds only. 113 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 114 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 115 | COMPONENT Runtime) 116 | endif() 117 | -------------------------------------------------------------------------------- /linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | 11 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 12 | # which isn't available in 3.10. 13 | function(list_prepend LIST_NAME PREFIX) 14 | set(NEW_LIST "") 15 | foreach(element ${${LIST_NAME}}) 16 | list(APPEND NEW_LIST "${PREFIX}${element}") 17 | endforeach(element) 18 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 19 | endfunction() 20 | 21 | # === Flutter Library === 22 | # System-level dependencies. 23 | find_package(PkgConfig REQUIRED) 24 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 25 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 26 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 27 | 28 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 29 | 30 | # Published to parent scope for install step. 31 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 32 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 33 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 34 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 35 | 36 | list(APPEND FLUTTER_LIBRARY_HEADERS 37 | "fl_basic_message_channel.h" 38 | "fl_binary_codec.h" 39 | "fl_binary_messenger.h" 40 | "fl_dart_project.h" 41 | "fl_engine.h" 42 | "fl_json_message_codec.h" 43 | "fl_json_method_codec.h" 44 | "fl_message_codec.h" 45 | "fl_method_call.h" 46 | "fl_method_channel.h" 47 | "fl_method_codec.h" 48 | "fl_method_response.h" 49 | "fl_plugin_registrar.h" 50 | "fl_plugin_registry.h" 51 | "fl_standard_message_codec.h" 52 | "fl_standard_method_codec.h" 53 | "fl_string_codec.h" 54 | "fl_value.h" 55 | "fl_view.h" 56 | "flutter_linux.h" 57 | ) 58 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 59 | add_library(flutter INTERFACE) 60 | target_include_directories(flutter INTERFACE 61 | "${EPHEMERAL_DIR}" 62 | ) 63 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 64 | target_link_libraries(flutter INTERFACE 65 | PkgConfig::GTK 66 | PkgConfig::GLIB 67 | PkgConfig::GIO 68 | ) 69 | add_dependencies(flutter flutter_assemble) 70 | 71 | # === Flutter tool backend === 72 | # _phony_ is a non-existent file to force this command to run every time, 73 | # since currently there's no way to get a full input/output list from the 74 | # flutter tool. 75 | add_custom_command( 76 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 77 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 78 | COMMAND ${CMAKE_COMMAND} -E env 79 | ${FLUTTER_TOOL_ENVIRONMENT} 80 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 81 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 82 | VERBATIM 83 | ) 84 | add_custom_target(flutter_assemble DEPENDS 85 | "${FLUTTER_LIBRARY}" 86 | ${FLUTTER_LIBRARY_HEADERS} 87 | ) 88 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | void fl_register_plugins(FlPluginRegistry* registry) { 14 | g_autoptr(FlPluginRegistrar) bitsdojo_window_linux_registrar = 15 | fl_plugin_registry_get_registrar_for_plugin(registry, "BitsdojoWindowPlugin"); 16 | bitsdojo_window_plugin_register_with_registrar(bitsdojo_window_linux_registrar); 17 | g_autoptr(FlPluginRegistrar) flutter_acrylic_registrar = 18 | fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterAcrylicPlugin"); 19 | flutter_acrylic_plugin_register_with_registrar(flutter_acrylic_registrar); 20 | g_autoptr(FlPluginRegistrar) system_theme_registrar = 21 | fl_plugin_registry_get_registrar_for_plugin(registry, "SystemThemePlugin"); 22 | system_theme_plugin_register_with_registrar(system_theme_registrar); 23 | } 24 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void fl_register_plugins(FlPluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /linux/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | bitsdojo_window_linux 7 | flutter_acrylic 8 | system_theme 9 | ) 10 | 11 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 12 | ) 13 | 14 | set(PLUGIN_BUNDLED_LIBRARIES) 15 | 16 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 17 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) 18 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 21 | endforeach(plugin) 22 | 23 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 24 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) 25 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 26 | endforeach(ffi_plugin) 27 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "my_application.h" 4 | 5 | #include 6 | #ifdef GDK_WINDOWING_X11 7 | #include 8 | #endif 9 | 10 | #include "flutter/generated_plugin_registrant.h" 11 | 12 | struct _MyApplication { 13 | GtkApplication parent_instance; 14 | char** dart_entrypoint_arguments; 15 | }; 16 | 17 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 18 | 19 | // Implements GApplication::activate. 20 | static void my_application_activate(GApplication* application) { 21 | MyApplication* self = MY_APPLICATION(application); 22 | GtkWindow* window = 23 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 24 | 25 | // Use a header bar when running in GNOME as this is the common style used 26 | // by applications and is the setup most users will be using (e.g. Ubuntu 27 | // desktop). 28 | // If running on X and not using GNOME then just use a traditional title bar 29 | // in case the window manager does more exotic layout, e.g. tiling. 30 | // If running on Wayland assume the header bar will work (may need changing 31 | // if future cases occur). 32 | gboolean use_header_bar = TRUE; 33 | #ifdef GDK_WINDOWING_X11 34 | GdkScreen* screen = gtk_window_get_screen(window); 35 | if (GDK_IS_X11_SCREEN(screen)) { 36 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 37 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 38 | use_header_bar = FALSE; 39 | } 40 | } 41 | #endif 42 | if (use_header_bar) { 43 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 44 | gtk_widget_show(GTK_WIDGET(header_bar)); 45 | gtk_header_bar_set_title(header_bar, "adb_gui"); 46 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 47 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 48 | } else { 49 | gtk_window_set_title(window, "adb_gui"); 50 | } 51 | 52 | auto bdw = bitsdojo_window_from(window); // <--- add this line 53 | bdw->setCustomFrame(true); // <-- add this line 54 | //gtk_window_set_default_size(window, 1280, 720); // <-- comment this line 55 | 56 | 57 | //gtk_widget_show(GTK_WIDGET(window)); 58 | 59 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 60 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 61 | 62 | FlView* view = fl_view_new(project); 63 | //gtk_widget_show(GTK_WIDGET(view)); 64 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 65 | 66 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 67 | 68 | gtk_widget_grab_focus(GTK_WIDGET(view)); 69 | } 70 | 71 | // Implements GApplication::local_command_line. 72 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 73 | MyApplication* self = MY_APPLICATION(application); 74 | // Strip out the first argument as it is the binary name. 75 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 76 | 77 | g_autoptr(GError) error = nullptr; 78 | if (!g_application_register(application, nullptr, &error)) { 79 | g_warning("Failed to register: %s", error->message); 80 | *exit_status = 1; 81 | return TRUE; 82 | } 83 | 84 | g_application_activate(application); 85 | *exit_status = 0; 86 | 87 | return TRUE; 88 | } 89 | 90 | // Implements GObject::dispose. 91 | static void my_application_dispose(GObject* object) { 92 | MyApplication* self = MY_APPLICATION(object); 93 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 94 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 95 | } 96 | 97 | static void my_application_class_init(MyApplicationClass* klass) { 98 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 99 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 100 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 101 | } 102 | 103 | static void my_application_init(MyApplication* self) {} 104 | 105 | MyApplication* my_application_new() { 106 | return MY_APPLICATION(g_object_new(my_application_get_type(), 107 | "application-id", APPLICATION_ID, 108 | "flags", G_APPLICATION_NON_UNIQUE, 109 | nullptr)); 110 | } 111 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import bitsdojo_window_macos 9 | import macos_window_utils 10 | import package_info_plus 11 | import path_provider_foundation 12 | import shared_preferences_foundation 13 | import system_theme 14 | 15 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 16 | BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin")) 17 | MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: "MacOSWindowUtilsPlugin")) 18 | FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) 19 | PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) 20 | SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) 21 | SystemThemePlugin.register(with: registry.registrar(forPlugin: "SystemThemePlugin")) 22 | } 23 | -------------------------------------------------------------------------------- /macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '14.0' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_macos_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - bitsdojo_window_macos (0.0.1): 3 | - FlutterMacOS 4 | - FlutterMacOS (1.0.0) 5 | - macos_window_utils (1.0.0): 6 | - FlutterMacOS 7 | - package_info_plus (0.0.1): 8 | - FlutterMacOS 9 | - path_provider_foundation (0.0.1): 10 | - Flutter 11 | - FlutterMacOS 12 | - shared_preferences_foundation (0.0.1): 13 | - Flutter 14 | - FlutterMacOS 15 | - system_theme (0.0.1): 16 | - FlutterMacOS 17 | 18 | DEPENDENCIES: 19 | - bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`) 20 | - FlutterMacOS (from `Flutter/ephemeral`) 21 | - macos_window_utils (from `Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos`) 22 | - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) 23 | - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) 24 | - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) 25 | - system_theme (from `Flutter/ephemeral/.symlinks/plugins/system_theme/macos`) 26 | 27 | EXTERNAL SOURCES: 28 | bitsdojo_window_macos: 29 | :path: Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos 30 | FlutterMacOS: 31 | :path: Flutter/ephemeral 32 | macos_window_utils: 33 | :path: Flutter/ephemeral/.symlinks/plugins/macos_window_utils/macos 34 | package_info_plus: 35 | :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos 36 | path_provider_foundation: 37 | :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin 38 | shared_preferences_foundation: 39 | :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin 40 | system_theme: 41 | :path: Flutter/ephemeral/.symlinks/plugins/system_theme/macos 42 | 43 | SPEC CHECKSUMS: 44 | bitsdojo_window_macos: 44e3b8fe3dd463820e0321f6256c5b1c16bb6a00 45 | FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 46 | macos_window_utils: 933f91f64805e2eb91a5bd057cf97cd097276663 47 | package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce 48 | path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c 49 | shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 50 | system_theme: c7b9f6659a5caa26c9bc2284da096781e9a6fcbc 51 | 52 | PODFILE CHECKSUM: 6acf97521436d16fc31cd5e1a02000905acdb3ae 53 | 54 | COCOAPODS: 1.15.2 55 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 43 | 49 | 50 | 51 | 52 | 53 | 63 | 65 | 71 | 72 | 73 | 74 | 80 | 82 | 88 | 89 | 90 | 91 | 93 | 94 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/macos/Runner/Assets.xcassets/AppIcon.appiconset/1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/macos/Runner/Assets.xcassets/AppIcon.appiconset/128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/macos/Runner/Assets.xcassets/AppIcon.appiconset/16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/macos/Runner/Assets.xcassets/AppIcon.appiconset/256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/macos/Runner/Assets.xcassets/AppIcon.appiconset/32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/macos/Runner/Assets.xcassets/AppIcon.appiconset/512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/macos/Runner/Assets.xcassets/AppIcon.appiconset/64.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "16.png", 5 | "idiom" : "mac", 6 | "scale" : "1x", 7 | "size" : "16x16" 8 | }, 9 | { 10 | "filename" : "32.png", 11 | "idiom" : "mac", 12 | "scale" : "2x", 13 | "size" : "16x16" 14 | }, 15 | { 16 | "filename" : "32.png", 17 | "idiom" : "mac", 18 | "scale" : "1x", 19 | "size" : "32x32" 20 | }, 21 | { 22 | "filename" : "64.png", 23 | "idiom" : "mac", 24 | "scale" : "2x", 25 | "size" : "32x32" 26 | }, 27 | { 28 | "filename" : "128.png", 29 | "idiom" : "mac", 30 | "scale" : "1x", 31 | "size" : "128x128" 32 | }, 33 | { 34 | "filename" : "256.png", 35 | "idiom" : "mac", 36 | "scale" : "2x", 37 | "size" : "128x128" 38 | }, 39 | { 40 | "filename" : "256.png", 41 | "idiom" : "mac", 42 | "scale" : "1x", 43 | "size" : "256x256" 44 | }, 45 | { 46 | "filename" : "512.png", 47 | "idiom" : "mac", 48 | "scale" : "2x", 49 | "size" : "256x256" 50 | }, 51 | { 52 | "filename" : "512.png", 53 | "idiom" : "mac", 54 | "scale" : "1x", 55 | "size" : "512x512" 56 | }, 57 | { 58 | "filename" : "1024.png", 59 | "idiom" : "mac", 60 | "scale" : "2x", 61 | "size" : "512x512" 62 | } 63 | ], 64 | "info" : { 65 | "author" : "xcode", 66 | "version" : 1 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = Android Toolbox 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.lightning.adbGui 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = GPL-3.0 2024 com.lightning. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.cs.allow-jit 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | UILaunchStoryboardName 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | import bitsdojo_window_macos 4 | 5 | class MainFlutterWindow: BitsdojoWindow { 6 | 7 | override func bitsdojo_window_configure() -> UInt { 8 | return BDW_CUSTOM_FRAME 9 | } 10 | 11 | override func awakeFromNib() { 12 | let flutterViewController = FlutterViewController() 13 | let windowFrame = self.frame 14 | self.contentViewController = flutterViewController 15 | self.setFrame(windowFrame, display: true) 16 | 17 | RegisterGeneratedPlugins(registry: flutterViewController) 18 | 19 | super.awakeFromNib() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 3 | import XCTest 4 | 5 | class RunnerTests: XCTestCase { 6 | 7 | func testExample() { 8 | // If you add code to the Runner application, consider adding tests here. 9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest. 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: adb_gui 2 | description: Android-Toolbox 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 2.0.0 19 | 20 | environment: 21 | sdk: '>=3.3.1 <4.0.0' 22 | 23 | # Dependencies specify other packages that your package needs in order to work. 24 | # To automatically upgrade your package dependencies to the latest versions 25 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 26 | # dependencies can be manually updated by changing the version numbers below to 27 | # the latest version available on pub.dev. To see which dependencies have newer 28 | # versions available, run `flutter pub outdated`. 29 | dependencies: 30 | flutter: 31 | sdk: flutter 32 | 33 | 34 | # The following adds the Cupertino Icons font to your application. 35 | # Use with the CupertinoIcons class for iOS style icons. 36 | # cupertino_icons: ^1.0.2 37 | # process_run: ^0.12.3+1 38 | file_picker: ^6.2.0 39 | font_awesome_flutter: ^10.7.0 40 | bitsdojo_window: ^0.1.6 41 | package_info_plus: ^5.0.1 42 | shared_preferences: ^2.2.2 43 | path_provider: ^2.1.2 44 | version: ^3.0.2 45 | shimmer: ^3.0.0 46 | percent_indicator: ^4.2.3 47 | google_fonts: ^6.2.1 48 | flutter_acrylic: ^1.1.3 49 | system_theme: ^2.3.1 50 | flutter_staggered_grid_view: ^0.7.0 51 | 52 | #msix_config: 53 | # display_name: Android-Toolbox 54 | # publisher_display_name: LightningBolt 55 | # identity_name: com.lightning.adbgui 56 | # msix_version: 1.5.3.0 57 | # logo_path: C:\Users\sasha\AndroidStudioProjects\adb_gui\assets\logo512.png 58 | # capabilities: "internetClient" 59 | # store: false 60 | 61 | 62 | dev_dependencies: 63 | flutter_test: 64 | sdk: flutter 65 | 66 | # The "flutter_lints" package below contains a set of recommended lints to 67 | # encourage good coding practices. The lint set provided by the package is 68 | # activated in the `analysis_options.yaml` file located at the root of your 69 | # package. See that file for information about deactivating specific lint 70 | # rules and activating additional ones. 71 | flutter_lints: ^2.0.1 72 | # msix: ^2.8.13 73 | 74 | # For information on the generic Dart part of this file, see the 75 | # following page: https://dart.dev/tools/pub/pubspec 76 | 77 | # The following section is specific to Flutter. 78 | flutter: 79 | 80 | # The following line ensures that the Material Icons font is 81 | # included with your application, so that you can use the icons in 82 | # the material Icons class. 83 | uses-material-design: true 84 | 85 | # To add assets to your application, add an assets section, like this: 86 | assets: 87 | - assets/adb.exe 88 | - assets/adb 89 | - assets/adbMac 90 | - assets/AdbWinApi.dll 91 | - assets/AdbWinUsbApi.dll 92 | - assets/WSA_logo.png 93 | - assets/lightningBoltLogo.png 94 | 95 | # An image asset can refer to one or more resolution-specific "variants", see 96 | # https://flutter.dev/assets-and-images/#resolution-aware. 97 | 98 | # For details regarding adding assets from package dependencies, see 99 | # https://flutter.dev/assets-and-images/#from-packages 100 | 101 | # To add custom fonts to your application, add a fonts section here, 102 | # in this "flutter" section. Each entry in this list should have a 103 | # "family" key with the font family name, and a "fonts" key with a 104 | # list giving the asset and other descriptors for the font. For 105 | # example: 106 | # fonts: 107 | # - family: Schyler 108 | # fonts: 109 | # - asset: fonts/Schyler-Regular.ttf 110 | # - asset: fonts/Schyler-Italic.ttf 111 | # style: italic 112 | # - family: Trajan Pro 113 | # fonts: 114 | # - asset: fonts/TrajanPro.ttf 115 | # - asset: fonts/TrajanPro_Bold.ttf 116 | # weight: 700 117 | # 118 | # For details regarding fonts from package dependencies, 119 | # see https://flutter.dev/custom-fonts/#from-packages 120 | -------------------------------------------------------------------------------- /readme_assets/lightningBoltLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/readme_assets/lightningBoltLogo.png -------------------------------------------------------------------------------- /readme_assets/screenshot_apps.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/readme_assets/screenshot_apps.png -------------------------------------------------------------------------------- /readme_assets/screenshot_connection_initiation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/readme_assets/screenshot_connection_initiation.png -------------------------------------------------------------------------------- /readme_assets/screenshot_device_info.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/readme_assets/screenshot_device_info.png -------------------------------------------------------------------------------- /readme_assets/screenshot_file_manager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/readme_assets/screenshot_file_manager.png -------------------------------------------------------------------------------- /readme_assets/screenshot_power_controls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/readme_assets/screenshot_power_controls.png -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:adb_gui/screens/connection_initiation_screen.dart'; 9 | import 'package:flutter/material.dart'; 10 | import 'package:flutter_test/flutter_test.dart'; 11 | 12 | void main() { 13 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 14 | // Build our app and trigger a frame. 15 | await tester.pumpWidget(const ConnectionInitiationScreen()); 16 | 17 | // Verify that our counter starts at 0. 18 | expect(find.text('0'), findsOneWidget); 19 | expect(find.text('1'), findsNothing); 20 | 21 | // Tap the '+' icon and trigger a frame. 22 | await tester.tap(find.byIcon(Icons.add)); 23 | await tester.pump(); 24 | 25 | // Verify that our counter has incremented. 26 | expect(find.text('0'), findsNothing); 27 | expect(find.text('1'), findsOneWidget); 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(adb_gui LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "adb_gui") 5 | 6 | cmake_policy(SET CMP0063 NEW) 7 | 8 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 9 | 10 | # Configure build options. 11 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 12 | if(IS_MULTICONFIG) 13 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 14 | CACHE STRING "" FORCE) 15 | else() 16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 17 | set(CMAKE_BUILD_TYPE "Debug" CACHE 18 | STRING "Flutter build mode" FORCE) 19 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 20 | "Debug" "Profile" "Release") 21 | endif() 22 | endif() 23 | 24 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 25 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 26 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 27 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 28 | 29 | # Use Unicode for all projects. 30 | add_definitions(-DUNICODE -D_UNICODE) 31 | 32 | # Compilation settings that should be applied to most targets. 33 | function(APPLY_STANDARD_SETTINGS TARGET) 34 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 35 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 36 | target_compile_options(${TARGET} PRIVATE /EHsc) 37 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 38 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 39 | endfunction() 40 | 41 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 42 | 43 | # Flutter library and tool build rules. 44 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 45 | 46 | # Application build 47 | add_subdirectory("runner") 48 | 49 | # Generated plugin build rules, which manage building the plugins and adding 50 | # them to the application. 51 | include(flutter/generated_plugins.cmake) 52 | 53 | 54 | # === Installation === 55 | # Support files are copied into place next to the executable, so that it can 56 | # run in place. This is done instead of making a separate bundle (as on Linux) 57 | # so that building and running from within Visual Studio will work. 58 | set(BUILD_BUNDLE_DIR "$") 59 | # Make the "install" step default, as it's required to run. 60 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 61 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 62 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 63 | endif() 64 | 65 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 66 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 67 | 68 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 69 | COMPONENT Runtime) 70 | 71 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 72 | COMPONENT Runtime) 73 | 74 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 75 | COMPONENT Runtime) 76 | 77 | if(PLUGIN_BUNDLED_LIBRARIES) 78 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 79 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 80 | COMPONENT Runtime) 81 | endif() 82 | 83 | # Fully re-copy the assets directory on each build to avoid having stale files 84 | # from a previous install. 85 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 86 | install(CODE " 87 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 88 | " COMPONENT Runtime) 89 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 90 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 91 | 92 | # Install the AOT library on non-Debug builds only. 93 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 94 | CONFIGURATIONS Profile;Release 95 | COMPONENT Runtime) 96 | -------------------------------------------------------------------------------- /windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 11 | 12 | # === Flutter Library === 13 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 14 | 15 | # Published to parent scope for install step. 16 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 17 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 18 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 19 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 20 | 21 | list(APPEND FLUTTER_LIBRARY_HEADERS 22 | "flutter_export.h" 23 | "flutter_windows.h" 24 | "flutter_messenger.h" 25 | "flutter_plugin_registrar.h" 26 | "flutter_texture_registrar.h" 27 | ) 28 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 29 | add_library(flutter INTERFACE) 30 | target_include_directories(flutter INTERFACE 31 | "${EPHEMERAL_DIR}" 32 | ) 33 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 34 | add_dependencies(flutter flutter_assemble) 35 | 36 | # === Wrapper === 37 | list(APPEND CPP_WRAPPER_SOURCES_CORE 38 | "core_implementations.cc" 39 | "standard_codec.cc" 40 | ) 41 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 42 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 43 | "plugin_registrar.cc" 44 | ) 45 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 46 | list(APPEND CPP_WRAPPER_SOURCES_APP 47 | "flutter_engine.cc" 48 | "flutter_view_controller.cc" 49 | ) 50 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 51 | 52 | # Wrapper sources needed for a plugin. 53 | add_library(flutter_wrapper_plugin STATIC 54 | ${CPP_WRAPPER_SOURCES_CORE} 55 | ${CPP_WRAPPER_SOURCES_PLUGIN} 56 | ) 57 | apply_standard_settings(flutter_wrapper_plugin) 58 | set_target_properties(flutter_wrapper_plugin PROPERTIES 59 | POSITION_INDEPENDENT_CODE ON) 60 | set_target_properties(flutter_wrapper_plugin PROPERTIES 61 | CXX_VISIBILITY_PRESET hidden) 62 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 63 | target_include_directories(flutter_wrapper_plugin PUBLIC 64 | "${WRAPPER_ROOT}/include" 65 | ) 66 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 67 | 68 | # Wrapper sources needed for the runner. 69 | add_library(flutter_wrapper_app STATIC 70 | ${CPP_WRAPPER_SOURCES_CORE} 71 | ${CPP_WRAPPER_SOURCES_APP} 72 | ) 73 | apply_standard_settings(flutter_wrapper_app) 74 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 75 | target_include_directories(flutter_wrapper_app PUBLIC 76 | "${WRAPPER_ROOT}/include" 77 | ) 78 | add_dependencies(flutter_wrapper_app flutter_assemble) 79 | 80 | # === Flutter tool backend === 81 | # _phony_ is a non-existent file to force this command to run every time, 82 | # since currently there's no way to get a full input/output list from the 83 | # flutter tool. 84 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 85 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 86 | add_custom_command( 87 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 88 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 89 | ${CPP_WRAPPER_SOURCES_APP} 90 | ${PHONY_OUTPUT} 91 | COMMAND ${CMAKE_COMMAND} -E env 92 | ${FLUTTER_TOOL_ENVIRONMENT} 93 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 94 | windows-x64 $ 95 | VERBATIM 96 | ) 97 | add_custom_target(flutter_assemble DEPENDS 98 | "${FLUTTER_LIBRARY}" 99 | ${FLUTTER_LIBRARY_HEADERS} 100 | ${CPP_WRAPPER_SOURCES_CORE} 101 | ${CPP_WRAPPER_SOURCES_PLUGIN} 102 | ${CPP_WRAPPER_SOURCES_APP} 103 | ) 104 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.cc: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #include "generated_plugin_registrant.h" 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | void RegisterPlugins(flutter::PluginRegistry* registry) { 14 | BitsdojoWindowPluginRegisterWithRegistrar( 15 | registry->GetRegistrarForPlugin("BitsdojoWindowPlugin")); 16 | FlutterAcrylicPluginRegisterWithRegistrar( 17 | registry->GetRegistrarForPlugin("FlutterAcrylicPlugin")); 18 | SystemThemePluginRegisterWithRegistrar( 19 | registry->GetRegistrarForPlugin("SystemThemePlugin")); 20 | } 21 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugin_registrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | // clang-format off 6 | 7 | #ifndef GENERATED_PLUGIN_REGISTRANT_ 8 | #define GENERATED_PLUGIN_REGISTRANT_ 9 | 10 | #include 11 | 12 | // Registers Flutter plugins. 13 | void RegisterPlugins(flutter::PluginRegistry* registry); 14 | 15 | #endif // GENERATED_PLUGIN_REGISTRANT_ 16 | -------------------------------------------------------------------------------- /windows/flutter/generated_plugins.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Generated file, do not edit. 3 | # 4 | 5 | list(APPEND FLUTTER_PLUGIN_LIST 6 | bitsdojo_window_windows 7 | flutter_acrylic 8 | system_theme 9 | ) 10 | 11 | list(APPEND FLUTTER_FFI_PLUGIN_LIST 12 | ) 13 | 14 | set(PLUGIN_BUNDLED_LIBRARIES) 15 | 16 | foreach(plugin ${FLUTTER_PLUGIN_LIST}) 17 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) 18 | target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) 19 | list(APPEND PLUGIN_BUNDLED_LIBRARIES $) 20 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) 21 | endforeach(plugin) 22 | 23 | foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) 24 | add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) 25 | list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) 26 | endforeach(ffi_plugin) 27 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | add_executable(${BINARY_NAME} WIN32 5 | "flutter_window.cpp" 6 | "main.cpp" 7 | "utils.cpp" 8 | "win32_window.cpp" 9 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 10 | "Runner.rc" 11 | "runner.exe.manifest" 12 | ) 13 | apply_standard_settings(${BINARY_NAME}) 14 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 15 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 16 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 17 | add_dependencies(${BINARY_NAME} flutter_assemble) 18 | -------------------------------------------------------------------------------- /windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #ifdef FLUTTER_BUILD_NUMBER 64 | #define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER 65 | #else 66 | #define VERSION_AS_NUMBER 2,0,0 67 | #endif 68 | 69 | #ifdef FLUTTER_BUILD_NAME 70 | #define VERSION_AS_STRING #FLUTTER_BUILD_NAME 71 | #else 72 | #define VERSION_AS_STRING "2.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.lightningbolt" "\0" 93 | VALUE "FileDescription", "Android-Toolbox" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "adb_gui" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2021 com.lightningbolt. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "adb_gui.exe" "\0" 98 | VALUE "ProductName", "adb_gui" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | return true; 30 | } 31 | 32 | void FlutterWindow::OnDestroy() { 33 | if (flutter_controller_) { 34 | flutter_controller_ = nullptr; 35 | } 36 | 37 | Win32Window::OnDestroy(); 38 | } 39 | 40 | LRESULT 41 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 42 | WPARAM const wparam, 43 | LPARAM const lparam) noexcept { 44 | // Give Flutter, including plugins, an opportunity to handle window messages. 45 | if (flutter_controller_) { 46 | std::optional result = 47 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 48 | lparam); 49 | if (result) { 50 | return *result; 51 | } 52 | } 53 | 54 | switch (message) { 55 | case WM_FONTCHANGE: 56 | flutter_controller_->engine()->ReloadSystemFonts(); 57 | break; 58 | } 59 | 60 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 61 | } 62 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | auto bdw = bitsdojo_window_configure(BDW_CUSTOM_FRAME | BDW_HIDE_ON_STARTUP); 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "flutter_window.h" 9 | #include "utils.h" 10 | 11 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 12 | _In_ wchar_t *command_line, _In_ int show_command) { 13 | // Attach to console when present (e.g., 'flutter run') or create a 14 | // new console when running with a debugger. 15 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 16 | CreateAndAttachConsole(); 17 | }else{ 18 | AllocConsole(); 19 | ShowWindow(GetConsoleWindow(), SW_HIDE); 20 | } 21 | 22 | // Initialize COM, so that it is available for use in the library and/or 23 | // plugins. 24 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 25 | 26 | flutter::DartProject project(L"data"); 27 | 28 | std::vector command_line_arguments = 29 | GetCommandLineArguments(); 30 | 31 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 32 | 33 | FlutterWindow window(project); 34 | Win32Window::Point origin(10, 10); 35 | Win32Window::Size size(1280, 720); 36 | if (!window.CreateAndShow(L"adb_gui", origin, size)) { 37 | return EXIT_FAILURE; 38 | } 39 | window.SetQuitOnClose(true); 40 | 41 | ::MSG msg; 42 | while (::GetMessage(&msg, nullptr, 0, 0)) { 43 | ::TranslateMessage(&msg); 44 | ::DispatchMessage(&msg); 45 | } 46 | 47 | ::CoUninitialize(); 48 | return EXIT_SUCCESS; 49 | } 50 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lightningbolt047/Android-Toolbox/393943c35f52e4f79876d042a7251d1c430271e6/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr); 51 | if (target_length == 0) { 52 | return std::string(); 53 | } 54 | std::string utf8_string; 55 | utf8_string.resize(target_length); 56 | int converted_length = ::WideCharToMultiByte( 57 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 58 | -1, utf8_string.data(), 59 | target_length, nullptr, nullptr); 60 | if (converted_length == 0) { 61 | return std::string(); 62 | } 63 | return utf8_string; 64 | } 65 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /windows/runner/win32_window.cpp: -------------------------------------------------------------------------------- 1 | #include "win32_window.h" 2 | 3 | #include 4 | 5 | #include "resource.h" 6 | 7 | namespace { 8 | 9 | constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; 10 | 11 | // The number of Win32Window objects that currently exist. 12 | static int g_active_window_count = 0; 13 | 14 | using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); 15 | 16 | // Scale helper to convert logical scaler values to physical using passed in 17 | // scale factor 18 | int Scale(int source, double scale_factor) { 19 | return static_cast(source * scale_factor); 20 | } 21 | 22 | // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. 23 | // This API is only needed for PerMonitor V1 awareness mode. 24 | void EnableFullDpiSupportIfAvailable(HWND hwnd) { 25 | HMODULE user32_module = LoadLibraryA("User32.dll"); 26 | if (!user32_module) { 27 | return; 28 | } 29 | auto enable_non_client_dpi_scaling = 30 | reinterpret_cast( 31 | GetProcAddress(user32_module, "EnableNonClientDpiScaling")); 32 | if (enable_non_client_dpi_scaling != nullptr) { 33 | enable_non_client_dpi_scaling(hwnd); 34 | FreeLibrary(user32_module); 35 | } 36 | } 37 | 38 | } // namespace 39 | 40 | // Manages the Win32Window's window class registration. 41 | class WindowClassRegistrar { 42 | public: 43 | ~WindowClassRegistrar() = default; 44 | 45 | // Returns the singleton registar instance. 46 | static WindowClassRegistrar* GetInstance() { 47 | if (!instance_) { 48 | instance_ = new WindowClassRegistrar(); 49 | } 50 | return instance_; 51 | } 52 | 53 | // Returns the name of the window class, registering the class if it hasn't 54 | // previously been registered. 55 | const wchar_t* GetWindowClass(); 56 | 57 | // Unregisters the window class. Should only be called if there are no 58 | // instances of the window. 59 | void UnregisterWindowClass(); 60 | 61 | private: 62 | WindowClassRegistrar() = default; 63 | 64 | static WindowClassRegistrar* instance_; 65 | 66 | bool class_registered_ = false; 67 | }; 68 | 69 | WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; 70 | 71 | const wchar_t* WindowClassRegistrar::GetWindowClass() { 72 | if (!class_registered_) { 73 | WNDCLASS window_class{}; 74 | window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); 75 | window_class.lpszClassName = kWindowClassName; 76 | window_class.style = CS_HREDRAW | CS_VREDRAW; 77 | window_class.cbClsExtra = 0; 78 | window_class.cbWndExtra = 0; 79 | window_class.hInstance = GetModuleHandle(nullptr); 80 | window_class.hIcon = 81 | LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); 82 | window_class.hbrBackground = 0; 83 | window_class.lpszMenuName = nullptr; 84 | window_class.lpfnWndProc = Win32Window::WndProc; 85 | RegisterClass(&window_class); 86 | class_registered_ = true; 87 | } 88 | return kWindowClassName; 89 | } 90 | 91 | void WindowClassRegistrar::UnregisterWindowClass() { 92 | UnregisterClass(kWindowClassName, nullptr); 93 | class_registered_ = false; 94 | } 95 | 96 | Win32Window::Win32Window() { 97 | ++g_active_window_count; 98 | } 99 | 100 | Win32Window::~Win32Window() { 101 | --g_active_window_count; 102 | Destroy(); 103 | } 104 | 105 | bool Win32Window::CreateAndShow(const std::wstring& title, 106 | const Point& origin, 107 | const Size& size) { 108 | Destroy(); 109 | 110 | const wchar_t* window_class = 111 | WindowClassRegistrar::GetInstance()->GetWindowClass(); 112 | 113 | const POINT target_point = {static_cast(origin.x), 114 | static_cast(origin.y)}; 115 | HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); 116 | UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); 117 | double scale_factor = dpi / 96.0; 118 | 119 | HWND window = CreateWindow( 120 | window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 121 | Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), 122 | Scale(size.width, scale_factor), Scale(size.height, scale_factor), 123 | nullptr, nullptr, GetModuleHandle(nullptr), this); 124 | 125 | if (!window) { 126 | return false; 127 | } 128 | 129 | return OnCreate(); 130 | } 131 | 132 | // static 133 | LRESULT CALLBACK Win32Window::WndProc(HWND const window, 134 | UINT const message, 135 | WPARAM const wparam, 136 | LPARAM const lparam) noexcept { 137 | if (message == WM_NCCREATE) { 138 | auto window_struct = reinterpret_cast(lparam); 139 | SetWindowLongPtr(window, GWLP_USERDATA, 140 | reinterpret_cast(window_struct->lpCreateParams)); 141 | 142 | auto that = static_cast(window_struct->lpCreateParams); 143 | EnableFullDpiSupportIfAvailable(window); 144 | that->window_handle_ = window; 145 | } else if (Win32Window* that = GetThisFromHandle(window)) { 146 | return that->MessageHandler(window, message, wparam, lparam); 147 | } 148 | 149 | return DefWindowProc(window, message, wparam, lparam); 150 | } 151 | 152 | LRESULT 153 | Win32Window::MessageHandler(HWND hwnd, 154 | UINT const message, 155 | WPARAM const wparam, 156 | LPARAM const lparam) noexcept { 157 | switch (message) { 158 | case WM_DESTROY: 159 | window_handle_ = nullptr; 160 | Destroy(); 161 | if (quit_on_close_) { 162 | PostQuitMessage(0); 163 | } 164 | return 0; 165 | 166 | case WM_DPICHANGED: { 167 | auto newRectSize = reinterpret_cast(lparam); 168 | LONG newWidth = newRectSize->right - newRectSize->left; 169 | LONG newHeight = newRectSize->bottom - newRectSize->top; 170 | 171 | SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, 172 | newHeight, SWP_NOZORDER | SWP_NOACTIVATE); 173 | 174 | return 0; 175 | } 176 | case WM_SIZE: { 177 | RECT rect = GetClientArea(); 178 | if (child_content_ != nullptr) { 179 | // Size and position the child window. 180 | MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, 181 | rect.bottom - rect.top, TRUE); 182 | } 183 | return 0; 184 | } 185 | 186 | case WM_ACTIVATE: 187 | if (child_content_ != nullptr) { 188 | SetFocus(child_content_); 189 | } 190 | return 0; 191 | } 192 | 193 | return DefWindowProc(window_handle_, message, wparam, lparam); 194 | } 195 | 196 | void Win32Window::Destroy() { 197 | OnDestroy(); 198 | 199 | if (window_handle_) { 200 | DestroyWindow(window_handle_); 201 | window_handle_ = nullptr; 202 | } 203 | if (g_active_window_count == 0) { 204 | WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); 205 | } 206 | } 207 | 208 | Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { 209 | return reinterpret_cast( 210 | GetWindowLongPtr(window, GWLP_USERDATA)); 211 | } 212 | 213 | void Win32Window::SetChildContent(HWND content) { 214 | child_content_ = content; 215 | SetParent(content, window_handle_); 216 | RECT frame = GetClientArea(); 217 | 218 | MoveWindow(content, frame.left, frame.top, frame.right - frame.left, 219 | frame.bottom - frame.top, true); 220 | 221 | SetFocus(child_content_); 222 | } 223 | 224 | RECT Win32Window::GetClientArea() { 225 | RECT frame; 226 | GetClientRect(window_handle_, &frame); 227 | return frame; 228 | } 229 | 230 | HWND Win32Window::GetHandle() { 231 | return window_handle_; 232 | } 233 | 234 | void Win32Window::SetQuitOnClose(bool quit_on_close) { 235 | quit_on_close_ = quit_on_close; 236 | } 237 | 238 | bool Win32Window::OnCreate() { 239 | // No-op; provided for subclasses. 240 | return true; 241 | } 242 | 243 | void Win32Window::OnDestroy() { 244 | // No-op; provided for subclasses. 245 | } 246 | -------------------------------------------------------------------------------- /windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates and shows a win32 window with |title| and position and size using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size to will treat the width height passed in to this function 35 | // as logical pixels and scale to appropriate for the default monitor. Returns 36 | // true if the window was created successfully. 37 | bool CreateAndShow(const std::wstring& title, 38 | const Point& origin, 39 | const Size& size); 40 | 41 | // Release OS resources associated with window. 42 | void Destroy(); 43 | 44 | // Inserts |content| into the window tree. 45 | void SetChildContent(HWND content); 46 | 47 | // Returns the backing Window handle to enable clients to set icon and other 48 | // window properties. Returns nullptr if the window has been destroyed. 49 | HWND GetHandle(); 50 | 51 | // If true, closing this window will quit the application. 52 | void SetQuitOnClose(bool quit_on_close); 53 | 54 | // Return a RECT representing the bounds of the current client area. 55 | RECT GetClientArea(); 56 | 57 | protected: 58 | // Processes and route salient window messages for mouse handling, 59 | // size change and DPI. Delegates handling of these to member overloads that 60 | // inheriting classes can handle. 61 | virtual LRESULT MessageHandler(HWND window, 62 | UINT const message, 63 | WPARAM const wparam, 64 | LPARAM const lparam) noexcept; 65 | 66 | // Called when CreateAndShow is called, allowing subclass window-related 67 | // setup. Subclasses should return false if setup fails. 68 | virtual bool OnCreate(); 69 | 70 | // Called when Destroy is called. 71 | virtual void OnDestroy(); 72 | 73 | private: 74 | friend class WindowClassRegistrar; 75 | 76 | // OS callback called by message pump. Handles the WM_NCCREATE message which 77 | // is passed when the non-client area is being created and enables automatic 78 | // non-client DPI scaling so that the non-client area automatically 79 | // responsponds to changes in DPI. All other messages are handled by 80 | // MessageHandler. 81 | static LRESULT CALLBACK WndProc(HWND const window, 82 | UINT const message, 83 | WPARAM const wparam, 84 | LPARAM const lparam) noexcept; 85 | 86 | // Retrieves a class instance pointer for |window| 87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 88 | 89 | bool quit_on_close_ = false; 90 | 91 | // window handle for top level window. 92 | HWND window_handle_ = nullptr; 93 | 94 | // window handle for hosted content. 95 | HWND child_content_ = nullptr; 96 | }; 97 | 98 | #endif // RUNNER_WIN32_WINDOW_H_ 99 | --------------------------------------------------------------------------------