Changed indentation to 2 spaces for all files.

This commit is contained in:
Thomas Dohmke 2011-12-05 16:07:59 +01:00
parent 37bcbb7793
commit d444c177df
17 changed files with 2439 additions and 2439 deletions

View File

@ -41,20 +41,20 @@
#pragma mark static
+ (BWApp *)appFromDict:(NSDictionary *)dict {
BWApp *app = [[[[self class] alloc] init] autorelease];
BWApp *app = [[[[self class] alloc] init] autorelease];
// NSParameterAssert([dict isKindOfClass:[NSDictionary class]]);
if ([dict isKindOfClass:[NSDictionary class]]) {
app.name = [dict objectForKey:@"title"];
app.version = [dict objectForKey:@"version"];
app.shortVersion = [dict objectForKey:@"shortversion"];
[app setDateWithTimestamp:[[dict objectForKey:@"timestamp"] doubleValue]];
app.size = [dict objectForKey:@"appsize"];
app.notes = [dict objectForKey:@"notes"];
app.mandatory = [dict objectForKey:@"mandatory"];
}
// NSParameterAssert([dict isKindOfClass:[NSDictionary class]]);
if ([dict isKindOfClass:[NSDictionary class]]) {
app.name = [dict objectForKey:@"title"];
app.version = [dict objectForKey:@"version"];
app.shortVersion = [dict objectForKey:@"shortversion"];
[app setDateWithTimestamp:[[dict objectForKey:@"timestamp"] doubleValue]];
app.size = [dict objectForKey:@"appsize"];
app.notes = [dict objectForKey:@"notes"];
app.mandatory = [dict objectForKey:@"mandatory"];
}
return app;
return app;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -62,43 +62,43 @@
#pragma mark NSObject
- (void)dealloc {
[name_ release];
[version_ release];
[shortVersion_ release];
[notes_ release];
[date_ release];
[size_ release];
[mandatory_ release];
[name_ release];
[version_ release];
[shortVersion_ release];
[notes_ release];
[date_ release];
[size_ release];
[mandatory_ release];
[super dealloc];
[super dealloc];
}
- (BOOL)isEqual:(id)other {
if (other == self)
return YES;
if (!other || ![other isKindOfClass:[self class]])
return NO;
return [self isEqualToBWApp:other];
if (other == self)
return YES;
if (!other || ![other isKindOfClass:[self class]])
return NO;
return [self isEqualToBWApp:other];
}
- (BOOL)isEqualToBWApp:(BWApp *)anApp {
if (self == anApp)
return YES;
if (self.name != anApp.name && ![self.name isEqualToString:anApp.name])
return NO;
if (self.version != anApp.version && ![self.version isEqualToString:anApp.version])
return NO;
if (self.shortVersion != anApp.shortVersion && ![self.shortVersion isEqualToString:anApp.shortVersion])
return NO;
if (self.notes != anApp.notes && ![self.notes isEqualToString:anApp.notes])
return NO;
if (self.date != anApp.date && ![self.date isEqualToDate:anApp.date])
return NO;
if (self.size != anApp.size && ![self.size isEqualToNumber:anApp.size])
return NO;
if (self.mandatory != anApp.mandatory && ![self.mandatory isEqualToNumber:anApp.mandatory])
return NO;
if (self == anApp)
return YES;
if (self.name != anApp.name && ![self.name isEqualToString:anApp.name])
return NO;
if (self.version != anApp.version && ![self.version isEqualToString:anApp.version])
return NO;
if (self.shortVersion != anApp.shortVersion && ![self.shortVersion isEqualToString:anApp.shortVersion])
return NO;
if (self.notes != anApp.notes && ![self.notes isEqualToString:anApp.notes])
return NO;
if (self.date != anApp.date && ![self.date isEqualToDate:anApp.date])
return NO;
if (self.size != anApp.size && ![self.size isEqualToNumber:anApp.size])
return NO;
if (self.mandatory != anApp.mandatory && ![self.mandatory isEqualToNumber:anApp.mandatory])
return NO;
return YES;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -106,26 +106,26 @@
#pragma mark NSCoder
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeObject:self.version forKey:@"version"];
[encoder encodeObject:self.shortVersion forKey:@"shortVersion"];
[encoder encodeObject:self.notes forKey:@"notes"];
[encoder encodeObject:self.date forKey:@"date"];
[encoder encodeObject:self.size forKey:@"size"];
[encoder encodeObject:self.mandatory forKey:@"mandatory"];
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeObject:self.version forKey:@"version"];
[encoder encodeObject:self.shortVersion forKey:@"shortVersion"];
[encoder encodeObject:self.notes forKey:@"notes"];
[encoder encodeObject:self.date forKey:@"date"];
[encoder encodeObject:self.size forKey:@"size"];
[encoder encodeObject:self.mandatory forKey:@"mandatory"];
}
- (id)initWithCoder:(NSCoder *)decoder {
if ((self = [super init])) {
self.name = [decoder decodeObjectForKey:@"name"];
self.version = [decoder decodeObjectForKey:@"version"];
self.shortVersion = [decoder decodeObjectForKey:@"shortVersion"];
self.notes = [decoder decodeObjectForKey:@"notes"];
self.date = [decoder decodeObjectForKey:@"date"];
self.size = [decoder decodeObjectForKey:@"size"];
self.mandatory = [decoder decodeObjectForKey:@"mandatory"];
}
return self;
if ((self = [super init])) {
self.name = [decoder decodeObjectForKey:@"name"];
self.version = [decoder decodeObjectForKey:@"version"];
self.shortVersion = [decoder decodeObjectForKey:@"shortVersion"];
self.notes = [decoder decodeObjectForKey:@"notes"];
self.date = [decoder decodeObjectForKey:@"date"];
self.size = [decoder decodeObjectForKey:@"size"];
self.mandatory = [decoder decodeObjectForKey:@"mandatory"];
}
return self;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -133,54 +133,54 @@
#pragma mark Properties
- (NSString *)nameAndVersionString {
NSString *appNameAndVersion = [NSString stringWithFormat:@"%@ %@", self.name, [self versionString]];
return appNameAndVersion;
NSString *appNameAndVersion = [NSString stringWithFormat:@"%@ %@", self.name, [self versionString]];
return appNameAndVersion;
}
- (NSString *)versionString {
NSString *shortString = ([self.shortVersion respondsToSelector:@selector(length)] && [self.shortVersion length]) ? [NSString stringWithFormat:@"%@", self.shortVersion] : @"";
NSString *versionString = [shortString length] ? [NSString stringWithFormat:@" (%@)", self.version] : self.version;
return [NSString stringWithFormat:@"%@ %@%@", BWHockeyLocalize(@"HockeyVersion"), shortString, versionString];
NSString *shortString = ([self.shortVersion respondsToSelector:@selector(length)] && [self.shortVersion length]) ? [NSString stringWithFormat:@"%@", self.shortVersion] : @"";
NSString *versionString = [shortString length] ? [NSString stringWithFormat:@" (%@)", self.version] : self.version;
return [NSString stringWithFormat:@"%@ %@%@", BWHockeyLocalize(@"HockeyVersion"), shortString, versionString];
}
- (NSString *)dateString {
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateStyle:NSDateFormatterMediumStyle];
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateStyle:NSDateFormatterMediumStyle];
return [formatter stringFromDate:self.date];
return [formatter stringFromDate:self.date];
}
- (NSString *)sizeInMB {
if ([size_ isKindOfClass: [NSNumber class]] && [size_ doubleValue] > 0) {
double appSizeInMB = [size_ doubleValue]/(1024*1024);
NSString *appSizeString = [NSString stringWithFormat:@"%.1f MB", appSizeInMB];
return appSizeString;
}
if ([size_ isKindOfClass: [NSNumber class]] && [size_ doubleValue] > 0) {
double appSizeInMB = [size_ doubleValue]/(1024*1024);
NSString *appSizeString = [NSString stringWithFormat:@"%.1f MB", appSizeInMB];
return appSizeString;
}
return @"0 MB";
return @"0 MB";
}
- (void)setDateWithTimestamp:(NSTimeInterval)timestamp {
if (timestamp) {
NSDate *appDate = [NSDate dateWithTimeIntervalSince1970:timestamp];
self.date = appDate;
} else {
self.date = nil;
}
if (timestamp) {
NSDate *appDate = [NSDate dateWithTimeIntervalSince1970:timestamp];
self.date = appDate;
} else {
self.date = nil;
}
}
- (NSString *)notesOrEmptyString {
if (self.notes) {
return self.notes;
}else {
return [NSString string];
}
if (self.notes) {
return self.notes;
}else {
return [NSString string];
}
}
// a valid app needs at least following properties: name, version, date
- (BOOL)isValid {
BOOL valid = [self.name length] && [self.version length] && self.date;
return valid;
BOOL valid = [self.name length] && [self.version length] && self.date;
return valid;
}
@end

View File

@ -26,13 +26,13 @@
#include <CommonCrypto/CommonDigest.h>
NSBundle *hockeyBundle(void) {
static NSBundle* bundle = nil;
if (!bundle) {
NSString* path = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:kHockeyBundleName];
bundle = [[NSBundle bundleWithPath:path] retain];
}
return bundle;
static NSBundle* bundle = nil;
if (!bundle) {
NSString* path = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:kHockeyBundleName];
bundle = [[NSBundle bundleWithPath:path] retain];
}
return bundle;
}
NSString *BWmd5(NSString *str) {
@ -40,22 +40,22 @@ NSString *BWmd5(NSString *str) {
unsigned char result[CC_MD5_DIGEST_LENGTH];
CC_MD5( cStr, strlen(cStr), result );
return [NSString
stringWithFormat: @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
result[0], result[1],
result[2], result[3],
result[4], result[5],
result[6], result[7],
result[8], result[9],
result[10], result[11],
result[12], result[13],
result[14], result[15]
];
stringWithFormat: @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
result[0], result[1],
result[2], result[3],
result[4], result[5],
result[6], result[7],
result[8], result[9],
result[10], result[11],
result[12], result[13],
result[14], result[15]
];
}
NSString *BWHockeyLocalize(NSString *stringToken) {
if (hockeyBundle()) {
return NSLocalizedStringFromTableInBundle(stringToken, @"Hockey", hockeyBundle(), @"");
} else {
return stringToken;
}
if (hockeyBundle()) {
return NSLocalizedStringFromTableInBundle(stringToken, @"Hockey", hockeyBundle(), @"");
} else {
return stringToken;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -7,11 +7,11 @@
//
#import <UIKit/UIKit.h>
@class BWHockeyManager;
@interface BWHockeySettingsViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
BWHockeyManager *hockeyManager_;
BWHockeyManager *hockeyManager_;
}
@property (nonatomic, retain) BWHockeyManager *hockeyManager;

View File

@ -24,41 +24,41 @@
#pragma mark Initialization
- (id)init:(BWHockeyManager *)newHockeyManager {
if ((self = [super init])) {
self.hockeyManager = newHockeyManager;
self.title = BWHockeyLocalize(@"HockeySettingsTitle");
if ((self = [super init])) {
self.hockeyManager = newHockeyManager;
self.title = BWHockeyLocalize(@"HockeySettingsTitle");
CGRect frame = self.view.frame;
frame.origin = CGPointZero;
CGRect frame = self.view.frame;
frame.origin = CGPointZero;
UITableView *tableView_ = [[[UITableView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 260, self.view.frame.size.width, 260) style:UITableViewStyleGrouped] autorelease];
tableView_.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
UITableView *tableView_ = [[[UITableView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 260, self.view.frame.size.width, 260) style:UITableViewStyleGrouped] autorelease];
tableView_.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleWidth;
BW_IF_3_2_OR_GREATER(
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
self.view.backgroundColor = BW_RGBCOLOR(200, 202, 204);
tableView_.backgroundColor = BW_RGBCOLOR(200, 202, 204);
} else {
tableView_.frame = frame;
tableView_.autoresizingMask = tableView_.autoresizingMask | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
}
)
BW_IF_PRE_3_2(
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:@selector(dismissSettings)] autorelease];
tableView_.frame = frame;
tableView_.autoresizingMask = tableView_.autoresizingMask | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
)
BW_IF_3_2_OR_GREATER(
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
self.view.backgroundColor = BW_RGBCOLOR(200, 202, 204);
tableView_.backgroundColor = BW_RGBCOLOR(200, 202, 204);
} else {
tableView_.frame = frame;
tableView_.autoresizingMask = tableView_.autoresizingMask | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
}
)
BW_IF_PRE_3_2(
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:@selector(dismissSettings)] autorelease];
tableView_.frame = frame;
tableView_.autoresizingMask = tableView_.autoresizingMask | UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin;
)
tableView_.delegate = self;
tableView_.dataSource = self;
tableView_.clipsToBounds = NO;
tableView_.delegate = self;
tableView_.dataSource = self;
tableView_.clipsToBounds = NO;
[self.view addSubview:tableView_];
[self.view addSubview:tableView_];
}
return self;
}
return self;
}
- (id)init {
@ -69,165 +69,165 @@
#pragma mark Table view data source
- (int)numberOfSections {
int numberOfSections = 1;
int numberOfSections = 1;
if ([self.hockeyManager isAllowUserToDisableSendData]) {
if ([self.hockeyManager shouldSendUserData]) numberOfSections++;
if ([self.hockeyManager shouldSendUsageTime]) numberOfSections++;
}
if ([self.hockeyManager isAllowUserToDisableSendData]) {
if ([self.hockeyManager shouldSendUserData]) numberOfSections++;
if ([self.hockeyManager shouldSendUsageTime]) numberOfSections++;
}
return numberOfSections;
return numberOfSections;
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
if (section == [self numberOfSections] - 1) {
return BWHockeyLocalize(@"HockeySectionCheckTitle");
} else {
return nil;
}
if (section == [self numberOfSections] - 1) {
return BWHockeyLocalize(@"HockeySectionCheckTitle");
} else {
return nil;
}
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
if (section < [self numberOfSections] - 1) {
return 66;
} else return 0;
if (section < [self numberOfSections] - 1) {
return 66;
} else return 0;
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
if ([self numberOfSections] > 1 && section < [self numberOfSections] - 1) {
UILabel *footer = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 285, 66)] autorelease];
footer.backgroundColor = [UIColor clearColor];
footer.numberOfLines = 3;
footer.textAlignment = UITextAlignmentCenter;
footer.adjustsFontSizeToFitWidth = YES;
footer.textColor = [UIColor grayColor];
footer.font = [UIFont systemFontOfSize:13];
if ([self numberOfSections] > 1 && section < [self numberOfSections] - 1) {
UILabel *footer = [[[UILabel alloc] initWithFrame:CGRectMake(0, 0, 285, 66)] autorelease];
footer.backgroundColor = [UIColor clearColor];
footer.numberOfLines = 3;
footer.textAlignment = UITextAlignmentCenter;
footer.adjustsFontSizeToFitWidth = YES;
footer.textColor = [UIColor grayColor];
footer.font = [UIFont systemFontOfSize:13];
if (section == 0 && [self.hockeyManager isAllowUserToDisableSendData] && [self.hockeyManager shouldSendUserData]) {
footer.text = BWHockeyLocalize(@"HockeySettingsUserDataDescription");
} else if ([self.hockeyManager isAllowUserToDisableSendData] && section < [self numberOfSections]) {
footer.text = BWHockeyLocalize(@"HockeySettingsUsageDataDescription");
}
UIView* view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 285, footer.frame.size.height + 6 + 11)] autorelease];
[view setBackgroundColor:[UIColor clearColor]];
CGRect frame = footer.frame;
frame.origin.y = 8;
frame.origin.x = 16;
frame.size.width = 285;
footer.frame = frame;
[view addSubview:footer];
[view sizeToFit];
return view;
if (section == 0 && [self.hockeyManager isAllowUserToDisableSendData] && [self.hockeyManager shouldSendUserData]) {
footer.text = BWHockeyLocalize(@"HockeySettingsUserDataDescription");
} else if ([self.hockeyManager isAllowUserToDisableSendData] && section < [self numberOfSections]) {
footer.text = BWHockeyLocalize(@"HockeySettingsUsageDataDescription");
}
return nil;
UIView* view = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 285, footer.frame.size.height + 6 + 11)] autorelease];
[view setBackgroundColor:[UIColor clearColor]];
CGRect frame = footer.frame;
frame.origin.y = 8;
frame.origin.x = 16;
frame.size.width = 285;
footer.frame = frame;
[view addSubview:footer];
[view sizeToFit];
return view;
}
return nil;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
// Return the number of sections.
return [self numberOfSections];
// Return the number of sections.
return [self numberOfSections];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
// Return the number of rows in the section.
if (section == [self numberOfSections] - 1)
return 3;
else
return 1;
// Return the number of rows in the section.
if (section == [self numberOfSections] - 1)
return 3;
else
return 1;
}
- (void)sendUserData:(UISwitch *)switcher {
[self.hockeyManager setUserAllowsSendUserData:switcher.on];
[self.hockeyManager setUserAllowsSendUserData:switcher.on];
}
- (void)sendUsageData:(UISwitch *)switcher {
[self.hockeyManager setUserAllowsSendUsageTime:switcher.on];
[self.hockeyManager setUserAllowsSendUsageTime:switcher.on];
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CheckmarkCellIdentifier = @"CheckmarkCell";
static NSString *SwitchCellIdentifier = @"SwitchCell";
static NSString *CheckmarkCellIdentifier = @"CheckmarkCell";
static NSString *SwitchCellIdentifier = @"SwitchCell";
NSString *requiredIdentifier = nil;
UITableViewCellStyle cellStyle = UITableViewCellStyleSubtitle;
NSString *requiredIdentifier = nil;
UITableViewCellStyle cellStyle = UITableViewCellStyleSubtitle;
if ((NSInteger)indexPath.section == [self numberOfSections] - 1) {
cellStyle = UITableViewCellStyleDefault;
requiredIdentifier = CheckmarkCellIdentifier;
if ((NSInteger)indexPath.section == [self numberOfSections] - 1) {
cellStyle = UITableViewCellStyleDefault;
requiredIdentifier = CheckmarkCellIdentifier;
} else {
cellStyle = UITableViewCellStyleValue1;
requiredIdentifier = SwitchCellIdentifier;
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:requiredIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:cellStyle reuseIdentifier:requiredIdentifier] autorelease];
}
cell.accessoryType = UITableViewCellAccessoryNone;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// Configure the cell...
if ((NSInteger)indexPath.section == [self numberOfSections] - 1) {
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
// update check selection
HockeyUpdateSetting hockeyAutoUpdateSetting = [[BWHockeyManager sharedHockeyManager] updateSetting];
if (indexPath.row == 0) {
// on startup
cell.textLabel.text = BWHockeyLocalize(@"HockeySectionCheckStartup");
if (hockeyAutoUpdateSetting == HockeyUpdateCheckStartup) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
} else if (indexPath.row == 1) {
// daily
cell.textLabel.text = BWHockeyLocalize(@"HockeySectionCheckDaily");
if (hockeyAutoUpdateSetting == HockeyUpdateCheckDaily) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
} else {
cellStyle = UITableViewCellStyleValue1;
requiredIdentifier = SwitchCellIdentifier;
// manually
cell.textLabel.text = BWHockeyLocalize(@"HockeySectionCheckManually");
if (hockeyAutoUpdateSetting == HockeyUpdateCheckManually) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
} else {
UISwitch *toggleSwitch = [[[UISwitch alloc] initWithFrame:CGRectZero] autorelease];
if (indexPath.section == 0 && [self.hockeyManager shouldSendUserData] && [self.hockeyManager isAllowUserToDisableSendData]) {
// send user data
cell.textLabel.text = BWHockeyLocalize(@"HockeySettingsUserData");
[toggleSwitch addTarget:self action:@selector(sendUserData:)
forControlEvents:UIControlEventValueChanged];
[toggleSwitch setOn:[self.hockeyManager doesUserAllowsSendUserData]];
} else if ([self.hockeyManager shouldSendUsageTime] && [self.hockeyManager isAllowUserToDisableSendData]) {
// send usage time
cell.textLabel.text = BWHockeyLocalize(@"HockeySettingsUsageData");
[toggleSwitch addTarget:self action:@selector(sendUsageData:)
forControlEvents:UIControlEventValueChanged];
[toggleSwitch setOn:[self.hockeyManager doesUserAllowsSendUsageTime]];
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:requiredIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:cellStyle reuseIdentifier:requiredIdentifier] autorelease];
}
cell.accessoryView = toggleSwitch;
cell.accessoryType = UITableViewCellAccessoryNone;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
}
// Configure the cell...
if ((NSInteger)indexPath.section == [self numberOfSections] - 1) {
cell.selectionStyle = UITableViewCellSelectionStyleBlue;
// update check selection
HockeyUpdateSetting hockeyAutoUpdateSetting = [[BWHockeyManager sharedHockeyManager] updateSetting];
if (indexPath.row == 0) {
// on startup
cell.textLabel.text = BWHockeyLocalize(@"HockeySectionCheckStartup");
if (hockeyAutoUpdateSetting == HockeyUpdateCheckStartup) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
} else if (indexPath.row == 1) {
// daily
cell.textLabel.text = BWHockeyLocalize(@"HockeySectionCheckDaily");
if (hockeyAutoUpdateSetting == HockeyUpdateCheckDaily) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
} else {
// manually
cell.textLabel.text = BWHockeyLocalize(@"HockeySectionCheckManually");
if (hockeyAutoUpdateSetting == HockeyUpdateCheckManually) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
}
} else {
UISwitch *toggleSwitch = [[[UISwitch alloc] initWithFrame:CGRectZero] autorelease];
if (indexPath.section == 0 && [self.hockeyManager shouldSendUserData] && [self.hockeyManager isAllowUserToDisableSendData]) {
// send user data
cell.textLabel.text = BWHockeyLocalize(@"HockeySettingsUserData");
[toggleSwitch addTarget:self action:@selector(sendUserData:)
forControlEvents:UIControlEventValueChanged];
[toggleSwitch setOn:[self.hockeyManager doesUserAllowsSendUserData]];
} else if ([self.hockeyManager shouldSendUsageTime] && [self.hockeyManager isAllowUserToDisableSendData]) {
// send usage time
cell.textLabel.text = BWHockeyLocalize(@"HockeySettingsUsageData");
[toggleSwitch addTarget:self action:@selector(sendUsageData:)
forControlEvents:UIControlEventValueChanged];
[toggleSwitch setOn:[self.hockeyManager doesUserAllowsSendUsageTime]];
}
cell.accessoryView = toggleSwitch;
}
return cell;
return cell;
}
@ -235,21 +235,21 @@
#pragma mark Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
// update check interval selection
if (indexPath.row == 0) {
// on startup
[BWHockeyManager sharedHockeyManager].updateSetting = HockeyUpdateCheckStartup;
} else if (indexPath.row == 1) {
// daily
[BWHockeyManager sharedHockeyManager].updateSetting = HockeyUpdateCheckDaily;
} else {
// manually
[BWHockeyManager sharedHockeyManager].updateSetting = HockeyUpdateCheckManually;
}
// update check interval selection
if (indexPath.row == 0) {
// on startup
[BWHockeyManager sharedHockeyManager].updateSetting = HockeyUpdateCheckStartup;
} else if (indexPath.row == 1) {
// daily
[BWHockeyManager sharedHockeyManager].updateSetting = HockeyUpdateCheckDaily;
} else {
// manually
[BWHockeyManager sharedHockeyManager].updateSetting = HockeyUpdateCheckManually;
}
[tableView reloadData];
[tableView reloadData];
}
@ -257,20 +257,20 @@
#pragma mark Memory management
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Relinquish ownership any cached data, images, etc. that aren't in use.
// Relinquish ownership any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
// For example: self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
[super dealloc];
}
@ -279,15 +279,15 @@
#pragma mark Rotation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
BOOL shouldAutorotate;
BOOL shouldAutorotate;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
shouldAutorotate = (interfaceOrientation == UIInterfaceOrientationPortrait);
} else {
shouldAutorotate = YES;
}
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
shouldAutorotate = (interfaceOrientation == UIInterfaceOrientationPortrait);
} else {
shouldAutorotate = YES;
}
return shouldAutorotate;
return shouldAutorotate;
}
@end

View File

@ -39,22 +39,22 @@ typedef enum {
@class BWHockeyManager;
@interface BWHockeyViewController : UITableViewController <PSStoreButtonDelegate> {
BWHockeyManager *hockeyManager_;
BWHockeyManager *hockeyManager_;
NSDictionary *cellLayout;
NSDictionary *cellLayout;
BOOL modal_;
BOOL kvoRegistered_;
BOOL showAllVersions_;
UIStatusBarStyle statusBarStyle_;
PSAppStoreHeader *appStoreHeader_;
PSStoreButton *appStoreButton_;
BOOL modal_;
BOOL kvoRegistered_;
BOOL showAllVersions_;
UIStatusBarStyle statusBarStyle_;
PSAppStoreHeader *appStoreHeader_;
PSStoreButton *appStoreButton_;
id popOverController_;
id popOverController_;
AppStoreButtonState appStoreButtonState_;
AppStoreButtonState appStoreButtonState_;
NSMutableArray *cells_;
NSMutableArray *cells_;
}
@property (nonatomic, retain) BWHockeyManager *hockeyManager;

View File

@ -56,174 +56,174 @@
#pragma mark private
- (void)restoreStoreButtonStateAnimated_:(BOOL)animated {
if ([self.hockeyManager isAppStoreEnvironment]) {
[self setAppStoreButtonState:AppStoreButtonStateOffline animated:animated];
} else if ([self.hockeyManager isUpdateAvailable]) {
[self setAppStoreButtonState:AppStoreButtonStateUpdate animated:animated];
} else {
[self setAppStoreButtonState:AppStoreButtonStateCheck animated:animated];
}
if ([self.hockeyManager isAppStoreEnvironment]) {
[self setAppStoreButtonState:AppStoreButtonStateOffline animated:animated];
} else if ([self.hockeyManager isUpdateAvailable]) {
[self setAppStoreButtonState:AppStoreButtonStateUpdate animated:animated];
} else {
[self setAppStoreButtonState:AppStoreButtonStateCheck animated:animated];
}
}
- (void)updateAppStoreHeader_ {
BWApp *app = self.hockeyManager.app;
appStoreHeader_.headerLabel = app.name;
appStoreHeader_.middleHeaderLabel = [app versionString];
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateStyle:NSDateFormatterMediumStyle];
NSMutableString *subHeaderString = [NSMutableString string];
if (app.date) {
[subHeaderString appendString:[formatter stringFromDate:app.date]];
BWApp *app = self.hockeyManager.app;
appStoreHeader_.headerLabel = app.name;
appStoreHeader_.middleHeaderLabel = [app versionString];
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
[formatter setDateStyle:NSDateFormatterMediumStyle];
NSMutableString *subHeaderString = [NSMutableString string];
if (app.date) {
[subHeaderString appendString:[formatter stringFromDate:app.date]];
}
if (app.size) {
if ([subHeaderString length]) {
[subHeaderString appendString:@" - "];
}
if (app.size) {
if ([subHeaderString length]) {
[subHeaderString appendString:@" - "];
}
[subHeaderString appendString:app.sizeInMB];
}
appStoreHeader_.subHeaderLabel = subHeaderString;
[subHeaderString appendString:app.sizeInMB];
}
appStoreHeader_.subHeaderLabel = subHeaderString;
}
- (void)appDidBecomeActive_ {
if (self.appStoreButtonState == AppStoreButtonStateInstalling) {
[self setAppStoreButtonState:AppStoreButtonStateUpdate animated:YES];
} else if (![self.hockeyManager isCheckInProgress]) {
[self restoreStoreButtonStateAnimated_:YES];
}
if (self.appStoreButtonState == AppStoreButtonStateInstalling) {
[self setAppStoreButtonState:AppStoreButtonStateUpdate animated:YES];
} else if (![self.hockeyManager isCheckInProgress]) {
[self restoreStoreButtonStateAnimated_:YES];
}
}
- (void)openSettings:(id)sender {
BWHockeySettingsViewController *settings = [[[BWHockeySettingsViewController alloc] init] autorelease];
BWHockeySettingsViewController *settings = [[[BWHockeySettingsViewController alloc] init] autorelease];
Class popoverControllerClass = NSClassFromString(@"UIPopoverController");
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad && popoverControllerClass) {
if (popOverController_ == nil) {
popOverController_ = [[popoverControllerClass alloc] initWithContentViewController:settings];
}
if ([popOverController_ contentViewController].view.window) {
[popOverController_ dismissPopoverAnimated:YES];
}else {
[popOverController_ setPopoverContentSize: CGSizeMake(320, 440)];
[popOverController_ presentPopoverFromBarButtonItem:self.navigationItem.rightBarButtonItem
permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
} else {
BW_IF_3_2_OR_GREATER(
settings.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentModalViewController:settings animated:YES];
)
BW_IF_PRE_3_2(
UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:settings] autorelease];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:navController animated:YES];
)
Class popoverControllerClass = NSClassFromString(@"UIPopoverController");
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad && popoverControllerClass) {
if (popOverController_ == nil) {
popOverController_ = [[popoverControllerClass alloc] initWithContentViewController:settings];
}
if ([popOverController_ contentViewController].view.window) {
[popOverController_ dismissPopoverAnimated:YES];
}else {
[popOverController_ setPopoverContentSize: CGSizeMake(320, 440)];
[popOverController_ presentPopoverFromBarButtonItem:self.navigationItem.rightBarButtonItem
permittedArrowDirections:UIPopoverArrowDirectionUp animated:YES];
}
} else {
BW_IF_3_2_OR_GREATER(
settings.modalTransitionStyle = UIModalTransitionStylePartialCurl;
[self presentModalViewController:settings animated:YES];
)
BW_IF_PRE_3_2(
UINavigationController *navController = [[[UINavigationController alloc] initWithRootViewController:settings] autorelease];
navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
[self presentModalViewController:navController animated:YES];
)
}
}
- (UIImage *)addGlossToImage_:(UIImage *)image {
BW_IF_IOS4_OR_GREATER(UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0);)
BW_IF_PRE_IOS4(UIGraphicsBeginImageContext(image.size);)
BW_IF_IOS4_OR_GREATER(UIGraphicsBeginImageContextWithOptions(image.size, NO, 0.0);)
BW_IF_PRE_IOS4(UIGraphicsBeginImageContext(image.size);)
[image drawAtPoint:CGPointZero];
UIImage *iconGradient = [UIImage bw_imageNamed:@"IconGradient.png" bundle:kHockeyBundleName];
[iconGradient drawInRect:CGRectMake(0, 0, image.size.width, image.size.height) blendMode:kCGBlendModeNormal alpha:0.5];
[image drawAtPoint:CGPointZero];
UIImage *iconGradient = [UIImage bw_imageNamed:@"IconGradient.png" bundle:kHockeyBundleName];
[iconGradient drawInRect:CGRectMake(0, 0, image.size.width, image.size.height) blendMode:kCGBlendModeNormal alpha:0.5];
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return result;
return result;
}
#define kMinPreviousVersionButtonHeight 50
- (void)realignPreviousVersionButton {
// manually collect actual table height size
NSUInteger tableViewContentHeight = 0;
for (int i=0; i < [self tableView:self.tableView numberOfRowsInSection:0]; i++) {
tableViewContentHeight += [self tableView:self.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
}
tableViewContentHeight += self.tableView.tableHeaderView.frame.size.height;
// manually collect actual table height size
NSUInteger tableViewContentHeight = 0;
for (int i=0; i < [self tableView:self.tableView numberOfRowsInSection:0]; i++) {
tableViewContentHeight += [self tableView:self.tableView heightForRowAtIndexPath:[NSIndexPath indexPathForRow:i inSection:0]];
}
tableViewContentHeight += self.tableView.tableHeaderView.frame.size.height;
NSUInteger footerViewSize = kMinPreviousVersionButtonHeight;
NSUInteger frameHeight = self.view.frame.size.height;
if(tableViewContentHeight < frameHeight && (frameHeight - tableViewContentHeight > 100)) {
footerViewSize = frameHeight - tableViewContentHeight;
}
NSUInteger footerViewSize = kMinPreviousVersionButtonHeight;
NSUInteger frameHeight = self.view.frame.size.height;
if(tableViewContentHeight < frameHeight && (frameHeight - tableViewContentHeight > 100)) {
footerViewSize = frameHeight - tableViewContentHeight;
}
// update footer view
if(self.tableView.tableFooterView) {
CGRect frame = self.tableView.tableFooterView.frame;
frame.size.height = footerViewSize;
self.tableView.tableFooterView.frame = frame;
}
// update footer view
if(self.tableView.tableFooterView) {
CGRect frame = self.tableView.tableFooterView.frame;
frame.size.height = footerViewSize;
self.tableView.tableFooterView.frame = frame;
}
}
- (void)changePreviousVersionButtonBackground:(id)sender {
[(UIButton *)sender setBackgroundColor:BW_RGBCOLOR(183,183,183)];
[(UIButton *)sender setBackgroundColor:BW_RGBCOLOR(183,183,183)];
}
- (void)changePreviousVersionButtonBackgroundHighlighted:(id)sender {
[(UIButton *)sender setBackgroundColor:BW_RGBCOLOR(183,183,183)];
[(UIButton *)sender setBackgroundColor:BW_RGBCOLOR(183,183,183)];
}
- (void)showHidePreviousVersionsButton {
BOOL multipleVersionButtonNeeded = [self.hockeyManager.apps count] > 1 && !showAllVersions_;
BOOL multipleVersionButtonNeeded = [self.hockeyManager.apps count] > 1 && !showAllVersions_;
if(multipleVersionButtonNeeded) {
// align at the bottom if tableview is small
UIView *footerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, kMinPreviousVersionButtonHeight)];
footerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
footerView.backgroundColor = BW_RGBCOLOR(200, 202, 204);
UIButton *footerButton = [UIButton buttonWithType:UIButtonTypeCustom];
BW_IF_IOS4_OR_GREATER(
//footerButton.layer.shadowOffset = CGSizeMake(-2, 2);
footerButton.layer.shadowColor = [[UIColor blackColor] CGColor];
footerButton.layer.shadowRadius = 2.0f;
)
footerButton.titleLabel.font = [UIFont boldSystemFontOfSize:14];
[footerButton setTitle:BWHockeyLocalize(@"HockeyShowPreviousVersions") forState:UIControlStateNormal];
[footerButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[footerButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
[footerButton setBackgroundImage:[UIImage bw_imageNamed:@"buttonHighlight.png" bundle:kHockeyBundleName] forState:UIControlStateHighlighted];
footerButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
[footerButton addTarget:self action:@selector(showPreviousVersionAction) forControlEvents:UIControlEventTouchUpInside];
footerButton.frame = CGRectMake(0, kMinPreviousVersionButtonHeight-44, self.view.frame.size.width, 44);
footerButton.backgroundColor = BW_RGBCOLOR(183,183,183);
[footerView addSubview:footerButton];
self.tableView.tableFooterView = footerView;
[self realignPreviousVersionButton];
[footerView release];
} else {
self.tableView.tableFooterView = nil;
self.tableView.backgroundColor = BW_RGBCOLOR(200, 202, 204);
}
if(multipleVersionButtonNeeded) {
// align at the bottom if tableview is small
UIView *footerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, kMinPreviousVersionButtonHeight)];
footerView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
footerView.backgroundColor = BW_RGBCOLOR(200, 202, 204);
UIButton *footerButton = [UIButton buttonWithType:UIButtonTypeCustom];
BW_IF_IOS4_OR_GREATER(
//footerButton.layer.shadowOffset = CGSizeMake(-2, 2);
footerButton.layer.shadowColor = [[UIColor blackColor] CGColor];
footerButton.layer.shadowRadius = 2.0f;
)
footerButton.titleLabel.font = [UIFont boldSystemFontOfSize:14];
[footerButton setTitle:BWHockeyLocalize(@"HockeyShowPreviousVersions") forState:UIControlStateNormal];
[footerButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
[footerButton setTitleColor:[UIColor whiteColor] forState:UIControlStateHighlighted];
[footerButton setBackgroundImage:[UIImage bw_imageNamed:@"buttonHighlight.png" bundle:kHockeyBundleName] forState:UIControlStateHighlighted];
footerButton.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
[footerButton addTarget:self action:@selector(showPreviousVersionAction) forControlEvents:UIControlEventTouchUpInside];
footerButton.frame = CGRectMake(0, kMinPreviousVersionButtonHeight-44, self.view.frame.size.width, 44);
footerButton.backgroundColor = BW_RGBCOLOR(183,183,183);
[footerView addSubview:footerButton];
self.tableView.tableFooterView = footerView;
[self realignPreviousVersionButton];
[footerView release];
} else {
self.tableView.tableFooterView = nil;
self.tableView.backgroundColor = BW_RGBCOLOR(200, 202, 204);
}
}
- (void)configureWebCell:(PSWebTableViewCell *)cell forApp_:(BWApp *)app {
// create web view for a version
NSString *installed = @"";
if ([app.version isEqualToString:[self.hockeyManager currentAppVersion]]) {
installed = [NSString stringWithFormat:@"<span style=\"float:%@;text-shadow:rgba(255,255,255,0.6) 1px 1px 0px;\"><b>%@</b></span>", [app isEqual:self.hockeyManager.app] ? @"left" : @"right", BWHockeyLocalize(@"HockeyInstalled")];
}
// create web view for a version
NSString *installed = @"";
if ([app.version isEqualToString:[self.hockeyManager currentAppVersion]]) {
installed = [NSString stringWithFormat:@"<span style=\"float:%@;text-shadow:rgba(255,255,255,0.6) 1px 1px 0px;\"><b>%@</b></span>", [app isEqual:self.hockeyManager.app] ? @"left" : @"right", BWHockeyLocalize(@"HockeyInstalled")];
}
if ([app isEqual:self.hockeyManager.app]) {
if ([app.notes length] > 0) {
installed = [NSString stringWithFormat:@"<p>&nbsp;%@</p>", installed];
cell.webViewContent = [NSString stringWithFormat:@"%@%@", installed, app.notes];
} else {
cell.webViewContent = [NSString stringWithFormat:@"<div style=\"min-height:200px;vertical-align:middle;text-align:center;text-shadow:rgba(255,255,255,0.6) 1px 1px 0px;\">%@</div>", BWHockeyLocalize(@"HockeyNoReleaseNotesAvailable")];
}
if ([app isEqual:self.hockeyManager.app]) {
if ([app.notes length] > 0) {
installed = [NSString stringWithFormat:@"<p>&nbsp;%@</p>", installed];
cell.webViewContent = [NSString stringWithFormat:@"%@%@", installed, app.notes];
} else {
cell.webViewContent = [NSString stringWithFormat:@"<p><b style=\"text-shadow:rgba(255,255,255,0.6) 1px 1px 0px;\">%@</b>%@<br/><small>%@</small></p><p>%@</p>", [app versionString], installed, [app dateString], [app notesOrEmptyString]];
cell.webViewContent = [NSString stringWithFormat:@"<div style=\"min-height:200px;vertical-align:middle;text-align:center;text-shadow:rgba(255,255,255,0.6) 1px 1px 0px;\">%@</div>", BWHockeyLocalize(@"HockeyNoReleaseNotesAvailable")];
}
cell.cellBackgroundColor = BW_RGBCOLOR(200, 202, 204);
} else {
cell.webViewContent = [NSString stringWithFormat:@"<p><b style=\"text-shadow:rgba(255,255,255,0.6) 1px 1px 0px;\">%@</b>%@<br/><small>%@</small></p><p>%@</p>", [app versionString], installed, [app dateString], [app notesOrEmptyString]];
}
cell.cellBackgroundColor = BW_RGBCOLOR(200, 202, 204);
[cell addWebView];
// hack
cell.textLabel.text = @"";
[cell addWebView];
// hack
cell.textLabel.text = @"";
[cell addObserver:self forKeyPath:@"webViewSize" options:0 context:nil];
[cell addObserver:self forKeyPath:@"webViewSize" options:0 context:nil];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -231,22 +231,22 @@
#pragma mark NSObject
- (id)init:(BWHockeyManager *)newHockeyManager modal:(BOOL)newModal {
if ((self = [super initWithStyle:UITableViewStylePlain])) {
self.hockeyManager = newHockeyManager;
self.modal = newModal;
self.title = BWHockeyLocalize(@"HockeyUpdateScreenTitle");
if ((self = [super initWithStyle:UITableViewStylePlain])) {
self.hockeyManager = newHockeyManager;
self.modal = newModal;
self.title = BWHockeyLocalize(@"HockeyUpdateScreenTitle");
if ([self.hockeyManager shouldShowUserSettings]) {
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithImage:[UIImage bw_imageNamed:@"gear.png" bundle:kHockeyBundleName]
style:UIBarButtonItemStyleBordered
target:self
action:@selector(openSettings:)] autorelease];
}
cells_ = [[NSMutableArray alloc] initWithCapacity:5];
popOverController_ = nil;
if ([self.hockeyManager shouldShowUserSettings]) {
self.navigationItem.rightBarButtonItem = [[[UIBarButtonItem alloc] initWithImage:[UIImage bw_imageNamed:@"gear.png" bundle:kHockeyBundleName]
style:UIBarButtonItemStyleBordered
target:self
action:@selector(openSettings:)] autorelease];
}
return self;
cells_ = [[NSMutableArray alloc] initWithCapacity:5];
popOverController_ = nil;
}
return self;
}
- (id)init {
@ -254,12 +254,12 @@
}
- (void)dealloc {
[self viewDidUnload];
for (UITableViewCell *cell in cells_) {
[cell removeObserver:self forKeyPath:@"webViewSize"];
}
[cells_ release];
[super dealloc];
[self viewDidUnload];
for (UITableViewCell *cell in cells_) {
[cell removeObserver:self forKeyPath:@"webViewSize"];
}
[cells_ release];
[super dealloc];
}
@ -268,30 +268,30 @@
#pragma mark View lifecycle
- (void)onAction:(id)sender {
if (self.modal) {
// Note that as of 5.0, parentViewController will no longer return the presenting view controller
SEL presentingViewControllerSelector = NSSelectorFromString(@"presentingViewController");
UIViewController *presentingViewController = nil;
if ([self respondsToSelector:presentingViewControllerSelector]) {
presentingViewController = [self performSelector:presentingViewControllerSelector];
}
else {
presentingViewController = [self parentViewController];
}
// If there is no presenting view controller just remove view
if (presentingViewController) {
[presentingViewController dismissModalViewControllerAnimated:YES];
}
else {
[self.navigationController.view removeFromSuperview];
}
if (self.modal) {
// Note that as of 5.0, parentViewController will no longer return the presenting view controller
SEL presentingViewControllerSelector = NSSelectorFromString(@"presentingViewController");
UIViewController *presentingViewController = nil;
if ([self respondsToSelector:presentingViewControllerSelector]) {
presentingViewController = [self performSelector:presentingViewControllerSelector];
}
else {
[self.navigationController popViewControllerAnimated:YES];
presentingViewController = [self parentViewController];
}
[[UIApplication sharedApplication] setStatusBarStyle:statusBarStyle_];
// If there is no presenting view controller just remove view
if (presentingViewController) {
[presentingViewController dismissModalViewControllerAnimated:YES];
}
else {
[self.navigationController.view removeFromSuperview];
}
}
else {
[self.navigationController popViewControllerAnimated:YES];
}
[[UIApplication sharedApplication] setStatusBarStyle:statusBarStyle_];
}
- (CAGradientLayer *)backgroundLayer {
@ -318,180 +318,180 @@
}
- (void)viewDidLoad {
[super viewDidLoad];
[super viewDidLoad];
// add notifications only to loaded view
NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
[dnc addObserver:self selector:@selector(appDidBecomeActive_) name:UIApplicationDidBecomeActiveNotification object:nil];
// add notifications only to loaded view
NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
[dnc addObserver:self selector:@selector(appDidBecomeActive_) name:UIApplicationDidBecomeActiveNotification object:nil];
// hook into manager with kvo!
[self.hockeyManager addObserver:self forKeyPath:@"checkInProgress" options:0 context:nil];
[self.hockeyManager addObserver:self forKeyPath:@"isUpdateURLOffline" options:0 context:nil];
[self.hockeyManager addObserver:self forKeyPath:@"updateAvailable" options:0 context:nil];
[self.hockeyManager addObserver:self forKeyPath:@"apps" options:0 context:nil];
kvoRegistered_ = YES;
// hook into manager with kvo!
[self.hockeyManager addObserver:self forKeyPath:@"checkInProgress" options:0 context:nil];
[self.hockeyManager addObserver:self forKeyPath:@"isUpdateURLOffline" options:0 context:nil];
[self.hockeyManager addObserver:self forKeyPath:@"updateAvailable" options:0 context:nil];
[self.hockeyManager addObserver:self forKeyPath:@"apps" options:0 context:nil];
kvoRegistered_ = YES;
self.tableView.backgroundColor = BW_RGBCOLOR(200, 202, 204);
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.backgroundColor = BW_RGBCOLOR(200, 202, 204);
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
UIView *topView = [[[UIView alloc] initWithFrame:CGRectMake(0, -(600-kAppStoreViewHeight), self.view.frame.size.width, 600)] autorelease];
topView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
topView.backgroundColor = BW_RGBCOLOR(140, 141, 142);
[self.tableView addSubview:topView];
UIView *topView = [[[UIView alloc] initWithFrame:CGRectMake(0, -(600-kAppStoreViewHeight), self.view.frame.size.width, 600)] autorelease];
topView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
topView.backgroundColor = BW_RGBCOLOR(140, 141, 142);
[self.tableView addSubview:topView];
appStoreHeader_ = [[PSAppStoreHeader alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, kAppStoreViewHeight)];
[self updateAppStoreHeader_];
appStoreHeader_ = [[PSAppStoreHeader alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, kAppStoreViewHeight)];
[self updateAppStoreHeader_];
NSString *iconString = nil;
NSArray *icons = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIconFiles"];
if (!icons) {
iconString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIconFile"];
if (!iconString) {
iconString = @"Icon.png";
}
} else {
BOOL useHighResIcon = NO;
BW_IF_IOS4_OR_GREATER(if ([UIScreen mainScreen].scale == 2.0f) useHighResIcon = YES;)
for(NSString *icon in icons) {
iconString = icon;
UIImage *iconImage = [UIImage imageNamed:icon];
if (iconImage.size.height == 57 && !useHighResIcon) {
// found!
break;
}
if (iconImage.size.height == 114 && useHighResIcon) {
// found!
break;
}
}
NSString *iconString = nil;
NSArray *icons = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIconFiles"];
if (!icons) {
iconString = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIconFile"];
if (!iconString) {
iconString = @"Icon.png";
}
} else {
BOOL useHighResIcon = NO;
BW_IF_IOS4_OR_GREATER(if ([UIScreen mainScreen].scale == 2.0f) useHighResIcon = YES;)
BOOL addGloss = YES;
NSNumber *prerendered = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIPrerenderedIcon"];
if (prerendered) {
addGloss = ![prerendered boolValue];
for(NSString *icon in icons) {
iconString = icon;
UIImage *iconImage = [UIImage imageNamed:icon];
if (iconImage.size.height == 57 && !useHighResIcon) {
// found!
break;
}
if (iconImage.size.height == 114 && useHighResIcon) {
// found!
break;
}
}
}
if (addGloss) {
appStoreHeader_.iconImage = [self addGlossToImage_:[UIImage imageNamed:iconString]];
} else {
appStoreHeader_.iconImage = [UIImage imageNamed:iconString];
}
BOOL addGloss = YES;
NSNumber *prerendered = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIPrerenderedIcon"];
if (prerendered) {
addGloss = ![prerendered boolValue];
}
self.tableView.tableHeaderView = appStoreHeader_;
if (addGloss) {
appStoreHeader_.iconImage = [self addGlossToImage_:[UIImage imageNamed:iconString]];
} else {
appStoreHeader_.iconImage = [UIImage imageNamed:iconString];
}
if (self.modal) {
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:@selector(onAction:)] autorelease];
}
self.tableView.tableHeaderView = appStoreHeader_;
PSStoreButton *storeButton = [[[PSStoreButton alloc] initWithPadding:CGPointMake(5, 40)] autorelease];
storeButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
storeButton.buttonDelegate = self;
[self.tableView.tableHeaderView addSubview:storeButton];
storeButton.buttonData = [PSStoreButtonData dataWithLabel:@"" colors:[PSStoreButton appStoreGrayColor] enabled:NO];
self.appStoreButtonState = AppStoreButtonStateCheck;
[storeButton alignToSuperview];
appStoreButton_ = [storeButton retain];
if (self.modal) {
self.navigationItem.leftBarButtonItem = [[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
target:self
action:@selector(onAction:)] autorelease];
}
PSStoreButton *storeButton = [[[PSStoreButton alloc] initWithPadding:CGPointMake(5, 40)] autorelease];
storeButton.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
storeButton.buttonDelegate = self;
[self.tableView.tableHeaderView addSubview:storeButton];
storeButton.buttonData = [PSStoreButtonData dataWithLabel:@"" colors:[PSStoreButton appStoreGrayColor] enabled:NO];
self.appStoreButtonState = AppStoreButtonStateCheck;
[storeButton alignToSuperview];
appStoreButton_ = [storeButton retain];
}
- (void)viewWillAppear:(BOOL)animated {
if ([self.hockeyManager isAppStoreEnvironment])
self.appStoreButtonState = AppStoreButtonStateOffline;
self.hockeyManager.currentHockeyViewController = self;
[super viewWillAppear:animated];
statusBarStyle_ = [[UIApplication sharedApplication] statusBarStyle];
[[UIApplication sharedApplication] setStatusBarStyle:(self.navigationController.navigationBar.barStyle == UIBarStyleDefault) ? UIStatusBarStyleDefault : UIStatusBarStyleBlackOpaque];
[self redrawTableView];
if ([self.hockeyManager isAppStoreEnvironment])
self.appStoreButtonState = AppStoreButtonStateOffline;
self.hockeyManager.currentHockeyViewController = self;
[super viewWillAppear:animated];
statusBarStyle_ = [[UIApplication sharedApplication] statusBarStyle];
[[UIApplication sharedApplication] setStatusBarStyle:(self.navigationController.navigationBar.barStyle == UIBarStyleDefault) ? UIStatusBarStyleDefault : UIStatusBarStyleBlackOpaque];
[self redrawTableView];
}
- (void)viewWillDisappear:(BOOL)animated {
self.hockeyManager.currentHockeyViewController = nil;
//if the popover is still visible, dismiss it
[popOverController_ dismissPopoverAnimated:YES];
[super viewWillDisappear:animated];
[[UIApplication sharedApplication] setStatusBarStyle:statusBarStyle_];
self.hockeyManager.currentHockeyViewController = nil;
//if the popover is still visible, dismiss it
[popOverController_ dismissPopoverAnimated:YES];
[super viewWillDisappear:animated];
[[UIApplication sharedApplication] setStatusBarStyle:statusBarStyle_];
}
- (void)redrawTableView {
[self restoreStoreButtonStateAnimated_:NO];
[self updateAppStoreHeader_];
[self restoreStoreButtonStateAnimated_:NO];
[self updateAppStoreHeader_];
// clean up and remove any pending overservers
for (UITableViewCell *cell in cells_) {
[cell removeObserver:self forKeyPath:@"webViewSize"];
}
[cells_ removeAllObjects];
// clean up and remove any pending overservers
for (UITableViewCell *cell in cells_) {
[cell removeObserver:self forKeyPath:@"webViewSize"];
}
[cells_ removeAllObjects];
int i = 0;
BOOL breakAfterThisApp = NO;
for (BWApp *app in self.hockeyManager.apps) {
i++;
int i = 0;
BOOL breakAfterThisApp = NO;
for (BWApp *app in self.hockeyManager.apps) {
i++;
// only show the newer version of the app by default, if we don't show all versions
if (!showAllVersions_) {
if ([app.version isEqualToString:[self.hockeyManager currentAppVersion]]) {
if (i == 1) {
breakAfterThisApp = YES;
} else {
break;
}
}
// only show the newer version of the app by default, if we don't show all versions
if (!showAllVersions_) {
if ([app.version isEqualToString:[self.hockeyManager currentAppVersion]]) {
if (i == 1) {
breakAfterThisApp = YES;
} else {
break;
}
PSWebTableViewCell *cell = [[[PSWebTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kWebCellIdentifier] autorelease];
[self configureWebCell:cell forApp_:app];
[cells_ addObject:cell];
if (breakAfterThisApp) break;
}
}
[self.tableView reloadData];
[self showHidePreviousVersionsButton];
PSWebTableViewCell *cell = [[[PSWebTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kWebCellIdentifier] autorelease];
[self configureWebCell:cell forApp_:app];
[cells_ addObject:cell];
if (breakAfterThisApp) break;
}
[self.tableView reloadData];
[self showHidePreviousVersionsButton];
}
- (void)showPreviousVersionAction {
showAllVersions_ = YES;
BOOL showAllPending = NO;
showAllVersions_ = YES;
BOOL showAllPending = NO;
for (BWApp *app in self.hockeyManager.apps) {
if (!showAllPending) {
if ([app.version isEqualToString:[self.hockeyManager currentAppVersion]]) {
showAllPending = YES;
if (app == self.hockeyManager.app) {
continue; // skip this version already if it the latest version is the installed one
}
} else {
continue; // skip already shown
}
for (BWApp *app in self.hockeyManager.apps) {
if (!showAllPending) {
if ([app.version isEqualToString:[self.hockeyManager currentAppVersion]]) {
showAllPending = YES;
if (app == self.hockeyManager.app) {
continue; // skip this version already if it the latest version is the installed one
}
PSWebTableViewCell *cell = [[[PSWebTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kWebCellIdentifier] autorelease];
[self configureWebCell:cell forApp_:app];
[cells_ addObject:cell];
} else {
continue; // skip already shown
}
}
[self.tableView reloadData];
[self showHidePreviousVersionsButton];
PSWebTableViewCell *cell = [[[PSWebTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kWebCellIdentifier] autorelease];
[self configureWebCell:cell forApp_:app];
[cells_ addObject:cell];
}
[self.tableView reloadData];
[self showHidePreviousVersionsButton];
}
- (void)viewDidUnload {
[appStoreHeader_ release]; appStoreHeader_ = nil;
[popOverController_ release], popOverController_ = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[appStoreHeader_ release]; appStoreHeader_ = nil;
[popOverController_ release], popOverController_ = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
// test if KVO's are registered. if class is destroyed before it was shown(viewDidLoad) no KVOs are registered.
if (kvoRegistered_) {
[self.hockeyManager removeObserver:self forKeyPath:@"checkInProgress"];
[self.hockeyManager removeObserver:self forKeyPath:@"isUpdateURLOffline"];
[self.hockeyManager removeObserver:self forKeyPath:@"updateAvailable"];
[self.hockeyManager removeObserver:self forKeyPath:@"apps"];
kvoRegistered_ = NO;
}
// test if KVO's are registered. if class is destroyed before it was shown(viewDidLoad) no KVOs are registered.
if (kvoRegistered_) {
[self.hockeyManager removeObserver:self forKeyPath:@"checkInProgress"];
[self.hockeyManager removeObserver:self forKeyPath:@"isUpdateURLOffline"];
[self.hockeyManager removeObserver:self forKeyPath:@"updateAvailable"];
[self.hockeyManager removeObserver:self forKeyPath:@"apps"];
kvoRegistered_ = NO;
}
[super viewDidUnload];
[super viewDidUnload];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -499,32 +499,32 @@
#pragma mark Table view data source
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
return 1;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
CGFloat rowHeight = 0;
CGFloat rowHeight = 0;
if ([cells_ count] > (NSUInteger)indexPath.row) {
PSWebTableViewCell *cell = [cells_ objectAtIndex:indexPath.row];
rowHeight = cell.webViewSize.height;
}
if ([cells_ count] > (NSUInteger)indexPath.row) {
PSWebTableViewCell *cell = [cells_ objectAtIndex:indexPath.row];
rowHeight = cell.webViewSize.height;
}
if ([self.hockeyManager.apps count] > 1 && !showAllVersions_) {
self.tableView.backgroundColor = BW_RGBCOLOR(183, 183, 183);
}
if ([self.hockeyManager.apps count] > 1 && !showAllVersions_) {
self.tableView.backgroundColor = BW_RGBCOLOR(183, 183, 183);
}
if (rowHeight == 0) {
rowHeight = indexPath.row == 0 ? 250 : 44; // fill screen on startup
self.tableView.backgroundColor = BW_RGBCOLOR(200, 202, 204);
}
if (rowHeight == 0) {
rowHeight = indexPath.row == 0 ? 250 : 44; // fill screen on startup
self.tableView.backgroundColor = BW_RGBCOLOR(200, 202, 204);
}
return rowHeight;
return rowHeight;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSInteger cellCount = [cells_ count];
return cellCount;
NSInteger cellCount = [cells_ count];
return cellCount;
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -535,32 +535,32 @@
// only make changes if we are visible
if(self.view.window) {
if ([keyPath isEqualToString:@"webViewSize"]) {
[self.tableView reloadData];
[self realignPreviousVersionButton];
[self.tableView reloadData];
[self realignPreviousVersionButton];
} else if ([keyPath isEqualToString:@"checkInProgress"]) {
if (self.hockeyManager.isCheckInProgress) {
[self setAppStoreButtonState:AppStoreButtonStateSearching animated:YES];
}else {
[self restoreStoreButtonStateAnimated_:YES];
}
if (self.hockeyManager.isCheckInProgress) {
[self setAppStoreButtonState:AppStoreButtonStateSearching animated:YES];
}else {
[self restoreStoreButtonStateAnimated_:YES];
}
} else if ([keyPath isEqualToString:@"isUpdateURLOffline"]) {
[self restoreStoreButtonStateAnimated_:YES];
[self restoreStoreButtonStateAnimated_:YES];
} else if ([keyPath isEqualToString:@"updateAvailable"]) {
[self restoreStoreButtonStateAnimated_:YES];
[self restoreStoreButtonStateAnimated_:YES];
} else if ([keyPath isEqualToString:@"apps"]) {
[self redrawTableView];
[self redrawTableView];
}
}
}
// Customize the appearance of table view cells.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if ([cells_ count] > (NSUInteger)indexPath.row) {
return [cells_ objectAtIndex:indexPath.row];
} else {
BWHockeyLog(@"Warning: cells_ and indexPath do not match? forgot calling redrawTableView?");
}
return nil;
if ([cells_ count] > (NSUInteger)indexPath.row) {
return [cells_ objectAtIndex:indexPath.row];
} else {
BWHockeyLog(@"Warning: cells_ and indexPath do not match? forgot calling redrawTableView?");
}
return nil;
}
@ -569,22 +569,22 @@
#pragma mark Rotation
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
BOOL shouldAutorotate;
BOOL shouldAutorotate;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
shouldAutorotate = (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationLandscapeRight ||
interfaceOrientation == UIInterfaceOrientationPortrait);
} else {
shouldAutorotate = YES;
}
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
shouldAutorotate = (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
interfaceOrientation == UIInterfaceOrientationLandscapeRight ||
interfaceOrientation == UIInterfaceOrientationPortrait);
} else {
shouldAutorotate = YES;
}
return shouldAutorotate;
return shouldAutorotate;
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
// update all cells
[cells_ makeObjectsPerformSelector:@selector(addWebView)];
// update all cells
[cells_ makeObjectsPerformSelector:@selector(addWebView)];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -592,46 +592,46 @@
#pragma mark PSAppStoreHeaderDelegate
- (void)setAppStoreButtonState:(AppStoreButtonState)anAppStoreButtonState {
[self setAppStoreButtonState:anAppStoreButtonState animated:NO];
[self setAppStoreButtonState:anAppStoreButtonState animated:NO];
}
- (void)setAppStoreButtonState:(AppStoreButtonState)anAppStoreButtonState animated:(BOOL)animated {
appStoreButtonState_ = anAppStoreButtonState;
appStoreButtonState_ = anAppStoreButtonState;
switch (anAppStoreButtonState) {
case AppStoreButtonStateOffline:
[appStoreButton_ setButtonData:[PSStoreButtonData dataWithLabel:BWHockeyLocalize(@"HockeyButtonOffline") colors:[PSStoreButton appStoreGrayColor] enabled:NO] animated:animated];
break;
case AppStoreButtonStateCheck:
[appStoreButton_ setButtonData:[PSStoreButtonData dataWithLabel:BWHockeyLocalize(@"HockeyButtonCheck") colors:[PSStoreButton appStoreGreenColor] enabled:YES] animated:animated];
break;
case AppStoreButtonStateSearching:
[appStoreButton_ setButtonData:[PSStoreButtonData dataWithLabel:BWHockeyLocalize(@"HockeyButtonSearching") colors:[PSStoreButton appStoreGrayColor] enabled:NO] animated:animated];
break;
case AppStoreButtonStateUpdate:
[appStoreButton_ setButtonData:[PSStoreButtonData dataWithLabel:BWHockeyLocalize(@"HockeyButtonUpdate") colors:[PSStoreButton appStoreBlueColor] enabled:YES] animated:animated];
break;
case AppStoreButtonStateInstalling:
[appStoreButton_ setButtonData:[PSStoreButtonData dataWithLabel:BWHockeyLocalize(@"HockeyButtonInstalling") colors:[PSStoreButton appStoreGrayColor] enabled:NO] animated:animated];
break;
default:
break;
}
switch (anAppStoreButtonState) {
case AppStoreButtonStateOffline:
[appStoreButton_ setButtonData:[PSStoreButtonData dataWithLabel:BWHockeyLocalize(@"HockeyButtonOffline") colors:[PSStoreButton appStoreGrayColor] enabled:NO] animated:animated];
break;
case AppStoreButtonStateCheck:
[appStoreButton_ setButtonData:[PSStoreButtonData dataWithLabel:BWHockeyLocalize(@"HockeyButtonCheck") colors:[PSStoreButton appStoreGreenColor] enabled:YES] animated:animated];
break;
case AppStoreButtonStateSearching:
[appStoreButton_ setButtonData:[PSStoreButtonData dataWithLabel:BWHockeyLocalize(@"HockeyButtonSearching") colors:[PSStoreButton appStoreGrayColor] enabled:NO] animated:animated];
break;
case AppStoreButtonStateUpdate:
[appStoreButton_ setButtonData:[PSStoreButtonData dataWithLabel:BWHockeyLocalize(@"HockeyButtonUpdate") colors:[PSStoreButton appStoreBlueColor] enabled:YES] animated:animated];
break;
case AppStoreButtonStateInstalling:
[appStoreButton_ setButtonData:[PSStoreButtonData dataWithLabel:BWHockeyLocalize(@"HockeyButtonInstalling") colors:[PSStoreButton appStoreGrayColor] enabled:NO] animated:animated];
break;
default:
break;
}
}
- (void)storeButtonFired:(PSStoreButton *)button {
switch (appStoreButtonState_) {
case AppStoreButtonStateCheck:
[self.hockeyManager checkForUpdateShowFeedback:YES];
break;
case AppStoreButtonStateUpdate:
if ([self.hockeyManager initiateAppDownload]) {
[self setAppStoreButtonState:AppStoreButtonStateInstalling animated:YES];
};
break;
default:
break;
}
switch (appStoreButtonState_) {
case AppStoreButtonStateCheck:
[self.hockeyManager checkForUpdateShowFeedback:YES];
break;
case AppStoreButtonStateUpdate:
if ([self.hockeyManager initiateAppDownload]) {
[self setAppStoreButtonState:AppStoreButtonStateInstalling animated:YES];
};
break;
default:
break;
}
}
@end

View File

@ -57,61 +57,61 @@ typedef enum QuincyKitAlertType {
} CrashAlertType;
typedef enum CrashReportStatus {
// The status of the crash is queued, need to check later (HockeyApp)
// The status of the crash is queued, need to check later (HockeyApp)
CrashReportStatusQueued = -80,
// This app version is set to discontinued, no new crash reports accepted by the server
// This app version is set to discontinued, no new crash reports accepted by the server
CrashReportStatusFailureVersionDiscontinued = -30,
// XML: Sender version string contains not allowed characters, only alphanumberical including space and . are allowed
// XML: Sender version string contains not allowed characters, only alphanumberical including space and . are allowed
CrashReportStatusFailureXMLSenderVersionNotAllowed = -21,
// XML: Version string contains not allowed characters, only alphanumberical including space and . are allowed
// XML: Version string contains not allowed characters, only alphanumberical including space and . are allowed
CrashReportStatusFailureXMLVersionNotAllowed = -20,
// SQL for adding a symoblicate todo entry in the database failed
// SQL for adding a symoblicate todo entry in the database failed
CrashReportStatusFailureSQLAddSymbolicateTodo = -18,
// SQL for adding crash log in the database failed
// SQL for adding crash log in the database failed
CrashReportStatusFailureSQLAddCrashlog = -17,
// SQL for adding a new version in the database failed
// SQL for adding a new version in the database failed
CrashReportStatusFailureSQLAddVersion = -16,
// SQL for checking if the version is already added in the database failed
CrashReportStatusFailureSQLCheckVersionExists = -15,
// SQL for checking if the version is already added in the database failed
CrashReportStatusFailureSQLCheckVersionExists = -15,
// SQL for creating a new pattern for this bug and set amount of occurrances to 1 in the database failed
CrashReportStatusFailureSQLAddPattern = -14,
// SQL for creating a new pattern for this bug and set amount of occurrances to 1 in the database failed
CrashReportStatusFailureSQLAddPattern = -14,
// SQL for checking the status of the bugfix version in the database failed
CrashReportStatusFailureSQLCheckBugfixStatus = -13,
// SQL for checking the status of the bugfix version in the database failed
CrashReportStatusFailureSQLCheckBugfixStatus = -13,
// SQL for updating the occurances of this pattern in the database failed
CrashReportStatusFailureSQLUpdatePatternOccurances = -12,
// SQL for updating the occurances of this pattern in the database failed
CrashReportStatusFailureSQLUpdatePatternOccurances = -12,
// SQL for getting all the known bug patterns for the current app version in the database failed
CrashReportStatusFailureSQLFindKnownPatterns = -11,
// SQL for getting all the known bug patterns for the current app version in the database failed
CrashReportStatusFailureSQLFindKnownPatterns = -11,
// SQL for finding the bundle identifier in the database failed
CrashReportStatusFailureSQLSearchAppName = -10,
// SQL for finding the bundle identifier in the database failed
CrashReportStatusFailureSQLSearchAppName = -10,
// the post request didn't contain valid data
CrashReportStatusFailureInvalidPostData = -3,
// the post request didn't contain valid data
CrashReportStatusFailureInvalidPostData = -3,
// incoming data may not be added, because e.g. bundle identifier wasn't found
CrashReportStatusFailureInvalidIncomingData = -2,
// incoming data may not be added, because e.g. bundle identifier wasn't found
CrashReportStatusFailureInvalidIncomingData = -2,
// database cannot be accessed, check hostname, username, password and database name settings in config.php
CrashReportStatusFailureDatabaseNotAvailable = -1,
// database cannot be accessed, check hostname, username, password and database name settings in config.php
CrashReportStatusFailureDatabaseNotAvailable = -1,
CrashReportStatusUnknown = 0,
CrashReportStatusUnknown = 0,
CrashReportStatusAssigned = 1,
CrashReportStatusAssigned = 1,
CrashReportStatusSubmitted = 2,
CrashReportStatusSubmitted = 2,
CrashReportStatusAvailable = 3,
CrashReportStatusAvailable = 3,
} CrashReportStatus;
// This protocol is used to send the image updates
@ -140,21 +140,21 @@ typedef enum CrashReportStatus {
@end
@interface BWQuincyManager : NSObject <NSXMLParserDelegate> {
NSString *_submissionURL;
NSString *_submissionURL;
id <BWQuincyManagerDelegate> _delegate;
id <BWQuincyManagerDelegate> _delegate;
BOOL _showAlwaysButton;
BOOL _feedbackActivated;
BOOL _autoSubmitCrashReport;
BOOL _autoSubmitDeviceUDID;
BOOL _showAlwaysButton;
BOOL _feedbackActivated;
BOOL _autoSubmitCrashReport;
BOOL _autoSubmitDeviceUDID;
BOOL _didCrashInLastSession;
BOOL _didCrashInLastSession;
NSString *_appIdentifier;
NSString *_appIdentifier;
NSString *_feedbackRequestID;
float _feedbackDelayInterval;
NSString *_feedbackRequestID;
float _feedbackDelayInterval;
NSMutableString *_contentOfProperty;
CrashReportStatus _serverResult;
@ -163,19 +163,19 @@ typedef enum CrashReportStatus {
NSString *_crashesDir;
BOOL _crashIdenticalCurrentVersion;
BOOL _crashReportActivated;
BOOL _crashReportActivated;
NSMutableArray *_crashFiles;
NSMutableData *_responseData;
NSInteger _statusCode;
NSURLConnection *_urlConnection;
NSURLConnection *_urlConnection;
NSData *_crashData;
NSData *_crashData;
NSString *_languageStyle;
BOOL _sendingInProgress;
NSString *_languageStyle;
BOOL _sendingInProgress;
}
+ (BWQuincyManager *)sharedQuincyManager;

View File

@ -36,22 +36,22 @@
#include <inttypes.h> //needed for PRIx64 macro
NSBundle *quincyBundle(void) {
static NSBundle* bundle = nil;
if (!bundle) {
NSString* path = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:kQuincyBundleName];
bundle = [[NSBundle bundleWithPath:path] retain];
}
return bundle;
static NSBundle* bundle = nil;
if (!bundle) {
NSString* path = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:kQuincyBundleName];
bundle = [[NSBundle bundleWithPath:path] retain];
}
return bundle;
}
NSString *BWQuincyLocalize(NSString *stringToken) {
if ([BWQuincyManager sharedQuincyManager].languageStyle == nil)
return NSLocalizedStringFromTableInBundle(stringToken, @"Quincy", quincyBundle(), @"");
else {
NSString *alternate = [NSString stringWithFormat:@"Quincy%@", [BWQuincyManager sharedQuincyManager].languageStyle];
return NSLocalizedStringFromTableInBundle(stringToken, alternate, quincyBundle(), @"");
}
if ([BWQuincyManager sharedQuincyManager].languageStyle == nil)
return NSLocalizedStringFromTableInBundle(stringToken, @"Quincy", quincyBundle(), @"");
else {
NSString *alternate = [NSString stringWithFormat:@"Quincy%@", [BWQuincyManager sharedQuincyManager].languageStyle];
return NSLocalizedStringFromTableInBundle(stringToken, alternate, quincyBundle(), @"");
}
}
@ -93,15 +93,15 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000
+(BWQuincyManager *)sharedQuincyManager
{
static BWQuincyManager *sharedInstance = nil;
static dispatch_once_t pred;
static BWQuincyManager *sharedInstance = nil;
static dispatch_once_t pred;
dispatch_once(&pred, ^{
sharedInstance = [BWQuincyManager alloc];
sharedInstance = [sharedInstance init];
});
dispatch_once(&pred, ^{
sharedInstance = [BWQuincyManager alloc];
sharedInstance = [sharedInstance init];
});
return sharedInstance;
return sharedInstance;
}
#else
+ (BWQuincyManager *)sharedQuincyManager {
@ -116,23 +116,23 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
#endif
- (id) init {
if ((self = [super init])) {
if ((self = [super init])) {
_serverResult = CrashReportStatusUnknown;
_crashIdenticalCurrentVersion = YES;
_crashData = nil;
_urlConnection = nil;
_urlConnection = nil;
_submissionURL = nil;
_responseData = nil;
_appIdentifier = nil;
_sendingInProgress = NO;
_languageStyle = nil;
_didCrashInLastSession = NO;
_responseData = nil;
_appIdentifier = nil;
_sendingInProgress = NO;
_languageStyle = nil;
_didCrashInLastSession = NO;
self.delegate = nil;
self.feedbackActivated = NO;
self.showAlwaysButton = NO;
self.autoSubmitCrashReport = NO;
self.autoSubmitDeviceUDID = NO;
self.feedbackActivated = NO;
self.showAlwaysButton = NO;
self.autoSubmitCrashReport = NO;
self.autoSubmitDeviceUDID = NO;
NSString *testValue = [[NSUserDefaults standardUserDefaults] stringForKey:kQuincyKitAnalyzerStarted];
if (testValue) {
@ -169,42 +169,42 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
// Check if we previously crashed
if ([crashReporter hasPendingCrashReport]) {
_didCrashInLastSession = YES;
_didCrashInLastSession = YES;
[self handleCrashReport];
}
}
// Enable the Crash Reporter
if (![crashReporter enableCrashReporterAndReturnError: &error])
NSLog(@"Warning: Could not enable crash reporter: %@", error);
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(startManager) name:BWQuincyNetworkBecomeReachable object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(startManager) name:BWQuincyNetworkBecomeReachable object:nil];
}
if (!quincyBundle()) {
if (!quincyBundle()) {
NSLog(@"WARNING: Quincy.bundle is missing in the app bundle!");
}
}
}
return self;
}
- (void) dealloc {
self.delegate = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:BWQuincyNetworkBecomeReachable object:nil];
self.delegate = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self name:BWQuincyNetworkBecomeReachable object:nil];
[_languageStyle release];
[_languageStyle release];
[_submissionURL release];
_submissionURL = nil;
[_submissionURL release];
_submissionURL = nil;
[_appIdentifier release];
_appIdentifier = nil;
[_appIdentifier release];
_appIdentifier = nil;
[_urlConnection cancel];
[_urlConnection release];
_urlConnection = nil;
[_urlConnection cancel];
[_urlConnection release];
_urlConnection = nil;
[_crashData release];
[_crashData release];
[_crashesDir release];
[_crashFiles release];
@ -216,21 +216,21 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
#pragma mark -
#pragma mark setter
- (void)setSubmissionURL:(NSString *)anSubmissionURL {
if (_submissionURL != anSubmissionURL) {
[_submissionURL release];
_submissionURL = [anSubmissionURL copy];
}
if (_submissionURL != anSubmissionURL) {
[_submissionURL release];
_submissionURL = [anSubmissionURL copy];
}
[self performSelector:@selector(startManager) withObject:nil afterDelay:1.0f];
[self performSelector:@selector(startManager) withObject:nil afterDelay:1.0f];
}
- (void)setAppIdentifier:(NSString *)anAppIdentifier {
if (_appIdentifier != anAppIdentifier) {
[_appIdentifier release];
_appIdentifier = [anAppIdentifier copy];
}
if (_appIdentifier != anAppIdentifier) {
[_appIdentifier release];
_appIdentifier = [anAppIdentifier copy];
}
[self setSubmissionURL:@"https://rink.hockeyapp.net/"];
[self setSubmissionURL:@"https://rink.hockeyapp.net/"];
}
@ -238,65 +238,65 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
#pragma mark private methods
- (BOOL)autoSendCrashReports {
BOOL result = NO;
BOOL result = NO;
if (!self.autoSubmitCrashReport) {
if (self.isShowingAlwaysButton && [[NSUserDefaults standardUserDefaults] boolForKey: kAutomaticallySendCrashReports]) {
result = YES;
}
} else {
result = YES;
if (!self.autoSubmitCrashReport) {
if (self.isShowingAlwaysButton && [[NSUserDefaults standardUserDefaults] boolForKey: kAutomaticallySendCrashReports]) {
result = YES;
}
} else {
result = YES;
}
return result;
return result;
}
// begin the startup process
- (void)startManager {
if (!_sendingInProgress && [self hasPendingCrashReport]) {
_sendingInProgress = YES;
if (!quincyBundle()) {
if (!_sendingInProgress && [self hasPendingCrashReport]) {
_sendingInProgress = YES;
if (!quincyBundle()) {
NSLog(@"Quincy.bundle is missing, sending report automatically!");
[self _sendCrashReports];
} else if (!self.autoSubmitCrashReport && [self hasNonApprovedCrashReports]) {
[self _sendCrashReports];
} else if (!self.autoSubmitCrashReport && [self hasNonApprovedCrashReports]) {
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(willShowSubmitCrashReportAlert)]) {
[self.delegate willShowSubmitCrashReportAlert];
}
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(willShowSubmitCrashReportAlert)]) {
[self.delegate willShowSubmitCrashReportAlert];
}
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:BWQuincyLocalize(@"CrashDataFoundTitle"), appName]
message:[NSString stringWithFormat:BWQuincyLocalize(@"CrashDataFoundDescription"), appName]
delegate:self
cancelButtonTitle:BWQuincyLocalize(@"CrashDontSendReport")
otherButtonTitles:BWQuincyLocalize(@"CrashSendReport"), nil];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[NSString stringWithFormat:BWQuincyLocalize(@"CrashDataFoundTitle"), appName]
message:[NSString stringWithFormat:BWQuincyLocalize(@"CrashDataFoundDescription"), appName]
delegate:self
cancelButtonTitle:BWQuincyLocalize(@"CrashDontSendReport")
otherButtonTitles:BWQuincyLocalize(@"CrashSendReport"), nil];
if ([self isShowingAlwaysButton]) {
[alertView addButtonWithTitle:BWQuincyLocalize(@"CrashSendReportAlways")];
}
if ([self isShowingAlwaysButton]) {
[alertView addButtonWithTitle:BWQuincyLocalize(@"CrashSendReportAlways")];
}
[alertView setTag: QuincyKitAlertTypeSend];
[alertView show];
[alertView release];
} else {
[self _sendCrashReports];
}
[alertView setTag: QuincyKitAlertTypeSend];
[alertView show];
[alertView release];
} else {
[self _sendCrashReports];
}
}
}
- (BOOL)hasNonApprovedCrashReports {
NSDictionary *approvedCrashReports = [[NSUserDefaults standardUserDefaults] dictionaryForKey: kApprovedCrashReports];
NSDictionary *approvedCrashReports = [[NSUserDefaults standardUserDefaults] dictionaryForKey: kApprovedCrashReports];
if (!approvedCrashReports || [approvedCrashReports count] == 0) return YES;
if (!approvedCrashReports || [approvedCrashReports count] == 0) return YES;
for (NSUInteger i=0; i < [_crashFiles count]; i++) {
NSString *filename = [_crashFiles objectAtIndex:i];
if (![approvedCrashReports objectForKey:filename]) return YES;
}
if (![approvedCrashReports objectForKey:filename]) return YES;
}
return NO;
return NO;
}
- (BOOL)hasPendingCrashReport {
@ -305,7 +305,7 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
if ([_crashFiles count] == 0 && [fm fileExistsAtPath:_crashesDir]) {
NSString *file = nil;
NSError *error = NULL;
NSError *error = NULL;
NSDirectoryEnumerator *dirEnum = [fm enumeratorAtPath: _crashesDir];
@ -330,31 +330,31 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
UIAlertView *alertView = nil;
if (_serverResult >= CrashReportStatusAssigned &&
_crashIdenticalCurrentVersion &&
quincyBundle()) {
_crashIdenticalCurrentVersion &&
quincyBundle()) {
// show some feedback to the user about the crash status
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"];
switch (_serverResult) {
case CrashReportStatusAssigned:
alertView = [[UIAlertView alloc] initWithTitle: [NSString stringWithFormat:BWQuincyLocalize(@"CrashResponseTitle"), appName ]
message: [NSString stringWithFormat:BWQuincyLocalize(@"CrashResponseNextRelease"), appName]
delegate: self
cancelButtonTitle: BWQuincyLocalize(@"CrashResponseTitleOK")
otherButtonTitles: nil];
message: [NSString stringWithFormat:BWQuincyLocalize(@"CrashResponseNextRelease"), appName]
delegate: self
cancelButtonTitle: BWQuincyLocalize(@"CrashResponseTitleOK")
otherButtonTitles: nil];
break;
case CrashReportStatusSubmitted:
alertView = [[UIAlertView alloc] initWithTitle: [NSString stringWithFormat:BWQuincyLocalize(@"CrashResponseTitle"), appName ]
message: [NSString stringWithFormat:BWQuincyLocalize(@"CrashResponseWaitingApple"), appName]
delegate: self
cancelButtonTitle: BWQuincyLocalize(@"CrashResponseTitleOK")
otherButtonTitles: nil];
message: [NSString stringWithFormat:BWQuincyLocalize(@"CrashResponseWaitingApple"), appName]
delegate: self
cancelButtonTitle: BWQuincyLocalize(@"CrashResponseTitleOK")
otherButtonTitles: nil];
break;
case CrashReportStatusAvailable:
alertView = [[UIAlertView alloc] initWithTitle: [NSString stringWithFormat:BWQuincyLocalize(@"CrashResponseTitle"), appName ]
message: [NSString stringWithFormat:BWQuincyLocalize(@"CrashResponseAvailable"), appName]
delegate: self
cancelButtonTitle: BWQuincyLocalize(@"CrashResponseTitleOK")
otherButtonTitles: nil];
message: [NSString stringWithFormat:BWQuincyLocalize(@"CrashResponseAvailable"), appName]
delegate: self
cancelButtonTitle: BWQuincyLocalize(@"CrashResponseTitleOK")
otherButtonTitles: nil];
break;
default:
alertView = nil;
@ -377,7 +377,7 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
if ([alertView tag] == QuincyKitAlertTypeSend) {
switch (buttonIndex) {
case 0:
_sendingInProgress = NO;
_sendingInProgress = NO;
[self _cleanCrashReports];
break;
case 1:
@ -404,7 +404,7 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
if ([elementName isEqualToString:@"result"]) {
_contentOfProperty = [NSMutableString string];
}
}
}
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
@ -412,12 +412,12 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
elementName = qName;
}
// open source implementation
// open source implementation
if ([elementName isEqualToString: @"result"]) {
if ([_contentOfProperty intValue] > _serverResult) {
_serverResult = (CrashReportStatus)[_contentOfProperty intValue];
} else {
CrashReportStatus errorcode = (CrashReportStatus)[_contentOfProperty intValue];
CrashReportStatus errorcode = (CrashReportStatus)[_contentOfProperty intValue];
NSLog(@"CrashReporter ended in error code: %i", errorcode);
}
}
@ -458,18 +458,18 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
}
- (void)_performSendingCrashReports {
NSMutableDictionary *approvedCrashReports = [NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults] dictionaryForKey: kApprovedCrashReports]];
NSMutableDictionary *approvedCrashReports = [NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults] dictionaryForKey: kApprovedCrashReports]];
NSFileManager *fm = [NSFileManager defaultManager];
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error = NULL;
NSString *userid = @"";
NSString *contact = @"";
NSString *description = @"";
if (self.autoSubmitDeviceUDID && [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"]) {
userid = [self deviceIdentifier];
} else if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashReportUserID)]) {
if (self.autoSubmitDeviceUDID && [[NSBundle mainBundle] pathForResource:@"embedded" ofType:@"mobileprovision"]) {
userid = [self deviceIdentifier];
} else if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashReportUserID)]) {
userid = [self.delegate crashReportUserID] ?: @"";
}
@ -481,8 +481,8 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
description = [self.delegate crashReportDescription] ?: @"";
}
NSMutableString *crashes = nil;
_crashIdenticalCurrentVersion = NO;
NSMutableString *crashes = nil;
_crashIdenticalCurrentVersion = NO;
for (NSUInteger i=0; i < [_crashFiles count]; i++) {
NSString *filename = [_crashesDir stringByAppendingPathComponent:[_crashFiles objectAtIndex:i]];
@ -491,10 +491,10 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
if ([crashData length] > 0) {
PLCrashReport *report = [[[PLCrashReport alloc] initWithData:crashData error:&error] autorelease];
if (report == nil) {
NSLog(@"Could not parse crash report");
continue;
}
if (report == nil) {
NSLog(@"Could not parse crash report");
continue;
}
NSString *crashLogString = [PLCrashReportTextFormatter stringValueForCrashReport:report withTextFormat:PLCrashReportTextFormatiOS];
@ -502,73 +502,73 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
_crashIdenticalCurrentVersion = YES;
}
if (crashes == nil) {
crashes = [NSMutableString string];
}
if (crashes == nil) {
crashes = [NSMutableString string];
}
[crashes appendFormat:@"<crash><applicationname>%s</applicationname><bundleidentifier>%@</bundleidentifier><systemversion>%@</systemversion><platform>%@</platform><senderversion>%@</senderversion><version>%@</version><log><![CDATA[%@]]></log><userid>%@</userid><contact>%@</contact><description><![CDATA[%@]]></description></crash>",
[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"] UTF8String],
report.applicationInfo.applicationIdentifier,
report.systemInfo.operatingSystemVersion,
[self _getDevicePlatform],
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],
report.applicationInfo.applicationVersion,
[crashLogString stringByReplacingOccurrencesOfString:@"]]>" withString:@"]]" @"]]><![CDATA[" @">" options:NSLiteralSearch range:NSMakeRange(0,crashLogString.length)],
userid,
contact,
[[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleExecutable"] UTF8String],
report.applicationInfo.applicationIdentifier,
report.systemInfo.operatingSystemVersion,
[self _getDevicePlatform],
[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],
report.applicationInfo.applicationVersion,
[crashLogString stringByReplacingOccurrencesOfString:@"]]>" withString:@"]]" @"]]><![CDATA[" @">" options:NSLiteralSearch range:NSMakeRange(0,crashLogString.length)],
userid,
contact,
[description stringByReplacingOccurrencesOfString:@"]]>" withString:@"]]" @"]]><![CDATA[" @">" options:NSLiteralSearch range:NSMakeRange(0,description.length)]];
// store this crash report as user approved, so if it fails it will retry automatically
[approvedCrashReports setObject:[NSNumber numberWithBool:YES] forKey:[_crashFiles objectAtIndex:i]];
// store this crash report as user approved, so if it fails it will retry automatically
[approvedCrashReports setObject:[NSNumber numberWithBool:YES] forKey:[_crashFiles objectAtIndex:i]];
} else {
// we cannot do anything with this report, so delete it
[fm removeItemAtPath:filename error:&error];
}
// we cannot do anything with this report, so delete it
[fm removeItemAtPath:filename error:&error];
}
}
[[NSUserDefaults standardUserDefaults] setObject:approvedCrashReports forKey:kApprovedCrashReports];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSUserDefaults standardUserDefaults] setObject:approvedCrashReports forKey:kApprovedCrashReports];
[[NSUserDefaults standardUserDefaults] synchronize];
if (crashes != nil) {
[self _postXML:[NSString stringWithFormat:@"<crashes>%@</crashes>", crashes]
toURL:[NSURL URLWithString:self.submissionURL]];
if (crashes != nil) {
[self _postXML:[NSString stringWithFormat:@"<crashes>%@</crashes>", crashes]
toURL:[NSURL URLWithString:self.submissionURL]];
}
}
}
- (void)_cleanCrashReports {
NSError *error = NULL;
NSError *error = NULL;
NSFileManager *fm = [NSFileManager defaultManager];
NSFileManager *fm = [NSFileManager defaultManager];
for (NSUInteger i=0; i < [_crashFiles count]; i++) {
[fm removeItemAtPath:[_crashesDir stringByAppendingPathComponent:[_crashFiles objectAtIndex:i]] error:&error];
}
[_crashFiles removeAllObjects];
for (NSUInteger i=0; i < [_crashFiles count]; i++) {
[fm removeItemAtPath:[_crashesDir stringByAppendingPathComponent:[_crashFiles objectAtIndex:i]] error:&error];
}
[_crashFiles removeAllObjects];
[[NSUserDefaults standardUserDefaults] setObject:nil forKey:kApprovedCrashReports];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSUserDefaults standardUserDefaults] setObject:nil forKey:kApprovedCrashReports];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)_sendCrashReports {
// send it to the next runloop
[self performSelector:@selector(_performSendingCrashReports) withObject:nil afterDelay:0.0f];
// send it to the next runloop
[self performSelector:@selector(_performSendingCrashReports) withObject:nil afterDelay:0.0f];
}
- (void)_checkForFeedbackStatus {
NSMutableURLRequest *request = nil;
NSMutableURLRequest *request = nil;
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@api/2/apps/%@/crashes/%@",
self.submissionURL,
[self.appIdentifier stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
_feedbackRequestID
]
]];
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@api/2/apps/%@/crashes/%@",
self.submissionURL,
[self.appIdentifier stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
_feedbackRequestID
]
]];
[request setCachePolicy: NSURLRequestReloadIgnoringLocalCacheData];
[request setValue:@"Quincy/iOS" forHTTPHeaderField:@"User-Agent"];
[request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
[request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
[request setTimeoutInterval: 15];
[request setHTTPMethod:@"GET"];
@ -587,21 +587,21 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
- (void)_postXML:(NSString*)xml toURL:(NSURL*)url {
NSMutableURLRequest *request = nil;
NSString *boundary = @"----FOO";
NSString *boundary = @"----FOO";
if (self.appIdentifier) {
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@api/2/apps/%@/crashes",
self.submissionURL,
[self.appIdentifier stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]
]
]];
} else {
request = [NSMutableURLRequest requestWithURL:url];
}
if (self.appIdentifier) {
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@api/2/apps/%@/crashes",
self.submissionURL,
[self.appIdentifier stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]
]
]];
} else {
request = [NSMutableURLRequest requestWithURL:url];
}
[request setCachePolicy: NSURLRequestReloadIgnoringLocalCacheData];
[request setValue:@"Quincy/iOS" forHTTPHeaderField:@"User-Agent"];
[request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
[request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
[request setTimeoutInterval: 15];
[request setHTTPMethod:@"POST"];
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
@ -609,16 +609,16 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
NSMutableData *postBody = [NSMutableData data];
[postBody appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
if (self.appIdentifier) {
[postBody appendData:[@"Content-Disposition: form-data; name=\"xml\"; filename=\"crash.xml\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"Content-Type: text/xml\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
} else {
[postBody appendData:[@"Content-Disposition: form-data; name=\"xmlstring\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
if (self.appIdentifier) {
[postBody appendData:[@"Content-Disposition: form-data; name=\"xml\"; filename=\"crash.xml\"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"Content-Type: text/xml\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]];
} else {
[postBody appendData:[@"Content-Disposition: form-data; name=\"xmlstring\"\r\n\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
[postBody appendData:[xml dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[xml dataUsingEncoding:NSUTF8StringEncoding]];
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
[request setHTTPBody:postBody];
[request setHTTPBody:postBody];
_serverResult = CrashReportStatusUnknown;
_statusCode = 200;
@ -632,9 +632,9 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
_urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
if (!_urlConnection) {
_sendingInProgress = NO;
}
if (!_urlConnection) {
_sendingInProgress = NO;
}
}
#pragma mark NSURLConnection Delegate
@ -652,70 +652,70 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
[_responseData release];
_responseData = nil;
_urlConnection = nil;
_urlConnection = nil;
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(connectionClosed)]) {
[self.delegate connectionClosed];
}
_sendingInProgress = NO;
_sendingInProgress = NO;
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
if (_statusCode >= 200 && _statusCode < 400) {
[self _cleanCrashReports];
[self _cleanCrashReports];
_feedbackRequestID = nil;
if (self.appIdentifier) {
// HockeyApp uses PList XML format
NSMutableDictionary *response = [NSPropertyListSerialization propertyListFromData:_responseData
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:nil
errorDescription:NULL];
_serverResult = (CrashReportStatus)[[response objectForKey:@"status"] intValue];
if ([response objectForKey:@"id"]) {
_feedbackRequestID = [[NSString alloc] initWithString:[response objectForKey:@"id"]];
_feedbackDelayInterval = [[response objectForKey:@"delay"] floatValue];
if (_feedbackDelayInterval > 0)
_feedbackDelayInterval *= 0.01;
}
} else {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:_responseData];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[parser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
_feedbackRequestID = nil;
if (self.appIdentifier) {
// HockeyApp uses PList XML format
NSMutableDictionary *response = [NSPropertyListSerialization propertyListFromData:_responseData
mutabilityOption:NSPropertyListMutableContainersAndLeaves
format:nil
errorDescription:NULL];
_serverResult = (CrashReportStatus)[[response objectForKey:@"status"] intValue];
if ([response objectForKey:@"id"]) {
_feedbackRequestID = [[NSString alloc] initWithString:[response objectForKey:@"id"]];
_feedbackDelayInterval = [[response objectForKey:@"delay"] floatValue];
if (_feedbackDelayInterval > 0)
_feedbackDelayInterval *= 0.01;
}
} else {
NSXMLParser *parser = [[NSXMLParser alloc] initWithData:_responseData];
// Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
[parser setDelegate:self];
// Depending on the XML document you're parsing, you may want to enable these features of NSXMLParser.
[parser setShouldProcessNamespaces:NO];
[parser setShouldReportNamespacePrefixes:NO];
[parser setShouldResolveExternalEntities:NO];
[parser parse];
[parser parse];
[parser release];
}
if ([self isFeedbackActivated]) {
// only proceed if the server did not report any problem
if ((self.appIdentifier) && (_serverResult == CrashReportStatusQueued)) {
// the report is still in the queue
if (_feedbackRequestID) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_checkForFeedbackStatus) object:nil];
[self performSelector:@selector(_checkForFeedbackStatus) withObject:nil afterDelay:_feedbackDelayInterval];
}
} else {
[self showCrashStatusMessage];
}
[parser release];
}
if ([self isFeedbackActivated]) {
// only proceed if the server did not report any problem
if ((self.appIdentifier) && (_serverResult == CrashReportStatusQueued)) {
// the report is still in the queue
if (_feedbackRequestID) {
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_checkForFeedbackStatus) object:nil];
[self performSelector:@selector(_checkForFeedbackStatus) withObject:nil afterDelay:_feedbackDelayInterval];
}
} else {
[self showCrashStatusMessage];
}
}
}
[_responseData release];
_responseData = nil;
_urlConnection = nil;
_urlConnection = nil;
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(connectionClosed)]) {
[self.delegate connectionClosed];
}
_sendingInProgress = NO;
_sendingInProgress = NO;
}
#pragma mark PLCrashReporter
@ -727,22 +727,22 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
PLCrashReporter *crashReporter = [PLCrashReporter sharedReporter];
NSError *error = NULL;
// check if the next call ran successfully the last time
// check if the next call ran successfully the last time
if (_analyzerStarted == 0) {
// mark the start of the routine
_analyzerStarted = 1;
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:_analyzerStarted] forKey:kQuincyKitAnalyzerStarted];
[[NSUserDefaults standardUserDefaults] synchronize];
// Try loading the crash report
_crashData = [[NSData alloc] initWithData:[crashReporter loadPendingCrashReportDataAndReturnError: &error]];
// Try loading the crash report
_crashData = [[NSData alloc] initWithData:[crashReporter loadPendingCrashReportDataAndReturnError: &error]];
NSString *cacheFilename = [NSString stringWithFormat: @"%.0f", [NSDate timeIntervalSinceReferenceDate]];
NSString *cacheFilename = [NSString stringWithFormat: @"%.0f", [NSDate timeIntervalSinceReferenceDate]];
if (_crashData == nil) {
NSLog(@"Could not load crash report: %@", error);
} else {
[_crashData writeToFile:[_crashesDir stringByAppendingPathComponent: cacheFilename] atomically:YES];
if (_crashData == nil) {
NSLog(@"Could not load crash report: %@", error);
} else {
[_crashData writeToFile:[_crashesDir stringByAppendingPathComponent: cacheFilename] atomically:YES];
}
}
@ -750,7 +750,7 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
// mark the end of the routine
_analyzerStarted = 0;
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:_analyzerStarted] forKey:kQuincyKitAnalyzerStarted];
[[NSUserDefaults standardUserDefaults] synchronize];
[[NSUserDefaults standardUserDefaults] synchronize];
[crashReporter purgePendingCrashReport];
return;

View File

@ -29,22 +29,22 @@
@implementation NSString (HockeyAdditions)
- (NSString *)bw_URLEncodedString {
NSString *result = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)self,
NULL,
CFSTR("!*'();:@&=+$,/?%#[]"),
kCFStringEncodingUTF8);
[result autorelease];
return result;
NSString *result = (NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
(CFStringRef)self,
NULL,
CFSTR("!*'();:@&=+$,/?%#[]"),
kCFStringEncodingUTF8);
[result autorelease];
return result;
}
- (NSString*)bw_URLDecodedString {
NSString *result = (NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault,
(CFStringRef)self,
CFSTR(""),
kCFStringEncodingUTF8);
[result autorelease];
return result;
NSString *result = (NSString *)CFURLCreateStringByReplacingPercentEscapesUsingEncoding(kCFAllocatorDefault,
(CFStringRef)self,
CFSTR(""),
kCFStringEncodingUTF8);
[result autorelease];
return result;
}
- (NSComparisonResult)versionCompare:(NSString *)other

View File

@ -26,12 +26,12 @@
#import <UIKit/UIKit.h>
@interface PSAppStoreHeader : UIView {
NSString *headerLabel_;
NSString *middleHeaderLabel_;
NSString *subHeaderLabel;
UIImage *iconImage_;
NSString *headerLabel_;
NSString *middleHeaderLabel_;
NSString *subHeaderLabel;
UIImage *iconImage_;
UIImage *reflectedImage_;
UIImage *reflectedImage_;
}
@property (nonatomic, copy) NSString *headerLabel;

View File

@ -50,20 +50,20 @@
#pragma mark NSObject
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
self.backgroundColor = kLightGrayColor;
}
return self;
if ((self = [super initWithFrame:frame])) {
self.autoresizingMask = UIViewAutoresizingFlexibleWidth;
self.backgroundColor = kLightGrayColor;
}
return self;
}
- (void)dealloc {
[headerLabel_ release];
[middleHeaderLabel_ release];
[subHeaderLabel release];
[iconImage_ release];
[headerLabel_ release];
[middleHeaderLabel_ release];
[subHeaderLabel release];
[iconImage_ release];
[super dealloc];
[super dealloc];
}
@ -72,56 +72,56 @@
#pragma mark UIView
- (void)drawRect:(CGRect)rect {
CGRect bounds = self.bounds;
CGFloat globalWidth = self.frame.size.width;
CGContextRef context = UIGraphicsGetCurrentContext();
CGRect bounds = self.bounds;
CGFloat globalWidth = self.frame.size.width;
CGContextRef context = UIGraphicsGetCurrentContext();
// draw the gradient
NSArray *colors = [NSArray arrayWithObjects:(id)kDarkGrayColor.CGColor, (id)kLightGrayColor.CGColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(CGColorGetColorSpace((CGColorRef)[colors objectAtIndex:0]), (CFArrayRef)colors, (CGFloat[2]){0, 1});
CGPoint top = CGPointMake(CGRectGetMidX(bounds), bounds.origin.y);
CGPoint bottom = CGPointMake(CGRectGetMidX(bounds), CGRectGetMaxY(bounds)-kReflectionHeight);
CGContextDrawLinearGradient(context, gradient, top, bottom, 0);
CGGradientRelease(gradient);
// draw the gradient
NSArray *colors = [NSArray arrayWithObjects:(id)kDarkGrayColor.CGColor, (id)kLightGrayColor.CGColor, nil];
CGGradientRef gradient = CGGradientCreateWithColors(CGColorGetColorSpace((CGColorRef)[colors objectAtIndex:0]), (CFArrayRef)colors, (CGFloat[2]){0, 1});
CGPoint top = CGPointMake(CGRectGetMidX(bounds), bounds.origin.y);
CGPoint bottom = CGPointMake(CGRectGetMidX(bounds), CGRectGetMaxY(bounds)-kReflectionHeight);
CGContextDrawLinearGradient(context, gradient, top, bottom, 0);
CGGradientRelease(gradient);
// draw header name
UIColor *mainTextColor = BW_RGBCOLOR(0,0,0);
UIColor *secondaryTextColor = BW_RGBCOLOR(48,48,48);
UIFont *mainFont = [UIFont boldSystemFontOfSize:20];
UIFont *secondaryFont = [UIFont boldSystemFontOfSize:12];
UIFont *smallFont = [UIFont systemFontOfSize:12];
// draw header name
UIColor *mainTextColor = BW_RGBCOLOR(0,0,0);
UIColor *secondaryTextColor = BW_RGBCOLOR(48,48,48);
UIFont *mainFont = [UIFont boldSystemFontOfSize:20];
UIFont *secondaryFont = [UIFont boldSystemFontOfSize:12];
UIFont *smallFont = [UIFont systemFontOfSize:12];
float myColorValues[] = {255, 255, 255, .6};
CGColorSpaceRef myColorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef myColor = CGColorCreate(myColorSpace, myColorValues);
float myColorValues[] = {255, 255, 255, .6};
CGColorSpaceRef myColorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef myColor = CGColorCreate(myColorSpace, myColorValues);
// icon
[iconImage_ drawAtPoint:CGPointMake(kImageMargin, kImageMargin)];
[reflectedImage_ drawAtPoint:CGPointMake(kImageMargin, kImageMargin+kImageHeight)];
// icon
[iconImage_ drawAtPoint:CGPointMake(kImageMargin, kImageMargin)];
[reflectedImage_ drawAtPoint:CGPointMake(kImageMargin, kImageMargin+kImageHeight)];
// shadows are a beast
NSInteger shadowOffset = 2;
BW_IF_IOS4_OR_GREATER(if([[UIScreen mainScreen] scale] == 2) shadowOffset = 1;)
BW_IF_IOS5_OR_GREATER(shadowOffset = 1;) // iOS5 changes this - again!
// shadows are a beast
NSInteger shadowOffset = 2;
BW_IF_IOS4_OR_GREATER(if([[UIScreen mainScreen] scale] == 2) shadowOffset = 1;)
BW_IF_IOS5_OR_GREATER(shadowOffset = 1;) // iOS5 changes this - again!
BW_IF_3_2_OR_GREATER(CGContextSetShadowWithColor(context, CGSizeMake(shadowOffset, shadowOffset), 0, myColor);)
BW_IF_PRE_3_2(shadowOffset=1;CGContextSetShadowWithColor(context, CGSizeMake(shadowOffset, -shadowOffset), 0, myColor);)
BW_IF_3_2_OR_GREATER(CGContextSetShadowWithColor(context, CGSizeMake(shadowOffset, shadowOffset), 0, myColor);)
BW_IF_PRE_3_2(shadowOffset=1;CGContextSetShadowWithColor(context, CGSizeMake(shadowOffset, -shadowOffset), 0, myColor);)
[mainTextColor set];
[headerLabel_ drawInRect:CGRectMake(kTextRow, kImageMargin, globalWidth-kTextRow, 20) withFont:mainFont lineBreakMode:UILineBreakModeTailTruncation];
[mainTextColor set];
[headerLabel_ drawInRect:CGRectMake(kTextRow, kImageMargin, globalWidth-kTextRow, 20) withFont:mainFont lineBreakMode:UILineBreakModeTailTruncation];
// middle
[secondaryTextColor set];
[middleHeaderLabel_ drawInRect:CGRectMake(kTextRow, kImageMargin + 25, globalWidth-kTextRow, 20) withFont:secondaryFont lineBreakMode:UILineBreakModeTailTruncation];
CGContextSetShadowWithColor(context, CGSizeZero, 0, nil);
// middle
[secondaryTextColor set];
[middleHeaderLabel_ drawInRect:CGRectMake(kTextRow, kImageMargin + 25, globalWidth-kTextRow, 20) withFont:secondaryFont lineBreakMode:UILineBreakModeTailTruncation];
CGContextSetShadowWithColor(context, CGSizeZero, 0, nil);
// sub
[secondaryTextColor set];
[subHeaderLabel drawAtPoint:CGPointMake(kTextRow, kImageMargin+kImageHeight-12) forWidth:globalWidth-kTextRow withFont:smallFont lineBreakMode:UILineBreakModeTailTruncation];
// sub
[secondaryTextColor set];
[subHeaderLabel drawAtPoint:CGPointMake(kTextRow, kImageMargin+kImageHeight-12) forWidth:globalWidth-kTextRow withFont:smallFont lineBreakMode:UILineBreakModeTailTruncation];
CGColorRelease(myColor);
CGColorSpaceRelease(myColorSpace);
CGColorRelease(myColor);
CGColorSpaceRelease(myColorSpace);
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -129,45 +129,45 @@
#pragma mark Properties
- (void)setHeaderLabel:(NSString *)anHeaderLabel {
if (headerLabel_ != anHeaderLabel) {
[headerLabel_ release];
headerLabel_ = [anHeaderLabel copy];
[self setNeedsDisplay];
}
if (headerLabel_ != anHeaderLabel) {
[headerLabel_ release];
headerLabel_ = [anHeaderLabel copy];
[self setNeedsDisplay];
}
}
- (void)setMiddleHeaderLabel:(NSString *)aMiddleHeaderLabel {
if (middleHeaderLabel_ != aMiddleHeaderLabel) {
[middleHeaderLabel_ release];
middleHeaderLabel_ = [aMiddleHeaderLabel copy];
[self setNeedsDisplay];
}
if (middleHeaderLabel_ != aMiddleHeaderLabel) {
[middleHeaderLabel_ release];
middleHeaderLabel_ = [aMiddleHeaderLabel copy];
[self setNeedsDisplay];
}
}
- (void)setSubHeaderLabel:(NSString *)aSubHeaderLabel {
if (subHeaderLabel != aSubHeaderLabel) {
[subHeaderLabel release];
subHeaderLabel = [aSubHeaderLabel copy];
[self setNeedsDisplay];
}
if (subHeaderLabel != aSubHeaderLabel) {
[subHeaderLabel release];
subHeaderLabel = [aSubHeaderLabel copy];
[self setNeedsDisplay];
}
}
- (void)setIconImage:(UIImage *)anIconImage {
if (iconImage_ != anIconImage) {
[iconImage_ release];
if (iconImage_ != anIconImage) {
[iconImage_ release];
// scale, make borders and reflection
iconImage_ = [anIconImage bw_imageToFitSize:CGSizeMake(kImageHeight, kImageHeight) honorScaleFactor:YES];
iconImage_ = [[iconImage_ bw_roundedCornerImage:kImageBorderRadius borderSize:0.0] retain];
// scale, make borders and reflection
iconImage_ = [anIconImage bw_imageToFitSize:CGSizeMake(kImageHeight, kImageHeight) honorScaleFactor:YES];
iconImage_ = [[iconImage_ bw_roundedCornerImage:kImageBorderRadius borderSize:0.0] retain];
// create reflected image
[reflectedImage_ release];
reflectedImage_ = nil;
if (anIconImage) {
reflectedImage_ = [[iconImage_ bw_reflectedImageWithHeight:kReflectionHeight fromAlpha:0.5 toAlpha:0.0] retain];
}
[self setNeedsDisplay];
// create reflected image
[reflectedImage_ release];
reflectedImage_ = nil;
if (anIconImage) {
reflectedImage_ = [[iconImage_ bw_reflectedImageWithHeight:kReflectionHeight fromAlpha:0.5 toAlpha:0.0] retain];
}
[self setNeedsDisplay];
}
}
@end

View File

@ -28,10 +28,10 @@
// defines a button action set (data container)
@interface PSStoreButtonData : NSObject {
CGPoint customPadding_;
NSString *label_;
NSArray *colors_;
BOOL enabled_;
CGPoint customPadding_;
NSString *label_;
NSArray *colors_;
BOOL enabled_;
}
+ (id)dataWithLabel:(NSString*)aLabel colors:(NSArray*)aColors enabled:(BOOL)flag;
@ -52,11 +52,11 @@
// Simulate the Paymeny-Button from the AppStore
// The interface is flexible, so there is now fixed order
@interface PSStoreButton : UIButton {
PSStoreButtonData *buttonData_;
id<PSStoreButtonDelegate> buttonDelegate_;
PSStoreButtonData *buttonData_;
id<PSStoreButtonDelegate> buttonDelegate_;
CAGradientLayer *gradient_;
CGPoint customPadding_;
CAGradientLayer *gradient_;
CGPoint customPadding_;
}
- (id)initWithFrame:(CGRect)frame;

View File

@ -50,23 +50,23 @@
#pragma mark NSObject
- (id)initWithLabel:(NSString*)aLabel colors:(NSArray*)aColors enabled:(BOOL)flag {
if ((self = [super init])) {
self.label = aLabel;
self.colors = aColors;
self.enabled = flag;
}
return self;
if ((self = [super init])) {
self.label = aLabel;
self.colors = aColors;
self.enabled = flag;
}
return self;
}
+ (id)dataWithLabel:(NSString*)aLabel colors:(NSArray*)aColors enabled:(BOOL)flag {
return [[[[self class] alloc] initWithLabel:aLabel colors:aColors enabled:flag] autorelease];
return [[[[self class] alloc] initWithLabel:aLabel colors:aColors enabled:flag] autorelease];
}
- (void)dealloc {
[label_ release];
[colors_ release];
[label_ release];
[colors_ release];
[super dealloc];
[super dealloc];
}
@end
@ -88,86 +88,86 @@
#pragma mark private
- (void)touchedUpOutside:(id)sender {
PSLog(@"touched outside...");
PSLog(@"touched outside...");
}
- (void)buttonPressed:(id)sender {
PSLog(@"calling delegate:storeButtonFired for %@", sender);
[buttonDelegate_ storeButtonFired:self];
PSLog(@"calling delegate:storeButtonFired for %@", sender);
[buttonDelegate_ storeButtonFired:self];
}
- (void)animationDidStop:(NSString *)animationID finished:(NSNumber *)finished context:(void *)context {
// show text again, but only if animation did finish (or else another animation is on the way)
if ([finished boolValue]) {
[self setTitle:self.buttonData.label forState:UIControlStateNormal];
}
// show text again, but only if animation did finish (or else another animation is on the way)
if ([finished boolValue]) {
[self setTitle:self.buttonData.label forState:UIControlStateNormal];
}
}
- (void)updateButtonAnimated:(BOOL)animated {
if (animated) {
// hide text, then start animation
[self setTitle:@"" forState:UIControlStateNormal];
[UIView beginAnimations:@"storeButtonUpdate" context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:kDefaultButtonAnimationTime];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
}else {
[self setTitle:self.buttonData.label forState:UIControlStateNormal];
}
self.enabled = self.buttonData.isEnabled;
gradient_.colors = self.buttonData.colors;
// show white or gray text, depending on the state
if (self.buttonData.isEnabled) {
[self setTitleShadowColor:[UIColor colorWithWhite:0.200 alpha:1.000] forState:UIControlStateNormal];
[self.titleLabel setShadowOffset:CGSizeMake(0.0, -0.6)];
[self setTitleColor:[UIColor colorWithWhite:1.0 alpha:1.000] forState:UIControlStateNormal];
}else {
[self.titleLabel setShadowOffset:CGSizeMake(0.0, 0.0)];
[self setTitleColor:PS_RGBCOLOR(148,150,151) forState:UIControlStateNormal];
}
// calculate optimal new size
CGSize sizeThatFits = [self sizeThatFits:CGSizeZero];
// move sublayer (can't be animated explcitely)
for (CALayer *aLayer in self.layer.sublayers) {
[CATransaction begin];
if (animated) {
// hide text, then start animation
[self setTitle:@"" forState:UIControlStateNormal];
[UIView beginAnimations:@"storeButtonUpdate" context:nil];
[UIView setAnimationBeginsFromCurrentState:YES];
[UIView setAnimationDuration:kDefaultButtonAnimationTime];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:@selector(animationDidStop:finished:context:)];
[CATransaction setAnimationDuration:kDefaultButtonAnimationTime];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
}else {
[self setTitle:self.buttonData.label forState:UIControlStateNormal];
// frame is calculated and explicitely animated. so we absolutely need kCATransactionDisableActions
[CATransaction setValue:[NSNumber numberWithBool:YES] forKey:kCATransactionDisableActions];
}
self.enabled = self.buttonData.isEnabled;
gradient_.colors = self.buttonData.colors;
CGRect newFrame = aLayer.frame;
newFrame.size.width = sizeThatFits.width;
aLayer.frame = newFrame;
// show white or gray text, depending on the state
if (self.buttonData.isEnabled) {
[self setTitleShadowColor:[UIColor colorWithWhite:0.200 alpha:1.000] forState:UIControlStateNormal];
[self.titleLabel setShadowOffset:CGSizeMake(0.0, -0.6)];
[self setTitleColor:[UIColor colorWithWhite:1.0 alpha:1.000] forState:UIControlStateNormal];
}else {
[self.titleLabel setShadowOffset:CGSizeMake(0.0, 0.0)];
[self setTitleColor:PS_RGBCOLOR(148,150,151) forState:UIControlStateNormal];
}
// calculate optimal new size
CGSize sizeThatFits = [self sizeThatFits:CGSizeZero];
// move sublayer (can't be animated explcitely)
for (CALayer *aLayer in self.layer.sublayers) {
[CATransaction begin];
if (animated) {
[CATransaction setAnimationDuration:kDefaultButtonAnimationTime];
[CATransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
}else {
// frame is calculated and explicitely animated. so we absolutely need kCATransactionDisableActions
[CATransaction setValue:[NSNumber numberWithBool:YES] forKey:kCATransactionDisableActions];
}
CGRect newFrame = aLayer.frame;
newFrame.size.width = sizeThatFits.width;
aLayer.frame = newFrame;
[CATransaction commit];
[CATransaction commit];
}
// set outer frame changes
self.titleEdgeInsets = UIEdgeInsetsMake(2.0, self.titleEdgeInsets.left, 0.0, 0.0);
[self alignToSuperview];
// set outer frame changes
self.titleEdgeInsets = UIEdgeInsetsMake(2.0, self.titleEdgeInsets.left, 0.0, 0.0);
[self alignToSuperview];
if (animated) {
[UIView commitAnimations];
}
if (animated) {
[UIView commitAnimations];
}
}
- (void)alignToSuperview {
[self sizeToFit];
if (self.superview) {
CGRect cr = self.frame;
cr.origin.y = customPadding_.y;
cr.origin.x = self.superview.frame.size.width - cr.size.width - customPadding_.x * 2;
self.frame = cr;
}
[self sizeToFit];
if (self.superview) {
CGRect cr = self.frame;
cr.origin.y = customPadding_.y;
cr.origin.x = self.superview.frame.size.width - cr.size.width - customPadding_.x * 2;
self.frame = cr;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -175,23 +175,23 @@
#pragma mark NSObject
- (id)initWithFrame:(CGRect)frame {
if ((self = [super initWithFrame:frame])) {
if ((self = [super initWithFrame:frame])) {
self.layer.needsDisplayOnBoundsChange = YES;
// setup title label
[self.titleLabel setFont:[UIFont boldSystemFontOfSize:13.0]];
// setup title label
[self.titleLabel setFont:[UIFont boldSystemFontOfSize:13.0]];
// register for touch events
[self addTarget:self action:@selector(touchedUpOutside:) forControlEvents:UIControlEventTouchUpOutside];
// register for touch events
[self addTarget:self action:@selector(touchedUpOutside:) forControlEvents:UIControlEventTouchUpOutside];
[self addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
// border layers for more sex!
CAGradientLayer *bevelLayer = [CAGradientLayer layer];
// border layers for more sex!
CAGradientLayer *bevelLayer = [CAGradientLayer layer];
bevelLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor colorWithWhite:0.4 alpha:1.0] CGColor], [[UIColor whiteColor] CGColor], nil];
bevelLayer.frame = CGRectMake(0.0, 0.0, CGRectGetWidth(frame), CGRectGetHeight(frame));
bevelLayer.cornerRadius = 2.5;
bevelLayer.needsDisplayOnBoundsChange = YES;
[self.layer addSublayer:bevelLayer];
[self.layer addSublayer:bevelLayer];
CAGradientLayer *topBorderLayer = [CAGradientLayer layer];
topBorderLayer.colors = [NSArray arrayWithObjects:(id)[[UIColor darkGrayColor] CGColor], [[UIColor lightGrayColor] CGColor], nil];
@ -200,30 +200,30 @@
topBorderLayer.needsDisplayOnBoundsChange = YES;
[self.layer addSublayer:topBorderLayer];
// main gradient layer
gradient_ = [[CAGradientLayer layer] retain];
gradient_.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:1.0], nil];//[NSNumber numberWithFloat:0.500], [NSNumber numberWithFloat:0.5001],
// main gradient layer
gradient_ = [[CAGradientLayer layer] retain];
gradient_.locations = [NSArray arrayWithObjects:[NSNumber numberWithFloat:0.0], [NSNumber numberWithFloat:1.0], nil];//[NSNumber numberWithFloat:0.500], [NSNumber numberWithFloat:0.5001],
gradient_.frame = CGRectMake(0.75, 0.75, CGRectGetWidth(frame) - 1.5, CGRectGetHeight(frame) - 1.5);
gradient_.cornerRadius = 2.5;
gradient_.needsDisplayOnBoundsChange = YES;
[self.layer addSublayer:gradient_];
[self bringSubviewToFront:self.titleLabel];
}
return self;
[self.layer addSublayer:gradient_];
[self bringSubviewToFront:self.titleLabel];
}
return self;
}
- (id)initWithPadding:(CGPoint)padding {
if ((self = [self initWithFrame:CGRectMake(0, 0, 40, PS_MIN_HEIGHT)])) {
customPadding_ = padding;
}
return self;
if ((self = [self initWithFrame:CGRectMake(0, 0, 40, PS_MIN_HEIGHT)])) {
customPadding_ = padding;
}
return self;
}
- (void)dealloc {
[buttonData_ release];
[gradient_ release];
[buttonData_ release];
[gradient_ release];
[super dealloc];
[super dealloc];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -231,25 +231,25 @@
#pragma mark UIView
- (CGSize)sizeThatFits:(CGSize)size {
CGSize constr = (CGSize){.height = self.frame.size.height, .width = PS_MAX_WIDTH};
CGSize constr = (CGSize){.height = self.frame.size.height, .width = PS_MAX_WIDTH};
CGSize newSize = [self.buttonData.label sizeWithFont:self.titleLabel.font constrainedToSize:constr lineBreakMode:UILineBreakModeMiddleTruncation];
CGFloat newWidth = newSize.width + (PS_PADDING * 2);
CGFloat newHeight = PS_MIN_HEIGHT > newSize.height ? PS_MIN_HEIGHT : newSize.height;
CGFloat newHeight = PS_MIN_HEIGHT > newSize.height ? PS_MIN_HEIGHT : newSize.height;
CGSize sizeThatFits = CGSizeMake(newWidth, newHeight);
return sizeThatFits;
CGSize sizeThatFits = CGSizeMake(newWidth, newHeight);
return sizeThatFits;
}
- (void)setFrame:(CGRect)aRect {
[super setFrame:aRect];
[super setFrame:aRect];
// copy frame changes to sublayers (but watch out for NaN's)
for (CALayer *aLayer in self.layer.sublayers) {
CGRect rect = aLayer.frame;
rect.size.width = self.frame.size.width;
rect.size.height = self.frame.size.height;
aLayer.frame = rect;
[aLayer layoutIfNeeded];
// copy frame changes to sublayers (but watch out for NaN's)
for (CALayer *aLayer in self.layer.sublayers) {
CGRect rect = aLayer.frame;
rect.size.width = self.frame.size.width;
rect.size.height = self.frame.size.height;
aLayer.frame = rect;
[aLayer layoutIfNeeded];
}
}
@ -258,16 +258,16 @@
#pragma mark Properties
- (void)setButtonData:(PSStoreButtonData *)aButtonData {
[self setButtonData:aButtonData animated:NO];
[self setButtonData:aButtonData animated:NO];
}
- (void)setButtonData:(PSStoreButtonData *)aButtonData animated:(BOOL)animated {
if (buttonData_ != aButtonData) {
[buttonData_ release];
buttonData_ = [aButtonData retain];
}
if (buttonData_ != aButtonData) {
[buttonData_ release];
buttonData_ = [aButtonData retain];
}
[self updateButtonAnimated:animated];
[self updateButtonAnimated:animated];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -275,21 +275,21 @@
#pragma mark Static
+ (NSArray *)appStoreGreenColor {
return [NSArray arrayWithObjects:(id)
[UIColor colorWithRed:0.482 green:0.674 blue:0.406 alpha:1.000].CGColor,
[UIColor colorWithRed:0.299 green:0.606 blue:0.163 alpha:1.000].CGColor, nil];
return [NSArray arrayWithObjects:(id)
[UIColor colorWithRed:0.482 green:0.674 blue:0.406 alpha:1.000].CGColor,
[UIColor colorWithRed:0.299 green:0.606 blue:0.163 alpha:1.000].CGColor, nil];
}
+ (NSArray *)appStoreBlueColor {
return [NSArray arrayWithObjects:(id)
[UIColor colorWithRed:0.306 green:0.380 blue:0.547 alpha:1.000].CGColor,
[UIColor colorWithRed:0.129 green:0.220 blue:0.452 alpha:1.000].CGColor, nil];
return [NSArray arrayWithObjects:(id)
[UIColor colorWithRed:0.306 green:0.380 blue:0.547 alpha:1.000].CGColor,
[UIColor colorWithRed:0.129 green:0.220 blue:0.452 alpha:1.000].CGColor, nil];
}
+ (NSArray *)appStoreGrayColor {
return [NSArray arrayWithObjects:(id)
PS_RGBCOLOR(187,189,191).CGColor,
PS_RGBCOLOR(210,210,210).CGColor, nil];
return [NSArray arrayWithObjects:(id)
PS_RGBCOLOR(187,189,191).CGColor,
PS_RGBCOLOR(210,210,210).CGColor, nil];
}
@end

View File

@ -27,11 +27,11 @@
#import <UIKit/UIKit.h>
@interface PSWebTableViewCell : UITableViewCell <UIWebViewDelegate> {
UIWebView *webView_;
NSString *webViewContent_;
CGSize webViewSize_;
UIWebView *webView_;
NSString *webViewContent_;
CGSize webViewSize_;
UIColor *cellBackgroundColor_;
UIColor *cellBackgroundColor_;
}
@property (nonatomic, retain) UIWebView *webView;

View File

@ -53,7 +53,7 @@ body { font: 13px 'Helvetica Neue', Helvetica; word-wrap:break-word; padding:8px
- (void)addWebView {
if(webViewContent_) {
CGRect webViewRect = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
CGRect webViewRect = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
if(!webView_) {
webView_ = [[[UIWebView alloc] initWithFrame:webViewRect] retain];
[self addSubview:webView_];
@ -61,37 +61,37 @@ body { font: 13px 'Helvetica Neue', Helvetica; word-wrap:break-word; padding:8px
webView_.backgroundColor = self.cellBackgroundColor;
webView_.opaque = NO;
webView_.delegate = self;
webView_.autoresizingMask = UIViewAutoresizingFlexibleWidth;
webView_.autoresizingMask = UIViewAutoresizingFlexibleWidth;
for(UIView* subView in webView_.subviews){
if([subView isKindOfClass:[UIScrollView class]]){
// disable scrolling
UIScrollView *sv = (UIScrollView *)subView;
sv.scrollEnabled = NO;
sv.bounces = NO;
for(UIView* subView in webView_.subviews){
if([subView isKindOfClass:[UIScrollView class]]){
// disable scrolling
UIScrollView *sv = (UIScrollView *)subView;
sv.scrollEnabled = NO;
sv.bounces = NO;
// hide shadow
for (UIView* shadowView in [subView subviews]) {
if ([shadowView isKindOfClass:[UIImageView class]]) {
shadowView.hidden = YES;
}
}
}
// hide shadow
for (UIView* shadowView in [subView subviews]) {
if ([shadowView isKindOfClass:[UIImageView class]]) {
shadowView.hidden = YES;
}
}
}
}
}
else
webView_.frame = webViewRect;
NSString *deviceWidth = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? [NSString stringWithFormat:@"%d", CGRectGetWidth(self.bounds)] : @"device-width";
//BWHockeyLog(@"%@\n%@\%@", PSWebTableViewCellHtmlTemplate, deviceWidth, self.webViewContent);
NSString *contentHtml = [NSString stringWithFormat:PSWebTableViewCellHtmlTemplate, deviceWidth, self.webViewContent];
NSString *deviceWidth = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad ? [NSString stringWithFormat:@"%d", CGRectGetWidth(self.bounds)] : @"device-width";
//BWHockeyLog(@"%@\n%@\%@", PSWebTableViewCellHtmlTemplate, deviceWidth, self.webViewContent);
NSString *contentHtml = [NSString stringWithFormat:PSWebTableViewCellHtmlTemplate, deviceWidth, self.webViewContent];
[webView_ loadHTMLString:contentHtml baseURL:nil];
}
}
- (void)showWebView {
webView_.hidden = NO;
self.textLabel.text = @"";
self.textLabel.text = @"";
[self setNeedsDisplay];
}
@ -109,13 +109,13 @@ body { font: 13px 'Helvetica Neue', Helvetica; word-wrap:break-word; padding:8px
- (void)setWebViewContent:(NSString *)aWebViewContent {
if (webViewContent_ != aWebViewContent) {
[webViewContent_ release];
webViewContent_ = [aWebViewContent retain];
if (webViewContent_ != aWebViewContent) {
[webViewContent_ release];
webViewContent_ = [aWebViewContent retain];
// add basic accessiblity (prevents "snarfed from ivar layout") logs
self.accessibilityLabel = aWebViewContent;
}
// add basic accessiblity (prevents "snarfed from ivar layout") logs
self.accessibilityLabel = aWebViewContent;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -123,16 +123,16 @@ body { font: 13px 'Helvetica Neue', Helvetica; word-wrap:break-word; padding:8px
#pragma mark NSObject
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
self.cellBackgroundColor = [UIColor clearColor];
}
return self;
if((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
self.cellBackgroundColor = [UIColor clearColor];
}
return self;
}
- (void)dealloc {
[self removeWebView];
[webViewContent_ release];
[super dealloc];
[self removeWebView];
[webViewContent_ release];
[super dealloc];
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -140,12 +140,12 @@ body { font: 13px 'Helvetica Neue', Helvetica; word-wrap:break-word; padding:8px
#pragma mark UIView
- (void)setFrame:(CGRect)aFrame {
BOOL needChange = !CGRectEqualToRect(aFrame, self.frame);
[super setFrame:aFrame];
BOOL needChange = !CGRectEqualToRect(aFrame, self.frame);
[super setFrame:aFrame];
if (needChange) {
[self addWebView];
}
if (needChange) {
[self addWebView];
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@ -154,7 +154,7 @@ body { font: 13px 'Helvetica Neue', Helvetica; word-wrap:break-word; padding:8px
- (void)prepareForReuse {
[self removeWebView];
self.webViewContent = nil;
self.webViewContent = nil;
[super prepareForReuse];
}
@ -166,24 +166,24 @@ body { font: 13px 'Helvetica Neue', Helvetica; word-wrap:break-word; padding:8px
if(navigationType == UIWebViewNavigationTypeOther)
return YES;
return NO;
return NO;
}
- (void)webViewDidFinishLoad:(UIWebView *)webView {
if(webViewContent_)
[self showWebView];
[self showWebView];
CGRect frame = webView_.frame;
frame.size.height = 1;
webView_.frame = frame;
CGSize fittingSize = [webView_ sizeThatFits:CGSizeZero];
frame.size = fittingSize;
webView_.frame = frame;
CGRect frame = webView_.frame;
frame.size.height = 1;
webView_.frame = frame;
CGSize fittingSize = [webView_ sizeThatFits:CGSizeZero];
frame.size = fittingSize;
webView_.frame = frame;
// sizeThatFits is not reliable - use javascript for optimal height
NSString *output = [webView_ stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight;"];
self.webViewSize = CGSizeMake(fittingSize.width, [output integerValue]);
// sizeThatFits is not reliable - use javascript for optimal height
NSString *output = [webView_ stringByEvaluatingJavaScriptFromString:@"document.body.scrollHeight;"];
self.webViewSize = CGSizeMake(fittingSize.width, [output integerValue]);
}
@end

View File

@ -38,107 +38,107 @@ CGImageRef CreateGradientImage(int pixelsWide, int pixelsHigh, float fromAlpha,
// Returns true if the image has an alpha layer
- (BOOL)hasAlpha {
CGImageAlphaInfo alpha = CGImageGetAlphaInfo(self.CGImage);
return (alpha == kCGImageAlphaFirst ||
alpha == kCGImageAlphaLast ||
alpha == kCGImageAlphaPremultipliedFirst ||
alpha == kCGImageAlphaPremultipliedLast);
CGImageAlphaInfo alpha = CGImageGetAlphaInfo(self.CGImage);
return (alpha == kCGImageAlphaFirst ||
alpha == kCGImageAlphaLast ||
alpha == kCGImageAlphaPremultipliedFirst ||
alpha == kCGImageAlphaPremultipliedLast);
}
// Returns a copy of the given image, adding an alpha channel if it doesn't already have one
- (UIImage *)imageWithAlpha {
if ([self hasAlpha]) {
return self;
}
if ([self hasAlpha]) {
return self;
}
CGImageRef imageRef = self.CGImage;
size_t width = CGImageGetWidth(imageRef) * self.scale;
size_t height = CGImageGetHeight(imageRef) * self.scale;
CGImageRef imageRef = self.CGImage;
size_t width = CGImageGetWidth(imageRef) * self.scale;
size_t height = CGImageGetHeight(imageRef) * self.scale;
// The bitsPerComponent and bitmapInfo values are hard-coded to prevent an "unsupported parameter combination" error
CGContextRef offscreenContext = CGBitmapContextCreate(NULL,
width,
height,
8,
0,
CGImageGetColorSpace(imageRef),
kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
// The bitsPerComponent and bitmapInfo values are hard-coded to prevent an "unsupported parameter combination" error
CGContextRef offscreenContext = CGBitmapContextCreate(NULL,
width,
height,
8,
0,
CGImageGetColorSpace(imageRef),
kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
// Draw the image into the context and retrieve the new image, which will now have an alpha layer
CGContextDrawImage(offscreenContext, CGRectMake(0, 0, width, height), imageRef);
CGImageRef imageRefWithAlpha = CGBitmapContextCreateImage(offscreenContext);
UIImage *imageWithAlpha = [UIImage imageWithCGImage:imageRefWithAlpha];
// Draw the image into the context and retrieve the new image, which will now have an alpha layer
CGContextDrawImage(offscreenContext, CGRectMake(0, 0, width, height), imageRef);
CGImageRef imageRefWithAlpha = CGBitmapContextCreateImage(offscreenContext);
UIImage *imageWithAlpha = [UIImage imageWithCGImage:imageRefWithAlpha];
// Clean up
CGContextRelease(offscreenContext);
CGImageRelease(imageRefWithAlpha);
// Clean up
CGContextRelease(offscreenContext);
CGImageRelease(imageRefWithAlpha);
return imageWithAlpha;
return imageWithAlpha;
}
// Creates a copy of this image with rounded corners
// If borderSize is non-zero, a transparent border of the given size will also be added
// Original author: Björn Sållarp. Used with permission. See: http://blog.sallarp.com/iphone-uiimage-round-corners/
- (UIImage *)bw_roundedCornerImage:(NSInteger)cornerSize borderSize:(NSInteger)borderSize {
// If the image does not have an alpha layer, add one
// If the image does not have an alpha layer, add one
UIImage *roundedImage = nil;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
BW_IF_IOS4_OR_GREATER(
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0); // 0.0 for scale means "correct scale for device's main screen".
CGImageRef sourceImg = CGImageCreateWithImageInRect([self CGImage], CGRectMake(0, 0, self.size.width * self.scale, self.size.height * self.scale)); // cropping happens here.
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0); // 0.0 for scale means "correct scale for device's main screen".
CGImageRef sourceImg = CGImageCreateWithImageInRect([self CGImage], CGRectMake(0, 0, self.size.width * self.scale, self.size.height * self.scale)); // cropping happens here.
// Create a clipping path with rounded corners
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextBeginPath(context);
[self addRoundedRectToPath:CGRectMake(borderSize, borderSize, self.size.width - borderSize * 2, self.size.height - borderSize * 2)
context:context
ovalWidth:cornerSize
ovalHeight:cornerSize];
CGContextClosePath(context);
CGContextClip(context);
// Create a clipping path with rounded corners
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextBeginPath(context);
[self addRoundedRectToPath:CGRectMake(borderSize, borderSize, self.size.width - borderSize * 2, self.size.height - borderSize * 2)
context:context
ovalWidth:cornerSize
ovalHeight:cornerSize];
CGContextClosePath(context);
CGContextClip(context);
roundedImage = [UIImage imageWithCGImage:sourceImg scale:0.0 orientation:self.imageOrientation]; // create cropped UIImage.
[roundedImage drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)]; // the actual scaling happens here, and orientation is taken care of automatically.
CGImageRelease(sourceImg);
roundedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
)
roundedImage = [UIImage imageWithCGImage:sourceImg scale:0.0 orientation:self.imageOrientation]; // create cropped UIImage.
[roundedImage drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)]; // the actual scaling happens here, and orientation is taken care of automatically.
CGImageRelease(sourceImg);
roundedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
)
#endif
if (!roundedImage) {
// Try older method.
UIImage *image = [self imageWithAlpha];
if (!roundedImage) {
// Try older method.
UIImage *image = [self imageWithAlpha];
// Build a context that's the same dimensions as the new size
CGContextRef context = CGBitmapContextCreate(NULL,
image.size.width,
image.size.height,
CGImageGetBitsPerComponent(image.CGImage),
0,
CGImageGetColorSpace(image.CGImage),
CGImageGetBitmapInfo(image.CGImage));
// Build a context that's the same dimensions as the new size
CGContextRef context = CGBitmapContextCreate(NULL,
image.size.width,
image.size.height,
CGImageGetBitsPerComponent(image.CGImage),
0,
CGImageGetColorSpace(image.CGImage),
CGImageGetBitmapInfo(image.CGImage));
// Create a clipping path with rounded corners
CGContextBeginPath(context);
[self addRoundedRectToPath:CGRectMake(borderSize, borderSize, image.size.width - borderSize * 2, image.size.height - borderSize * 2)
context:context
ovalWidth:cornerSize
ovalHeight:cornerSize];
CGContextClosePath(context);
CGContextClip(context);
// Create a clipping path with rounded corners
CGContextBeginPath(context);
[self addRoundedRectToPath:CGRectMake(borderSize, borderSize, image.size.width - borderSize * 2, image.size.height - borderSize * 2)
context:context
ovalWidth:cornerSize
ovalHeight:cornerSize];
CGContextClosePath(context);
CGContextClip(context);
// Draw the image to the context; the clipping path will make anything outside the rounded rect transparent
CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);
// Draw the image to the context; the clipping path will make anything outside the rounded rect transparent
CGContextDrawImage(context, CGRectMake(0, 0, image.size.width, image.size.height), image.CGImage);
// Create a CGImage from the context
CGImageRef clippedImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);
// Create a CGImage from the context
CGImageRef clippedImage = CGBitmapContextCreateImage(context);
CGContextRelease(context);
// Create a UIImage from the CGImage
roundedImage = [UIImage imageWithCGImage:clippedImage];
CGImageRelease(clippedImage);
}
return roundedImage;
// Create a UIImage from the CGImage
roundedImage = [UIImage imageWithCGImage:clippedImage];
CGImageRelease(clippedImage);
}
return roundedImage;
}
#pragma mark -
@ -147,83 +147,83 @@ CGImageRef CreateGradientImage(int pixelsWide, int pixelsHigh, float fromAlpha,
// Adds a rectangular path to the given context and rounds its corners by the given extents
// Original author: Björn Sållarp. Used with permission. See: http://blog.sallarp.com/iphone-uiimage-round-corners/
- (void)addRoundedRectToPath:(CGRect)rect context:(CGContextRef)context ovalWidth:(CGFloat)ovalWidth ovalHeight:(CGFloat)ovalHeight {
if (ovalWidth == 0 || ovalHeight == 0) {
CGContextAddRect(context, rect);
return;
}
CGContextSaveGState(context);
CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextScaleCTM(context, ovalWidth, ovalHeight);
CGFloat fw = CGRectGetWidth(rect) / ovalWidth;
CGFloat fh = CGRectGetHeight(rect) / ovalHeight;
CGContextMoveToPoint(context, fw, fh/2);
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1);
CGContextClosePath(context);
CGContextRestoreGState(context);
if (ovalWidth == 0 || ovalHeight == 0) {
CGContextAddRect(context, rect);
return;
}
CGContextSaveGState(context);
CGContextTranslateCTM(context, CGRectGetMinX(rect), CGRectGetMinY(rect));
CGContextScaleCTM(context, ovalWidth, ovalHeight);
CGFloat fw = CGRectGetWidth(rect) / ovalWidth;
CGFloat fh = CGRectGetHeight(rect) / ovalHeight;
CGContextMoveToPoint(context, fw, fh/2);
CGContextAddArcToPoint(context, fw, fh, fw/2, fh, 1);
CGContextAddArcToPoint(context, 0, fh, 0, fh/2, 1);
CGContextAddArcToPoint(context, 0, 0, fw/2, 0, 1);
CGContextAddArcToPoint(context, fw, 0, fw, fh/2, 1);
CGContextClosePath(context);
CGContextRestoreGState(context);
}
- (UIImage *)bw_imageToFitSize:(CGSize)fitSize honorScaleFactor:(BOOL)honorScaleFactor
{
float imageScaleFactor = 1.0;
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
if (honorScaleFactor) {
if ([self respondsToSelector:@selector(scale)]) {
imageScaleFactor = [self scale];
}
if (honorScaleFactor) {
if ([self respondsToSelector:@selector(scale)]) {
imageScaleFactor = [self scale];
}
}
#endif
float sourceWidth = [self size].width * imageScaleFactor;
float sourceHeight = [self size].height * imageScaleFactor;
float targetWidth = fitSize.width;
float targetHeight = fitSize.height;
float sourceWidth = [self size].width * imageScaleFactor;
float sourceHeight = [self size].height * imageScaleFactor;
float targetWidth = fitSize.width;
float targetHeight = fitSize.height;
// Calculate aspect ratios
float sourceRatio = sourceWidth / sourceHeight;
float targetRatio = targetWidth / targetHeight;
// Calculate aspect ratios
float sourceRatio = sourceWidth / sourceHeight;
float targetRatio = targetWidth / targetHeight;
// Determine what side of the source image to use for proportional scaling
BOOL scaleWidth = (sourceRatio <= targetRatio);
// Deal with the case of just scaling proportionally to fit, without cropping
scaleWidth = !scaleWidth;
// Determine what side of the source image to use for proportional scaling
BOOL scaleWidth = (sourceRatio <= targetRatio);
// Deal with the case of just scaling proportionally to fit, without cropping
scaleWidth = !scaleWidth;
// Proportionally scale source image
float scalingFactor, scaledWidth, scaledHeight;
if (scaleWidth) {
scalingFactor = 1.0 / sourceRatio;
scaledWidth = targetWidth;
scaledHeight = round(targetWidth * scalingFactor);
} else {
scalingFactor = sourceRatio;
scaledWidth = round(targetHeight * scalingFactor);
scaledHeight = targetHeight;
}
// Proportionally scale source image
float scalingFactor, scaledWidth, scaledHeight;
if (scaleWidth) {
scalingFactor = 1.0 / sourceRatio;
scaledWidth = targetWidth;
scaledHeight = round(targetWidth * scalingFactor);
} else {
scalingFactor = sourceRatio;
scaledWidth = round(targetHeight * scalingFactor);
scaledHeight = targetHeight;
}
// Calculate compositing rectangles
CGRect sourceRect, destRect;
sourceRect = CGRectMake(0, 0, sourceWidth, sourceHeight);
destRect = CGRectMake(0, 0, scaledWidth, scaledHeight);
// Calculate compositing rectangles
CGRect sourceRect, destRect;
sourceRect = CGRectMake(0, 0, sourceWidth, sourceHeight);
destRect = CGRectMake(0, 0, scaledWidth, scaledHeight);
// Create appropriately modified image.
// Create appropriately modified image.
UIImage *image = nil;
BW_IF_IOS4_OR_GREATER
(
UIGraphicsBeginImageContextWithOptions(destRect.size, NO, honorScaleFactor ? 0.0 : 1.0); // 0.0 for scale means "correct scale for device's main screen".
CGImageRef sourceImg = CGImageCreateWithImageInRect([self CGImage], sourceRect); // cropping happens here.
image = [UIImage imageWithCGImage:sourceImg scale:0.0 orientation:self.imageOrientation]; // create cropped UIImage.
[image drawInRect:destRect]; // the actual scaling happens here, and orientation is taken care of automatically.
CGImageRelease(sourceImg);
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
)
(
UIGraphicsBeginImageContextWithOptions(destRect.size, NO, honorScaleFactor ? 0.0 : 1.0); // 0.0 for scale means "correct scale for device's main screen".
CGImageRef sourceImg = CGImageCreateWithImageInRect([self CGImage], sourceRect); // cropping happens here.
image = [UIImage imageWithCGImage:sourceImg scale:0.0 orientation:self.imageOrientation]; // create cropped UIImage.
[image drawInRect:destRect]; // the actual scaling happens here, and orientation is taken care of automatically.
CGImageRelease(sourceImg);
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
)
if (!image) {
// Try older method.
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(NULL, scaledWidth, scaledHeight, 8, (fitSize.width * 4),
colorSpace, kCGImageAlphaPremultipliedLast);
colorSpace, kCGImageAlphaPremultipliedLast);
CGImageRef sourceImg = CGImageCreateWithImageInRect([self CGImage], sourceRect);
CGContextDrawImage(context, destRect, sourceImg);
CGImageRelease(sourceImg);
@ -234,7 +234,7 @@ CGImageRef CreateGradientImage(int pixelsWide, int pixelsHigh, float fromAlpha,
CGImageRelease(finalImage);
}
return image;
return image;
}
@ -243,11 +243,11 @@ CGImageRef CreateGradientImage(int pixelsWide, int pixelsHigh, float fromAlpha,
CGImageRef theCGImage = NULL;
// gradient is always black-white and the mask must be in the gray colorspace
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
// create the bitmap context
CGContextRef gradientBitmapContext = CGBitmapContextCreate(NULL, pixelsWide, pixelsHigh,
8, 0, colorSpace, kCGImageAlphaNone);
8, 0, colorSpace, kCGImageAlphaNone);
// define the start and end grayscale values (with the alpha, even though
// our bitmap context doesn't support alpha the gradient requires it)
@ -263,7 +263,7 @@ CGImageRef CreateGradientImage(int pixelsWide, int pixelsHigh, float fromAlpha,
// draw the gradient into the gray bitmap context
CGContextDrawLinearGradient(gradientBitmapContext, grayScaleGradient, gradientStartPoint,
gradientEndPoint, kCGGradientDrawsAfterEndLocation);
gradientEndPoint, kCGGradientDrawsAfterEndLocation);
CGGradientRelease(grayScaleGradient);
// convert the context into a CGImageRef and release the context
@ -271,23 +271,23 @@ CGImageRef CreateGradientImage(int pixelsWide, int pixelsHigh, float fromAlpha,
CGContextRelease(gradientBitmapContext);
// return the imageref containing the gradient
return theCGImage;
return theCGImage;
}
CGContextRef MyOpenBitmapContext(int pixelsWide, int pixelsHigh) {
CGSize size = CGSizeMake(pixelsWide, pixelsHigh);
if (UIGraphicsBeginImageContextWithOptions != NULL) {
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
}
else {
UIGraphicsBeginImageContext(size);
}
CGSize size = CGSizeMake(pixelsWide, pixelsHigh);
if (UIGraphicsBeginImageContextWithOptions != NULL) {
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
}
else {
UIGraphicsBeginImageContext(size);
}
return UIGraphicsGetCurrentContext();
return UIGraphicsGetCurrentContext();
}
- (UIImage *)bw_reflectedImageWithHeight:(NSUInteger)height fromAlpha:(float)fromAlpha toAlpha:(float)toAlpha {
if(height == 0)
if(height == 0)
return nil;
// create a bitmap graphics context the size of the image
@ -308,30 +308,30 @@ CGContextRef MyOpenBitmapContext(int pixelsWide, int pixelsHigh) {
// convert the finished reflection image to a UIImage
UIImage *theImage = UIGraphicsGetImageFromCurrentImageContext(); // returns autoreleased
UIGraphicsEndImageContext();
UIGraphicsEndImageContext();
return theImage;
}
- (id)bw_initWithContentsOfResolutionIndependentFile:(NSString *)path {
if ([UIScreen instancesRespondToSelector:@selector(scale)] && (int)[[UIScreen mainScreen] scale] == 2.0) {
NSString *path2x = [[path stringByDeletingLastPathComponent]
stringByAppendingPathComponent:[NSString stringWithFormat:@"%@@2x.%@",
[[path lastPathComponent] stringByDeletingPathExtension],
[path pathExtension]]];
if ([UIScreen instancesRespondToSelector:@selector(scale)] && (int)[[UIScreen mainScreen] scale] == 2.0) {
NSString *path2x = [[path stringByDeletingLastPathComponent]
stringByAppendingPathComponent:[NSString stringWithFormat:@"%@@2x.%@",
[[path lastPathComponent] stringByDeletingPathExtension],
[path pathExtension]]];
if ([[NSFileManager defaultManager] fileExistsAtPath:path2x]) {
return [self initWithContentsOfFile:path2x];
}
if ([[NSFileManager defaultManager] fileExistsAtPath:path2x]) {
return [self initWithContentsOfFile:path2x];
}
}
return [self initWithContentsOfFile:path];
return [self initWithContentsOfFile:path];
}
+ (UIImage*)bw_imageWithContentsOfResolutionIndependentFile:(NSString *)path {
#ifndef __clang_analyzer__
// clang alayzer in 4.2b3 thinks here's a leak, which is not the case.
return [[[UIImage alloc] bw_initWithContentsOfResolutionIndependentFile:path] autorelease];
// clang alayzer in 4.2b3 thinks here's a leak, which is not the case.
return [[[UIImage alloc] bw_initWithContentsOfResolutionIndependentFile:path] autorelease];
#endif
}