Skip to content

Commit

Permalink
Merge pull request #1051 from sparkle-project/mainalert
Browse files Browse the repository at this point in the history
Ensure UI code always runs on the main thread
  • Loading branch information
kornelski committed Jul 16, 2017
2 parents 043ad04 + 423ee5a commit 39d6c61
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 69 deletions.
43 changes: 23 additions & 20 deletions Sparkle/SUAutomaticUpdateDriver.m
Expand Up @@ -49,26 +49,29 @@ - (void)showUpdateAlert
self.interruptible = NO;
[self invalidateShowUpdateAlertTimer];

if (self.alert) {
[self.alert close];
}

self.alert = [[SUAutomaticUpdateAlert alloc] initWithAppcastItem:self.updateItem host:self.host completionBlock:^(SUAutomaticInstallationChoice choice) {
[self automaticUpdateAlertFinishedWithChoice:choice];
}];

// If the app is a menubar app or the like, we need to focus it first and alter the
// update prompt to behave like a normal window. Otherwise if the window were hidden
// there may be no way for the application to be activated to make it visible again.
if ([SUApplicationInfo isBackgroundApplication:[NSApplication sharedApplication]]) {
[[self.alert window] setHidesOnDeactivate:NO];
[NSApp activateIgnoringOtherApps:YES];
}

if ([NSApp isActive])
[[self.alert window] makeKeyAndOrderFront:self];
else
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:NSApplicationDidBecomeActiveNotification object:NSApp];
dispatch_async(dispatch_get_main_queue(), ^{
if (self.alert) {
[self.alert close];
}

self.alert = [[SUAutomaticUpdateAlert alloc] initWithAppcastItem:self.updateItem host:self.host completionBlock:^(SUAutomaticInstallationChoice choice) {
[self automaticUpdateAlertFinishedWithChoice:choice];
}];

// If the app is a menubar app or the like, we need to focus it first and alter the
// update prompt to behave like a normal window. Otherwise if the window were hidden
// there may be no way for the application to be activated to make it visible again.
if ([SUApplicationInfo isBackgroundApplication:[NSApplication sharedApplication]]) {
[[self.alert window] setHidesOnDeactivate:NO];
[NSApp activateIgnoringOtherApps:YES];
}

if ([NSApp isActive]) {
[[self.alert window] makeKeyAndOrderFront:self];
} else {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:NSApplicationDidBecomeActiveNotification object:NSApp];
}
});
}

- (void)unarchiverDidFinish:(id)__unused ua
Expand Down
78 changes: 41 additions & 37 deletions Sparkle/SUUIBasedUpdateDriver.m
Expand Up @@ -68,33 +68,35 @@ - (void)didFindValidUpdate
return;
}

self.updateAlert = [[SUUpdateAlert alloc] initWithAppcastItem:self.updateItem host:self.host completionBlock:^(SUUpdateAlertChoice choice) {
[self updateAlertFinishedWithChoice:choice];
}];

id<SUVersionDisplay> versDisp = nil;
if ([[updater delegate] respondsToSelector:@selector(versionDisplayerForUpdater:)]) {
versDisp = [[updater delegate] versionDisplayerForUpdater:self.updater];
}
[self.updateAlert setVersionDisplayer:versDisp];

// If the app is a menubar app or the like, we need to focus it first and alter the
// update prompt to behave like a normal window. Otherwise if the window were hidden
// there may be no way for the application to be activated to make it visible again.
if ([SUApplicationInfo isBackgroundApplication:[NSApplication sharedApplication]]) {
[[self.updateAlert window] setHidesOnDeactivate:NO];
[NSApp activateIgnoringOtherApps:YES];
}

// Only show the update alert if the app is active; otherwise, we'll wait until it is.
if ([NSApp isActive]) {
NSWindow *window = [self.updateAlert window];
if ([self shouldDisableKeyboardShortcutForInstallButton]) {
[self.updateAlert disableKeyboardShortcutForInstallButton];
dispatch_async(dispatch_get_main_queue(), ^{
self.updateAlert = [[SUUpdateAlert alloc] initWithAppcastItem:self.updateItem host:self.host completionBlock:^(SUUpdateAlertChoice choice) {
[self updateAlertFinishedWithChoice:choice];
}];

id<SUVersionDisplay> versDisp = nil;
if ([[updater delegate] respondsToSelector:@selector(versionDisplayerForUpdater:)]) {
versDisp = [[updater delegate] versionDisplayerForUpdater:self.updater];
}
[self.updateAlert setVersionDisplayer:versDisp];

// If the app is a menubar app or the like, we need to focus it first and alter the
// update prompt to behave like a normal window. Otherwise if the window were hidden
// there may be no way for the application to be activated to make it visible again.
if ([SUApplicationInfo isBackgroundApplication:[NSApplication sharedApplication]]) {
[[self.updateAlert window] setHidesOnDeactivate:NO];
[NSApp activateIgnoringOtherApps:YES];
}
[window makeKeyAndOrderFront:self];
} else
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:NSApplicationDidBecomeActiveNotification object:NSApp];

// Only show the update alert if the app is active; otherwise, we'll wait until it is.
if ([NSApp isActive]) {
NSWindow *window = [self.updateAlert window];
if ([self shouldDisableKeyboardShortcutForInstallButton]) {
[self.updateAlert disableKeyboardShortcutForInstallButton];
}
[window makeKeyAndOrderFront:self];
} else
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidBecomeActive:) name:NSApplicationDidBecomeActiveNotification object:NSApp];
});
}

- (BOOL)shouldDisableKeyboardShortcutForInstallButton {
Expand Down Expand Up @@ -317,20 +319,22 @@ - (void)abortUpdate

- (void)showAlert:(NSAlert *)alert
{
id<SUUpdaterPrivate> updater = self.updater;
if ([[updater delegate] respondsToSelector:@selector(updaterWillShowModalAlert:)]) {
[[updater delegate] updaterWillShowModalAlert:self.updater];
}
dispatch_sync(dispatch_get_main_queue(), ^{
id<SUUpdaterPrivate> updater = self.updater;
if ([[updater delegate] respondsToSelector:@selector(updaterWillShowModalAlert:)]) {
[[updater delegate] updaterWillShowModalAlert:self.updater];
}

// When showing a modal alert we need to ensure that background applications
// are focused to inform the user since there is no dock icon to notify them.
if ([SUApplicationInfo isBackgroundApplication:[NSApplication sharedApplication]]) { [NSApp activateIgnoringOtherApps:YES]; }
// When showing a modal alert we need to ensure that background applications
// are focused to inform the user since there is no dock icon to notify them.
if ([SUApplicationInfo isBackgroundApplication:[NSApplication sharedApplication]]) { [NSApp activateIgnoringOtherApps:YES]; }

[alert setIcon:[SUApplicationInfo bestIconForHost:self.host]];
[alert runModal];
[alert setIcon:[SUApplicationInfo bestIconForHost:self.host]];
[alert runModal];

if ([[updater delegate] respondsToSelector:@selector(updaterDidShowModalAlert:)])
[[updater delegate] updaterDidShowModalAlert:self.updater];
if ([[updater delegate] respondsToSelector:@selector(updaterDidShowModalAlert:)])
[[updater delegate] updaterDidShowModalAlert:self.updater];
});
}

@end
26 changes: 14 additions & 12 deletions Sparkle/SUUpdatePermissionPrompt.m
Expand Up @@ -72,20 +72,22 @@ - (instancetype)initWithHost:(SUHost *)aHost systemProfile:(NSArray *)profile re

+ (void)promptWithHost:(SUHost *)host systemProfile:(NSArray *)profile reply:(void (^)(SUUpdatePermissionResponse *))reply
{
// If this is a background application we need to focus it in order to bring the prompt
// to the user's attention. Otherwise the prompt would be hidden behind other applications and
// the user would not know why the application was paused.
if ([SUApplicationInfo isBackgroundApplication:[NSApplication sharedApplication]]) {
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
}
dispatch_async(dispatch_get_main_queue(), ^{
// If this is a background application we need to focus it in order to bring the prompt
// to the user's attention. Otherwise the prompt would be hidden behind other applications and
// the user would not know why the application was paused.
if ([SUApplicationInfo isBackgroundApplication:[NSApplication sharedApplication]]) {
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
}

if (![NSApp modalWindow]) { // do not prompt if there is is another modal window on screen
SUUpdatePermissionPrompt *prompt = [[[self class] alloc] initWithHost:host systemProfile:profile reply:reply];
NSWindow *window = [prompt window];
if (window) {
[NSApp runModalForWindow:window];
if (![NSApp modalWindow]) { // do not prompt if there is is another modal window on screen
SUUpdatePermissionPrompt *prompt = [[[self class] alloc] initWithHost:host systemProfile:profile reply:reply];
NSWindow *window = [prompt window];
if (window) {
[NSApp runModalForWindow:window];
}
}
}
});
}

- (NSString *)description { return [NSString stringWithFormat:@"%@ <%@>", [self class], [self.host bundlePath]]; }
Expand Down

0 comments on commit 39d6c61

Please sign in to comment.