mirror of
https://github.com/sparkle-project/Sparkle.git
synced 2025-11-01 15:34:38 +00:00
273 lines
8.1 KiB
Objective-C
273 lines
8.1 KiB
Objective-C
//
|
|
// SUPopUpTitlebarUserDriver.m
|
|
// Sparkle
|
|
//
|
|
// Created by Mayur Pawashe on 3/5/16.
|
|
// Copyright © 2016 Sparkle Project. All rights reserved.
|
|
//
|
|
|
|
#import "SUPopUpTitlebarUserDriver.h"
|
|
#import "SUInstallUpdateViewController.h"
|
|
#import <AppKit/AppKit.h>
|
|
|
|
@implementation SUPopUpTitlebarUserDriver
|
|
{
|
|
void (^_updateButtonAction)(NSButton *);
|
|
|
|
NSWindow *_window;
|
|
SUInstallUpdateViewController *_installUpdateViewController;
|
|
NSTitlebarAccessoryViewController *_accessoryViewController;
|
|
NSButton *_updateButton;
|
|
|
|
uint64_t _expectedContentLength;
|
|
uint64_t _contentLengthDownloaded;
|
|
|
|
BOOL _addedAccessory;
|
|
}
|
|
|
|
- (instancetype)initWithWindow:(NSWindow *)window
|
|
{
|
|
self = [super init];
|
|
if (self != nil) {
|
|
_window = window;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)addUpdateButtonWithTitle:(NSString *)title SPU_OBJC_DIRECT
|
|
{
|
|
[self addUpdateButtonWithTitle:title action:nil];
|
|
}
|
|
|
|
- (void)addUpdateButtonWithTitle:(NSString *)title action:(void (^)(NSButton *button))action SPU_OBJC_DIRECT
|
|
{
|
|
if (_updateButton == nil) {
|
|
NSButton *updateButton = [[NSButton alloc] initWithFrame:NSMakeRect(0, 0, 160, 100)];
|
|
updateButton.title = title;
|
|
updateButton.bezelStyle = NSBezelStyleRecessed;
|
|
|
|
_updateButton = updateButton;
|
|
} else {
|
|
_updateButton.title = title;
|
|
}
|
|
|
|
if (action != nil) {
|
|
_updateButton.target = self;
|
|
_updateButton.action = @selector(updateButtonAction:);
|
|
_updateButtonAction = action;
|
|
_updateButton.enabled = YES;
|
|
} else {
|
|
_updateButton.enabled = NO;
|
|
_updateButton.target = nil;
|
|
_updateButtonAction = nil;
|
|
}
|
|
|
|
if (_accessoryViewController == nil) {
|
|
_accessoryViewController = [[NSTitlebarAccessoryViewController alloc] init];
|
|
_accessoryViewController.layoutAttribute = NSLayoutAttributeRight;
|
|
_accessoryViewController.view = _updateButton;
|
|
}
|
|
|
|
if (!_addedAccessory) {
|
|
[_window addTitlebarAccessoryViewController:_accessoryViewController];
|
|
_addedAccessory = YES;
|
|
}
|
|
}
|
|
|
|
- (void)updateButtonAction:(NSButton *)sender
|
|
{
|
|
if (_updateButtonAction != nil) {
|
|
_updateButtonAction(sender);
|
|
}
|
|
}
|
|
|
|
- (void)removeUpdateButton SPU_OBJC_DIRECT
|
|
{
|
|
[_accessoryViewController removeFromParentViewController];
|
|
_addedAccessory = NO;
|
|
_updateButtonAction = nil;
|
|
}
|
|
|
|
#pragma mark Update Permission
|
|
|
|
- (void)showUpdatePermissionRequest:(SPUUpdatePermissionRequest *)__unused request reply:(void (^)(SUUpdatePermissionResponse *))reply
|
|
{
|
|
// Just make a decision..
|
|
SUUpdatePermissionResponse *response = [[SUUpdatePermissionResponse alloc] initWithAutomaticUpdateChecks:YES sendSystemProfile:NO];
|
|
reply(response);
|
|
}
|
|
|
|
#pragma mark Update Found
|
|
|
|
- (void)showUpdateWithAppcastItem:(SUAppcastItem *)appcastItem reply:(void (^)(SPUUserUpdateChoice))reply
|
|
{
|
|
NSPopover *popover = [[NSPopover alloc] init];
|
|
popover.behavior = NSPopoverBehaviorTransient;
|
|
|
|
__weak __typeof__(self) weakSelf = self;
|
|
__block NSButton *actionButton = nil;
|
|
|
|
SUInstallUpdateViewController *viewController = [[SUInstallUpdateViewController alloc] initWithAppcastItem:appcastItem reply:^(SPUUserUpdateChoice choice) {
|
|
reply(choice);
|
|
|
|
[popover close];
|
|
actionButton.enabled = NO;
|
|
|
|
__typeof__(self) strongSelf = weakSelf;
|
|
if (strongSelf != nil) {
|
|
strongSelf->_installUpdateViewController = nil;
|
|
}
|
|
}];
|
|
|
|
_installUpdateViewController = viewController;
|
|
|
|
[self addUpdateButtonWithTitle:@"Update Available" action:^(NSButton *button) {
|
|
actionButton = button;
|
|
popover.contentViewController = viewController;
|
|
[popover showRelativeToRect:button.bounds ofView:button preferredEdge:NSMaxYEdge];
|
|
}];
|
|
}
|
|
|
|
- (void)showUpdateFoundWithAppcastItem:(SUAppcastItem *)appcastItem state:(SPUUserUpdateState *)state reply:(void (^)(SPUUserUpdateChoice))reply
|
|
{
|
|
if (appcastItem.informationOnlyUpdate) {
|
|
// Todo: show user interface for this
|
|
NSLog(@"Found info URL: %@", appcastItem.infoURL);
|
|
|
|
// Remove UI from user initiated check
|
|
[self removeUpdateButton];
|
|
|
|
reply(SPUUserUpdateChoiceDismiss);
|
|
} else {
|
|
[self showUpdateWithAppcastItem:appcastItem reply:reply];
|
|
}
|
|
}
|
|
|
|
- (void)showUpdateReleaseNotesWithDownloadData:(SPUDownloadData *)downloadData
|
|
{
|
|
[_installUpdateViewController showReleaseNotesWithDownloadData:downloadData];
|
|
}
|
|
|
|
- (void)showUpdateReleaseNotesFailedToDownloadWithError:(NSError *)__unused error
|
|
{
|
|
}
|
|
|
|
- (void)showUpdateInFocus
|
|
{
|
|
[_window makeKeyAndOrderFront:nil];
|
|
|
|
if (_updateButton.enabled) {
|
|
// Not the proper way to do things but ignoring warnings in Test App.
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wobjc-messaging-id"
|
|
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
|
[_updateButton.target performSelector:_updateButton.action withObject:_updateButton];
|
|
#pragma clang diagnostic pop
|
|
}
|
|
}
|
|
|
|
#pragma mark Install & Relaunch Update
|
|
|
|
- (void)showReadyToInstallAndRelaunch:(void (^)(SPUUserUpdateChoice))installUpdateHandler
|
|
{
|
|
[self addUpdateButtonWithTitle:@"Install & Relaunch" action:^(NSButton *__unused button) {
|
|
installUpdateHandler(SPUUserUpdateChoiceInstall);
|
|
}];
|
|
}
|
|
|
|
#pragma mark Check for Updates
|
|
|
|
- (void)showUserInitiatedUpdateCheckWithCancellation:(void (^)(void))__unused cancellation
|
|
{
|
|
[self addUpdateButtonWithTitle:@"Checking for Updates…"];
|
|
}
|
|
|
|
#pragma mark Update Errors
|
|
|
|
- (void)acceptAcknowledgementAfterDelay:(void (^)(void))acknowledgement
|
|
{
|
|
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
|
|
// Installation will be dismissed shortly after this
|
|
acknowledgement();
|
|
});
|
|
}
|
|
|
|
- (void)showUpdaterError:(NSError *)error acknowledgement:(void (^)(void))acknowledgement
|
|
{
|
|
NSLog(@"Error: %@", error);
|
|
[self addUpdateButtonWithTitle:@"Update Errored!" action:nil];
|
|
|
|
[self acceptAcknowledgementAfterDelay:acknowledgement];
|
|
}
|
|
|
|
- (void)showUpdateNotFoundWithError:(NSError *)error acknowledgement:(void (^)(void))acknowledgement
|
|
{
|
|
[self addUpdateButtonWithTitle:@"No Update Available" action:nil];
|
|
|
|
[self acceptAcknowledgementAfterDelay:acknowledgement];
|
|
}
|
|
|
|
#pragma mark Download & Install Updates
|
|
|
|
- (void)showDownloadInitiatedWithCancellation:(void (^)(void))__unused cancellation
|
|
{
|
|
}
|
|
|
|
- (void)showDownloadDidReceiveExpectedContentLength:(uint64_t)expectedContentLength
|
|
{
|
|
[self addUpdateButtonWithTitle:@"Downloading…"];
|
|
_contentLengthDownloaded = 0;
|
|
_expectedContentLength = expectedContentLength;
|
|
}
|
|
|
|
- (void)showDownloadDidReceiveDataOfLength:(uint64_t)length
|
|
{
|
|
_contentLengthDownloaded += length;
|
|
|
|
// In case our expected content length was incorrect
|
|
if (_contentLengthDownloaded > _expectedContentLength) {
|
|
_expectedContentLength = _contentLengthDownloaded;
|
|
}
|
|
|
|
if (_expectedContentLength > 0) {
|
|
double progress = (double)_contentLengthDownloaded / _expectedContentLength;
|
|
[self addUpdateButtonWithTitle:[NSString stringWithFormat:@"Downloading (%0.0f%%)", progress * 100] action:nil];
|
|
}
|
|
}
|
|
|
|
- (void)showDownloadDidStartExtractingUpdate
|
|
{
|
|
[self addUpdateButtonWithTitle:@"Extracting…"];
|
|
}
|
|
|
|
- (void)showExtractionReceivedProgress:(double)progress
|
|
{
|
|
[self addUpdateButtonWithTitle:[NSString stringWithFormat:@"Extracting (%d%%)…", (int)(progress * 100)]];
|
|
}
|
|
|
|
- (void)showInstallingUpdateWithApplicationTerminated:(BOOL)applicationTerminated retryTerminatingApplication:(void (^)(void))__unused retryTerminatingApplication
|
|
{
|
|
if (applicationTerminated) {
|
|
[self addUpdateButtonWithTitle:@"Installing…"];
|
|
} else {
|
|
// In case our termination request fails or is delayed
|
|
[self removeUpdateButton];
|
|
}
|
|
}
|
|
|
|
- (void)showUpdateInstalledAndRelaunched:(BOOL)__unused relaunched acknowledgement:(void (^)(void))acknowledgement
|
|
{
|
|
[self addUpdateButtonWithTitle:@"Installation Finished!"];
|
|
|
|
[self acceptAcknowledgementAfterDelay:acknowledgement];
|
|
}
|
|
|
|
#pragma mark Aborting Everything
|
|
|
|
- (void)dismissUpdateInstallation
|
|
{
|
|
[self removeUpdateButton];
|
|
}
|
|
|
|
@end
|