SplashViewController.m 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933
  1. // _____ _
  2. // |_ _| |_ _ _ ___ ___ _ __ __ _
  3. // | | | ' \| '_/ -_) -_) ' \/ _` |_
  4. // |_| |_||_|_| \___\___|_|_|_\__,_(_)
  5. //
  6. // Threema iOS Client
  7. // Copyright (c) 2015-2020 Threema GmbH
  8. //
  9. // This program is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU Affero General Public License, version 3,
  11. // as published by the Free Software Foundation.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU Affero General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU Affero General Public License
  19. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. #import "SplashViewController.h"
  21. #import "FLAnimatedImageView.h"
  22. #import "FLAnimatedImage.h"
  23. #import "BundleUtil.h"
  24. #import "RectUtil.h"
  25. #import <QuartzCore/QuartzCore.h>
  26. #import "NibUtil.h"
  27. #import "RandomSeedViewController.h"
  28. #import "MyIdentityStore.h"
  29. #import "IdentityBackupStore.h"
  30. #import "ServerAPIConnector.h"
  31. #import "MBProgressHUD.h"
  32. #import "ParallaxPageViewController.h"
  33. #import "AppDelegate.h"
  34. #import "UIDefines.h"
  35. #import "UserSettings.h"
  36. #import "NibUtil.h"
  37. #import "ConfirmIDViewController.h"
  38. #import "PickNicknameViewController.h"
  39. #import "LinkIDViewController.h"
  40. #import "SyncContactsViewController.h"
  41. #import "CompletedIDViewController.h"
  42. #import "RestoreIdentityViewController.h"
  43. #import "IntroQuestionView.h"
  44. #import "LicenseStore.h"
  45. #import "EnterLicenseViewController.h"
  46. #import "MDMSetup.h"
  47. #import "ContactStore.h"
  48. #import "Contact.h"
  49. #import "GatewayAvatarMaker.h"
  50. #import "Threema-Swift.h"
  51. #import "ConversationUtils.h"
  52. #import "WorkDataFetcher.h"
  53. #import <StoreKit/StoreKit.h>
  54. @interface SplashViewController () <FLAnimatedImageViewDelegate, RandomSeedViewControllerDelegate, CompletedIDDelegate, RestoreOptionDataViewControllerDelegate, RestoreOptionBackupViewControllerDelegate, RestoreSafeViewControllerDelegate, RestoreIdentityViewControllerDelegate, IntroQuestionDelegate, EnterLicenseDelegate, ZSWTappableLabelTapDelegate>
  55. @property FLAnimatedImageView *animatedView;
  56. @property RandomSeedViewController *randomSeedViewController;
  57. @property RestoreOptionDataViewController *restoreOptionDataViewController;
  58. @property RestoreOptionBackupViewController *restoreOptionBackupViewController;
  59. @property RestoreSafeViewController *restoreSafeViewController;
  60. @property RestoreIdentityViewController *restoreIdentityViewController;
  61. @property IntroQuestionView *acceptPrivacyPolicyQuestionView;
  62. @property IntroQuestionView *existingBackupQuestionView;
  63. @property IntroQuestionView *existingIdQuestionView;
  64. @property CGFloat parallaxDeltaX;
  65. @property CGFloat bgImagescale;
  66. @property NSString *idBackup;
  67. @property BOOL triggeredSetup;
  68. @property BOOL isRestoreOptionBackupDisplayed;
  69. @property (assign) BOOL hasDataOnDevice;
  70. @end
  71. @implementation SplashViewController {
  72. MDMSetup *mdmSetup;
  73. }
  74. - (instancetype)initWithCoder:(NSCoder *)coder
  75. {
  76. self = [super initWithCoder:coder];
  77. if (self) {
  78. mdmSetup = [[MDMSetup alloc] initWithSetup:YES];
  79. }
  80. return self;
  81. }
  82. - (void)viewDidLoad {
  83. [super viewDidLoad];
  84. if ([MyIdentityStore sharedMyIdentityStore].pendingCreateID) {
  85. _bgImagescale = 1.5;
  86. } else {
  87. // during intro image will be zoomed
  88. _bgImagescale = 1.2;
  89. }
  90. [self setupControls];
  91. [self setupBackgroundView];
  92. [self setNeedsStatusBarAppearanceUpdate];
  93. [mdmSetup loadIDCreationValues];
  94. // Work logo
  95. if ([LicenseStore requiresLicenseKey]) {
  96. _threemaLogoView.image = [BundleUtil imageNamed:@"ThreemaWork"];
  97. }
  98. }
  99. - (void)viewWillAppear:(BOOL)animated {
  100. [super viewWillAppear:animated];
  101. _restoreButton.hidden = [mdmSetup disableBackups];
  102. _threemaLogoView.hidden = YES;
  103. }
  104. - (void)setupBackgroundView {
  105. CGFloat width = self.view.frame.size.width;
  106. CGFloat height = self.view.frame.size.height;
  107. _parallaxDeltaX = - width*_bgImagescale/20.0;
  108. CGRect bgRect = CGRectMake(0.0, 0.0, width*(_bgImagescale*2), height*_bgImagescale);
  109. bgRect = [RectUtil rect:bgRect centerIn:self.view.frame];
  110. // fix for iPad landscape
  111. _bgView.frame = CGRectMake(bgRect.origin.x, bgRect.origin.y, bgRect.size.width, bgRect.size.height);
  112. [self.view sendSubviewToBack:_bgView];
  113. }
  114. - (void)setupAnimatedView {
  115. if (_animatedView == nil) {
  116. CGFloat logoScale = 2.2;
  117. CGRect rect = CGRectMake(0, 0, 250.0/logoScale, 300.0/logoScale);
  118. rect = [RectUtil rect:rect centerIn:self.view.frame round:YES];
  119. _animatedView = [[FLAnimatedImageView alloc] initWithFrame:rect];
  120. NSString *animationName = nil;
  121. if ([LicenseStore requiresLicenseKey]) {
  122. animationName = @"logoAnimation_work";
  123. } else {
  124. animationName = @"logoAnimation";
  125. }
  126. NSURL *url = [BundleUtil URLForResource:animationName withExtension:@"gif"];
  127. FLAnimatedImage *image = [FLAnimatedImage animatedImageWithGIFData:[NSData dataWithContentsOfURL:url] ];
  128. _animatedView.animatedImage = image;
  129. _animatedView.delegate = self;
  130. }
  131. }
  132. - (void)setupControls {
  133. _setupButton.backgroundColor = [Colors mainThemeDark];
  134. _privacyView.hidden = YES;
  135. _privacyView.frame = [RectUtil rect:_privacyView.frame centerHorizontalIn:_containerView.frame];
  136. _controlsView.hidden = YES;
  137. _controlsView.frame = [RectUtil rect:_controlsView.frame centerHorizontalIn:_containerView.frame];
  138. _setupButton.layer.cornerRadius = 5;
  139. [_setupButton setTitleColor:[Colors white] forState:UIControlStateNormal];
  140. _restoreButton.layer.borderWidth = 1;
  141. _restoreButton.layer.borderColor = _setupButton.backgroundColor.CGColor;
  142. _restoreButton.layer.cornerRadius = 5;
  143. _restoreButton.titleLabel.adjustsFontSizeToFitWidth = YES;
  144. _restoreButton.titleLabel.minimumScaleFactor = 0.6;
  145. [_restoreButton setTitleColor:[Colors mainThemeDark] forState:UIControlStateNormal];
  146. _privacyPolicySwitch.onTintColor = [Colors mainThemeDark];
  147. _welcomeLabel.text = [BundleUtil localizedStringForKey:@"lets_get_started"];
  148. NSString *privacyPolicyText;
  149. if ([LicenseStore requiresLicenseKey]) {
  150. [_setupButton setTitle:[BundleUtil localizedStringForKey:@"setup_threema_work"] forState:UIControlStateNormal];
  151. privacyPolicyText = [BundleUtil localizedStringForKey:@"privacy_policy_about_work"];
  152. } else {
  153. [_setupButton setTitle:[BundleUtil localizedStringForKey:@"setup_threema"] forState:UIControlStateNormal];
  154. privacyPolicyText = [BundleUtil localizedStringForKey:@"privacy_policy_about"];
  155. }
  156. [_restoreButton setTitle:[BundleUtil localizedStringForKey:@"restore_id"] forState:UIControlStateNormal];
  157. _privacyPolicyInfo.font = [UIFont systemFontOfSize:16.0];
  158. _privacyPolicyInfo.tapDelegate = self;
  159. NSDictionary *normalAttributes = @{NSFontAttributeName: _privacyPolicyInfo.font, NSForegroundColorAttributeName: [UIColor whiteColor]};
  160. NSDictionary *linkAttributes = @{@"ZSWTappableLabelTappableRegionAttributeName": @YES,
  161. @"ZSWTappableLabelHighlightedForegroundAttributeName": [Colors red],
  162. NSForegroundColorAttributeName: [Colors privacyPolicyLink],
  163. NSUnderlineStyleAttributeName: @(NSUnderlineStyleSingle),
  164. @"NSTextCheckingResult": @1
  165. };
  166. NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:privacyPolicyText attributes:normalAttributes];
  167. CGRect infoRect = [attributedString boundingRectWithSize:CGSizeMake(_privacyView.frame.size.width, 400.0) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading context:nil];
  168. if (MAX([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width) <= 480) {
  169. /* iPhone 4s */
  170. _welcomeLabel.frame = CGRectMake(_welcomeLabel.frame.origin.x, _welcomeLabel.frame.origin.y - 20.0, _welcomeLabel.frame.size.width, _welcomeLabel.frame.size.height);
  171. _privacyPolicyInfo.frame = CGRectMake(_privacyPolicyInfo.frame.origin.x, _privacyPolicyInfo.frame.origin.y - 50.0, infoRect.size.width, infoRect.size.height + 20.0);
  172. } else {
  173. _privacyPolicyInfo.frame = CGRectMake(_privacyPolicyInfo.frame.origin.x, _privacyPolicyInfo.frame.origin.y, infoRect.size.width, infoRect.size.height + 20.0);
  174. }
  175. [attributedString addAttributes:linkAttributes range:[privacyPolicyText rangeOfString:[BundleUtil localizedStringForKey:@"privacy_policy_about_link"]]];
  176. _privacyPolicyInfo.attributedText = attributedString;
  177. _privacyPolicyLabel.text = [BundleUtil localizedStringForKey:@"accept_privacy_policy"];
  178. _privacyPolicySwitch.accessibilityLabel = [BundleUtil localizedStringForKey:@"privacy_policy_switch"];
  179. 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];
  180. _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);
  181. if (_privacyPolicyLabel.frame.size.height > _privacyPolicySwitch.frame.size.height) {
  182. _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);
  183. } else {
  184. _privacyPolicySwitch.frame = CGRectMake(0.0, _privacyPolicyInfo.frame.origin.y + _privacyPolicyInfo.frame.size.height + 8.0, _privacyPolicySwitch.frame.size.width, _privacyPolicySwitch.frame.size.height);
  185. }
  186. AppSetupState *appSetupState = [[AppSetupState alloc] init];
  187. [self setHasDataOnDevice:[appSetupState existsDatabaseFile]];
  188. }
  189. - (BOOL)prefersStatusBarHidden {
  190. return YES;
  191. }
  192. - (BOOL)shouldAutorotate {
  193. return NO;
  194. }
  195. -(UIInterfaceOrientationMask)supportedInterfaceOrientations {
  196. if (SYSTEM_IS_IPAD) {
  197. return UIInterfaceOrientationMaskAll;
  198. } else {
  199. return UIInterfaceOrientationMaskPortrait;
  200. }
  201. }
  202. - (void)viewDidAppear:(BOOL)animated {
  203. [super viewDidAppear:animated];
  204. if (@available(iOS 11.0, *)) {
  205. _threemaLogoView.frame = CGRectMake(_threemaLogoView.frame.origin.x, self.view.safeAreaLayoutGuide.layoutFrame.origin.y + 26.0, _threemaLogoView.frame.size.width, _threemaLogoView.frame.size.height);
  206. }
  207. [self presentUI];
  208. }
  209. - (void)presentUI {
  210. AppSetupState *appSetupState = [[AppSetupState alloc] initWithMyIdentityStore:[MyIdentityStore sharedMyIdentityStore]];
  211. if ([[LicenseStore sharedLicenseStore] isValid] == NO) {
  212. [self performLicenseCheck];
  213. } else if ([mdmSetup isSafeRestoreForce]) {
  214. [self showRestoreSafeViewController:[self hasDataOnDevice]];
  215. [self slideOut:self fromRightToLeft:YES onCompletion:nil];
  216. [self slideIn:_restoreSafeViewController fromLeftToRight:YES onCompletion:nil];
  217. } else if ([mdmSetup hasIDBackup] && appSetupState.isAppSetupCompleted == false) {
  218. [self restoreIDFromMDM];
  219. } else if ([MyIdentityStore sharedMyIdentityStore].pendingCreateID) {
  220. [self presentPageViewController];
  221. } else {
  222. _threemaLogoView.hidden = NO;
  223. [self setupAnimatedView];
  224. [self checkRefreshStoreReceipt];
  225. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1200 * NSEC_PER_MSEC)), dispatch_get_main_queue(), ^{
  226. if (_animatedView.superview == nil) {
  227. [_containerView addSubview:_animatedView];
  228. }
  229. });
  230. }
  231. }
  232. - (void)slidePrivacyControlsIn {
  233. CGRect viewFrame;
  234. if (@available(iOS 11.0, *)) {
  235. viewFrame = self.view.safeAreaLayoutGuide.layoutFrame;
  236. } else {
  237. viewFrame = self.view.frame;
  238. }
  239. CGRect privacyTargetRect;
  240. if (MAX([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width) <= 480) {
  241. /* iPhone 4s */
  242. privacyTargetRect = [RectUtil setYPositionOf:_privacyView.frame y:120.0];
  243. } else {
  244. privacyTargetRect = [RectUtil setYPositionOf:_privacyView.frame y:170.0];
  245. }
  246. CGRect animationTargetRect = [RectUtil setYPositionOf:_animatedView.frame y:privacyTargetRect.origin.y - _animatedView.frame.size.height];
  247. CGRect privacySourceRect = [RectUtil setYPositionOf:_privacyView.frame y:_privacyView.frame.origin.y];
  248. CGRect controlsTargetRect;
  249. if (MAX([UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width) <= 480) {
  250. /* iPhone 4s */
  251. controlsTargetRect = [RectUtil setYPositionOf:_controlsView.frame y:privacyTargetRect.origin.y + privacyTargetRect.size.height - 40.0];
  252. } else {
  253. controlsTargetRect = [RectUtil setYPositionOf:_controlsView.frame y:privacyTargetRect.origin.y + privacyTargetRect.size.height];
  254. }
  255. CGRect controlsSourceRect = [RectUtil setYPositionOf:_controlsView.frame y:viewFrame.size.height];
  256. _privacyView.hidden = NO;
  257. _privacyView.alpha = 0.0;
  258. _privacyView.frame = privacySourceRect;
  259. _controlsView.hidden = NO;
  260. _controlsView.alpha = 0.0;
  261. _controlsView.frame = controlsSourceRect;
  262. [UIView animateWithDuration:1.2 delay:0.0 usingSpringWithDamping:0.9 initialSpringVelocity:10.0 options:0 animations:^{
  263. _privacyView.alpha = 1.0;
  264. _privacyView.frame = privacyTargetRect;
  265. _animatedView.frame = animationTargetRect;
  266. _controlsView.alpha = 1.0;
  267. _controlsView.frame = controlsTargetRect;
  268. } completion:^(BOOL finished) {
  269. UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, self.welcomeLabel);
  270. ;//nop
  271. }];
  272. }
  273. - (void)restoreIDFromMDM {
  274. [self setAcceptPrivacyPolicyValues:AcceptPrivacyPolicyVariantImplicitly];
  275. [mdmSetup restoreIDBackupOnCompletion:^{
  276. [self presentPageViewController];
  277. } onError:^(NSError *error) {
  278. _restoreIdentityViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RestoreIdentityViewController"];
  279. _restoreIdentityViewController.delegate = self;
  280. _restoreIdentityViewController.backupData = mdmSetup.idBackup;
  281. _restoreIdentityViewController.passwordData = mdmSetup.idBackupPassword;
  282. [_restoreIdentityViewController setup];
  283. [self slideOut:self fromRightToLeft:YES onCompletion:nil];
  284. [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:^{
  285. // make sure controls are visible
  286. _privacyView.alpha = 1.0;
  287. _privacyView.hidden = NO;
  288. _privacyView.frame = [RectUtil rect:_privacyView.frame centerIn:self.view.frame];
  289. // show error message
  290. [_restoreIdentityViewController handleError:error];
  291. }];
  292. }];
  293. }
  294. - (void)performLicenseCheck {
  295. LicenseStore *licenseStore = [LicenseStore sharedLicenseStore];
  296. [licenseStore performLicenseCheckWithCompletion:^(BOOL success) {
  297. dispatch_async(dispatch_get_main_queue(), ^{
  298. if (success) {
  299. [self presentUI];
  300. } else {
  301. // present anyway, to also fail early if there is no network connection
  302. [self presentLicenseViewController];
  303. }
  304. });
  305. }];
  306. }
  307. - (void)presentLicenseViewController {
  308. UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"License" bundle:nil];
  309. EnterLicenseViewController *viewController = [storyboard instantiateInitialViewController];
  310. viewController.delegate = self;
  311. viewController.modalPresentationStyle = UIModalPresentationFullScreen;
  312. [self presentViewController:viewController animated:NO completion:nil];
  313. }
  314. - (void)presentPageViewController {
  315. ConfirmIDViewController *confirmVc = [self.storyboard instantiateViewControllerWithIdentifier:@"ConfirmIDViewController"];
  316. SafeViewController *safeVc = [self.storyboard instantiateViewControllerWithIdentifier:@"SafeSetup"];
  317. PickNicknameViewController *pickNicknameVc = [self.storyboard instantiateViewControllerWithIdentifier:@"PickNicknameViewController"];
  318. LinkIDViewController *linkIdVc = [self.storyboard instantiateViewControllerWithIdentifier:@"LinkIDViewController"];
  319. SyncContactsViewController *syncVc = [self.storyboard instantiateViewControllerWithIdentifier:@"SyncContactsViewController"];
  320. CompletedIDViewController *complededVc = [self.storyboard instantiateViewControllerWithIdentifier:@"CompletedIDViewController"];
  321. complededVc.delegate = self;
  322. ParallaxPageViewController *pageVc = [self.storyboard instantiateViewControllerWithIdentifier:@"ParallaxPageViewController"];
  323. if ([mdmSetup skipWizard]) {
  324. pageVc.viewControllers = @[complededVc];
  325. } else {
  326. if ([mdmSetup isSafeBackupDisable] || ([mdmSetup isSafeBackupForce] && [mdmSetup isSafeBackupPasswordPreset])) {
  327. pageVc.viewControllers = @[confirmVc, pickNicknameVc, linkIdVc, syncVc, complededVc];
  328. } else {
  329. pageVc.viewControllers = @[confirmVc, safeVc, pickNicknameVc, linkIdVc, syncVc, complededVc];
  330. }
  331. }
  332. pageVc.bgView = _bgView;
  333. pageVc.parallaxFactor = [NSNumber numberWithDouble: fabs(_parallaxDeltaX/self.view.frame.size.width)];
  334. pageVc.modalPresentationStyle = UIModalPresentationFullScreen;
  335. // make sure to clean up
  336. [_randomSeedViewController willMoveToParentViewController:nil];
  337. [_restoreIdentityViewController willMoveToParentViewController:nil];
  338. [self presentViewController:pageVc animated:NO completion:^{
  339. [_randomSeedViewController.view removeFromSuperview];
  340. [_randomSeedViewController removeFromParentViewController];
  341. [_restoreIdentityViewController.view removeFromSuperview];
  342. [_restoreIdentityViewController removeFromParentViewController];
  343. }];
  344. }
  345. - (void)showApplicaitonUI {
  346. [[AppDelegate sharedAppDelegate] completedIDSetup];
  347. }
  348. #pragma mark - IntroQuestionView
  349. - (void)showAlertAcceptPrivacyPolicy {
  350. if (_acceptPrivacyPolicyQuestionView == nil) {
  351. _acceptPrivacyPolicyQuestionView = (IntroQuestionView *)[NibUtil loadViewFromNibWithName:@"IntroQuestionView"];
  352. if ([LicenseStore requiresLicenseKey]) {
  353. _acceptPrivacyPolicyQuestionView.questionLabel.text = [BundleUtil localizedStringForKey:@"privacy_policy_alert_text_work"];
  354. } else {
  355. _acceptPrivacyPolicyQuestionView.questionLabel.text = [BundleUtil localizedStringForKey:@"privacy_policy_alert_text"];
  356. }
  357. _acceptPrivacyPolicyQuestionView.delegate = self;
  358. _acceptPrivacyPolicyQuestionView.showOnlyOkButton = YES;
  359. _acceptPrivacyPolicyQuestionView.frame = [RectUtil rect:_acceptPrivacyPolicyQuestionView.frame centerIn:self.view.frame round:YES];
  360. [self.view addSubview:_acceptPrivacyPolicyQuestionView];
  361. }
  362. [self showMessageView:_acceptPrivacyPolicyQuestionView];
  363. }
  364. - (void)hideAcceptPrivacyPolicyQuestion {
  365. [self hideMessageView:_acceptPrivacyPolicyQuestionView];
  366. }
  367. - (void)showIDBackupQuestion {
  368. if (_existingBackupQuestionView == nil) {
  369. _existingBackupQuestionView = (IntroQuestionView *)[NibUtil loadViewFromNibWithName:@"IntroQuestionView"];
  370. _existingBackupQuestionView.tag = 1;
  371. _existingBackupQuestionView.questionLabel.text = [BundleUtil localizedStringForKey:@"backup_found_message"];;
  372. _existingBackupQuestionView.delegate = self;
  373. _existingBackupQuestionView.frame = [RectUtil rect:_existingBackupQuestionView.frame centerIn:self.view.frame round:YES];
  374. [self.view addSubview:_existingBackupQuestionView];
  375. }
  376. [self showMessageView:_existingBackupQuestionView];
  377. }
  378. - (void)hideIDBackupQuestion {
  379. [self hideMessageView:_existingBackupQuestionView];
  380. }
  381. - (void)showIDExistsQuestion {
  382. if (_existingIdQuestionView == nil) {
  383. _existingIdQuestionView = (IntroQuestionView *)[NibUtil loadViewFromNibWithName:@"IntroQuestionView"];
  384. _existingIdQuestionView.tag = 2;
  385. _existingIdQuestionView.questionLabel.text = [[NSString alloc] initWithFormat:[BundleUtil localizedStringForKey:@"id_exists"], [[MyIdentityStore sharedMyIdentityStore] identity]];
  386. _existingIdQuestionView.delegate = self;
  387. _existingIdQuestionView.frame = [RectUtil rect:_existingIdQuestionView.frame centerIn:self.view.frame round:YES];
  388. [self.view addSubview:_existingIdQuestionView];
  389. }
  390. [self showMessageView:_existingIdQuestionView];
  391. }
  392. - (void)hideIDExistsQuestion {
  393. [self hideMessageView:_existingIdQuestionView];
  394. }
  395. #pragma mark - FLAnimatedImageViewDelegate
  396. - (void)animatedImageViewWillDrawFrame:(NSUInteger)frameIndex {
  397. if ((int)frameIndex == 0 && _privacyView.hidden == NO) {
  398. // stay at last frame
  399. _animatedView.currentFrameIndex = 78;
  400. } else if ((int)frameIndex == 0) {
  401. CGFloat duration = 1.0;
  402. UIViewAnimationOptions options = UIViewAnimationOptionCurveEaseInOut;
  403. [UIView animateWithDuration:duration delay:0.0 options:options animations:^{
  404. _bgImagescale = 1.5;
  405. [self setupBackgroundView];
  406. } completion:nil];
  407. } else if ((int)frameIndex == 72 && _privacyView.hidden == YES) {
  408. [self slidePrivacyControlsIn];
  409. }
  410. }
  411. #pragma mark - manage views
  412. - (void)showSetupViewController {
  413. _randomSeedViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RandomSeedViewController"];
  414. _randomSeedViewController.delegate = self;
  415. [_randomSeedViewController setup];
  416. [self setAcceptPrivacyPolicyValues:AcceptPrivacyPolicyVariantExplicitly];
  417. }
  418. - (void)showRestoreOptionDataViewController {
  419. _restoreOptionDataViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RestoreOptionDataViewController"];
  420. _restoreOptionDataViewController.delegate = self;
  421. [self setAcceptPrivacyPolicyValues:AcceptPrivacyPolicyVariantExplicitly];
  422. }
  423. - (void)showRestoreOptionBackupViewController {
  424. _restoreOptionBackupViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RestoreOptionBackupViewController"];
  425. _restoreOptionBackupViewController.delegate = self;
  426. _restoreOptionBackupViewController.hasDataOnDevice = [self hasDataOnDevice];
  427. [self setAcceptPrivacyPolicyValues:AcceptPrivacyPolicyVariantExplicitly];
  428. }
  429. - (void)showRestoreSafeViewController:(BOOL)doRestoreIdentityOnly {
  430. _restoreSafeViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RestoreSafeViewController"];
  431. _restoreSafeViewController.delegate = self;
  432. _restoreSafeViewController.restoreIdentityOnly = doRestoreIdentityOnly;
  433. }
  434. - (void)showRestoreIdentityViewController:(NSString *)backupData {
  435. _restoreIdentityViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"RestoreIdentityViewController"];
  436. _restoreIdentityViewController.delegate = self;
  437. _restoreIdentityViewController.backupData = backupData;
  438. }
  439. - (void)showRestoreIdentityViewController {
  440. if (_privacyPolicySwitch.on) {
  441. _triggeredSetup = NO;
  442. _isRestoreOptionBackupDisplayed = ([self.childViewControllers count] > 0 && [self.childViewControllers[0] isKindOfClass:[RestoreOptionBackupViewController class]]) != 0 ? YES : NO;
  443. if (_isRestoreOptionBackupDisplayed) {
  444. [self showRestoreIdentityViewController:nil];
  445. [self slideOut:_restoreOptionBackupViewController fromRightToLeft:YES onCompletion:nil];
  446. [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:nil];
  447. } else if ([self.childViewControllers count] == 0) {
  448. [self showRestoreIdentityViewController:nil];
  449. [self slideOut:self fromRightToLeft:YES onCompletion:nil];
  450. [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:nil];
  451. }
  452. } else {
  453. [self showAlertAcceptPrivacyPolicy];
  454. }
  455. }
  456. - (void)slideIn:(UIViewController *)child fromLeftToRight:(BOOL)toRight onCompletion:(void(^)(void))onCompletion {
  457. UIView *childView;
  458. if ([child isKindOfClass:[SplashViewController class]]) {
  459. childView = _containerView;
  460. } else {
  461. [self addChildViewController:child];
  462. childView = child.view;
  463. [self.view addSubview:childView];
  464. }
  465. //start position
  466. if (toRight) {
  467. childView.frame = [RectUtil setXPositionOf:childView.frame x:self.view.frame.size.width];
  468. } else {
  469. childView.frame = [RectUtil setXPositionOf:childView.frame x:self.view.frame.size.width * -1.0];
  470. }
  471. [child beginAppearanceTransition:YES animated:YES];
  472. [UIView animateWithDuration:0.5 animations:^{
  473. //end position
  474. childView.frame = [RectUtil setXPositionOf:childView.frame x:0];
  475. CGFloat parallaxFactor = toRight == YES ? 1.0 : -1.0;
  476. _bgView.frame = [RectUtil offsetRect:_bgView.frame byX:_parallaxDeltaX*parallaxFactor byY:0.0];
  477. } completion:^(BOOL finished) {
  478. [child endAppearanceTransition];
  479. [child didMoveToParentViewController:self];
  480. if (onCompletion) {
  481. onCompletion();
  482. }
  483. }];
  484. }
  485. - (void)slideOut:(UIViewController *)child fromRightToLeft:(BOOL)toLeft onCompletion:(void(^)(void))onCompletion {
  486. UIView *childView;
  487. if ([child isKindOfClass:[SplashViewController class]]) {
  488. childView = _containerView;
  489. } else {
  490. childView = child.view;
  491. }
  492. //start position
  493. childView.frame = [RectUtil setXPositionOf:childView.frame x:0];
  494. [child beginAppearanceTransition:NO animated:YES];
  495. [UIView animateWithDuration:0.5 animations:^{
  496. //end position
  497. if (toLeft) {
  498. childView.frame = [RectUtil setXPositionOf:childView.frame x:self.view.frame.size.width * -1.0];
  499. } else {
  500. childView.frame = [RectUtil setXPositionOf:childView.frame x:self.view.frame.size.width];
  501. }
  502. } completion:^(BOOL finished) {
  503. [child endAppearanceTransition];
  504. if (![child isKindOfClass:[SplashViewController class]]) {
  505. [childView removeFromSuperview];
  506. [child removeFromParentViewController];
  507. }
  508. if (onCompletion) {
  509. onCompletion();
  510. }
  511. }];
  512. }
  513. - (void)createIdentity {
  514. [MBProgressHUD showHUDAddedTo:self.view animated:YES];
  515. [[[ServerAPIConnector alloc] init] createIdentityWithStore:[MyIdentityStore sharedMyIdentityStore] onCompletion:^(MyIdentityStore *store) {
  516. [MBProgressHUD hideHUDForView:self.view animated:YES];
  517. store.pendingCreateID = YES;
  518. [self presentPageViewController];
  519. } onError:^(NSError *error) {
  520. [MBProgressHUD hideHUDForView:self.view animated:YES];
  521. UIAlertController *errAlert = [UIAlertController alertControllerWithTitle:error.localizedDescription message:error.localizedFailureReason preferredStyle:UIAlertControllerStyleAlert];
  522. [errAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"try_again", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) {
  523. [self createIdentity];
  524. }]];
  525. [[[AppDelegate sharedAppDelegate] currentTopViewController] presentViewController:errAlert animated:YES completion:nil];
  526. }];
  527. }
  528. - (void)setAcceptPrivacyPolicyValues:(int)variant {
  529. [[UserSettings sharedUserSettings] setAcceptedPrivacyPolicyDate:[NSDate date]];
  530. [[UserSettings sharedUserSettings] setAcceptedPrivacyPolicyVariant:variant];
  531. }
  532. #pragma mark - private
  533. - (NSString *)getIDBackup {
  534. NSString *backupData = [IdentityBackupStore loadIdentityBackup];
  535. if (backupData != nil && [[MyIdentityStore sharedMyIdentityStore] isValidBackupFormat:backupData]) {
  536. return backupData;
  537. }
  538. return nil;
  539. }
  540. - (BOOL)checkForIDBackup {
  541. _idBackup = [self getIDBackup];
  542. if (_idBackup) {
  543. [self showIDBackupQuestion];
  544. return YES;
  545. }
  546. return NO;
  547. }
  548. - (BOOL)checkForIDExists {
  549. if ([[MyIdentityStore sharedMyIdentityStore] isProvisioned]) {
  550. [self showIDExistsQuestion];
  551. return YES;
  552. }
  553. return NO;
  554. }
  555. - (void)addEchoEchoToContacts {
  556. [[ContactStore sharedContactStore] addContactWithIdentity:@"ECHOECHO" verificationLevel:kVerificationLevelUnverified onCompletion:^(Contact *contact, BOOL alreadyExists) {
  557. if (contact.isGatewayId) {
  558. [[GatewayAvatarMaker gatewayAvatarMaker] loadAndSaveAvatarForId:contact.identity];
  559. }
  560. } onError:^(NSError *error) {
  561. // do nothing
  562. }];
  563. }
  564. - (void)checkRefreshStoreReceipt {
  565. if ([LicenseStore requiresLicenseKey]) {
  566. return;
  567. }
  568. NSURL *receiptUrl = [[NSBundle mainBundle] appStoreReceiptURL];
  569. if (receiptUrl && ![[NSFileManager defaultManager] fileExistsAtPath:receiptUrl.path]) {
  570. // No receipt available; try to refresh
  571. SKReceiptRefreshRequest *refreshRequest = [[SKReceiptRefreshRequest alloc] initWithReceiptProperties:nil];
  572. [refreshRequest start];
  573. }
  574. }
  575. #pragma mark - IBActions
  576. - (IBAction)setupAction:(id)sender {
  577. if (_privacyPolicySwitch.on) {
  578. _triggeredSetup = YES;
  579. // Check for ID Export, if Threema Work or if Threema and has no existing ID
  580. if ([LicenseStore requiresLicenseKey] || (![LicenseStore requiresLicenseKey] && ![self checkForIDExists])) {
  581. if ([self checkForIDBackup] == NO) {
  582. [self showSetupViewController];
  583. [self slideOut:self fromRightToLeft:YES onCompletion:nil];
  584. [self slideIn:_randomSeedViewController fromLeftToRight:YES onCompletion:nil];
  585. }
  586. }
  587. } else {
  588. [self showAlertAcceptPrivacyPolicy];
  589. }
  590. }
  591. - (IBAction)restoreAction:(id)sender {
  592. if (_privacyPolicySwitch.on) {
  593. _triggeredSetup = NO;
  594. if ([mdmSetup isSafeRestoreDisable]) {
  595. if ([self checkForIDBackup] == NO) {
  596. [self showRestoreIdentityViewController:nil];
  597. [self slideOut:self fromRightToLeft:YES onCompletion:nil];
  598. [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:nil];
  599. }
  600. } else {
  601. if ([self hasDataOnDevice] == YES) {
  602. [self showRestoreOptionDataViewController];
  603. [self slideOut:self fromRightToLeft:YES onCompletion:nil];
  604. [self slideIn:_restoreOptionDataViewController fromLeftToRight:YES onCompletion:nil];
  605. } else {
  606. [self showRestoreOptionBackupViewController];
  607. [self slideOut:self fromRightToLeft:YES onCompletion:nil];
  608. [self slideIn:_restoreOptionBackupViewController fromLeftToRight:YES onCompletion:nil];
  609. }
  610. }
  611. } else {
  612. [self showAlertAcceptPrivacyPolicy];
  613. }
  614. }
  615. #pragma mark - IntroQuestionViewDelegate
  616. - (void)selectedYes:(IntroQuestionView *)sender {
  617. if (sender.tag == 1) {
  618. [self hideIDBackupQuestion];
  619. [self showRestoreIdentityViewController:_idBackup];
  620. [self slideOut:self fromRightToLeft:YES onCompletion:nil];
  621. [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:nil];
  622. }
  623. else if (sender.tag == 2) {
  624. [self hideIDExistsQuestion];
  625. [self presentPageViewController];
  626. }
  627. }
  628. - (void)selectedNo:(IntroQuestionView *)sender {
  629. if (sender.tag == 1) {
  630. [self hideIDBackupQuestion];
  631. if (_triggeredSetup) {
  632. [self showSetupViewController];
  633. [self slideOut:self fromRightToLeft:YES onCompletion:nil];
  634. [self slideIn:_randomSeedViewController fromLeftToRight:YES onCompletion:nil];
  635. } else {
  636. [self showRestoreIdentityViewController:nil];
  637. [self slideOut:self fromRightToLeft:YES onCompletion:nil];
  638. [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:nil];
  639. }
  640. }
  641. else if (sender.tag == 2) {
  642. [self hideIDExistsQuestion];
  643. [self showSetupViewController];
  644. [self slideOut:self fromRightToLeft:YES onCompletion:nil];
  645. [self slideIn:_randomSeedViewController fromLeftToRight:YES onCompletion:nil];
  646. }
  647. }
  648. - (void)selectedOk:(IntroQuestionView *)sender {
  649. [self hideAcceptPrivacyPolicyQuestion];
  650. }
  651. #pragma mark - RandomSeedViewControllerDelegate
  652. - (void)generatedRandomSeed:(NSData *)seed {
  653. [[MyIdentityStore sharedMyIdentityStore] generateKeyPairWithSeed:seed];
  654. [self createIdentity];
  655. }
  656. #pragma mark - CompletedIDDelegate
  657. - (void)completedIDSetup {
  658. if ([[DatabaseManager dbManager] shouldUpdateProtection]) {
  659. MyIdentityStore *myIdentityStore = [MyIdentityStore sharedMyIdentityStore];
  660. [myIdentityStore updateConnectionRights];
  661. [[DatabaseManager dbManager] updateProtection];
  662. }
  663. // Delete decrypted backup data from application documents folder
  664. [FileUtility deleteWithFileUrl: [[DocumentManager applicationDocumentsDirectory] URLByAppendingPathComponent:@"safe-backup.json"]];
  665. AppSetupState *appSetupState = [[AppSetupState alloc] initWithMyIdentityStore:[MyIdentityStore sharedMyIdentityStore]];
  666. [appSetupState appSetupCompleted];
  667. [[ContactStore sharedContactStore] updateAllContactsToCNContact];
  668. [[ContactStore sharedContactStore] updateAllContacts];
  669. [ConversationUtils resetUnreadMessageCount];
  670. [NotificationManager generatePushSettingForAllGroups];
  671. [self addEchoEchoToContacts];
  672. [self showApplicaitonUI];
  673. }
  674. #pragma mark - RestoreOptionDataViewControllerDelegate
  675. - (void)optionDataKeepLocal {
  676. _restoreOptionDataViewController.delegate = nil;
  677. [self showRestoreOptionBackupViewController];
  678. [self slideOut:_restoreOptionDataViewController fromRightToLeft:YES onCompletion:nil];
  679. [self slideIn:_restoreOptionBackupViewController fromLeftToRight:YES onCompletion:nil];
  680. }
  681. - (void)optionDataCancelled {
  682. _restoreOptionDataViewController.delegate = nil;
  683. [self slideOut:_restoreOptionDataViewController fromRightToLeft:NO onCompletion:nil];
  684. [self slideIn:self fromLeftToRight:NO onCompletion:nil];
  685. }
  686. #pragma mark - RestoreOptionBackupViewControllerDelegate
  687. - (void)restoreSafe {
  688. _restoreOptionBackupViewController.delegate = nil;
  689. [self showRestoreSafeViewController:NO];
  690. [self slideOut:_restoreOptionBackupViewController fromRightToLeft:YES onCompletion:nil];
  691. [self slideIn:_restoreSafeViewController fromLeftToRight:YES onCompletion:nil];
  692. }
  693. - (void)restoreIdentityFromSafe {
  694. _restoreOptionBackupViewController.delegate = nil;
  695. [self showRestoreSafeViewController:YES];
  696. [self slideOut:_restoreOptionBackupViewController fromRightToLeft:YES onCompletion:nil];
  697. [self slideIn:_restoreSafeViewController fromLeftToRight:YES onCompletion:nil];
  698. }
  699. - (void)restoreIdentity {
  700. _restoreOptionBackupViewController.delegate = nil;
  701. [self slideOut:_restoreOptionBackupViewController fromRightToLeft:YES onCompletion:nil];
  702. _isRestoreOptionBackupDisplayed = YES;
  703. if ([self checkForIDBackup] == NO) {
  704. [self showRestoreIdentityViewController:nil];
  705. [self slideIn:_restoreIdentityViewController fromLeftToRight:YES onCompletion:nil];
  706. }
  707. }
  708. - (void)restoreCancelled {
  709. _isRestoreOptionBackupDisplayed = NO;
  710. _restoreOptionBackupViewController.delegate = nil;
  711. [self slideOut:_restoreOptionBackupViewController fromRightToLeft:NO onCompletion:nil];
  712. if ([self hasDataOnDevice]) {
  713. [self showRestoreOptionDataViewController];
  714. [self slideIn:_restoreOptionDataViewController fromLeftToRight:NO onCompletion:nil];
  715. } else {
  716. [self slideIn:self fromLeftToRight:NO onCompletion:nil];
  717. }
  718. }
  719. #pragma mark - RestoreSafeViewControllerDelegate
  720. - (void)restoreSafeCancelled {
  721. _restoreSafeViewController.delegate = nil;
  722. [self showRestoreOptionBackupViewController];
  723. [self slideOut:_restoreSafeViewController fromRightToLeft:NO onCompletion:nil];
  724. [self slideIn:_restoreOptionBackupViewController fromLeftToRight:NO onCompletion:nil];
  725. }
  726. - (void)restoreSafeDone {
  727. _restoreSafeViewController.delegate = nil;
  728. [self completedIDSetup];
  729. }
  730. #pragma mark - RestoreIdentityViewControllerDelegate
  731. - (void)restoreIdentityCancelled {
  732. _restoreIdentityViewController.delegate = nil;
  733. [self slideOut:_restoreIdentityViewController fromRightToLeft:NO onCompletion:nil];
  734. if (!_triggeredSetup && _isRestoreOptionBackupDisplayed) {
  735. [self showRestoreOptionBackupViewController];
  736. [self slideIn:_restoreOptionBackupViewController fromLeftToRight:NO onCompletion:nil];
  737. } else if (!_isRestoreOptionBackupDisplayed) {
  738. [self slideIn:self fromLeftToRight:NO onCompletion:nil];
  739. }
  740. }
  741. - (void)restoreIdentityDone {
  742. _restoreIdentityViewController.delegate = nil;
  743. [MyIdentityStore sharedMyIdentityStore].pendingCreateID = YES;
  744. [self presentPageViewController];
  745. }
  746. #pragma mark - EnterLicenseDelegate
  747. - (void)licenseConfirmed {
  748. [self dismissViewControllerAnimated:YES completion:nil];
  749. }
  750. #pragma mark - ZSWTappableLabel delegate
  751. - (void)tappableLabel:(ZSWTappableLabel *)tappableLabel tappedAtIndex:(NSInteger)idx withAttributes:(NSDictionary *)attributes {
  752. UIStoryboard *storyboard = [AppDelegate getSettingsStoryboard];
  753. UIViewController *vc = [storyboard instantiateViewControllerWithIdentifier:@"PrivacyPolicyViewController"];
  754. UINavigationController *nc = [[UINavigationController alloc] initWithRootViewController:vc];
  755. [self presentViewController:nc animated:YES completion:nil];
  756. }
  757. @end