Import from SVN r48.
[sparkle2.git] / Stub / SURegistrar.m
blob975f99926329756c3de998bf7fc40956746910f7
1 //
2 //  SURegistrar.m
3 //  Sparkle
4 //
5 //  Created by Andy Matuschak on 9/10/07.
6 //  Copyright 2007 __MyCompanyName__. All rights reserved.
7 //
9 #import "SURegistrar.h"
10 #import "SURegistryProtocol.h"
11 #import "SUProduct.h"
12 #import "BLAuthentication.h"
14 NSString *SULaunchCountKey = @"SULaunchCount";
15 NSString *SUHasPromptedUserKey = @"SUHasPromptedUser";
16 NSString *SULaunchesRequiredForPromptKey = @"SULaunchesRequiredForPrompt";
18 @interface SURegistrar (Private)
19 - (int)launchCount;
20 - (int)launchesRequiredForPrompt;
21 - (BOOL)isNewSparkleUser;
22 - (void)recordPrompt;
23 - (void)attemptInstallation;
24 - (void)updateAppRegistration:(id <SURegistryProtocol>)registry;
25 - (NSString *)productIdentifier;
26 @end
28 @implementation SURegistrar
30 + (SURegistrar *)sharedRegistrar
32         static SURegistrar *sharedRegistrar = nil;
33         if (sharedRegistrar == nil)
34                 sharedRegistrar = [[SURegistrar alloc] init];
35         return sharedRegistrar;
38 // This method attempts to inform the Sparkle daemon about this app. If the Sparkle daemon isn't installed,
39 // it will ask the user if he wants to install it, but only if he's never been asked before and the app has been launched enough times.
40 - (void)attemptRegistration
42         // First, we try to connect to the Sparkle daemon.
43         id proxy = [[NSConnection rootProxyForConnectionWithRegisteredName:SUDaemonServiceName host:nil] retain];
44         if (proxy)
45         {
46                 [proxy setProtocolForProxy:@protocol(SURegistryProtocol)];
47                 // We update the application's registration every time it launches in case something changes.
48                 [self updateAppRegistration:proxy];
49         }
50         else
51         {
52                 // Either the daemon isn't running right now, or the user is new to Sparkle.
53                 if ([self isNewSparkleUser])
54                 {
55                         // They're a Sparkle virgin! But we don't want to disrupt the first run of this app, so have we launched enough times?
56                         if ([self launchCount] >= [self launchesRequiredForPrompt])
57                                 [self attemptInstallation];
58                 }
59                 // Otherwise, the daemon isn't running, or they just don't want Sparkle. So we're done.
60         }
63 - (void)updateAppRegistration:(id <SURegistryProtocol>)registry
65         SUProduct *product;
66         if ([delegate respondsToSelector:@selector(productForRegistrar:)]) // Does the delegate want to take control?
67         {
68                 product = [delegate productForRegistrar:self];
69         }
70         else
71         {
72                 // The app doesn't want any special behavior, so it's probably just a plain .app.
73                 product = [[[SUProduct alloc] initWithMainBundle:[NSBundle mainBundle]] autorelease];
74         }
75         [registry registerProduct:product withIdentifier:[self productIdentifier]];
78 - (void)attemptInstallation
80         // First, though, we have to ask.
81         [self recordPrompt];
82         NSAlert *alert = [NSAlert alertWithMessageText:@"Would you like to keep your applications up-to-date?" defaultButton:@"Install Sparkle" alternateButton:@"Don't Install" otherButton:@"Learn More..." informativeTextWithFormat:@"Sparkle keeps APP_NAME and other supported applications up-to-date. Would you like to install Sparkle now?"];
83         [alert setIcon:[[NSImage alloc] initWithContentsOfFile:[[NSBundle bundleForClass:[self class]] pathForImageResource:@"Sparkle"]]];
84         [alert runModal];
85         
86         SUSetupSupportFolder();
89 - (void)recordPrompt
91         // Right now, the behavior is that we're asking every user individually if he wants to install Sparkle so as to avoid authenticating for /Library.
92         // This is probably sub-optimal.
93         CFPreferencesSetValue((CFStringRef)SUHasPromptedUserKey, [NSNumber numberWithBool:YES], (CFStringRef)SUSparkleIdentifier, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
94         CFPreferencesSynchronize((CFStringRef)SUSparkleIdentifier, kCFPreferencesCurrentUser, kCFPreferencesAnyHost);
97 - (NSString *)productIdentifier
99         if ([delegate respondsToSelector:@selector(productIdentifierForRegistrar:)]) // Give the delegate a chance to provide a custom identifier.
100                 return [delegate productIdentifierForRegistrar:self];
101         return [[NSBundle mainBundle] bundleIdentifier];
104 - (BOOL)isNewSparkleUser
106         BOOL hasPromptedUser;
107         Boolean validKey;
108         hasPromptedUser = CFPreferencesGetAppBooleanValue((CFStringRef)SUHasPromptedUserKey, (CFStringRef)SUSparkleIdentifier, &validKey);
109         return !(hasPromptedUser && validKey);
112 - (void)setDelegate:aDelegate
114         delegate = aDelegate;
117 + (void)initialize
119         // Whenever the registrar is initialized, we increment the launch count. This assumes that the class is loaded on every launch, which may not be the case.
120         [[NSUserDefaults standardUserDefaults] setInteger:([[NSUserDefaults standardUserDefaults] integerForKey:SULaunchCountKey] + 1) forKey:SULaunchCountKey];
123 - (int)launchCount
125         return [[NSUserDefaults standardUserDefaults] integerForKey:SULaunchCountKey];
128 - (int)launchesRequiredForPrompt
130         NSNumber *requirement = [[NSBundle mainBundle] objectForInfoDictionaryKey:SULaunchesRequiredForPromptKey]; 
131         if (!requirement) { return 2; } // Default is to prompt on the *second* launch.
132         return [requirement intValue];
135 @end