QuietUnrar/QuietUnrarAppDelegate.m

301 lines
11 KiB
Objective-C

//
// QuietUnrarAppDelegate.m
// QuietUnrar
//
// Created by Robert McGovern on 2009/09/06.
// Copyright 2009 Tarasis. All rights reserved.
//
@import UserNotifications;
#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];
// temp set to YES for testing preferences
// my preference is not to have the app become front and central when run.
[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