// _____ _ // |_ _| |_ _ _ ___ ___ _ __ __ _ // | | | ' \| '_/ -_) -_) ' \/ _` |_ // |_| |_||_|_| \___\___|_|_|_\__,_(_) // // Threema iOS Client // Copyright (c) 2015-2020 Threema GmbH // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License, version 3, // as published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #import "SplashViewController.h" #import "FLAnimatedImageView.h" #import "FLAnimatedImage.h" #import "BundleUtil.h" #import "RectUtil.h" #import #import "NibUtil.h" #import "RandomSeedViewController.h" #import "MyIdentityStore.h" #import "IdentityBackupStore.h" #import "ServerAPIConnector.h" #import "MBProgressHUD.h" #import "ParallaxPageViewController.h" #import "AppDelegate.h" #import "UIDefines.h" #import "UserSettings.h" #import "NibUtil.h" #import "ConfirmIDViewController.h" #import "PickNicknameViewController.h" #import "LinkIDViewController.h" #import "SyncContactsViewController.h" #import "CompletedIDViewController.h" #import "RestoreIdentityViewController.h" #import "IntroQuestionView.h" #import "LicenseStore.h" #import "EnterLicenseViewController.h" #import "MDMSetup.h" #import "ContactStore.h" #import "Contact.h" #import "GatewayAvatarMaker.h" #import "Threema-Swift.h" #import "ConversationUtils.h" #import "WorkDataFetcher.h" #import @interface SplashViewController () @property FLAnimatedImageView *animatedView; @property RandomSeedViewController *randomSeedViewController; @property RestoreOptionDataViewController *restoreOptionDataViewController; @property RestoreOptionBackupViewController *restoreOptionBackupViewController; @property RestoreSafeViewController *restoreSafeViewController; @property RestoreIdentityViewController *restoreIdentityViewController; @property IntroQuestionView *acceptPrivacyPolicyQuestionView; @property IntroQuestionView *existingBackupQuestionView; @property IntroQuestionView *existingIdQuestionView; @property CGFloat parallaxDeltaX; @property CGFloat bgImagescale; @property NSString *idBackup; @property BOOL triggeredSetup; @property BOOL isRestoreOptionBackupDisplayed; @property (assign) BOOL hasDataOnDevice; @end @implementation SplashViewController { MDMSetup *mdmSetup; } - (instancetype)initWithCoder:(NSCoder *)coder { self = [super initWithCoder:coder]; if (self) { mdmSetup = [[MDMSetup alloc] initWithSetup:YES]; } return self; } - (void)viewDidLoad { [super viewDidLoad]; if ([MyIdentityStore sharedMyIdentityStore].pendingCreateID) { _bgImagescale = 1.5; } else { // during intro image will be zoomed _bgImagescale = 1.2; } [self setupControls]; [self setupBackgroundView]; [self setNeedsStatusBarAppearanceUpdate]; [mdmSetup loadIDCreationValues]; // Work logo if ([LicenseStore requiresLicenseKey]) { _threemaLogoView.image = [BundleUtil imageNamed:@"ThreemaWork"]; } } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; _restoreButton.hidden = [mdmSetup disableBackups]; _threemaLogoView.hidden = YES; } - (void)setupBackgroundView { CGFloat width = self.view.frame.size.width; CGFloat height = self.view.frame.size.height; _parallaxDeltaX = - width*_bgImagescale/20.0; CGRect bgRect = CGRectMake(0.0, 0.0, width*(_bgImagescale*2), height*_bgImagescale); bgRect = [RectUtil rect:bgRect centerIn:self.view.frame]; // fix for iPad landscape _bgView.frame = CGRectMake(bgRect.origin.x, bgRect.origin.y, bgRect.size.width, bgRect.size.height); [self.view sendSubviewToBack:_bgView]; } - (void)setupAnimatedView { if (_animatedView == nil) { CGFloat logoScale = 2.2; CGRect rect = CGRectMake(0, 0, 250.0/logoScale, 300.0/logoScale); rect = [RectUtil rect:rect centerIn:self.view.frame round:YES]; _animatedView = [[FLAnimatedImageView alloc] initWithFrame:rect]; NSString *animationName = nil; if ([LicenseStore requiresLicenseKey]) { animationName = @"logoAnimation_work"; } else { animationName = @"logoAnimation"; } NSURL *url = [BundleUtil URLForResource:animationName withExtension:@"gif"]; FLAnimatedImage *image = [FLAnimatedImage animatedImageWithGIFData:[NSData dataWithContentsOfURL:url] ]; _animatedView.animatedImage = image; _animatedView.delegate = self; } } - (void)setupControls { _setupButton.backgroundColor = [Colors mainThemeDark]; _privacyView.hidden = YES; _privacyView.frame = [RectUtil rect:_privacyView.frame centerHorizontalIn:_containerView.frame]; _controlsView.hidden = YES; _controlsView.frame = [RectUtil rect:_controlsView.frame centerHorizontalIn:_containerView.frame]; _setupButton.layer.cornerRadius = 5; [_setupButton setTitleColor:[Colors white] forState:UIControlStateNormal]; _restoreButton.layer.borderWidth = 1; _restoreButton.layer.borderColor = _setupButton.backgroundColor.CGColor; _restoreButton.layer.cornerRadius = 5; _restoreButton.titleLabel.adjustsFontSizeToFitWidth = YES; _restoreButton.titleLabel.minimumScaleFactor = 0.6; [_restoreButton setTitleColor:[Colors mainThemeDark] forState:UIControlStateNormal]; _privacyPolicySwitch.onTintColor = [Colors mainThemeDark]; _welcomeLabel.text = [BundleUtil localizedStringForKey:@"lets_get_started"]; NSString *privacyPolicyText; if ([LicenseStore requiresLicenseKey]) { [_setupButton setTitle:[BundleUtil localizedStringForKey:@"setup_threema_work"] forState:UIControlStateNormal]; privacyPolicyText = [BundleUtil localizedStringForKey:@"privacy_policy_about_work"]; } else { [_setupButton setTitle:[BundleUtil localizedStringForKey:@"setup_threema"] forState:UIControlStateNormal]; privacyPolicyText = [BundleUtil localizedStringForKey:@"privacy_policy_about"]; } [_restoreButton setTitle:[BundleUtil localizedStringForKey:@"restore_id"] forState:UIControlStateNormal]; _privacyPolicyInfo.font = [UIFont systemFontOfSize:16.0]; _privacyPolicyInfo.tapDelegate = self; NSDictionary *normalAttributes = @{NSFontAttributeName: _privacyPolicyInfo.font, NSForegroundColorAttributeName: [UIColor whiteColor]}; NSDictionary *linkAttributes = @{@"ZSWTappableLabelTappableRegionAttributeName": @YES, @"ZSWTappableLabelHighlightedForegroundAttributeName": [Colors red], NSForegroundColorAttributeName: [Colors privacyPolicyLink], NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle), @"NSTextCheckingResult": @1 }; NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:privacyPolicyText attributes:normalAttributes]; CGRect infoRect = [attributedString boundingRectWithSize:CGSizeMake(_privacyView.frame.size.width, 400.0) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil]; if (MAX([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width) <= 480) { /* iPhone 4s */ _welcomeLabel.frame = CGRectMake(_welcomeLabel.frame.origin.x, _welcomeLabel.frame.origin.y - 20.0, _welcomeLabel.frame.size.width, _welcomeLabel.frame.size.height); _privacyPolicyInfo.frame = CGRectMake(_privacyPolicyInfo.frame.origin.x, _privacyPolicyInfo.frame.origin.y - 50.0, infoRect.size.width, infoRect.size.height + 20.0); } else { _privacyPolicyInfo.frame = CGRectMake(_privacyPolicyInfo.frame.origin.x, _privacyPolicyInfo.frame.origin.y, infoRect.size.width, infoRect.size.height + 20.0); } [attributedString addAttributes:linkAttributes range:[privacyPolicyText rangeOfString:[BundleUtil localizedStringForKey:@"privacy_policy_about_link"]]]; _privacyPolicyInfo.attributedText = attributedString; _privacyPolicyLabel.text = [BundleUtil localizedStringForKey:@"accept_privacy_policy"]; _privacyPolicySwitch.accessibilityLabel = [BundleUtil localizedStringForKey:@"privacy_policy_switch"]; CGRect labelRect = [_privacyPolicyLabel.text boundingRectWithSize:CGSizeMake(_privacyView.frame.size.width - _privacyPolicySwitch.frame.size.width - 15.0, 400.0) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:normalAttributes context:nil]; _privacyPolicyLabel.frame = CGRectMake(_privacyPolicySwitch.frame.size.width + 15.0, _privacyPolicyInfo.frame.origin.y + _privacyPolicyInfo.frame.size.height + 15.0, labelRect.size.width, labelRect.size.height); if (_privacyPolicyLabel.frame.size.height > _privacyPolicySwitch.frame.size.height) { _privacyPolicySwitch.frame = CGRectMake(0.0, _privacyPolicyLabel.frame.origin.y + ((_privacyPolicyLabel.frame.size.height - _privacyPolicySwitch.frame.size.height) /2), _privacyPolicySwitch.frame.size.width, _privacyPolicySwitch.frame.size.height); } else { _privacyPolicySwitch.frame = CGRectMake(0.0, _privacyPolicyInfo.frame.origin.y + _privacyPolicyInfo.frame.size.height + 8.0, _privacyPolicySwitch.frame.size.width, _privacyPolicySwitch.frame.size.height); } AppSetupState *appSetupState = [[AppSetupState alloc] init]; [self setHasDataOnDevice:[appSetupState existsDatabaseFile]]; } - (BOOL)prefersStatusBarHidden { return YES; } - (BOOL)shouldAutorotate { return NO; } -(UIInterfaceOrientationMask)supportedInterfaceOrientations { if (SYSTEM_IS_IPAD) { return UIInterfaceOrientationMaskAll; } else { return UIInterfaceOrientationMaskPortrait; } } - (void)viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; if (@available(iOS 11.0, *)) { _threemaLogoView.frame = CGRectMake(_threemaLogoView.frame.origin.x, self.view.safeAreaLayoutGuide.layoutFrame.origin.y + 26.0, _threemaLogoView.frame.size.width, _threemaLogoView.frame.size.height); } [self presentUI]; } - (void)presentUI { AppSetupState *appSetupState = [[AppSetupState alloc] initWithMyIdentityStore:[MyIdentityStore sharedMyIdentityStore]]; if ([[LicenseStore sharedLicenseStore] isValid] == NO) { [self performLicenseCheck]; } else if ([mdmSetup isSafeRestoreForce]) { [self showRestoreSafeViewController:[self hasDataOnDevice]]; [self slideOut:self fromRightToLeft:YES onCompletion:nil]; [self slideIn:_restoreSafeViewController fromLeftToRight:YES onCompletion:nil]; } else if ([mdmSetup hasIDBackup] && appSetupState.isAppSetupCompleted == false) { [self restoreIDFromMDM]; } else if ([MyIdentityStore sharedMyIdentityStore].pendingCreateID) { [self presentPageViewController]; } else { _threemaLogoView.hidden = NO; [self setupAnimatedView]; [self checkRefreshStoreReceipt]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1200 * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{ if (_animatedView.superview == nil) { [_containerView addSubview:_animatedView]; } }); } } - (void)slidePrivacyControlsIn { CGRect viewFrame; if (@available(iOS 11.0, *)) { viewFrame = self.view.safeAreaLayoutGuide.layoutFrame; } else { viewFrame = self.view.frame; } CGRect privacyTargetRect; if (MAX([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width) <= 480) { /* iPhone 4s */ privacyTargetRect = [RectUtil setYPositionOf:_privacyView.frame y:120.0]; } else { privacyTargetRect = [RectUtil setYPositionOf:_privacyView.frame y:170.0]; } CGRect animationTargetRect = [RectUtil setYPositionOf:_animatedView.frame y:privacyTargetRect.origin.y - _animatedView.frame.size.height]; CGRect privacySourceRect = [RectUtil setYPositionOf:_privacyView.frame y:_privacyView.frame.origin.y]; CGRect controlsTargetRect; if (MAX([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width) <= 480) { /* iPhone 4s */ controlsTargetRect = [RectUtil setYPositionOf:_controlsView.frame y:privacyTargetRect.origin.y + privacyTargetRect.size.height - 40.0]; } else { controlsTargetRect = [RectUtil setYPositionOf:_controlsView.frame y:privacyTargetRect.origin.y + privacyTargetRect.size.height]; } CGRect controlsSourceRect = [RectUtil setYPositionOf:_controlsView.frame y:viewFrame.size.height]; _privacyView.hidden = NO; _privacyView.alpha = 0.0; _privacyView.frame = privacySourceRect; _controlsView.hidden = NO; _controlsView.alpha = 0.0; _controlsView.frame = controlsSourceRect; [UIView animateWithDuration:1.2 delay:0.0 usingSpringWithDamping:0.9 initialSpringVelocity:10.0 options:0 animations:^{ _privacyView.alpha = 1.0; _privacyView.frame = privacyTargetRect; _animatedView.frame = animationTargetRect; _controlsView.alpha = 1.0; _controlsView.frame = controlsTargetRect; } completion:^(BOOL finished) { UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, self.welcomeLabel); ;//nop }]; } - (void)restoreIDFromMDM { [self setAcceptPrivacyPolicyValues:AcceptPrivacyPolicyVariantImplicitly]; [mdmSetup restoreIDBackupOnCompletion:^{ [self presentPageViewController]; } onError:^(NSError *error) { _restoreIdentityViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RestoreIdentityViewController"]; _restoreIdentityViewController.delegate = self; _restoreIdentityViewController.backupData = mdmSetup.idBackup; _restoreIdentityViewController.passwordData = mdmSetup.idBackupPassword; [_restoreIdentityViewController setup]; [self slideOut:self fromRightToLeft:YES onCompletion:nil]; [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:^{ // make sure controls are visible _privacyView.alpha = 1.0; _privacyView.hidden = NO; _privacyView.frame = [RectUtil rect:_privacyView.frame centerIn:self.view.frame]; // show error message [_restoreIdentityViewController handleError:error]; }]; }]; } - (void)performLicenseCheck { LicenseStore *licenseStore = [LicenseStore sharedLicenseStore]; [licenseStore performLicenseCheckWithCompletion:^(BOOL success) { dispatch_async(dispatch_get_main_queue(), ^{ if (success) { [self presentUI]; } else { // present anyway, to also fail early if there is no network connection [self presentLicenseViewController]; } }); }]; } - (void)presentLicenseViewController { UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"License" bundle:nil]; EnterLicenseViewController *viewController = [storyboard instantiateInitialViewController]; viewController.delegate = self; viewController.modalPresentationStyle = UIModalPresentationFullScreen; [self presentViewController:viewController animated:NO completion:nil]; } - (void)presentPageViewController { ConfirmIDViewController *confirmVc = [self.storyboard instantiateViewControllerWithIdentifier:@"ConfirmIDViewController"]; SafeViewController *safeVc = [self.storyboard instantiateViewControllerWithIdentifier:@"SafeSetup"]; PickNicknameViewController *pickNicknameVc = [self.storyboard instantiateViewControllerWithIdentifier:@"PickNicknameViewController"]; LinkIDViewController *linkIdVc = [self.storyboard instantiateViewControllerWithIdentifier:@"LinkIDViewController"]; SyncContactsViewController *syncVc = [self.storyboard instantiateViewControllerWithIdentifier:@"SyncContactsViewController"]; CompletedIDViewController *complededVc = [self.storyboard instantiateViewControllerWithIdentifier:@"CompletedIDViewController"]; complededVc.delegate = self; ParallaxPageViewController *pageVc = [self.storyboard instantiateViewControllerWithIdentifier:@"ParallaxPageViewController"]; if ([mdmSetup skipWizard]) { pageVc.viewControllers = @[complededVc]; } else { if ([mdmSetup isSafeBackupDisable] || ([mdmSetup isSafeBackupForce] && [mdmSetup isSafeBackupPasswordPreset])) { pageVc.viewControllers = @[confirmVc, pickNicknameVc, linkIdVc, syncVc, complededVc]; } else { pageVc.viewControllers = @[confirmVc, safeVc, pickNicknameVc, linkIdVc, syncVc, complededVc]; } } pageVc.bgView = _bgView; pageVc.parallaxFactor = [NSNumber numberWithDouble: fabs(_parallaxDeltaX/self.view.frame.size.width)]; pageVc.modalPresentationStyle = UIModalPresentationFullScreen; // make sure to clean up [_randomSeedViewController willMoveToParentViewController:nil]; [_restoreIdentityViewController willMoveToParentViewController:nil]; [self presentViewController:pageVc animated:NO completion:^{ [_randomSeedViewController.view removeFromSuperview]; [_randomSeedViewController removeFromParentViewController]; [_restoreIdentityViewController.view removeFromSuperview]; [_restoreIdentityViewController removeFromParentViewController]; }]; } - (void)showApplicaitonUI { [[AppDelegate sharedAppDelegate] completedIDSetup]; } #pragma mark - IntroQuestionView - (void)showAlertAcceptPrivacyPolicy { if (_acceptPrivacyPolicyQuestionView == nil) { _acceptPrivacyPolicyQuestionView = (IntroQuestionView *)[NibUtil loadViewFromNibWithName:@"IntroQuestionView"]; if ([LicenseStore requiresLicenseKey]) { _acceptPrivacyPolicyQuestionView.questionLabel.text = [BundleUtil localizedStringForKey:@"privacy_policy_alert_text_work"]; } else { _acceptPrivacyPolicyQuestionView.questionLabel.text = [BundleUtil localizedStringForKey:@"privacy_policy_alert_text"]; } _acceptPrivacyPolicyQuestionView.delegate = self; _acceptPrivacyPolicyQuestionView.showOnlyOkButton = YES; _acceptPrivacyPolicyQuestionView.frame = [RectUtil rect:_acceptPrivacyPolicyQuestionView.frame centerIn:self.view.frame round:YES]; [self.view addSubview:_acceptPrivacyPolicyQuestionView]; } [self showMessageView:_acceptPrivacyPolicyQuestionView]; } - (void)hideAcceptPrivacyPolicyQuestion { [self hideMessageView:_acceptPrivacyPolicyQuestionView]; } - (void)showIDBackupQuestion { if (_existingBackupQuestionView == nil) { _existingBackupQuestionView = (IntroQuestionView *)[NibUtil loadViewFromNibWithName:@"IntroQuestionView"]; _existingBackupQuestionView.tag = 1; _existingBackupQuestionView.questionLabel.text = [BundleUtil localizedStringForKey:@"backup_found_message"];; _existingBackupQuestionView.delegate = self; _existingBackupQuestionView.frame = [RectUtil rect:_existingBackupQuestionView.frame centerIn:self.view.frame round:YES]; [self.view addSubview:_existingBackupQuestionView]; } [self showMessageView:_existingBackupQuestionView]; } - (void)hideIDBackupQuestion { [self hideMessageView:_existingBackupQuestionView]; } - (void)showIDExistsQuestion { if (_existingIdQuestionView == nil) { _existingIdQuestionView = (IntroQuestionView *)[NibUtil loadViewFromNibWithName:@"IntroQuestionView"]; _existingIdQuestionView.tag = 2; _existingIdQuestionView.questionLabel.text = [[NSString alloc] initWithFormat:[BundleUtil localizedStringForKey:@"id_exists"], [[MyIdentityStore sharedMyIdentityStore] identity]]; _existingIdQuestionView.delegate = self; _existingIdQuestionView.frame = [RectUtil rect:_existingIdQuestionView.frame centerIn:self.view.frame round:YES]; [self.view addSubview:_existingIdQuestionView]; } [self showMessageView:_existingIdQuestionView]; } - (void)hideIDExistsQuestion { [self hideMessageView:_existingIdQuestionView]; } #pragma mark - FLAnimatedImageViewDelegate - (void)animatedImageViewWillDrawFrame:(NSUInteger)frameIndex { if ((int)frameIndex == 0 && _privacyView.hidden == NO) { // stay at last frame _animatedView.currentFrameIndex = 78; } else if ((int)frameIndex == 0) { CGFloat duration = 1.0; UIViewAnimationOptions options = UIViewAnimationOptionCurveEaseInOut; [UIView animateWithDuration:duration delay:0.0 options:options animations:^{ _bgImagescale = 1.5; [self setupBackgroundView]; } completion:nil]; } else if ((int)frameIndex == 72 && _privacyView.hidden == YES) { [self slidePrivacyControlsIn]; } } #pragma mark - manage views - (void)showSetupViewController { _randomSeedViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RandomSeedViewController"]; _randomSeedViewController.delegate = self; [_randomSeedViewController setup]; [self setAcceptPrivacyPolicyValues:AcceptPrivacyPolicyVariantExplicitly]; } - (void)showRestoreOptionDataViewController { _restoreOptionDataViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RestoreOptionDataViewController"]; _restoreOptionDataViewController.delegate = self; [self setAcceptPrivacyPolicyValues:AcceptPrivacyPolicyVariantExplicitly]; } - (void)showRestoreOptionBackupViewController { _restoreOptionBackupViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RestoreOptionBackupViewController"]; _restoreOptionBackupViewController.delegate = self; _restoreOptionBackupViewController.hasDataOnDevice = [self hasDataOnDevice]; [self setAcceptPrivacyPolicyValues:AcceptPrivacyPolicyVariantExplicitly]; } - (void)showRestoreSafeViewController:(BOOL)doRestoreIdentityOnly { _restoreSafeViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RestoreSafeViewController"]; _restoreSafeViewController.delegate = self; _restoreSafeViewController.restoreIdentityOnly = doRestoreIdentityOnly; } - (void)showRestoreIdentityViewController:(NSString *)backupData { _restoreIdentityViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RestoreIdentityViewController"]; _restoreIdentityViewController.delegate = self; _restoreIdentityViewController.backupData = backupData; } - (void)showRestoreIdentityViewController { if (_privacyPolicySwitch.on) { _triggeredSetup = NO; _isRestoreOptionBackupDisplayed = ([self.childViewControllers count] > 0 && [self.childViewControllers[0] isKindOfClass:[RestoreOptionBackupViewController class]]) != 0 ? YES : NO; if (_isRestoreOptionBackupDisplayed) { [self showRestoreIdentityViewController:nil]; [self slideOut:_restoreOptionBackupViewController fromRightToLeft:YES onCompletion:nil]; [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:nil]; } else if ([self.childViewControllers count] == 0) { [self showRestoreIdentityViewController:nil]; [self slideOut:self fromRightToLeft:YES onCompletion:nil]; [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:nil]; } } else { [self showAlertAcceptPrivacyPolicy]; } } - (void)slideIn:(UIViewController *)child fromLeftToRight:(BOOL)toRight onCompletion:(void(^)(void))onCompletion { UIView *childView; if ([child isKindOfClass:[SplashViewController class]]) { childView = _containerView; } else { [self addChildViewController:child]; childView = child.view; [self.view addSubview:childView]; } //start position if (toRight) { childView.frame = [RectUtil setXPositionOf:childView.frame x:self.view.frame.size.width]; } else { childView.frame = [RectUtil setXPositionOf:childView.frame x:self.view.frame.size.width * -1.0]; } [child beginAppearanceTransition:YES animated:YES]; [UIView animateWithDuration:0.5 animations:^{ //end position childView.frame = [RectUtil setXPositionOf:childView.frame x:0]; CGFloat parallaxFactor = toRight == YES ? 1.0 : -1.0; _bgView.frame = [RectUtil offsetRect:_bgView.frame byX:_parallaxDeltaX*parallaxFactor byY:0.0]; } completion:^(BOOL finished) { [child endAppearanceTransition]; [child didMoveToParentViewController:self]; if (onCompletion) { onCompletion(); } }]; } - (void)slideOut:(UIViewController *)child fromRightToLeft:(BOOL)toLeft onCompletion:(void(^)(void))onCompletion { UIView *childView; if ([child isKindOfClass:[SplashViewController class]]) { childView = _containerView; } else { childView = child.view; } //start position childView.frame = [RectUtil setXPositionOf:childView.frame x:0]; [child beginAppearanceTransition:NO animated:YES]; [UIView animateWithDuration:0.5 animations:^{ //end position if (toLeft) { childView.frame = [RectUtil setXPositionOf:childView.frame x:self.view.frame.size.width * -1.0]; } else { childView.frame = [RectUtil setXPositionOf:childView.frame x:self.view.frame.size.width]; } } completion:^(BOOL finished) { [child endAppearanceTransition]; if (![child isKindOfClass:[SplashViewController class]]) { [childView removeFromSuperview]; [child removeFromParentViewController]; } if (onCompletion) { onCompletion(); } }]; } - (void)createIdentity { [MBProgressHUD showHUDAddedTo:self.view animated:YES]; [[[ServerAPIConnector alloc] init] createIdentityWithStore:[MyIdentityStore sharedMyIdentityStore] onCompletion:^(MyIdentityStore *store) { [MBProgressHUD hideHUDForView:self.view animated:YES]; store.pendingCreateID = YES; [self presentPageViewController]; } onError:^(NSError *error) { [MBProgressHUD hideHUDForView:self.view animated:YES]; UIAlertController *errAlert = [UIAlertController alertControllerWithTitle:error.localizedDescription message:error.localizedFailureReason preferredStyle:UIAlertControllerStyleAlert]; [errAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"try_again", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self createIdentity]; }]]; [[[AppDelegate sharedAppDelegate] currentTopViewController] presentViewController:errAlert animated:YES completion:nil]; }]; } - (void)setAcceptPrivacyPolicyValues:(int)variant { [[UserSettings sharedUserSettings] setAcceptedPrivacyPolicyDate:[NSDate date]]; [[UserSettings sharedUserSettings] setAcceptedPrivacyPolicyVariant:variant]; } #pragma mark - private - (NSString *)getIDBackup { NSString *backupData = [IdentityBackupStore loadIdentityBackup]; if (backupData != nil && [[MyIdentityStore sharedMyIdentityStore] isValidBackupFormat:backupData]) { return backupData; } return nil; } - (BOOL)checkForIDBackup { _idBackup = [self getIDBackup]; if (_idBackup) { [self showIDBackupQuestion]; return YES; } return NO; } - (BOOL)checkForIDExists { if ([[MyIdentityStore sharedMyIdentityStore] isProvisioned]) { [self showIDExistsQuestion]; return YES; } return NO; } - (void)addEchoEchoToContacts { [[ContactStore sharedContactStore] addContactWithIdentity:@"ECHOECHO" verificationLevel:kVerificationLevelUnverified onCompletion:^(Contact *contact, BOOL alreadyExists) { if (contact.isGatewayId) { [[GatewayAvatarMaker gatewayAvatarMaker] loadAndSaveAvatarForId:contact.identity]; } } onError:^(NSError *error) { // do nothing }]; } - (void)checkRefreshStoreReceipt { if ([LicenseStore requiresLicenseKey]) { return; } NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL]; if (receiptUrl && ![[NSFileManager defaultManager] fileExistsAtPath:receiptUrl.path]) { // No receipt available; try to refresh SKReceiptRefreshRequest *refreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil]; [refreshRequest start]; } } #pragma mark - IBActions - (IBAction)setupAction:(id)sender { if (_privacyPolicySwitch.on) { _triggeredSetup = YES; // Check for ID Export, if Threema Work or if Threema and has no existing ID if ([LicenseStore requiresLicenseKey] || (![LicenseStore requiresLicenseKey] && ![self checkForIDExists])) { if ([self checkForIDBackup] == NO) { [self showSetupViewController]; [self slideOut:self fromRightToLeft:YES onCompletion:nil]; [self slideIn:_randomSeedViewController fromLeftToRight:YES onCompletion:nil]; } } } else { [self showAlertAcceptPrivacyPolicy]; } } - (IBAction)restoreAction:(id)sender { if (_privacyPolicySwitch.on) { _triggeredSetup = NO; if ([mdmSetup isSafeRestoreDisable]) { if ([self checkForIDBackup] == NO) { [self showRestoreIdentityViewController:nil]; [self slideOut:self fromRightToLeft:YES onCompletion:nil]; [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:nil]; } } else { if ([self hasDataOnDevice] == YES) { [self showRestoreOptionDataViewController]; [self slideOut:self fromRightToLeft:YES onCompletion:nil]; [self slideIn:_restoreOptionDataViewController fromLeftToRight:YES onCompletion:nil]; } else { [self showRestoreOptionBackupViewController]; [self slideOut:self fromRightToLeft:YES onCompletion:nil]; [self slideIn:_restoreOptionBackupViewController fromLeftToRight:YES onCompletion:nil]; } } } else { [self showAlertAcceptPrivacyPolicy]; } } #pragma mark - IntroQuestionViewDelegate - (void)selectedYes:(IntroQuestionView *)sender { if (sender.tag == 1) { [self hideIDBackupQuestion]; [self showRestoreIdentityViewController:_idBackup]; [self slideOut:self fromRightToLeft:YES onCompletion:nil]; [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:nil]; } else if (sender.tag == 2) { [self hideIDExistsQuestion]; [self presentPageViewController]; } } - (void)selectedNo:(IntroQuestionView *)sender { if (sender.tag == 1) { [self hideIDBackupQuestion]; if (_triggeredSetup) { [self showSetupViewController]; [self slideOut:self fromRightToLeft:YES onCompletion:nil]; [self slideIn:_randomSeedViewController fromLeftToRight:YES onCompletion:nil]; } else { [self showRestoreIdentityViewController:nil]; [self slideOut:self fromRightToLeft:YES onCompletion:nil]; [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:nil]; } } else if (sender.tag == 2) { [self hideIDExistsQuestion]; [self showSetupViewController]; [self slideOut:self fromRightToLeft:YES onCompletion:nil]; [self slideIn:_randomSeedViewController fromLeftToRight:YES onCompletion:nil]; } } - (void)selectedOk:(IntroQuestionView *)sender { [self hideAcceptPrivacyPolicyQuestion]; } #pragma mark - RandomSeedViewControllerDelegate - (void)generatedRandomSeed:(NSData *)seed { [[MyIdentityStore sharedMyIdentityStore] generateKeyPairWithSeed:seed]; [self createIdentity]; } #pragma mark - CompletedIDDelegate - (void)completedIDSetup { if ([[DatabaseManager dbManager] shouldUpdateProtection]) { MyIdentityStore *myIdentityStore = [MyIdentityStore sharedMyIdentityStore]; [myIdentityStore updateConnectionRights]; [[DatabaseManager dbManager] updateProtection]; } // Delete decrypted backup data from application documents folder [FileUtility deleteWithFileUrl: [[DocumentManager applicationDocumentsDirectory] URLByAppendingPathComponent:@"safe-backup.json"]]; AppSetupState *appSetupState = [[AppSetupState alloc] initWithMyIdentityStore:[MyIdentityStore sharedMyIdentityStore]]; [appSetupState appSetupCompleted]; [[ContactStore sharedContactStore] updateAllContactsToCNContact]; [[ContactStore sharedContactStore] updateAllContacts]; [ConversationUtils resetUnreadMessageCount]; [NotificationManager generatePushSettingForAllGroups]; [self addEchoEchoToContacts]; [self showApplicaitonUI]; } #pragma mark - RestoreOptionDataViewControllerDelegate - (void)optionDataKeepLocal { _restoreOptionDataViewController.delegate = nil; [self showRestoreOptionBackupViewController]; [self slideOut:_restoreOptionDataViewController fromRightToLeft:YES onCompletion:nil]; [self slideIn:_restoreOptionBackupViewController fromLeftToRight:YES onCompletion:nil]; } - (void)optionDataCancelled { _restoreOptionDataViewController.delegate = nil; [self slideOut:_restoreOptionDataViewController fromRightToLeft:NO onCompletion:nil]; [self slideIn:self fromLeftToRight:NO onCompletion:nil]; } #pragma mark - RestoreOptionBackupViewControllerDelegate - (void)restoreSafe { _restoreOptionBackupViewController.delegate = nil; [self showRestoreSafeViewController:NO]; [self slideOut:_restoreOptionBackupViewController fromRightToLeft:YES onCompletion:nil]; [self slideIn:_restoreSafeViewController fromLeftToRight:YES onCompletion:nil]; } - (void)restoreIdentityFromSafe { _restoreOptionBackupViewController.delegate = nil; [self showRestoreSafeViewController:YES]; [self slideOut:_restoreOptionBackupViewController fromRightToLeft:YES onCompletion:nil]; [self slideIn:_restoreSafeViewController fromLeftToRight:YES onCompletion:nil]; } - (void)restoreIdentity { _restoreOptionBackupViewController.delegate = nil; [self slideOut:_restoreOptionBackupViewController fromRightToLeft:YES onCompletion:nil]; _isRestoreOptionBackupDisplayed = YES; if ([self checkForIDBackup] == NO) { [self showRestoreIdentityViewController:nil]; [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:nil]; } } - (void)restoreCancelled { _isRestoreOptionBackupDisplayed = NO; _restoreOptionBackupViewController.delegate = nil; [self slideOut:_restoreOptionBackupViewController fromRightToLeft:NO onCompletion:nil]; if ([self hasDataOnDevice]) { [self showRestoreOptionDataViewController]; [self slideIn:_restoreOptionDataViewController fromLeftToRight:NO onCompletion:nil]; } else { [self slideIn:self fromLeftToRight:NO onCompletion:nil]; } } #pragma mark - RestoreSafeViewControllerDelegate - (void)restoreSafeCancelled { _restoreSafeViewController.delegate = nil; [self showRestoreOptionBackupViewController]; [self slideOut:_restoreSafeViewController fromRightToLeft:NO onCompletion:nil]; [self slideIn:_restoreOptionBackupViewController fromLeftToRight:NO onCompletion:nil]; } - (void)restoreSafeDone { _restoreSafeViewController.delegate = nil; [self completedIDSetup]; } #pragma mark - RestoreIdentityViewControllerDelegate - (void)restoreIdentityCancelled { _restoreIdentityViewController.delegate = nil; [self slideOut:_restoreIdentityViewController fromRightToLeft:NO onCompletion:nil]; if (!_triggeredSetup && _isRestoreOptionBackupDisplayed) { [self showRestoreOptionBackupViewController]; [self slideIn:_restoreOptionBackupViewController fromLeftToRight:NO onCompletion:nil]; } else if (!_isRestoreOptionBackupDisplayed) { [self slideIn:self fromLeftToRight:NO onCompletion:nil]; } } - (void)restoreIdentityDone { _restoreIdentityViewController.delegate = nil; [MyIdentityStore sharedMyIdentityStore].pendingCreateID = YES; [self presentPageViewController]; } #pragma mark - EnterLicenseDelegate - (void)licenseConfirmed { [self dismissViewControllerAnimated:YES completion:nil]; } #pragma mark - ZSWTappableLabel delegate - (void)tappableLabel:(ZSWTappableLabel *)tappableLabel tappedAtIndex:(NSInteger)idx withAttributes:(NSDictionary *)attributes { UIStoryboard *storyboard = [AppDelegate getSettingsStoryboard]; UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"PrivacyPolicyViewController"]; UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:vc]; [self presentViewController:nc animated:YES completion:nil]; } @end