mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00

git-subtree-dir: submodules/HockeySDK-iOS git-subtree-mainline: 085acd26c4432939403765234266e3c1be0f3dd9 git-subtree-split: c7d0c7026303253e2ac576c02655691e5d314fe2
373 lines
14 KiB
Objective-C
373 lines
14 KiB
Objective-C
/*
|
|
* Author: Andreas Linde <mail@andreaslinde.de>
|
|
*
|
|
* Copyright (c) 2012-2014 HockeyApp, Bit Stadium GmbH.
|
|
* 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 "HockeySDK.h"
|
|
#import "HockeySDKPrivate.h"
|
|
|
|
#import "BITHockeyHelper.h"
|
|
|
|
#import "BITHockeyBaseManager.h"
|
|
#import "BITHockeyBaseManagerPrivate.h"
|
|
#if HOCKEYSDK_FEATURE_AUTHENTICATOR || HOCKEYSDK_FEATURE_UPDATES || HOCKEYSDK_FEATURE_FEEDBACK
|
|
#import "BITHockeyBaseViewController.h"
|
|
#endif
|
|
|
|
#import "BITKeychainUtils.h"
|
|
|
|
#import <sys/sysctl.h>
|
|
#import <mach-o/dyld.h>
|
|
#import <mach-o/loader.h>
|
|
|
|
// We need BIT_UNUSED macro to make sure there aren't any warnings when building
|
|
// HockeySDK Distribution scheme. Since several configurations are build in this scheme
|
|
// and different features can be turned on and off we can't just use __unused attribute.
|
|
#if !defined (HOCKEYSDK_CONFIGURATION_ReleaseCrashOnlyExtensions)
|
|
#if HOCKEYSDK_FEATURE_AUTHENTICATOR || HOCKEYSDK_FEATURE_UPDATES || HOCKEYSDK_FEATURE_FEEDBACK
|
|
#define BIT_UNUSED
|
|
#else
|
|
#define BIT_UNUSED __unused
|
|
#endif
|
|
#endif
|
|
|
|
@interface BITHockeyBaseManager ()
|
|
|
|
@property (nonatomic, strong) UINavigationController *navController;
|
|
@property (nonatomic, strong) NSDateFormatter *rfc3339Formatter;
|
|
|
|
@end
|
|
|
|
static void (^_presentAlert)(UIAlertController *) = nil;
|
|
static void (^_presentView)(UIViewController *) = nil;
|
|
|
|
@implementation BITHockeyBaseManager
|
|
|
|
- (instancetype)init {
|
|
if ((self = [super init])) {
|
|
_serverURL = BITHOCKEYSDK_URL;
|
|
_barStyle = UIBarStyleDefault;
|
|
_modalPresentationStyle = UIModalPresentationFormSheet;
|
|
|
|
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
|
|
_rfc3339Formatter = [[NSDateFormatter alloc] init];
|
|
[_rfc3339Formatter setLocale:enUSPOSIXLocale];
|
|
[_rfc3339Formatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
|
|
[_rfc3339Formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (instancetype)initWithAppIdentifier:(NSString *)appIdentifier appEnvironment:(BITEnvironment)environment {
|
|
if ((self = [self init])) {
|
|
_appIdentifier = appIdentifier;
|
|
_appEnvironment = environment;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
|
|
#pragma mark - Private
|
|
|
|
- (void)reportError:(NSError *)error {
|
|
BITHockeyLogError(@"ERROR: %@", [error localizedDescription]);
|
|
}
|
|
|
|
- (NSString *)encodedAppIdentifier {
|
|
return bit_encodeAppIdentifier(self.appIdentifier);
|
|
}
|
|
|
|
- (NSString *)getDevicePlatform {
|
|
size_t size;
|
|
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
|
|
char *answer = (char*)malloc(size);
|
|
if (answer == NULL)
|
|
return @"";
|
|
sysctlbyname("hw.machine", answer, &size, NULL, 0);
|
|
NSString *platform = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding];
|
|
free(answer);
|
|
return platform;
|
|
}
|
|
|
|
- (NSString *)executableUUID {
|
|
const struct mach_header *executableHeader = NULL;
|
|
for (uint32_t i = 0; i < _dyld_image_count(); i++) {
|
|
const struct mach_header *header = _dyld_get_image_header(i);
|
|
if (header->filetype == MH_EXECUTE) {
|
|
executableHeader = header;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!executableHeader)
|
|
return @"";
|
|
|
|
BOOL is64bit = executableHeader->magic == MH_MAGIC_64 || executableHeader->magic == MH_CIGAM_64;
|
|
uintptr_t cursor = (uintptr_t)executableHeader + (is64bit ? sizeof(struct mach_header_64) : sizeof(struct mach_header));
|
|
const struct segment_command *segmentCommand = NULL;
|
|
for (uint32_t i = 0; i < executableHeader->ncmds; i++, cursor += segmentCommand->cmdsize) {
|
|
segmentCommand = (struct segment_command *)cursor;
|
|
if (segmentCommand->cmd == LC_UUID) {
|
|
const struct uuid_command *uuidCommand = (const struct uuid_command *)segmentCommand;
|
|
const uint8_t *uuid = uuidCommand->uuid;
|
|
return [[NSString stringWithFormat:@"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
|
|
uuid[0], uuid[1], uuid[2], uuid[3],
|
|
uuid[4], uuid[5], uuid[6], uuid[7],
|
|
uuid[8], uuid[9], uuid[10], uuid[11],
|
|
uuid[12], uuid[13], uuid[14], uuid[15]]
|
|
lowercaseString];
|
|
}
|
|
}
|
|
|
|
return @"";
|
|
}
|
|
|
|
#if !defined (HOCKEYSDK_CONFIGURATION_ReleaseCrashOnlyExtensions)
|
|
- (UIWindow *)findVisibleWindow {
|
|
UIWindow *visibleWindow = [UIApplication sharedApplication].keyWindow;
|
|
|
|
if (!(visibleWindow.hidden)) {
|
|
return visibleWindow;
|
|
}
|
|
|
|
// if the rootViewController property (available >= iOS 4.0) of the main window is set, we present the modal view controller on top of the rootViewController
|
|
NSArray *windows = [[UIApplication sharedApplication] windows];
|
|
for (UIWindow *window in windows) {
|
|
if (!window.hidden && !visibleWindow) {
|
|
visibleWindow = window;
|
|
}
|
|
if ([UIWindow instancesRespondToSelector:@selector(rootViewController)]) {
|
|
if (!(window.hidden) && ([window rootViewController])) {
|
|
visibleWindow = window;
|
|
BITHockeyLogDebug(@"INFO: UIWindow with rootViewController found: %@", visibleWindow);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return visibleWindow;
|
|
}
|
|
|
|
/**
|
|
* Provide a custom UINavigationController with customized appearance settings
|
|
*
|
|
* @param viewController The root viewController
|
|
* @param modalPresentationStyle The modal presentation style
|
|
*
|
|
* @return A UINavigationController
|
|
*/
|
|
- (UINavigationController *)customNavigationControllerWithRootViewController:(UIViewController *)viewController presentationStyle:(UIModalPresentationStyle)modalPresentationStyle {
|
|
UINavigationController *navController = [[UINavigationController alloc] initWithRootViewController:viewController];
|
|
navController.navigationBar.barStyle = self.barStyle;
|
|
if (self.navigationBarTintColor) {
|
|
navController.navigationBar.tintColor = self.navigationBarTintColor;
|
|
} else {
|
|
// in case of iOS 7 we overwrite the tint color on the navigation bar
|
|
if ([UIWindow instancesRespondToSelector:NSSelectorFromString(@"tintColor")]) {
|
|
[navController.navigationBar setTintColor:BIT_RGBCOLOR(0, 122, 255)];
|
|
}
|
|
}
|
|
navController.modalPresentationStyle = modalPresentationStyle;
|
|
|
|
return navController;
|
|
}
|
|
|
|
- (UIViewController *)visibleWindowRootViewController {
|
|
UIViewController *parentViewController = nil;
|
|
id strongDelegate = [BITHockeyManager sharedHockeyManager].delegate;
|
|
if ([strongDelegate respondsToSelector:@selector(viewControllerForHockeyManager:componentManager:)]) {
|
|
parentViewController = [strongDelegate viewControllerForHockeyManager:[BITHockeyManager sharedHockeyManager] componentManager:self];
|
|
}
|
|
|
|
UIWindow *visibleWindow = [self findVisibleWindow];
|
|
|
|
if (parentViewController == nil) {
|
|
parentViewController = [visibleWindow rootViewController];
|
|
}
|
|
|
|
// use topmost modal view
|
|
while (parentViewController.presentedViewController) {
|
|
parentViewController = parentViewController.presentedViewController;
|
|
}
|
|
|
|
// special addition to get rootViewController from three20 which has it's own controller handling
|
|
if (NSClassFromString(@"TTNavigator")) {
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
|
|
UIViewController *ttParentViewController = nil;
|
|
ttParentViewController = [[NSClassFromString(@"TTNavigator") performSelector:(NSSelectorFromString(@"navigator"))] visibleViewController];
|
|
if (ttParentViewController)
|
|
parentViewController = ttParentViewController;
|
|
#pragma clang diagnostic pop
|
|
}
|
|
|
|
return parentViewController;
|
|
}
|
|
|
|
- (void)showAlertController:(UIViewController *)alertController {
|
|
|
|
// always execute this on the main thread
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if (_presentAlert) {
|
|
_presentAlert(alertController);
|
|
} else {
|
|
UIViewController *parentViewController = [self visibleWindowRootViewController];
|
|
|
|
// as per documentation this only works if called from within viewWillAppear: or viewDidAppear:
|
|
// in tests this also worked fine on iOS 6 and 7 but not on iOS 5 so we are still trying this
|
|
if ([parentViewController isKindOfClass:NSClassFromString(@"UIAlertController")] || [parentViewController isBeingPresented]) {
|
|
BITHockeyLogWarning(@"WARNING: There is already a view controller being presented onto the parentViewController. Delaying presenting the new view controller by 0.5s.");
|
|
[self performSelector:@selector(showAlertController:) withObject:alertController afterDelay:0.5];
|
|
return;
|
|
}
|
|
|
|
if (parentViewController) {
|
|
[parentViewController presentViewController:alertController animated:YES completion:nil];
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
- (void)showView:(UIViewController *)viewController {
|
|
if (_presentView) {
|
|
_presentView(viewController);
|
|
return;
|
|
}
|
|
|
|
// if we compile Crash only, then BITHockeyBaseViewController is not included
|
|
// in the headers and will cause a warning with the modulemap file
|
|
#if HOCKEYSDK_FEATURE_AUTHENTICATOR || HOCKEYSDK_FEATURE_UPDATES || HOCKEYSDK_FEATURE_FEEDBACK
|
|
UIViewController *parentViewController = [self visibleWindowRootViewController];
|
|
|
|
// as per documentation this only works if called from within viewWillAppear: or viewDidAppear:
|
|
// in tests this also worked fine on iOS 6 and 7 but not on iOS 5 so we are still trying this
|
|
if ([parentViewController isBeingPresented]) {
|
|
BITHockeyLogDebug(@"INFO: There is already a view controller being presented onto the parentViewController. Delaying presenting the new view controller by 0.5s.");
|
|
[self performSelector:@selector(showView:) withObject:viewController afterDelay:0.5];
|
|
return;
|
|
}
|
|
|
|
if (self.navController != nil) self.navController = nil;
|
|
|
|
self.navController = [self customNavigationControllerWithRootViewController:viewController presentationStyle:self.modalPresentationStyle];
|
|
|
|
if (parentViewController) {
|
|
self.navController.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
|
|
|
|
// page sheet for the iPad
|
|
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
|
|
self.navController.modalPresentationStyle = UIModalPresentationFormSheet;
|
|
}
|
|
|
|
if ([viewController isKindOfClass:[BITHockeyBaseViewController class]])
|
|
[(BITHockeyBaseViewController *)viewController setModalAnimated:YES];
|
|
|
|
[parentViewController presentViewController:self.navController animated:YES completion:nil];
|
|
} else {
|
|
// if not, we add a subview to the window. A bit hacky but should work in most circumstances.
|
|
// Also, we don't get a nice animation for free, but hey, this is for beta not production users ;)
|
|
UIWindow *visibleWindow = [self findVisibleWindow];
|
|
|
|
BITHockeyLogDebug(@"INFO: No rootViewController found, using UIWindow-approach: %@", visibleWindow);
|
|
if ([viewController isKindOfClass:[BITHockeyBaseViewController class]])
|
|
[(BITHockeyBaseViewController *)viewController setModalAnimated:NO];
|
|
[visibleWindow addSubview:self.navController.view];
|
|
}
|
|
#endif /* HOCKEYSDK_FEATURE_AUTHENTICATOR || HOCKEYSDK_FEATURE_UPDATES || HOCKEYSDK_FEATURE_FEEDBACK */
|
|
}
|
|
#endif // HOCKEYSDK_CONFIGURATION_ReleaseCrashOnlyExtensions && HOCKEYSDK_CONFIGURATION_RelaseCrashOnlyWatchOS
|
|
|
|
|
|
- (BOOL)addStringValueToKeychain:(NSString *)stringValue forKey:(NSString *)key {
|
|
if (!key || !stringValue)
|
|
return NO;
|
|
|
|
NSError *error = nil;
|
|
return [BITKeychainUtils storeUsername:key
|
|
andPassword:stringValue
|
|
forServiceName:bit_keychainHockeySDKServiceName()
|
|
updateExisting:YES
|
|
error:&error];
|
|
}
|
|
|
|
- (BOOL)addStringValueToKeychainForThisDeviceOnly:(NSString *)stringValue forKey:(NSString *)key {
|
|
if (!key || !stringValue)
|
|
return NO;
|
|
|
|
NSError *error = nil;
|
|
return [BITKeychainUtils storeUsername:key
|
|
andPassword:stringValue
|
|
forServiceName:bit_keychainHockeySDKServiceName()
|
|
updateExisting:YES
|
|
accessibility:kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
|
|
error:&error];
|
|
}
|
|
|
|
- (NSString *)stringValueFromKeychainForKey:(NSString *)key {
|
|
if (!key)
|
|
return nil;
|
|
|
|
NSError *error = nil;
|
|
return [BITKeychainUtils getPasswordForUsername:key
|
|
andServiceName:bit_keychainHockeySDKServiceName()
|
|
error:&error];
|
|
}
|
|
|
|
- (BOOL)removeKeyFromKeychain:(NSString *)key {
|
|
NSError *error = nil;
|
|
return [BITKeychainUtils deleteItemForUsername:key
|
|
andServiceName:bit_keychainHockeySDKServiceName()
|
|
error:&error];
|
|
}
|
|
|
|
|
|
#pragma mark - Manager Control
|
|
|
|
- (void)startManager {
|
|
}
|
|
|
|
#pragma mark - Helpers
|
|
|
|
- (NSDate *)parseRFC3339Date:(NSString *)dateString {
|
|
NSDate *date = nil;
|
|
NSError *error = nil;
|
|
if (![self.rfc3339Formatter getObjectValue:&date forString:dateString range:nil error:&error]) {
|
|
BITHockeyLogWarning(@"WARNING: Invalid date '%@' string: %@", dateString, error);
|
|
}
|
|
|
|
return date;
|
|
}
|
|
|
|
+ (void)setPresentAlert:(void (^)(UIAlertController *))presentAlert {
|
|
_presentAlert = [presentAlert copy];
|
|
}
|
|
|
|
+ (void)setPresentView:(void (^)(UIViewController *))presentView {
|
|
_presentView = [presentView copy];
|
|
}
|
|
|
|
@end
|