Merge branch 'charlesmchen/modalActivityIndicator'

This commit is contained in:
Matthew Chen 2017-09-12 11:06:48 -04:00
commit c95ff44ea4
9 changed files with 283 additions and 104 deletions

View file

@ -90,6 +90,7 @@
34D8C0281ED3673300188D7C /* DebugUITableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C0261ED3673300188D7C /* DebugUITableViewController.m */; };
34D8C02B1ED3685800188D7C /* DebugUIContacts.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D8C02A1ED3685800188D7C /* DebugUIContacts.m */; };
34D9134B1F62D4A500722898 /* SignalAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D913491F62D4A500722898 /* SignalAttachment.swift */; };
34D9134D1F66DB7C00722898 /* ModalActivityIndicatorViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D9134C1F66DB7C00722898 /* ModalActivityIndicatorViewController.swift */; };
34D99C8C1F27B13B00D284D6 /* OWSViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 34D99C8B1F27B13B00D284D6 /* OWSViewController.m */; };
34D99C931F2937CC00D284D6 /* OWSAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D99C911F2937CC00D284D6 /* OWSAnalytics.swift */; };
34D99C941F2937CC00D284D6 /* OWSSwiftUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34D99C921F2937CC00D284D6 /* OWSSwiftUtils.swift */; };
@ -551,6 +552,7 @@
34D8C0291ED3685800188D7C /* DebugUIContacts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DebugUIContacts.h; sourceTree = "<group>"; };
34D8C02A1ED3685800188D7C /* DebugUIContacts.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DebugUIContacts.m; sourceTree = "<group>"; };
34D913491F62D4A500722898 /* SignalAttachment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalAttachment.swift; sourceTree = "<group>"; };
34D9134C1F66DB7C00722898 /* ModalActivityIndicatorViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ModalActivityIndicatorViewController.swift; sourceTree = "<group>"; };
34D99C8A1F27B13B00D284D6 /* OWSViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OWSViewController.h; sourceTree = "<group>"; };
34D99C8B1F27B13B00D284D6 /* OWSViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OWSViewController.m; sourceTree = "<group>"; };
34D99C911F2937CC00D284D6 /* OWSAnalytics.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OWSAnalytics.swift; sourceTree = "<group>"; };
@ -1029,6 +1031,7 @@
34B3F84C1E8DF1700035BE1A /* InviteFlow.swift */,
34B3F84D1E8DF1700035BE1A /* LockInteractionController.h */,
34B3F84E1E8DF1700035BE1A /* LockInteractionController.m */,
34D9134C1F66DB7C00722898 /* ModalActivityIndicatorViewController.swift */,
34B3F84F1E8DF1700035BE1A /* NewContactThreadViewController.h */,
34B3F8501E8DF1700035BE1A /* NewContactThreadViewController.m */,
34B3F8541E8DF1700035BE1A /* NewGroupViewController.h */,
@ -2263,6 +2266,7 @@
451686AB1F520CDA00AC3D4B /* MultiDeviceProfileKeyUpdateJob.swift in Sources */,
76EB058A18170B33006006FC /* Release.m in Sources */,
45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */,
34D9134D1F66DB7C00722898 /* ModalActivityIndicatorViewController.swift in Sources */,
4563ADF11F22BD7100DEB8C7 /* OWS106EnsureProfileComplete.swift in Sources */,
450873C71D9D867B006B54F2 /* OWSIncomingMessageCollectionViewCell.m in Sources */,
76EB057A18170B33006006FC /* OWSContactsManager.m in Sources */,

View file

@ -538,7 +538,7 @@ static NSString *const kURLHostVerifyPrefix = @"verify";
performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem
completionHandler:(void (^)(BOOL succeeded))completionHandler {
if ([TSAccountManager isRegistered]) {
[[Environment getCurrent].signalsViewController composeNew];
[[Environment getCurrent].signalsViewController showNewConversationView];
completionHandler(YES);
} else {
UIAlertController *controller =

View file

@ -374,12 +374,24 @@
- (void)proceedToUnregistration
{
[TSAccountManager unregisterTextSecureWithSuccess:^{
[Environment resetAppData];
}
failure:^(NSError *error) {
[OWSAlerts showAlertWithTitle:NSLocalizedString(@"UNREGISTER_SIGNAL_FAIL", @"")];
}];
[ModalActivityIndicatorViewController
presentFromViewController:self
canCancel:NO
presentCompletion:^(ModalActivityIndicatorViewController *modalActivityIndicator) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[TSAccountManager unregisterTextSecureWithSuccess:^{
[Environment resetAppData];
}
failure:^(NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
[modalActivityIndicator dismissWithCompletion:^{
[OWSAlerts
showAlertWithTitle:NSLocalizedString(@"UNREGISTER_SIGNAL_FAIL", @"")];
}];
});
}];
});
}];
}
#pragma mark - Socket Status Notifications

View file

@ -3445,31 +3445,51 @@ typedef NS_ENUM(NSInteger, MessagesRangeSizeMode) {
{
OWSAssert([NSThread isMainThread]);
AVAsset *video = [AVAsset assetWithURL:movieURL];
AVAssetExportSession *exportSession =
[AVAssetExportSession exportSessionWithAsset:video presetName:AVAssetExportPresetMediumQuality];
exportSession.shouldOptimizeForNetworkUse = YES;
exportSession.outputFileType = AVFileTypeMPEG4;
NSURL *compressedVideoUrl = [[self videoTempFolder]
URLByAppendingPathComponent:[[[NSUUID UUID] UUIDString] stringByAppendingPathExtension:@"mp4"]];
exportSession.outputURL = compressedVideoUrl;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
id<DataSource> _Nullable dataSource = [DataSourcePath dataSourceWithURL:compressedVideoUrl];
[dataSource setSourceFilename:filename];
SignalAttachment *attachment =
[SignalAttachment attachmentWithDataSource:dataSource dataUTI:(NSString *)kUTTypeMPEG4];
if (!attachment || [attachment hasError]) {
DDLogError(@"%@ %s Invalid attachment: %@.",
self.tag,
__PRETTY_FUNCTION__,
attachment ? [attachment errorName] : @"Missing data");
[self showErrorAlertForAttachment:attachment];
} else {
[self tryToSendAttachmentIfApproved:attachment skipApprovalDialog:skipApprovalDialog];
}
});
}];
[ModalActivityIndicatorViewController
presentFromViewController:self
canCancel:YES
presentCompletion:^(ModalActivityIndicatorViewController *modalActivityIndicator) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
AVAsset *video = [AVAsset assetWithURL:movieURL];
AVAssetExportSession *exportSession =
[AVAssetExportSession exportSessionWithAsset:video
presetName:AVAssetExportPresetMediumQuality];
exportSession.shouldOptimizeForNetworkUse = YES;
exportSession.outputFileType = AVFileTypeMPEG4;
NSURL *compressedVideoUrl = [[self videoTempFolder]
URLByAppendingPathComponent:[[[NSUUID UUID] UUIDString]
stringByAppendingPathExtension:@"mp4"]];
exportSession.outputURL = compressedVideoUrl;
[exportSession exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
OWSAssert([NSThread isMainThread]);
if (modalActivityIndicator.wasCancelled) {
return;
}
[modalActivityIndicator dismissWithCompletion:^{
id<DataSource> _Nullable dataSource =
[DataSourcePath dataSourceWithURL:compressedVideoUrl];
[dataSource setSourceFilename:filename];
SignalAttachment *attachment =
[SignalAttachment attachmentWithDataSource:dataSource
dataUTI:(NSString *)kUTTypeMPEG4];
if (!attachment || [attachment hasError]) {
DDLogError(@"%@ %s Invalid attachment: %@.",
self.tag,
__PRETTY_FUNCTION__,
attachment ? [attachment errorName] : @"Missing data");
[self showErrorAlertForAttachment:attachment];
} else {
[self tryToSendAttachmentIfApproved:attachment
skipApprovalDialog:skipApprovalDialog];
}
}];
});
}];
});
}];
}

View file

@ -18,7 +18,7 @@
- (void)updateInboxCountLabel;
- (void)composeNew;
- (void)showNewConversationView;
- (void)presentTopLevelModalViewController:(UIViewController *)viewController
animateDismissal:(BOOL)animateDismissal

View file

@ -175,7 +175,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
self.navigationItem.rightBarButtonItem =
[[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCompose
target:self
action:@selector(composeNew)];
action:@selector(showNewConversationView)];
ReminderView *archiveReminderView = [ReminderView new];
archiveReminderView.text = NSLocalizedString(
@ -346,7 +346,7 @@ typedef NS_ENUM(NSInteger, CellState) { kArchiveState, kInboxState };
[self.navigationController pushViewController:vc animated:NO];
}
- (void)composeNew
- (void)showNewConversationView
{
NewContactThreadViewController *viewController = [NewContactThreadViewController new];

View file

@ -0,0 +1,152 @@
//
// Copyright (c) 2017 Open Whisper Systems. All rights reserved.
//
import Foundation
import MediaPlayer
// A modal view that be used during blocking interactions (e.g. waiting on response from
// service or on the completion of a long-running local operation).
class ModalActivityIndicatorViewController: OWSViewController {
let TAG = "[ModalActivityIndicatorViewController]"
let canCancel: Bool
public var wasCancelled: Bool = false
var activityIndicator: UIActivityIndicatorView?
var presentTimer: Timer?
var wasDimissed: Bool = false
// MARK: Initializers
@available(*, unavailable, message:"use canCancel:completion: constructor instead.")
required init?(coder aDecoder: NSCoder) {
self.canCancel = false
super.init(coder: aDecoder)
owsFail("\(self.TAG) invalid constructor")
}
required init(canCancel: Bool) {
self.canCancel = canCancel
super.init(nibName: nil, bundle: nil)
}
public class func present(fromViewController: UIViewController,
canCancel: Bool, presentCompletion : @escaping (ModalActivityIndicatorViewController) -> Void) {
AssertIsOnMainThread()
let view = ModalActivityIndicatorViewController(canCancel:canCancel)
// Present this modal _over_ the current view contents.
view.modalPresentationStyle = .overFullScreen
fromViewController.present(view,
animated: false) {
presentCompletion(view)
}
}
public func dismiss(completion : @escaping () -> Void) {
AssertIsOnMainThread()
if !wasDimissed {
// Only dismiss once.
self.dismiss(animated:false, completion: completion)
wasDimissed = true
} else {
// If already dismissed, wait a beat then call completion.
DispatchQueue.main.async {
completion()
}
}
}
override func loadView() {
super.loadView()
self.view.backgroundColor = UIColor(colorLiteralRed: 0, green: 0, blue: 0, alpha: 0.25)
self.view.isOpaque = false
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle:.whiteLarge)
self.activityIndicator = activityIndicator
self.view.addSubview(activityIndicator)
activityIndicator.autoCenterInSuperview()
if canCancel {
let cancelButton = UIButton(type:.custom)
cancelButton.setTitle(CommonStrings.cancelButton, for: .normal)
cancelButton.setTitleColor(UIColor.white, for: .normal)
cancelButton.backgroundColor = UIColor.ows_darkGray()
cancelButton.titleLabel?.font = UIFont.ows_mediumFont(withSize:ScaleFromIPhone5To7Plus(18, 22))
cancelButton.layer.cornerRadius = ScaleFromIPhone5To7Plus(4, 5)
cancelButton.clipsToBounds = true
cancelButton.addTarget(self, action:#selector(cancelPressed), for:.touchUpInside)
let buttonWidth = ScaleFromIPhone5To7Plus(140, 160)
let buttonHeight = ScaleFromIPhone5To7Plus(40, 50)
self.view.addSubview(cancelButton)
cancelButton.autoHCenterInSuperview()
cancelButton.autoPinEdge(toSuperviewEdge: .bottom, withInset:50)
cancelButton.autoSetDimension(.width, toSize:buttonWidth)
cancelButton.autoSetDimension(.height, toSize:buttonHeight)
}
// Hide the modal until the presentation animation completes.
self.view.layer.opacity = 0.0
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.activityIndicator?.startAnimating()
// Hide the the modal and wait for a second before revealing it,
// to avoid "blipping" in the modal during short blocking operations.
//
// NOTE: It will still intercept user interactions while hidden, as it
// should.
let kPresentationDelaySeconds = TimeInterval(1)
self.presentTimer?.invalidate()
self.presentTimer = Timer.weakScheduledTimer(withTimeInterval: kPresentationDelaySeconds, target: self, selector: #selector(presentTimerFired), userInfo: nil, repeats: false)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
clearTimer()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.activityIndicator?.stopAnimating()
clearTimer()
}
private func clearTimer() {
self.presentTimer?.invalidate()
self.presentTimer = nil
}
func presentTimerFired() {
AssertIsOnMainThread()
clearTimer()
// Fade in the modal.
UIView.animate(withDuration: 0.35) {
self.view.layer.opacity = 1.0
}
}
func cancelPressed() {
AssertIsOnMainThread()
wasCancelled = true
dismiss {
}
}
}

View file

@ -483,36 +483,32 @@ const NSUInteger kNewGroupViewControllerAvatarWidth = 68;
});
};
UIAlertController *alertController =
[UIAlertController alertControllerWithTitle:NSLocalizedString(@"GROUP_CREATING", nil)
message:nil
preferredStyle:UIAlertControllerStyleAlert];
[ModalActivityIndicatorViewController
presentFromViewController:self
canCancel:NO
presentCompletion:^(ModalActivityIndicatorViewController *modalActivityIndicator) {
TSOutgoingMessage *message =
[[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:thread
groupMetaMessage:TSGroupMessageNew];
[self presentViewController:alertController
animated:YES
completion:^{
TSOutgoingMessage *message =
[[TSOutgoingMessage alloc] initWithTimestamp:[NSDate ows_millisecondTimeStamp]
inThread:thread
groupMetaMessage:TSGroupMessageNew];
// This will save the message.
[message updateWithCustomMessage:NSLocalizedString(@"GROUP_CREATED", nil)];
// This will save the message.
[message updateWithCustomMessage:NSLocalizedString(@"GROUP_CREATED", nil)];
if (model.groupImage) {
NSData *data = UIImagePNGRepresentation(model.groupImage);
id<DataSource> _Nullable dataSource =
[DataSourceValue dataSourceWithData:data fileExtension:@"png"];
[self.messageSender sendAttachmentData:dataSource
contentType:OWSMimeTypeImagePng
sourceFilename:nil
inMessage:message
success:successHandler
failure:failureHandler];
} else {
[self.messageSender sendMessage:message success:successHandler failure:failureHandler];
}
}];
if (model.groupImage) {
NSData *data = UIImagePNGRepresentation(model.groupImage);
id<DataSource> _Nullable dataSource =
[DataSourceValue dataSourceWithData:data fileExtension:@"png"];
[self.messageSender sendAttachmentData:dataSource
contentType:OWSMimeTypeImagePng
sourceFilename:nil
inMessage:message
success:successHandler
failure:failureHandler];
} else {
[self.messageSender sendMessage:message success:successHandler failure:failureHandler];
}
}];
}
- (TSGroupModel *)makeGroup

View file

@ -310,51 +310,46 @@ NSString *const kSelectRecipientViewControllerCellIdentifier = @"kSelectRecipien
if ([self.delegate shouldValidatePhoneNumbers]) {
// Show an alert while validating the recipient.
__block BOOL wasCancelled = NO;
UIAlertController *activityAlert = [UIAlertController
alertControllerWithTitle:NSLocalizedString(@"ALERT_VALIDATE_RECIPIENT_TITLE",
@"A title for the alert shown while validating a signal account")
message:NSLocalizedString(@"ALERT_VALIDATE_RECIPIENT_MESSAGE",
@"A message for the alert shown while validating a signal account")
preferredStyle:UIAlertControllerStyleAlert];
[activityAlert addAction:[UIAlertAction actionWithTitle:CommonStrings.cancelButton
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *_Nonnull action) {
wasCancelled = YES;
}]];
[[UIApplication sharedApplication].frontmostViewController presentViewController:activityAlert
animated:YES
completion:nil];
__weak SelectRecipientViewController *weakSelf = self;
[[ContactsUpdater sharedUpdater] lookupIdentifiers:possiblePhoneNumbers
success:^(NSArray<SignalRecipient *> *recipients) {
OWSAssert([NSThread isMainThread]);
OWSAssert(recipients.count > 0);
[ModalActivityIndicatorViewController
presentFromViewController:self
canCancel:YES
presentCompletion:^(ModalActivityIndicatorViewController *modalActivityIndicator) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[[ContactsUpdater sharedUpdater] lookupIdentifiers:possiblePhoneNumbers
success:^(NSArray<SignalRecipient *> *recipients) {
OWSAssert([NSThread isMainThread]);
OWSAssert(recipients.count > 0);
if (wasCancelled) {
return;
}
if (modalActivityIndicator.wasCancelled) {
return;
}
NSString *recipientId = recipients[0].uniqueId;
[activityAlert dismissViewControllerAnimated:NO
completion:^{
[weakSelf.delegate phoneNumberWasSelected:recipientId];
}];
}
failure:^(NSError *error) {
OWSAssert([NSThread isMainThread]);
if (wasCancelled) {
return;
}
[activityAlert dismissViewControllerAnimated:NO
completion:^{
[OWSAlerts
showAlertWithTitle:NSLocalizedString(@"ALERT_ERROR_TITLE",
@"Title for a generic error alert.")
message:error.localizedDescription];
}];
}];
NSString *recipientId = recipients[0].uniqueId;
[modalActivityIndicator
dismissViewControllerAnimated:NO
completion:^{
[weakSelf.delegate phoneNumberWasSelected:recipientId];
}];
}
failure:^(NSError *error) {
OWSAssert([NSThread isMainThread]);
if (modalActivityIndicator.wasCancelled) {
return;
}
[modalActivityIndicator
dismissViewControllerAnimated:NO
completion:^{
[OWSAlerts
showAlertWithTitle:
NSLocalizedString(@"ALERT_ERROR_TITLE",
@"Title for a generic error alert.")
message:error.localizedDescription];
}];
}];
});
}];
} else {
NSString *recipientId = possiblePhoneNumbers[0];
[self.delegate phoneNumberWasSelected:recipientId];