mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Changed indentation to 2 spaces for all files.
This commit is contained in:
parent
37bcbb7793
commit
d444c177df
180
Classes/BWApp.m
180
Classes/BWApp.m
@ -41,20 +41,20 @@
|
||||
#pragma mark static
|
||||
|
||||
+ (BWApp *)appFromDict:(NSDictionary *)dict {
|
||||
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"];
|
||||
}
|
||||
|
||||
return app;
|
||||
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"];
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
[super dealloc];
|
||||
[name_ release];
|
||||
[version_ release];
|
||||
[shortVersion_ release];
|
||||
[notes_ release];
|
||||
[date_ release];
|
||||
[size_ release];
|
||||
[mandatory_ release];
|
||||
|
||||
[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];
|
||||
|
||||
return [formatter stringFromDate:self.date];
|
||||
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
|
||||
[formatter setDateStyle:NSDateFormatterMediumStyle];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return @"0 MB";
|
||||
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";
|
||||
}
|
||||
|
||||
- (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
|
||||
|
@ -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
@ -7,11 +7,11 @@
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@class BWHockeyManager;
|
||||
|
||||
|
||||
@interface BWHockeySettingsViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
|
||||
BWHockeyManager *hockeyManager_;
|
||||
BWHockeyManager *hockeyManager_;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) BWHockeyManager *hockeyManager;
|
||||
|
@ -24,41 +24,41 @@
|
||||
#pragma mark Initialization
|
||||
|
||||
- (id)init:(BWHockeyManager *)newHockeyManager {
|
||||
if ((self = [super init])) {
|
||||
self.hockeyManager = newHockeyManager;
|
||||
self.title = BWHockeyLocalize(@"HockeySettingsTitle");
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
[self.view addSubview:tableView_];
|
||||
|
||||
}
|
||||
return self;
|
||||
if ((self = [super init])) {
|
||||
self.hockeyManager = newHockeyManager;
|
||||
self.title = BWHockeyLocalize(@"HockeySettingsTitle");
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
[self.view addSubview:tableView_];
|
||||
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
@ -69,165 +69,165 @@
|
||||
#pragma mark Table view data source
|
||||
|
||||
- (int)numberOfSections {
|
||||
int numberOfSections = 1;
|
||||
|
||||
if ([self.hockeyManager isAllowUserToDisableSendData]) {
|
||||
if ([self.hockeyManager shouldSendUserData]) numberOfSections++;
|
||||
if ([self.hockeyManager shouldSendUsageTime]) numberOfSections++;
|
||||
}
|
||||
|
||||
return numberOfSections;
|
||||
int numberOfSections = 1;
|
||||
|
||||
if ([self.hockeyManager isAllowUserToDisableSendData]) {
|
||||
if ([self.hockeyManager shouldSendUserData]) numberOfSections++;
|
||||
if ([self.hockeyManager shouldSendUsageTime]) 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 (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 ([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");
|
||||
}
|
||||
|
||||
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";
|
||||
|
||||
NSString *requiredIdentifier = nil;
|
||||
UITableViewCellStyle cellStyle = UITableViewCellStyleSubtitle;
|
||||
|
||||
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;
|
||||
|
||||
static NSString *CheckmarkCellIdentifier = @"CheckmarkCell";
|
||||
static NSString *SwitchCellIdentifier = @"SwitchCell";
|
||||
|
||||
NSString *requiredIdentifier = nil;
|
||||
UITableViewCellStyle cellStyle = UITableViewCellStyleSubtitle;
|
||||
|
||||
if ((NSInteger)indexPath.section == [self numberOfSections] - 1) {
|
||||
cellStyle = UITableViewCellStyleDefault;
|
||||
requiredIdentifier = CheckmarkCellIdentifier;
|
||||
// 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];
|
||||
|
||||
// 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 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;
|
||||
}
|
||||
|
||||
[tableView reloadData];
|
||||
}
|
||||
|
||||
|
||||
@ -257,20 +257,20 @@
|
||||
#pragma mark Memory management
|
||||
|
||||
- (void)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.
|
||||
// Releases the view if it doesn't have a superview.
|
||||
[super didReceiveMemoryWarning];
|
||||
|
||||
// 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;
|
||||
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
|
||||
shouldAutorotate = (interfaceOrientation == UIInterfaceOrientationPortrait);
|
||||
} else {
|
||||
shouldAutorotate = YES;
|
||||
}
|
||||
|
||||
return shouldAutorotate;
|
||||
BOOL shouldAutorotate;
|
||||
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
|
||||
shouldAutorotate = (interfaceOrientation == UIInterfaceOrientationPortrait);
|
||||
} else {
|
||||
shouldAutorotate = YES;
|
||||
}
|
||||
|
||||
return shouldAutorotate;
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -39,22 +39,22 @@ typedef enum {
|
||||
@class BWHockeyManager;
|
||||
|
||||
@interface BWHockeyViewController : UITableViewController <PSStoreButtonDelegate> {
|
||||
BWHockeyManager *hockeyManager_;
|
||||
|
||||
NSDictionary *cellLayout;
|
||||
|
||||
BOOL modal_;
|
||||
BOOL kvoRegistered_;
|
||||
BOOL showAllVersions_;
|
||||
UIStatusBarStyle statusBarStyle_;
|
||||
PSAppStoreHeader *appStoreHeader_;
|
||||
PSStoreButton *appStoreButton_;
|
||||
|
||||
id popOverController_;
|
||||
|
||||
AppStoreButtonState appStoreButtonState_;
|
||||
|
||||
NSMutableArray *cells_;
|
||||
BWHockeyManager *hockeyManager_;
|
||||
|
||||
NSDictionary *cellLayout;
|
||||
|
||||
BOOL modal_;
|
||||
BOOL kvoRegistered_;
|
||||
BOOL showAllVersions_;
|
||||
UIStatusBarStyle statusBarStyle_;
|
||||
PSAppStoreHeader *appStoreHeader_;
|
||||
PSStoreButton *appStoreButton_;
|
||||
|
||||
id popOverController_;
|
||||
|
||||
AppStoreButtonState appStoreButtonState_;
|
||||
|
||||
NSMutableArray *cells_;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) BWHockeyManager *hockeyManager;
|
||||
|
@ -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];
|
||||
|
||||
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];
|
||||
)
|
||||
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];
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
- (UIImage *)addGlossToImage_:(UIImage *)image {
|
||||
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];
|
||||
|
||||
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
return result;
|
||||
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];
|
||||
|
||||
UIImage *result = UIGraphicsGetImageFromCurrentImageContext();
|
||||
UIGraphicsEndImageContext();
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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_;
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
- (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")];
|
||||
}
|
||||
|
||||
if ([app isEqual:self.hockeyManager.app]) {
|
||||
if ([app.notes length] > 0) {
|
||||
installed = [NSString stringWithFormat:@"<p> %@</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")];
|
||||
}
|
||||
// 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> %@</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);
|
||||
|
||||
[cell addWebView];
|
||||
// hack
|
||||
cell.textLabel.text = @"";
|
||||
|
||||
[cell addObserver:self forKeyPath:@"webViewSize" options:0 context:nil];
|
||||
} 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 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.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 = [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];
|
||||
}
|
||||
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 {
|
||||
@ -299,199 +299,199 @@
|
||||
UIColor *colorTwo = [UIColor colorWithHue:0.625 saturation:0.0 brightness:0.85 alpha:1.0];
|
||||
UIColor *colorThree = [UIColor colorWithHue:0.625 saturation:0.0 brightness:0.7 alpha:1.0];
|
||||
UIColor *colorFour = [UIColor colorWithHue:0.625 saturation:0.0 brightness:0.4 alpha:1.0];
|
||||
|
||||
|
||||
NSArray *colors = [NSArray arrayWithObjects:(id)colorOne.CGColor, colorTwo.CGColor, colorThree.CGColor, colorFour.CGColor, nil];
|
||||
|
||||
|
||||
NSNumber *stopOne = [NSNumber numberWithFloat:0.0];
|
||||
NSNumber *stopTwo = [NSNumber numberWithFloat:0.02];
|
||||
NSNumber *stopThree = [NSNumber numberWithFloat:0.99];
|
||||
NSNumber *stopFour = [NSNumber numberWithFloat:1.0];
|
||||
|
||||
|
||||
NSArray *locations = [NSArray arrayWithObjects:stopOne, stopTwo, stopThree, stopFour, nil];
|
||||
|
||||
|
||||
CAGradientLayer *headerLayer = [CAGradientLayer layer];
|
||||
//headerLayer.frame = CGRectMake(0.0, 0.0, 320.0, 77.0);
|
||||
headerLayer.colors = colors;
|
||||
headerLayer.locations = locations;
|
||||
|
||||
|
||||
return headerLayer;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
// 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;
|
||||
|
||||
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];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
[super viewDidLoad];
|
||||
|
||||
// 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;
|
||||
|
||||
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];
|
||||
|
||||
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";
|
||||
}
|
||||
|
||||
BOOL addGloss = YES;
|
||||
NSNumber *prerendered = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIPrerenderedIcon"];
|
||||
if (prerendered) {
|
||||
addGloss = ![prerendered boolValue];
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (addGloss) {
|
||||
appStoreHeader_.iconImage = [self addGlossToImage_:[UIImage imageNamed:iconString]];
|
||||
} else {
|
||||
appStoreHeader_.iconImage = [UIImage imageNamed:iconString];
|
||||
}
|
||||
|
||||
self.tableView.tableHeaderView = appStoreHeader_;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
BOOL addGloss = YES;
|
||||
NSNumber *prerendered = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIPrerenderedIcon"];
|
||||
if (prerendered) {
|
||||
addGloss = ![prerendered boolValue];
|
||||
}
|
||||
|
||||
if (addGloss) {
|
||||
appStoreHeader_.iconImage = [self addGlossToImage_:[UIImage imageNamed:iconString]];
|
||||
} else {
|
||||
appStoreHeader_.iconImage = [UIImage imageNamed:iconString];
|
||||
}
|
||||
|
||||
self.tableView.tableHeaderView = appStoreHeader_;
|
||||
|
||||
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_];
|
||||
|
||||
// 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++;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
[self restoreStoreButtonStateAnimated_:NO];
|
||||
[self updateAppStoreHeader_];
|
||||
|
||||
// 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++;
|
||||
|
||||
// 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;
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
// 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];
|
||||
[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;
|
||||
}
|
||||
|
||||
[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;
|
||||
|
||||
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 (rowHeight == 0) {
|
||||
rowHeight = indexPath.row == 0 ? 250 : 44; // fill screen on startup
|
||||
self.tableView.backgroundColor = BW_RGBCOLOR(200, 202, 204);
|
||||
}
|
||||
|
||||
return rowHeight;
|
||||
CGFloat rowHeight = 0;
|
||||
|
||||
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 (rowHeight == 0) {
|
||||
rowHeight = indexPath.row == 0 ? 250 : 44; // fill screen on startup
|
||||
self.tableView.backgroundColor = BW_RGBCOLOR(200, 202, 204);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
|
||||
shouldAutorotate = (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
|
||||
interfaceOrientation == UIInterfaceOrientationLandscapeRight ||
|
||||
interfaceOrientation == UIInterfaceOrientationPortrait);
|
||||
} else {
|
||||
shouldAutorotate = YES;
|
||||
}
|
||||
|
||||
return shouldAutorotate;
|
||||
BOOL shouldAutorotate;
|
||||
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
|
||||
shouldAutorotate = (interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
|
||||
interfaceOrientation == UIInterfaceOrientationLandscapeRight ||
|
||||
interfaceOrientation == UIInterfaceOrientationPortrait);
|
||||
} else {
|
||||
shouldAutorotate = YES;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
- (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
|
||||
|
@ -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,42 +140,42 @@ typedef enum CrashReportStatus {
|
||||
@end
|
||||
|
||||
@interface BWQuincyManager : NSObject <NSXMLParserDelegate> {
|
||||
NSString *_submissionURL;
|
||||
|
||||
id <BWQuincyManagerDelegate> _delegate;
|
||||
|
||||
BOOL _showAlwaysButton;
|
||||
BOOL _feedbackActivated;
|
||||
BOOL _autoSubmitCrashReport;
|
||||
BOOL _autoSubmitDeviceUDID;
|
||||
|
||||
BOOL _didCrashInLastSession;
|
||||
|
||||
NSString *_appIdentifier;
|
||||
|
||||
NSString *_feedbackRequestID;
|
||||
float _feedbackDelayInterval;
|
||||
|
||||
NSString *_submissionURL;
|
||||
|
||||
id <BWQuincyManagerDelegate> _delegate;
|
||||
|
||||
BOOL _showAlwaysButton;
|
||||
BOOL _feedbackActivated;
|
||||
BOOL _autoSubmitCrashReport;
|
||||
BOOL _autoSubmitDeviceUDID;
|
||||
|
||||
BOOL _didCrashInLastSession;
|
||||
|
||||
NSString *_appIdentifier;
|
||||
|
||||
NSString *_feedbackRequestID;
|
||||
float _feedbackDelayInterval;
|
||||
|
||||
NSMutableString *_contentOfProperty;
|
||||
CrashReportStatus _serverResult;
|
||||
|
||||
|
||||
int _analyzerStarted;
|
||||
NSString *_crashesDir;
|
||||
|
||||
BOOL _crashIdenticalCurrentVersion;
|
||||
BOOL _crashReportActivated;
|
||||
|
||||
BOOL _crashReportActivated;
|
||||
|
||||
NSMutableArray *_crashFiles;
|
||||
|
||||
NSMutableData *_responseData;
|
||||
NSInteger _statusCode;
|
||||
|
||||
NSURLConnection *_urlConnection;
|
||||
|
||||
NSData *_crashData;
|
||||
|
||||
NSString *_languageStyle;
|
||||
BOOL _sendingInProgress;
|
||||
|
||||
NSURLConnection *_urlConnection;
|
||||
|
||||
NSData *_crashData;
|
||||
|
||||
NSString *_languageStyle;
|
||||
BOOL _sendingInProgress;
|
||||
}
|
||||
|
||||
+ (BWQuincyManager *)sharedQuincyManager;
|
||||
|
@ -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;
|
||||
|
||||
dispatch_once(&pred, ^{
|
||||
sharedInstance = [BWQuincyManager alloc];
|
||||
sharedInstance = [sharedInstance init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
static BWQuincyManager *sharedInstance = nil;
|
||||
static dispatch_once_t pred;
|
||||
|
||||
dispatch_once(&pred, ^{
|
||||
sharedInstance = [BWQuincyManager alloc];
|
||||
sharedInstance = [sharedInstance init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
#else
|
||||
+ (BWQuincyManager *)sharedQuincyManager {
|
||||
@ -116,24 +116,24 @@ 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) {
|
||||
_analyzerStarted = [[NSUserDefaults standardUserDefaults] integerForKey:kQuincyKitAnalyzerStarted];
|
||||
@ -149,7 +149,7 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
|
||||
_crashReportActivated = YES;
|
||||
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:YES] forKey:kQuincyKitActivated];
|
||||
}
|
||||
|
||||
|
||||
if (_crashReportActivated) {
|
||||
_crashFiles = [[NSMutableArray alloc] init];
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||
@ -163,52 +163,52 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
|
||||
|
||||
[fm createDirectoryAtPath:_crashesDir withIntermediateDirectories: YES attributes: attributes error: &theError];
|
||||
}
|
||||
|
||||
|
||||
PLCrashReporter *crashReporter = [PLCrashReporter sharedReporter];
|
||||
NSError *error = NULL;
|
||||
|
||||
// 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];
|
||||
|
||||
[_languageStyle release];
|
||||
|
||||
[_submissionURL release];
|
||||
_submissionURL = nil;
|
||||
|
||||
[_appIdentifier release];
|
||||
_appIdentifier = nil;
|
||||
|
||||
[_urlConnection cancel];
|
||||
[_urlConnection release];
|
||||
_urlConnection = nil;
|
||||
|
||||
[_crashData release];
|
||||
|
||||
self.delegate = nil;
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self name:BWQuincyNetworkBecomeReachable object:nil];
|
||||
|
||||
[_languageStyle release];
|
||||
|
||||
[_submissionURL release];
|
||||
_submissionURL = nil;
|
||||
|
||||
[_appIdentifier release];
|
||||
_appIdentifier = nil;
|
||||
|
||||
[_urlConnection cancel];
|
||||
[_urlConnection release];
|
||||
_urlConnection = nil;
|
||||
|
||||
[_crashData release];
|
||||
|
||||
[_crashesDir release];
|
||||
[_crashFiles release];
|
||||
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@ -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];
|
||||
}
|
||||
|
||||
[self performSelector:@selector(startManager) withObject:nil afterDelay:1.0f];
|
||||
if (_submissionURL != anSubmissionURL) {
|
||||
[_submissionURL release];
|
||||
_submissionURL = [anSubmissionURL copy];
|
||||
}
|
||||
|
||||
[self performSelector:@selector(startManager) withObject:nil afterDelay:1.0f];
|
||||
}
|
||||
|
||||
- (void)setAppIdentifier:(NSString *)anAppIdentifier {
|
||||
if (_appIdentifier != anAppIdentifier) {
|
||||
[_appIdentifier release];
|
||||
_appIdentifier = [anAppIdentifier copy];
|
||||
}
|
||||
|
||||
[self setSubmissionURL:@"https://rink.hockeyapp.net/"];
|
||||
if (_appIdentifier != anAppIdentifier) {
|
||||
[_appIdentifier release];
|
||||
_appIdentifier = [anAppIdentifier copy];
|
||||
}
|
||||
|
||||
[self setSubmissionURL:@"https://rink.hockeyapp.net/"];
|
||||
}
|
||||
|
||||
|
||||
@ -238,65 +238,65 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
|
||||
#pragma mark private methods
|
||||
|
||||
- (BOOL)autoSendCrashReports {
|
||||
BOOL result = NO;
|
||||
|
||||
if (!self.autoSubmitCrashReport) {
|
||||
if (self.isShowingAlwaysButton && [[NSUserDefaults standardUserDefaults] boolForKey: kAutomaticallySendCrashReports]) {
|
||||
result = YES;
|
||||
}
|
||||
} else {
|
||||
result = YES;
|
||||
BOOL result = NO;
|
||||
|
||||
if (!self.autoSubmitCrashReport) {
|
||||
if (self.isShowingAlwaysButton && [[NSUserDefaults standardUserDefaults] boolForKey: kAutomaticallySendCrashReports]) {
|
||||
result = YES;
|
||||
}
|
||||
|
||||
return result;
|
||||
} else {
|
||||
result = YES;
|
||||
}
|
||||
|
||||
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]) {
|
||||
|
||||
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(willShowSubmitCrashReportAlert)]) {
|
||||
[self.delegate willShowSubmitCrashReportAlert];
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
if ([self isShowingAlwaysButton]) {
|
||||
[alertView addButtonWithTitle:BWQuincyLocalize(@"CrashSendReportAlways")];
|
||||
}
|
||||
|
||||
[alertView setTag: QuincyKitAlertTypeSend];
|
||||
[alertView show];
|
||||
[alertView release];
|
||||
} else {
|
||||
[self _sendCrashReports];
|
||||
}
|
||||
[self _sendCrashReports];
|
||||
} else if (!self.autoSubmitCrashReport && [self hasNonApprovedCrashReports]) {
|
||||
|
||||
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(willShowSubmitCrashReportAlert)]) {
|
||||
[self.delegate willShowSubmitCrashReportAlert];
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
if ([self isShowingAlwaysButton]) {
|
||||
[alertView addButtonWithTitle:BWQuincyLocalize(@"CrashSendReportAlways")];
|
||||
}
|
||||
|
||||
[alertView setTag: QuincyKitAlertTypeSend];
|
||||
[alertView show];
|
||||
[alertView release];
|
||||
} else {
|
||||
[self _sendCrashReports];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)hasNonApprovedCrashReports {
|
||||
NSDictionary *approvedCrashReports = [[NSUserDefaults standardUserDefaults] dictionaryForKey: kApprovedCrashReports];
|
||||
|
||||
if (!approvedCrashReports || [approvedCrashReports count] == 0) return YES;
|
||||
|
||||
NSDictionary *approvedCrashReports = [[NSUserDefaults standardUserDefaults] dictionaryForKey: kApprovedCrashReports];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return NO;
|
||||
if (![approvedCrashReports objectForKey:filename]) return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasPendingCrashReport {
|
||||
@ -305,8 +305,8 @@ 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];
|
||||
|
||||
while ((file = [dirEnum nextObject])) {
|
||||
@ -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]];
|
||||
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSMutableDictionary *approvedCrashReports = [NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults] dictionaryForKey: kApprovedCrashReports]];
|
||||
|
||||
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,9 +481,9 @@ 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]];
|
||||
NSData *crashData = [NSData dataWithContentsOfFile:filename];
|
||||
@ -491,87 +491,87 @@ 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];
|
||||
|
||||
|
||||
if ([report.applicationInfo.applicationVersion compare:[[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"]] == NSOrderedSame) {
|
||||
_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];
|
||||
|
||||
if (crashes != nil) {
|
||||
[self _postXML:[NSString stringWithFormat:@"<crashes>%@</crashes>", crashes]
|
||||
toURL:[NSURL URLWithString:self.submissionURL]];
|
||||
|
||||
}
|
||||
[[NSUserDefaults standardUserDefaults] setObject:approvedCrashReports forKey:kApprovedCrashReports];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
|
||||
if (crashes != nil) {
|
||||
[self _postXML:[NSString stringWithFormat:@"<crashes>%@</crashes>", crashes]
|
||||
toURL:[NSURL URLWithString:self.submissionURL]];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_cleanCrashReports {
|
||||
NSError *error = NULL;
|
||||
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
|
||||
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];
|
||||
NSError *error = NULL;
|
||||
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
- (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;
|
||||
|
||||
request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@api/2/apps/%@/crashes/%@",
|
||||
self.submissionURL,
|
||||
[self.appIdentifier stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
|
||||
_feedbackRequestID
|
||||
]
|
||||
]];
|
||||
|
||||
NSMutableURLRequest *request = nil;
|
||||
|
||||
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"];
|
||||
|
||||
|
||||
_serverResult = CrashReportStatusUnknown;
|
||||
_statusCode = 200;
|
||||
|
||||
@ -587,21 +587,21 @@ NSString *BWQuincyLocalize(NSString *stringToken) {
|
||||
|
||||
- (void)_postXML:(NSString*)xml toURL:(NSURL*)url {
|
||||
NSMutableURLRequest *request = nil;
|
||||
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];
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
[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]];
|
||||
|
||||
[request setHTTPBody:postBody];
|
||||
[postBody appendData:[xml dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
[request setHTTPBody:postBody];
|
||||
|
||||
_serverResult = CrashReportStatusUnknown;
|
||||
_statusCode = 200;
|
||||
@ -631,10 +631,10 @@ 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];
|
||||
|
||||
_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 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];
|
||||
}
|
||||
[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];
|
||||
|
||||
[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];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[_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]];
|
||||
|
||||
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];
|
||||
|
||||
// Try loading the crash report
|
||||
_crashData = [[NSData alloc] initWithData:[crashReporter loadPendingCrashReportDataAndReturnError: &error]];
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@ -750,8 +750,8 @@ 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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -26,12 +26,12 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface PSAppStoreHeader : UIView {
|
||||
NSString *headerLabel_;
|
||||
NSString *middleHeaderLabel_;
|
||||
NSString *subHeaderLabel;
|
||||
UIImage *iconImage_;
|
||||
|
||||
UIImage *reflectedImage_;
|
||||
NSString *headerLabel_;
|
||||
NSString *middleHeaderLabel_;
|
||||
NSString *subHeaderLabel;
|
||||
UIImage *iconImage_;
|
||||
|
||||
UIImage *reflectedImage_;
|
||||
}
|
||||
|
||||
@property (nonatomic, copy) NSString *headerLabel;
|
||||
|
@ -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];
|
||||
|
||||
[super dealloc];
|
||||
[headerLabel_ release];
|
||||
[middleHeaderLabel_ release];
|
||||
[subHeaderLabel release];
|
||||
[iconImage_ release];
|
||||
|
||||
[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();
|
||||
|
||||
// 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];
|
||||
|
||||
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)];
|
||||
|
||||
// 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);)
|
||||
|
||||
|
||||
[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);
|
||||
|
||||
// sub
|
||||
[secondaryTextColor set];
|
||||
[subHeaderLabel drawAtPoint:CGPointMake(kTextRow, kImageMargin+kImageHeight-12) forWidth:globalWidth-kTextRow withFont:smallFont lineBreakMode:UILineBreakModeTailTruncation];
|
||||
|
||||
CGColorRelease(myColor);
|
||||
CGColorSpaceRelease(myColorSpace);
|
||||
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 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);
|
||||
|
||||
// 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!
|
||||
|
||||
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];
|
||||
|
||||
// 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];
|
||||
|
||||
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];
|
||||
|
||||
// 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];
|
||||
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];
|
||||
|
||||
// create reflected image
|
||||
[reflectedImage_ release];
|
||||
reflectedImage_ = nil;
|
||||
if (anIconImage) {
|
||||
reflectedImage_ = [[iconImage_ bw_reflectedImageWithHeight:kReflectionHeight fromAlpha:0.5 toAlpha:0.0] retain];
|
||||
}
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -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_;
|
||||
|
||||
CAGradientLayer *gradient_;
|
||||
CGPoint customPadding_;
|
||||
PSStoreButtonData *buttonData_;
|
||||
id<PSStoreButtonDelegate> buttonDelegate_;
|
||||
|
||||
CAGradientLayer *gradient_;
|
||||
CGPoint customPadding_;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame;
|
||||
|
@ -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];
|
||||
|
||||
[super dealloc];
|
||||
[label_ release];
|
||||
[colors_ release];
|
||||
|
||||
[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;
|
||||
|
||||
// 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];
|
||||
|
||||
CGRect newFrame = aLayer.frame;
|
||||
newFrame.size.width = sizeThatFits.width;
|
||||
aLayer.frame = newFrame;
|
||||
|
||||
[CATransaction commit];
|
||||
}
|
||||
|
||||
// set outer frame changes
|
||||
self.titleEdgeInsets = UIEdgeInsetsMake(2.0, self.titleEdgeInsets.left, 0.0, 0.0);
|
||||
[self alignToSuperview];
|
||||
|
||||
if (animated) {
|
||||
[UIView commitAnimations];
|
||||
}
|
||||
|
||||
// set outer frame changes
|
||||
self.titleEdgeInsets = UIEdgeInsetsMake(2.0, self.titleEdgeInsets.left, 0.0, 0.0);
|
||||
[self alignToSuperview];
|
||||
|
||||
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,55 +175,55 @@
|
||||
#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]];
|
||||
|
||||
// register for touch events
|
||||
[self addTarget:self action:@selector(touchedUpOutside:) forControlEvents:UIControlEventTouchUpOutside];
|
||||
|
||||
// setup title label
|
||||
[self.titleLabel setFont:[UIFont boldSystemFontOfSize:13.0]];
|
||||
|
||||
// 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];
|
||||
topBorderLayer.frame = CGRectMake(0.5, 0.5, CGRectGetWidth(frame) - 1.0, CGRectGetHeight(frame) - 1.0);
|
||||
topBorderLayer.cornerRadius = 2.6;
|
||||
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];
|
||||
|
||||
[super dealloc];
|
||||
[buttonData_ release];
|
||||
[gradient_ release];
|
||||
|
||||
[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;
|
||||
|
||||
CGSize sizeThatFits = CGSizeMake(newWidth, newHeight);
|
||||
return sizeThatFits;
|
||||
CGFloat newHeight = PS_MIN_HEIGHT > newSize.height ? PS_MIN_HEIGHT : newSize.height;
|
||||
|
||||
CGSize sizeThatFits = CGSizeMake(newWidth, newHeight);
|
||||
return sizeThatFits;
|
||||
}
|
||||
|
||||
- (void)setFrame:(CGRect)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];
|
||||
[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];
|
||||
}
|
||||
}
|
||||
|
||||
@ -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];
|
||||
}
|
||||
|
||||
[self updateButtonAnimated:animated];
|
||||
if (buttonData_ != aButtonData) {
|
||||
[buttonData_ release];
|
||||
buttonData_ = [aButtonData retain];
|
||||
}
|
||||
|
||||
[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
|
||||
|
@ -27,11 +27,11 @@
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface PSWebTableViewCell : UITableViewCell <UIWebViewDelegate> {
|
||||
UIWebView *webView_;
|
||||
NSString *webViewContent_;
|
||||
CGSize webViewSize_;
|
||||
|
||||
UIColor *cellBackgroundColor_;
|
||||
UIWebView *webView_;
|
||||
NSString *webViewContent_;
|
||||
CGSize webViewSize_;
|
||||
|
||||
UIColor *cellBackgroundColor_;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) UIWebView *webView;
|
||||
|
@ -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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
// 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];
|
||||
|
||||
// add basic accessiblity (prevents "snarfed from ivar layout") logs
|
||||
self.accessibilityLabel = aWebViewContent;
|
||||
}
|
||||
if (webViewContent_ != aWebViewContent) {
|
||||
[webViewContent_ release];
|
||||
webViewContent_ = [aWebViewContent retain];
|
||||
|
||||
// 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];
|
||||
|
||||
if (needChange) {
|
||||
[self addWebView];
|
||||
}
|
||||
BOOL needChange = !CGRectEqualToRect(aFrame, self.frame);
|
||||
[super setFrame:aFrame];
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
@ -165,25 +165,25 @@ body { font: 13px 'Helvetica Neue', Helvetica; word-wrap:break-word; padding:8px
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
|
||||
if(navigationType == UIWebViewNavigationTypeOther)
|
||||
return YES;
|
||||
|
||||
return NO;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
- (void)webViewDidFinishLoad:(UIWebView *)webView {
|
||||
if(webViewContent_)
|
||||
[self showWebView];
|
||||
|
||||
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]);
|
||||
[self showWebView];
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
return imageWithAlpha;
|
||||
if ([self hasAlpha]) {
|
||||
return self;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
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.
|
||||
|
||||
// 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();
|
||||
)
|
||||
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);
|
||||
|
||||
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];
|
||||
|
||||
// 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);
|
||||
|
||||
// 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 UIImage from the CGImage
|
||||
roundedImage = [UIImage imageWithCGImage:clippedImage];
|
||||
CGImageRelease(clippedImage);
|
||||
}
|
||||
return roundedImage;
|
||||
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));
|
||||
|
||||
// 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);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
#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;
|
||||
|
||||
// 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;
|
||||
|
||||
// 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);
|
||||
|
||||
// Create appropriately modified image.
|
||||
|
||||
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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Calculate compositing rectangles
|
||||
CGRect sourceRect, destRect;
|
||||
sourceRect = CGRectMake(0, 0, sourceWidth, sourceHeight);
|
||||
destRect = CGRectMake(0, 0, scaledWidth, scaledHeight);
|
||||
|
||||
// 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);
|
||||
@ -233,105 +233,105 @@ CGImageRef CreateGradientImage(int pixelsWide, int pixelsHigh, float fromAlpha,
|
||||
image = [UIImage imageWithCGImage:finalImage];
|
||||
CGImageRelease(finalImage);
|
||||
}
|
||||
|
||||
return image;
|
||||
|
||||
return image;
|
||||
}
|
||||
|
||||
|
||||
|
||||
CGImageRef CreateGradientImage(int pixelsWide, int pixelsHigh, float fromAlpha, float toAlpha) {
|
||||
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)
|
||||
CGFloat colors[] = {toAlpha, 1.0, fromAlpha, 1.0};
|
||||
|
||||
|
||||
// create the CGGradient and then release the gray color space
|
||||
CGGradientRef grayScaleGradient = CGGradientCreateWithColorComponents(colorSpace, colors, NULL, 2);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
|
||||
|
||||
// create the start and end points for the gradient vector (straight down)
|
||||
CGPoint gradientEndPoint = CGPointZero;
|
||||
CGPoint gradientStartPoint = CGPointMake(0, pixelsHigh);
|
||||
|
||||
|
||||
// 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
|
||||
theCGImage = CGBitmapContextCreateImage(gradientBitmapContext);
|
||||
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);
|
||||
}
|
||||
|
||||
return UIGraphicsGetCurrentContext();
|
||||
CGSize size = CGSizeMake(pixelsWide, pixelsHigh);
|
||||
if (UIGraphicsBeginImageContextWithOptions != NULL) {
|
||||
UIGraphicsBeginImageContextWithOptions(size, NO, 0.0);
|
||||
}
|
||||
else {
|
||||
UIGraphicsBeginImageContext(size);
|
||||
}
|
||||
|
||||
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
|
||||
CGContextRef mainViewContentContext = MyOpenBitmapContext(self.size.width, height);
|
||||
|
||||
|
||||
// create a 2 bit CGImage containing a gradient that will be used for masking the
|
||||
// main view content to create the 'fade' of the reflection. The CGImageCreateWithMask
|
||||
// function will stretch the bitmap image as required, so we can create a 1 pixel wide gradient
|
||||
CGImageRef gradientMaskImage = CreateGradientImage(1, height, fromAlpha, toAlpha);
|
||||
|
||||
|
||||
// create an image by masking the bitmap of the mainView content with the gradient view
|
||||
// then release the pre-masked content bitmap and the gradient bitmap
|
||||
CGContextClipToMask(mainViewContentContext, CGRectMake(0.0, 0.0, self.size.width, height), gradientMaskImage);
|
||||
CGImageRelease(gradientMaskImage);
|
||||
|
||||
|
||||
// draw the image into the bitmap context
|
||||
CGContextDrawImage(mainViewContentContext, CGRectMake(0, 0, self.size.width, self.size.height), self.CGImage);
|
||||
|
||||
|
||||
// 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 ([[NSFileManager defaultManager] fileExistsAtPath:path2x]) {
|
||||
return [self initWithContentsOfFile:path2x];
|
||||
}
|
||||
}
|
||||
if ([UIScreen instancesRespondToSelector:@selector(scale)] && (int)[[UIScreen mainScreen] scale] == 2.0) {
|
||||
NSString *path2x = [[path stringByDeletingLastPathComponent]
|
||||
stringByAppendingPathComponent:[NSString stringWithFormat:@"%@@2x.%@",
|
||||
[[path lastPathComponent] stringByDeletingPathExtension],
|
||||
[path pathExtension]]];
|
||||
|
||||
return [self initWithContentsOfFile:path];
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path2x]) {
|
||||
return [self initWithContentsOfFile:path2x];
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user