1
- // Copyright (c) Files Community
1
+ // Copyright (c) Files Community
2
2
// Licensed under the MIT License.
3
3
4
4
using Files . Shared . Helpers ;
5
+ using Microsoft . UI . Windowing ;
6
+ using Microsoft . UI . Xaml . Controls ;
5
7
using System . IO ;
8
+ using System . Security . Cryptography ;
6
9
using System . Windows . Input ;
7
10
8
11
namespace Files . App . ViewModels . Properties
9
12
{
10
13
public sealed partial class HashesViewModel : ObservableObject , IDisposable
11
14
{
15
+ private ICommonDialogService CommonDialogService { get ; } = Ioc . Default . GetRequiredService < ICommonDialogService > ( ) ;
12
16
private IUserSettingsService UserSettingsService { get ; } = Ioc . Default . GetService < IUserSettingsService > ( ) ! ;
13
17
18
+ private readonly AppWindow _appWindow ;
19
+
14
20
private HashInfoItem _selectedItem ;
15
21
public HashInfoItem SelectedItem
16
22
{
@@ -23,16 +29,48 @@ public HashInfoItem SelectedItem
23
29
public Dictionary < string , bool > ShowHashes { get ; private set ; }
24
30
25
31
public ICommand ToggleIsEnabledCommand { get ; private set ; }
32
+ public ICommand CompareFileCommand { get ; private set ; }
26
33
27
34
private ListedItem _item ;
28
35
29
36
private CancellationTokenSource _cancellationTokenSource ;
30
37
31
- public HashesViewModel ( ListedItem item )
38
+ private string _hashInput ;
39
+ public string HashInput
40
+ {
41
+ get => _hashInput ;
42
+ set
43
+ {
44
+ SetProperty ( ref _hashInput , value ) ;
45
+
46
+ OnHashInputTextChanged ( ) ;
47
+ OnPropertyChanged ( nameof ( IsInfoBarOpen ) ) ;
48
+ }
49
+ }
50
+
51
+ private InfoBarSeverity _infoBarSeverity ;
52
+ public InfoBarSeverity InfoBarSeverity
53
+ {
54
+ get => _infoBarSeverity ;
55
+ set => SetProperty ( ref _infoBarSeverity , value ) ;
56
+ }
57
+
58
+ private string _infoBarTitle ;
59
+ public string InfoBarTitle
60
+ {
61
+ get => _infoBarTitle ;
62
+ set => SetProperty ( ref _infoBarTitle , value ) ;
63
+ }
64
+
65
+ public bool IsInfoBarOpen
66
+ => ! string . IsNullOrEmpty ( HashInput ) ;
67
+
68
+ public HashesViewModel ( ListedItem item , AppWindow appWindow )
32
69
{
33
70
ToggleIsEnabledCommand = new RelayCommand < string > ( ToggleIsEnabled ) ;
34
71
35
72
_item = item ;
73
+ _appWindow = appWindow ;
36
74
_cancellationTokenSource = new ( ) ;
37
75
38
76
Hashes =
@@ -55,6 +93,8 @@ public HashesViewModel(ListedItem item)
55
93
ShowHashes . TryAdd ( "SHA512" , false ) ;
56
94
57
95
Hashes . Where ( x => ShowHashes [ x . Algorithm ] ) . ForEach ( x => ToggleIsEnabledCommand . Execute ( x . Algorithm ) ) ;
96
+
97
+ CompareFileCommand = new RelayCommand ( async ( ) => await OnCompareFileAsync ( ) ) ;
58
98
}
59
99
60
100
private void ToggleIsEnabled ( string ? algorithm )
@@ -71,7 +111,7 @@ private void ToggleIsEnabled(string? algorithm)
71
111
// Don't calculate hashes for online files
72
112
if ( _item . SyncStatusUI . SyncStatus is CloudDriveSyncStatus . FileOnline or CloudDriveSyncStatus . FolderOnline )
73
113
{
74
- hashInfoItem . HashValue = " CalculationOnlineFileHashError" . GetLocalizedResource ( ) ;
114
+ hashInfoItem . HashValue = Strings . CalculationOnlineFileHashError . GetLocalizedResource ( ) ;
75
115
return ;
76
116
}
77
117
@@ -106,11 +146,11 @@ private void ToggleIsEnabled(string? algorithm)
106
146
catch ( IOException )
107
147
{
108
148
// File is currently open
109
- hashInfoItem . HashValue = " CalculationErrorFileIsOpen" . GetLocalizedResource ( ) ;
149
+ hashInfoItem . HashValue = Strings . CalculationErrorFileIsOpen . GetLocalizedResource ( ) ;
110
150
}
111
151
catch ( Exception )
112
152
{
113
- hashInfoItem . HashValue = " CalculationError" . GetLocalizedResource ( ) ;
153
+ hashInfoItem . HashValue = Strings . CalculationError . GetLocalizedResource ( ) ;
114
154
}
115
155
finally
116
156
{
@@ -120,6 +160,51 @@ private void ToggleIsEnabled(string? algorithm)
120
160
}
121
161
}
122
162
163
+ public string FindMatchingAlgorithm ( string hash )
164
+ {
165
+ if ( string . IsNullOrEmpty ( hash ) )
166
+ return string . Empty ;
167
+
168
+ return Hashes . FirstOrDefault ( h => h . HashValue ? . Equals ( hash , StringComparison . OrdinalIgnoreCase ) == true ) ? . Algorithm ?? string . Empty ;
169
+ }
170
+
171
+ public async Task < string > CalculateFileHashAsync ( string filePath )
172
+ {
173
+ using var stream = File . OpenRead ( filePath ) ;
174
+ using var md5 = MD5 . Create ( ) ;
175
+ var hash = await Task . Run ( ( ) => md5 . ComputeHash ( stream ) ) ;
176
+ return BitConverter . ToString ( hash ) . Replace ( "-" , "" ) . ToLowerInvariant ( ) ;
177
+ }
178
+
179
+ private void OnHashInputTextChanged ( )
180
+ {
181
+ string matchingAlgorithm = FindMatchingAlgorithm ( HashInput ) ;
182
+
183
+ InfoBarSeverity = string . IsNullOrEmpty ( matchingAlgorithm )
184
+ ? InfoBarSeverity . Error
185
+ : InfoBarSeverity . Success ;
186
+
187
+ InfoBarTitle = string . IsNullOrEmpty ( matchingAlgorithm )
188
+ ? Strings . HashesDoNotMatch . GetLocalizedResource ( )
189
+ : string . Format ( Strings . HashesMatch . GetLocalizedResource ( ) , matchingAlgorithm ) ;
190
+ }
191
+
192
+ private async Task OnCompareFileAsync ( )
193
+ {
194
+ var hWnd = Microsoft . UI . Win32Interop . GetWindowFromWindowId ( _appWindow . Id ) ;
195
+
196
+ var result = CommonDialogService . Open_FileOpenDialog (
197
+ hWnd ,
198
+ false ,
199
+ [ ] ,
200
+ Environment . SpecialFolder . Desktop ,
201
+ out var filePath ) ;
202
+
203
+ HashInput = result && filePath != null
204
+ ? await CalculateFileHashAsync ( filePath )
205
+ : string . Empty ;
206
+ }
207
+
123
208
public void Dispose ( )
124
209
{
125
210
_cancellationTokenSource . Cancel ( ) ;
0 commit comments