From 857ca56ab6bf470ebc2eb30467072c58c46beb2a Mon Sep 17 00:00:00 2001 From: Matthew Chen Date: Mon, 8 Jan 2018 11:01:09 -0500 Subject: [PATCH] Rework progress mode of export backup UI. --- .../ViewControllers/OWSBackupViewController.m | 79 ++++++++++++++----- Signal/src/util/OWSBackup.h | 7 +- Signal/src/util/OWSBackup.m | 40 +++++++--- 3 files changed, 97 insertions(+), 29 deletions(-) diff --git a/Signal/src/ViewControllers/OWSBackupViewController.m b/Signal/src/ViewControllers/OWSBackupViewController.m index 5bf508975..2b3bb03c0 100644 --- a/Signal/src/ViewControllers/OWSBackupViewController.m +++ b/Signal/src/ViewControllers/OWSBackupViewController.m @@ -4,6 +4,7 @@ #import "OWSBackupViewController.h" #import "OWSBackup.h" +#import "OWSProgressView.h" #import "Signal-Swift.h" #import "ThreadUtil.h" #import @@ -20,6 +21,8 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic) OWSBackup *backup; +@property (nonatomic, nullable) OWSProgressView *progressView; + @end #pragma mark - @@ -58,6 +61,7 @@ NS_ASSUME_NONNULL_BEGIN for (UIView *subview in self.view.subviews) { [subview removeFromSuperview]; } + self.progressView = nil; switch (self.backup.backupState) { case OWSBackupState_InProgress: @@ -69,32 +73,37 @@ NS_ASSUME_NONNULL_BEGIN case OWSBackupState_Complete: [self showCompleteUI]; break; + case OWSBackupState_Failed: + [self showFailedUI]; + break; } } - (void)showInProgressUI { - - UIView *container = [UIView new]; - [self.view addSubview:container]; - [container autoCenterInSuperview]; - - UIActivityIndicatorView *activityIndicatorView = - [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; - [container addSubview:activityIndicatorView]; - [activityIndicatorView autoPinEdgeToSuperviewEdge:ALEdgeTop]; - [activityIndicatorView autoHCenterInSuperview]; - [activityIndicatorView startAnimating]; + self.progressView = [OWSProgressView new]; + self.progressView.color = [UIColor ows_materialBlueColor]; + self.progressView.progress = self.backup.backupProgress; + [self.progressView autoSetDimension:ALDimensionWidth toSize:300]; + [self.progressView autoSetDimension:ALDimensionHeight toSize:20]; UILabel *label = [UILabel new]; label.text = NSLocalizedString( @"BACKUP_EXPORT_IN_PROGRESS_MESSAGE", @"Message indicating that backup export is in progress."); label.textColor = [UIColor blackColor]; label.font = [UIFont ows_regularFontWithSize:18.f]; - [container addSubview:label]; - [label autoPinEdge:ALEdgeTop toEdge:ALEdgeBottom ofView:activityIndicatorView withOffset:20.f]; - [label autoPinEdgeToSuperviewEdge:ALEdgeBottom]; - [label autoPinWidthToSuperview]; + label.textAlignment = NSTextAlignmentCenter; + label.numberOfLines = 0; + label.lineBreakMode = NSLineBreakByWordWrapping; + + UIView *container = [UIView verticalStackWithSubviews:@[ + label, + self.progressView, + ] + spacing:10]; + [self.view addSubview:container]; + [container autoVCenterInSuperview]; + [container autoPinWidthToSuperviewWithMargin:25.f]; } - (void)showCancelledUI @@ -108,7 +117,7 @@ NS_ASSUME_NONNULL_BEGIN { NSString *message = NSLocalizedString( - @"BACKUP_EXPORT_COMPLETE_MESSAGE", @"Message indicating that backup export without password is complete."); + @"BACKUP_EXPORT_COMPLETE_MESSAGE", @"Message indicating that backup export is complete."); UILabel *label = [UILabel new]; label.text = message; @@ -122,8 +131,8 @@ NS_ASSUME_NONNULL_BEGIN if (self.backup.backupPassword) { NSString *message = [NSString stringWithFormat:NSLocalizedString(@"BACKUP_EXPORT_PASSWORD_MESSAGE_FORMAT", - @"Format for message indicating that backup export with " - @"password is complete. Embeds: {{the backup password}}."), + @"Format for message indicating that backup export " + @"is complete. Embeds: {{the backup password}}."), self.backup.backupPassword]; UILabel *label = [UILabel new]; @@ -164,6 +173,32 @@ NS_ASSUME_NONNULL_BEGIN [container autoPinWidthToSuperviewWithMargin:25.f]; } +- (void)showFailedUI +{ + NSMutableArray *subviews = [NSMutableArray new]; + + { + NSString *message + = NSLocalizedString(@"BACKUP_EXPORT_FAILED_MESSAGE", @"Message indicating that backup export failed."); + + UILabel *label = [UILabel new]; + label.text = message; + label.textColor = [UIColor blackColor]; + label.font = [UIFont ows_regularFontWithSize:18.f]; + label.textAlignment = NSTextAlignmentCenter; + label.numberOfLines = 0; + label.lineBreakMode = NSLineBreakByWordWrapping; + [subviews addObject:label]; + } + + // TODO: We should offer the option to save the backup to "Files", iCloud, Dropbox, etc. + + UIView *container = [UIView verticalStackWithSubviews:subviews spacing:10]; + [self.view addSubview:container]; + [container autoVCenterInSuperview]; + [container autoPinWidthToSuperviewWithMargin:25.f]; +} + - (UIView *)makeButtonWithTitle:(NSString *)title selector:(SEL)selector { const CGFloat kButtonHeight = 40; @@ -264,6 +299,14 @@ NS_ASSUME_NONNULL_BEGIN [self updateUI]; } + +- (void)backupProgressDidChange +{ + DDLogInfo(@"%@ %s.", self.logTag, __PRETTY_FUNCTION__); + + self.progressView.progress = self.backup.backupProgress; +} + @end NS_ASSUME_NONNULL_END diff --git a/Signal/src/util/OWSBackup.h b/Signal/src/util/OWSBackup.h index c9e103b66..85d5bba94 100644 --- a/Signal/src/util/OWSBackup.h +++ b/Signal/src/util/OWSBackup.h @@ -8,6 +8,8 @@ NS_ASSUME_NONNULL_BEGIN - (void)backupStateDidChange; +- (void)backupProgressDidChange; + @end #pragma mark - @@ -16,6 +18,7 @@ typedef NS_ENUM(NSUInteger, OWSBackupState) { OWSBackupState_InProgress, OWSBackupState_Cancelled, OWSBackupState_Complete, + OWSBackupState_Failed, }; @class TSThread; @@ -24,7 +27,9 @@ typedef NS_ENUM(NSUInteger, OWSBackupState) { @property (nonatomic, weak) id delegate; -@property (nonatomic) OWSBackupState backupState; +@property (nonatomic, readonly) OWSBackupState backupState; + +@property (nonatomic, readonly) CGFloat backupProgress; // If non-nil, backup is encrypted. @property (nonatomic, nullable, readonly) NSString *backupPassword; diff --git a/Signal/src/util/OWSBackup.m b/Signal/src/util/OWSBackup.m index 06d5dba52..376d5940a 100644 --- a/Signal/src/util/OWSBackup.m +++ b/Signal/src/util/OWSBackup.m @@ -23,6 +23,10 @@ NS_ASSUME_NONNULL_BEGIN @interface OWSBackup () +@property (nonatomic) OWSBackupState backupState; + +@property (nonatomic) CGFloat backupProgress; + @property (nonatomic, nullable) TSThread *currentThread; @property (nonatomic, nullable) NSString *backupPassword; @@ -51,14 +55,21 @@ NS_ASSUME_NONNULL_BEGIN [self.delegate backupStateDidChange]; } +- (void)fail +{ + if (!self.isCancelledOrFailed) { + self.backupState = OWSBackupState_Failed; + } +} + - (void)cancel { self.backupState = OWSBackupState_Cancelled; } -- (BOOL)isCancelled +- (BOOL)isCancelledOrFailed { - return self.backupState == OWSBackupState_Cancelled; + return (self.backupState == OWSBackupState_Cancelled || self.backupState == OWSBackupState_Failed); } - (void)exportBackup:(nullable TSThread *)currentThread skipPassword:(BOOL)skipPassword @@ -90,7 +101,7 @@ NS_ASSUME_NONNULL_BEGIN [self exportToFilesAndZip]; dispatch_async(dispatch_get_main_queue(), ^{ - if (!self.isCancelled) { + if (!self.isCancelledOrFailed) { self.backupState = OWSBackupState_Complete; } [self.delegate backupStateDidChange]; @@ -125,32 +136,35 @@ NS_ASSUME_NONNULL_BEGIN [OWSFileSystem protectFolderAtPath:rootDirPath]; [OWSFileSystem ensureDirectoryExists:backupDirPath]; - if (self.isCancelled) { + if (self.isCancelledOrFailed) { return; } NSData *databasePassword = [TSStorageManager sharedManager].databasePassword; if (![self writeData:databasePassword fileName:@"databasePassword" backupDirPath:backupDirPath]) { + [self fail]; return; } - if (self.isCancelled) { + if (self.isCancelledOrFailed) { return; } if (![self writeUserDefaults:NSUserDefaults.standardUserDefaults fileName:@"standardUserDefaults" backupDirPath:backupDirPath]) { + [self fail]; return; } - if (self.isCancelled) { + if (self.isCancelledOrFailed) { return; } if (![self writeUserDefaults:NSUserDefaults.appUserDefaults fileName:@"appUserDefaults" backupDirPath:backupDirPath]) { + [self fail]; return; } - if (self.isCancelled) { + if (self.isCancelledOrFailed) { return; } // Use a read/write transaction to acquire a file lock on the database files. @@ -161,21 +175,24 @@ NS_ASSUME_NONNULL_BEGIN if (![self copyDirectory:OWSFileSystem.appDocumentDirectoryPath dstDirName:@"appDocumentDirectoryPath" backupDirPath:backupDirPath]) { + [self fail]; return; } - if (self.isCancelled) { + if (self.isCancelledOrFailed) { return; } if (![self copyDirectory:OWSFileSystem.appSharedDataDirectoryPath dstDirName:@"appSharedDataDirectoryPath" backupDirPath:backupDirPath]) { + [self fail]; return; } }]; - if (self.isCancelled) { + if (self.isCancelledOrFailed) { return; } if (![self zipDirectory:backupDirPath dstFilePath:backupZipPath]) { + [self fail]; return; } @@ -281,7 +298,10 @@ NS_ASSUME_NONNULL_BEGIN entryNumber, total, entryNumber / (CGFloat)total); - // TODO: + + CGFloat progress = entryNumber / (CGFloat)total; + self.backupProgress = progress; + [self.delegate backupProgressDidChange]; }]; if (!success) {