├── .gitattributes ├── .gitignore ├── ADB Explorer.sln ├── ADB Explorer ├── ADB Explorer.csproj ├── App.xaml ├── App.xaml.cs ├── AssemblyInfo.cs ├── Controls │ ├── Icons │ │ ├── InstallIcon.xaml │ │ ├── InstallIcon.xaml.cs │ │ ├── PathIcon.xaml │ │ ├── PathIcon.xaml.cs │ │ ├── PullIcon.xaml │ │ ├── PullIcon.xaml.cs │ │ ├── PushIcon.xaml │ │ ├── PushIcon.xaml.cs │ │ ├── RecycleIcon.xaml │ │ ├── RecycleIcon.xaml.cs │ │ ├── UninstallIcon.xaml │ │ └── UninstallIcon.xaml.cs │ ├── MaskedTextBox.xaml │ ├── MaskedTextBox.xaml.cs │ ├── NavigationBox.xaml │ ├── NavigationBox.xaml.cs │ ├── PasteAndPullTooltip.xaml │ ├── PasteAndPullTooltip.xaml.cs │ ├── RenameTooltip.xaml │ ├── RenameTooltip.xaml.cs │ ├── ResetSettingsIcon.xaml │ ├── ResetSettingsIcon.xaml.cs │ ├── SearchBox.xaml │ ├── SearchBox.xaml.cs │ ├── SplashScreen.xaml │ ├── SplashScreen.xaml.cs │ ├── TransferIndicator.xaml │ └── TransferIndicator.xaml.cs ├── Converters │ ├── CellConverter.cs │ ├── CompletedStatsConverter.cs │ ├── ControlSize.cs │ ├── DoubleEquals.cs │ ├── EnumToBooleanConverter.cs │ ├── FileOpProgressConverter.cs │ ├── FileOpTreeStatusConverter.cs │ ├── MarginConverter.cs │ ├── MenuItemConverter.cs │ ├── SizeConverter.cs │ ├── StringFormatConverter.cs │ ├── TreeViewIndentConverter.cs │ └── TrimmedTooltipConverter.cs ├── DragWindow.xaml ├── DragWindow.xaml.cs ├── Helpers │ ├── AdbHelper.cs │ ├── AppInfra │ │ ├── AsyncHelper.cs │ │ ├── ByteHelper.cs │ │ ├── CommandHandler.cs │ │ ├── DictionaryHelper.cs │ │ ├── ListHelper.cs │ │ ├── ObservableList.cs │ │ ├── ObservableProperty.cs │ │ ├── RandomString.cs │ │ └── SettingsHelper.cs │ ├── Attachable │ │ ├── DialogHelper.cs │ │ ├── ExpanderHelper.cs │ │ ├── MenuHelper.cs │ │ ├── SelectionHelper.cs │ │ ├── StyleHelper.cs │ │ ├── TextHelper.cs │ │ └── VisibilityHelper.cs │ ├── DeviceHelper.cs │ ├── DriveHelper.cs │ ├── ExplorerHelper.cs │ ├── File │ │ ├── FileHelper.cs │ │ ├── FileToIcon.cs │ │ ├── FolderHelper.cs │ │ └── TrashHelper.cs │ ├── FileOp │ │ ├── FileOpTest.cs │ │ ├── FinishedTestOperation.cs │ │ └── InProgressTestOperation.cs │ ├── QrGenerator.cs │ └── TemplateSelectors │ │ ├── DeviceTemplateSelector.cs │ │ ├── DriveTemplateSelector.cs │ │ ├── FileOpFileNameTemplateSelector.cs │ │ ├── FileOpProgressTemplateSelector.cs │ │ ├── FileOpTreeTemplateSelector.cs │ │ ├── FileOperationTemplateSelector.cs │ │ ├── MenuTemplateSelector.cs │ │ ├── SettingsGroupTemplateSelector.cs │ │ └── SettingsTemplateSelector.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Models │ ├── Battery.cs │ ├── Device │ │ ├── Device.cs │ │ ├── HistoryDevice.cs │ │ ├── LogicalDevice.cs │ │ ├── NewDevice.cs │ │ ├── ServiceDevice.cs │ │ └── WsaPkgDevice.cs │ ├── DirectoryLister.cs │ ├── Drive │ │ ├── Drive.cs │ │ ├── LogicalDrive.cs │ │ └── VirtualDrive.cs │ ├── File │ │ ├── FileClass.cs │ │ ├── FilePath.cs │ │ ├── FileStat.cs │ │ ├── IBaseFile.cs │ │ ├── Package.cs │ │ ├── SyncFile.cs │ │ └── TrashIndexer.cs │ ├── FileOpColumnConfig.cs │ ├── FileOpFilter.cs │ ├── Log.cs │ └── Static │ │ ├── AdbRegEx.cs │ │ ├── Const.cs │ │ ├── Data.cs │ │ └── NavHistory.cs ├── PortableLauncher.bat ├── Properties │ ├── AppGlobal.Designer.cs │ └── AppGlobal.resx ├── Resources │ ├── APK_format_icon_2014-2019_.ico │ ├── AdbProgressRedirection_ARM │ ├── AdbProgressRedirection_x86 │ ├── Fonts │ │ └── Nunito-VariableFont_wght.ttf │ └── Links.cs ├── Services │ ├── ADB │ │ ├── ADBDevice.cs │ │ ├── ADBService.cs │ │ ├── FileOpProgressInfo.cs │ │ ├── MDNS.cs │ │ ├── ShellCommands.cs │ │ └── WiFiPairingService.cs │ ├── AppInfra │ │ ├── AppRuntimeSettings.cs │ │ ├── AppSettings.cs │ │ ├── CopyPasteService.cs │ │ ├── DebugLog.cs │ │ ├── DialogService.cs │ │ ├── FileAction │ │ │ ├── ActionMenu.cs │ │ │ ├── FileAction.cs │ │ │ ├── FileActionLogic.cs │ │ │ ├── FileActionsEnable.cs │ │ │ ├── ToggleMenu.cs │ │ │ └── ToolBar.cs │ │ ├── IpcService.cs │ │ ├── LowLevel │ │ │ ├── DataFormats.cs │ │ │ ├── DiskUsage.cs │ │ │ ├── ExplorerWindow.cs │ │ │ ├── FileContentsStream.cs │ │ │ ├── FileDescriptor.cs │ │ │ ├── MonitorInfo.cs │ │ │ ├── ProcessHandling.cs │ │ │ ├── ThemeService.cs │ │ │ ├── VirtualFileDataObject.cs │ │ │ └── WindowStyle.cs │ │ ├── NativeMethods │ │ │ ├── DragDropNative.cs │ │ │ ├── ExplorerWatcher.cs │ │ │ ├── InterceptClipboard.cs │ │ │ ├── InterceptMouse.cs │ │ │ ├── NativeMethods.cs │ │ │ ├── SysImageList.cs │ │ │ └── UnusedNativeCode.cs │ │ ├── Network.cs │ │ ├── Security.cs │ │ ├── SettingsModel.cs │ │ └── Storage.cs │ └── FileOperation │ │ ├── FileArchiveOperation.cs │ │ ├── FileChangeModifiedOperation.cs │ │ ├── FileDeleteOperation.cs │ │ ├── FileMoveOperation.cs │ │ ├── FileOperation.cs │ │ ├── FileOperationQueue.cs │ │ ├── FileRenameOperation.cs │ │ ├── FileSyncOperation.cs │ │ ├── PackageInstallOperation.cs │ │ └── ShellFileOperation.cs ├── Strings │ ├── Resources.Designer.cs │ ├── Resources.ar.resx │ ├── Resources.bn.resx │ ├── Resources.de.resx │ ├── Resources.es-419.resx │ ├── Resources.fa.resx │ ├── Resources.fr.resx │ ├── Resources.he.resx │ ├── Resources.id.resx │ ├── Resources.it.resx │ ├── Resources.ja.resx │ ├── Resources.ko.resx │ ├── Resources.pl.resx │ ├── Resources.pt-BR.resx │ ├── Resources.resx │ ├── Resources.ru.resx │ ├── Resources.tr.resx │ ├── Resources.zh-CN.resx │ └── Resources.zh-TW.resx ├── Styles │ ├── BasicStyles.xaml │ ├── Colors.xaml │ ├── Controls │ │ ├── Button.xaml │ │ ├── CheckBox.xaml │ │ ├── ComboBox.xaml │ │ ├── ContentDialog.xaml │ │ ├── ContextMenu.xaml │ │ ├── DataGrid.xaml │ │ ├── Expander.xaml │ │ ├── MenuItem.xaml │ │ ├── RadioButton.xaml │ │ ├── ScrollBar.xaml │ │ ├── ScrollViewer.xaml │ │ ├── SplitView.xaml │ │ ├── TextBox.xaml │ │ └── ToggleButton.xaml │ ├── CustomStyles.xaml │ ├── Device.xaml │ ├── Drive.xaml │ ├── FileAction.xaml │ ├── FileOp.xaml │ ├── FileOpColumn.xaml │ └── Settings.xaml ├── Usings.cs ├── ViewModels │ ├── Device │ │ ├── DeviceAction.cs │ │ ├── DeviceViewModel.cs │ │ ├── Devices.cs │ │ ├── HistoryDeviceViewModel.cs │ │ ├── LogicalDeviceViewModel.cs │ │ ├── NewDeviceViewModel.cs │ │ ├── ServiceDeviceViewModel.cs │ │ └── WsaPkgDeviceViewModel.cs │ ├── Drive │ │ ├── DriveViewModel.cs │ │ ├── LogicalDriveViewModel.cs │ │ └── VirtualDriveViewModel.cs │ ├── FileOp │ │ ├── CanceledOpProgressViewModel.cs │ │ ├── CompletedShellProgressViewModel.cs │ │ ├── CompletedSyncProgressViewModel.cs │ │ ├── FailedOpProgressViewModel.cs │ │ ├── FileOpProgressViewModel.cs │ │ ├── InProgShellProgressViewModel.cs │ │ ├── InProgSyncProgressViewModel.cs │ │ └── WaitingOpProgressViewModel.cs │ └── ViewModelBase.cs ├── Views │ ├── Battery │ │ ├── CompactBatteryControl.xaml │ │ ├── CompactBatteryControl.xaml.cs │ │ ├── DetailedBatteryControl.xaml │ │ └── DetailedBatteryControl.xaml.cs │ ├── Device │ │ ├── HistoryDeviceControl.xaml │ │ ├── HistoryDeviceControl.xaml.cs │ │ ├── LogicalDeviceControl.xaml │ │ ├── LogicalDeviceControl.xaml.cs │ │ ├── NewDeviceControl.xaml │ │ ├── NewDeviceControl.xaml.cs │ │ ├── ServiceDeviceControl.xaml │ │ ├── ServiceDeviceControl.xaml.cs │ │ ├── WsaPkgDeviceControl.xaml │ │ └── WsaPkgDeviceControl.xaml.cs │ └── Drive │ │ ├── LogicalDriveControl.xaml │ │ ├── LogicalDriveControl.xaml.cs │ │ ├── VirtualDriveControl.xaml │ │ └── VirtualDriveControl.xaml.cs ├── app.manifest ├── app_icon_2023_combined.ico └── app_icon_black_2023_256px.png ├── ADB Package ├── ADB_Package.wapproj ├── Images │ ├── LargeTile.scale-100.png │ ├── LargeTile.scale-125.png │ ├── LargeTile.scale-150.png │ ├── LargeTile.scale-200.png │ ├── LargeTile.scale-400.png │ ├── SmallTile.scale-100.png │ ├── SmallTile.scale-125.png │ ├── SmallTile.scale-150.png │ ├── SmallTile.scale-200.png │ ├── SmallTile.scale-400.png │ ├── SplashScreen.scale-100.png │ ├── SplashScreen.scale-125.png │ ├── SplashScreen.scale-150.png │ ├── SplashScreen.scale-200.png │ ├── SplashScreen.scale-400.png │ ├── Square150x150Logo.scale-100.png │ ├── Square150x150Logo.scale-125.png │ ├── Square150x150Logo.scale-150.png │ ├── Square150x150Logo.scale-200.png │ ├── Square150x150Logo.scale-400.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-16.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-24.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-256.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-32.png │ ├── Square44x44Logo.altform-lightunplated_targetsize-48.png │ ├── Square44x44Logo.altform-unplated_targetsize-16.png │ ├── Square44x44Logo.altform-unplated_targetsize-256.png │ ├── Square44x44Logo.altform-unplated_targetsize-32.png │ ├── Square44x44Logo.altform-unplated_targetsize-48.png │ ├── Square44x44Logo.scale-100.png │ ├── Square44x44Logo.scale-125.png │ ├── Square44x44Logo.scale-150.png │ ├── Square44x44Logo.scale-200.png │ ├── Square44x44Logo.scale-400.png │ ├── Square44x44Logo.targetsize-16.png │ ├── Square44x44Logo.targetsize-24.png │ ├── Square44x44Logo.targetsize-24_altform-unplated.png │ ├── Square44x44Logo.targetsize-256.png │ ├── Square44x44Logo.targetsize-32.png │ ├── Square44x44Logo.targetsize-48.png │ ├── StoreLogo.scale-100.png │ ├── StoreLogo.scale-125.png │ ├── StoreLogo.scale-150.png │ ├── StoreLogo.scale-200.png │ ├── StoreLogo.scale-400.png │ ├── Wide310x150Logo.scale-100.png │ ├── Wide310x150Logo.scale-125.png │ ├── Wide310x150Logo.scale-150.png │ ├── Wide310x150Logo.scale-200.png │ └── Wide310x150Logo.scale-400.png ├── Package.appxmanifest └── Strings │ └── en-us │ └── Resources.resw ├── ADB_Test ├── ADB_Test.csproj └── Tests.cs ├── AdbProgressRedirection ├── .vscode │ ├── settings.json │ └── tasks.json ├── AdbProgressRedirection.vcxproj ├── AdbProgressRedirection.vcxproj.filters ├── main.cpp └── output │ └── .placeholder ├── FUNDING.yml ├── LICENSE ├── Privacy.md ├── README.md ├── ResXCleaner ├── Program.cs └── ResxCleaner.csproj ├── icons ├── APK_format_icon_(2014-2019).png ├── APK_format_icon_2014-2019_.ico ├── App_icon.pdn ├── Store_icon.png ├── Store_icon_2023.png ├── Store_icon_beta.png ├── Store_icon_beta_2023.png ├── Store_icon_v2.png ├── Store_icon_v3.png ├── adb_explorer_icon_v2_icons8.ico ├── adb_explorer_icon_v2_icons8.pdn ├── adb_explorer_icon_v2_icons8.png ├── adb_explorer_icon_v3_icons8.png ├── adb_explorer_icon_v3_icons8_tile.png ├── adb_explorer_icon_v4_icons8.ico ├── adb_explorer_icon_v4_icons8.png ├── adb_explorer_icon_v4_icons8_combined.ico ├── adb_explorer_icon_v4_icons8_inverted - Copy.png ├── adb_explorer_icon_v4_icons8_inverted.png ├── adb_explorer_icon_v4_icons8_medium.png ├── adb_explorer_icon_v4_icons8_medium_v2.png ├── adb_explorer_icon_v4_icons8_medium_white.png ├── adb_explorer_icon_v4_icons8_simple _white.png ├── adb_explorer_icon_v4_icons8_simple.ico ├── adb_explorer_icon_v4_icons8_simple.png ├── adb_explorer_icon_v4_icons8_simple_v2.png ├── adb_explorer_icon_v4_icons8_tile.png ├── adb_explorer_icon_v5_icons8_combined.ico ├── app_icon_black_2023.png ├── app_icon_black_2023_256px.png ├── app_icon_white_2023.png ├── icons8-android-os-240.png ├── icons8-android-os-250.png ├── icons8-folder-208.png ├── icons8-folder-250.png ├── icons8-sync-100 - Copy.png ├── icons8-sync-100.png ├── icons8_folder&sync&android.png └── icons8_folder&sync.png └── licences ├── Android_icon_license.txt ├── Apache License.txt ├── CC BY-SA 3.0.txt ├── LGPLv3.txt └── icons8.com license.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /ADB Explorer/App.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | Segoe Fluent Icons, Segoe MDL2 Assets 43 | pack://application:,,,/Resources/Fonts/#Nunito 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /ADB Explorer/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | [assembly: ThemeInfo( 2 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located 3 | //(used if a resource is not found in the page, 4 | // or application resource dictionaries) 5 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located 6 | //(used if a resource is not found in the page, 7 | // app, or any theme specific resource dictionaries) 8 | )] 9 | -------------------------------------------------------------------------------- /ADB Explorer/Controls/Icons/InstallIcon.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 22 | 23 | 24 | 30 | 31 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /ADB Explorer/Controls/Icons/PathIcon.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Controls; 2 | 3 | /// 4 | /// Interaction logic for PathIcon.xaml 5 | /// 6 | public partial class PathIcon : UserControl 7 | { 8 | public PathIcon() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ADB Explorer/Controls/Icons/PullIcon.xaml: -------------------------------------------------------------------------------- 1 |  10 | 11 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /ADB Explorer/Controls/PasteAndPullTooltip.xaml.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | 3 | namespace ADB_Explorer.Controls; 4 | 5 | /// 6 | /// Interaction logic for PasteAndPullTooltip.xaml 7 | /// 8 | public partial class PasteAndPullTooltip : UserControl 9 | { 10 | public bool TempHide { get; set; } = false; 11 | 12 | public PasteAndPullTooltip() 13 | { 14 | InitializeComponent(); 15 | } 16 | 17 | private void Button_Click(object sender, RoutedEventArgs e) 18 | { 19 | Data.Settings.HidePasteNamingInfo = PermanentHideCheckBox.IsChecked is true; 20 | Visibility = Visibility.Hidden; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ADB Explorer/Controls/RenameTooltip.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Controls; 2 | 3 | /// 4 | /// Interaction logic for RenameTooltip.xaml 5 | /// 6 | public partial class RenameTooltip : UserControl 7 | { 8 | public RenameTooltip() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ADB Explorer/Controls/ResetSettingsIcon.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ADB Explorer/Controls/ResetSettingsIcon.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Controls; 2 | 3 | /// 4 | /// Interaction logic for ResetSettingsIcon.xaml 5 | /// 6 | public partial class ResetSettingsIcon : UserControl 7 | { 8 | public ResetSettingsIcon() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ADB Explorer/Controls/TransferIndicator.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Controls; 2 | 3 | /// 4 | /// Interaction logic for TransferIndicator.xaml 5 | /// 6 | public partial class TransferIndicator : UserControl 7 | { 8 | public TransferIndicator() 9 | { 10 | InitializeComponent(); 11 | } 12 | 13 | public bool IsUpVisible 14 | { 15 | get => (bool)GetValue(IsUpVisibleProperty); 16 | set => SetValue(IsUpVisibleProperty, value); 17 | } 18 | 19 | public static readonly DependencyProperty IsUpVisibleProperty = 20 | DependencyProperty.Register("IsUpVisible", typeof(bool), 21 | typeof(TransferIndicator), new PropertyMetadata(null)); 22 | 23 | public bool IsDownVisible 24 | { 25 | get => (bool)GetValue(IsDownVisibleProperty); 26 | set => SetValue(IsDownVisibleProperty, value); 27 | } 28 | 29 | public static readonly DependencyProperty IsDownVisibleProperty = 30 | DependencyProperty.Register("IsDownVisible", typeof(bool), 31 | typeof(TransferIndicator), new PropertyMetadata(null)); 32 | 33 | public Visibility UpVisibility => IsUpVisible ? Visibility.Visible : Visibility.Collapsed; 34 | 35 | public Visibility DownVisibility => IsDownVisible ? Visibility.Visible : Visibility.Collapsed; 36 | } 37 | -------------------------------------------------------------------------------- /ADB Explorer/Converters/CellConverter.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Converters; 2 | 3 | public class CellConverter 4 | { 5 | public static DataGridCell GetDataGridCell(DataGridCellInfo cellInfo) 6 | { 7 | var cellContent = cellInfo.Column?.GetCellContent(cellInfo.Item); 8 | if (cellContent != null) 9 | return (DataGridCell)cellContent.Parent; 10 | 11 | return null; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ADB Explorer/Converters/CompletedStatsConverter.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.ViewModels; 2 | 3 | namespace ADB_Explorer.Converters; 4 | 5 | internal class CompletedStatsConverter : IValueConverter 6 | { 7 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 8 | { 9 | var result = ""; 10 | if (value is not CompletedSyncProgressViewModel info) 11 | return result; 12 | 13 | if (string.IsNullOrEmpty(info.TotalSize)) 14 | return result; 15 | 16 | result += info.TotalSize; 17 | if (string.IsNullOrEmpty(info.TotalTime)) 18 | return result; 19 | 20 | result += $" in {info.TotalTime}"; 21 | 22 | if (string.IsNullOrEmpty(info.AverageRateString)) 23 | return result; 24 | 25 | result += $" ({info.AverageRateString})"; 26 | return result; 27 | } 28 | 29 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 30 | { 31 | throw new NotImplementedException(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /ADB Explorer/Converters/ControlSize.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Converters; 2 | 3 | public static class ControlSize 4 | { 5 | /// 6 | /// Returns the length of a path button. 7 | /// 8 | /// 9 | /// 10 | public static double GetWidth(UIElement item) 11 | { 12 | item.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity)); 13 | return item.DesiredSize.Width; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ADB Explorer/Converters/DoubleEquals.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Converters; 2 | 3 | public class DoubleEquals : IValueConverter 4 | { 5 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 6 | { 7 | if (parameter is string paramString) 8 | { 9 | if (double.TryParse(paramString, out double result)) 10 | { 11 | var val = double.Parse($"{value}"); 12 | 13 | return val == result; 14 | } 15 | } 16 | 17 | return null; 18 | } 19 | 20 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 21 | { 22 | return null; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ADB Explorer/Converters/EnumToBooleanConverter.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Converters; 2 | 3 | public class EnumToBooleanConverter : IValueConverter 4 | { 5 | public Type EnumType { get; set; } 6 | 7 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 8 | { 9 | if (parameter is string enumString) 10 | { 11 | if (Enum.IsDefined(EnumType, value)) 12 | { 13 | var enumValue = Enum.Parse(EnumType, enumString); 14 | 15 | return enumValue.Equals(value); 16 | } 17 | } 18 | 19 | return false; 20 | } 21 | 22 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 23 | { 24 | if (parameter is string enumString) 25 | { 26 | return Enum.Parse(EnumType, enumString); 27 | } 28 | 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ADB Explorer/Converters/FileOpProgressConverter.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Converters; 2 | 3 | public class FileOpProgressConverter : IValueConverter 4 | { 5 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 6 | { 7 | return value switch 8 | { 9 | double val when val 10 | is double.NaN 11 | or double.PositiveInfinity 12 | or double.NegativeInfinity => 0.0, 13 | int or long or double => value, 14 | _ => 0.0 15 | }; 16 | } 17 | 18 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 19 | { 20 | return null; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ADB Explorer/Converters/FileOpTreeStatusConverter.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Helpers; 2 | using ADB_Explorer.Models; 3 | using ADB_Explorer.Services; 4 | using static ADB_Explorer.Converters.FileOpStatusConverter; 5 | 6 | namespace ADB_Explorer.Converters; 7 | 8 | internal class FileOpTreeStatusConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | if (value is ObservableList children) 13 | { 14 | var (failed, type) = CountFails(children); 15 | 16 | return StatusString(type, children.Count - failed, failed); 17 | } 18 | else if (value is ObservableList updates) 19 | { 20 | return StatusString(updates.First().GetType(), message: updates.OfType().LastOrDefault()?.Message); 21 | } 22 | 23 | throw new NotSupportedException(); 24 | } 25 | 26 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 27 | { 28 | throw new NotImplementedException(); 29 | } 30 | } 31 | 32 | public static class FileOpStatusConverter 33 | { 34 | public static string StatusString(Type type, int completed = 0, int failed = -1, string message = "", bool total = false) 35 | { 36 | if (!string.IsNullOrEmpty(message)) 37 | { 38 | return message.StartsWith(Strings.Resources.S_REDIRECTION) 39 | ? message 40 | : string.Format(Strings.Resources.S_FILEOP_ERROR, message); 41 | } 42 | 43 | var completedString = (type == typeof(HashFailInfo) || type == typeof(HashSuccessInfo)) 44 | ? Strings.Resources.S_FILEOP_VALIDATED 45 | : Strings.Resources.S_FILEOP_COMPLETED; 46 | 47 | if (failed == -1) 48 | return completedString; 49 | 50 | if (type == typeof(ShellErrorInfo)) 51 | return $"({string.Format(Strings.Resources.S_FILEOP_SUBITEM_FAILED, failed)})"; 52 | 53 | var failedString = failed > 0 ? $"{string.Format(Strings.Resources.S_FILEOP_SUBITEM_FAILED, failed)}, " : ""; 54 | 55 | return $"({(total ? $"{Strings.Resources.S_FILEOP_TOTAL} " : "")}{failedString}{completed} {completedString})"; 56 | } 57 | 58 | public static (int, Type) CountFails(ObservableList children) 59 | { 60 | int total = 0; 61 | 62 | foreach (var item in children.Where(c => c.Children.Count > 0)) 63 | { 64 | if (CountFails(item.Children).Item1 > 0) 65 | total++; 66 | } 67 | 68 | total += children.Count(c => c.Children.Count == 0 && c.ProgressUpdates.OfType().Any()); 69 | 70 | var type = typeof(SyncErrorInfo); 71 | if (children.Any(c => c.ProgressUpdates.Any(u => u is ShellErrorInfo))) 72 | type = typeof(ShellErrorInfo); 73 | else if (children.Any(c => c.ProgressUpdates.Any(u => u is HashFailInfo or HashSuccessInfo))) 74 | type = typeof(HashFailInfo); 75 | 76 | return (total, type); 77 | } 78 | } -------------------------------------------------------------------------------- /ADB Explorer/Converters/MarginConverter.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Converters; 2 | 3 | public class MarginConverter : IMultiValueConverter 4 | { 5 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 6 | { 7 | // arg 0 - ExpansionProgress 8 | // arg 1 - ExpandDirection 9 | // arg 2 - ContentHeight 10 | // arg 3 - ContentWidth 11 | 12 | if (values.Length < 4) 13 | throw new ArgumentException("This function requires 4 arguments."); 14 | else if (values[0] is not double || values[1] is not ExpandDirection || values[2] is not double || values[3] is not double) 15 | throw new ArgumentException("Provided arguments are not of correct format."); 16 | 17 | var expansionProgress = (double)values[0]; 18 | var expandDirection = (ExpandDirection)values[1]; 19 | var contentHeight = (double)values[2]; 20 | var contentWidth = (double)values[3]; 21 | 22 | var size = expandDirection switch 23 | { 24 | ExpandDirection.Down or ExpandDirection.Up => contentHeight, 25 | ExpandDirection.Left or ExpandDirection.Right => contentWidth, 26 | _ => throw new NotSupportedException(), 27 | }; 28 | 29 | var result = -size * (1 - expansionProgress); 30 | 31 | return expandDirection switch 32 | { 33 | ExpandDirection.Down => new Thickness(0, result, 0, 0), 34 | ExpandDirection.Up => new Thickness(0, 0, 0, result), 35 | ExpandDirection.Left => new Thickness(0, 0, result, 0), 36 | ExpandDirection.Right => new Thickness(result, 0, 0, 0), 37 | _ => throw new NotSupportedException(), 38 | }; 39 | } 40 | 41 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 42 | { 43 | throw new NotImplementedException(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ADB Explorer/Converters/MenuItemConverter.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Converters; 2 | 3 | class MenuItemConverter : IValueConverter 4 | { 5 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 6 | { 7 | DependencyObject parent = value as UIElement; 8 | while (parent is not null and not Menu) 9 | parent = VisualTreeHelper.GetParent(parent); 10 | 11 | if (parent is null) 12 | return null; 13 | 14 | return parameter switch 15 | { 16 | "Padding" => Helpers.MenuHelper.GetItemPadding(parent as UIElement), 17 | "Margin" => Helpers.MenuHelper.GetItemMargin(parent as UIElement), 18 | "Style" => Helpers.MenuHelper.GetIsButtonMenu(parent as UIElement), 19 | _ => throw new NotSupportedException(), 20 | }; 21 | } 22 | 23 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 24 | { 25 | throw new NotSupportedException(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ADB Explorer/Converters/SizeConverter.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Converters 2 | { 3 | public static class UnitConverter 4 | { 5 | private static readonly Dictionary scale_table = new() { { -3, "n" }, { -2, "u" }, { -1, "m" }, { 0, "" }, { 1, "K" }, { 2, "M" }, { 3, "G" }, { 4, "T" }, { 5, "P" }, { 6, "E" } }; 6 | 7 | public static string ToSize(this UInt64 bytes, bool scaleSpace = false, int bigRound = 1, int smallRound = 0) 8 | { 9 | int scale = (bytes == 0) ? 0 : Convert.ToInt32(Math.Floor(Math.Round(Math.Log(bytes, 1024), 2))); // 0 <= scale <= 6 10 | double value = bytes / Math.Pow(1024, scale); 11 | return $"{(Math.Round(value, value < 100 ? bigRound : smallRound))}{(scaleSpace ? " " : "")}{scale_table[scale]}B"; 12 | } 13 | 14 | public static string ToSize(this double source, bool scaleSpace = false, int bigRound = 1, int smallRound = 0) 15 | { 16 | int scale = (source == 0) ? 0 : Convert.ToInt32(Math.Floor(Math.Round(Math.Log(Math.Abs(source), 1000), 2))); 17 | double value = source / Math.Pow(1000, scale); 18 | return $"{(Math.Round(value, value < 100 ? bigRound : smallRound))}{(scaleSpace ? " " : "")}{scale_table[scale]}"; 19 | } 20 | 21 | public static string ToTime(this decimal? seconds, bool scaleSpace = false, bool useMilli = true, int digits = 2) 22 | { 23 | if (seconds is null) 24 | return ""; 25 | 26 | TimeSpan span = TimeSpan.FromSeconds((double)seconds); 27 | string resolution; 28 | string value; 29 | 30 | if (span.Days == 0) 31 | { 32 | if (span.Hours == 0) 33 | { 34 | if (span.Minutes == 0) 35 | { 36 | if (useMilli && span.Seconds == 0) 37 | { 38 | value = $"{Math.Round(span.TotalMilliseconds, span.TotalMilliseconds < 100 ? digits : 1)}"; 39 | resolution = Strings.Resources.S_MILLISECONDS_SHORT; 40 | } 41 | else 42 | { 43 | value = $"{Math.Round(span.TotalSeconds, digits)}"; 44 | resolution = Strings.Resources.S_SECONDS_SHORT; 45 | } 46 | } 47 | else 48 | { 49 | value = $"{span.Minutes}:{span.Seconds:00}"; 50 | resolution = Strings.Resources.S_MINUTES_SHORT; 51 | } 52 | } 53 | else 54 | { 55 | value = $"{span.Hours}:{span.Minutes:00}:{span.Seconds:00}"; 56 | resolution = Strings.Resources.S_HOURS_SHORT; 57 | } 58 | } 59 | else 60 | { 61 | value = $"{Math.Round(span.TotalHours)}"; 62 | resolution = Strings.Resources.S_HOURS_SHORT; 63 | } 64 | 65 | return string.Format(resolution, $"{value}{(scaleSpace ? " " : "")}"); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ADB Explorer/Converters/StringFormatConverter.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Converters; 2 | 3 | public class StringFormatConverter : IMultiValueConverter 4 | { 5 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 6 | { 7 | if (parameter is not string format) 8 | return string.Empty; 9 | 10 | try 11 | { 12 | return string.Format(format, values); 13 | } 14 | catch 15 | { 16 | return format; 17 | } 18 | } 19 | 20 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) 21 | { 22 | throw new NotSupportedException(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ADB Explorer/Converters/TreeViewIndentConverter.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Converters; 2 | 3 | internal class TreeViewIndentConverter : IValueConverter 4 | { 5 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 6 | { 7 | if (value is Thickness margin) 8 | return new Thickness(-1 * margin.Left, margin.Top, margin.Right, margin.Bottom); 9 | 10 | return null; 11 | } 12 | 13 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 14 | { 15 | throw new NotImplementedException(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ADB Explorer/Converters/TrimmedTooltipConverter.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Converters; 2 | 3 | public class TrimmedTooltipConverter : IValueConverter 4 | { 5 | 6 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 7 | { 8 | if (value is null) 9 | return Visibility.Collapsed; 10 | 11 | // FrameworkElement to include both TextBlock and TextBox 12 | var textBlock = value as FrameworkElement; 13 | 14 | textBlock.Measure(new(double.PositiveInfinity, double.PositiveInfinity)); 15 | var margin = textBlock.Margin.Left + textBlock.Margin.Right; 16 | 17 | return textBlock.ActualWidth + margin < textBlock.DesiredSize.Width 18 | ? Visibility.Visible 19 | : Visibility.Collapsed; 20 | } 21 | 22 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 23 | { 24 | throw new NotImplementedException(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/AppInfra/AsyncHelper.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Helpers; 2 | 3 | public static class AsyncHelper 4 | { 5 | public static async Task WaitUntil(Func condition, TimeSpan timeout, TimeSpan assertDelay, CancellationToken cancellationToken) 6 | { 7 | var waitTask = Task.Run(async () => 8 | { 9 | while (!condition()) await Task.Delay(assertDelay, cancellationToken); 10 | }, cancellationToken); 11 | 12 | await Task.WhenAny(waitTask, Task.Delay(timeout, cancellationToken)); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/AppInfra/ByteHelper.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Helpers; 2 | 3 | public class ByteHelper 4 | { 5 | public static int PatternAt(byte[] source, byte[] pattern, int startIndex = 0, bool evenAlign = false) 6 | { 7 | for (int i = startIndex; i < source.Length; i++) 8 | { 9 | if (source.Skip(i).Take(pattern.Length).SequenceEqual(pattern) 10 | && (int.IsEvenInteger(i) || !evenAlign)) 11 | { 12 | return i; 13 | } 14 | } 15 | 16 | return -1; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/AppInfra/CommandHandler.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.ViewModels; 2 | 3 | namespace ADB_Explorer.Helpers; 4 | 5 | public class CommandHandler : ICommand 6 | { 7 | private readonly Action _action; 8 | private readonly Func _canExecute; 9 | 10 | /// 11 | /// Raises an event when the command is executed. 12 | /// 13 | public ObservableProperty OnExecute { get; set; } = new(); 14 | 15 | public void Execute(object parameter) 16 | { 17 | _action(); 18 | 19 | OnExecute.Value ^= true; 20 | } 21 | 22 | public bool CanExecute(object parameter) 23 | { 24 | return _canExecute.Invoke(); 25 | } 26 | 27 | public CommandHandler(Action action, Func canExecute) 28 | { 29 | _action = action; 30 | _canExecute = canExecute; 31 | } 32 | 33 | public event EventHandler CanExecuteChanged 34 | { 35 | add => CommandManager.RequerySuggested += value; 36 | remove => CommandManager.RequerySuggested -= value; 37 | } 38 | 39 | } 40 | 41 | public class BaseAction : ViewModelBase 42 | { 43 | private readonly Func canExecute; 44 | public bool IsEnabled => canExecute(); 45 | 46 | private readonly Action action; 47 | 48 | private ICommand command; 49 | public ICommand Command => command ??= new CommandHandler(action, canExecute); 50 | 51 | public BaseAction(Func canExecute, Action action) 52 | { 53 | this.canExecute = canExecute ??= () => true; 54 | this.action = action; 55 | } 56 | 57 | public BaseAction() 58 | { 59 | canExecute = () => true; 60 | action = () => { }; 61 | } 62 | 63 | public void Execute() => Command.Execute(null); 64 | } 65 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/AppInfra/DictionaryHelper.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Helpers; 2 | 3 | public static class DictionaryHelper 4 | { 5 | public static Dictionary TryToDictionary(this IEnumerable source, Func keySelector, Func elementSelector) where TKey : notnull => 6 | TryToDictionary(source, keySelector, elementSelector, null); 7 | 8 | public static Dictionary TryToDictionary(this IEnumerable source, Func keySelector, Func elementSelector, IEqualityComparer? comparer) where TKey : notnull 9 | { 10 | ArgumentNullException.ThrowIfNull(source); 11 | 12 | ArgumentNullException.ThrowIfNull(keySelector); 13 | 14 | ArgumentNullException.ThrowIfNull(elementSelector); 15 | 16 | int capacity = 0; 17 | if (source is ICollection collection) 18 | { 19 | capacity = collection.Count; 20 | if (capacity == 0) 21 | { 22 | return new Dictionary(comparer); 23 | } 24 | 25 | if (collection is TSource[] array) 26 | { 27 | return TryToDictionary(array, keySelector, elementSelector, comparer); 28 | } 29 | 30 | if (collection is List list) 31 | { 32 | return TryToDictionary(list, keySelector, elementSelector, comparer); 33 | } 34 | } 35 | 36 | Dictionary d = new Dictionary(capacity, comparer); 37 | foreach (TSource element in source) 38 | { 39 | d.TryAdd(keySelector(element), elementSelector(element)); 40 | } 41 | 42 | return d; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/AppInfra/ListHelper.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Helpers; 2 | 3 | internal static class ListHelper 4 | { 5 | public static ListSortDirection Invert(ListSortDirection? value) 6 | { 7 | return value is ListSortDirection and ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending; 8 | } 9 | 10 | /// 11 | /// Appends an IEnumerable to the end of another IEnumerable.
12 | /// *** ENUMERATES BOTH OF THE ENUMERABLES *** 13 | ///
14 | public static IEnumerable AppendRange(this IEnumerable self, IEnumerable other) 15 | { 16 | foreach (var item in self) 17 | { 18 | yield return item; 19 | } 20 | 21 | foreach (var item in other) 22 | { 23 | yield return item; 24 | } 25 | } 26 | 27 | /// 28 | /// Determines whether all elements of a sequence satisfy a condition 29 | /// 30 | /// if the source sequence contains any elements and every element passes the test in the specified predicate; otherwise, 31 | public static bool AnyAll(this IEnumerable source, Func predicate) 32 | { 33 | return source.Any() && source.All(predicate); 34 | } 35 | 36 | /// 37 | /// Performs the specified on each element of the collection 38 | /// 39 | public static void ForEach(this IEnumerable self, Action action) 40 | { 41 | foreach (var item in self) 42 | { 43 | action(item); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/AppInfra/ObservableProperty.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Helpers; 2 | 3 | public class PropertyChangedEventArgs : EventArgs 4 | { 5 | public T OldValue { get; set; } 6 | public T NewValue { get; set; } 7 | } 8 | 9 | public class ObservableProperty 10 | { 11 | public event EventHandler> PropertyChanged; 12 | 13 | private T _value; 14 | public T Value 15 | { 16 | get => _value; 17 | set 18 | { 19 | if (Equals(_value, value)) 20 | return; 21 | 22 | PropertyChangedEventArgs args = new() 23 | { 24 | OldValue = _value, 25 | NewValue = value 26 | }; 27 | 28 | _value = value; 29 | PropertyChanged?.Invoke(this, args); 30 | } 31 | } 32 | 33 | public static implicit operator T(ObservableProperty p) => p.Value; 34 | } 35 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/AppInfra/RandomString.cs: -------------------------------------------------------------------------------- 1 | using static ADB_Explorer.Models.AdbExplorerConst; 2 | 3 | namespace ADB_Explorer.Helpers; 4 | 5 | public class RandomString 6 | { 7 | public static string GetUniqueKey(int size, char[] chars = null) 8 | { 9 | if (chars is null) 10 | { 11 | chars = WIFI_PAIRING_ALPHABET; 12 | } 13 | 14 | byte[] data = new byte[4 * size]; 15 | RandomNumberGenerator.Create().GetBytes(data); 16 | 17 | StringBuilder result = new(); 18 | for (int i = 0; i < size; i++) 19 | { 20 | var rnd = BitConverter.ToUInt32(data, i * 4); 21 | var idx = rnd % chars.Length; 22 | 23 | result.Append(chars[idx]); 24 | } 25 | 26 | return result.ToString(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/Attachable/DialogHelper.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Helpers; 2 | 3 | public static class DialogHelper 4 | { 5 | public static string GetDialogIcon(ContentDialog control) => 6 | (string)control.GetValue(DialogIconProperty); 7 | 8 | public static void SetDialogIcon(ContentDialog control, string value) => 9 | control.SetValue(DialogIconProperty, value); 10 | 11 | public static readonly DependencyProperty DialogIconProperty = 12 | DependencyProperty.RegisterAttached( 13 | "DialogIcon", 14 | typeof(string), 15 | typeof(DialogHelper), 16 | null); 17 | } 18 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/Attachable/MenuHelper.cs: -------------------------------------------------------------------------------- 1 | using Windows.UI.Popups; 2 | 3 | namespace ADB_Explorer.Helpers; 4 | 5 | public static class MenuHelper 6 | { 7 | public static bool GetIsMouseSelectionVisible(UIElement control) => 8 | (bool)control.GetValue(IsMouseSelectionVisibleProperty); 9 | 10 | public static void SetIsMouseSelectionVisible(UIElement control, bool value) => 11 | control.SetValue(IsMouseSelectionVisibleProperty, value); 12 | 13 | public static readonly DependencyProperty IsMouseSelectionVisibleProperty = 14 | DependencyProperty.RegisterAttached( 15 | "IsMouseSelectionVisible", 16 | typeof(bool), 17 | typeof(MenuHelper), 18 | null); 19 | 20 | public static Brush GetCheckBackground(UIElement control) => 21 | (Brush)control.GetValue(CheckBackgroundProperty); 22 | 23 | public static void SetCheckBackground(UIElement control, Brush value) => 24 | control.SetValue(CheckBackgroundProperty, value); 25 | 26 | public static readonly DependencyProperty CheckBackgroundProperty = 27 | DependencyProperty.RegisterAttached( 28 | "CheckBackground", 29 | typeof(Brush), 30 | typeof(MenuHelper), 31 | null); 32 | 33 | public static Thickness GetItemPadding(UIElement control) => 34 | (Thickness)control.GetValue(ItemPaddingProperty); 35 | 36 | public static void SetItemPadding(UIElement control, Thickness value) => 37 | control.SetValue(ItemPaddingProperty, value); 38 | 39 | public static readonly DependencyProperty ItemPaddingProperty = 40 | DependencyProperty.RegisterAttached( 41 | "ItemPadding", 42 | typeof(Thickness), 43 | typeof(MenuHelper), 44 | null); 45 | 46 | public static Thickness GetItemMargin(UIElement control) => 47 | (Thickness)control.GetValue(ItemMarginProperty); 48 | 49 | public static void SetItemMargin(UIElement control, Thickness value) => 50 | control.SetValue(ItemMarginProperty, value); 51 | 52 | public static readonly DependencyProperty ItemMarginProperty = 53 | DependencyProperty.RegisterAttached( 54 | "ItemMargin", 55 | typeof(Thickness), 56 | typeof(MenuHelper), 57 | null); 58 | 59 | public static bool? GetIsButtonMenu(UIElement control) => 60 | (bool?)control.GetValue(IsButtonMenuProperty); 61 | 62 | public static void SetIsButtonMenu(UIElement control, bool? value) => 63 | control.SetValue(IsButtonMenuProperty, value); 64 | 65 | public static readonly DependencyProperty IsButtonMenuProperty = 66 | DependencyProperty.RegisterAttached( 67 | "IsButtonMenu", 68 | typeof(bool?), 69 | typeof(MenuHelper), 70 | null); 71 | 72 | public static PlacementMode GetDropDownPlacement(UIElement control) => 73 | (PlacementMode)control.GetValue(DropDownPlacementProperty); 74 | 75 | public static void SetDropDownPlacement(UIElement control, PlacementMode value) => 76 | control.SetValue(DropDownPlacementProperty, value); 77 | 78 | public static readonly DependencyProperty DropDownPlacementProperty = 79 | DependencyProperty.RegisterAttached( 80 | "DropDownPlacement", 81 | typeof(PlacementMode), 82 | typeof(MenuHelper), 83 | null); 84 | } 85 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/Attachable/VisibilityHelper.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Helpers; 2 | 3 | public static class VisibilityHelper 4 | { 5 | public static Visibility Visible(bool? value) => value is bool and true ? Visibility.Visible : Visibility.Collapsed; 6 | 7 | public static void Visible(this FrameworkElement control, bool value) => control.Visibility = Visible(value); 8 | 9 | public static bool Visible(this FrameworkElement control) => control.Visibility == Visibility.Visible; 10 | 11 | public static void ToggleVisibility(this FrameworkElement control) => control.Visible(!control.Visible()); 12 | } 13 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/DriveHelper.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | using ADB_Explorer.ViewModels; 3 | 4 | namespace ADB_Explorer.Helpers; 5 | 6 | internal class DriveHelper 7 | { 8 | public static void ClearSelectedDrives() 9 | { 10 | Data.RuntimeSettings.CollapseDrives = true; 11 | Data.RuntimeSettings.CollapseDrives = false; 12 | } 13 | 14 | public static void ClearDrives() 15 | { 16 | Data.DevicesObject.Current?.Drives.Clear(); 17 | Data.FileActions.IsDriveViewVisible = false; 18 | } 19 | 20 | public static DriveViewModel GetCurrentDrive(string path) 21 | { 22 | if (string.IsNullOrEmpty(path)) return null; 23 | 24 | // First search for a non-root drive that matches the path 25 | var nonRoot = Data.DevicesObject.Current?.Drives.FirstOrDefault(d => d.Type is not AbstractDrive.DriveType.Root && path.StartsWith(d.Path)); 26 | if (nonRoot is null) 27 | return Data.DevicesObject.Current?.Drives.FirstOrDefault(d => d.Type is AbstractDrive.DriveType.Root); 28 | 29 | return nonRoot; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/File/FolderHelper.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | using ADB_Explorer.Services; 3 | using ADB_Explorer.ViewModels; 4 | 5 | namespace ADB_Explorer.Helpers; 6 | 7 | public class FolderHelper 8 | { 9 | public static void CombineDisplayNames() 10 | { 11 | var driveView = NavHistory.StringFromLocation(NavHistory.SpecialLocation.DriveView); 12 | if (Data.CurrentDisplayNames.ContainsKey(driveView)) 13 | Data.CurrentDisplayNames[driveView] = Data.DevicesObject.Current.Name; 14 | else 15 | Data.CurrentDisplayNames.Add(driveView, Data.DevicesObject.Current.Name); 16 | 17 | foreach (var drive in Data.DevicesObject.Current.Drives.OfType().Where(d => d.Type 18 | is not AbstractDrive.DriveType.Root 19 | and not AbstractDrive.DriveType.Internal)) 20 | { 21 | Data.CurrentDisplayNames.TryAdd(drive.Path, drive.Type is AbstractDrive.DriveType.External 22 | ? drive.ID : drive.DisplayName); 23 | } 24 | 25 | foreach (var item in AdbExplorerConst.DRIVE_TYPES.Where(d => d.Value is AbstractDrive.DriveType.Root or AbstractDrive.DriveType.Internal)) 26 | { 27 | Data.CurrentDisplayNames.TryAdd(item.Key, AbstractDrive.GetDriveDisplayName(item.Value)); 28 | } 29 | 30 | foreach (var item in AdbExplorerConst.DRIVE_TYPES) 31 | { 32 | var names = Enum.GetValues().Where(n => n == item.Value && item.Value 33 | is not AbstractDrive.DriveType.Root 34 | and not AbstractDrive.DriveType.Internal) 35 | .Select(AbstractDrive.GetDriveDisplayName); 36 | 37 | if (names.Any()) 38 | Data.CurrentDisplayNames.TryAdd(item.Key, names.First()); 39 | } 40 | 41 | Data.RuntimeSettings.RefreshBreadcrumbs = true; 42 | } 43 | 44 | public static string FolderExists(string path) 45 | { 46 | if (path == NavHistory.StringFromLocation(NavHistory.SpecialLocation.PackageDrive)) 47 | return path; 48 | 49 | if (path == NavHistory.StringFromLocation(NavHistory.SpecialLocation.RecycleBin)) 50 | return AdbExplorerConst.RECYCLE_PATH; 51 | 52 | try 53 | { 54 | return Data.CurrentADBDevice.TranslateDevicePath(path); 55 | } 56 | catch (Exception e) 57 | { 58 | if (path != AdbExplorerConst.RECYCLE_PATH) 59 | DialogService.ShowMessage(e.Message, Strings.Resources.S_NAV_ERR_TITLE, DialogService.DialogIcon.Critical, copyToClipboard: true); 60 | 61 | return null; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/File/TrashHelper.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | using ADB_Explorer.Services; 3 | using ADB_Explorer.ViewModels; 4 | 5 | namespace ADB_Explorer.Helpers; 6 | 7 | internal static class TrashHelper 8 | { 9 | public static void EnableRecycleButtons(IEnumerable fileList = null) 10 | { 11 | if (fileList is null) 12 | fileList = Data.DirList.FileList; 13 | 14 | Data.FileActions.RestoreEnabled = fileList.Any(file => file.TrashIndex is not null && !string.IsNullOrEmpty(file.TrashIndex.OriginalPath)); 15 | Data.FileActions.DeleteEnabled = fileList.Any(item => item.Extension != AdbExplorerConst.RECYCLE_INDEX_SUFFIX); 16 | } 17 | 18 | public static void UpdateRecycledItemsCount() 19 | { 20 | var countTask = Task.Run(() => ADBService.CountRecycle(Data.DevicesObject.Current.ID)); 21 | countTask.ContinueWith((t) => 22 | { 23 | if (t.IsCanceled || Data.DevicesObject.Current is null) 24 | return; 25 | 26 | var count = t.Result; 27 | if (count < 1) 28 | count = FolderHelper.FolderExists(AdbExplorerConst.RECYCLE_PATH) is null ? -1 : 0; 29 | 30 | var trash = Data.DevicesObject.Current?.Drives.Find(d => d.Type is AbstractDrive.DriveType.Trash); 31 | App.Current.Dispatcher.Invoke(() => ((VirtualDriveViewModel)trash)?.SetItemsCount(count)); 32 | }); 33 | } 34 | 35 | public static Task ParseIndexersAsync() => Task.Run(() => 36 | { 37 | Data.RecycleIndex.Clear(); 38 | 39 | var indexers = ADBService.FindFilesInPath(Data.CurrentADBDevice.ID, 40 | AdbExplorerConst.RECYCLE_PATH, 41 | includeNames: ["*" + AdbExplorerConst.RECYCLE_INDEX_SUFFIX]); 42 | 43 | var lines = ShellFileOperation.ReadAllText(Data.CurrentADBDevice, indexers).Split(ADBService.LINE_SEPARATORS, 44 | StringSplitOptions.RemoveEmptyEntries); 45 | 46 | lines.ToList().ForEach(line => Data.RecycleIndex.Add(new(line))); 47 | }); 48 | 49 | public static void ParseIndexers() 50 | { 51 | Data.RecycleIndex.Clear(); 52 | 53 | var indexers = ADBService.FindFilesInPath(Data.CurrentADBDevice.ID, 54 | AdbExplorerConst.RECYCLE_PATH, 55 | includeNames: ["*" + AdbExplorerConst.RECYCLE_INDEX_SUFFIX]); 56 | 57 | var lines = ShellFileOperation.ReadAllText(Data.CurrentADBDevice, indexers).Split(ADBService.LINE_SEPARATORS, 58 | StringSplitOptions.RemoveEmptyEntries); 59 | 60 | lines.ToList().ForEach(line => Data.RecycleIndex.Add(new(line))); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/FileOp/FinishedTestOperation.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | using ADB_Explorer.Services; 3 | using ADB_Explorer.ViewModels; 4 | 5 | namespace ADB_Explorer.Helpers; 6 | 7 | public class CompletedTestOperation : FileOperation 8 | { 9 | private readonly CompletedSyncProgressViewModel info; 10 | 11 | public override SyncFile AndroidPath => TargetPath; 12 | 13 | public CompletedTestOperation(Dispatcher dispatcher, ADBService.AdbDevice adbDevice, SyncFile filePath, AdbSyncStatsInfo adbInfo) : 14 | base(filePath, adbDevice, dispatcher) 15 | { 16 | info = new(adbInfo); 17 | TargetPath = filePath; 18 | } 19 | 20 | public static CompletedTestOperation CreateFileCompleted(Dispatcher dispatcher, ADBService.AdbDevice adbDevice, string filePath) 21 | => new(dispatcher, adbDevice, new(filePath), new(filePath, 1, 0, 2.3m, 3141592653589, 123)); 22 | 23 | public static CompletedTestOperation CreateFolderCompleted(Dispatcher dispatcher, ADBService.AdbDevice adbDevice, string folderPath) 24 | => new( 25 | dispatcher, 26 | adbDevice, 27 | new(folderPath, AbstractFile.FileType.Folder), 28 | new(folderPath, 10, 0, 2.3m, 3141592653589, 123)); 29 | 30 | public static CompletedTestOperation CreateFileSkipped(Dispatcher dispatcher, ADBService.AdbDevice adbDevice, string filePath) 31 | => new(dispatcher, adbDevice, new(filePath), new(filePath, 0, 1, 0m, 0, 0)); 32 | 33 | public static CompletedTestOperation CreateFolderPartiallySkipped(Dispatcher dispatcher, ADBService.AdbDevice adbDevice, string folderPath) 34 | => new(dispatcher, adbDevice, new(folderPath, AbstractFile.FileType.Folder), new(folderPath, 7, 3, 2.3m, 3141592653589, 123)); 35 | 36 | public override void Start() 37 | { 38 | Status = OperationStatus.Completed; 39 | StatusInfo = info; 40 | } 41 | 42 | public override void Cancel() 43 | { 44 | Status = OperationStatus.Canceled; 45 | StatusInfo = new CanceledOpProgressViewModel(); 46 | } 47 | 48 | public override void ClearChildren() 49 | => TargetPath.Children.Clear(); 50 | 51 | public override void AddUpdates(IEnumerable newUpdates) 52 | => TargetPath.AddUpdates(newUpdates); 53 | 54 | public override void AddUpdates(params FileOpProgressInfo[] newUpdates) 55 | => TargetPath.AddUpdates(newUpdates); 56 | } 57 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/FileOp/InProgressTestOperation.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | using ADB_Explorer.Services; 3 | using ADB_Explorer.ViewModels; 4 | 5 | namespace ADB_Explorer.Helpers; 6 | 7 | public class InProgressTestOperation : FileOperation 8 | { 9 | private readonly InProgSyncProgressViewModel info; 10 | 11 | public override SyncFile FilePath { get; } 12 | 13 | public override SyncFile AndroidPath => FilePath; 14 | 15 | private InProgressTestOperation(Dispatcher dispatcher, ADBService.AdbDevice adbDevice, string filePath, AdbSyncProgressInfo adbInfo) : 16 | base(new(filePath), adbDevice, dispatcher) 17 | { 18 | info = new(adbInfo); 19 | FilePath = new SyncFile(filePath, adbInfo.CurrentFilePercentage.HasValue ? AbstractFile.FileType.Folder : AbstractFile.FileType.File); 20 | } 21 | 22 | public static InProgressTestOperation CreateProgressStart(Dispatcher dispatcher, ADBService.AdbDevice adbDevice, string filePath) 23 | { 24 | return new InProgressTestOperation(dispatcher, adbDevice, filePath, new AdbSyncProgressInfo(null, null, null, null)); 25 | } 26 | 27 | public static InProgressTestOperation CreateFileInProgress(Dispatcher dispatcher, ADBService.AdbDevice adbDevice, string filePath) 28 | { 29 | return new InProgressTestOperation(dispatcher, adbDevice, filePath, new AdbSyncProgressInfo(null, 40, null, null)); 30 | } 31 | 32 | public static InProgressTestOperation CreateFolderInProgress(Dispatcher dispatcher, ADBService.AdbDevice adbDevice, string filePath) 33 | { 34 | return new InProgressTestOperation(dispatcher, adbDevice, filePath, new AdbSyncProgressInfo("Subfile.txt", 40, 60, null)); 35 | } 36 | 37 | public override void Start() 38 | { 39 | Status = OperationStatus.InProgress; 40 | StatusInfo = info; 41 | } 42 | 43 | public override void Cancel() 44 | { 45 | Status = OperationStatus.Canceled; 46 | StatusInfo = new CanceledOpProgressViewModel(); 47 | } 48 | 49 | public void Fail(string errorMsg) 50 | { 51 | Status = OperationStatus.Failed; 52 | StatusInfo = new FailedOpProgressViewModel(errorMsg); 53 | } 54 | 55 | public void UpdateStatus(FileOpProgressInfo update) 56 | { 57 | if (update is SyncErrorInfo) 58 | return; 59 | 60 | StatusInfo = new InProgSyncProgressViewModel((AdbSyncProgressInfo)update); 61 | } 62 | 63 | public override void ClearChildren() 64 | { 65 | AndroidPath.Children.Clear(); 66 | AndroidPath.ProgressUpdates.Clear(); 67 | } 68 | 69 | public override void AddUpdates(IEnumerable newUpdates) 70 | => AndroidPath.AddUpdates(newUpdates); 71 | 72 | public override void AddUpdates(params FileOpProgressInfo[] newUpdates) 73 | => AndroidPath.AddUpdates(newUpdates); 74 | } 75 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/QrGenerator.cs: -------------------------------------------------------------------------------- 1 | using QRCoder; 2 | using QRCoder.Xaml; 3 | 4 | namespace ADB_Explorer.Helpers; 5 | 6 | public static class QrGenerator 7 | { 8 | public static DrawingImage GenerateQR(string val, SolidColorBrush background, SolidColorBrush foreground) 9 | { 10 | QRCodeGenerator qrGenerator = new QRCodeGenerator(); 11 | QRCodeData qrCodeData = qrGenerator.CreateQrCode(val, QRCodeGenerator.ECCLevel.Q); 12 | var qrCode = new XamlQRCode(qrCodeData); 13 | var image = qrCode.GetGraphic(new System.Windows.Size(256, 256), foreground, background, false); 14 | 15 | return image; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/TemplateSelectors/DeviceTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.ViewModels; 2 | 3 | namespace ADB_Explorer.Helpers; 4 | 5 | public class DeviceTemplateSelector : DataTemplateSelector 6 | { 7 | public DataTemplate LogicalDeviceTemplate { get; set; } 8 | public DataTemplate ServiceDeviceTemplate { get; set; } 9 | public DataTemplate NewDeviceTemplate { get; set; } 10 | public DataTemplate HistoryDeviceTemplate { get; set; } 11 | public DataTemplate WsaPkgDeviceTemplate { get; set; } 12 | 13 | public override DataTemplate SelectTemplate(object item, DependencyObject container) => item switch 14 | { 15 | LogicalDeviceViewModel => LogicalDeviceTemplate, 16 | ServiceDeviceViewModel => ServiceDeviceTemplate, 17 | HistoryDeviceViewModel => HistoryDeviceTemplate, 18 | NewDeviceViewModel => NewDeviceTemplate, 19 | WsaPkgDeviceViewModel => WsaPkgDeviceTemplate, 20 | _ => throw new NotImplementedException(), 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/TemplateSelectors/DriveTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.ViewModels; 2 | 3 | namespace ADB_Explorer.Helpers; 4 | 5 | public class DriveTemplateSelector : DataTemplateSelector 6 | { 7 | public DataTemplate LogicalDriveTemplate { get; set; } 8 | public DataTemplate VirtualDriveTemplate { get; set; } 9 | 10 | public override DataTemplate SelectTemplate(object item, DependencyObject container) => item switch 11 | { 12 | LogicalDriveViewModel => LogicalDriveTemplate, 13 | VirtualDriveViewModel => VirtualDriveTemplate, 14 | _ => throw new NotImplementedException(), 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/TemplateSelectors/FileOpFileNameTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Services; 2 | 3 | namespace ADB_Explorer.Helpers; 4 | 5 | internal class FileOpFileNameTemplateSelector : DataTemplateSelector 6 | { 7 | public DataTemplate UninstallOpFileNameTemplate { get; set; } 8 | public DataTemplate FolderCompletedOpFileNameTemplate { get; set; } 9 | public DataTemplate FolderInProgOpFileNameTemplate { get; set; } 10 | public DataTemplate RegularOpFileNameTemplate { get; set; } 11 | 12 | public override DataTemplate SelectTemplate(object item, DependencyObject container) 13 | { 14 | if (item is not FileOperation fileop) 15 | return null; 16 | 17 | if (fileop is PackageInstallOperation pkgInstall && pkgInstall.IsUninstall) 18 | return UninstallOpFileNameTemplate; 19 | 20 | if (fileop.FilePath.IsDirectory) 21 | return fileop.Status is FileOperation.OperationStatus.InProgress ? FolderInProgOpFileNameTemplate : FolderCompletedOpFileNameTemplate; 22 | 23 | return RegularOpFileNameTemplate; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/TemplateSelectors/FileOpProgressTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.ViewModels; 2 | 3 | namespace ADB_Explorer.Helpers; 4 | 5 | internal class FileOpProgressTemplateSelector : DataTemplateSelector 6 | { 7 | public DataTemplate WaitingOpProgressTemplate { get; set; } 8 | public DataTemplate InProgSyncProgressTemplate { get; set; } 9 | public DataTemplate InProgShellProgressTemplate { get; set; } 10 | public DataTemplate CompletedSyncProgressTemplate { get; set; } 11 | public DataTemplate CompletedShellProgressTemplate { get; set; } 12 | public DataTemplate CanceledOpProgressTemplate { get; set; } 13 | public DataTemplate FailedOpProgressTemplate { get; set; } 14 | 15 | public override DataTemplate SelectTemplate(object item, DependencyObject container) 16 | { 17 | if (item is null) 18 | return null; 19 | 20 | return item switch 21 | { 22 | WaitingOpProgressViewModel => WaitingOpProgressTemplate, 23 | InProgSyncProgressViewModel => InProgSyncProgressTemplate, 24 | InProgShellProgressViewModel => InProgShellProgressTemplate, 25 | CompletedSyncProgressViewModel => CompletedSyncProgressTemplate, 26 | CompletedShellProgressViewModel => CompletedShellProgressTemplate, 27 | CanceledOpProgressViewModel => CanceledOpProgressTemplate, 28 | FailedOpProgressViewModel => FailedOpProgressTemplate, 29 | _ => throw new NotSupportedException(), 30 | }; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/TemplateSelectors/FileOpTreeTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | 3 | namespace ADB_Explorer.Helpers; 4 | 5 | internal class FileOpTreeTemplateSelector : DataTemplateSelector 6 | { 7 | public HierarchicalDataTemplate FileOpTreeFolderTemplate { get; set; } 8 | 9 | public HierarchicalDataTemplate FileOpTreeFileTemplate { get; set; } 10 | 11 | public override DataTemplate SelectTemplate(object item, DependencyObject container) 12 | { 13 | return item switch 14 | { 15 | SyncFile dir when dir.IsDirectory => FileOpTreeFolderTemplate, 16 | SyncFile => FileOpTreeFileTemplate, 17 | _ => throw new NotSupportedException(), 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/TemplateSelectors/FileOperationTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Services; 2 | 3 | namespace ADB_Explorer.Helpers; 4 | 5 | public class FileOperationTemplateSelector : DataTemplateSelector 6 | { 7 | public DataTemplate PullTemplate { get; set; } 8 | public DataTemplate PushTemplate { get; set; } 9 | public DataTemplate SyncTemplate { get; set; } 10 | 11 | public override DataTemplate SelectTemplate(object item, DependencyObject container) 12 | { 13 | return item switch 14 | { 15 | FileSyncOperation op when op.OperationName is FileOperation.OperationType.Pull => PullTemplate, 16 | FileSyncOperation op when op.OperationName is FileOperation.OperationType.Push => PushTemplate, 17 | FileSyncOperation => SyncTemplate, 18 | _ => throw new NotImplementedException(), 19 | }; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/TemplateSelectors/SettingsGroupTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Services; 2 | 3 | namespace ADB_Explorer.Helpers; 4 | 5 | public class SettingsGroupTemplateSelector : DataTemplateSelector 6 | { 7 | public DataTemplate SettingsGroupTemplate { get; set; } 8 | public DataTemplate SettingsSeparatorTemplate { get; set; } 9 | 10 | public override DataTemplate SelectTemplate(object item, DependencyObject container) 11 | { 12 | return item switch 13 | { 14 | SettingsGroup => SettingsGroupTemplate, 15 | SettingsSeparator => SettingsSeparatorTemplate, 16 | Ungrouped => null, 17 | _ => throw new NotImplementedException(), 18 | }; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /ADB Explorer/Helpers/TemplateSelectors/SettingsTemplateSelector.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Services; 2 | 3 | namespace ADB_Explorer.Helpers; 4 | 5 | public class SettingsTemplateSelector : DataTemplateSelector 6 | { 7 | public DataTemplate BoolSettingTemplate { get; set; } 8 | public DataTemplate StringSettingTemplate { get; set; } 9 | public DataTemplate EnumSettingTemplate { get; set; } 10 | public DataTemplate ComboSettingTemplate { get; set; } 11 | 12 | public override DataTemplate SelectTemplate(object item, DependencyObject container) 13 | { 14 | return item switch 15 | { 16 | BoolSetting => BoolSettingTemplate, 17 | StringSetting => StringSettingTemplate, 18 | EnumSetting => EnumSettingTemplate, 19 | ComboSetting => ComboSettingTemplate, 20 | _ => throw new NotImplementedException(), 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ADB Explorer/Models/Device/Device.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.ViewModels; 2 | 3 | namespace ADB_Explorer.Models; 4 | 5 | public abstract class AbstractDevice : ViewModelBase 6 | { 7 | public enum DeviceType 8 | { 9 | Service, 10 | Local, 11 | Remote, 12 | Recovery, 13 | Sideload, 14 | WSA, 15 | Emulator, 16 | History, 17 | New, 18 | } 19 | 20 | public enum DeviceStatus 21 | { 22 | Ok, // online \ does not require attention 23 | Offline, 24 | Unauthorized, 25 | } 26 | 27 | public enum RootStatus 28 | { 29 | Unchecked, 30 | Forbidden, 31 | Disabled, 32 | Enabled, 33 | } 34 | } 35 | 36 | public abstract class Device : AbstractDevice 37 | { 38 | #region Full properties 39 | 40 | private DeviceType type; 41 | public virtual DeviceType Type 42 | { 43 | get => type; 44 | protected set => Set(ref type, value); 45 | } 46 | 47 | private DeviceStatus status; 48 | public virtual DeviceStatus Status 49 | { 50 | get => status; 51 | set => Set(ref status, value); 52 | } 53 | 54 | private string ipAddress; 55 | public virtual string IpAddress 56 | { 57 | get => ipAddress; 58 | set => Set(ref ipAddress, value); 59 | } 60 | 61 | public virtual string ID { get; protected set; } 62 | 63 | #endregion 64 | 65 | public static implicit operator bool(Device obj) 66 | { 67 | return obj is not null && !string.IsNullOrEmpty(obj.ID); 68 | } 69 | } 70 | 71 | /// 72 | /// Represents all device types that require pairing properties 73 | /// 74 | public abstract class PairingDevice : Device 75 | { 76 | #region Full properties 77 | 78 | private string pairingPort; 79 | public virtual string PairingPort 80 | { 81 | get => pairingPort; 82 | set => Set(ref pairingPort, value); 83 | } 84 | 85 | private string pairingCode; 86 | public string PairingCode 87 | { 88 | get => pairingCode; 89 | set => Set(ref pairingCode, value); 90 | } 91 | 92 | #endregion 93 | } 94 | -------------------------------------------------------------------------------- /ADB Explorer/Models/Device/HistoryDevice.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Models; 2 | 3 | public class HistoryDevice : NewDevice 4 | { 5 | private string deviceName = null; 6 | public string DeviceName 7 | { 8 | get => deviceName; 9 | set => Set(ref deviceName, value); 10 | } 11 | 12 | public HistoryDevice() 13 | { 14 | Type = DeviceType.History; 15 | Status = DeviceStatus.Ok; 16 | } 17 | 18 | public HistoryDevice(NewDevice device) : this() 19 | { 20 | IpAddress = device.IpAddress; 21 | ConnectPort = device.ConnectPort; 22 | } 23 | 24 | [JsonConstructor] 25 | public HistoryDevice(string ipAddress, string connectPort, string deviceName = "") : this() 26 | { 27 | DeviceName = deviceName; 28 | IpAddress = ipAddress; 29 | ConnectPort = connectPort; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ADB Explorer/Models/Device/NewDevice.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Models; 2 | 3 | public class NewDevice : PairingDevice 4 | { 5 | private string connectPort; 6 | public string ConnectPort 7 | { 8 | get => connectPort; 9 | set => Set(ref connectPort, value); 10 | } 11 | 12 | private string hostName; 13 | public string HostName 14 | { 15 | get => hostName; 16 | set => Set(ref hostName, value); 17 | } 18 | 19 | public NewDevice() 20 | { 21 | Type = DeviceType.New; 22 | Status = DeviceStatus.Ok; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ADB Explorer/Models/Device/ServiceDevice.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Models; 2 | 3 | /// 4 | /// Represents all services acquired by mdns services 5 | /// 6 | public abstract class ServiceDevice : PairingDevice 7 | { 8 | public enum ServiceType 9 | { 10 | QrCode, 11 | PairingCode 12 | } 13 | 14 | #region Full properties 15 | 16 | private ServiceType mdnsType; 17 | public ServiceType MdnsType 18 | { 19 | get => mdnsType; 20 | set => Set(ref mdnsType, value); 21 | } 22 | 23 | #endregion 24 | 25 | public ServiceDevice() 26 | { 27 | Type = DeviceType.Service; 28 | } 29 | 30 | public ServiceDevice(string id, string ipAddress, string port = "") : this() 31 | { 32 | PropertyChanged += ServiceDevice_PropertyChanged; 33 | 34 | ID = id; 35 | IpAddress = ipAddress; 36 | PairingPort = port; 37 | } 38 | 39 | private void ServiceDevice_PropertyChanged(object sender, PropertyChangedEventArgs e) 40 | { 41 | if (e.PropertyName is nameof(PairingPort) or nameof(MdnsType)) 42 | { 43 | UpdateStatus(); 44 | } 45 | } 46 | 47 | private void UpdateStatus() 48 | { 49 | Status = MdnsType is ServiceType.QrCode ? DeviceStatus.Ok : DeviceStatus.Unauthorized; 50 | } 51 | } 52 | 53 | public class PairingService : ServiceDevice 54 | { 55 | public PairingService(string id, string ipAddress, string port) : base(id, ipAddress, port) 56 | { } 57 | } 58 | 59 | public class ConnectService : ServiceDevice 60 | { 61 | public ConnectService(string id, string ipAddress, string port) : base(id, ipAddress, port) 62 | { } 63 | } 64 | -------------------------------------------------------------------------------- /ADB Explorer/Models/Device/WsaPkgDevice.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Models; 2 | 3 | public class WsaPkgDevice : Device 4 | { 5 | private DateTime lastLaunch = DateTime.MinValue; 6 | public DateTime LastLaunch 7 | { 8 | get => lastLaunch; 9 | set => Set(ref lastLaunch, value); 10 | } 11 | 12 | public WsaPkgDevice() 13 | { 14 | Type = DeviceType.WSA; 15 | Status = DeviceStatus.Offline; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ADB Explorer/Models/Drive/Drive.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.ViewModels; 2 | using static ADB_Explorer.Models.AdbExplorerConst; 3 | 4 | namespace ADB_Explorer.Models; 5 | 6 | public abstract class AbstractDrive : ViewModelBase 7 | { 8 | public enum DriveType 9 | { 10 | Root, 11 | Internal, 12 | Expansion, 13 | External, 14 | Emulated, 15 | Unknown, 16 | Trash, 17 | Temp, 18 | Package, 19 | } 20 | 21 | private DriveType type = DriveType.Unknown; 22 | public DriveType Type 23 | { 24 | get => type; 25 | set => Set(ref type, value); 26 | } 27 | 28 | 29 | public static implicit operator bool(AbstractDrive obj) 30 | { 31 | return obj is not null; 32 | } 33 | 34 | public string DisplayName 35 | { 36 | get => GetDriveDisplayName(Type); 37 | } 38 | 39 | public static string GetDriveDisplayName(DriveType type) => type switch 40 | { 41 | DriveType.Root => Strings.Resources.S_DRIVE_ROOT, 42 | DriveType.Internal => Strings.Resources.S_DRIVE_INTERNAL_STORAGE, 43 | DriveType.Expansion => Strings.Resources.S_DRIVE_SD, 44 | DriveType.External => Strings.Resources.S_DRIVE_OTG, 45 | DriveType.Unknown => "", 46 | DriveType.Emulated => Strings.Resources.S_DRIVE_EMULATED, 47 | DriveType.Trash => Strings.Resources.S_DRIVE_TRASH, 48 | DriveType.Temp => Strings.Resources.S_DRIVE_TEMP, 49 | DriveType.Package => Strings.Resources.S_DRIVE_APPS, 50 | _ => null, 51 | }; 52 | } 53 | 54 | public class Drive : AbstractDrive 55 | { 56 | public string Path { get; protected set; } 57 | 58 | /// 59 | /// Filesystem in USEr space. An emulated / virtual filesystem on Android.

60 | /// Does not support:
61 | /// • Symbolic links
62 | /// • Special chars in file name (like NTFS)
63 | /// • Installing APK from it 64 | ///
65 | public virtual bool IsFUSE { get; } 66 | 67 | 68 | public Drive(string path = "") 69 | { 70 | Path = path; 71 | 72 | if (Type is DriveType.Unknown && DRIVE_TYPES.TryGetValue(path, out var type)) 73 | { 74 | Type = type; 75 | if (Type is DriveType.Internal) 76 | Path = "/sdcard"; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /ADB Explorer/Models/Drive/LogicalDrive.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Converters; 2 | 3 | namespace ADB_Explorer.Models; 4 | 5 | public class LogicalDrive : Drive 6 | { 7 | private string size; 8 | public string Size 9 | { 10 | get => size; 11 | set => Set(ref size, value); 12 | } 13 | 14 | private string used; 15 | public string Used 16 | { 17 | get => used; 18 | set => Set(ref used, value); 19 | } 20 | 21 | private string available; 22 | public string Available 23 | { 24 | get => available; 25 | set => Set(ref available, value); 26 | } 27 | 28 | private sbyte usageP; 29 | public sbyte UsageP 30 | { 31 | get => usageP; 32 | set => Set(ref usageP, value); 33 | } 34 | 35 | private string fileSystem = ""; 36 | public string FileSystem 37 | { 38 | get => fileSystem; 39 | set 40 | { 41 | if (Set(ref fileSystem, value)) 42 | OnPropertyChanged(nameof(IsFUSE)); 43 | } 44 | } 45 | 46 | public override bool IsFUSE => FileSystem.Contains("fuse"); 47 | 48 | public string ID => Path.Count(c => c == '/') > 1 ? Path[(Path.LastIndexOf('/') + 1)..] : Path; 49 | 50 | 51 | public LogicalDrive(string size = "", 52 | string used = "", 53 | string available = "", 54 | sbyte usageP = -1, 55 | string path = "", 56 | bool isMMC = false, 57 | bool isEmulator = false, 58 | string fileSystem = "") 59 | : base(path) 60 | { 61 | if (usageP is < -1 or > 100) 62 | throw new ArgumentOutOfRangeException(nameof(usageP)); 63 | 64 | Size = size; 65 | Used = used; 66 | Available = available; 67 | UsageP = usageP; 68 | 69 | if (isMMC) 70 | { 71 | Type = DriveType.Expansion; 72 | } 73 | else if (isEmulator) 74 | { 75 | Type = DriveType.Emulated; 76 | } 77 | 78 | FileSystem = fileSystem; 79 | } 80 | 81 | public LogicalDrive(GroupCollection match, bool isMMC = false, bool isEmulator = false, string forcePath = "") 82 | : this( 83 | (ulong.Parse(match["size_kB"].Value) * 1024).ToSize(true, 2, 2), 84 | (ulong.Parse(match["used_kB"].Value) * 1024).ToSize(true, 2, 2), 85 | (ulong.Parse(match["available_kB"].Value) * 1024).ToSize(true, 2, 2), 86 | sbyte.Parse(match["usage_P"].Value), 87 | string.IsNullOrEmpty(forcePath) ? match["path"].Value : forcePath, 88 | isMMC, 89 | isEmulator, 90 | match["FileSystem"].Value) 91 | { } 92 | } 93 | -------------------------------------------------------------------------------- /ADB Explorer/Models/Drive/VirtualDrive.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Models; 2 | 3 | public class VirtualDrive : Drive 4 | { 5 | private long? itemsCount = 0; 6 | public long? ItemsCount 7 | { 8 | get => itemsCount; 9 | set => Set(ref itemsCount, value); 10 | } 11 | 12 | public override bool IsFUSE => Type switch 13 | { 14 | // Temp drive is under the root filesystem 15 | DriveType.Temp => Data.DevicesObject.Current?.Drives?.Find(d => d.Type is DriveType.Root).IsFUSE is true, 16 | // App drive isn't really a drive, and the recycle bin doesn't allow any of the actions limited on FUSE 17 | // So it is useless to display the icon 18 | _ => false, 19 | }; 20 | 21 | public VirtualDrive(string path = "", long itemsCount = 0) : base(path) 22 | { 23 | ItemsCount = itemsCount; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ADB Explorer/Models/File/FileStat.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Models; 2 | 3 | public class FileStat : AbstractFile, IBaseFile, IFileStat 4 | { 5 | public FileStat(string fileName, 6 | string path, 7 | FileType type, 8 | bool isLink, 9 | ulong? size, 10 | DateTime? modifiedTime) 11 | { 12 | FullName = fileName; 13 | FullPath = path; 14 | 15 | Type = type; 16 | Size = size; 17 | ModifiedTime = modifiedTime; 18 | 19 | IsLink = isLink; 20 | } 21 | 22 | public string FullName { get; set; } 23 | 24 | public string FullPath { get; set; } 25 | 26 | public FileType Type { get; set; } 27 | 28 | public ulong? Size { get; set; } 29 | 30 | public DateTime? ModifiedTime { get; set; } 31 | 32 | public bool IsLink { get; set; } 33 | } 34 | -------------------------------------------------------------------------------- /ADB Explorer/Models/File/IBaseFile.cs: -------------------------------------------------------------------------------- 1 | using static ADB_Explorer.Models.AbstractFile; 2 | 3 | namespace ADB_Explorer.Models; 4 | 5 | /// 6 | /// Represents the basic properties returned by the `stat` command on Android devices. 7 | /// 8 | public interface IFileStat 9 | { 10 | public FileType Type { get; set; } 11 | 12 | public ulong? Size { get; set; } 13 | 14 | public DateTime? ModifiedTime { get; set; } 15 | 16 | public bool IsLink { get; set; } 17 | } 18 | 19 | /// 20 | /// Represents the basic properties of a file or directory. 21 | /// 22 | public interface IBaseFile 23 | { 24 | public string FullName { get; } 25 | 26 | public string FullPath { get; } 27 | } 28 | 29 | /// 30 | /// Represents the items that can be displayed in the browser - and . 31 | /// 32 | public interface IBrowserItem 33 | { 34 | public string DisplayName { get; } 35 | } 36 | -------------------------------------------------------------------------------- /ADB Explorer/Models/File/Package.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.ViewModels; 2 | 3 | namespace ADB_Explorer.Models; 4 | 5 | public class Package : ViewModelBase, IBrowserItem 6 | { 7 | public enum PackageType 8 | { 9 | System, 10 | User, 11 | } 12 | 13 | private string name; 14 | public string Name 15 | { 16 | get => name; 17 | set => Set(ref name, value); 18 | } 19 | 20 | public string DisplayName => Name; 21 | 22 | private PackageType type; 23 | public PackageType Type 24 | { 25 | get => type; 26 | set => Set(ref type, value); 27 | } 28 | 29 | private long? uid = null; 30 | public long? Uid 31 | { 32 | get => uid; 33 | set => Set(ref uid, value); 34 | } 35 | 36 | private long? version = null; 37 | public long? Version 38 | { 39 | get => version; 40 | set => Set(ref version, value); 41 | } 42 | 43 | public static Package New(string package, PackageType type) 44 | { 45 | var match = AdbRegEx.RE_PACKAGE_LISTING().Match(package); 46 | if (!match.Success) 47 | return null; 48 | 49 | return new Package(match.Groups["package"].Value, type, match.Groups["uid"].Value, match.Groups["version"].Value); 50 | } 51 | 52 | public Package(string name, PackageType type, string uid, string version) 53 | { 54 | Name = name; 55 | Type = type; 56 | 57 | if (long.TryParse(uid, out long resU)) 58 | Uid = resU; 59 | 60 | if (long.TryParse(version, out long resV)) 61 | Version = resV; 62 | } 63 | 64 | public override string ToString() 65 | { 66 | return $"{Name}\n{Type}\n{Uid}\n{Version}"; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ADB Explorer/Models/File/SyncFile.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Helpers; 2 | using ADB_Explorer.Services; 3 | using Vanara.Windows.Shell; 4 | 5 | namespace ADB_Explorer.Models; 6 | 7 | public class SyncFile : FilePath 8 | { 9 | public ObservableList ProgressUpdates { get; } = []; 10 | 11 | public ObservableList Children { get; } = []; 12 | 13 | public ulong? Size { get; } 14 | 15 | public SyncFile(string androidPath, FileType fileType = FileType.File) 16 | : base(androidPath, fileType: fileType) 17 | { 18 | 19 | } 20 | 21 | public SyncFile(ShellItem windowsPath) 22 | : base(windowsPath) 23 | { 24 | Size = IsDirectory ? null : (ulong?)windowsPath.FileInfo.Length; 25 | } 26 | 27 | public SyncFile(FileClass fileClass) 28 | : base(fileClass.FullPath, fileClass.FullName, fileClass.Type) 29 | { 30 | Size = fileClass.Size; 31 | } 32 | 33 | public void AddUpdates(params FileOpProgressInfo[] newUpdates) 34 | => AddUpdates(newUpdates.Where(o => o is not null)); 35 | 36 | public void AddUpdates(IEnumerable newUpdates, FileOperation fileOp = null) 37 | { 38 | if (!newUpdates.Any()) 39 | return; 40 | 41 | if (!IsDirectory || newUpdates.All(u => u.AndroidPath is not null && u.AndroidPath.Equals(FullPath))) 42 | { 43 | ProgressUpdates.AddRange(newUpdates); 44 | return; 45 | } 46 | 47 | if (fileOp is FileSyncOperation) 48 | { 49 | foreach (var update in newUpdates) 50 | { 51 | if (string.IsNullOrEmpty(update.AndroidPath)) 52 | update.SetPathToCurrent(fileOp); 53 | } 54 | } 55 | else 56 | newUpdates = newUpdates.Where(u => !string.IsNullOrEmpty(u.AndroidPath)); 57 | 58 | var groups = newUpdates.GroupBy(update => DirectChildPath(update.AndroidPath)); 59 | 60 | foreach (var group in groups.Where(g => g.Key is not null)) 61 | { 62 | SyncFile file = Children.FirstOrDefault(child => child.FullPath.Equals(group.Key)); 63 | 64 | if (file is null) 65 | { 66 | bool isDir = !group.Key.Equals(group.First().AndroidPath); 67 | file = new(group.Key, isDir ? FileType.Folder : FileType.File); 68 | 69 | ExecuteInDispatcher(() => 70 | { 71 | Children.Add(file); 72 | 73 | OnPropertyChanged(nameof(Children)); 74 | }); 75 | } 76 | 77 | file.AddUpdates(group); 78 | } 79 | } 80 | 81 | public string DirectChildPath(string fullPath) 82 | => FileHelper.DirectChildPath(FullPath, fullPath); 83 | } 84 | 85 | public class SyncFileComparer : IEqualityComparer 86 | { 87 | public bool Equals(SyncFile x, SyncFile y) 88 | => x.FullPath.Equals(y.FullPath); 89 | 90 | public int GetHashCode([DisallowNull] SyncFile obj) 91 | { 92 | throw new NotImplementedException(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /ADB Explorer/Models/File/TrashIndexer.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Services; 2 | using ADB_Explorer.ViewModels; 3 | 4 | namespace ADB_Explorer.Models; 5 | 6 | public class TrashIndexer : ViewModelBase 7 | { 8 | private string recycleName; 9 | public string RecycleName 10 | { 11 | get => recycleName; 12 | set => Set(ref recycleName, value); 13 | } 14 | 15 | private string originalPath; 16 | public string OriginalPath 17 | { 18 | get => originalPath; 19 | set => Set(ref originalPath, value); 20 | } 21 | 22 | private DateTime? dateModified; 23 | public DateTime? DateModified 24 | { 25 | get => dateModified; 26 | set => Set(ref dateModified, value); 27 | } 28 | 29 | public string ModifiedTimeString => DateModified?.ToString(CultureInfo.CurrentCulture.DateTimeFormat); 30 | 31 | public string IndexerPath => $"{AdbExplorerConst.RECYCLE_PATH}/.{RecycleName}{AdbExplorerConst.RECYCLE_INDEX_SUFFIX}"; 32 | 33 | public string ParentPath 34 | { 35 | get 36 | { 37 | int originalIndex = OriginalPath.LastIndexOf('/'); 38 | Index index; 39 | if (originalIndex == 0) 40 | index = 1; 41 | else if (originalIndex < 0) 42 | index = ^0; 43 | else 44 | index = originalIndex; 45 | 46 | return OriginalPath[..index]; 47 | } 48 | } 49 | 50 | public TrashIndexer() 51 | { } 52 | 53 | public TrashIndexer(string recycleIndex) : this(recycleIndex.Split('|')) 54 | { } 55 | 56 | public TrashIndexer(params string[] recycleIndex) : this(recycleIndex[0], recycleIndex[1], recycleIndex[2]) 57 | { } 58 | 59 | public TrashIndexer(string recycleName, string originalPath, string dateModified) 60 | : this(recycleName, originalPath, DateTime.TryParseExact(dateModified, AdbExplorerConst.ADB_EXPLORER_DATE_FORMAT, null, DateTimeStyles.None, out var res) ? res : null) 61 | { } 62 | 63 | public TrashIndexer(string recycleName, string originalPath, DateTime? dateModified) 64 | { 65 | RecycleName = recycleName; 66 | OriginalPath = originalPath; 67 | DateModified = dateModified; 68 | } 69 | 70 | public TrashIndexer(FileMoveOperation op) 71 | { 72 | RecycleName = op.RecycleName; 73 | OriginalPath = op.FilePath.FullPath; 74 | DateModified = op.DateModified; 75 | } 76 | 77 | public override string ToString() 78 | { 79 | var date = DateModified is null ? "?" : DateModified.Value.ToString(AdbExplorerConst.ADB_EXPLORER_DATE_FORMAT); 80 | return $"{RecycleName}|{OriginalPath}|{date}"; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /ADB Explorer/Models/FileOpFilter.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Helpers; 2 | using ADB_Explorer.Services; 3 | using ADB_Explorer.ViewModels; 4 | 5 | namespace ADB_Explorer.Models; 6 | 7 | public static class FileOpFilters 8 | { 9 | private static List list = null; 10 | public static List List 11 | { 12 | get 13 | { 14 | if (list is null) 15 | { 16 | list = [.. Enum.GetValues().Select(f => new FileOpFilter(f))]; 17 | 18 | UpdateCheckedColumns(); 19 | } 20 | 21 | return list; 22 | } 23 | } 24 | 25 | public static ObservableProperty CheckedFilterCount { get; private set; } = new() { Value = -1 }; 26 | 27 | public static void UpdateCheckedColumns() => 28 | CheckedFilterCount.Value = List.Count(col => col.IsChecked is true); 29 | } 30 | 31 | public class FileOpFilter : ViewModelBase 32 | { 33 | public enum FilterType 34 | { 35 | Running, 36 | Pending, 37 | Completed, 38 | Validated, 39 | Failed, 40 | Canceled, 41 | Previous, 42 | } 43 | 44 | private bool? isChecked = null; 45 | public bool? IsChecked 46 | { 47 | get => isChecked; 48 | set 49 | { 50 | if (Set(ref isChecked, value)) 51 | { 52 | if (FileOpFilters.CheckedFilterCount > 0) 53 | FileOpFilters.UpdateCheckedColumns(); 54 | 55 | Store(); 56 | } 57 | } 58 | } 59 | 60 | private CheckBox checkBox = null; 61 | public CheckBox CheckBox 62 | { 63 | get 64 | { 65 | if (checkBox is null) 66 | { 67 | checkBox = new CheckBox() 68 | { 69 | Style = (Style)App.Current.FindResource("FileOpFilterCheckBox"), 70 | DataContext = this, 71 | Content = AdbExplorerConst.FILE_OP_NAMES[Type], 72 | Margin = new(0, -6, 0, -6), 73 | }; 74 | } 75 | 76 | return checkBox; 77 | } 78 | } 79 | 80 | public FilterType Type { get; } 81 | 82 | public bool IsEnabled 83 | { 84 | get 85 | { 86 | if (IsChecked is null) 87 | return false; 88 | 89 | return IsChecked is false || FileOpFilters.CheckedFilterCount > 1; 90 | } 91 | } 92 | 93 | public FileOpFilter(FilterType type) 94 | { 95 | Type = type; 96 | 97 | if (type is FilterType.Running) 98 | IsChecked = null; 99 | else 100 | IsChecked = Retrieve() is bool val ? val : true; 101 | 102 | FileOpFilters.CheckedFilterCount.PropertyChanged += (object sender, PropertyChangedEventArgs e) => OnPropertyChanged(nameof(IsEnabled)); 103 | } 104 | 105 | private bool? Retrieve() => Storage.RetrieveBool($"{nameof(FilterType)}_{Type}"); 106 | 107 | private void Store() => Storage.StoreValue($"{nameof(FilterType)}_{Type}", $"{IsChecked}"); 108 | } 109 | -------------------------------------------------------------------------------- /ADB Explorer/Models/Log.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Models; 2 | 3 | internal class Log 4 | { 5 | public string Content { get; set; } 6 | 7 | public DateTime TimeStamp { get; set; } 8 | 9 | public Log(string content, DateTime? timeStamp = null) 10 | { 11 | Content = content; 12 | TimeStamp = timeStamp is null ? DateTime.Now : timeStamp.Value; 13 | } 14 | 15 | public override string ToString() 16 | { 17 | return $"{TimeStamp:HH:mm:ss:fff} ⁞ {Content}"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /ADB Explorer/Models/Static/Data.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Helpers; 2 | using ADB_Explorer.Services; 3 | using ADB_Explorer.ViewModels; 4 | 5 | namespace ADB_Explorer.Models; 6 | 7 | internal static class Data 8 | { 9 | public static ADBService.AdbDevice CurrentADBDevice { get; set; } = null; 10 | 11 | public static string CurrentPath { get; set; } 12 | public static string ParentPath { get; set; } 13 | 14 | public static DriveViewModel CurrentDrive { get; set; } = null; 15 | 16 | public static FileOperationQueue FileOpQ { get; set; } 17 | 18 | public static Dictionary CurrentDisplayNames { get; set; } = []; 19 | 20 | public static AppSettings Settings { get; set; } = new(); 21 | 22 | public static AppRuntimeSettings RuntimeSettings { get; set; } = new(); 23 | 24 | public static CopyPasteService CopyPaste { get; } = new(); 25 | 26 | public static ObservableCollection CommandLog { get; set; } = []; 27 | 28 | public static ObservableList RecycleIndex { get; set; } = []; 29 | 30 | public static ObservableList Packages { get; set; } = []; 31 | 32 | public static Version AppVersion => new(Properties.AppGlobal.AppVersion); 33 | 34 | public static FileActionsEnable FileActions { get; set; } = new(); 35 | 36 | public static DirectoryLister DirList { get; set; } 37 | 38 | public static string AppDataPath { get; set; } = ""; 39 | 40 | public static string ProgressRedirectionPath { get; set; } = null; 41 | 42 | public static Devices DevicesObject { get; set; } 43 | 44 | public static MDNS MdnsService { get; set; } = new(); 45 | 46 | public static PairingQrClass QrClass { get; set; } 47 | 48 | public static IEnumerable SelectedFiles { get; set; } = Enumerable.Empty(); 49 | 50 | public static IEnumerable SelectedPackages { get; set; } = Enumerable.Empty(); 51 | } 52 | -------------------------------------------------------------------------------- /ADB Explorer/PortableLauncher.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | start "" "ADB Explorer.exe" ./ 3 | exit -------------------------------------------------------------------------------- /ADB Explorer/Resources/APK_format_icon_2014-2019_.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alex4SSB/ADB-Explorer/84077b57171e9333a6744712f69eeb683cbede15/ADB Explorer/Resources/APK_format_icon_2014-2019_.ico -------------------------------------------------------------------------------- /ADB Explorer/Resources/AdbProgressRedirection_ARM: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alex4SSB/ADB-Explorer/84077b57171e9333a6744712f69eeb683cbede15/ADB Explorer/Resources/AdbProgressRedirection_ARM -------------------------------------------------------------------------------- /ADB Explorer/Resources/AdbProgressRedirection_x86: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alex4SSB/ADB-Explorer/84077b57171e9333a6744712f69eeb683cbede15/ADB Explorer/Resources/AdbProgressRedirection_x86 -------------------------------------------------------------------------------- /ADB Explorer/Resources/Fonts/Nunito-VariableFont_wght.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Alex4SSB/ADB-Explorer/84077b57171e9333a6744712f69eeb683cbede15/ADB Explorer/Resources/Fonts/Nunito-VariableFont_wght.ttf -------------------------------------------------------------------------------- /ADB Explorer/Resources/Links.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | 3 | namespace ADB_Explorer.Resources; 4 | 5 | public static class Links 6 | { 7 | public static readonly Uri L_ADB_PAGE = Data.RuntimeSettings.IsAppDeployed 8 | ? new("https://developer.android.com/studio/command-line/adb") 9 | : new("https://developer.android.com/tools/releases/platform-tools"); 10 | public static readonly Uri L_CC_LIC = new("https://creativecommons.org/licenses/by-sa/3.0/"); 11 | public static readonly Uri L_APACHE_LIC = new("https://www.apache.org/licenses/LICENSE-2.0"); 12 | public static readonly Uri REPO_RELEASES_URL = new("https://api.github.com/repos/Alex4SSB/ADB-Explorer/releases"); 13 | public static readonly Uri MODERN_WPF = new("https://github.com/Kinnara/ModernWpf"); 14 | public static readonly Uri ICONS8 = new("https://icons8.com"); 15 | public static readonly Uri ADB_EXPLORER_PRIVACY = new("https://github.com/Alex4SSB/ADB-Explorer/blob/master/Privacy.md"); 16 | public static readonly Uri ADB_EXPLORER_GITHUB = new("https://github.com/Alex4SSB/ADB-Explorer"); 17 | public static readonly Uri LGPL3 = new("https://opensource.org/license/lgpl-3-0"); 18 | public static readonly Uri SPONSOR = new("https://github.com/sponsors/Alex4SSB"); 19 | } 20 | -------------------------------------------------------------------------------- /ADB Explorer/Services/ADB/MDNS.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | using ADB_Explorer.ViewModels; 3 | 4 | namespace ADB_Explorer.Services; 5 | 6 | public class MDNS : ViewModelBase 7 | { 8 | public MDNS() 9 | { 10 | State = MdnsState.Disabled; 11 | } 12 | 13 | public enum MdnsState 14 | { 15 | Disabled, 16 | InProgress, 17 | NotRunning, 18 | Running, 19 | } 20 | 21 | private MdnsState state; 22 | public MdnsState State 23 | { 24 | get => state; 25 | set 26 | { 27 | if (Set(ref state, value)) 28 | { 29 | if (value is MdnsState.InProgress) 30 | checkStart = DateTime.Now; 31 | else 32 | Progress = 0.0; 33 | } 34 | } 35 | } 36 | 37 | public void CheckMdns() 38 | { 39 | if (ADBService.CheckMDNS()) 40 | State = MdnsState.Running; 41 | else 42 | State = MdnsState.NotRunning; 43 | } 44 | 45 | private double progress; 46 | public double Progress 47 | { 48 | get => progress; 49 | set 50 | { 51 | if (Set(ref progress, value)) 52 | OnPropertyChanged(nameof(TimePassedString)); 53 | } 54 | } 55 | 56 | private DateTime checkStart; 57 | 58 | private TimeSpan timePassed = TimeSpan.MinValue; 59 | 60 | public string TimePassedString => timePassed == TimeSpan.MinValue ? "" : Converters.UnitConverter.ToTime((decimal)timePassed.TotalSeconds, useMilli: false, digits: 0); 61 | 62 | public void UpdateProgress() 63 | { 64 | timePassed = DateTime.Now.Subtract(checkStart); 65 | 66 | Progress = timePassed < AdbExplorerConst.MDNS_DOWN_RESPONSE_TIME 67 | ? timePassed / AdbExplorerConst.MDNS_DOWN_RESPONSE_TIME * 100 68 | : 100; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ADB Explorer/Services/ADB/WiFiPairingService.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Helpers; 2 | using ADB_Explorer.Models; 3 | using static ADB_Explorer.Models.AdbExplorerConst; 4 | 5 | namespace ADB_Explorer.Services; 6 | 7 | public class PairingQrClass 8 | { 9 | public string ServiceName { get; } 10 | public string Password { get; } 11 | public SolidColorBrush Background { get; } 12 | public SolidColorBrush Foreground { get; } 13 | 14 | public DrawingImage Image => string.IsNullOrEmpty(PairingString) ? null : QrGenerator.GenerateQR(PairingString, Background, Foreground); 15 | public string PairingString => WiFiPairingService.CreatePairingString(ServiceName, Password); 16 | 17 | public PairingQrClass() 18 | { 19 | ServiceName = PAIRING_SERVICE_PREFIX + RandomString.GetUniqueKey(10); 20 | Password = RandomString.GetUniqueKey(12); 21 | 22 | Background = (SolidColorBrush)App.Current.FindResource("QrBackgroundBrush"); 23 | Foreground = (SolidColorBrush)App.Current.FindResource("QrForegroundBrush"); 24 | } 25 | } 26 | 27 | public class WiFiPairingService 28 | { 29 | /** 30 | * Format is "WIFI:T:ADB;S:service;P:password;;" (without the quotes) 31 | */ 32 | public static string CreatePairingString(string service, string password) 33 | { 34 | return $"WIFI:T:ADB;S:{service};P:{password};;"; 35 | } 36 | 37 | public static IEnumerable GetServices() 38 | { 39 | ADBService.ExecuteAdbCommand("mdns", out string services, out _, new(), "services"); 40 | 41 | List mdnsServices = []; 42 | var matches = AdbRegEx.RE_MDNS_SERVICE().Matches(services); 43 | foreach (Match item in matches) 44 | { 45 | var id = item.Groups["ID"].Value; 46 | var portType = item.Groups["PortType"].Value; 47 | var ipAddress = item.Groups["IpAddress"].Value; 48 | var port = item.Groups["Port"].Value; 49 | 50 | if (LOOPBACK_ADDRESSES.Contains(ipAddress)) 51 | continue; 52 | 53 | ServiceDevice service = portType == "pairing" 54 | ? new PairingService(id, ipAddress, port) 55 | : new ConnectService(id, ipAddress, port); 56 | 57 | service.MdnsType = id.Contains(PAIRING_SERVICE_PREFIX) 58 | ? ServiceDevice.ServiceType.QrCode 59 | : ServiceDevice.ServiceType.PairingCode; 60 | 61 | mdnsServices.Add(service); 62 | } 63 | 64 | return mdnsServices.Distinct(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ADB Explorer/Services/AppInfra/DebugLog.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Services; 2 | 3 | public static class DebugLog 4 | { 5 | private static readonly Mutex mutex = new(); 6 | 7 | public static void PrintLine(string message) 8 | { 9 | mutex.WaitOne(); 10 | 11 | if (!string.IsNullOrEmpty(Properties.AppGlobal.DragDropLogPath)) 12 | File.AppendAllText(Properties.AppGlobal.DragDropLogPath, $"{DateTime.Now:HH:mm:ss:fff} | {message}\n"); 13 | 14 | mutex.ReleaseMutex(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ADB Explorer/Services/AppInfra/FileAction/ToggleMenu.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Helpers; 2 | using ADB_Explorer.ViewModels; 3 | 4 | namespace ADB_Explorer.Services; 5 | 6 | internal class ToggleMenu : ViewModelBase 7 | { 8 | public ObservableProperty IsChecked { get; set; } = new(); 9 | 10 | public ObservableProperty Description { get; private set; } = new(); 11 | 12 | public ObservableProperty Icon { get; private set; } = new(); 13 | 14 | public FileAction FileAction { get; } 15 | 16 | public DualActionButton Button { get; } 17 | 18 | public ToggleMenu(FileAction.FileActionType type, 19 | Func canExecute, 20 | string checkedDescription, 21 | string checkedIcon, 22 | Action action, 23 | string uncheckedDescription = "", 24 | string uncheckedIcon = "", 25 | KeyGesture gesture = null, 26 | Brush checkBackground = null, 27 | IEnumerable children = null, 28 | ObservableProperty isVisible = null, 29 | bool toggleOnClick = true, 30 | bool clearClipboard = false) 31 | { 32 | uncheckedDescription = string.IsNullOrEmpty(uncheckedDescription) ? checkedDescription : uncheckedDescription; 33 | uncheckedIcon = string.IsNullOrEmpty(uncheckedIcon) ? checkedIcon : uncheckedIcon; 34 | 35 | IsChecked.Value = false; 36 | Description.Value = uncheckedDescription; 37 | Icon.Value = uncheckedIcon; 38 | 39 | FileAction = new(type, canExecute, action, Description, gesture, gesture is not null, clearClipboard); 40 | Button = new(FileAction, Icon, IsChecked, checkBackground: checkBackground, children: children, isVisible: isVisible, isCheckable: toggleOnClick); 41 | 42 | IsChecked.PropertyChanged += (object sender, PropertyChangedEventArgs e) => 43 | { 44 | Description.Value = e.NewValue ? checkedDescription : uncheckedDescription; 45 | Icon.Value = e.NewValue ? checkedIcon : uncheckedIcon; 46 | }; 47 | } 48 | 49 | public void Toggle(bool? toggle = null) 50 | { 51 | if (toggle is null || IsChecked.Value != toggle.Value) 52 | { 53 | IsChecked.Value ^= true; 54 | FileAction.Command.Execute(); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /ADB Explorer/Services/AppInfra/IpcService.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | 3 | namespace ADB_Explorer.Services.AppInfra; 4 | 5 | public class IpcService 6 | { 7 | public enum MessageType 8 | { 9 | DragCanceled, 10 | FileMoved, 11 | } 12 | 13 | public static void AcceptIpcMessage(string message) 14 | { 15 | string[] msgContent = message.Split('|'); 16 | if (!Enum.TryParse(typeof(MessageType), msgContent[0], true, out var res)) 17 | return; 18 | 19 | switch ((MessageType)res) 20 | { 21 | case MessageType.DragCanceled: 22 | if (Enum.TryParse(msgContent[1], out NativeMethods.HResult hr) && hr is NativeMethods.HResult.DRAGDROP_S_CANCEL) 23 | Data.CopyPaste.ClearDrag(); 24 | break; 25 | case MessageType.FileMoved: 26 | var content = msgContent[1].Split('\n'); 27 | if (Data.CurrentADBDevice.ID != content[0]) 28 | return; 29 | 30 | FilePath file = new(content[1]); 31 | if (Data.CurrentPath != file.ParentPath) 32 | return; 33 | 34 | Data.DirList.FileList.RemoveAll(f => f.FullPath == file.FullPath); 35 | 36 | break; 37 | } 38 | } 39 | 40 | public static bool SendIpcMessage(HANDLE hWnd, MessageType type, string content = "") 41 | { 42 | var message = $"{Enum.GetName(type)}|{content}"; 43 | 44 | NativeMethods.COPYDATASTRUCT cds = new() 45 | { 46 | dwData = IntPtr.Zero, 47 | cbData = Encoding.Unicode.GetByteCount(message) + 2, 48 | lpData = message 49 | }; 50 | 51 | return NativeMethods.SendMessage(hWnd, NativeMethods.WindowMessages.WM_COPYDATA, ref cds); 52 | } 53 | 54 | public static void NotifyDropCancel(NativeMethods.HResult hr) 55 | { 56 | if (Data.RuntimeSettings.DragWithinSlave) 57 | SendIpcMessage(NativeMethods.InterceptMouse.WindowUnderMouse, MessageType.DragCanceled, $"{hr}"); 58 | } 59 | 60 | public static void NotifyFileMoved(int remotePid, ADBService.AdbDevice device, FilePath file) 61 | { 62 | var process = Process.GetProcessById(remotePid); 63 | 64 | SendIpcMessage(process.MainWindowHandle, MessageType.FileMoved, $"{device.ID}\n{file.FullPath}"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /ADB Explorer/Services/AppInfra/LowLevel/ExplorerWindow.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Helpers; 2 | using ADB_Explorer.Models; 3 | using System.Windows.Automation; 4 | 5 | namespace ADB_Explorer.Services; 6 | 7 | public class ExplorerWindow : IComparable 8 | { 9 | public HANDLE Hwnd { get; } 10 | 11 | private AutomationElement _rootElement = null; 12 | public AutomationElement RootElement 13 | { 14 | get 15 | { 16 | if (_rootElement is null) 17 | { 18 | _rootElement = AutomationElement.FromHandle(Hwnd); 19 | } 20 | 21 | return _rootElement; 22 | } 23 | } 24 | 25 | private AutomationElement _fileList = null; 26 | public AutomationElement FileList 27 | { 28 | get 29 | { 30 | if (_fileList is null) 31 | { 32 | _fileList = RootElement.FindFirst(TreeScope.Children, new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.List)); 33 | } 34 | return _fileList; 35 | } 36 | } 37 | 38 | private Process _process = null; 39 | public Process Process 40 | { 41 | get 42 | { 43 | if (_process is null) 44 | { 45 | // Don't use automation just to get pid if it isn't ready yet 46 | int processId = _fileList is null 47 | ? ProcessHandling.GetProcessIdFromWindowHandle(Hwnd) 48 | : RootElement.Current.ProcessId; 49 | 50 | _process = Process.GetProcessById(processId); 51 | } 52 | 53 | return _process; 54 | } 55 | } 56 | 57 | public string[] Paths { get; } 58 | 59 | /// 60 | /// Gets the resolved file system path based on the current state and context. 61 | /// 62 | /// If multiple paths are available, the method attempts to match using window title. 63 | public string Path 64 | { 65 | get 66 | { 67 | if (Paths.Length == 0) 68 | return null; 69 | else if (Paths.Length == 1) 70 | return Paths[0]; 71 | else 72 | // 22H2 with more than one tab 73 | { 74 | var pathDict = Paths.ToDictionary(FileHelper.GetFullName, path => path); 75 | 76 | var query = pathDict.Where(item => RootElement.Current.Name.StartsWith(item.Key)); 77 | 78 | return query.Any() ? query.First().Value : null; 79 | } 80 | } 81 | } 82 | 83 | public ExplorerWindow(IGrouping paths) 84 | { 85 | Hwnd = paths.Key; 86 | Paths = [.. paths]; 87 | } 88 | 89 | public ExplorerWindow(nint hwnd) 90 | { 91 | Hwnd = hwnd; 92 | } 93 | 94 | public ExplorerWindow(nint hwnd, string path) 95 | { 96 | Hwnd = hwnd; 97 | Paths = [ path ]; 98 | } 99 | 100 | public override bool Equals(object obj) 101 | { 102 | return obj is ExplorerWindow window && 103 | Hwnd.Equals(window.Hwnd); 104 | } 105 | 106 | public override int GetHashCode() => Hwnd.GetHashCode(); 107 | 108 | public int CompareTo(object obj) => Hwnd.CompareTo(obj); 109 | } 110 | -------------------------------------------------------------------------------- /ADB Explorer/Services/AppInfra/LowLevel/FileContentsStream.cs: -------------------------------------------------------------------------------- 1 | // Copied and modified from https://github.com/asklar/clipview under MIT License 2 | 3 | using System.Runtime.InteropServices.ComTypes; 4 | 5 | namespace ADB_Explorer.Services.AppInfra; 6 | 7 | public class FileContentsStream : IDisposable 8 | { 9 | private readonly STATSTG stat; 10 | 11 | private readonly IStream stream; 12 | 13 | public FileContentsStream(IStream stream) 14 | { 15 | this.stream = stream; 16 | stream.Stat(out stat, 0); 17 | } 18 | 19 | public string FileName => stat.pwcsName; 20 | public long Length => stat.cbSize; 21 | 22 | enum STREAM_SEEK 23 | { 24 | STREAM_SEEK_SET, 25 | STREAM_SEEK_CUR, 26 | STREAM_SEEK_END 27 | }; 28 | 29 | public void SaveToStream(Stream outputStream) 30 | { 31 | // Initialize the buffer to the size of the stream. 32 | // This should work as long as we are able to create a buffer of such size. 33 | byte[] buffer = new byte[stat.cbSize]; 34 | stream.Seek(0, (int)STREAM_SEEK.STREAM_SEEK_SET, HANDLE.Zero); 35 | 36 | HANDLE pcbRead = Marshal.AllocHGlobal(sizeof(int)); 37 | try 38 | { 39 | stream.Read(buffer, buffer.Length, pcbRead); 40 | outputStream.Write(buffer, 0, Marshal.ReadInt32(pcbRead)); 41 | } 42 | catch (EndOfStreamException) 43 | { } 44 | finally 45 | { 46 | Marshal.FreeHGlobal(pcbRead); 47 | } 48 | } 49 | 50 | public void Save(string filepath, bool disposeAfter = true) 51 | { 52 | using var file = File.Create(filepath); 53 | SaveToStream(file); 54 | 55 | if (disposeAfter) 56 | Dispose(); 57 | } 58 | 59 | public void Dispose() 60 | { 61 | Marshal.ReleaseComObject(stream); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /ADB Explorer/Services/AppInfra/LowLevel/MonitorInfo.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | 3 | namespace ADB_Explorer.Services; 4 | 5 | public static partial class NativeMethods 6 | { 7 | public static class MonitorInfo 8 | { 9 | private enum MonitorType 10 | { 11 | Primary = 0x00000001, 12 | Nearest = 0x00000002, 13 | } 14 | 15 | private static HANDLE? mainWinHandle; 16 | private static HANDLE primaryMonitor => PrimaryMonitor(); 17 | 18 | public static void Init(Window window) 19 | { 20 | mainWinHandle ??= new WindowInteropHelper(window).EnsureHandle(); 21 | } 22 | 23 | public static bool? IsPrimaryMonitor(Window window) 24 | { 25 | Init(window); 26 | return IsPrimaryMonitor(); 27 | } 28 | 29 | public static bool? IsPrimaryMonitor() 30 | { 31 | if (mainWinHandle is null) 32 | return null; 33 | 34 | var current = NearestMonitor(mainWinHandle.Value); 35 | 36 | return current == primaryMonitor; 37 | } 38 | 39 | public static HANDLE NearestMonitor(HANDLE handle) => MonitorFromWindow(handle, MonitorType.Nearest); 40 | 41 | public static HANDLE PrimaryMonitor() => MonitorFromWindow(IntPtr.Zero, MonitorType.Primary); 42 | 43 | public static POINT MousePositionToDpi(POINT mousePosition, HANDLE hWnd) 44 | { 45 | // Get the DPI for this window 46 | var dpi = GetDpiForWindow(hWnd); 47 | if (dpi == 0) // fallback to default DPI if window not found 48 | dpi = 96; 49 | 50 | // Calculate the scaling factor (96 is the default DPI) 51 | Data.RuntimeSettings.DpiScalingFactor = 96f / dpi; 52 | 53 | // Convert the coordinates 54 | return new( 55 | (int)(mousePosition.X * Data.RuntimeSettings.DpiScalingFactor), 56 | (int)(mousePosition.Y * Data.RuntimeSettings.DpiScalingFactor)); 57 | } 58 | 59 | [DllImport("User32.dll")] 60 | private static extern HANDLE MonitorFromWindow(HANDLE hwnd, MonitorType dwFlags); 61 | 62 | [DllImport("User32.dll")] 63 | static extern uint GetDpiForWindow(HANDLE hwnd); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /ADB Explorer/Services/AppInfra/LowLevel/ProcessHandling.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | 3 | namespace ADB_Explorer.Services; 4 | 5 | public class ProcessHandling 6 | { 7 | public static void KillProcess(Process process, bool recursive = true) 8 | { 9 | var procs = GetChildProcesses(process, recursive); 10 | 11 | foreach (var item in procs) 12 | { 13 | item.Kill(); 14 | } 15 | } 16 | 17 | public static IEnumerable GetChildProcesses(Process process, bool recursive = true) 18 | { 19 | ManagementObjectSearcher searcher; 20 | try 21 | { 22 | searcher = new( 23 | "SELECT * " + 24 | "FROM Win32_Process " + 25 | "WHERE ParentProcessId=" + process.Id); 26 | } 27 | catch 28 | { 29 | // if we can't get the pid without throwing an exception, the process is useless 30 | yield break; 31 | } 32 | 33 | foreach (var item in searcher.Get()) 34 | { 35 | Process proc; 36 | 37 | try 38 | { 39 | proc = Process.GetProcessById((int)(uint)item["ProcessId"]); 40 | } 41 | catch (Exception) 42 | { 43 | continue; 44 | } 45 | 46 | if (recursive) 47 | { 48 | foreach (var subItem in GetChildProcesses(proc)) 49 | { 50 | yield return subItem; 51 | } 52 | } 53 | else 54 | yield return proc; 55 | } 56 | 57 | yield return process; 58 | } 59 | 60 | public static IEnumerable GetConflictingApps() 61 | => Process.GetProcesses() 62 | .Where(p => AdbExplorerConst.INCOMPATIBLE_APPS.Contains(p.ProcessName)) 63 | .Select(p => p.ProcessName); 64 | 65 | public static int GetProcessIdFromWindowHandle(HANDLE hwnd) 66 | { 67 | GetWindowThreadProcessId(hwnd, out uint processId); 68 | return (int)processId; 69 | } 70 | 71 | [DllImport("User32.dll", SetLastError = true)] 72 | private static extern uint GetWindowThreadProcessId(HANDLE hWnd, out uint lpdwProcessId); 73 | } 74 | -------------------------------------------------------------------------------- /ADB Explorer/Services/AppInfra/LowLevel/ThemeService.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.ViewModels; 2 | 3 | namespace ADB_Explorer.Services; 4 | 5 | internal class ThemeService : ViewModelBase 6 | { 7 | //https://medium.com/southworks/handling-dark-light-modes-in-wpf-3f89c8a4f2db 8 | 9 | private const string RegistryKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize"; 10 | private const string RegistryValueName = "AppsUseLightTheme"; 11 | private const string QueryPrefix = "SELECT * FROM RegistryValueChangeEvent WHERE Hive = 'HKEY_USERS' AND KeyPath"; 12 | 13 | private AppSettings.AppTheme? windowsTheme; 14 | public AppSettings.AppTheme WindowsTheme 15 | { 16 | get 17 | { 18 | if (windowsTheme is null) 19 | WatchTheme(); 20 | 21 | return windowsTheme.Value; 22 | } 23 | set => Set(ref windowsTheme, value); 24 | } 25 | 26 | public void WatchTheme() 27 | { 28 | var currentUser = WindowsIdentity.GetCurrent(); 29 | string query = $@"{QueryPrefix} = '{currentUser.User.Value}\\{RegistryKeyPath.Replace(@"\", @"\\")}' AND ValueName = '{RegistryValueName}'"; 30 | 31 | // This can fail on Windows 7, but we do not support 32 | ManagementEventWatcher watcher = new(query); 33 | watcher.EventArrived += (sender, args) => 34 | { 35 | WindowsTheme = GetWindowsTheme(); 36 | }; 37 | 38 | watcher.Start(); 39 | 40 | WindowsTheme = GetWindowsTheme(); 41 | } 42 | 43 | private static AppSettings.AppTheme GetWindowsTheme() 44 | { 45 | using var key = Registry.CurrentUser.OpenSubKey(RegistryKeyPath); 46 | return key?.GetValue(RegistryValueName) is < 1 ? AppSettings.AppTheme.dark : AppSettings.AppTheme.light; 47 | } 48 | 49 | public ApplicationTheme AppThemeToActual(AppSettings.AppTheme appTheme) => appTheme switch 50 | { 51 | AppSettings.AppTheme.light => ApplicationTheme.Light, 52 | AppSettings.AppTheme.dark => ApplicationTheme.Dark, 53 | AppSettings.AppTheme.windowsDefault => AppThemeToActual(WindowsTheme), 54 | _ => throw new NotSupportedException(), 55 | }; 56 | 57 | public void SetTheme(AppSettings.AppTheme theme) => SetTheme(AppThemeToActual(theme)); 58 | 59 | public static void SetTheme(ApplicationTheme theme) => App.Current.Dispatcher.Invoke(() => 60 | { 61 | ThemeManager.Current.ApplicationTheme = theme; 62 | 63 | Task.Run(() => 64 | { 65 | var keys = ((ResourceDictionary)Application.Current.Resources["DynamicBrushes"]).Keys; 66 | string[] brushes = new string[keys.Count]; 67 | keys.CopyTo(brushes, 0); 68 | 69 | Parallel.ForEach(brushes, (brush) => SetResourceColor(theme, brush)); 70 | }); 71 | }); 72 | 73 | public static void SetResourceColor(ApplicationTheme theme, string resource) 74 | { 75 | App.Current.Dispatcher.Invoke(() => Application.Current.Resources[resource] = new SolidColorBrush((Color)Application.Current.Resources[$"{theme}{resource}"])); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /ADB Explorer/Services/AppInfra/LowLevel/WindowStyle.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Services; 2 | 3 | public static class WindowStyle 4 | { 5 | public static void SetWindowHidden(HANDLE hwnd) 6 | { 7 | var style = GetWindowLong(hwnd, NativeMethods.WindowIndex.GWL_EXSTYLE) 8 | | NativeMethods.ExtendedWindowStyle.WS_EX_TOOLWINDOW 9 | | NativeMethods.ExtendedWindowStyle.WS_EX_NOACTIVATE; 10 | 11 | SetWindowLong(hwnd, NativeMethods.WindowIndex.GWL_EXSTYLE, style); 12 | } 13 | 14 | [DllImport("user32.dll")] 15 | private static extern NativeMethods.ExtendedWindowStyle GetWindowLong(HANDLE hWnd, NativeMethods.WindowIndex nIndex); 16 | 17 | 18 | [DllImport("user32.dll")] 19 | private static extern int SetWindowLong(HANDLE hWnd, NativeMethods.WindowIndex nIndex, NativeMethods.ExtendedWindowStyle dwNewLong); 20 | } 21 | -------------------------------------------------------------------------------- /ADB Explorer/Services/AppInfra/Network.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | using System.Net.Http; 3 | using System.Net.NetworkInformation; 4 | 5 | namespace ADB_Explorer.Services; 6 | 7 | public static class Network 8 | { 9 | private static readonly HttpClient Client = new(); 10 | 11 | public static async Task GetRequestAsync(Uri url) 12 | { 13 | try 14 | { 15 | return await Client.GetStringAsync(url); 16 | } 17 | catch (Exception) 18 | { 19 | return null; 20 | } 21 | } 22 | 23 | public static async Task LatestAppReleaseAsync() 24 | { 25 | Client.DefaultRequestHeaders.Add("User-Agent", "Unity web player"); 26 | 27 | var response = await GetRequestAsync(Resources.Links.REPO_RELEASES_URL); 28 | if (response is null) 29 | return null; 30 | 31 | JArray json = (JArray)JsonConvert.DeserializeObject(response); 32 | 33 | if (!json.HasValues) 34 | return null; 35 | 36 | var ver = json[0]["tag_name"].ToString().TrimStart('v'); 37 | return new(ver); 38 | } 39 | 40 | public static string GetWsaIp() 41 | { 42 | var wsaInterface = NetworkInterface.GetAllNetworkInterfaces().Where(net => net.Name.Contains(AdbExplorerConst.WSA_INTERFACE_NAME)); 43 | if (!wsaInterface.Any()) 44 | return null; 45 | 46 | var addresses = wsaInterface.First().GetIPProperties().UnicastAddresses; 47 | if (!addresses.Any()) 48 | return null; 49 | 50 | var ipv4 = addresses.Where(add => add.Address.AddressFamily is System.Net.Sockets.AddressFamily.InterNetwork); 51 | if (!ipv4.Any()) 52 | return null; 53 | 54 | return ipv4.First().Address.ToString(); 55 | } 56 | 57 | public static string GetDefaultBrowser() 58 | { 59 | try 60 | { 61 | var browserName = Registry.CurrentUser.OpenSubKey(@"Software\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice"); 62 | var browserCommand = Registry.ClassesRoot.OpenSubKey($@"{browserName.GetValue("Progid")}\shell\open\command").GetValue(null); 63 | return AdbRegEx.RE_EXE_FROM_REG().Match($"{browserCommand}").Value; 64 | } 65 | catch 66 | { 67 | return null; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /ADB Explorer/Services/AppInfra/Storage.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Services; 2 | 3 | public static class Storage 4 | { 5 | public static object RetrieveValue(Enum key) => RetrieveValue(key.ToString()); 6 | 7 | public static object RetrieveValue(string key) 8 | { 9 | return Application.Current.Properties[key]; 10 | } 11 | 12 | public static T Retrieve(string key) 13 | { 14 | return (T)Application.Current.Properties[key]; 15 | } 16 | 17 | public static void StoreValue(Enum key, object value) => StoreValue(key.ToString(), value); 18 | 19 | public static void StoreValue(string key, object value) 20 | { 21 | Application.Current.Properties[key] = value; 22 | } 23 | 24 | public static object RetrieveEnum(Type type) => Application.Current.Properties[type.ToString()]; 25 | public static object RetrieveEnum(string key) => Application.Current.Properties[key]; 26 | 27 | public static T RetrieveEnum(string key = "") => RetrieveEnum(string.IsNullOrEmpty(key) ? typeof(T).ToString() : key) switch 28 | { 29 | string strVal => (T)Enum.Parse(typeof(T), strVal), 30 | long longVal => (T)Enum.ToObject(typeof(T), longVal), 31 | _ => default 32 | }; 33 | 34 | public static void StoreEnum(Enum value) 35 | { 36 | Application.Current.Properties[value.GetType().ToString()] = value; 37 | } 38 | 39 | public static bool? RetrieveBool(Enum value) => RetrieveBool(value.ToString()); 40 | 41 | public static bool? RetrieveBool(string key) 42 | { 43 | return Application.Current?.Properties[key] switch 44 | { 45 | string value when !string.IsNullOrEmpty(value) => bool.Parse(value), 46 | bool val => val, 47 | _ => null 48 | }; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /ADB Explorer/Services/FileOperation/FileArchiveOperation.cs: -------------------------------------------------------------------------------- 1 | //namespace ADB_Explorer.Services; 2 | 3 | //public class FileArchiveOperation : AbstractShellFileOperation 4 | //{ 5 | // // tar -cz /sdcard/Download > /sdcard/Download/arch.tar.gz 6 | //} 7 | -------------------------------------------------------------------------------- /ADB Explorer/Services/FileOperation/FileChangeModifiedOperation.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | using ADB_Explorer.ViewModels; 3 | 4 | namespace ADB_Explorer.Services; 5 | 6 | public class FileChangeModifiedOperation : AbstractShellFileOperation 7 | { 8 | public readonly DateTime NewDate; 9 | 10 | public FileChangeModifiedOperation(FileClass filePath, DateTime newDate, ADBService.AdbDevice adbDevice, Dispatcher dispatcher) 11 | : base(filePath, adbDevice, dispatcher) 12 | { 13 | OperationName = OperationType.Update; 14 | NewDate = newDate; 15 | } 16 | 17 | public override void Start() 18 | { 19 | if (Status == OperationStatus.InProgress) 20 | { 21 | throw new Exception("Cannot start an already active operation!"); 22 | } 23 | 24 | Status = OperationStatus.InProgress; 25 | StatusInfo = new InProgShellProgressViewModel(); 26 | 27 | var operationTask = ADBService.ExecuteVoidShellCommand(Device.ID, 28 | CancelTokenSource.Token, 29 | "touch", 30 | "-m", 31 | "-t", 32 | NewDate.ToString("yyyyMMddHHmm.ss"), 33 | ADBService.EscapeAdbShellString(FilePath.FullPath)); 34 | 35 | operationTask.ContinueWith((t) => 36 | { 37 | if (t.Result == "") 38 | { 39 | Status = OperationStatus.Completed; 40 | StatusInfo = new CompletedShellProgressViewModel(); 41 | } 42 | else 43 | { 44 | Status = OperationStatus.Failed; 45 | StatusInfo = new FailedOpProgressViewModel(t.Result); 46 | } 47 | 48 | }, TaskContinuationOptions.OnlyOnRanToCompletion); 49 | 50 | operationTask.ContinueWith((t) => 51 | { 52 | Status = OperationStatus.Canceled; 53 | StatusInfo = new CanceledOpProgressViewModel(); 54 | }, TaskContinuationOptions.OnlyOnCanceled); 55 | 56 | operationTask.ContinueWith((t) => 57 | { 58 | Status = OperationStatus.Failed; 59 | StatusInfo = new FailedOpProgressViewModel(t.Exception.InnerException.Message); 60 | }, TaskContinuationOptions.OnlyOnFaulted); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /ADB Explorer/Services/FileOperation/FileDeleteOperation.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Converters; 2 | using ADB_Explorer.Models; 3 | using ADB_Explorer.ViewModels; 4 | 5 | namespace ADB_Explorer.Services; 6 | 7 | public class FileDeleteOperation : AbstractShellFileOperation 8 | { 9 | public FileDeleteOperation(Dispatcher dispatcher, ADBService.AdbDevice adbDevice, FileClass path) 10 | : base(path, adbDevice, dispatcher) 11 | { 12 | OperationName = OperationType.Delete; 13 | AltTarget = NavHistory.SpecialLocation.devNull; 14 | } 15 | 16 | public override void Start() 17 | { 18 | if (Status == OperationStatus.InProgress) 19 | { 20 | throw new Exception("Cannot start an already active operation!"); 21 | } 22 | 23 | Status = OperationStatus.InProgress; 24 | StatusInfo = new InProgShellProgressViewModel(); 25 | 26 | var task = ADBService.ExecuteVoidShellCommand(Device.ID, CancelTokenSource.Token, "rm", "-rf", ADBService.EscapeAdbShellString(FilePath.FullPath)); 27 | 28 | task.ContinueWith((t) => 29 | { 30 | if (t.Result == "") 31 | { 32 | Status = OperationStatus.Completed; 33 | StatusInfo = new CompletedShellProgressViewModel(); 34 | } 35 | else 36 | { 37 | Status = OperationStatus.Failed; 38 | var res = AdbRegEx.RE_SHELL_ERROR().Matches(t.Result); 39 | var updates = res.Where(m => m.Success).Select(m => new ShellErrorInfo(m, base.FilePath.FullPath)); 40 | base.AddUpdates(updates); 41 | 42 | var message = updates.Any() ? updates.Last().Message : ""; 43 | if (message.Contains(':')) 44 | message = message.Split(':').Last().TrimStart(); 45 | 46 | var errorString = FileOpStatusConverter.StatusString(typeof(ShellErrorInfo), 47 | failed: Children.Count > 0 ? updates.Count() : -1, 48 | message: message, 49 | total: true); 50 | 51 | StatusInfo = new FailedOpProgressViewModel(errorString); 52 | } 53 | 54 | }, TaskContinuationOptions.OnlyOnRanToCompletion); 55 | 56 | task.ContinueWith((t) => 57 | { 58 | Status = OperationStatus.Canceled; 59 | StatusInfo = new CanceledOpProgressViewModel(); 60 | }, TaskContinuationOptions.OnlyOnCanceled); 61 | 62 | task.ContinueWith((t) => 63 | { 64 | Status = OperationStatus.Failed; 65 | StatusInfo = new FailedOpProgressViewModel(t.Exception.InnerException.Message); 66 | }, TaskContinuationOptions.OnlyOnFaulted); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ADB Explorer/Services/FileOperation/FileRenameOperation.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | using ADB_Explorer.ViewModels; 3 | 4 | namespace ADB_Explorer.Services; 5 | 6 | public class FileRenameOperation : AbstractShellFileOperation 7 | { 8 | public FileRenameOperation(FileClass filePath, string targetPath, ADBService.AdbDevice adbDevice, Dispatcher dispatcher) 9 | : base(new(filePath), adbDevice, dispatcher) 10 | { 11 | OperationName = OperationType.Rename; 12 | 13 | TargetPath = new(targetPath, FilePath.Type); 14 | } 15 | 16 | public override void Start() 17 | { 18 | if (Status == OperationStatus.InProgress) 19 | { 20 | throw new Exception("Cannot start an already active operation!"); 21 | } 22 | 23 | Status = OperationStatus.InProgress; 24 | StatusInfo = new InProgShellProgressViewModel(); 25 | CancelTokenSource = new(); 26 | 27 | var operationTask = ADBService.ExecuteVoidShellCommand(Device.ID, 28 | CancelTokenSource.Token, 29 | "mv", 30 | ADBService.EscapeAdbShellString(FilePath.FullPath), 31 | ADBService.EscapeAdbShellString(TargetPath.FullPath)); 32 | 33 | operationTask.ContinueWith((t) => 34 | { 35 | if (t.Result == "") 36 | { 37 | Status = OperationStatus.Completed; 38 | StatusInfo = new CompletedShellProgressViewModel(); 39 | } 40 | else 41 | { 42 | Status = OperationStatus.Failed; 43 | StatusInfo = new FailedOpProgressViewModel(t.Result); 44 | } 45 | 46 | }, TaskContinuationOptions.OnlyOnRanToCompletion); 47 | 48 | operationTask.ContinueWith((t) => 49 | { 50 | Status = OperationStatus.Canceled; 51 | StatusInfo = new CanceledOpProgressViewModel(); 52 | }, TaskContinuationOptions.OnlyOnCanceled); 53 | 54 | operationTask.ContinueWith((t) => 55 | { 56 | Status = OperationStatus.Failed; 57 | StatusInfo = new FailedOpProgressViewModel(t.Exception.InnerException.Message); 58 | }, TaskContinuationOptions.OnlyOnFaulted); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /ADB Explorer/Styles/FileOpColumn.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 13 | 14 | 19 | 28 | 29 | 30 | 37 | -------------------------------------------------------------------------------- /ADB Explorer/Usings.cs: -------------------------------------------------------------------------------- 1 | global using Microsoft.Win32; 2 | global using Microsoft.WindowsAPICodePack.Dialogs; 3 | global using ModernWpf; 4 | global using ModernWpf.Controls; 5 | global using ModernWpf.Controls.Primitives; 6 | global using Newtonsoft.Json; 7 | global using Newtonsoft.Json.Linq; 8 | global using System; 9 | global using System.Collections.Concurrent; 10 | global using System.Collections.Generic; 11 | global using System.Collections.ObjectModel; 12 | global using System.Collections.Specialized; 13 | global using System.ComponentModel; 14 | global using System.Diagnostics; 15 | global using System.Diagnostics.CodeAnalysis; 16 | global using System.Globalization; 17 | global using System.IO; 18 | global using System.IO.IsolatedStorage; 19 | global using System.Linq; 20 | global using System.Management; 21 | global using System.Reflection; 22 | global using System.Runtime.CompilerServices; 23 | global using System.Runtime.InteropServices; 24 | global using System.Security.Cryptography; 25 | global using System.Security.Principal; 26 | global using System.Text; 27 | global using System.Text.RegularExpressions; 28 | global using System.Threading; 29 | global using System.Threading.Tasks; 30 | global using System.Windows; 31 | global using System.Windows.Controls; 32 | global using System.Windows.Controls.Primitives; 33 | global using System.Windows.Data; 34 | global using System.Windows.Input; 35 | global using System.Windows.Interop; 36 | global using System.Windows.Media; 37 | global using System.Windows.Media.Imaging; 38 | global using System.Windows.Shell; 39 | global using System.Windows.Threading; 40 | 41 | global using HANDLE = nint; 42 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/Device/DeviceAction.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Helpers; 2 | using ADB_Explorer.Services; 3 | 4 | namespace ADB_Explorer.ViewModels; 5 | 6 | public class DeviceAction : BaseAction 7 | { 8 | public string Description { get; } 9 | 10 | public DeviceAction(Func canExecute, Action action, string description = null) 11 | : base(canExecute, action) 12 | { 13 | Description = description; 14 | } 15 | 16 | public override string ToString() 17 | { 18 | return Description; 19 | } 20 | } 21 | 22 | public class RebootCommand : DeviceAction 23 | { 24 | public enum RebootType 25 | { 26 | Title, 27 | Regular, 28 | Bootloader, 29 | Recovery, 30 | Sideload, 31 | SideloadAuto, 32 | } 33 | 34 | public RebootCommand(LogicalDeviceViewModel device, RebootType type) 35 | : base(() => type is not RebootType.Title, 36 | () => Task.Run(() => ADBService.AdbDevice.Reboot(device.ID, RebootParam(type))), 37 | RebootString(type)) 38 | { } 39 | 40 | private static string RebootParam(RebootType type) => type switch 41 | { 42 | RebootType.Regular => "", 43 | RebootType.Bootloader => "bootloader", 44 | RebootType.Recovery => "recovery", 45 | RebootType.Sideload => "sideload", 46 | RebootType.SideloadAuto => "sideload-auto-reboot", 47 | _ => throw new NotSupportedException(), 48 | }; 49 | 50 | private static string RebootString(RebootType type) => type switch 51 | { 52 | RebootType.Title => Strings.Resources.S_DEVICE_REBOOT_TITLE, 53 | RebootType.Regular => Strings.Resources.S_REBOOT_SYSTEM, 54 | RebootType.Bootloader => Strings.Resources.S_REBOOT_BOOTLOADER, 55 | RebootType.Recovery => Strings.Resources.S_RECOVERY_MODE, 56 | RebootType.Sideload => Strings.Resources.S_REBOOT_SIDELOAD, 57 | RebootType.SideloadAuto => Strings.Resources.S_REBOOT_SIDELOAD_AUTO, 58 | _ => throw new NotSupportedException(), 59 | }; 60 | } 61 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/Device/HistoryDeviceViewModel.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Helpers; 2 | using ADB_Explorer.Models; 3 | 4 | namespace ADB_Explorer.ViewModels; 5 | 6 | public class HistoryDeviceViewModel : NewDeviceViewModel 7 | { 8 | private HistoryDevice device; 9 | protected new HistoryDevice Device 10 | { 11 | get => device; 12 | set => Set(ref device, value); 13 | } 14 | 15 | #region Read only properties 16 | 17 | public string DeviceName => Device.DeviceName; 18 | 19 | public override string Tooltip => Strings.Resources.S_DEVICE_SAVED; 20 | 21 | public override bool DeviceExists => false; 22 | 23 | public bool IsDeviceNameValid => !string.IsNullOrEmpty(DeviceName); 24 | 25 | #endregion 26 | 27 | public DeviceAction RemoveCommand { get; } 28 | 29 | public HistoryDeviceViewModel(HistoryDevice device) : base(device) 30 | { 31 | Device = device; 32 | 33 | RemoveCommand = DeviceHelper.RemoveDeviceCommand(this); 34 | } 35 | 36 | public static HistoryDeviceViewModel New(NewDeviceViewModel device) 37 | { 38 | return new(new HistoryDevice() 39 | { 40 | IpAddress = device.IsIpAddressValid ? device.IpAddress : null, 41 | HostName = device.IsIpAddressValid ? null : device.HostName, 42 | ConnectPort = device.ConnectPort 43 | }); 44 | } 45 | 46 | public static HistoryDeviceViewModel New(StorageDevice device) 47 | { 48 | HistoryDeviceViewModel historyDevice = new(new HistoryDevice() 49 | { 50 | DeviceName = device.DeviceName, 51 | IpAddress = device.IpAddress, 52 | ConnectPort = device.ConnectPort 53 | }); 54 | 55 | if (!historyDevice.IsIpAddressValid) 56 | { 57 | historyDevice.HostName = historyDevice.IpAddress; 58 | } 59 | 60 | return historyDevice; 61 | } 62 | 63 | public StorageDevice GetStorage() => new(this); 64 | 65 | public bool SetDeviceName(string name) 66 | { 67 | if (DeviceName != name) 68 | { 69 | Device.DeviceName = name; 70 | 71 | return true; 72 | } 73 | 74 | return false; 75 | } 76 | } 77 | 78 | public class StorageDevice 79 | { 80 | public string DeviceName { get; private set; } 81 | public string IpAddress { get; private set; } 82 | public string ConnectPort { get; private set; } 83 | 84 | public StorageDevice(HistoryDeviceViewModel device) 85 | { 86 | DeviceName = device.DeviceName; 87 | IpAddress = device.IsIpAddressValid ? device.IpAddress : device.HostName; 88 | ConnectPort = device.ConnectPort; 89 | } 90 | 91 | [JsonConstructor] 92 | public StorageDevice(string deviceName, string ipAddress, string connectPort) 93 | { 94 | DeviceName = deviceName; 95 | IpAddress = ipAddress; 96 | ConnectPort = connectPort; 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/Device/WsaPkgDeviceViewModel.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Helpers; 2 | using ADB_Explorer.Models; 3 | 4 | namespace ADB_Explorer.ViewModels; 5 | 6 | public class WsaPkgDeviceViewModel : DeviceViewModel 7 | { 8 | private WsaPkgDevice device; 9 | protected new WsaPkgDevice Device 10 | { 11 | get => device; 12 | set => Set(ref device, value); 13 | } 14 | 15 | public DateTime LastLaunch => Device.LastLaunch; 16 | 17 | public DeviceAction LaunchWsaCommand { get; } 18 | 19 | public override string Tooltip => Strings.Resources.S_WSA_PKG_TIP; 20 | 21 | public override bool DeviceExists => false; 22 | 23 | public WsaPkgDeviceViewModel(WsaPkgDevice device) : base(device) 24 | { 25 | Device = device; 26 | 27 | LaunchWsaCommand = DeviceHelper.LaunchWsa(this); 28 | } 29 | 30 | public void SetLastLaunch(DateTime? newDate = null) 31 | { 32 | Device.LastLaunch = newDate is null ? DateTime.Now : newDate.Value; 33 | OnPropertyChanged(nameof(LastLaunch)); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/Drive/DriveViewModel.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Helpers; 2 | using ADB_Explorer.Models; 3 | using ADB_Explorer.Services; 4 | 5 | namespace ADB_Explorer.ViewModels; 6 | 7 | public class DriveViewModel : AbstractDrive 8 | { 9 | #region Full properties 10 | 11 | private Drive drive; 12 | public Drive Drive 13 | { 14 | get => drive; 15 | set => Set(ref drive, value); 16 | } 17 | 18 | private bool driveSelected = false; 19 | public bool DriveSelected 20 | { 21 | get => driveSelected; 22 | set => Set(ref driveSelected, value); 23 | } 24 | 25 | private bool driveEnabled = true; 26 | public bool DriveEnabled 27 | { 28 | get => driveEnabled; 29 | protected set => Set(ref driveEnabled, value); 30 | } 31 | 32 | #endregion 33 | 34 | #region Read only properties 35 | 36 | public string Path => Drive.Path; 37 | public new DriveType Type => Drive.Type; 38 | public bool IsFUSE => Drive.IsFUSE; 39 | 40 | public new string DisplayName => Drive.DisplayName; 41 | 42 | public string DriveIcon => Type switch 43 | { 44 | DriveType.Root => "\uF259", 45 | DriveType.Internal => "\uEDA2", 46 | DriveType.Expansion => "\uE7F1", 47 | DriveType.External => "\uE88E", 48 | DriveType.Unknown => null, 49 | DriveType.Emulated => "\uEDA2", 50 | DriveType.Trash => "\uE74D", 51 | DriveType.Temp => "\uE912", 52 | DriveType.Package => "\uE7B8", 53 | _ => throw new NotImplementedException(), 54 | }; 55 | 56 | #endregion 57 | 58 | #region Commands 59 | 60 | public BaseAction BrowseCommand { get; private set; } 61 | public BaseAction SelectCommand { get; private set; } 62 | 63 | #endregion 64 | 65 | public DriveViewModel(Drive drive) 66 | { 67 | Drive = drive; 68 | 69 | BrowseCommand = new(() => true, () => Data.RuntimeSettings.BrowseDrive = this); 70 | SelectCommand = new(() => true, () => DriveSelected = true); 71 | 72 | Data.RuntimeSettings.PropertyChanged += RuntimeSettings_PropertyChanged; 73 | } 74 | 75 | private void RuntimeSettings_PropertyChanged(object sender, PropertyChangedEventArgs e) 76 | { 77 | if (e.PropertyName == nameof(AppRuntimeSettings.CollapseDrives) && Data.RuntimeSettings.CollapseDrives) 78 | { 79 | DriveSelected = false; 80 | } 81 | } 82 | 83 | public void SetType(DriveType type) 84 | { 85 | if (Drive.Type != type) 86 | { 87 | Drive.Type = type; 88 | OnPropertyChanged(nameof(Type)); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/Drive/LogicalDriveViewModel.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | 3 | namespace ADB_Explorer.ViewModels; 4 | 5 | class LogicalDriveViewModel : DriveViewModel 6 | { 7 | private LogicalDrive drive; 8 | protected new LogicalDrive Drive 9 | { 10 | get => drive; 11 | set => Set(ref drive, value); 12 | } 13 | 14 | public string Size => Drive.Size; 15 | public string Used => Drive.Used; 16 | public string Available => Drive.Available; 17 | public sbyte UsageP => Drive.UsageP; 18 | 19 | public bool UsageWarning => UsageP >= AdbExplorerConst.DRIVE_WARNING; 20 | public string ID => Drive.ID; 21 | 22 | 23 | public LogicalDriveViewModel(LogicalDrive drive) : base(drive) 24 | { 25 | Drive = drive; 26 | } 27 | 28 | public void UpdateDrive(LogicalDrive other) 29 | { 30 | if (Drive.Size != other.Size) 31 | { 32 | Drive.Size = other.Size; 33 | OnPropertyChanged(nameof(Size)); 34 | } 35 | 36 | if (Drive.Used != other.Used) 37 | { 38 | Drive.Used = other.Used; 39 | OnPropertyChanged(nameof(Used)); 40 | } 41 | 42 | if (Drive.Available != other.Available) 43 | { 44 | Drive.Available = other.Available; 45 | OnPropertyChanged(nameof(Available)); 46 | } 47 | 48 | if (Drive.UsageP != other.UsageP) 49 | { 50 | Drive.UsageP = other.UsageP; 51 | OnPropertyChanged(nameof(UsageP)); 52 | } 53 | 54 | if (Drive.FileSystem != other.FileSystem) 55 | { 56 | Drive.FileSystem = other.FileSystem; 57 | OnPropertyChanged(nameof(IsFUSE)); 58 | } 59 | } 60 | 61 | public void SetExtension(bool isMMC = true) => SetType(isMMC ? DriveType.Expansion : DriveType.External); 62 | 63 | public override string ToString() => DisplayName is null ? ID : DisplayName; 64 | } 65 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/Drive/VirtualDriveViewModel.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | 3 | namespace ADB_Explorer.ViewModels; 4 | 5 | class VirtualDriveViewModel : DriveViewModel 6 | { 7 | private VirtualDrive drive; 8 | protected new VirtualDrive Drive 9 | { 10 | get => drive; 11 | set => Set(ref drive, value); 12 | } 13 | 14 | public long? ItemsCount => Drive.ItemsCount; 15 | 16 | 17 | public VirtualDriveViewModel(VirtualDrive drive) : base(drive) 18 | { 19 | Drive = drive; 20 | } 21 | 22 | public void SetItemsCount(int? count) => SetItemsCount((long?)count); 23 | 24 | public void SetItemsCount(long? newCount) 25 | { 26 | if (Drive.ItemsCount != newCount) 27 | { 28 | Drive.ItemsCount = newCount; 29 | OnPropertyChanged(nameof(ItemsCount)); 30 | } 31 | 32 | DriveEnabled = ItemsCount != -1; 33 | } 34 | 35 | public override string ToString() => $"{Type}"; 36 | } 37 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/FileOp/CanceledOpProgressViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.ViewModels; 2 | 3 | internal class CanceledOpProgressViewModel : FileOpProgressViewModel 4 | { 5 | public CanceledOpProgressViewModel() : base(Services.FileOperation.OperationStatus.Canceled) 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/FileOp/CompletedShellProgressViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.ViewModels; 2 | 3 | internal class CompletedShellProgressViewModel : FileOpProgressViewModel 4 | { 5 | public string Message { get; } 6 | 7 | public CompletedShellProgressViewModel(string message = "Completed") : base(Services.FileOperation.OperationStatus.Completed) 8 | { 9 | Message = message; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/FileOp/CompletedSyncProgressViewModel.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Converters; 2 | using ADB_Explorer.Services; 3 | 4 | namespace ADB_Explorer.ViewModels; 5 | 6 | public class CompletedSyncProgressViewModel : FileOpProgressViewModel 7 | { 8 | private readonly AdbSyncStatsInfo adbInfo; 9 | 10 | public CompletedSyncProgressViewModel(AdbSyncStatsInfo adbInfo) : base(FileOperation.OperationStatus.Completed) 11 | { 12 | this.adbInfo = adbInfo; 13 | } 14 | 15 | public UInt64 FilesTransferred => adbInfo.FilesTransferred; 16 | 17 | public UInt64 FilesSkipped => adbInfo.FilesSkipped; 18 | 19 | public decimal? AverageRateMBps => adbInfo.AverageRate; 20 | 21 | public UInt64? TotalBytes => adbInfo.TotalBytes; 22 | 23 | public decimal? TotalSeconds => adbInfo.TotalTime; 24 | 25 | public int FileCountCompletedRate => (int)((float)FilesTransferred / (FilesTransferred + FilesSkipped) * 100.0); 26 | 27 | public string FileCountCompletedString => $"{FilesTransferred} of {FilesTransferred + FilesSkipped}"; 28 | 29 | public string AverageRateString => AverageRateMBps.HasValue ? $"{UnitConverter.ToSize((UInt64)(AverageRateMBps.Value * 1024 * 1024))}/s" : string.Empty; 30 | 31 | public string TotalSize => TotalBytes.HasValue ? UnitConverter.ToSize(TotalBytes.Value) : string.Empty; 32 | 33 | public string TotalTime => TotalSeconds.HasValue ? UnitConverter.ToTime(TotalSeconds.Value) : string.Empty; 34 | } 35 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/FileOp/FailedOpProgressViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.ViewModels; 2 | 3 | public class FailedOpProgressViewModel : FileOpProgressViewModel 4 | { 5 | public string Error { get; } 6 | 7 | public FailedOpProgressViewModel(string error) : base(Services.FileOperation.OperationStatus.Failed) 8 | { 9 | error = error.TrimEnd('\r', '\n'); 10 | 11 | var firstDoubleSlash = error.StartsWith(@"\\"); 12 | error = string.Join(@"\", error.Split(@"\\")); 13 | if (firstDoubleSlash) 14 | error = @"\\" + error; 15 | 16 | Error = error; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/FileOp/FileOpProgressViewModel.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Models; 2 | 3 | namespace ADB_Explorer.ViewModels; 4 | 5 | public abstract class FileOpProgressViewModel : ViewModelBase 6 | { 7 | public Services.FileOperation.OperationStatus Status { get; } 8 | 9 | private FileOpFilter.FilterType FilterType => Status switch 10 | { 11 | Services.FileOperation.OperationStatus.Waiting => FileOpFilter.FilterType.Pending, 12 | Services.FileOperation.OperationStatus.InProgress => FileOpFilter.FilterType.Running, 13 | Services.FileOperation.OperationStatus.Completed => FileOpFilter.FilterType.Completed, 14 | Services.FileOperation.OperationStatus.Canceled => FileOpFilter.FilterType.Canceled, 15 | Services.FileOperation.OperationStatus.Failed => FileOpFilter.FilterType.Failed, 16 | _ => throw new NotSupportedException(), 17 | }; 18 | 19 | public string Name => AdbExplorerConst.FILE_OP_NAMES[FilterType]; 20 | 21 | private bool isValidationInProgress = false; 22 | public bool IsValidationInProgress 23 | { 24 | get => isValidationInProgress; 25 | set => Set(ref isValidationInProgress, value); 26 | } 27 | 28 | public FileOpProgressViewModel(Services.FileOperation.OperationStatus status) 29 | { 30 | Status = status; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/FileOp/InProgShellProgressViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.ViewModels; 2 | 3 | internal class InProgShellProgressViewModel : FileOpProgressViewModel 4 | { 5 | public string CurrentFileName => null; 6 | 7 | public string CurrentFileNameWithoutExtension => null; 8 | 9 | public InProgShellProgressViewModel() : base(Services.FileOperation.OperationStatus.InProgress) 10 | { 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/FileOp/InProgSyncProgressViewModel.cs: -------------------------------------------------------------------------------- 1 | using ADB_Explorer.Converters; 2 | using ADB_Explorer.Services; 3 | 4 | namespace ADB_Explorer.ViewModels; 5 | 6 | internal class InProgSyncProgressViewModel : FileOpProgressViewModel 7 | { 8 | private readonly AdbSyncProgressInfo adbInfo = null; 9 | 10 | public InProgSyncProgressViewModel() : base(FileOperation.OperationStatus.InProgress) 11 | { 12 | 13 | } 14 | 15 | public InProgSyncProgressViewModel(AdbSyncProgressInfo adbInfo) : this() 16 | { 17 | this.adbInfo = adbInfo; 18 | } 19 | 20 | public int? TotalPercentage => adbInfo?.TotalPercentage; 21 | 22 | public ulong? TotalBytesTransferred => adbInfo?.TotalBytesTransferred; 23 | 24 | public string TotalBytes => TotalBytesTransferred?.ToSize(); 25 | 26 | public int? CurrentFilePercentage => adbInfo?.CurrentFilePercentage; 27 | 28 | public UInt64? CurrentFileBytesTransferred => adbInfo?.CurrentFileBytesTransferred; 29 | 30 | public string CurrentFilePath => adbInfo?.AndroidPath; 31 | 32 | public string CurrentFileName => Path.GetFileName(CurrentFilePath); 33 | 34 | public string CurrentFileNameWithoutExtension => Path.GetFileNameWithoutExtension(CurrentFilePath); 35 | } 36 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/FileOp/WaitingOpProgressViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.ViewModels; 2 | 3 | internal class WaitingOpProgressViewModel : FileOpProgressViewModel 4 | { 5 | public WaitingOpProgressViewModel() : base(Services.FileOperation.OperationStatus.Waiting) 6 | { 7 | 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ADB Explorer/ViewModels/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.ViewModels; 2 | 3 | public abstract class ViewModelBase : INotifyPropertyChanged 4 | { 5 | public event PropertyChangedEventHandler PropertyChanged; 6 | 7 | protected virtual bool Set(ref T storage, T value, [CallerMemberName] string propertyName = null) 8 | { 9 | if (Equals(storage, value)) 10 | { 11 | return false; 12 | } 13 | 14 | storage = value; 15 | OnPropertyChanged(propertyName); 16 | 17 | return true; 18 | } 19 | 20 | protected void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 21 | 22 | public static void ExecuteInDispatcher(Action action) 23 | { 24 | // When running unit tests, App.Current is null 25 | if (App.Current is null) 26 | action(); 27 | else 28 | App.Current.Dispatcher.Invoke(action); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ADB Explorer/Views/Battery/CompactBatteryControl.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 22 | 23 | 28 | 29 | -------------------------------------------------------------------------------- /ADB Explorer/Views/Battery/CompactBatteryControl.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Views; 2 | 3 | /// 4 | /// Interaction logic for CompactBatteryControl.xaml 5 | /// 6 | public partial class CompactBatteryControl : UserControl 7 | { 8 | public CompactBatteryControl() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ADB Explorer/Views/Battery/DetailedBatteryControl.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Views; 2 | 3 | /// 4 | /// Interaction logic for DetailedBatteryControl.xaml 5 | /// 6 | public partial class DetailedBatteryControl : UserControl 7 | { 8 | public DetailedBatteryControl() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ADB Explorer/Views/Device/HistoryDeviceControl.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Views; 2 | 3 | /// 4 | /// Interaction logic for HistoryDeviceControl.xaml 5 | /// 6 | public partial class HistoryDeviceControl : UserControl 7 | { 8 | public HistoryDeviceControl() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ADB Explorer/Views/Device/LogicalDeviceControl.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Views; 2 | 3 | /// 4 | /// Interaction logic for LogicalDeviceControl.xaml 5 | /// 6 | public partial class LogicalDeviceControl : UserControl 7 | { 8 | public LogicalDeviceControl() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ADB Explorer/Views/Device/NewDeviceControl.xaml.cs: -------------------------------------------------------------------------------- 1 | namespace ADB_Explorer.Views; 2 | 3 | /// 4 | /// Interaction logic for NewDeviceControl.xaml 5 | /// 6 | public partial class NewDeviceControl : UserControl 7 | { 8 | public NewDeviceControl() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /ADB Explorer/Views/Device/ServiceDeviceControl.xaml: -------------------------------------------------------------------------------- 1 |  13 | 14 | 15 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 48 | 49 | 50 | 51 |