[26] | 1 | //
|
---|
| 2 | // PSAlarms.m
|
---|
| 3 | // Pester
|
---|
| 4 | //
|
---|
| 5 | // Created by Nicholas Riley on Fri Oct 11 2002.
|
---|
| 6 | // Copyright (c) 2002 Nicholas Riley. All rights reserved.
|
---|
| 7 | //
|
---|
| 8 |
|
---|
| 9 | #import "PSAlarms.h"
|
---|
| 10 | #import "PSAlarm.h"
|
---|
[53] | 11 | #import "NSDictionary-NJRExtensions.h"
|
---|
[26] | 12 |
|
---|
[53] | 13 | NSString * const PSAlarmImportException = @"PSAlarmImportException";
|
---|
| 14 |
|
---|
[26] | 15 | NSString * const PSAlarmsDidChangeNotification = @"PSAlarmsDidChangeNotification";
|
---|
[28] | 16 | NSString * const PSAlarmsNextAlarmDidChangeNotification = @"PSAlarmsNextAlarmDidChangeNotification";
|
---|
[26] | 17 |
|
---|
[53] | 18 | // NSUserDefaults key
|
---|
| 19 | static NSString * const PSPendingAlarms = @"Pester pending alarms"; // 1.0 Ð 1.1a3
|
---|
| 20 | static NSString * const PSAllAlarms = @"Pester alarms"; // 1.1a4 Ð
|
---|
[26] | 21 |
|
---|
[53] | 22 | // property list keys
|
---|
| 23 | static NSString * const PLAlarmsPending = @"pending";
|
---|
| 24 | static NSString * const PLAlarmsExpired = @"expired";
|
---|
| 25 |
|
---|
[26] | 26 | static PSAlarms *PSAlarmsAllAlarms = nil;
|
---|
| 27 |
|
---|
[28] | 28 | @interface PSAlarms (Private)
|
---|
| 29 |
|
---|
| 30 | - (void)_updateNextAlarm;
|
---|
| 31 |
|
---|
| 32 | @end
|
---|
| 33 |
|
---|
[26] | 34 | @implementation PSAlarms
|
---|
| 35 |
|
---|
[28] | 36 | + (void)setUp;
|
---|
[26] | 37 | {
|
---|
| 38 | if (PSAlarmsAllAlarms == nil) {
|
---|
[53] | 39 | NSDictionary *plAlarms = [[NSUserDefaults standardUserDefaults] objectForKey: PSAllAlarms];
|
---|
| 40 | if (plAlarms == nil) {
|
---|
| 41 | PSAlarmsAllAlarms = [[self alloc] init];
|
---|
| 42 | } else {
|
---|
| 43 | PSAlarmsAllAlarms = [[self alloc] initWithPropertyList: plAlarms];
|
---|
| 44 | }
|
---|
[28] | 45 | [PSAlarmsAllAlarms _updateNextAlarm]; // only generate notifications after singleton established
|
---|
[26] | 46 | }
|
---|
| 47 | }
|
---|
| 48 |
|
---|
| 49 | + (PSAlarms *)allAlarms;
|
---|
| 50 | {
|
---|
[28] | 51 | NSAssert(PSAlarmsAllAlarms != nil, @"Attempt to use +[PSAlarms allAlarms] before setup complete");
|
---|
[26] | 52 | return PSAlarmsAllAlarms;
|
---|
| 53 | }
|
---|
| 54 |
|
---|
[53] | 55 | #pragma mark private
|
---|
| 56 |
|
---|
| 57 | - (void)_changed;
|
---|
| 58 | {
|
---|
| 59 | NSMutableArray *alarmsData = [[NSMutableArray alloc] init];
|
---|
| 60 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
---|
| 61 | [self _updateNextAlarm];
|
---|
| 62 | // NSLog(@"PSAlarms _changed:\n%@", alarms);
|
---|
| 63 | [defaults setObject: [self propertyListRepresentation] forKey: PSAllAlarms];
|
---|
| 64 | [defaults synchronize];
|
---|
| 65 | [alarmsData release];
|
---|
| 66 | [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmsDidChangeNotification object: self];
|
---|
| 67 | }
|
---|
| 68 |
|
---|
| 69 | - (void)_alarmTimerExpired:(NSNotification *)notification;
|
---|
| 70 | {
|
---|
| 71 | PSAlarm *alarm = [notification object];
|
---|
| 72 | NSLog(@"timer expired: %@ retainCount %d", alarm, [alarm retainCount]);
|
---|
| 73 | [expiredAlarms addObject: alarm];
|
---|
| 74 | NSLog(@"expired alarms: %@", [expiredAlarms description]);
|
---|
| 75 | [alarms removeObject: alarm];
|
---|
| 76 | [self _changed];
|
---|
| 77 | }
|
---|
| 78 |
|
---|
| 79 | - (void)_alarmTimerSet:(NSNotification *)notification;
|
---|
| 80 | {
|
---|
| 81 | PSAlarm *alarm = [notification object];
|
---|
| 82 | NSLog(@"timer set: %@ retainCount %d", alarm, [alarm retainCount]);
|
---|
| 83 | [alarms addObject: alarm];
|
---|
| 84 | [expiredAlarms removeObject: alarm];
|
---|
| 85 | [self _changed];
|
---|
| 86 | }
|
---|
| 87 |
|
---|
| 88 | - (void)_alarmDied:(NSNotification *)notification;
|
---|
| 89 | {
|
---|
| 90 | PSAlarm *alarm = [notification object];
|
---|
| 91 | NSLog(@"alarm died: %@ retainCount %d", alarm, [alarm retainCount]);
|
---|
| 92 | [alarms removeObject: alarm];
|
---|
| 93 | [expiredAlarms removeObject: alarm];
|
---|
| 94 | [self _changed];
|
---|
| 95 | }
|
---|
| 96 |
|
---|
[28] | 97 | - (void)_updateNextAlarm;
|
---|
| 98 | {
|
---|
| 99 | NSEnumerator *e;
|
---|
| 100 | PSAlarm *alarm, *oldNextAlarm = nextAlarm;
|
---|
| 101 | [nextAlarm release];
|
---|
| 102 | nextAlarm = nil;
|
---|
| 103 | // sort alarms so earliest is first
|
---|
[51] | 104 | [alarms sortUsingSelector: @selector(compareDate:)];
|
---|
[28] | 105 | // find first un-expired alarm
|
---|
| 106 | e = [alarms objectEnumerator];
|
---|
| 107 | while ( (alarm = [e nextObject]) != nil) {
|
---|
| 108 | if ([alarm isValid]) {
|
---|
| 109 | nextAlarm = [alarm retain];
|
---|
| 110 | break;
|
---|
| 111 | }
|
---|
| 112 | }
|
---|
| 113 | if (oldNextAlarm != nextAlarm)
|
---|
| 114 | [[NSNotificationCenter defaultCenter] postNotificationName: PSAlarmsNextAlarmDidChangeNotification object: nextAlarm];
|
---|
| 115 | }
|
---|
| 116 |
|
---|
[53] | 117 | - (void)_setUpNotifications;
|
---|
| 118 | {
|
---|
| 119 | [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmTimerSet:) name: PSAlarmTimerSetNotification object: nil];
|
---|
| 120 | [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmTimerExpired:) name: PSAlarmTimerExpiredNotification object: nil];
|
---|
| 121 | [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(_alarmDied:) name: PSAlarmDiedNotification object: nil];
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | #pragma mark initialize-release
|
---|
| 125 |
|
---|
[26] | 126 | - (id)init;
|
---|
| 127 | {
|
---|
| 128 | if ( (self = [super init]) != nil) {
|
---|
[28] | 129 | alarms = [[NSMutableArray alloc] init];
|
---|
[53] | 130 | expiredAlarms = [[NSMutableSet alloc] init];
|
---|
| 131 | [self _setUpNotifications];
|
---|
[26] | 132 | }
|
---|
| 133 | return self;
|
---|
| 134 | }
|
---|
| 135 |
|
---|
| 136 | - (void)dealloc;
|
---|
| 137 | {
|
---|
[28] | 138 | [alarms release];
|
---|
[53] | 139 | [expiredAlarms release];
|
---|
[26] | 140 | [[NSNotificationCenter defaultCenter] removeObserver: self];
|
---|
| 141 | [super dealloc];
|
---|
| 142 | }
|
---|
| 143 |
|
---|
[53] | 144 | #pragma mark accessing
|
---|
[26] | 145 |
|
---|
[53] | 146 | - (NSArray *)alarms;
|
---|
[26] | 147 | {
|
---|
[53] | 148 | return alarms;
|
---|
[26] | 149 | }
|
---|
| 150 |
|
---|
[28] | 151 | - (PSAlarm *)nextAlarm;
|
---|
| 152 | {
|
---|
| 153 | return nextAlarm;
|
---|
| 154 | }
|
---|
| 155 |
|
---|
[26] | 156 | - (int)alarmCount;
|
---|
| 157 | {
|
---|
| 158 | return [alarms count];
|
---|
| 159 | }
|
---|
| 160 |
|
---|
| 161 | - (PSAlarm *)alarmAtIndex:(int)index;
|
---|
| 162 | {
|
---|
| 163 | return [alarms objectAtIndex: index];
|
---|
| 164 | }
|
---|
| 165 |
|
---|
| 166 | - (void)removeAlarmAtIndex:(int)index;
|
---|
| 167 | {
|
---|
[51] | 168 | [(PSAlarm *)[alarms objectAtIndex: index] cancelTimer];
|
---|
[26] | 169 | [alarms removeObjectAtIndex: index];
|
---|
| 170 | }
|
---|
| 171 |
|
---|
| 172 | - (void)removeAlarmsAtIndices:(NSArray *)indices;
|
---|
| 173 | {
|
---|
| 174 | NSEnumerator *e = [indices objectEnumerator];
|
---|
| 175 | NSNumber *n;
|
---|
[28] | 176 | int indexCount = [indices count], i = 0, alarmIndex;
|
---|
[26] | 177 | int *indexArray = (int *)malloc(indexCount * sizeof(int));
|
---|
| 178 | NS_DURING
|
---|
| 179 | while ( (n = [e nextObject]) != nil) {
|
---|
[28] | 180 | alarmIndex = [n intValue];
|
---|
[51] | 181 | [(PSAlarm *)[alarms objectAtIndex: alarmIndex] cancelTimer];
|
---|
[28] | 182 | indexArray[i] = alarmIndex;
|
---|
[26] | 183 | i++;
|
---|
| 184 | }
|
---|
| 185 | [alarms removeObjectsFromIndices: indexArray numIndices: indexCount];
|
---|
[51] | 186 | free(indexArray); indexArray = NULL;
|
---|
[26] | 187 | [self _changed];
|
---|
| 188 | NS_HANDLER
|
---|
| 189 | free(indexArray);
|
---|
| 190 | [self _changed];
|
---|
| 191 | [localException raise];
|
---|
| 192 | NS_ENDHANDLER
|
---|
| 193 | }
|
---|
| 194 |
|
---|
[51] | 195 | - (void)removeAlarms:(NSSet *)alarmsToRemove;
|
---|
| 196 | {
|
---|
| 197 | NSEnumerator *e = [alarms objectEnumerator];
|
---|
| 198 | PSAlarm *alarm;
|
---|
| 199 | NSMutableArray *indices = [NSMutableArray arrayWithCapacity: [alarmsToRemove count]];
|
---|
| 200 | int alarmIndex = 0;
|
---|
| 201 |
|
---|
| 202 | while ( (alarm = [e nextObject]) != nil) {
|
---|
| 203 | if ([alarmsToRemove containsObject: alarm])
|
---|
| 204 | [indices addObject: [NSNumber numberWithInt: alarmIndex]];
|
---|
| 205 | alarmIndex++;
|
---|
| 206 | }
|
---|
| 207 | [self removeAlarmsAtIndices: indices];
|
---|
| 208 | }
|
---|
| 209 |
|
---|
[60] | 210 | - (BOOL)alarmsExpiring;
|
---|
| 211 | {
|
---|
| 212 | return [expiredAlarms count] != 0;
|
---|
| 213 | }
|
---|
| 214 |
|
---|
[53] | 215 | #pragma mark property list serialization (Pester 1.1)
|
---|
| 216 |
|
---|
| 217 | - (NSDictionary *)propertyListRepresentation;
|
---|
| 218 | {
|
---|
| 219 | NSMutableArray *plPendingAlarms = [[NSMutableArray alloc] initWithCapacity: [alarms count]];
|
---|
| 220 | NSMutableArray *plExpiredAlarms = [[NSMutableArray alloc] initWithCapacity: [expiredAlarms count]];
|
---|
| 221 | NSDictionary *plAllAlarms, *plAlarm;
|
---|
| 222 | NSEnumerator *e;
|
---|
| 223 | PSAlarm *alarm;
|
---|
| 224 |
|
---|
| 225 | e = [alarms objectEnumerator];
|
---|
| 226 | while ( (alarm = [e nextObject]) != nil) {
|
---|
| 227 | plAlarm = [alarm propertyListRepresentation];
|
---|
| 228 | if (plAlarm != nil)
|
---|
| 229 | [plPendingAlarms addObject: plAlarm];
|
---|
| 230 | }
|
---|
| 231 |
|
---|
| 232 | e = [expiredAlarms objectEnumerator];
|
---|
| 233 | while ( (alarm = [e nextObject]) != nil) {
|
---|
| 234 | plAlarm = [alarm propertyListRepresentation];
|
---|
| 235 | if (plAlarm != nil)
|
---|
| 236 | [plExpiredAlarms addObject: plAlarm];
|
---|
| 237 | }
|
---|
| 238 |
|
---|
| 239 | plAllAlarms = [NSDictionary dictionaryWithObjectsAndKeys:
|
---|
| 240 | plPendingAlarms, PLAlarmsPending, plExpiredAlarms, PLAlarmsExpired, nil];
|
---|
| 241 | [plPendingAlarms release];
|
---|
| 242 | [plExpiredAlarms release];
|
---|
| 243 |
|
---|
| 244 | return plAllAlarms;
|
---|
| 245 | }
|
---|
| 246 |
|
---|
| 247 | - (id)initWithPropertyList:(NSDictionary *)dict;
|
---|
| 248 | {
|
---|
| 249 | if ( (self = [super init]) != nil) {
|
---|
| 250 | NSArray *plPendingAlarms = [dict objectForRequiredKey: PLAlarmsPending];
|
---|
| 251 | NSArray *plExpiredAlarms = [dict objectForRequiredKey: PLAlarmsExpired];
|
---|
| 252 | NSEnumerator *e;
|
---|
| 253 | NSDictionary *plAlarm;
|
---|
| 254 | PSAlarm *alarm;
|
---|
| 255 |
|
---|
| 256 | alarms = [[NSMutableArray alloc] initWithCapacity: [plPendingAlarms count]];
|
---|
| 257 | e = [plPendingAlarms objectEnumerator];
|
---|
| 258 | while ( (plAlarm = [e nextObject]) != nil) {
|
---|
| 259 | [alarms addObject: [[PSAlarm alloc] initWithPropertyList: plAlarm]];
|
---|
| 260 | }
|
---|
| 261 |
|
---|
| 262 | e = [plExpiredAlarms objectEnumerator];
|
---|
| 263 | while ( (plAlarm = [e nextObject]) != nil) {
|
---|
| 264 | // expired alarms may be just that, or they may have outstanding repeats - if the latter, PSAlarm will reschedule the alarm.
|
---|
| 265 | if ( (alarm = [[PSAlarm alloc] initWithPropertyList: plAlarm]) != nil)
|
---|
| 266 | [alarms addObject: alarm];
|
---|
| 267 | }
|
---|
| 268 | expiredAlarms = [[NSMutableSet alloc] init];
|
---|
| 269 |
|
---|
| 270 | [self _setUpNotifications];
|
---|
| 271 | }
|
---|
| 272 | return self;
|
---|
| 273 | }
|
---|
| 274 |
|
---|
| 275 | #pragma mark archiving (Pester 1.0)
|
---|
| 276 |
|
---|
| 277 | - (unsigned)countOfVersion1Alarms;
|
---|
| 278 | {
|
---|
| 279 | return [[[NSUserDefaults standardUserDefaults] objectForKey: PSPendingAlarms] count];
|
---|
| 280 | }
|
---|
| 281 |
|
---|
| 282 | - (void)discardVersion1Alarms;
|
---|
| 283 | {
|
---|
| 284 | NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
---|
| 285 | [defaults removeObjectForKey: PSPendingAlarms];
|
---|
| 286 | [defaults synchronize];
|
---|
| 287 | }
|
---|
| 288 |
|
---|
| 289 | - (void)importVersion1Alarms;
|
---|
| 290 | {
|
---|
| 291 | NSArray *alarmsData = [[NSUserDefaults standardUserDefaults] arrayForKey: PSPendingAlarms];
|
---|
| 292 | NSEnumerator *e = [alarmsData objectEnumerator];
|
---|
| 293 | NSData *alarmData;
|
---|
| 294 | PSAlarm *alarm;
|
---|
| 295 | while ( (alarmData = [e nextObject]) != nil) {
|
---|
| 296 | NS_DURING
|
---|
| 297 | alarm = [NSUnarchiver unarchiveObjectWithData: alarmData];
|
---|
| 298 | NS_HANDLER
|
---|
| 299 | alarm = nil;
|
---|
| 300 | // XXX
|
---|
| 301 | NS_ENDHANDLER
|
---|
| 302 | if (alarm != nil)
|
---|
| 303 | [alarms addObject: alarm];
|
---|
| 304 | }
|
---|
| 305 | }
|
---|
| 306 |
|
---|
[26] | 307 | @end
|
---|