-
Notifications
You must be signed in to change notification settings - Fork 1
/
QuietUnrarAppDelegate.m
300 lines (241 loc) · 11.1 KB
/
QuietUnrarAppDelegate.m
1
2
3
4
5
6
7
8
9
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
//
// QuietUnrarAppDelegate.m
// QuietUnrar
//
// Created by Robert McGovern on 2009/09/06.
// Copyright 2009 Tarasis. All rights reserved.
//
#import <Carbon/Carbon.h>
#import <Cocoa/Cocoa.h>
#import <UserNotifications/UserNotifications.h>
#import "QuietUnrarAppDelegate.h"
#import "TDNUnarchiver.h"
#import "TDNUserDefaults.h"
#import "TDNPreferencesWindowController.h"
@interface QuietUnrarAppDelegate ()
@property TDNUnarchiver * unarchiver;
@property TDNUserDefaults * userDefaults;
@property NSStatusItem * statusBarItem;
@property NSArray * arrayOfFilesToProcess;
@end
#pragma mark
@implementation QuietUnrarAppDelegate
@synthesize window, passwordView, passwordField, preferencesWindowController, unarchiver, userDefaults, statusBarItem, arrayOfFilesToProcess;
BOOL appRunning = NO;
- (void) applicationWillFinishLaunching:(NSNotification *)notification {
NSLog(@"applicationWillFinishLaunching");
// The following is used to determine is the left or right shift keys were depressed
// as the application was launched. Could be used to display a gui on Application start.
KeyMap map;
GetKeys(map);
if (KEYMAP_GET(map, kVKC_Shift) || KEYMAP_GET(map, kVKC_rShift))
NSLog(@"Shift or Right Shift");
userDefaults = [TDNUserDefaults sharedInstance];
if (userDefaults.hideDock) {
[self hideDockIcon:TRUE];
}
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
NSLog(@"applicationDidFinishLaunching");
[self requestUserPermissionForNotifications];
if (arrayOfFilesToProcess == nil || arrayOfFilesToProcess.count == 0) {
preferencesWindowController = [[TDNPreferencesWindowController alloc] init];
preferencesWindowController.quietUnrar = self;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self->preferencesWindowController.window makeKeyAndOrderFront:self];
[NSApp activateIgnoringOtherApps:YES];
});
appRunning = YES;
} else {
unarchiver = [[TDNUnarchiver alloc] init];
unarchiver.quietUnrar = self;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
for (NSString * filename in self->arrayOfFilesToProcess) {
BOOL extracted = [self->unarchiver extractArchiveWithFilename:filename];
if (extracted) {
// post notification based on user preference
if (self->userDefaults.showNotification && self->userDefaults.notificationsAllowed) { // if show notification + permission granted ...
[self postNotificationUncompressedFile:filename];
// maybe don't want to spam lots of notifications if unarchiving a lot of archives
}
}
}
[[NSApplication sharedApplication] terminate:self];
});
}
}
// Call one at a time for each file selected when app is run
// This is seemingly never called, even when only selecting a single file.
- (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename {
//NSLog(@"openFile: %@", filename);
//[self extractRarWith:filename];
// [self extractRarUsingUnrarKitWithFilename:filename];
// Always return YES even if there is an error to avoid dialog indicating unable to
// handle files of type RAR if the archive is corrupt or part of it is missing
return YES;
}
- (void)application:(NSApplication *)theApplication openFiles:(NSArray *) arrayOfFilenames {
NSLog(@"openFiles: %@", arrayOfFilenames);
arrayOfFilesToProcess = arrayOfFilenames;
if (appRunning) {
if (!unarchiver) {
unarchiver = [[TDNUnarchiver alloc] init];
unarchiver.quietUnrar = self;
}
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
for (NSString * filename in self->arrayOfFilesToProcess) {
BOOL extracted = [self->unarchiver extractArchiveWithFilename:filename];
if (extracted) {
// post notification based on user preference
if (self->userDefaults.showNotification && self->userDefaults.notificationsAllowed) { // if show notification + permission granted ...
[self postNotificationUncompressedFile:filename];
// maybe don't want to spam lots of notifications if unarchiving a lot of archives
}
}
}
// If we're in the foreground, hide and return focus to whereever
// has side effect of making the preferences window disappear after a short time
// which is not what I want.
//
// I'd like to use the frontmostApplication trick to store old app, and restore
// focus to it, but if app already running it appears to be my app, even though
// I did the open from Finder. Hmm
// NSRunningApplication * oldApp = [[NSWorkspace sharedWorkspace] frontmostApplication];
// do activity
// [_oldApp activateWithOptions:NSApplicationActivateIgnoringOtherApps];
dispatch_async(dispatch_get_main_queue(), ^{
[NSApp hide:self];
});
});
}
}
- (void)applicationWillBecomeActive:(NSNotification *)notification {
//no use, if app is already running when I try and openfile it is my apps bundle id
NSLog(@"%@", [NSWorkspace sharedWorkspace].frontmostApplication.bundleIdentifier);
}
- (BOOL)application:(id)sender openFileWithoutUI:(NSString *)filename {
NSLog(@"called openFileWithoutUI %@", filename);
return YES;
}
- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender {
// Possibly not the behaviour wanted
if (userDefaults.hideDock) {
NSLog(@"applicationShouldTerminateAfterLastWindowClosed -- NO");
return NO;
} else {
NSLog(@"applicationShouldTerminateAfterLastWindowClosed -- YES");
return YES;
}
}
#pragma mark UI Methods
// Presents a dialog to the user allowing them to Skip a file or overwrite an existing version
// returns YES or NO
- (BOOL) shouldFileBeReplaced:(NSString *) filename {
BOOL result = NO;
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:@"Overwrite"];
NSButton * skipButton = [alert addButtonWithTitle:@"Skip"];
skipButton.keyEquivalent = @"\e";
alert.messageText = [NSString stringWithFormat:@"Overwrite %@?", filename];
alert.informativeText = [NSString stringWithFormat:@"The file %@ already exists. Do you wish to extract it again, overwriting the original file?", filename];
alert.alertStyle = NSAlertStyleWarning;
if ([alert runModal] == NSAlertFirstButtonReturn) {
result = YES;
}
return result;
}
// Indicate to the user that part of the RAR volume is missing.
- (void) alertUserOfMissing:(const char *) volume {
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:@"OK"];
alert.messageText = [NSString stringWithFormat:@"Archive part %s is missing.", volume];
alert.informativeText = @"Unable to extract all files from RAR archive as part of it is missing";
alert.alertStyle = NSAlertStyleCritical;
[alert runModal];
}
// Creates a dialog with a custom view with a NSSecureTextField which is displayed
// to the user so they can provide a password. Returns the entered password or nil
- (NSString *) requestArchivePassword {
if (!passwordView) {
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
[bundle loadNibNamed:@"PasswordView" owner:self topLevelObjects: nil];
} else {
passwordField.stringValue = @"";
}
NSString * password = nil;
NSAlert *alert = [[NSAlert alloc] init];
[alert addButtonWithTitle:@"OK"];
[alert addButtonWithTitle:@"Cancel"];
alert.messageText = @"Archive Requires a password";
alert.informativeText = @"To extract the contents of this archive a password is required.";
alert.accessoryView = passwordView;
alert.alertStyle = NSAlertStyleWarning;
if ([alert runModal] == NSAlertFirstButtonReturn) {
password = passwordField.stringValue;
}
return password;
}
- (void) hideDockIcon: (BOOL) hide {
BOOL result;
if (hide) {
NSLog(@"Setting Policy to Accessory");
result = [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
NSLog(@"Result of setting ActivationPolicy %d", result);
[self showStatusBarItem:TRUE];
} else {
NSLog(@"Setting Policy to Regular");
result = [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
NSLog(@"Result of setting ActivationPolicy %d", result);
[self showStatusBarItem:FALSE];
}
}
- (void) showStatusBarItem: (BOOL) show {
if (show) {
statusBarItem = [NSStatusBar.systemStatusBar statusItemWithLength:NSVariableStatusItemLength];
statusBarItem.button.title = @"🎫"; //RMCG change to something more appropriate
statusBarItem.menu = [self makeStatusBarMenu];
} else {
[NSStatusBar.systemStatusBar removeStatusItem:statusBarItem];
}
}
- (NSMenu *) makeStatusBarMenu {
NSMenu * statusBarMenu = [[NSMenu alloc] init];
statusBarMenu.title = @"QuietUnarchiver Menu";
NSMenuItem * preferencesMenuItem = [[NSMenuItem alloc] initWithTitle:@"Show Preferences" action:@selector(showPreferencesWindow) keyEquivalent:@""];
[statusBarMenu addItem:preferencesMenuItem];
// NSMenuItem * showDockItem = [[NSMenuItem alloc] initWithTitle:@"Show Dock " action:@selector(showPreferencesWindow) keyEquivalent:@""];
// [statusBarMenu addItem:showDockItem];
//
NSMenuItem * quitMenuItem = [[NSMenuItem alloc] initWithTitle:@"Quit QuietUnarchiver" action:@selector(quit) keyEquivalent:@""];
[statusBarMenu addItem:quitMenuItem];
return statusBarMenu;
}
- (void) showPreferencesWindow {
if (preferencesWindowController == nil) {
preferencesWindowController = [[TDNPreferencesWindowController alloc] init];
preferencesWindowController.quietUnrar = self;
}
[preferencesWindowController.window makeKeyAndOrderFront:self];
[NSApp activateIgnoringOtherApps:YES];
}
- (void) quit {
[[NSApplication sharedApplication] terminate:self];
}
#pragma mark "Notifications"
- (void) requestUserPermissionForNotifications {
UNUserNotificationCenter * center = [UNUserNotificationCenter currentNotificationCenter];
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
completionHandler:^(BOOL granted, NSError * _Nullable error) {
// Enable or disable features based on authorization.
if (granted) {
// set some flag, that would be used to see if notifications should be posted
NSLog(@"Notification Permission Granted");
self->userDefaults.notificationsAllowed = TRUE;
}
}];
}
- (void) postNotificationUncompressedFile:(NSString *) filename {
// add details of notification
NSLog(@"Posting notification for %@", filename);
}
@end