Initial revision with new CNSHockeyManager.
BIN
Classes/.DS_Store
vendored
Normal file
56
Classes/BWApp.h
Normal file
@ -0,0 +1,56 @@
|
||||
//
|
||||
// BWApp.h
|
||||
// HockeyDemo
|
||||
//
|
||||
// Created by Peter Steinberger on 04.02.11.
|
||||
// Copyright 2011 Buzzworks. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface BWApp : NSObject {
|
||||
NSString *name_;
|
||||
NSString *version_;
|
||||
NSString *shortVersion_;
|
||||
NSString *notes_;
|
||||
NSDate *date_;
|
||||
NSNumber *size_;
|
||||
NSNumber *mandatory_;
|
||||
}
|
||||
@property (nonatomic, copy) NSString *name;
|
||||
@property (nonatomic, copy) NSString *version;
|
||||
@property (nonatomic, copy) NSString *shortVersion;
|
||||
@property (nonatomic, copy) NSString *notes;
|
||||
@property (nonatomic, copy) NSDate *date;
|
||||
@property (nonatomic, copy) NSNumber *size;
|
||||
@property (nonatomic, copy) NSNumber *mandatory;
|
||||
|
||||
- (NSString *)nameAndVersionString;
|
||||
- (NSString *)versionString;
|
||||
- (NSString *)dateString;
|
||||
- (NSString *)sizeInMB;
|
||||
- (NSString *)notesOrEmptyString;
|
||||
- (void)setDateWithTimestamp:(NSTimeInterval)timestamp;
|
||||
- (BOOL)isValid;
|
||||
- (BOOL)isEqualToBWApp:(BWApp *)anApp;
|
||||
|
||||
+ (BWApp *)appFromDict:(NSDictionary *)dict;
|
||||
|
||||
@end
|
186
Classes/BWApp.m
Normal file
@ -0,0 +1,186 @@
|
||||
//
|
||||
// BWApp.m
|
||||
// HockeyDemo
|
||||
//
|
||||
// Created by Peter Steinberger on 04.02.11.
|
||||
// Copyright 2011 Buzzworks. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "BWApp.h"
|
||||
#import "BWGlobal.h"
|
||||
|
||||
@implementation BWApp
|
||||
|
||||
@synthesize name = name_;
|
||||
@synthesize version = version_;
|
||||
@synthesize shortVersion = shortVersion_;
|
||||
@synthesize notes = notes_;
|
||||
@synthesize date = date_;
|
||||
@synthesize size = size_;
|
||||
@synthesize mandatory = mandatory_;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#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;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark NSObject
|
||||
|
||||
- (void)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];
|
||||
}
|
||||
|
||||
- (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;
|
||||
return YES;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#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"];
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark Properties
|
||||
|
||||
- (NSString *)nameAndVersionString {
|
||||
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 *)dateString {
|
||||
NSDateFormatter *formatter = [[[NSDateFormatter alloc] init] autorelease];
|
||||
[formatter setDateStyle:NSDateFormatterMediumStyle];
|
||||
|
||||
return [formatter stringFromDate:self.date];
|
||||
}
|
||||
|
||||
- (NSString *)sizeInMB {
|
||||
if ([[size_ class] 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;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)notesOrEmptyString {
|
||||
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;
|
||||
}
|
||||
|
||||
@end
|
116
Classes/BWGlobal.h
Normal file
@ -0,0 +1,116 @@
|
||||
//
|
||||
// BWGlobal.h
|
||||
//
|
||||
// Created by Andreas Linde on 08/17/10.
|
||||
// Copyright 2010-2011 Andreas Linde, Peter Steinberger. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "BWHockeyManager.h"
|
||||
#import "BWApp.h"
|
||||
|
||||
#define HOCKEYKIT_VERSION_MAJOR 2
|
||||
#define HOCKEYKIT_VERSION_MINOR 0
|
||||
|
||||
// uncomment this line to enable NSLog-debugging output
|
||||
//#define kHockeyDebugEnabled
|
||||
|
||||
#define kArrayOfLastHockeyCheck @"ArrayOfLastHockeyCheck"
|
||||
#define kDateOfLastHockeyCheck @"DateOfLastHockeyCheck"
|
||||
#define kDateOfVersionInstallation @"DateOfVersionInstallation"
|
||||
#define kUsageTimeOfCurrentVersion @"UsageTimeOfCurrentVersion"
|
||||
#define kUsageTimeForVersionString @"kUsageTimeForVersionString"
|
||||
#define kHockeyAutoUpdateSetting @"HockeyAutoUpdateSetting"
|
||||
#define kHockeyAllowUserSetting @"HockeyAllowUserSetting"
|
||||
#define kHockeyAllowUsageSetting @"HockeyAllowUsageSetting"
|
||||
#define kHockeyAutoUpdateSetting @"HockeyAutoUpdateSetting"
|
||||
#define kHockeyAuthorizedVersion @"HockeyAuthorizedVersion"
|
||||
#define kHockeyAuthorizedToken @"HockeyAuthorizedToken"
|
||||
|
||||
#define kHockeyBundleName @"Hockey.bundle"
|
||||
|
||||
// Notification message which HockeyManager is listening to, to retry requesting updated from the server
|
||||
#define BWHockeyNetworkBecomeReachable @"NetworkDidBecomeReachable"
|
||||
|
||||
#define BWHockeyLog(fmt, ...) do { if([BWHockeyManager sharedHockeyManager].isLoggingEnabled) { NSLog((@"[HockeyLib] %s/%d " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__); }} while(0)
|
||||
|
||||
NSBundle *hockeyBundle(void);
|
||||
NSString *BWmd5(NSString *str);
|
||||
|
||||
#define BWHockeyLocalize(StringToken) NSLocalizedStringFromTableInBundle(StringToken, @"Hockey", hockeyBundle(), @"")
|
||||
|
||||
|
||||
// compatibility helper
|
||||
#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_3_2
|
||||
#define kCFCoreFoundationVersionNumber_iPhoneOS_3_2 478.61
|
||||
#endif
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 32000
|
||||
#define BW_IF_3_2_OR_GREATER(...) \
|
||||
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_3_2) \
|
||||
{ \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
#else
|
||||
#define BW_IF_3_2_OR_GREATER(...)
|
||||
#endif
|
||||
#define BW_IF_PRE_3_2(...) \
|
||||
if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_3_2) \
|
||||
{ \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
|
||||
#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_4_0
|
||||
#define kCFCoreFoundationVersionNumber_iPhoneOS_4_0 550.32
|
||||
#endif
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
|
||||
#define BW_IF_IOS4_OR_GREATER(...) \
|
||||
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_4_0) \
|
||||
{ \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
#else
|
||||
#define BW_IF_IOS4_OR_GREATER(...)
|
||||
#endif
|
||||
|
||||
#define BW_IF_PRE_IOS4(...) \
|
||||
if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_4_0) \
|
||||
{ \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifndef kCFCoreFoundationVersionNumber_iPhoneOS_5_0
|
||||
#define kCFCoreFoundationVersionNumber_iPhoneOS_5_0 674.0
|
||||
#endif
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 50000
|
||||
#define BW_IF_IOS5_OR_GREATER(...) \
|
||||
if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iPhoneOS_5_0) \
|
||||
{ \
|
||||
__VA_ARGS__ \
|
||||
}
|
||||
#else
|
||||
#define BW_IF_IOS5_OR_GREATER(...)
|
||||
#endif
|
||||
|
||||
#define BW_IF_PRE_IOS5(...) \
|
||||
if (kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iPhoneOS_5_0) \
|
||||
{ \
|
||||
__VA_ARGS__ \
|
||||
}
|
53
Classes/BWGlobal.m
Normal file
@ -0,0 +1,53 @@
|
||||
//
|
||||
// BWGlobal.h
|
||||
//
|
||||
// Created by Andreas Linde on 08/17/10.
|
||||
// Copyright 2010-2011 Andreas Linde, Peter Steinberger. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "BWGlobal.h"
|
||||
#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;
|
||||
}
|
||||
|
||||
NSString *BWmd5(NSString *str) {
|
||||
const char *cStr = [str UTF8String];
|
||||
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]
|
||||
];
|
||||
}
|
227
Classes/BWHockeyManager.h
Normal file
@ -0,0 +1,227 @@
|
||||
//
|
||||
// BWHockeyManager.h
|
||||
//
|
||||
// Created by Andreas Linde on 8/17/10.
|
||||
// Copyright 2010-2011 Andreas Linde. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "BWHockeyViewController.h"
|
||||
|
||||
typedef enum {
|
||||
HockeyComparisonResultDifferent,
|
||||
HockeyComparisonResultGreater
|
||||
} HockeyComparisonResult;
|
||||
|
||||
typedef enum {
|
||||
HockeyAuthorizationDenied,
|
||||
HockeyAuthorizationAllowed,
|
||||
HockeyAuthorizationPending
|
||||
} HockeyAuthorizationState;
|
||||
|
||||
typedef enum {
|
||||
HockeyUpdateCheckStartup = 0,
|
||||
HockeyUpdateCheckDaily = 1,
|
||||
HockeyUpdateCheckManually = 2
|
||||
} HockeyUpdateSetting;
|
||||
|
||||
@protocol BWHockeyManagerDelegate;
|
||||
|
||||
@class BWApp;
|
||||
|
||||
@interface BWHockeyManager : NSObject <UIAlertViewDelegate> {
|
||||
id <BWHockeyManagerDelegate> delegate_;
|
||||
NSArray *apps_;
|
||||
|
||||
NSString *updateURL_;
|
||||
NSString *appIdentifier_;
|
||||
NSString *currentAppVersion_;
|
||||
|
||||
UINavigationController *navController_;
|
||||
BWHockeyViewController *currentHockeyViewController_;
|
||||
UIView *authorizeView_;
|
||||
|
||||
NSMutableData *receivedData_;
|
||||
|
||||
BOOL loggingEnabled_;
|
||||
BOOL checkInProgress_;
|
||||
BOOL dataFound;
|
||||
BOOL updateAvailable_;
|
||||
BOOL showFeedback_;
|
||||
BOOL updateURLOffline_;
|
||||
BOOL updateAlertShowing_;
|
||||
BOOL lastCheckFailed_;
|
||||
|
||||
NSURLConnection *urlConnection_;
|
||||
NSDate *lastCheck_;
|
||||
NSDate *usageStartTimestamp_;
|
||||
|
||||
BOOL sendUserData_;
|
||||
BOOL sendUsageTime_;
|
||||
BOOL allowUserToDisableSendData_;
|
||||
BOOL userAllowsSendUserData_;
|
||||
BOOL userAllowsSendUsageTime_;
|
||||
BOOL showUpdateReminder_;
|
||||
BOOL checkForUpdateOnLaunch_;
|
||||
HockeyComparisonResult compareVersionType_;
|
||||
HockeyUpdateSetting updateSetting_;
|
||||
BOOL showUserSettings_;
|
||||
BOOL showDirectInstallOption_;
|
||||
|
||||
BOOL requireAuthorization_;
|
||||
NSString *authenticationSecret_;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// this is a singleton
|
||||
+ (BWHockeyManager *)sharedHockeyManager;
|
||||
|
||||
// update url needs to be set
|
||||
@property (nonatomic, retain) NSString *updateURL;
|
||||
|
||||
// private app identifier (optional)
|
||||
@property (nonatomic, retain) NSString *appIdentifier;
|
||||
|
||||
// delegate is optional
|
||||
@property (nonatomic, assign) id <BWHockeyManagerDelegate> delegate;
|
||||
|
||||
// hockey secret is required if authentication is used
|
||||
@property (nonatomic, retain) NSString *authenticationSecret;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// settings
|
||||
|
||||
// if YES, the API will return an existing JMC config
|
||||
// if NO, the API will return only version information
|
||||
@property (nonatomic, assign) BOOL checkForTracker;
|
||||
|
||||
@property (nonatomic, retain, readonly) NSDictionary *trackerConfig;
|
||||
|
||||
// if YES, states will be logged using NSLog. Only enable this for debugging!
|
||||
// if NO, nothing will be logged. (default)
|
||||
@property (nonatomic, assign, getter=isLoggingEnabled) BOOL loggingEnabled;
|
||||
|
||||
// if YES, the current user data is send: device type, iOS version, app version, UDID (default)
|
||||
// if NO, no such data is send to the server
|
||||
@property (nonatomic, assign, getter=shouldSendUserData) BOOL sendUserData;
|
||||
|
||||
// if YES, the the users usage time of the app to the service, only in 1 minute granularity! (default)
|
||||
// if NO, no such data is send to the server
|
||||
@property (nonatomic, assign, getter=shouldSendUsageTime) BOOL sendUsageTime;
|
||||
|
||||
// if YES, the user agrees to send the usage data, user can change it if the developer shows the settings (default)
|
||||
// if NO, the user overwrites the developer setting and no such data is sent
|
||||
@property (nonatomic, assign, getter=isAllowUserToDisableSendData) BOOL allowUserToDisableSendData;
|
||||
|
||||
// if YES, the user allowed to send user data (default)
|
||||
// if NO, the user denied to send user data
|
||||
@property (nonatomic, assign, getter=doesUserAllowsSendUserData) BOOL userAllowsSendUserData;
|
||||
|
||||
// if YES, the user allowed to send usage data (default)
|
||||
// if NO, the user denied to send usage data
|
||||
@property (nonatomic, assign, getter=doesUserAllowsSendUsageTime) BOOL userAllowsSendUsageTime;
|
||||
|
||||
// if YES, the new version alert will be displayed always if the current version is outdated (default)
|
||||
// if NO, the alert will be displayed only once for each new update
|
||||
@property (nonatomic, assign) BOOL alwaysShowUpdateReminder;
|
||||
|
||||
// if YES, the user can change the HockeyUpdateSetting value (default)
|
||||
// if NO, the user can not change it, and the default or developer defined value will be used
|
||||
@property (nonatomic, assign, getter=shouldShowUserSettings) BOOL showUserSettings;
|
||||
|
||||
// if YES, then an update check will be performed after the application becomes active (default)
|
||||
// if NO, then the update check will not happen unless invoked explicitly
|
||||
@property (nonatomic, assign, getter=isCheckForUpdateOnLaunch) BOOL checkForUpdateOnLaunch;
|
||||
|
||||
// if YES, the alert notifying about an new update also shows a button to install the update directly
|
||||
// if NO, the alert notifying about an new update only shows ignore and show update button
|
||||
@property (nonatomic, assign, getter=ishowingDirectInstallOption) BOOL showDirectInstallOption;
|
||||
|
||||
// if YES, each app version needs to be authorized by the server to run on this device
|
||||
// if NO, each app version does not need to be authorized (default)
|
||||
@property (nonatomic, assign, getter=isRequireAuthorization) BOOL requireAuthorization;
|
||||
|
||||
// HockeyComparisonResultDifferent: alerts if the version on the server is different (default)
|
||||
// HockeyComparisonResultGreater: alerts if the version on the server is greater
|
||||
@property (nonatomic, assign) HockeyComparisonResult compareVersionType;
|
||||
|
||||
// see HockeyUpdateSetting-enum. Will be saved in user defaults.
|
||||
// default value: HockeyUpdateCheckStartup
|
||||
@property (nonatomic, assign) HockeyUpdateSetting updateSetting;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// is an update available?
|
||||
- (BOOL)isUpdateAvailable;
|
||||
|
||||
// are we currently checking for updates?
|
||||
- (BOOL)isCheckInProgress;
|
||||
|
||||
// open update info view
|
||||
- (void)showUpdateView;
|
||||
|
||||
// manually start an update check
|
||||
- (void)checkForUpdate;
|
||||
|
||||
// checks for update, informs the user (error, no update found, etc)
|
||||
- (void)checkForUpdateShowFeedback:(BOOL)feedback;
|
||||
|
||||
// initiates app-download call. displays an system UIAlertView
|
||||
- (BOOL)initiateAppDownload;
|
||||
|
||||
// checks wether this app version is authorized
|
||||
- (BOOL)appVersionIsAuthorized;
|
||||
|
||||
// start checking for an authorization key
|
||||
- (void)checkForAuthorization;
|
||||
|
||||
// convenience methode to create hockey view controller
|
||||
- (BWHockeyViewController *)hockeyViewController:(BOOL)modal;
|
||||
|
||||
// get/set current active hockey view controller
|
||||
@property (nonatomic, retain) BWHockeyViewController *currentHockeyViewController;
|
||||
|
||||
// convenience method to get current running version string
|
||||
- (NSString *)currentAppVersion;
|
||||
|
||||
// get newest app or array of all available versions
|
||||
- (BWApp *)app;
|
||||
|
||||
- (NSArray *)apps;
|
||||
|
||||
@end
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@protocol BWHockeyManagerDelegate <NSObject>
|
||||
@optional
|
||||
|
||||
// Invoked when the internet connection is started, to let the app enable the activity indicator
|
||||
- (void)connectionOpened;
|
||||
|
||||
// Invoked when the internet connection is closed, to let the app disable the activity indicator
|
||||
- (void)connectionClosed;
|
||||
|
||||
// optional parent view controller for the update screen when invoked via the alert view, default is the root UIWindow instance
|
||||
- (UIViewController *)viewControllerForHockeyController:(BWHockeyManager *)hockeyController;
|
||||
|
||||
@end
|
1152
Classes/BWHockeyManager.m
Normal file
22
Classes/BWHockeySettingsViewController.h
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// BWHockeySettingsViewController.h
|
||||
// HockeyDemo
|
||||
//
|
||||
// Created by Andreas Linde on 3/8/11.
|
||||
// Copyright 2011 Andreas Linde. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
@class BWHockeyManager;
|
||||
|
||||
|
||||
@interface BWHockeySettingsViewController : UIViewController <UITableViewDelegate, UITableViewDataSource> {
|
||||
BWHockeyManager *hockeyManager_;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) BWHockeyManager *hockeyManager;
|
||||
|
||||
- (id)init:(BWHockeyManager *)newHockeyManager;
|
||||
- (id)init;
|
||||
|
||||
@end
|
294
Classes/BWHockeySettingsViewController.m
Normal file
@ -0,0 +1,294 @@
|
||||
//
|
||||
// BWHockeySettingsViewController.m
|
||||
// HockeyDemo
|
||||
//
|
||||
// Created by Andreas Linde on 3/8/11.
|
||||
// Copyright 2011 Andreas Linde. All rights reserved.
|
||||
//
|
||||
|
||||
#import "BWHockeySettingsViewController.h"
|
||||
#import "BWHockeyManager.h"
|
||||
#import "BWGlobal.h"
|
||||
|
||||
#define BW_RGBCOLOR(r,g,b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
|
||||
|
||||
@implementation BWHockeySettingsViewController
|
||||
|
||||
@synthesize hockeyManager = hockeyManager_;
|
||||
|
||||
- (void)dismissSettings {
|
||||
[self.navigationController dismissModalViewControllerAnimated:YES];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#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;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
return [self init:[BWHockeyManager sharedHockeyManager]];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
- (void)sendUserData:(UISwitch *)switcher {
|
||||
[self.hockeyManager setUserAllowsSendUserData:switcher.on];
|
||||
}
|
||||
|
||||
- (void)sendUsageData:(UISwitch *)switcher {
|
||||
[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 (indexPath.section == (NSUInteger)[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 (indexPath.section == (NSUInteger)[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;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#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];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#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.
|
||||
}
|
||||
|
||||
- (void)viewDidUnload {
|
||||
// Relinquish ownership of anything that can be recreated in viewDidLoad or on demand.
|
||||
// For example: self.myOutlet = nil;
|
||||
}
|
||||
|
||||
|
||||
- (void)dealloc {
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark Rotation
|
||||
|
||||
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
|
||||
BOOL shouldAutorotate;
|
||||
|
||||
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
|
||||
shouldAutorotate = (interfaceOrientation == UIInterfaceOrientationPortrait);
|
||||
} else {
|
||||
shouldAutorotate = YES;
|
||||
}
|
||||
|
||||
return shouldAutorotate;
|
||||
}
|
||||
|
||||
@end
|
||||
|
65
Classes/BWHockeyViewController.h
Normal file
@ -0,0 +1,65 @@
|
||||
//
|
||||
// BWHockeyViewController.h
|
||||
//
|
||||
// Created by Andreas Linde on 8/17/10.
|
||||
// Copyright 2010-2011 Andreas Linde. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "PSStoreButton.h"
|
||||
#import "PSAppStoreHeader.h"
|
||||
|
||||
typedef enum {
|
||||
AppStoreButtonStateOffline,
|
||||
AppStoreButtonStateCheck,
|
||||
AppStoreButtonStateSearching,
|
||||
AppStoreButtonStateUpdate,
|
||||
AppStoreButtonStateInstalling
|
||||
} AppStoreButtonState;
|
||||
|
||||
|
||||
@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_;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) BWHockeyManager *hockeyManager;
|
||||
@property (nonatomic, readwrite) BOOL modal;
|
||||
|
||||
- (id)init:(BWHockeyManager *)newHockeyManager modal:(BOOL)newModal;
|
||||
- (id)init;
|
||||
|
||||
@end
|
626
Classes/BWHockeyViewController.m
Normal file
@ -0,0 +1,626 @@
|
||||
//
|
||||
// BWHockeyViewController.m
|
||||
//
|
||||
// Created by Andreas Linde on 8/17/10.
|
||||
// Copyright 2010-2011 Andreas Linde, Peter Steinberger. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import "NSString+HockeyAdditions.h"
|
||||
#import "BWHockeyViewController.h"
|
||||
#import "BWHockeyManager.h"
|
||||
#import "BWGlobal.h"
|
||||
#import "UIImage+HockeyAdditions.h"
|
||||
#import "PSWebTableViewCell.h"
|
||||
#import "BWHockeySettingsViewController.h"
|
||||
|
||||
#define BW_RGBCOLOR(r,g,b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
|
||||
#define kWebCellIdentifier @"PSWebTableViewCell"
|
||||
#define kAppStoreViewHeight 90
|
||||
|
||||
@interface BWHockeyViewController ()
|
||||
// updates the whole view
|
||||
- (void)showPreviousVersionAction;
|
||||
- (void)redrawTableView;
|
||||
@property (nonatomic, assign) AppStoreButtonState appStoreButtonState;
|
||||
- (void)setAppStoreButtonState:(AppStoreButtonState)anAppStoreButtonState animated:(BOOL)animated;
|
||||
@end
|
||||
|
||||
|
||||
@implementation BWHockeyViewController
|
||||
|
||||
@synthesize appStoreButtonState = appStoreButtonState_;
|
||||
@synthesize hockeyManager = hockeyManager_;
|
||||
@synthesize modal = modal_;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark private
|
||||
|
||||
- (void)restoreStoreButtonStateAnimated_:(BOOL)animated {
|
||||
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]];
|
||||
}
|
||||
if (app.size) {
|
||||
if ([subHeaderString length]) {
|
||||
[subHeaderString appendString:@" - "];
|
||||
}
|
||||
[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];
|
||||
}
|
||||
}
|
||||
|
||||
- (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];
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)changePreviousVersionButtonBackground:(id)sender {
|
||||
[(UIButton *)sender setBackgroundColor:BW_RGBCOLOR(183,183,183)];
|
||||
}
|
||||
|
||||
- (void)changePreviousVersionButtonBackgroundHighlighted:(id)sender {
|
||||
[(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);
|
||||
}
|
||||
}
|
||||
|
||||
- (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")];
|
||||
}
|
||||
} 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];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#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;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init {
|
||||
return [self init:[BWHockeyManager sharedHockeyManager] modal:NO];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self viewDidUnload];
|
||||
for (UITableViewCell *cell in cells_) {
|
||||
[cell removeObserver:self forKeyPath:@"webViewSize"];
|
||||
}
|
||||
[cells_ release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#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
|
||||
UIViewController *presentingViewController = nil;
|
||||
|
||||
BW_IF_IOS5_OR_GREATER(presentingViewController = self.navigationController.presentingViewController;);
|
||||
BW_IF_PRE_IOS5(presentingViewController = self.navigationController.parentViewController;)
|
||||
|
||||
if (presentingViewController) {
|
||||
[self.navigationController dismissModalViewControllerAnimated:YES];
|
||||
} else {
|
||||
[self.navigationController.view removeFromSuperview];
|
||||
}
|
||||
}
|
||||
else
|
||||
[self.navigationController popViewControllerAnimated:YES];
|
||||
|
||||
[[UIApplication sharedApplication] setStatusBarStyle:statusBarStyle_];
|
||||
}
|
||||
|
||||
- (CAGradientLayer *)backgroundLayer {
|
||||
UIColor *colorOne = [UIColor colorWithWhite:0.9 alpha:1.0];
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
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_];
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark Table view data source
|
||||
|
||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
|
||||
return 1;
|
||||
}
|
||||
|
||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
CGFloat rowHeight = 0;
|
||||
|
||||
if ([cells_ count] > 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;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark KVO
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
// only make changes if we are visible
|
||||
if(self.view.window) {
|
||||
if ([keyPath isEqualToString:@"webViewSize"]) {
|
||||
[self.tableView reloadData];
|
||||
[self realignPreviousVersionButton];
|
||||
} else if ([keyPath isEqualToString:@"checkInProgress"]) {
|
||||
if (self.hockeyManager.isCheckInProgress) {
|
||||
[self setAppStoreButtonState:AppStoreButtonStateSearching animated:YES];
|
||||
}else {
|
||||
[self restoreStoreButtonStateAnimated_:YES];
|
||||
}
|
||||
} else if ([keyPath isEqualToString:@"isUpdateURLOffline"]) {
|
||||
[self restoreStoreButtonStateAnimated_:YES];
|
||||
} else if ([keyPath isEqualToString:@"updateAvailable"]) {
|
||||
[self restoreStoreButtonStateAnimated_:YES];
|
||||
} else if ([keyPath isEqualToString:@"apps"]) {
|
||||
[self redrawTableView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Customize the appearance of table view cells.
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
if ([cells_ count] > indexPath.row) {
|
||||
return [cells_ objectAtIndex:indexPath.row];
|
||||
} else {
|
||||
BWHockeyLog(@"Warning: cells_ and indexPath do not match? forgot calling redrawTableView?");
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#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;
|
||||
}
|
||||
|
||||
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation duration:(NSTimeInterval)duration {
|
||||
// update all cells
|
||||
[cells_ makeObjectsPerformSelector:@selector(addWebView)];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark PSAppStoreHeaderDelegate
|
||||
|
||||
- (void)setAppStoreButtonState:(AppStoreButtonState)anAppStoreButtonState {
|
||||
[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;
|
||||
}
|
||||
}
|
||||
|
||||
- (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;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
221
Classes/BWQuincyManager.h
Executable file
@ -0,0 +1,221 @@
|
||||
/*
|
||||
* Author: Andreas Linde <mail@andreaslinde.de>
|
||||
* Kent Sutherland
|
||||
*
|
||||
* Copyright (c) 2011 Andreas Linde & Kent Sutherland.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <MessageUI/MessageUI.h>
|
||||
|
||||
#define kQuincyBundleName @"Quincy.bundle"
|
||||
|
||||
NSBundle *quincyBundle(void);
|
||||
NSString *BWQuincyLocalize(NSString *stringToken);
|
||||
|
||||
//#define BWQuincyLocalize(StringToken) NSLocalizedStringFromTableInBundle(StringToken, @"Quincy", quincyBundle(), @"")
|
||||
|
||||
// flags if the crashlog analyzer is started. since this may theoretically crash we need to track it
|
||||
#define kQuincyKitAnalyzerStarted @"QuincyKitAnalyzerStarted"
|
||||
|
||||
// flags if the QuincyKit is activated at all
|
||||
#define kQuincyKitActivated @"QuincyKitActivated"
|
||||
|
||||
// flags if the crashreporter should automatically send crashes without asking the user again
|
||||
#define kAutomaticallySendCrashReports @"AutomaticallySendCrashReports"
|
||||
|
||||
// stores the set of crashreports that have been approved but aren't sent yet
|
||||
#define kApprovedCrashReports @"ApprovedCrashReports"
|
||||
|
||||
// Notification message which QuincyManager is listening to, to retry sending pending crash reports to the server
|
||||
#define BWQuincyNetworkBecomeReachable @"NetworkDidBecomeReachable"
|
||||
|
||||
typedef enum QuincyKitAlertType {
|
||||
QuincyKitAlertTypeSend = 0,
|
||||
QuincyKitAlertTypeFeedback = 1,
|
||||
} CrashAlertType;
|
||||
|
||||
typedef enum CrashReportStatus {
|
||||
// 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
|
||||
CrashReportStatusFailureVersionDiscontinued = -30,
|
||||
|
||||
// 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
|
||||
CrashReportStatusFailureXMLVersionNotAllowed = -20,
|
||||
|
||||
// SQL for adding a symoblicate todo entry in the database failed
|
||||
CrashReportStatusFailureSQLAddSymbolicateTodo = -18,
|
||||
|
||||
// SQL for adding crash log in the database failed
|
||||
CrashReportStatusFailureSQLAddCrashlog = -17,
|
||||
|
||||
// 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 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 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 finding the bundle identifier in the database failed
|
||||
CrashReportStatusFailureSQLSearchAppName = -10,
|
||||
|
||||
// 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,
|
||||
|
||||
// database cannot be accessed, check hostname, username, password and database name settings in config.php
|
||||
CrashReportStatusFailureDatabaseNotAvailable = -1,
|
||||
|
||||
CrashReportStatusUnknown = 0,
|
||||
|
||||
CrashReportStatusAssigned = 1,
|
||||
|
||||
CrashReportStatusSubmitted = 2,
|
||||
|
||||
CrashReportStatusAvailable = 3,
|
||||
|
||||
CrashReportStatusDiscontinued = 4,
|
||||
|
||||
CrashReportStatusMoreInfo = 5,
|
||||
|
||||
} CrashReportStatus;
|
||||
|
||||
// This protocol is used to send the image updates
|
||||
@protocol BWQuincyManagerDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
|
||||
// Return the userid the crashreport should contain, empty by default
|
||||
-(NSString *) crashReportUserID;
|
||||
|
||||
// Return the contact value (e.g. email) the crashreport should contain, empty by default
|
||||
-(NSString *) crashReportContact;
|
||||
|
||||
// Return the description the crashreport should contain, empty by default. The string will automatically be wrapped into <[DATA[ ]]>, so make sure you don't do that in your string.
|
||||
-(NSString *) crashReportDescription;
|
||||
|
||||
// Invoked when the internet connection is started, to let the app enable the activity indicator
|
||||
-(void) connectionOpened;
|
||||
|
||||
// Invoked when the internet connection is closed, to let the app disable the activity indicator
|
||||
-(void) connectionClosed;
|
||||
|
||||
-(void) askForCrashInfo:(NSString*)messageBody;
|
||||
|
||||
// Invoked before the user is asked to send a crash report, so you can do additional actions. E.g. to make sure not to ask the user for an app rating :)
|
||||
-(void) willShowSubmitCrashReportAlert;
|
||||
|
||||
@end
|
||||
|
||||
@interface BWQuincyManager : NSObject <NSXMLParserDelegate> {
|
||||
NSString *_submissionURL;
|
||||
|
||||
id <BWQuincyManagerDelegate> _delegate;
|
||||
|
||||
BOOL _showAlwaysButton;
|
||||
BOOL _feedbackActivated;
|
||||
BOOL _autoSubmitCrashReport;
|
||||
BOOL _autoSubmitDeviceUDID;
|
||||
|
||||
NSString *_appIdentifier;
|
||||
|
||||
NSString *_feedbackRequestID;
|
||||
float _feedbackDelayInterval;
|
||||
|
||||
NSMutableString *_contentOfProperty;
|
||||
CrashReportStatus _serverResult;
|
||||
|
||||
int _analyzerStarted;
|
||||
NSString *_crashesDir;
|
||||
|
||||
BOOL _crashIdenticalCurrentVersion;
|
||||
BOOL _crashReportActivated;
|
||||
|
||||
NSMutableArray *_crashFiles;
|
||||
|
||||
NSMutableData *_responseData;
|
||||
NSInteger _statusCode;
|
||||
|
||||
NSURLConnection *_urlConnection;
|
||||
|
||||
NSData *_crashData;
|
||||
|
||||
NSString *_languageStyle;
|
||||
BOOL _sendingInProgress;
|
||||
}
|
||||
|
||||
+ (BWQuincyManager *)sharedQuincyManager;
|
||||
|
||||
// submission URL defines where to send the crash reports to (required)
|
||||
@property (nonatomic, retain) NSString *submissionURL;
|
||||
|
||||
// delegate is optional
|
||||
@property (nonatomic, assign) id <BWQuincyManagerDelegate> delegate;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// settings
|
||||
|
||||
// nil, using the default localization files (Default)
|
||||
// set to another string which will be appended to the Quincy localization file name, "Alternate" is another provided text set
|
||||
@property (nonatomic, retain) NSString *languageStyle;
|
||||
|
||||
// if YES, the user will get the option to choose "Always" for sending crash reports. This will cause the dialog not to show the alert description text landscape mode! (default)
|
||||
// if NO, the dialog will not show a "Always" button
|
||||
@property (nonatomic, assign, getter=isShowingAlwaysButton) BOOL showAlwaysButton;
|
||||
|
||||
// if YES, the user will be presented with a status of the crash, if known
|
||||
// if NO, the user will not see any feedback information (default)
|
||||
@property (nonatomic, assign, getter=isFeedbackActivated) BOOL feedbackActivated;
|
||||
|
||||
// if YES, the crash report will be submitted without asking the user
|
||||
// if NO, the user will be asked if the crash report can be submitted (default)
|
||||
@property (nonatomic, assign, getter=isAutoSubmitCrashReport) BOOL autoSubmitCrashReport;
|
||||
|
||||
// if YES, the device UDID will be submitted as the user id, without the need to define it in the crashReportUserID delegate (meant for beta versions!)
|
||||
// if NO, the crashReportUserID delegate defines what to be sent as user id (default)
|
||||
@property (nonatomic, assign, getter=isAutoSubmitDeviceUDID) BOOL autoSubmitDeviceUDID;
|
||||
|
||||
// If you want to use HockeyApp instead of your own server, this is required
|
||||
@property (nonatomic, retain) NSString *appIdentifier;
|
||||
|
||||
@end
|
772
Classes/BWQuincyManager.m
Executable file
@ -0,0 +1,772 @@
|
||||
/*
|
||||
* Author: Andreas Linde <mail@andreaslinde.de>
|
||||
* Kent Sutherland
|
||||
*
|
||||
* Copyright (c) 2011 Andreas Linde & Kent Sutherland.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <CrashReporter/CrashReporter.h>
|
||||
#import <SystemConfiguration/SystemConfiguration.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "BWQuincyManager.h"
|
||||
|
||||
#include <sys/sysctl.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
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(), @"");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@interface BWQuincyManager ()
|
||||
|
||||
- (void)startManager;
|
||||
|
||||
- (void)showCrashStatusMessage;
|
||||
|
||||
- (void)handleCrashReport;
|
||||
- (void)_cleanCrashReports;
|
||||
|
||||
- (void)_checkForFeedbackStatus;
|
||||
|
||||
- (void)_performSendingCrashReports;
|
||||
- (void)_sendCrashReports;
|
||||
|
||||
- (void)_postXML:(NSString*)xml toURL:(NSURL*)url;
|
||||
- (NSString *)_getDevicePlatform;
|
||||
|
||||
- (BOOL)hasNonApprovedCrashReports;
|
||||
- (BOOL)hasPendingCrashReport;
|
||||
|
||||
@end
|
||||
|
||||
@implementation BWQuincyManager
|
||||
|
||||
@synthesize delegate = _delegate;
|
||||
@synthesize submissionURL = _submissionURL;
|
||||
@synthesize showAlwaysButton = _showAlwaysButton;
|
||||
@synthesize feedbackActivated = _feedbackActivated;
|
||||
@synthesize autoSubmitCrashReport = _autoSubmitCrashReport;
|
||||
@synthesize autoSubmitDeviceUDID = _autoSubmitDeviceUDID;
|
||||
@synthesize languageStyle = _languageStyle;
|
||||
|
||||
@synthesize appIdentifier = _appIdentifier;
|
||||
|
||||
#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;
|
||||
}
|
||||
#else
|
||||
+ (BWQuincyManager *)sharedQuincyManager {
|
||||
static BWQuincyManager *quincyManager = nil;
|
||||
|
||||
if (quincyManager == nil) {
|
||||
quincyManager = [[BWQuincyManager alloc] init];
|
||||
}
|
||||
|
||||
return quincyManager;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (id) init {
|
||||
if ((self = [super init])) {
|
||||
_serverResult = CrashReportStatusUnknown;
|
||||
_crashIdenticalCurrentVersion = YES;
|
||||
_crashData = nil;
|
||||
_urlConnection = nil;
|
||||
_submissionURL = nil;
|
||||
_responseData = nil;
|
||||
_appIdentifier = nil;
|
||||
_sendingInProgress = NO;
|
||||
_languageStyle = nil;
|
||||
|
||||
self.delegate = nil;
|
||||
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];
|
||||
} else {
|
||||
_analyzerStarted = 0;
|
||||
}
|
||||
|
||||
testValue = nil;
|
||||
testValue = [[NSUserDefaults standardUserDefaults] stringForKey:kQuincyKitActivated];
|
||||
if (testValue) {
|
||||
_crashReportActivated = [[NSUserDefaults standardUserDefaults] boolForKey:kQuincyKitActivated];
|
||||
} else {
|
||||
_crashReportActivated = YES;
|
||||
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithBool:YES] forKey:kQuincyKitActivated];
|
||||
}
|
||||
|
||||
if ([[NSUserDefaults standardUserDefaults] stringForKey:kAutomaticallySendCrashReports]) {
|
||||
self.autoSubmitCrashReport = [[NSUserDefaults standardUserDefaults] boolForKey: kAutomaticallySendCrashReports];
|
||||
}
|
||||
|
||||
if (_crashReportActivated) {
|
||||
_crashFiles = [[NSMutableArray alloc] init];
|
||||
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
|
||||
_crashesDir = [[NSString stringWithFormat:@"%@", [[paths objectAtIndex:0] stringByAppendingPathComponent:@"/crashes/"]] retain];
|
||||
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
|
||||
if (![fm fileExistsAtPath:_crashesDir]) {
|
||||
NSDictionary *attributes = [NSDictionary dictionaryWithObject: [NSNumber numberWithUnsignedLong: 0755] forKey: NSFilePosixPermissions];
|
||||
NSError *theError = NULL;
|
||||
|
||||
[fm createDirectoryAtPath:_crashesDir withIntermediateDirectories: YES attributes: attributes error: &theError];
|
||||
}
|
||||
|
||||
PLCrashReporter *crashReporter = [PLCrashReporter sharedReporter];
|
||||
NSError *error = NULL;
|
||||
|
||||
// Check if we previously crashed
|
||||
if ([crashReporter hasPendingCrashReport]) {
|
||||
[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];
|
||||
}
|
||||
}
|
||||
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];
|
||||
|
||||
[_crashesDir release];
|
||||
[_crashFiles release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
|
||||
#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];
|
||||
}
|
||||
|
||||
- (void)setAppIdentifier:(NSString *)anAppIdentifier {
|
||||
if (_appIdentifier != anAppIdentifier) {
|
||||
[_appIdentifier release];
|
||||
_appIdentifier = [anAppIdentifier copy];
|
||||
}
|
||||
|
||||
[self setSubmissionURL:@"https://rink.hockeyapp.net/"];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark private methods
|
||||
|
||||
// begin the startup process
|
||||
- (void)startManager {
|
||||
if (!_sendingInProgress && [self hasPendingCrashReport]) {
|
||||
_sendingInProgress = YES;
|
||||
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;
|
||||
|
||||
for (NSUInteger i=0; i < [_crashFiles count]; i++) {
|
||||
NSString *filename = [_crashFiles objectAtIndex:i];
|
||||
|
||||
if (![approvedCrashReports objectForKey:filename]) return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)hasPendingCrashReport {
|
||||
if (_crashReportActivated) {
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
|
||||
if ([_crashFiles count] == 0 && [fm fileExistsAtPath:_crashesDir]) {
|
||||
NSString *file = nil;
|
||||
NSError *error = NULL;
|
||||
|
||||
NSDirectoryEnumerator *dirEnum = [fm enumeratorAtPath: _crashesDir];
|
||||
|
||||
while ((file = [dirEnum nextObject])) {
|
||||
NSDictionary *fileAttributes = [fm attributesOfItemAtPath:[_crashesDir stringByAppendingPathComponent:file] error:&error];
|
||||
if ([[fileAttributes objectForKey:NSFileSize] intValue] > 0) {
|
||||
[_crashFiles addObject:file];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ([_crashFiles count] > 0) {
|
||||
return YES;
|
||||
} else
|
||||
return NO;
|
||||
} else
|
||||
return NO;
|
||||
}
|
||||
|
||||
|
||||
- (void) showCrashStatusMessage {
|
||||
UIAlertView *alertView = nil;
|
||||
|
||||
if (_serverResult >= CrashReportStatusAssigned &&
|
||||
_crashIdenticalCurrentVersion) {
|
||||
// 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];
|
||||
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];
|
||||
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];
|
||||
break;
|
||||
case CrashReportStatusMoreInfo:
|
||||
if ([MFMailComposeViewController canSendMail]) {
|
||||
alertView = [[UIAlertView alloc] initWithTitle: [NSString stringWithFormat:BWQuincyLocalize(@"CrashResponseTitle"), appName ]
|
||||
message: [NSString stringWithFormat:BWQuincyLocalize(@"CrashResponseMoreInfo"), appName]
|
||||
delegate: self
|
||||
cancelButtonTitle: BWQuincyLocalize(@"Skip")
|
||||
otherButtonTitles: BWQuincyLocalize(@"Send Email"), nil];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
alertView = nil;
|
||||
break;
|
||||
}
|
||||
|
||||
if (alertView) {
|
||||
[alertView setTag: QuincyKitAlertTypeFeedback];
|
||||
[alertView show];
|
||||
[alertView release];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark UIAlertView Delegate
|
||||
|
||||
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
|
||||
|
||||
switch ([alertView tag]) {
|
||||
case QuincyKitAlertTypeSend:
|
||||
switch (buttonIndex) {
|
||||
case 0:
|
||||
_sendingInProgress = NO;
|
||||
[self _cleanCrashReports];
|
||||
break;
|
||||
case 1:
|
||||
[self _sendCrashReports];
|
||||
break;
|
||||
case 2:
|
||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:kAutomaticallySendCrashReports];
|
||||
|
||||
[self _sendCrashReports];
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case QuincyKitAlertTypeFeedback:
|
||||
switch (buttonIndex) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
[self.delegate askForCrashInfo:@"Please describe what you were doing when the crash occured:\n\n"];
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark NSXMLParser Delegate
|
||||
|
||||
#pragma mark NSXMLParser
|
||||
|
||||
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
|
||||
if (qName) {
|
||||
elementName = qName;
|
||||
}
|
||||
|
||||
if ([elementName isEqualToString:@"result"]) {
|
||||
_contentOfProperty = [NSMutableString string];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
|
||||
if (qName) {
|
||||
elementName = qName;
|
||||
}
|
||||
|
||||
// open source implementation
|
||||
if ([elementName isEqualToString: @"result"]) {
|
||||
if ([_contentOfProperty intValue] > _serverResult) {
|
||||
_serverResult = (CrashReportStatus)[_contentOfProperty intValue];
|
||||
}
|
||||
CrashReportStatus errorcode = (CrashReportStatus)[_contentOfProperty intValue];
|
||||
NSLog(@"CrashReporter ended in error code: %i", errorcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
|
||||
if (_contentOfProperty) {
|
||||
// If the current element is one whose content we care about, append 'string'
|
||||
// to the property that holds the content of the current element.
|
||||
if (string != nil) {
|
||||
[_contentOfProperty appendString:string];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Private
|
||||
|
||||
|
||||
- (NSString *)_getOSVersionBuild {
|
||||
size_t size = 0;
|
||||
NSString *osBuildVersion = nil;
|
||||
|
||||
sysctlbyname("kern.osversion", NULL, &size, NULL, 0);
|
||||
char *answer = (char*)malloc(size);
|
||||
int result = sysctlbyname("kern.osversion", answer, &size, NULL, 0);
|
||||
if (result >= 0) {
|
||||
osBuildVersion = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding];
|
||||
}
|
||||
|
||||
return osBuildVersion;
|
||||
}
|
||||
|
||||
- (NSString *)_getDevicePlatform {
|
||||
size_t size = 0;
|
||||
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
|
||||
char *answer = (char*)malloc(size);
|
||||
sysctlbyname("hw.machine", answer, &size, NULL, 0);
|
||||
NSString *platform = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding];
|
||||
free(answer);
|
||||
return platform;
|
||||
}
|
||||
|
||||
- (NSString *)deviceIdentifier {
|
||||
if ([[UIDevice currentDevice] respondsToSelector:@selector(uniqueIdentifier)]) {
|
||||
return [[UIDevice currentDevice] performSelector:@selector(uniqueIdentifier)];
|
||||
}
|
||||
else {
|
||||
return @"invalid";
|
||||
}
|
||||
}
|
||||
|
||||
- (void)_performSendingCrashReports {
|
||||
NSMutableDictionary *approvedCrashReports = [NSMutableDictionary dictionaryWithDictionary:[[NSUserDefaults standardUserDefaults] dictionaryForKey: kApprovedCrashReports]];
|
||||
|
||||
NSFileManager *fm = [NSFileManager defaultManager];
|
||||
NSError *error = NULL;
|
||||
|
||||
NSString *userid = @"";
|
||||
NSString *contact = @"";
|
||||
NSString *description = @"";
|
||||
|
||||
if (self.autoSubmitDeviceUDID) {
|
||||
userid = [self deviceIdentifier];
|
||||
} else if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashReportUserID)]) {
|
||||
userid = [self.delegate crashReportUserID] ?: @"";
|
||||
}
|
||||
|
||||
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashReportContact)]) {
|
||||
contact = [self.delegate crashReportContact] ?: @"";
|
||||
}
|
||||
|
||||
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(crashReportDescription)]) {
|
||||
description = [self.delegate crashReportDescription] ?: @"";
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
if ([crashData length] > 0) {
|
||||
PLCrashReport *report = [[[PLCrashReport alloc] initWithData:crashData error:&error] autorelease];
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
[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,
|
||||
userid,
|
||||
contact,
|
||||
description];
|
||||
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
|
||||
[[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];
|
||||
}
|
||||
|
||||
- (void)_sendCrashReports {
|
||||
// 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
|
||||
]
|
||||
]];
|
||||
|
||||
[request setCachePolicy: NSURLRequestReloadIgnoringLocalCacheData];
|
||||
[request setValue:@"Quincy/iOS" forHTTPHeaderField:@"User-Agent"];
|
||||
[request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
|
||||
[request setTimeoutInterval: 15];
|
||||
[request setHTTPMethod:@"GET"];
|
||||
|
||||
_serverResult = CrashReportStatusUnknown;
|
||||
_statusCode = 200;
|
||||
|
||||
// Release when done in the delegate method
|
||||
_responseData = [[NSMutableData alloc] init];
|
||||
|
||||
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(connectionOpened)]) {
|
||||
[self.delegate connectionOpened];
|
||||
}
|
||||
|
||||
_urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
|
||||
}
|
||||
|
||||
- (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];
|
||||
}
|
||||
|
||||
[request setCachePolicy: NSURLRequestReloadIgnoringLocalCacheData];
|
||||
[request setValue:@"Quincy/iOS" forHTTPHeaderField:@"User-Agent"];
|
||||
[request setValue:@"gzip" forHTTPHeaderField:@"Accept-Encoding"];
|
||||
[request setTimeoutInterval: 15];
|
||||
[request setHTTPMethod:@"POST"];
|
||||
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
|
||||
[request setValue:contentType forHTTPHeaderField:@"Content-type"];
|
||||
|
||||
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]];
|
||||
}
|
||||
[postBody appendData:[xml dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
[postBody appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
[request setHTTPBody:postBody];
|
||||
|
||||
_serverResult = CrashReportStatusUnknown;
|
||||
_statusCode = 200;
|
||||
|
||||
//Release when done in the delegate method
|
||||
_responseData = [[NSMutableData alloc] init];
|
||||
|
||||
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(connectionOpened)]) {
|
||||
[self.delegate connectionOpened];
|
||||
}
|
||||
|
||||
_urlConnection = [[NSURLConnection alloc] initWithRequest:request delegate:self];
|
||||
|
||||
if (!_urlConnection) {
|
||||
_sendingInProgress = NO;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark NSURLConnection Delegate
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
|
||||
if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
_statusCode = [(NSHTTPURLResponse *)response statusCode];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
|
||||
[_responseData appendData:data];
|
||||
}
|
||||
|
||||
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
|
||||
[_responseData release];
|
||||
_responseData = nil;
|
||||
_urlConnection = nil;
|
||||
|
||||
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(connectionClosed)]) {
|
||||
[self.delegate connectionClosed];
|
||||
}
|
||||
|
||||
_sendingInProgress = NO;
|
||||
}
|
||||
|
||||
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
|
||||
if (_statusCode >= 200 && _statusCode < 400) {
|
||||
[self _cleanCrashReports];
|
||||
|
||||
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];
|
||||
_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]) {
|
||||
if (self.appIdentifier) {
|
||||
// only proceed if the server did not report any problem
|
||||
if (_serverResult == CrashReportStatusQueued) {
|
||||
// the report is still in the queue
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(_checkForFeedbackStatus) object:nil];
|
||||
[self performSelector:@selector(_checkForFeedbackStatus) withObject:nil afterDelay:_feedbackDelayInterval];
|
||||
} else {
|
||||
// we do have a status, show it if needed
|
||||
[self showCrashStatusMessage];
|
||||
}
|
||||
} else {
|
||||
[self showCrashStatusMessage];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[_responseData release];
|
||||
_responseData = nil;
|
||||
_urlConnection = nil;
|
||||
|
||||
if (self.delegate != nil && [self.delegate respondsToSelector:@selector(connectionClosed)]) {
|
||||
[self.delegate connectionClosed];
|
||||
}
|
||||
|
||||
_sendingInProgress = NO;
|
||||
}
|
||||
|
||||
#pragma mark PLCrashReporter
|
||||
|
||||
//
|
||||
// Called to handle a pending crash report.
|
||||
//
|
||||
- (void) handleCrashReport {
|
||||
PLCrashReporter *crashReporter = [PLCrashReporter sharedReporter];
|
||||
NSError *error = NULL;
|
||||
|
||||
// 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];
|
||||
}
|
||||
}
|
||||
|
||||
// Purge the report
|
||||
// mark the end of the routine
|
||||
_analyzerStarted = 0;
|
||||
[[NSUserDefaults standardUserDefaults] setValue:[NSNumber numberWithInt:_analyzerStarted] forKey:kQuincyKitAnalyzerStarted];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
|
||||
[crashReporter purgePendingCrashReport];
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@end
|
29
Classes/CNSHockeyManager.h
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2011 Codenauts UG (haftungsbeschränkt). All rights reserved.
|
||||
// See LICENSE.txt for author information.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
|
||||
@interface CNSHockeyManager : NSObject
|
||||
|
||||
+ (CNSHockeyManager *)sharedHockeyManager;
|
||||
|
||||
- (void)configureWithIdentifier:(NSString *)appIdentifier delegate:(id)delegate;
|
||||
|
||||
@end
|
111
Classes/CNSHockeyManager.m
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2011 Codenauts UG (haftungsbeschränkt). All rights reserved.
|
||||
// See LICENSE.txt for author information.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "CNSHockeyManager.h"
|
||||
#import "BWQuincyManager.h"
|
||||
#import "BWHockeyManager.h"
|
||||
|
||||
#ifdef JMC_PRESENT
|
||||
#import "JMC.h"
|
||||
#endif
|
||||
|
||||
@interface CNSHockeyManager ()
|
||||
|
||||
#ifdef JMC_PRESENT
|
||||
- (void)configureJMC;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
@implementation CNSHockeyManager
|
||||
|
||||
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 40000
|
||||
+ (CNSHockeyManager *)sharedHockeyManager {
|
||||
static CNSHockeyManager *sharedInstance = nil;
|
||||
static dispatch_once_t pred;
|
||||
|
||||
if (sharedInstance) {
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
dispatch_once(&pred, ^{
|
||||
sharedInstance = [CNSHockeyManager alloc];
|
||||
sharedInstance = [sharedInstance init];
|
||||
});
|
||||
|
||||
return sharedInstance;
|
||||
}
|
||||
#else
|
||||
+ (CNSHockeyManager *)sharedHockeyManager {
|
||||
static CNSHockeyManager *hockeyManager = nil;
|
||||
|
||||
if (hockeyManager == nil) {
|
||||
hockeyManager = [[CNSHockeyManager alloc] init];
|
||||
}
|
||||
|
||||
return hockeyManager;
|
||||
}
|
||||
#endif
|
||||
|
||||
- (void)configureWithIdentifier:(NSString *)appIdentifier delegate:(id)delegate {
|
||||
// Crash Reporting
|
||||
[[BWQuincyManager sharedQuincyManager] setAppIdentifier:appIdentifier];
|
||||
|
||||
// Distribution
|
||||
[[BWHockeyManager sharedHockeyManager] setAppIdentifier:appIdentifier];
|
||||
[[BWHockeyManager sharedHockeyManager] setUpdateURL:@"http://192.168.178.53:3000"];
|
||||
[[BWHockeyManager sharedHockeyManager] setCheckForTracker:YES];
|
||||
|
||||
#ifdef JMC_PRESENT
|
||||
// JMC
|
||||
[[[JMC instance] options] setCrashReportingEnabled:NO];
|
||||
[[BWHockeyManager sharedHockeyManager] addObserver:self forKeyPath:@"trackerConfig" options:0 context:nil];
|
||||
[self performSelector:@selector(configureJMC) withObject:nil afterDelay:0];
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef JMC_PRESENT
|
||||
- (void)configureJMC {
|
||||
// Return if JMC is already configured
|
||||
if ([[JMC instance] url]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Configure JMC from user defaults
|
||||
NSDictionary *config = [[NSUserDefaults standardUserDefaults] valueForKey:@"CNSTrackerConfig"];
|
||||
if (([[config valueForKey:@"enabled"] boolValue]) &&
|
||||
([[config valueForKey:@"url"] length] > 0) &&
|
||||
([[config valueForKey:@"key"] length] > 0) &&
|
||||
([[config valueForKey:@"project"] length] > 0)) {
|
||||
[[JMC instance] configureJiraConnect:[config valueForKey:@"url"] projectKey:[config valueForKey:@"project"] apiKey:[config valueForKey:@"key"]];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
|
||||
if ([object trackerConfig]) {
|
||||
[[NSUserDefaults standardUserDefaults] setValue:[object trackerConfig] forKey:@"CNSTrackerConfig"];
|
||||
[[NSUserDefaults standardUserDefaults] synchronize];
|
||||
[self configureJMC];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@end
|
33
Classes/NSString+HockeyAdditions.h
Normal file
@ -0,0 +1,33 @@
|
||||
//
|
||||
// NSString+HockeyAdditions.h
|
||||
//
|
||||
// Created by Jon Crosby on 10/19/07.
|
||||
// Copyright 2007 Kaboomerang LLC. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSString (HockeyAdditions)
|
||||
|
||||
- (NSString *)bw_URLEncodedString;
|
||||
- (NSString *)bw_URLDecodedString;
|
||||
|
||||
@end
|
50
Classes/NSString+HockeyAdditions.m
Normal file
@ -0,0 +1,50 @@
|
||||
//
|
||||
// NSString+HockeyAdditions.m
|
||||
//
|
||||
// Created by Jon Crosby on 10/19/07.
|
||||
// Copyright 2007 Kaboomerang LLC. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
|
||||
#import "NSString+HockeyAdditions.h"
|
||||
|
||||
|
||||
@implementation NSString (HockeyAdditions)
|
||||
|
||||
- (NSString *)bw_URLEncodedString {
|
||||
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;
|
||||
}
|
||||
|
||||
@end
|
42
Classes/PSAppStoreHeader.h
Normal file
@ -0,0 +1,42 @@
|
||||
//
|
||||
// PSAppStoreHeader.h
|
||||
// HockeyDemo
|
||||
//
|
||||
// Created by Peter Steinberger on 09.01.11.
|
||||
// Copyright 2011 Peter Steinberger. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface PSAppStoreHeader : UIView {
|
||||
NSString *headerLabel_;
|
||||
NSString *middleHeaderLabel_;
|
||||
NSString *subHeaderLabel;
|
||||
UIImage *iconImage_;
|
||||
|
||||
UIImage *reflectedImage_;
|
||||
}
|
||||
|
||||
@property (nonatomic, copy) NSString *headerLabel;
|
||||
@property (nonatomic, copy) NSString *middleHeaderLabel;
|
||||
@property (nonatomic, copy) NSString *subHeaderLabel;
|
||||
@property (nonatomic, retain) UIImage *iconImage;
|
||||
|
||||
@end
|
173
Classes/PSAppStoreHeader.m
Normal file
@ -0,0 +1,173 @@
|
||||
//
|
||||
// PSAppStoreHeader.m
|
||||
// HockeyDemo
|
||||
//
|
||||
// Created by Peter Steinberger on 09.01.11.
|
||||
// Copyright 2011 Peter Steinberger. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "PSAppStoreHeader.h"
|
||||
#import "UIImage+HockeyAdditions.h"
|
||||
#import "BWGlobal.h"
|
||||
|
||||
#define BW_RGBCOLOR(r,g,b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
|
||||
|
||||
#define kLightGrayColor BW_RGBCOLOR(200, 202, 204)
|
||||
#define kDarkGrayColor BW_RGBCOLOR(140, 141, 142)
|
||||
|
||||
#define kImageHeight 57
|
||||
#define kReflectionHeight 20
|
||||
#define kImageBorderRadius 10
|
||||
#define kImageMargin 8
|
||||
#define kTextRow kImageMargin*2 + kImageHeight
|
||||
|
||||
@implementation PSAppStoreHeader
|
||||
|
||||
@synthesize headerLabel = headerLabel_;
|
||||
@synthesize middleHeaderLabel = middleHeaderLabel_;
|
||||
@synthesize subHeaderLabel;
|
||||
@synthesize iconImage = iconImage_;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark NSObject
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame {
|
||||
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];
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#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);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark Properties
|
||||
|
||||
- (void)setHeaderLabel:(NSString *)anHeaderLabel {
|
||||
if (headerLabel_ != anHeaderLabel) {
|
||||
[headerLabel_ release];
|
||||
headerLabel_ = [anHeaderLabel copy];
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setMiddleHeaderLabel:(NSString *)aMiddleHeaderLabel {
|
||||
if (middleHeaderLabel_ != aMiddleHeaderLabel) {
|
||||
[middleHeaderLabel_ release];
|
||||
middleHeaderLabel_ = [aMiddleHeaderLabel copy];
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)setSubHeaderLabel:(NSString *)aSubHeaderLabel {
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
81
Classes/PSStoreButton.h
Normal file
@ -0,0 +1,81 @@
|
||||
//
|
||||
// PSStoreButton.h
|
||||
// HockeyDemo
|
||||
//
|
||||
// Created by Peter Steinberger on 09.01.11.
|
||||
// Copyright 2011 Peter Steinberger. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
// defines a button action set (data container)
|
||||
@interface PSStoreButtonData : NSObject {
|
||||
CGPoint customPadding_;
|
||||
NSString *label_;
|
||||
NSArray *colors_;
|
||||
BOOL enabled_;
|
||||
}
|
||||
|
||||
+ (id)dataWithLabel:(NSString*)aLabel colors:(NSArray*)aColors enabled:(BOOL)flag;
|
||||
|
||||
@property (nonatomic, copy) NSString *label;
|
||||
@property (nonatomic, retain) NSArray *colors;
|
||||
@property (nonatomic, assign, getter=isEnabled) BOOL enabled;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@class PSStoreButton;
|
||||
@protocol PSStoreButtonDelegate
|
||||
- (void)storeButtonFired:(PSStoreButton *)button;
|
||||
@end
|
||||
|
||||
|
||||
// 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_;
|
||||
}
|
||||
|
||||
- (id)initWithFrame:(CGRect)frame;
|
||||
- (id)initWithPadding:(CGPoint)padding;
|
||||
|
||||
// action delegate
|
||||
@property (nonatomic, assign) id<PSStoreButtonDelegate> buttonDelegate;
|
||||
|
||||
// change the button layer
|
||||
@property (nonatomic, retain) PSStoreButtonData *buttonData;
|
||||
- (void)setButtonData:(PSStoreButtonData *)aButtonData animated:(BOOL)animated;
|
||||
|
||||
// align helper
|
||||
@property (nonatomic, assign) CGPoint customPadding;
|
||||
- (void)alignToSuperview;
|
||||
|
||||
// helpers to mimic an AppStore button
|
||||
+ (NSArray *)appStoreGreenColor;
|
||||
+ (NSArray *)appStoreBlueColor;
|
||||
+ (NSArray *)appStoreGrayColor;
|
||||
|
||||
@end
|
295
Classes/PSStoreButton.m
Normal file
@ -0,0 +1,295 @@
|
||||
//
|
||||
// PSStoreButton.m
|
||||
// HockeyDemo
|
||||
//
|
||||
// Created by Peter Steinberger on 09.01.11.
|
||||
// Copyright 2011 Peter Steinberger. All rights reserved.
|
||||
//
|
||||
// This code was inspired by https://github.com/dhmspector/ZIStoreButton
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "PSStoreButton.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#define PSLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
|
||||
#else
|
||||
#define PSLog(...)
|
||||
#endif
|
||||
|
||||
#define PS_RGBCOLOR(r,g,b) [UIColor colorWithRed:(r)/255.0 green:(g)/255.0 blue:(b)/255.0 alpha:1]
|
||||
#define PS_MIN_HEIGHT 25.0f
|
||||
#define PS_MAX_WIDTH 120.0f
|
||||
#define PS_PADDING 12.0f
|
||||
#define kDefaultButtonAnimationTime 0.25f
|
||||
|
||||
@implementation PSStoreButtonData
|
||||
|
||||
@synthesize label = label_;
|
||||
@synthesize colors = colors_;
|
||||
@synthesize enabled = enabled_;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#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;
|
||||
}
|
||||
|
||||
+ (id)dataWithLabel:(NSString*)aLabel colors:(NSArray*)aColors enabled:(BOOL)flag {
|
||||
return [[[[self class] alloc] initWithLabel:aLabel colors:aColors enabled:flag] autorelease];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[label_ release];
|
||||
[colors_ release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
@end
|
||||
|
||||
|
||||
@interface PSStoreButton ()
|
||||
// call when buttonData was updated
|
||||
- (void)updateButtonAnimated:(BOOL)animated;
|
||||
@end
|
||||
|
||||
|
||||
@implementation PSStoreButton
|
||||
|
||||
@synthesize buttonData = buttonData_;
|
||||
@synthesize buttonDelegate = buttonDelegate_;
|
||||
@synthesize customPadding = customPadding_;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark private
|
||||
|
||||
- (void)touchedUpOutside:(id)sender {
|
||||
PSLog(@"touched outside...");
|
||||
}
|
||||
|
||||
- (void)buttonPressed:(id)sender {
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
- (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) {
|
||||
[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];
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark NSObject
|
||||
|
||||
- (id)initWithFrame:(CGRect)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];
|
||||
[self addTarget:self action:@selector(buttonPressed:) forControlEvents:UIControlEventTouchUpInside];
|
||||
|
||||
// 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];
|
||||
|
||||
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],
|
||||
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;
|
||||
}
|
||||
|
||||
- (id)initWithPadding:(CGPoint)padding {
|
||||
if ((self = [self initWithFrame:CGRectMake(0, 0, 40, PS_MIN_HEIGHT)])) {
|
||||
customPadding_ = padding;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[buttonData_ release];
|
||||
[gradient_ release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark UIView
|
||||
|
||||
- (CGSize)sizeThatFits:(CGSize)size {
|
||||
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;
|
||||
}
|
||||
|
||||
- (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];
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark Properties
|
||||
|
||||
- (void)setButtonData:(PSStoreButtonData *)aButtonData {
|
||||
[self setButtonData:aButtonData animated:NO];
|
||||
}
|
||||
|
||||
- (void)setButtonData:(PSStoreButtonData *)aButtonData animated:(BOOL)animated {
|
||||
if (buttonData_ != aButtonData) {
|
||||
[buttonData_ release];
|
||||
buttonData_ = [aButtonData retain];
|
||||
}
|
||||
|
||||
[self updateButtonAnimated:animated];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#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];
|
||||
}
|
||||
|
||||
+ (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];
|
||||
}
|
||||
|
||||
+ (NSArray *)appStoreGrayColor {
|
||||
return [NSArray arrayWithObjects:(id)
|
||||
PS_RGBCOLOR(187,189,191).CGColor,
|
||||
PS_RGBCOLOR(210,210,210).CGColor, nil];
|
||||
}
|
||||
|
||||
@end
|
44
Classes/PSWebTableViewCell.h
Normal file
@ -0,0 +1,44 @@
|
||||
//
|
||||
// PSWebTableViewCell.h
|
||||
// HockeyDemo
|
||||
//
|
||||
// Created by Peter Steinberger on 04.02.11.
|
||||
// Copyright 2011 Peter Steinberger. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface PSWebTableViewCell : UITableViewCell <UIWebViewDelegate> {
|
||||
UIWebView *webView_;
|
||||
NSString *webViewContent_;
|
||||
CGSize webViewSize_;
|
||||
|
||||
UIColor *cellBackgroundColor_;
|
||||
}
|
||||
|
||||
@property (nonatomic, retain) UIWebView *webView;
|
||||
@property (nonatomic, copy) NSString *webViewContent;
|
||||
@property (nonatomic, assign) CGSize webViewSize;
|
||||
@property (nonatomic, retain) UIColor *cellBackgroundColor;
|
||||
|
||||
- (void)addWebView;
|
||||
|
||||
@end
|
189
Classes/PSWebTableViewCell.m
Normal file
@ -0,0 +1,189 @@
|
||||
//
|
||||
// PSWebTableViewCell.m
|
||||
// HockeyDemo
|
||||
//
|
||||
// Created by Peter Steinberger on 04.02.11.
|
||||
// Copyright 2011 Peter Steinberger. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "PSWebTableViewCell.h"
|
||||
#import "BWGlobal.h"
|
||||
|
||||
@implementation PSWebTableViewCell
|
||||
|
||||
static NSString* PSWebTableViewCellHtmlTemplate = @"\
|
||||
<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\
|
||||
<html xmlns=\"http://www.w3.org/1999/xhtml\">\
|
||||
<head>\
|
||||
<style type=\"text/css\">\
|
||||
body { font: 13px 'Helvetica Neue', Helvetica; word-wrap:break-word; padding:8px;} p {margin:0;} ul {padding-left: 18px;}\
|
||||
</style>\
|
||||
<meta name=\"viewport\" content=\"user-scalable=no width=%@\" /></head>\
|
||||
<body>\
|
||||
%@\
|
||||
</body>\
|
||||
</html>\
|
||||
";
|
||||
|
||||
@synthesize webView = webView_;
|
||||
@synthesize webViewContent = webViewContent_;
|
||||
@synthesize webViewSize = webViewSize_;
|
||||
@synthesize cellBackgroundColor = cellBackgroundColor_;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark private
|
||||
|
||||
- (void)addWebView {
|
||||
if(webViewContent_) {
|
||||
CGRect webViewRect = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height);
|
||||
if(!webView_) {
|
||||
webView_ = [[[UIWebView alloc] initWithFrame:webViewRect] retain];
|
||||
[self addSubview:webView_];
|
||||
webView_.hidden = YES;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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];
|
||||
[webView_ loadHTMLString:contentHtml baseURL:nil];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)showWebView {
|
||||
webView_.hidden = NO;
|
||||
self.textLabel.text = @"";
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
|
||||
- (void)removeWebView {
|
||||
if(webView_) {
|
||||
webView_.delegate = nil;
|
||||
[webView_ resignFirstResponder];
|
||||
[webView_ removeFromSuperview];
|
||||
[webView_ release];
|
||||
}
|
||||
webView_ = nil;
|
||||
[self setNeedsDisplay];
|
||||
}
|
||||
|
||||
|
||||
- (void)setWebViewContent:(NSString *)aWebViewContent {
|
||||
if (webViewContent_ != aWebViewContent) {
|
||||
[webViewContent_ release];
|
||||
webViewContent_ = [aWebViewContent retain];
|
||||
|
||||
// add basic accessiblity (prevents "snarfed from ivar layout") logs
|
||||
self.accessibilityLabel = aWebViewContent;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark NSObject
|
||||
|
||||
- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
|
||||
if((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) {
|
||||
self.cellBackgroundColor = [UIColor clearColor];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self removeWebView];
|
||||
[webViewContent_ release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark UIView
|
||||
|
||||
- (void)setFrame:(CGRect)aFrame {
|
||||
BOOL needChange = !CGRectEqualToRect(aFrame, self.frame);
|
||||
[super setFrame:aFrame];
|
||||
|
||||
if (needChange) {
|
||||
[self addWebView];
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark UITableViewCell
|
||||
|
||||
- (void)prepareForReuse {
|
||||
[self removeWebView];
|
||||
self.webViewContent = nil;
|
||||
[super prepareForReuse];
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark -
|
||||
#pragma mark UIWebView
|
||||
|
||||
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
|
||||
if(navigationType == UIWebViewNavigationTypeOther)
|
||||
return YES;
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
@end
|
38
Classes/UIImage+HockeyAdditions.h
Normal file
@ -0,0 +1,38 @@
|
||||
//
|
||||
// UIImage+HockeyAdditions.h
|
||||
// HockeyDemo
|
||||
//
|
||||
// Created by Peter Steinberger on 10.01.11.
|
||||
// Copyright 2011 Peter Steinberger. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface UIImage (HockeyAdditions)
|
||||
|
||||
- (UIImage *)bw_roundedCornerImage:(NSInteger)cornerSize borderSize:(NSInteger)borderSize;
|
||||
- (UIImage *)bw_imageToFitSize:(CGSize)fitSize honorScaleFactor:(BOOL)honorScaleFactor;
|
||||
- (UIImage *)bw_reflectedImageWithHeight:(NSUInteger)height fromAlpha:(float)fromAlpha toAlpha:(float)toAlpha;
|
||||
|
||||
- (id)bw_initWithContentsOfResolutionIndependentFile:(NSString *)path NS_RETURNS_RETAINED;
|
||||
+ (UIImage*)bw_imageWithContentsOfResolutionIndependentFile:(NSString *)path;
|
||||
+ (UIImage *)bw_imageNamed:(NSString *)imageName bundle:(NSString *)bundleName;
|
||||
|
||||
@end
|
346
Classes/UIImage+HockeyAdditions.m
Normal file
@ -0,0 +1,346 @@
|
||||
//
|
||||
// UIImage+HockeyAdditions.m
|
||||
// HockeyDemo
|
||||
//
|
||||
// Created by Peter Steinberger on 10.01.11.
|
||||
// Copyright 2011 Peter Steinberger. All rights reserved.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
|
||||
#import "UIImage+HockeyAdditions.h"
|
||||
#import "BWGlobal.h"
|
||||
|
||||
// Private helper methods
|
||||
@interface UIImage (HockeyAdditionsPrivate)
|
||||
- (void)addRoundedRectToPath:(CGRect)rect context:(CGContextRef)context ovalWidth:(CGFloat)ovalWidth ovalHeight:(CGFloat)ovalHeight;
|
||||
|
||||
CGContextRef MyOpenBitmapContext(int pixelsWide, int pixelsHigh);
|
||||
CGImageRef CreateGradientImage(int pixelsWide, int pixelsHigh, float fromAlpha, float toAlpha);
|
||||
@end
|
||||
|
||||
@implementation UIImage (HockeyAdditions)
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
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();
|
||||
)
|
||||
#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;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Private helper methods
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
- (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];
|
||||
}
|
||||
}
|
||||
#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.
|
||||
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();
|
||||
)
|
||||
if (!image) {
|
||||
// Try older method.
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGContextRef context = CGBitmapContextCreate(NULL, scaledWidth, scaledHeight, 8, (fitSize.width * 4),
|
||||
colorSpace, kCGImageAlphaPremultipliedLast);
|
||||
CGImageRef sourceImg = CGImageCreateWithImageInRect([self CGImage], sourceRect);
|
||||
CGContextDrawImage(context, destRect, sourceImg);
|
||||
CGImageRelease(sourceImg);
|
||||
CGImageRef finalImage = CGBitmapContextCreateImage(context);
|
||||
CGContextRelease(context);
|
||||
CGColorSpaceRelease(colorSpace);
|
||||
image = [UIImage imageWithCGImage:finalImage];
|
||||
CGImageRelease(finalImage);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// create the bitmap context
|
||||
CGContextRef gradientBitmapContext = CGBitmapContextCreate(NULL, pixelsWide, pixelsHigh,
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
CGContextRef MyOpenBitmapContext(int pixelsWide, int pixelsHigh) {
|
||||
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)
|
||||
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();
|
||||
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+ (UIImage *)bw_imageNamed:(NSString *)imageName bundle:(NSString *)bundleName {
|
||||
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
|
||||
NSString *bundlePath = [resourcePath stringByAppendingPathComponent:bundleName];
|
||||
NSString *imagePath = [bundlePath stringByAppendingPathComponent:imageName];
|
||||
return [UIImage bw_imageWithContentsOfResolutionIndependentFile:imagePath];
|
||||
}
|
||||
|
||||
@end
|
81
LICENSE.txt
Executable file
@ -0,0 +1,81 @@
|
||||
## Authors
|
||||
|
||||
Andreas Linde <andy@buzzworks.de>
|
||||
Stanley Rost <soryu2@gmail.com>
|
||||
Fabian Kreiser <fabian@fabian-kreiser.com>
|
||||
Tobias Höhmann
|
||||
FutureTap
|
||||
Kent Sutherland
|
||||
Peter Steinberger <me@petersteinberger.com>
|
||||
Thomas Dohmke <thomas@dohmke.de>
|
||||
|
||||
## Licenses
|
||||
|
||||
The Hockey SDK is provided under the following license:
|
||||
|
||||
The MIT License
|
||||
Copyright (c) 2011 Codenauts UG (haftungsbeschränkt). All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
Except as noted below, PLCrashReporter
|
||||
is provided under the following license:
|
||||
|
||||
Copyright (c) 2008 - 2009 Plausible Labs Cooperative, Inc.
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
The protobuf-c library, as well as the PLCrashLogWriterEncoding.c
|
||||
file are licensed as follows:
|
||||
|
||||
Copyright 2008, Dave Benson.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with
|
||||
the License. You may obtain a copy of the License
|
||||
at http://www.apache.org/licenses/LICENSE-2.0 Unless
|
||||
required by applicable law or agreed to in writing,
|
||||
software distributed under the License is distributed on
|
||||
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
KIND, either express or implied. See the License for the
|
||||
specific language governing permissions and limitations
|
||||
under the License.
|
BIN
Resources/.DS_Store
vendored
Normal file
BIN
Resources/Hockey.bundle/IconGradient.png
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
Resources/Hockey.bundle/IconGradient@2x.png
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
Resources/Hockey.bundle/authorize_denied.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
Resources/Hockey.bundle/authorize_denied@2x.png
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
Resources/Hockey.bundle/authorize_request.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
Resources/Hockey.bundle/authorize_request@2x.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
Resources/Hockey.bundle/bg.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
Resources/Hockey.bundle/buttonHighlight.png
Normal file
After Width: | Height: | Size: 1021 B |
BIN
Resources/Hockey.bundle/buttonHighlight@2x.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
86
Resources/Hockey.bundle/de.lproj/Hockey.strings
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
Hockey.strings
|
||||
Hockey
|
||||
|
||||
Created by Andreas Linde on 11/15/10.
|
||||
Copyright 2010 buzzworks.de. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
/* Alert view */
|
||||
|
||||
/* For dialogs yes buttons */
|
||||
"HockeyYes" = "Ja";
|
||||
|
||||
/* For dialogs no buttons */
|
||||
"HockeyNo" = "Nein";
|
||||
|
||||
/* Update available */
|
||||
"HockeyUpdateAvailable" = "Aktualisierung verfügbar";
|
||||
|
||||
/* Would you like to check out the new update? You can do this later on at any time in the In-App settings. */
|
||||
"HockeyUpdateAlertText" = "Möchten Sie sich weitere Informationen zu der Aktualisierung ansehen?";
|
||||
|
||||
|
||||
/* Update details screen */
|
||||
|
||||
/* Update Details */
|
||||
"HockeyUpdateScreenTitle" = "Aktualisierung";
|
||||
|
||||
|
||||
/* Settings */
|
||||
|
||||
/* Screen title for settings view */
|
||||
"HockeySettingsTitle" = "Einstellungen";
|
||||
|
||||
/* Text asking the user to send user data (on/off switch) */
|
||||
"HockeySettingsUserData" = "Daten senden";
|
||||
|
||||
/* Description text for turning on/off sending user data */
|
||||
"HockeySettingsUserDataDescription" = "Daten senden liefert dem Entwickler die folgenden Daten: Programmversion, Sprache, Gerätetyp und iOS Version.";
|
||||
|
||||
/* Text asking the user to send usage data (on/off switch) */
|
||||
"HockeySettingsUsageData" = "Testzeit senden";
|
||||
|
||||
/* Description text for turning on/off sending usage data */
|
||||
"HockeySettingsUsageDataDescription" = "Testzeit senden informiert den Entwickler über die Summe der Testzeit, in einer Genauigkeit von 1 Minute.";
|
||||
|
||||
/* Title for defining when update checks may be made */
|
||||
"HockeySectionCheckTitle" = "Nach Aktualisierung Prüfen";
|
||||
|
||||
/* On Startup */
|
||||
"HockeySectionCheckStartup" = "Beim Start";
|
||||
|
||||
/* Daily */
|
||||
"HockeySectionCheckDaily" = "Täglich";
|
||||
|
||||
/* Manually */
|
||||
"HockeySectionCheckManually" = "Manuell";
|
||||
|
||||
|
||||
"HockeyVersion" = "Version";
|
||||
"HockeyShowPreviousVersions" = "Zeige frühere Versionen...";
|
||||
"HockeyNoUpdateNeededTitle" = "Kein Update Verfügbar";
|
||||
"HockeyNoUpdateNeededMessage" = "%@ ist bereits die aktuellste Version.";
|
||||
"HockeyUpdateAlertTextWithAppVersion" = "%@ ist verfügbar.";
|
||||
"HockeyIgnore" = "Ignorieren";
|
||||
"HockeyShowUpdate" = "Anzeigen";
|
||||
"HockeyInstallUpdate" = "Installieren";
|
||||
"HockeyError" = "Fehler";
|
||||
"HockeyOK" = "OK";
|
||||
"HockeyWarning" = "Warnung";
|
||||
"HockeyNoReleaseNotesAvailable" = "Keine Release Notes verfügbar.";
|
||||
|
||||
"HockeyAuthorizationProgress" = "Autorisierung...";
|
||||
"HockeyAuthorizationOffline" = "Internet Verbindung wird benötigt!";
|
||||
"HockeyAuthorizationDenied" = "Autorisierung verweigert. Bitte kontaktieren Sie den Entwickler.";
|
||||
|
||||
"HockeyiOS3Message" = "In-App download benötigt iOS 4 oder höher. Sie können die Applikation manuell updaten, indem sie das IPA von %@ laden und es via iTunes syncen.";
|
||||
"HockeySimulatorMessage" = "Hockey funktioniert nicht im Simulator.";
|
||||
|
||||
"HockeyButtonCheck" = "PRÜFE";
|
||||
"HockeyButtonSearching" = "PRÜFEN";
|
||||
"HockeyButtonUpdate" = "UPDATE";
|
||||
"HockeyButtonInstalling" = "INSTALLIEREN";
|
||||
"HockeyButtonOffline" = "OFFLINE";
|
||||
"HockeyInstalled" = "INSTALLIERT";
|
86
Resources/Hockey.bundle/en.lproj/Hockey.strings
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
Hockey.strings
|
||||
Hockey
|
||||
|
||||
Created by Andreas Linde on 11/15/10.
|
||||
Copyright 2010 buzzworks.de. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
/* Alert view */
|
||||
|
||||
/* For dialogs yes buttons */
|
||||
"HockeyYes" = "Yes";
|
||||
|
||||
/* For dialogs no buttons */
|
||||
"HockeyNo" = "No";
|
||||
|
||||
/* Update available */
|
||||
"HockeyUpdateAvailable" = "Update available";
|
||||
|
||||
/* Would you like to check out the new update? You can do this later on at any time in the In-App settings. */
|
||||
"HockeyUpdateAlertText" = "Would you like to check out the new update?";
|
||||
|
||||
|
||||
/* Update details screen */
|
||||
|
||||
/* Update Details */
|
||||
"HockeyUpdateScreenTitle" = "Update";
|
||||
|
||||
|
||||
/* Settings */
|
||||
|
||||
/* Screen title for settings view */
|
||||
"HockeySettingsTitle" = "Settings";
|
||||
|
||||
/* Text asking the user to send user data (on/off switch) */
|
||||
"HockeySettingsUserData" = "Send User Data";
|
||||
|
||||
/* Description text for turning on/off sending user data */
|
||||
"HockeySettingsUserDataDescription" = "Send User Data will send the following data to the developer: app version, language, device type and iOS version.";
|
||||
|
||||
/* Text asking the user to send usage data (on/off switch) */
|
||||
"HockeySettingsUsageData" = "Send Usage Data";
|
||||
|
||||
/* Description text for turning on/off sending usage data */
|
||||
"HockeySettingsUsageDataDescription" = "Send Usage Data will send the amount of test time to the developer, in a granularity of 1 minute.";
|
||||
|
||||
/* Title for defining when update checks may be made */
|
||||
"HockeySectionCheckTitle" = "Check For Update";
|
||||
|
||||
/* On Startup */
|
||||
"HockeySectionCheckStartup" = "On Startup";
|
||||
|
||||
/* Daily */
|
||||
"HockeySectionCheckDaily" = "Daily";
|
||||
|
||||
/* Manually */
|
||||
"HockeySectionCheckManually" = "Manually";
|
||||
|
||||
|
||||
"HockeyVersion" = "Version";
|
||||
"HockeyShowPreviousVersions" = "Show previous versions...";
|
||||
"HockeyNoUpdateNeededTitle" = "No Update Available";
|
||||
"HockeyNoUpdateNeededMessage" = "%@ is already the latest version.";
|
||||
"HockeyUpdateAlertTextWithAppVersion" = "%@ is available.";
|
||||
"HockeyIgnore" = "Ignore";
|
||||
"HockeyShowUpdate" = "Show";
|
||||
"HockeyInstallUpdate" = "Install";
|
||||
"HockeyError" = "Error";
|
||||
"HockeyOK" = "OK";
|
||||
"HockeyWarning" = "Warning";
|
||||
"HockeyNoReleaseNotesAvailable" = "No release notes available.";
|
||||
|
||||
"HockeyAuthorizationProgress" = "Authorizing...";
|
||||
"HockeyAuthorizationOffline" = "Internet connection required!";
|
||||
"HockeyAuthorizationDenied" = "Authorizing denied. Please contact the developer.";
|
||||
|
||||
"HockeyiOS3Message" = "In-App download requires iOS 4 or higher. You can update this application via downloading the IPA from %@ and syncing it with iTunes.";
|
||||
"HockeySimulatorMessage" = "Hockey Update does not work in the Simulator.\nThe itms-services:// url scheme is implemented but nonfunctional.";
|
||||
|
||||
"HockeyButtonCheck" = "CHECK";
|
||||
"HockeyButtonSearching" = "CHECKING";
|
||||
"HockeyButtonUpdate" = "UPDATE";
|
||||
"HockeyButtonInstalling" = "INSTALLING";
|
||||
"HockeyButtonOffline" = "OFFLINE";
|
||||
"HockeyInstalled" = "INSTALLED";
|
BIN
Resources/Hockey.bundle/gear.png
Normal file
After Width: | Height: | Size: 521 B |
BIN
Resources/Hockey.bundle/gear@2x.png
Normal file
After Width: | Height: | Size: 911 B |
86
Resources/Hockey.bundle/it.lproj/Hockey.strings
Executable file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
Hockey.strings
|
||||
Hockey
|
||||
|
||||
Created by Andreas Linde on 11/15/10.
|
||||
Copyright 2010 buzzworks.de. All rights reserved.
|
||||
*/
|
||||
|
||||
|
||||
/* Alert view */
|
||||
|
||||
/* For dialogs yes buttons */
|
||||
"HockeyYes" = "Si";
|
||||
|
||||
/* For dialogs no buttons */
|
||||
"HockeyNo" = "No";
|
||||
|
||||
/* Update available */
|
||||
"HockeyUpdateAvailable" = "Aggiornamento disponibile";
|
||||
|
||||
/* Would you like to check out the new update? You can do this later on at any time in the In-App settings. */
|
||||
"HockeyUpdateAlertText" = "Vuoi scaricare il nuovo aggiornamento ?";
|
||||
|
||||
|
||||
/* Update details screen */
|
||||
|
||||
/* Update Details */
|
||||
"HockeyUpdateScreenTitle" = "Aggiorna";
|
||||
|
||||
|
||||
/* Settings */
|
||||
|
||||
/* Screen title for settings view */
|
||||
"HockeySettingsTitle" = "Impostazioni";
|
||||
|
||||
/* Text asking the user to send user data (on/off switch) */
|
||||
"HockeySettingsUserData" = "Invia dati di sistema";
|
||||
|
||||
/* Description text for turning on/off sending user data */
|
||||
"HockeySettingsUserDataDescription" = "Invia le seguenti informazioni allo sviluppatore: versione app, lingua, tipo dispositivo e versione iOS.";
|
||||
|
||||
/* Text asking the user to send usage data (on/off switch) */
|
||||
"HockeySettingsUsageData" = "Invia dati di utilizzo";
|
||||
|
||||
/* Description text for turning on/off sending usage data */
|
||||
"HockeySettingsUsageDataDescription" = "Invia il tempo di utilizzo allo svilupatore, con un dettaglio di 1 minuto.";
|
||||
|
||||
/* Title for defining when update checks may be made */
|
||||
"HockeySectionCheckTitle" = "Controlla gli aggiornamenti";
|
||||
|
||||
/* On Startup */
|
||||
"HockeySectionCheckStartup" = "All'avvio";
|
||||
|
||||
/* Daily */
|
||||
"HockeySectionCheckDaily" = "Ogni giorno";
|
||||
|
||||
/* Manually */
|
||||
"HockeySectionCheckManually" = "Manualmente";
|
||||
|
||||
|
||||
"HockeyVersion" = "Versione";
|
||||
"HockeyShowPreviousVersions" = "Mostra le versioni precedenti...";
|
||||
"HockeyNoUpdateNeededTitle" = "Nessun aggiornamento disponibile";
|
||||
"HockeyNoUpdateNeededMessage" = "%@ <20> aggiornata all'ultima versione.";
|
||||
"HockeyUpdateAlertTextWithAppVersion" = "%@ <20> disponibile.";
|
||||
"HockeyIgnore" = "Ignora";
|
||||
"HockeyShowUpdate" = "Mostra";
|
||||
"HockeyInstallUpdate" = "Installa";
|
||||
"HockeyError" = "Errore";
|
||||
"HockeyOK" = "OK";
|
||||
"HockeyWarning" = "Avviso";
|
||||
"HockeyNoReleaseNotesAvailable" = "Non sono disponibili note di rilascio.";
|
||||
|
||||
"HockeyAuthorizationProgress" = "Attendo autorizzazione...";
|
||||
"HockeyAuthorizationOffline" = "E' richiesta la connessione internet!";
|
||||
"HockeyAuthorizationDenied" = "Autorizzazione negata, contattare lo sviluppatore.";
|
||||
|
||||
"HockeyiOS3Message" = "L'aggioranento dall'applicazione <20> possibile con iOS 4 o superiore. Puoi aggiornare questa applicazione scaricandola da %@.";
|
||||
"HockeySimulatorMessage" = "Hockey Update does not work in the Simulator.\nThe itms-services:// url scheme is implemented but nonfunctional.";
|
||||
|
||||
"HockeyButtonCheck" = "CONTROLLA";
|
||||
"HockeyButtonSearching" = "CONTROLLO";
|
||||
"HockeyButtonUpdate" = "AGGIORNA";
|
||||
"HockeyButtonInstalling" = "INSTALLO";
|
||||
"HockeyButtonOffline" = "OFFLINE";
|
||||
"HockeyInstalled" = "INSTALLATA";
|
87
Resources/Hockey.bundle/sv.lproj/Hockey.strings
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
Hockey.strings
|
||||
Hockey
|
||||
|
||||
Created by Andreas Linde on 11/15/10.
|
||||
Copyright 2010 buzzworks.de. All rights reserved.
|
||||
Swedish translation by Joakim Ramer.
|
||||
*/
|
||||
|
||||
|
||||
/* Alert view */
|
||||
|
||||
/* For dialogs yes buttons */
|
||||
"HockeyYes" = "Ja";
|
||||
|
||||
/* For dialogs no buttons */
|
||||
"HockeyNo" = "Nej";
|
||||
|
||||
/* Update available */
|
||||
"HockeyUpdateAvailable" = "Uppdatering tillgänglig";
|
||||
|
||||
/* Would you like to check out the new update? You can do this later on at any time in the In-App settings. */
|
||||
"HockeyUpdateAlertText" = "Vill du hämta den nya uppdateringen?";
|
||||
|
||||
|
||||
/* Update details screen */
|
||||
|
||||
/* Update Details */
|
||||
"HockeyUpdateScreenTitle" = "Uppdatera";
|
||||
|
||||
|
||||
/* Settings */
|
||||
|
||||
/* Screen title for settings view */
|
||||
"HockeySettingsTitle" = "Inställningar";
|
||||
|
||||
/* Text asking the user to send user data (on/off switch) */
|
||||
"HockeySettingsUserData" = "Skicka användardata";
|
||||
|
||||
/* Description text for turning on/off sending user data */
|
||||
"HockeySettingsUserDataDescription" = "Skicka användardata skickar följande till utvecklaren: app version, språk, enhetstyp och iOS version.";
|
||||
|
||||
/* Text asking the user to send usage data (on/off switch) */
|
||||
"HockeySettingsUsageData" = "Skicka användn.data";
|
||||
|
||||
/* Description text for turning on/off sending usage data */
|
||||
"HockeySettingsUsageDataDescription" = "Skicka användningsdata skickar information om hur länge appen testats (i antal minuter), till utvecklaren.";
|
||||
|
||||
/* Title for defining when update checks may be made */
|
||||
"HockeySectionCheckTitle" = "Leta efter uppdateringar";
|
||||
|
||||
/* On Startup */
|
||||
"HockeySectionCheckStartup" = "Vid uppstart";
|
||||
|
||||
/* Daily */
|
||||
"HockeySectionCheckDaily" = "Dagligen";
|
||||
|
||||
/* Manually */
|
||||
"HockeySectionCheckManually" = "Manuellt";
|
||||
|
||||
|
||||
"HockeyVersion" = "Version";
|
||||
"HockeyShowPreviousVersions" = "Se tidigare versioner...";
|
||||
"HockeyNoUpdateNeededTitle" = "Ingen uppdatering tillgänglig";
|
||||
"HockeyNoUpdateNeededMessage" = "%@ är den senaste versionen.";
|
||||
"HockeyUpdateAlertTextWithAppVersion" = "%@ finns.";
|
||||
"HockeyIgnore" = "Ignorera";
|
||||
"HockeyShowUpdate" = "Visa";
|
||||
"HockeyInstallUpdate" = "Installera";
|
||||
"HockeyError" = "Error";
|
||||
"HockeyOK" = "OK";
|
||||
"HockeyWarning" = "Varning";
|
||||
"HockeyNoReleaseNotesAvailable" = "Inga releasenoteringar tillgängliga.";
|
||||
|
||||
"HockeyAuthorizationProgress" = "Auktoriserar...";
|
||||
"HockeyAuthorizationOffline" = "Internetuppkoppling krävs!";
|
||||
"HockeyAuthorizationDenied" = "Auktoriseringen nekades. Kontakta utvecklaren.";
|
||||
|
||||
"HockeyiOS3Message" = "In-App nedladdning kräver iOS 4 eller högre. Du kan uppdatera applikationen genoma att ladda ner IPA-filen från %@ och synka in den i iTunes.";
|
||||
"HockeySimulatorMessage" = "Hockey Update fungerar inte i Simulatorn.\nitms-services:// url schemat är implementerat men fungerar ej.";
|
||||
|
||||
"HockeyButtonCheck" = "FRÅGA";
|
||||
"HockeyButtonSearching" = "FRÅGAR";
|
||||
"HockeyButtonUpdate" = "UPPDATERA";
|
||||
"HockeyButtonInstalling" = "INSTALLERAR";
|
||||
"HockeyButtonOffline" = "OFFLINE";
|
||||
"HockeyInstalled" = "INSTALLERAD";
|
BIN
Resources/Quincy.bundle/de.lproj/Quincy.strings
Executable file
BIN
Resources/Quincy.bundle/de.lproj/QuincyAlternate.strings
Executable file
BIN
Resources/Quincy.bundle/en.lproj/Quincy.strings
Executable file
BIN
Resources/Quincy.bundle/en.lproj/QuincyAlternate.strings
Executable file
BIN
Resources/Quincy.bundle/es.lproj/Quincy.strings
Executable file
BIN
Resources/Quincy.bundle/es.lproj/QuincyAlternate.strings
Executable file
BIN
Resources/Quincy.bundle/fr.lproj/Quincy.strings
Executable file
BIN
Resources/Quincy.bundle/fr.lproj/QuincyAlternate.strings
Executable file
BIN
Resources/Quincy.bundle/it.lproj/Quincy.strings
Executable file
BIN
Resources/Quincy.bundle/it.lproj/QuincyAlternate.strings
Executable file
BIN
Resources/Quincy.bundle/ja.lproj/Quincy.strings
Executable file
BIN
Resources/Quincy.bundle/ja.lproj/QuincyAlternate.strings
Executable file
BIN
Resources/Quincy.bundle/nl.lproj/Quincy.strings
Executable file
BIN
Resources/Quincy.bundle/nl.lproj/QuincyAlternate.strings
Executable file
BIN
Resources/Quincy.bundle/pt-PT.lproj/Quincy.strings
Executable file
BIN
Resources/Quincy.bundle/pt-PT.lproj/QuincyAlternate.strings
Executable file
BIN
Resources/Quincy.bundle/pt.lproj/Quincy.strings
Executable file
BIN
Resources/Quincy.bundle/pt.lproj/QuincyAlternate.strings
Executable file
BIN
Resources/Quincy.bundle/ru.lproj/Quincy.strings
Executable file
BIN
Resources/Quincy.bundle/ru.lproj/QuincyAlternate.strings
Executable file
BIN
Resources/Quincy.bundle/tr.lproj/Quincy.strings
Normal file
BIN
Resources/Quincy.bundle/tr.lproj/QuincyAlternate.strings
Normal file
BIN
Resources/Quincy.bundle/zh_CN.lproj/Quincy.strings
Normal file
BIN
Resources/Quincy.bundle/zh_CN.lproj/QuincyAlternate.strings
Normal file
BIN
Resources/Quincy.bundle/zh_TW.lproj/Quincy.strings
Normal file
BIN
Resources/Quincy.bundle/zh_TW.lproj/QuincyAlternate.strings
Normal file
1
Vendor/CrashReporter.framework/CrashReporter
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
Versions/Current/CrashReporter
|
1
Vendor/CrashReporter.framework/Headers
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
Versions/Current/Headers
|
1
Vendor/CrashReporter.framework/Resources
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
Versions/Current/Resources
|
BIN
Vendor/CrashReporter.framework/Versions/A/CrashReporter
vendored
Normal file
229
Vendor/CrashReporter.framework/Versions/A/Headers/CrashReporter.h
vendored
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Author: Landon Fuller <landonf@plausiblelabs.com>
|
||||
*
|
||||
* Copyright (c) 2008-2009 Plausible Labs Cooperative, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#import <AvailabilityMacros.h>
|
||||
#endif
|
||||
|
||||
#import "PLCrashReporter.h"
|
||||
#import "PLCrashReport.h"
|
||||
#import "PLCrashReportTextFormatter.h"
|
||||
|
||||
/**
|
||||
* @defgroup functions Crash Reporter Functions Reference
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup types Crash Reporter Data Types Reference
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup constants Crash Reporter Constants Reference
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @defgroup plcrash_internal Crash Reporter Internal Documentation
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup enums Enumerations
|
||||
* @ingroup constants
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup globals Global Variables
|
||||
* @ingroup constants
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup exceptions Exceptions
|
||||
* @ingroup constants
|
||||
*/
|
||||
|
||||
/* Exceptions */
|
||||
extern NSString *PLCrashReporterException;
|
||||
|
||||
/* Error Domain and Codes */
|
||||
extern NSString *PLCrashReporterErrorDomain;
|
||||
|
||||
/**
|
||||
* NSError codes in the Plausible Crash Reporter error domain.
|
||||
* @ingroup enums
|
||||
*/
|
||||
typedef enum {
|
||||
/** An unknown error has occured. If this
|
||||
* code is received, it is a bug, and should be reported. */
|
||||
PLCrashReporterErrorUnknown = 0,
|
||||
|
||||
/** An Mach or POSIX operating system error has occured. The underlying NSError cause may be fetched from the userInfo
|
||||
* dictionary using the NSUnderlyingErrorKey key. */
|
||||
PLCrashReporterErrorOperatingSystem = 1,
|
||||
|
||||
/** The crash report log file is corrupt or invalid */
|
||||
PLCrashReporterErrorCrashReportInvalid = 2,
|
||||
} PLCrashReporterError;
|
||||
|
||||
|
||||
/* Library Imports */
|
||||
#import "PLCrashReporter.h"
|
||||
#import "PLCrashReport.h"
|
||||
#import "PLCrashReportTextFormatter.h"
|
||||
|
||||
/**
|
||||
* @mainpage Plausible Crash Reporter
|
||||
*
|
||||
* @section intro_sec Introduction
|
||||
*
|
||||
* Plausile CrashReporter implements in-process crash reporting on the iPhone and Mac OS X.
|
||||
*
|
||||
* The following features are supported:
|
||||
*
|
||||
* - Implemented as an in-process signal handler.
|
||||
* - Does not interfer with debugging in gdb..
|
||||
* - Handles both uncaught Objective-C exceptions and fatal signals (SIGSEGV, SIGBUS, etc).
|
||||
* - Full thread state for all active threads (backtraces, register dumps) is provided.
|
||||
*
|
||||
* If your application crashes, a crash report will be written. When the application is next run, you may check for a
|
||||
* pending crash report, and submit the report to your own HTTP server, send an e-mail, or even introspect the
|
||||
* report locally.
|
||||
*
|
||||
* @section intro_encoding Crash Report Format
|
||||
*
|
||||
* Crash logs are encoded using <a href="http://code.google.com/p/protobuf/">google protobuf</a>, and may be decoded
|
||||
* using the provided PLCrashReport API. Additionally, the include plcrashutil handles conversion of binary crash reports to the
|
||||
* symbolicate-compatible iPhone text format.
|
||||
*
|
||||
* @section doc_sections Documentation Sections
|
||||
* - @subpage example_usage_iphone
|
||||
* - @subpage error_handling
|
||||
* - @subpage async_safety
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page example_usage_iphone Example iPhone Usage
|
||||
*
|
||||
* @code
|
||||
* //
|
||||
* // Called to handle a pending crash report.
|
||||
* //
|
||||
* - (void) handleCrashReport {
|
||||
* PLCrashReporter *crashReporter = [PLCrashReporter sharedReporter];
|
||||
* NSData *crashData;
|
||||
* NSError *error;
|
||||
*
|
||||
* // Try loading the crash report
|
||||
* crashData = [crashReporter loadPendingCrashReportDataAndReturnError: &error];
|
||||
* if (crashData == nil) {
|
||||
* NSLog(@"Could not load crash report: %@", error);
|
||||
* goto finish;
|
||||
* }
|
||||
*
|
||||
* // We could send the report from here, but we'll just print out
|
||||
* // some debugging info instead
|
||||
* PLCrashReport *report = [[[PLCrashReport alloc] initWithData: crashData error: &error] autorelease];
|
||||
* if (report == nil) {
|
||||
* NSLog(@"Could not parse crash report");
|
||||
* goto finish;
|
||||
* }
|
||||
*
|
||||
* NSLog(@"Crashed on %@", report.systemInfo.timestamp);
|
||||
* NSLog(@"Crashed with signal %@ (code %@, address=0x%" PRIx64 ")", report.signalInfo.name,
|
||||
* report.signalInfo.code, report.signalInfo.address);
|
||||
*
|
||||
* // Purge the report
|
||||
* finish:
|
||||
* [crashReporter purgePendingCrashReport];
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* // from UIApplicationDelegate protocol
|
||||
* - (void) applicationDidFinishLaunching: (UIApplication *) application {
|
||||
* PLCrashReporter *crashReporter = [PLCrashReporter sharedReporter];
|
||||
* NSError *error;
|
||||
*
|
||||
* // Check if we previously crashed
|
||||
* if ([crashReporter hasPendingCrashReport])
|
||||
* [self handleCrashReport];
|
||||
|
||||
* // Enable the Crash Reporter
|
||||
* if (![crashReporter enableCrashReporterAndReturnError: &error])
|
||||
* NSLog(@"Warning: Could not enable crash reporter: %@", error);
|
||||
*
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page error_handling Error Handling Programming Guide
|
||||
*
|
||||
* Where a method may return an error, Plausible Crash Reporter provides access to the underlying
|
||||
* cause via an optional NSError argument.
|
||||
*
|
||||
* All returned errors will be a member of one of the below defined domains, however, new domains and
|
||||
* error codes may be added at any time. If you do not wish to report on the error cause, many methods
|
||||
* support a simple form that requires no NSError argument.
|
||||
*
|
||||
* @section error_domains Error Domains, Codes, and User Info
|
||||
*
|
||||
* @subsection crashreporter_errors Crash Reporter Errors
|
||||
*
|
||||
* Any errors in Plausible Crash Reporter use the #PLCrashReporterErrorDomain error domain, and and one
|
||||
* of the error codes defined in #PLCrashReporterError.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @page async_safety Async-Safe Programming Guide
|
||||
*
|
||||
* Plausible CrashReporter provides support for executing an application specified function in the context of the
|
||||
* crash reporter's signal handler, after the crash report has been written to disk. This was a regularly requested
|
||||
* feature, and provides the ability to implement application finalization in the event of a crash. However, writing
|
||||
* code intended for execution inside of a signal handler is exceptionally difficult, and is not recommended.
|
||||
*
|
||||
* @section program_flow Program Flow and Signal Handlers
|
||||
*
|
||||
* When the signal handler is called the normal flow of the program is interrupted, and your program is an unknown
|
||||
* state. Locks may be held, the heap may be corrupt (or in the process of being updated), and your signal
|
||||
* handler may invoke a function that was being executed at the time of the signal. This may result in deadlocks,
|
||||
* data corruption, and program termination.
|
||||
*
|
||||
* @section functions Async-Safe Functions
|
||||
*
|
||||
* A subset of functions are defined to be async-safe by the OS, and are safely callable from within a signal handler. If
|
||||
* you do implement a custom post-crash handler, it must be async-safe. A table of POSIX-defined async-safe functions
|
||||
* and additional information is available from the
|
||||
* <a href="https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers">CERT programming guide - SIG30-C</a>
|
||||
*
|
||||
* Most notably, the Objective-C runtime itself is not async-safe, and Objective-C may not be used within a signal
|
||||
* handler.
|
||||
*
|
||||
* @sa PLCrashReporter::setCrashCallbacks:
|
||||
*/
|
169
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReport.h
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Author: Landon Fuller <landonf@plausiblelabs.com>
|
||||
*
|
||||
* Copyright (c) 2008-2010 Plausible Labs Cooperative, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "PLCrashReportSystemInfo.h"
|
||||
#import "PLCrashReportMachineInfo.h"
|
||||
#import "PLCrashReportApplicationInfo.h"
|
||||
#import "PLCrashReportProcessInfo.h"
|
||||
#import "PLCrashReportSignalInfo.h"
|
||||
#import "PLCrashReportThreadInfo.h"
|
||||
#import "PLCrashReportBinaryImageInfo.h"
|
||||
#import "PLCrashReportExceptionInfo.h"
|
||||
|
||||
/**
|
||||
* @ingroup constants
|
||||
* Crash file magic identifier */
|
||||
#define PLCRASH_REPORT_FILE_MAGIC "plcrash"
|
||||
|
||||
/**
|
||||
* @ingroup constants
|
||||
* Crash format version byte identifier. Will not change outside of the introduction of
|
||||
* an entirely new crash log format. */
|
||||
#define PLCRASH_REPORT_FILE_VERSION 1
|
||||
|
||||
/**
|
||||
* @ingroup types
|
||||
* Crash log file header format.
|
||||
*
|
||||
* Crash log files start with 7 byte magic identifier (#PLCRASH_REPORT_FILE_MAGIC),
|
||||
* followed by a single unsigned byte version number (#PLCRASH_REPORT_FILE_VERSION).
|
||||
* The crash log message format itself is extensible, so this version number will only
|
||||
* be incremented in the event of an incompatible encoding or format change.
|
||||
*/
|
||||
struct PLCrashReportFileHeader {
|
||||
/** Crash log magic identifier, not NULL terminated */
|
||||
const char magic[7];
|
||||
|
||||
/** Crash log encoding/format version */
|
||||
const uint8_t version;
|
||||
|
||||
/** File data */
|
||||
const uint8_t data[];
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Private decoder instance variables (used to hide the underlying protobuf parser).
|
||||
*/
|
||||
typedef struct _PLCrashReportDecoder _PLCrashReportDecoder;
|
||||
|
||||
@interface PLCrashReport : NSObject {
|
||||
@private
|
||||
/** Private implementation variables (used to hide the underlying protobuf parser) */
|
||||
_PLCrashReportDecoder *_decoder;
|
||||
|
||||
/** System info */
|
||||
PLCrashReportSystemInfo *_systemInfo;
|
||||
|
||||
/** Machine info */
|
||||
PLCrashReportMachineInfo *_machineInfo;
|
||||
|
||||
/** Application info */
|
||||
PLCrashReportApplicationInfo *_applicationInfo;
|
||||
|
||||
/** Process info */
|
||||
PLCrashReportProcessInfo *_processInfo;
|
||||
|
||||
/** Signal info */
|
||||
PLCrashReportSignalInfo *_signalInfo;
|
||||
|
||||
/** Thread info (PLCrashReportThreadInfo instances) */
|
||||
NSArray *_threads;
|
||||
|
||||
/** Binary images (PLCrashReportBinaryImageInfo instances */
|
||||
NSArray *_images;
|
||||
|
||||
/** Exception information (may be nil) */
|
||||
PLCrashReportExceptionInfo *_exceptionInfo;
|
||||
}
|
||||
|
||||
- (id) initWithData: (NSData *) encodedData error: (NSError **) outError;
|
||||
|
||||
- (PLCrashReportBinaryImageInfo *) imageForAddress: (uint64_t) address;
|
||||
|
||||
/**
|
||||
* System information.
|
||||
*/
|
||||
@property(nonatomic, readonly) PLCrashReportSystemInfo *systemInfo;
|
||||
|
||||
/**
|
||||
* YES if machine information is available.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL hasMachineInfo;
|
||||
|
||||
/**
|
||||
* Machine information. Only available in later (v1.1+) crash report format versions. If not available,
|
||||
* will be nil.
|
||||
*/
|
||||
@property(nonatomic, readonly) PLCrashReportMachineInfo *machineInfo;
|
||||
|
||||
/**
|
||||
* Application information.
|
||||
*/
|
||||
@property(nonatomic, readonly) PLCrashReportApplicationInfo *applicationInfo;
|
||||
|
||||
/**
|
||||
* YES if process information is available.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL hasProcessInfo;
|
||||
|
||||
/**
|
||||
* Process information. Only available in later (v1.1+) crash report format versions. If not available,
|
||||
* will be nil.
|
||||
*/
|
||||
@property(nonatomic, readonly) PLCrashReportProcessInfo *processInfo;
|
||||
|
||||
/**
|
||||
* Signal information. This provides the signal and signal code of the fatal signal.
|
||||
*/
|
||||
@property(nonatomic, readonly) PLCrashReportSignalInfo *signalInfo;
|
||||
|
||||
/**
|
||||
* Thread information. Returns a list of PLCrashReportThreadInfo instances.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray *threads;
|
||||
|
||||
/**
|
||||
* Binary image information. Returns a list of PLCrashReportBinaryImageInfo instances.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray *images;
|
||||
|
||||
/**
|
||||
* YES if exception information is available.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL hasExceptionInfo;
|
||||
|
||||
/**
|
||||
* Exception information. Only available if a crash was caused by an uncaught exception,
|
||||
* otherwise nil.
|
||||
*/
|
||||
@property(nonatomic, readonly) PLCrashReportExceptionInfo *exceptionInfo;
|
||||
|
||||
@end
|
53
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportApplicationInfo.h
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Author: Landon Fuller <landonf@plausiblelabs.com>
|
||||
*
|
||||
* Copyright (c) 2008-2009 Plausible Labs Cooperative, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface PLCrashReportApplicationInfo : NSObject {
|
||||
@private
|
||||
/** Application identifier */
|
||||
NSString *_applicationIdentifier;
|
||||
|
||||
/** Application version */
|
||||
NSString *_applicationVersion;
|
||||
}
|
||||
|
||||
- (id) initWithApplicationIdentifier: (NSString *) applicationIdentifier
|
||||
applicationVersion: (NSString *) applicationVersion;
|
||||
|
||||
/**
|
||||
* The application identifier. This is usually the application's CFBundleIdentifier value.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *applicationIdentifier;
|
||||
|
||||
/**
|
||||
* The application version. This is usually the application's CFBundleVersion value.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *applicationVersion;
|
||||
|
||||
@end
|
90
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportBinaryImageInfo.h
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Author: Landon Fuller <landonf@plausiblelabs.com>
|
||||
*
|
||||
* Copyright (c) 2008-2009 Plausible Labs Cooperative, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "PLCrashReportProcessorInfo.h"
|
||||
|
||||
@interface PLCrashReportBinaryImageInfo : NSObject {
|
||||
@private
|
||||
/** Code type */
|
||||
PLCrashReportProcessorInfo *_processorInfo;
|
||||
|
||||
/** Base image address */
|
||||
uint64_t _baseAddress;
|
||||
|
||||
/** Image segment size */
|
||||
uint64_t _imageSize;
|
||||
|
||||
/** Name of binary image */
|
||||
NSString *_imageName;
|
||||
|
||||
/** If the UUID is available */
|
||||
BOOL _hasImageUUID;
|
||||
|
||||
/** 128-bit object UUID. May be nil. */
|
||||
NSString *_imageUUID;
|
||||
}
|
||||
|
||||
- (id) initWithCodeType: (PLCrashReportProcessorInfo *) processorInfo
|
||||
baseAddress: (uint64_t) baseAddress
|
||||
size: (uint64_t) imageSize
|
||||
name: (NSString *) imageName
|
||||
uuid: (NSData *) uuid;
|
||||
|
||||
/**
|
||||
* Image code type, or nil if unavailable.
|
||||
*/
|
||||
@property(nonatomic, readonly) PLCrashReportProcessorInfo *codeType;
|
||||
|
||||
/**
|
||||
* Image base address.
|
||||
*/
|
||||
@property(nonatomic, readonly) uint64_t imageBaseAddress;
|
||||
|
||||
/**
|
||||
* Segment size.
|
||||
*/
|
||||
@property(nonatomic, readonly) uint64_t imageSize;
|
||||
|
||||
/**
|
||||
* Image name (absolute path)
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *imageName;
|
||||
|
||||
|
||||
/**
|
||||
* YES if this image has an associated UUID.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL hasImageUUID;
|
||||
|
||||
/**
|
||||
* 128-bit object UUID (matches Mach-O DWARF dSYM files). May be nil if unavailable.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *imageUUID;
|
||||
|
||||
@end
|
65
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportExceptionInfo.h
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Author: Landon Fuller <landonf@plausiblelabs.com>
|
||||
*
|
||||
* Copyright (c) 2008-2009 Plausible Labs Cooperative, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "PLCrashReportThreadInfo.h"
|
||||
|
||||
|
||||
@interface PLCrashReportExceptionInfo : NSObject {
|
||||
@private
|
||||
/** Name */
|
||||
NSString *_name;
|
||||
|
||||
/** Reason */
|
||||
NSString *_reason;
|
||||
|
||||
/** Ordered list of PLCrashReportStackFrame instances, or nil if unavailable. */
|
||||
NSArray *_stackFrames;
|
||||
}
|
||||
|
||||
- (id) initWithExceptionName: (NSString *) name reason: (NSString *) reason;
|
||||
|
||||
- (id) initWithExceptionName: (NSString *) name
|
||||
reason: (NSString *) reason
|
||||
stackFrames: (NSArray *) stackFrames;
|
||||
|
||||
/**
|
||||
* The exception name.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *exceptionName;
|
||||
|
||||
/**
|
||||
* The exception reason.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *exceptionReason;
|
||||
|
||||
/* The exception's original call stack, as an array of PLCrashReportStackFrameInfo instances, or nil if unavailable.
|
||||
* This may be preserved across rethrow of an exception, and can be used to determine the original call stack. */
|
||||
@property(nonatomic, readonly) NSArray *stackFrames;
|
||||
|
||||
@end
|
51
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportFormatter.h
vendored
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Author: Landon Fuller <landonf@plausiblelabs.com>
|
||||
*
|
||||
* Copyright (c) 2008-2010 Plausible Labs Cooperative, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "PLCrashReport.h"
|
||||
|
||||
/**
|
||||
* A crash report formatter accepts a PLCrashReport instance, formats it according to implementation-specified rules,
|
||||
* (such as implementing text output support), and returns the result.
|
||||
*/
|
||||
@protocol PLCrashReportFormatter
|
||||
|
||||
/**
|
||||
* Format the provided @a report.
|
||||
*
|
||||
* @param report Report to be formatted.
|
||||
* @param outError A pointer to an NSError object variable. If an error occurs, this pointer will contain an error
|
||||
* object indicating why the pending crash report could not be formatted. If no error occurs, this parameter will
|
||||
* be left unmodified. You may specify nil for this parameter, and no error information will be provided.
|
||||
*
|
||||
* @return Returns the formatted report data on success, or nil on failure.
|
||||
*/
|
||||
- (NSData *) formatReport: (PLCrashReport *) report error: (NSError **) outError;
|
||||
|
||||
@end
|
73
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportMachineInfo.h
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Author: Landon Fuller <landonf@plausible.coop>
|
||||
*
|
||||
* Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "PLCrashReportProcessorInfo.h"
|
||||
|
||||
@interface PLCrashReportMachineInfo : NSObject {
|
||||
@private
|
||||
/** The hardware model name (eg, MacBookPro6,1). This may be unavailable, and this property will be nil. */
|
||||
NSString *_modelName;
|
||||
|
||||
/** The processor type. */
|
||||
PLCrashReportProcessorInfo *_processorInfo;
|
||||
|
||||
/* The number of actual physical processor cores. */
|
||||
NSUInteger _processorCount;
|
||||
|
||||
/* The number of logical processors. */
|
||||
NSUInteger _logicalProcessorCount;
|
||||
}
|
||||
|
||||
- (id) initWithModelName: (NSString *) modelName
|
||||
processorInfo: (PLCrashReportProcessorInfo *) processorInfo
|
||||
processorCount: (NSUInteger) processorCount
|
||||
logicalProcessorCount: (NSUInteger) logicalProcessorCount;
|
||||
|
||||
/** The hardware model name (eg, MacBookPro6,1). This may be unavailable, and this property will be nil. */
|
||||
@property(nonatomic, readonly) NSString *modelName;
|
||||
|
||||
/** The processor type. */
|
||||
@property(nonatomic, readonly) PLCrashReportProcessorInfo *processorInfo;
|
||||
|
||||
/*
|
||||
* The number of actual physical processor cores. Note that the number of active processors may be managed by the
|
||||
* operating system's power management system, and this value may not reflect the number of active
|
||||
* processors at the time of the crash.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSUInteger processorCount;
|
||||
|
||||
/*
|
||||
* The number of logical processors. Note that the number of active processors may be managed by the
|
||||
* operating system's power management system, and this value may not reflect the number of active
|
||||
* processors at the time of the crash.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSUInteger logicalProcessorCount;
|
||||
|
||||
@end
|
92
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportProcessInfo.h
vendored
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Author: Damian Morris <damian@moso.com.au>
|
||||
*
|
||||
* Copyright (c) 2010 MOSO Corporation, Pty Ltd.
|
||||
* Copyright (c) 2010 Plausible Labs Cooperative, Inc.
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface PLCrashReportProcessInfo : NSObject {
|
||||
@private
|
||||
/** Process name */
|
||||
NSString *_processName;
|
||||
|
||||
/** Process ID */
|
||||
NSUInteger _processID;
|
||||
|
||||
/** Process path */
|
||||
NSString* _processPath;
|
||||
|
||||
/** Parent process name */
|
||||
NSString *_parentProcessName;
|
||||
|
||||
/** Parent process ID */
|
||||
NSUInteger _parentProcessID;
|
||||
|
||||
/** If false, the process is being run via process-level CPU emulation (such as Rosetta). */
|
||||
BOOL _native;
|
||||
}
|
||||
|
||||
- (id) initWithProcessName: (NSString *) processName
|
||||
processID: (NSUInteger) processID
|
||||
processPath: (NSString *) processPath
|
||||
parentProcessName: (NSString *) parentProcessName
|
||||
parentProcessID: (NSUInteger) parentProcessID
|
||||
native: (BOOL) native;
|
||||
|
||||
/**
|
||||
* The process name. This value may not be included in the crash report, in which case this property
|
||||
* will be nil.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *processName;
|
||||
|
||||
/**
|
||||
* The process ID.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSUInteger processID;
|
||||
|
||||
/**
|
||||
* The path to the process executable. This value may not be included in the crash report, in which case this property
|
||||
* will be nil.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *processPath;
|
||||
|
||||
/**
|
||||
* The parent process name. This value may not be included in the crash report, in which case this property
|
||||
* will be nil.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *parentProcessName;
|
||||
|
||||
/**
|
||||
* The parent process ID.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSUInteger parentProcessID;
|
||||
|
||||
/** The process' native execution status. If false, the process is being run via process-level CPU emulation (such as Rosetta). */
|
||||
@property(nonatomic, readonly) BOOL native;
|
||||
|
||||
@end
|
74
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportProcessorInfo.h
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Author: Landon Fuller <landonf@plausible.coop>
|
||||
*
|
||||
* Copyright (c) 2008-2011 Plausible Labs Cooperative, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <mach/machine.h>
|
||||
|
||||
/**
|
||||
* @ingroup constants
|
||||
*
|
||||
* The type encodings supported for CPU types and subtypes. Currently only Apple
|
||||
* Mach-O defined encodings are supported.
|
||||
*
|
||||
* @internal
|
||||
* These enum values match the protobuf values. Keep them synchronized.
|
||||
*/
|
||||
typedef enum {
|
||||
/** Unknown cpu type encoding. */
|
||||
PLCrashReportProcessorTypeEncodingUnknown = 0,
|
||||
|
||||
/** Apple Mach-defined processor types. */
|
||||
PLCrashReportProcessorTypeEncodingMach = 1
|
||||
} PLCrashReportProcessorTypeEncoding;
|
||||
|
||||
@interface PLCrashReportProcessorInfo : NSObject {
|
||||
@private
|
||||
/** Type encoding */
|
||||
PLCrashReportProcessorTypeEncoding _typeEncoding;
|
||||
|
||||
/** CPU type */
|
||||
uint64_t _type;
|
||||
|
||||
/** CPU subtype */
|
||||
uint64_t _subtype;
|
||||
}
|
||||
|
||||
- (id) initWithTypeEncoding: (PLCrashReportProcessorTypeEncoding) typeEncoding
|
||||
type: (uint64_t) type
|
||||
subtype: (uint64_t) subtype;
|
||||
|
||||
/** The CPU type encoding. */
|
||||
@property(nonatomic, readonly) PLCrashReportProcessorTypeEncoding typeEncoding;
|
||||
|
||||
/** The CPU type. */
|
||||
@property(nonatomic, readonly) uint64_t type;
|
||||
|
||||
/** The CPU subtype. */
|
||||
@property(nonatomic, readonly) uint64_t subtype;
|
||||
|
||||
@end
|
60
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportSignalInfo.h
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Author: Landon Fuller <landonf@plausiblelabs.com>
|
||||
*
|
||||
* Copyright (c) 2008-2009 Plausible Labs Cooperative, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface PLCrashReportSignalInfo : NSObject {
|
||||
@private
|
||||
/** Signal name */
|
||||
NSString *_name;
|
||||
|
||||
/** Signal code */
|
||||
NSString *_code;
|
||||
|
||||
/** Fauling instruction or address */
|
||||
uint64_t _address;
|
||||
}
|
||||
|
||||
- (id) initWithSignalName: (NSString *) name code: (NSString *) code address: (uint64_t) address;
|
||||
|
||||
/**
|
||||
* The signal name.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *name;
|
||||
|
||||
/**
|
||||
* The signal code.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *code;
|
||||
|
||||
/**
|
||||
* The faulting instruction or address.
|
||||
*/
|
||||
@property(nonatomic, readonly) uint64_t address;
|
||||
|
||||
@end
|
142
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportSystemInfo.h
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
/*
|
||||
* Author: Landon Fuller <landonf@plausiblelabs.com>
|
||||
*
|
||||
* Copyright (c) 2008-2009 Plausible Labs Cooperative, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* @ingroup constants
|
||||
*
|
||||
* Indicates the Operating System under which a Crash Log was generated.
|
||||
*
|
||||
* @internal
|
||||
* These enum values match the protobuf values. Keep them synchronized.
|
||||
*/
|
||||
typedef enum {
|
||||
/** Mac OS X. */
|
||||
PLCrashReportOperatingSystemMacOSX = 0,
|
||||
|
||||
/** iPhone OS */
|
||||
PLCrashReportOperatingSystemiPhoneOS = 1,
|
||||
|
||||
/** iPhone Simulator (Mac OS X with additional simulator-specific runtime libraries) */
|
||||
PLCrashReportOperatingSystemiPhoneSimulator = 2,
|
||||
|
||||
/** Unknown operating system */
|
||||
PLCrashReportOperatingSystemUnknown = 3,
|
||||
} PLCrashReportOperatingSystem;
|
||||
|
||||
/**
|
||||
* @ingroup constants
|
||||
*
|
||||
* Indicates the architecture under which a Crash Log was generated.
|
||||
*
|
||||
* @deprecated The architecture value has been deprecated in v1.1 and later crash reports. All new reports
|
||||
* will make use of the new PLCrashReportProcessorInfo CPU type encodings.
|
||||
*
|
||||
* @internal
|
||||
* These enum values match the protobuf values. Keep them synchronized.
|
||||
*/
|
||||
typedef enum {
|
||||
/** x86-32. */
|
||||
PLCrashReportArchitectureX86_32 = 0,
|
||||
|
||||
/** x86-64 */
|
||||
PLCrashReportArchitectureX86_64 = 1,
|
||||
|
||||
/** ARMv6 */
|
||||
PLCrashReportArchitectureARMv6 = 2,
|
||||
|
||||
/**
|
||||
* ARMv6
|
||||
* @deprecated
|
||||
* @sa PLCrashReportArchitectureARMv6
|
||||
*/
|
||||
PLCrashReportArchitectureARM = PLCrashReportArchitectureARMv6,
|
||||
|
||||
/** PPC */
|
||||
PLCrashReportArchitecturePPC = 3,
|
||||
|
||||
/** PPC64 */
|
||||
PLCrashReportArchitecturePPC64 = 4,
|
||||
|
||||
/** ARMv7 */
|
||||
PLCrashReportArchitectureARMv7 = 5,
|
||||
|
||||
/** Unknown */
|
||||
PLCrashReportArchitectureUnknown = 6
|
||||
} PLCrashReportArchitecture;
|
||||
|
||||
|
||||
extern PLCrashReportOperatingSystem PLCrashReportHostOperatingSystem;
|
||||
extern PLCrashReportArchitecture PLCrashReportHostArchitecture;
|
||||
|
||||
@interface PLCrashReportSystemInfo : NSObject {
|
||||
@private
|
||||
/** Operating system */
|
||||
PLCrashReportOperatingSystem _operatingSystem;
|
||||
|
||||
/** Operating system version */
|
||||
NSString *_osVersion;
|
||||
|
||||
/** OS build. May be nil. */
|
||||
NSString *_osBuild;
|
||||
|
||||
/** Architecture */
|
||||
PLCrashReportArchitecture _architecture;
|
||||
|
||||
/** Date crash report was generated. May be nil if the date is unknown. */
|
||||
NSDate *_timestamp;
|
||||
}
|
||||
|
||||
- (id) initWithOperatingSystem: (PLCrashReportOperatingSystem) operatingSystem
|
||||
operatingSystemVersion: (NSString *) operatingSystemVersion
|
||||
architecture: (PLCrashReportArchitecture) architecture
|
||||
timestamp: (NSDate *) timestamp;
|
||||
|
||||
- (id) initWithOperatingSystem: (PLCrashReportOperatingSystem) operatingSystem
|
||||
operatingSystemVersion: (NSString *) operatingSystemVersion
|
||||
operatingSystemBuild: (NSString *) operatingSystemBuild
|
||||
architecture: (PLCrashReportArchitecture) architecture
|
||||
timestamp: (NSDate *) timestamp;
|
||||
|
||||
/** The operating system. */
|
||||
@property(nonatomic, readonly) PLCrashReportOperatingSystem operatingSystem;
|
||||
|
||||
/** The operating system's release version. */
|
||||
@property(nonatomic, readonly) NSString *operatingSystemVersion;
|
||||
|
||||
/** The operating system's build identifier (eg, 10J869). This may be unavailable, and this property will be nil. */
|
||||
@property(nonatomic, readonly) NSString *operatingSystemBuild;
|
||||
|
||||
/** Architecture. */
|
||||
@property(nonatomic, readonly) PLCrashReportArchitecture architecture;
|
||||
|
||||
/** Date and time that the crash report was generated. This may be unavailable, and this property will be nil. */
|
||||
@property(nonatomic, readonly) NSDate *timestamp;
|
||||
|
||||
@end
|
62
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportTextFormatter.h
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Authors:
|
||||
* Landon Fuller <landonf@plausiblelabs.com>
|
||||
* Damian Morris <damian@moso.com.au>
|
||||
*
|
||||
* Copyright (c) 2008-2010 Plausible Labs Cooperative, Inc.
|
||||
* Copyright (c) 2010 MOSO Corporation, Pty Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "PLCrashReportFormatter.h"
|
||||
|
||||
/**
|
||||
* Supported text output formats.
|
||||
*
|
||||
* @ingroup enums
|
||||
*/
|
||||
typedef enum {
|
||||
/** An iOS-compatible crash log text format. Compatible with the crash logs generated by the device and available
|
||||
* through iTunes Connect. */
|
||||
PLCrashReportTextFormatiOS = 0
|
||||
} PLCrashReportTextFormat;
|
||||
|
||||
|
||||
@interface PLCrashReportTextFormatter : NSObject <PLCrashReportFormatter> {
|
||||
@private
|
||||
/** Text output format. */
|
||||
PLCrashReportTextFormat _textFormat;
|
||||
|
||||
/** Encoding to use for string output. */
|
||||
NSStringEncoding _stringEncoding;
|
||||
}
|
||||
|
||||
+ (NSString *) stringValueForCrashReport: (PLCrashReport *) report withTextFormat: (PLCrashReportTextFormat) textFormat;
|
||||
|
||||
- (id) initWithTextFormat: (PLCrashReportTextFormat) textFormat stringEncoding: (NSStringEncoding) stringEncoding;
|
||||
|
||||
@end
|
114
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReportThreadInfo.h
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Author: Landon Fuller <landonf@plausiblelabs.com>
|
||||
*
|
||||
* Copyright (c) 2008-2009 Plausible Labs Cooperative, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface PLCrashReportStackFrameInfo : NSObject {
|
||||
@private
|
||||
/** Frame instruction pointer. */
|
||||
uint64_t _instructionPointer;
|
||||
}
|
||||
|
||||
- (id) initWithInstructionPointer: (uint64_t) instructionPointer;
|
||||
|
||||
/**
|
||||
* Frame's instruction pointer.
|
||||
*/
|
||||
@property(nonatomic, readonly) uint64_t instructionPointer;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface PLCrashReportRegisterInfo : NSObject {
|
||||
@private
|
||||
/** Register name */
|
||||
NSString *_registerName;
|
||||
|
||||
/** Register value */
|
||||
uint64_t _registerValue;
|
||||
}
|
||||
|
||||
- (id) initWithRegisterName: (NSString *) registerName registerValue: (uint64_t) registerValue;
|
||||
|
||||
/**
|
||||
* Register name.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSString *registerName;
|
||||
|
||||
/**
|
||||
* Register value.
|
||||
*/
|
||||
@property(nonatomic, readonly) uint64_t registerValue;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@interface PLCrashReportThreadInfo : NSObject {
|
||||
@private
|
||||
/** The thread number. Should be unique within a given crash log. */
|
||||
NSInteger _threadNumber;
|
||||
|
||||
/** Ordered list of PLCrashReportStackFrame instances */
|
||||
NSArray *_stackFrames;
|
||||
|
||||
/** YES if this thread crashed. */
|
||||
BOOL _crashed;
|
||||
|
||||
/** List of PLCrashReportRegister instances. Will be empty if _crashed is NO. */
|
||||
NSArray *_registers;
|
||||
}
|
||||
|
||||
- (id) initWithThreadNumber: (NSInteger) threadNumber
|
||||
stackFrames: (NSArray *) stackFrames
|
||||
crashed: (BOOL) crashed
|
||||
registers: (NSArray *) registers;
|
||||
|
||||
/**
|
||||
* Application thread number.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSInteger threadNumber;
|
||||
|
||||
/**
|
||||
* Thread backtrace. Provides an array of PLCrashReportStackFrameInfo instances.
|
||||
* The array is ordered, last callee to first.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray *stackFrames;
|
||||
|
||||
/**
|
||||
* If this thread crashed, set to YES.
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL crashed;
|
||||
|
||||
/**
|
||||
* State of the general purpose and related registers, as a list of
|
||||
* PLCrashReportRegister instances. If this thead did not crash (crashed returns NO),
|
||||
* this list will be empty.
|
||||
*/
|
||||
@property(nonatomic, readonly) NSArray *registers;
|
||||
|
||||
@end
|
97
Vendor/CrashReporter.framework/Versions/A/Headers/PLCrashReporter.h
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
* Author: Landon Fuller <landonf@plausiblelabs.com>
|
||||
*
|
||||
* Copyright (c) 2008-2009 Plausible Labs Cooperative, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
/**
|
||||
* @ingroup functions
|
||||
*
|
||||
* Prototype of a callback function used to execute additional user code with signal information as provided
|
||||
* by PLCrashReporter. Called upon completion of crash handling, after the crash report has been written to disk.
|
||||
*
|
||||
* @param info The signal info.
|
||||
* @param uap The crash's threads context.
|
||||
* @param context The API client's supplied context value.
|
||||
*
|
||||
* @sa @ref async_safety
|
||||
* @sa PLCrashReporter::setPostCrashCallbacks:
|
||||
*/
|
||||
typedef void (*PLCrashReporterPostCrashSignalCallback)(siginfo_t *info, ucontext_t *uap, void *context);
|
||||
|
||||
/**
|
||||
* @ingroup types
|
||||
*
|
||||
* This structure contains callbacks supported by PLCrashReporter to allow the host application to perform
|
||||
* additional tasks prior to program termination after a crash has occured.
|
||||
*
|
||||
* @sa @ref async_safety
|
||||
*/
|
||||
typedef struct PLCrashReporterCallbacks {
|
||||
/** The version number of this structure. If not one of the defined version numbers for this type, the behavior
|
||||
* is undefined. The current version of this structure is 0. */
|
||||
uint16_t version;
|
||||
|
||||
/** An arbitrary user-supplied context value. This value may be NULL. */
|
||||
void *context;
|
||||
|
||||
/** The callback used to report caught signal information. In version 0 of this structure, all crashes will be
|
||||
* reported via this function. */
|
||||
PLCrashReporterPostCrashSignalCallback handleSignal;
|
||||
} PLCrashReporterCallbacks;
|
||||
|
||||
@interface PLCrashReporter : NSObject {
|
||||
@private
|
||||
/** YES if the crash reporter has been enabled */
|
||||
BOOL _enabled;
|
||||
|
||||
/** Application identifier */
|
||||
NSString *_applicationIdentifier;
|
||||
|
||||
/** Application version */
|
||||
NSString *_applicationVersion;
|
||||
|
||||
/** Path to the crash reporter internal data directory */
|
||||
NSString *_crashReportDirectory;
|
||||
}
|
||||
|
||||
+ (PLCrashReporter *) sharedReporter;
|
||||
|
||||
- (BOOL) hasPendingCrashReport;
|
||||
|
||||
- (NSData *) loadPendingCrashReportData;
|
||||
- (NSData *) loadPendingCrashReportDataAndReturnError: (NSError **) outError;
|
||||
|
||||
- (BOOL) purgePendingCrashReport;
|
||||
- (BOOL) purgePendingCrashReportAndReturnError: (NSError **) outError;
|
||||
|
||||
- (BOOL) enableCrashReporter;
|
||||
- (BOOL) enableCrashReporterAndReturnError: (NSError **) outError;
|
||||
|
||||
- (void) setCrashCallbacks: (PLCrashReporterCallbacks *) callbacks;
|
||||
|
||||
@end
|
38
Vendor/CrashReporter.framework/Versions/A/Resources/Info.plist
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>BuildMachineOSBuild</key>
|
||||
<string>11C74</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>English</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>CrashReporter</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.yourcompany.CrashReporter</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>CrashReporter</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>DTCompiler</key>
|
||||
<string></string>
|
||||
<key>DTPlatformBuild</key>
|
||||
<string>4D199</string>
|
||||
<key>DTPlatformVersion</key>
|
||||
<string>GM</string>
|
||||
<key>DTSDKBuild</key>
|
||||
<string>11C63</string>
|
||||
<key>DTSDKName</key>
|
||||
<string>macosx10.7</string>
|
||||
<key>DTXcode</key>
|
||||
<string>0420</string>
|
||||
<key>DTXcodeBuild</key>
|
||||
<string>4D199</string>
|
||||
</dict>
|
||||
</plist>
|
1
Vendor/CrashReporter.framework/Versions/Current
vendored
Symbolic link
@ -0,0 +1 @@
|
||||
A
|