diff --git a/LegacyComponents.xcodeproj/project.pbxproj b/LegacyComponents.xcodeproj/project.pbxproj index deefe3536a..db94bbd2f7 100644 --- a/LegacyComponents.xcodeproj/project.pbxproj +++ b/LegacyComponents.xcodeproj/project.pbxproj @@ -524,6 +524,8 @@ D02660871F34B9B1000E2DC5 /* TGSearchBar.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660851F34B9B1000E2DC5 /* TGSearchBar.m */; }; D026608A1F34B9F9000E2DC5 /* TGSearchDisplayMixin.h in Headers */ = {isa = PBXBuildFile; fileRef = D02660881F34B9F9000E2DC5 /* TGSearchDisplayMixin.h */; settings = {ATTRIBUTES = (Public, ); }; }; D026608B1F34B9F9000E2DC5 /* TGSearchDisplayMixin.m in Sources */ = {isa = PBXBuildFile; fileRef = D02660891F34B9F9000E2DC5 /* TGSearchDisplayMixin.m */; }; + D0353815211CDF3700DBCA77 /* TGProxyWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = D0353813211CDF3600DBCA77 /* TGProxyWindow.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D0353816211CDF3700DBCA77 /* TGProxyWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = D0353814211CDF3700DBCA77 /* TGProxyWindow.m */; }; D04268F91F58687D0037ECE8 /* TGVideoCameraGLView.h in Headers */ = {isa = PBXBuildFile; fileRef = D04268F71F58687D0037ECE8 /* TGVideoCameraGLView.h */; settings = {ATTRIBUTES = (Public, ); }; }; D04268FA1F58687D0037ECE8 /* TGVideoCameraGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = D04268F81F58687D0037ECE8 /* TGVideoCameraGLView.m */; }; D04269031F586A070037ECE8 /* TGVideoMessageRingView.h in Headers */ = {isa = PBXBuildFile; fileRef = D04268FB1F586A070037ECE8 /* TGVideoMessageRingView.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1718,6 +1720,8 @@ D02660851F34B9B1000E2DC5 /* TGSearchBar.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGSearchBar.m; sourceTree = ""; }; D02660881F34B9F9000E2DC5 /* TGSearchDisplayMixin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGSearchDisplayMixin.h; sourceTree = ""; }; D02660891F34B9F9000E2DC5 /* TGSearchDisplayMixin.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGSearchDisplayMixin.m; sourceTree = ""; }; + D0353813211CDF3600DBCA77 /* TGProxyWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGProxyWindow.h; sourceTree = ""; }; + D0353814211CDF3700DBCA77 /* TGProxyWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGProxyWindow.m; sourceTree = ""; }; D04268F71F58687D0037ECE8 /* TGVideoCameraGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGVideoCameraGLView.h; sourceTree = ""; }; D04268F81F58687D0037ECE8 /* TGVideoCameraGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TGVideoCameraGLView.m; sourceTree = ""; }; D04268FB1F586A070037ECE8 /* TGVideoMessageRingView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TGVideoMessageRingView.h; sourceTree = ""; }; @@ -3591,6 +3595,8 @@ D07BC87A1F2A365000ED97AA /* TGProgressSpinnerView.m */, D07BC87B1F2A365000ED97AA /* TGProgressWindow.h */, D07BC87C1F2A365000ED97AA /* TGProgressWindow.m */, + D0353813211CDF3600DBCA77 /* TGProxyWindow.h */, + D0353814211CDF3700DBCA77 /* TGProxyWindow.m */, ); name = "Progress Window"; sourceTree = ""; @@ -4209,6 +4215,7 @@ D07BC8431F2A2D7900ED97AA /* TGPhotoCaptionInputMixin.h in Headers */, D01779311F20FFAC0044446D /* TGMediaAssetsLibrary.h in Headers */, D07BCAF71F2B50AA00ED97AA /* TGMediaAvatarEditorTransition.h in Headers */, + D0353815211CDF3700DBCA77 /* TGProxyWindow.h in Headers */, D07BC7211F2A29E400ED97AA /* TGPhotoToolCell.h in Headers */, D07BC9FF1F2A9A2B00ED97AA /* TGMediaPickerGalleryPhotoItem.h in Headers */, D07BC77E1F2A2B3700ED97AA /* TGPhotoEditorLinearBlurView.h in Headers */, @@ -4759,6 +4766,7 @@ D07BC6F01F2A19A700ED97AA /* TGCameraFlipButton.m in Sources */, D07BCB711F2B6A5600ED97AA /* TGEmbedVinePlayerView.m in Sources */, D07BCA0A1F2A9A2B00ED97AA /* TGMediaPickerGalleryVideoScrubber.m in Sources */, + D0353816211CDF3700DBCA77 /* TGProxyWindow.m in Sources */, D07BC9861F2A472900ED97AA /* TGPhotoStickersCollectionView.m in Sources */, D0B6D3E11F5986DD003BBB35 /* TGModernConversationInputMicButton.m in Sources */, D01778EC1F20CAE60044446D /* TGViewController+TGRecursiveEnumeration.m in Sources */, @@ -5864,6 +5872,85 @@ }; name = "Release AppStore"; }; + D0ADF938212B3AE400310BBC /* Debug AppStore LLC */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = "Debug AppStore LLC"; + }; + D0ADF939212B3AE400310BBC /* Debug AppStore LLC */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_IDENTITY = ""; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = ""; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(inherited)"; + HEADER_SEARCH_PATHS = ( + "$(PROJECT_DIR)/../../thirdparty/SSignalKit", + "$(inherited)", + ); + INFOPLIST_FILE = LegacyComponents/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 6.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + PRODUCT_BUNDLE_IDENTIFIER = org.Telegram.LegacyComponents; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SKIP_INSTALL = YES; + }; + name = "Debug AppStore LLC"; + }; D0F9720D1FFDBE23002595C8 /* Release Hockeyapp Internal */ = { isa = XCBuildConfiguration; buildSettings = { @@ -5944,6 +6031,7 @@ isa = XCConfigurationList; buildConfigurations = ( D017772D1F1F8F100044446D /* Debug AppStore */, + D0ADF938212B3AE400310BBC /* Debug AppStore LLC */, D013CAE8209C3C1E009A0B6F /* Debug Fork AppStore */, D07BCBFF1F2B79AE00ED97AA /* Debug Hockeyapp */, D01778931F1FA58C0044446D /* Debug */, @@ -5961,6 +6049,7 @@ isa = XCConfigurationList; buildConfigurations = ( D01777301F1F8F100044446D /* Debug AppStore */, + D0ADF939212B3AE400310BBC /* Debug AppStore LLC */, D013CAE9209C3C1E009A0B6F /* Debug Fork AppStore */, D07BCC001F2B79AE00ED97AA /* Debug Hockeyapp */, D01778941F1FA58C0044446D /* Debug */, diff --git a/LegacyComponents/LegacyComponents.h b/LegacyComponents/LegacyComponents.h index ce2c9b1fa4..33e5bd8eb0 100644 --- a/LegacyComponents/LegacyComponents.h +++ b/LegacyComponents/LegacyComponents.h @@ -317,11 +317,12 @@ FOUNDATION_EXPORT const unsigned char LegacyComponentsVersionString[]; #import #import +#import #import #import #import #import #import #import - +#import #import diff --git a/LegacyComponents/TGCache.h b/LegacyComponents/TGCache.h index df51b84d7f..f31845d248 100644 --- a/LegacyComponents/TGCache.h +++ b/LegacyComponents/TGCache.h @@ -31,6 +31,9 @@ typedef UIImage *(^TGCacheJpegDecodingBlock)(NSData *data); + (dispatch_queue_t)diskCacheQueue; + (NSFileManager *)diskFileManager; +- (id)init; +- (id)initWithCachesPath:(NSString *)cachesPath; + - (void)addTemporaryCachedImagesSource:(NSDictionary *)source autoremove:(bool)autoremove; - (void)removeTemporaryCachedImageSource:(NSDictionary *)source; diff --git a/LegacyComponents/TGCache.m b/LegacyComponents/TGCache.m index 9f0ede87a9..e202bfe64e 100644 --- a/LegacyComponents/TGCache.m +++ b/LegacyComponents/TGCache.m @@ -110,7 +110,11 @@ static NSFileManager *cacheFileManager = nil; return cacheFileManager; } -- (id)init +- (id)init { + return [self initWithCachesPath:[[LegacyComponentsGlobals provider] dataCachePath]]; +} + +- (id)initWithCachesPath:(NSString *)cachesPath { self = [super init]; if (self != nil) @@ -147,8 +151,6 @@ static NSFileManager *cacheFileManager = nil; _dataMemoryCache = [[NSMutableDictionary alloc] init]; _dataMemoryCacheSize = 0; - NSString *cachesPath = [[LegacyComponentsGlobals provider] dataCachePath]; - _diskCachePath = cachesPath; NSFileManager *fileManager = [[NSFileManager alloc] init]; if (![fileManager fileExistsAtPath:_diskCachePath]) diff --git a/LegacyComponents/TGMediaAvatarMenuMixin.h b/LegacyComponents/TGMediaAvatarMenuMixin.h index 885771f2ac..8ef40f1c5b 100644 --- a/LegacyComponents/TGMediaAvatarMenuMixin.h +++ b/LegacyComponents/TGMediaAvatarMenuMixin.h @@ -5,6 +5,8 @@ @class TGMenuSheetController; @class TGMediaAssetsController; +typedef void (^TGMediaAvatarPresentImpl)(id, void (^)(UIViewController *)); + @interface TGMediaAvatarMenuMixin : NSObject @property (nonatomic, copy) void (^didFinishWithImage)(UIImage *image); diff --git a/LegacyComponents/TGMediaAvatarMenuMixin.m b/LegacyComponents/TGMediaAvatarMenuMixin.m index 5802fac573..ce4615d76f 100644 --- a/LegacyComponents/TGMediaAvatarMenuMixin.m +++ b/LegacyComponents/TGMediaAvatarMenuMixin.m @@ -339,18 +339,24 @@ return; __weak TGMediaAvatarMenuMixin *weakSelf = self; - void (^presentBlock)(TGMediaAssetsController *) = nil; + UIViewController *(^presentBlock)(TGMediaAssetsController *) = nil; if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) { - presentBlock = ^(TGMediaAssetsController *controller) + presentBlock = ^UIViewController * (TGMediaAssetsController *controller) { __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; if (strongSelf == nil) - return; + return nil; + __weak TGMediaAssetsController *weakController = controller; controller.dismissalBlock = ^ { + __strong TGMediaAssetsController *strongController = weakController; + if (strongController != nil) { + [strongController dismissViewControllerAnimated:true completion:nil]; + } + __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; if (strongSelf == nil) return; @@ -361,16 +367,16 @@ strongSelf.didDismiss(); }; - [strongSelf->_parentController presentViewController:controller animated:true completion:nil]; + return controller; }; } else { - presentBlock = ^(TGMediaAssetsController *controller) + presentBlock = ^UIViewController * (TGMediaAssetsController *controller) { __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; if (strongSelf == nil) - return; + return nil; controller.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet; controller.modalPresentationStyle = UIModalPresentationFormSheet; @@ -402,6 +408,7 @@ if (strongSelf.didDismiss != nil) strongSelf.didDismiss(); }; + return nil; }; } @@ -411,31 +418,47 @@ if (strongSelf == nil) return; - TGMediaAssetsController *controller = [TGMediaAssetsController controllerWithContext:strongSelf->_context assetGroup:group intent:TGMediaAssetsControllerSetProfilePhotoIntent recipientName:nil saveEditedPhotos:strongSelf->_saveEditedPhotos allowGrouping:false]; - __weak TGMediaAssetsController *weakController = controller; - controller.avatarCompletionBlock = ^(UIImage *resultImage) - { + UIViewController *(^initPresent)(id) = ^UIViewController * (id context) { __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; - if (strongSelf == nil) - return; - - if (strongSelf.didFinishWithImage != nil) - strongSelf.didFinishWithImage(resultImage); - - __strong TGMediaAssetsController *strongController = weakController; - if (strongController != nil && strongController.dismissalBlock != nil) - strongController.dismissalBlock(); - }; - controller.requestSearchController = ^TGViewController * - { - __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; - __strong TGMediaAssetsController *strongController = weakController; if (strongSelf == nil) return nil; - return strongSelf.requestSearchController(strongController); + TGMediaAssetsController *controller = [TGMediaAssetsController controllerWithContext:context assetGroup:group intent:TGMediaAssetsControllerSetProfilePhotoIntent recipientName:nil saveEditedPhotos:strongSelf->_saveEditedPhotos allowGrouping:false]; + __weak TGMediaAssetsController *weakController = controller; + controller.avatarCompletionBlock = ^(UIImage *resultImage) + { + __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; + if (strongSelf == nil) + return; + + if (strongSelf.didFinishWithImage != nil) + strongSelf.didFinishWithImage(resultImage); + + __strong TGMediaAssetsController *strongController = weakController; + if (strongController != nil && strongController.dismissalBlock != nil) + strongController.dismissalBlock(); + }; + if (strongSelf.requestSearchController != nil) { + controller.requestSearchController = ^TGViewController * + { + __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; + __strong TGMediaAssetsController *strongController = weakController; + if (strongSelf == nil) + return nil; + + return strongSelf.requestSearchController(strongController); + }; + } + return presentBlock(controller); }; - presentBlock(controller); + + [strongSelf->_parentController presentWithContext:^UIViewController *(id context) { + __strong TGMediaAvatarMenuMixin *strongSelf = weakSelf; + if (strongSelf == nil) + return nil; + + return initPresent(context); + }]; }; if ([TGMediaAssetsLibrary authorizationStatus] == TGMediaLibraryAuthorizationStatusNotDetermined) diff --git a/LegacyComponents/TGNavigationController.h b/LegacyComponents/TGNavigationController.h index 6308cffd2e..8acf92201c 100644 --- a/LegacyComponents/TGNavigationController.h +++ b/LegacyComponents/TGNavigationController.h @@ -30,6 +30,8 @@ typedef enum { @property (nonatomic) CGFloat currentAdditionalNavigationBarHeight; @property (nonatomic) bool forceAdditionalNavigationBarHeight; +@property (nonatomic, copy) void (^customDismissSelf)(void); + + (TGNavigationController *)navigationControllerWithControllers:(NSArray *)controllers; + (TGNavigationController *)navigationControllerWithControllers:(NSArray *)controllers navigationBarClass:(Class)navigationBarClass; + (TGNavigationController *)navigationControllerWithControllers:(NSArray *)controllers navigationBarClass:(Class)navigationBarClass inhibitPresentation:(bool)inhibitPresentation; diff --git a/LegacyComponents/TGNavigationController.m b/LegacyComponents/TGNavigationController.m index f3c907db29..fbe49578f6 100644 --- a/LegacyComponents/TGNavigationController.m +++ b/LegacyComponents/TGNavigationController.m @@ -914,6 +914,17 @@ TGNavigationController *findNavigationController() return self.topViewController.preferredStatusBarStyle; } +- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)())completion { + if (_customDismissSelf) { + _customDismissSelf(); + if (completion) { + completion(); + } + } else { + [super dismissViewControllerAnimated:flag completion:completion]; + } +} + @end @implementation TGNavigationPercentTransition diff --git a/LegacyComponents/TGProxyWindow.h b/LegacyComponents/TGProxyWindow.h new file mode 100644 index 0000000000..de02893a55 --- /dev/null +++ b/LegacyComponents/TGProxyWindow.h @@ -0,0 +1,21 @@ +#import + +#import + +@interface TGProxyWindowController : TGOverlayWindowViewController + +- (instancetype)initWithLight:(bool)light; + +- (void)dismissWithSuccess:(void (^)(void))completion; +- (void)updateLayout; + +@end + +@interface TGProxyWindow : UIWindow + +- (void)dismissWithSuccess; ++ (void)setDarkStyle:(bool)dark; + +@end + + diff --git a/LegacyComponents/TGProxyWindow.m b/LegacyComponents/TGProxyWindow.m new file mode 100644 index 0000000000..aed95891b6 --- /dev/null +++ b/LegacyComponents/TGProxyWindow.m @@ -0,0 +1,468 @@ +#import "TGProxyWindow.h" +#import + +#import "LegacyComponentsInternal.h" +#import "TGImageUtils.h" + +static UIImage *generateShieldImage(UIColor *color) { + NSString *code = @"M100,6.56393754 L6,48.2657557 L6,110.909091 C6,169.509174 46.3678836,223.966692 100,237.814087 C153.632116,223.966692 194,169.509174 194,110.909091 L194,48.2657557 L100,6.56393754 S"; + + UIGraphicsBeginImageContextWithOptions(CGSizeMake(67, 82), false, 0.0f); + + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextScaleCTM(context, 0.333333f, 0.333333f); + CGContextSetLineWidth(context, 12.0f); + CGContextSetStrokeColorWithColor(context, color.CGColor); + TGDrawSvgPath(context, code); + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; +} + +static bool TGProxyWindowIsLight = true; + +@interface TGProxySpinnerView : UIView + +@property (nonatomic, copy) void (^onSuccess)(void); + +- (instancetype)initWithFrame:(CGRect)frame light:(bool)light; + +- (void)setSucceed; + +@end + +@interface TGProxyWindowController () +{ + bool _light; + UIVisualEffectView *_effectView; + UIView *_backgroundView; + TGProxySpinnerView *_spinner; + UIImageView *_shield; + UILabel *_label; +} + +@property (nonatomic, weak) UIWindow *weakWindow; +@property (nonatomic, strong) UIView *containerView; + +@end + +@implementation TGProxyWindowController + +- (instancetype)init { + return [self initWithLight:TGProxyWindowIsLight]; +} + +- (instancetype)initWithLight:(bool)light { + self = [super init]; + if (self != nil) { + _light = light; + } + return self; +} + +- (void)loadView +{ + [super loadView]; + + if (self.view.bounds.size.width > FLT_EPSILON) { + [self updateLayout]; + } +} + +- (void)updateLayout { + if (_containerView == nil) { + _containerView = [[UIView alloc] initWithFrame:CGRectMake(CGFloor(self.view.frame.size.width - 156) / 2, CGFloor(self.view.frame.size.height - 176) / 2, 156, 176)]; + _containerView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleBottomMargin; + _containerView.alpha = 0.0f; + _containerView.clipsToBounds = true; + _containerView.layer.cornerRadius = 20.0f; + [self.view addSubview:_containerView]; + + if (iosMajorVersion() >= 9) { + _effectView = [[UIVisualEffectView alloc] initWithEffect:_light ? [UIBlurEffect effectWithStyle:UIBlurEffectStyleLight] : [UIBlurEffect effectWithStyle:UIBlurEffectStyleDark]]; + _effectView.frame = _containerView.bounds; + [_containerView addSubview:_effectView]; + + if (_light) + { + UIView *tintView = [[UIView alloc] initWithFrame:_effectView.bounds]; + tintView.backgroundColor = UIColorRGBA(0xf4f4f4, 0.75f); + [_containerView addSubview:tintView]; + } + } else { + _backgroundView = [[UIView alloc] initWithFrame:_containerView.bounds]; + _backgroundView.backgroundColor = _light ? UIColorRGBA(0xeaeaea, 0.92f) : UIColorRGBA(0x000000, 0.9f); + [_containerView addSubview:_backgroundView]; + } + + UIColor *color = _light ? UIColorRGB(0x5a5a5a) : [UIColor whiteColor]; + + _shield = [[UIImageView alloc] initWithImage:generateShieldImage(color)]; + _shield.frame = CGRectMake((_containerView.frame.size.width - _shield.frame.size.width) / 2.0f, 23.0f, _shield.frame.size.width, _shield.frame.size.height); + [_containerView addSubview:_shield]; + + _spinner = [[TGProxySpinnerView alloc] initWithFrame:CGRectMake((_containerView.frame.size.width - 48.0f) / 2.0f, 40.0f, 48.0f, 48.0f) light:_light]; + [_containerView addSubview:_spinner]; + + NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init]; + style.lineBreakMode = NSLineBreakByWordWrapping; + style.lineSpacing = 2.0f; + style.alignment = NSTextAlignmentCenter; + + NSDictionary *attributes = @{NSForegroundColorAttributeName:_light ? UIColorRGB(0x5a5a5a) : [UIColor whiteColor], NSFontAttributeName:TGMediumSystemFontOfSize(17.0f), NSParagraphStyleAttributeName:style}; + NSAttributedString *string = [[NSAttributedString alloc] initWithString:TGLocalized(@"SocksProxySetup.ProxyEnabled") attributes:attributes]; + + UILabel *label = [[UILabel alloc] init]; + label.font = TGSystemFontOfSize(15.0f); + label.numberOfLines = 2; + label.textAlignment = NSTextAlignmentCenter; + label.attributedText = string; + _label = label; + [label sizeToFit]; + label.frame = CGRectMake((_containerView.frame.size.width - label.frame.size.width) / 2.0f, _containerView.frame.size.height - label.frame.size.height - 18.0f, label.frame.size.width, label.frame.size.height); + [_containerView addSubview:label]; + } else { + _containerView.frame = CGRectMake(CGFloor(self.view.frame.size.width - 156) / 2, CGFloor(self.view.frame.size.height - 176) / 2, 156, 176); + _effectView.frame = _containerView.bounds; + _backgroundView.frame = _containerView.bounds; + _spinner.frame = CGRectMake((_containerView.frame.size.width - 48.0f) / 2.0f, 40.0f, 48.0f, 48.0f); + _shield.frame = CGRectMake((_containerView.frame.size.width - _shield.frame.size.width) / 2.0f, 23.0f, _shield.frame.size.width, _shield.frame.size.height); + [_label sizeToFit]; + _label.frame = CGRectMake((_containerView.frame.size.width - _label.frame.size.width) / 2.0f, _containerView.frame.size.height - _label.frame.size.height - 18.0f, _label.frame.size.width, _label.frame.size.height); + } +} + +- (void)dismissWithSuccess:(void (^)(void))completion +{ + TGProxyWindow *window = (TGProxyWindow *)_weakWindow; + + window.userInteractionEnabled = false; + + void (^dismissBlock)(void) = ^{ + [UIView animateWithDuration:0.3 delay:0.55 options:0 animations:^{ + _containerView.alpha = 0.0f; + } completion:^(BOOL finished) { + if (finished) { + if (completion) { + completion(); + } + window.hidden = true; + } + }]; + }; + + if (window.hidden || window == nil) { + window.hidden = false; + _containerView.transform = CGAffineTransformMakeScale(0.6f, 0.6f); + + if (iosMajorVersion() >= 7) { + [UIView animateWithDuration:0.3 delay:0.0 options:7 << 16 animations:^{ + _containerView.transform = CGAffineTransformIdentity; + } completion:nil]; + } + + [UIView animateWithDuration:0.3f animations:^{ + _containerView.alpha = 1.0f; + if (iosMajorVersion() < 7) + _containerView.transform = CGAffineTransformIdentity; + } completion:^(__unused BOOL finished) { + dismissBlock(); + }]; + + TGDispatchAfter(0.15, dispatch_get_main_queue(), ^{ + [_spinner setSucceed]; + }); + } else { + _spinner.onSuccess = ^{ + dismissBlock(); + }; + [_spinner setSucceed]; + } +} + +- (BOOL)canBecomeFirstResponder { + return false; +} + +@end + +@interface TGProxyWindow () { + bool _dismissed; + bool _appeared; +} + +@end + +@implementation TGProxyWindow + +- (instancetype)init { + return [self initWithFrame:[[UIScreen mainScreen] bounds]]; +} + +- (id)initWithFrame:(CGRect)frame +{ + self = [super initWithFrame:frame]; + if (self) + { + self.windowLevel = UIWindowLevelStatusBar; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + + TGProxyWindowController *controller = [[TGProxyWindowController alloc] init]; + controller.weakWindow = self; + self.rootViewController = controller; + + self.opaque = false; + } + return self; +} + +- (void)dismissWithSuccess +{ + if (!_dismissed) { + _dismissed = true; + [((TGProxyWindowController *)self.rootViewController) dismissWithSuccess:nil]; + } +} + ++ (void)setDarkStyle:(bool)dark +{ + TGProxyWindowIsLight = !dark; +} + +@end + + +@interface TGProxySpinnerViewInternal : UIView + +@property (nonatomic, copy) void (^onDraw)(void); +@property (nonatomic, copy) void (^onSuccess)(void); + +- (instancetype)initWithFrame:(CGRect)frame light:(bool)light; + +- (void)setSucceed:(bool)fromRotation progress:(CGFloat)progress; + +@end + +@interface TGProxySpinnerView () +{ + TGProxySpinnerViewInternal *_internalView; + + bool _progressing; +} +@end + +@implementation TGProxySpinnerView + +- (instancetype)initWithFrame:(CGRect)frame light:(bool)light { + self = [super initWithFrame:frame]; + if (self != nil) { + self.backgroundColor = [UIColor clearColor]; + self.opaque = false; + self.userInteractionEnabled = false; + + _internalView = [[TGProxySpinnerViewInternal alloc] initWithFrame:self.bounds light:light]; + _internalView.hidden = true; + [self addSubview:_internalView]; + } + return self; +} + +- (void)setSucceed { + _internalView.hidden = false; + + [_internalView setSucceed:false progress:0.0f]; +} + +@end + +@interface TGProxySpinnerViewInternal () +{ + CADisplayLink *_displayLink; + + bool _light; + + bool _isProgressing; + CGFloat _rotationValue; + bool _isRotating; + + CGFloat _checkValue; + bool _delay; + bool _isSucceed; + bool _isChecking; + + NSTimeInterval _previousTime; +} +@end + +@implementation TGProxySpinnerViewInternal + +- (instancetype)initWithFrame:(CGRect)frame light:(bool)light { + self = [super initWithFrame:frame]; + if (self != nil) { + _light = light; + + self.backgroundColor = [UIColor clearColor]; + self.opaque = false; + self.userInteractionEnabled = false; + } + return self; +} + +- (void)dealloc { + _displayLink.paused = true; + [_displayLink removeFromRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; +} + +- (CADisplayLink *)displayLink { + if (_displayLink == nil) { + _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkUpdate)]; + _displayLink.paused = true; + [_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; + } + return _displayLink; +} + +- (void)drawRect:(CGRect)rect { + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGPoint centerPoint = CGPointMake(rect.size.width / 2.0f, rect.size.height / 2.0f); + CGFloat lineWidth = 4.0f; + CGFloat inset = 3.0f; + + UIColor *foregroundColor = _light ? UIColorRGB(0x5a5a5a) : [UIColor whiteColor]; + CGContextSetFillColorWithColor(context, foregroundColor.CGColor); + CGContextSetStrokeColorWithColor(context, foregroundColor.CGColor); + + if (_isProgressing) + { + CGMutablePathRef path = CGPathCreateMutable(); + CGFloat offset = -_rotationValue * 2.0f * M_PI; + CGPathAddArc(path, NULL, centerPoint.x, centerPoint.y, (rect.size.width - inset * 2.0f - lineWidth) / 2.0f, offset, offset + (3.0f * M_PI_2) * (1.0f - _checkValue), false); + CGPathRef strokedArc = CGPathCreateCopyByStrokingPath(path, NULL, lineWidth, kCGLineCapRound, kCGLineJoinMiter, 10); + CGContextAddPath(context, strokedArc); + CGPathRelease(strokedArc); + CGPathRelease(path); + + CGContextFillPath(context); + } + + if (_checkValue > FLT_EPSILON) + { + CGContextSetLineWidth(context, 4.0f); + CGContextSetLineCap(context, kCGLineCapRound); + CGContextSetLineJoin(context, kCGLineJoinRound); + CGContextSetMiterLimit(context, 10); + + CGFloat firstSegment = MIN(1.0f, _checkValue * 3.0f); + CGPoint s = CGPointMake(inset + 5.0f, centerPoint.y + 1.0f); + CGPoint p1 = CGPointMake(10.0f, 10.0f); + CGPoint p2 = CGPointMake(23.0f, -23.0f); + + if (firstSegment < 1.0f) + { + CGContextMoveToPoint(context, s.x + p1.x * firstSegment, s.y + p1.y * firstSegment); + CGContextAddLineToPoint(context, s.x, s.y); + } + else + { + CGFloat secondSegment = (_checkValue - 0.33f) * 1.5f; + CGContextMoveToPoint(context, s.x + p1.x + p2.x * secondSegment, s.y + p1.y + p2.y * secondSegment); + CGContextAddLineToPoint(context, s.x + p1.x, s.y + p1.y); + CGContextAddLineToPoint(context, s.x, s.y); + } + + CGContextStrokePath(context); + } +} + +- (void)displayLinkUpdate +{ + NSTimeInterval previousTime = _previousTime; + NSTimeInterval currentTime = CACurrentMediaTime(); + _previousTime = currentTime; + + NSTimeInterval delta = previousTime > DBL_EPSILON ? currentTime - previousTime : 0.0; + if (delta < DBL_EPSILON) + return; + + if (_isRotating) + { + _rotationValue += delta * 1.35f; + } + + if (_isSucceed && _isRotating && !_delay && _rotationValue >= 0.5f) + { + _rotationValue = 0.5f; + _isRotating = false; + _isChecking = true; + } + + if (_isChecking) + _checkValue += delta * M_PI * 1.6f; + + if (_rotationValue > 1.0f) + { + _rotationValue = 0.0f; + _delay = false; + } + + if (_checkValue > 1.0f) + { + _checkValue = 1.0f; + [self displayLink].paused = true; + + if (self.onSuccess != nil) + { + void (^onSuccess)(void) = [self.onSuccess copy]; + self.onSuccess = nil; + onSuccess(); + } + } + + [self setNeedsDisplay]; + + if (self.onDraw != nil) + { + void (^onDraw)(void) = [self.onDraw copy]; + self.onDraw = nil; + onDraw(); + } +} + +- (void)setProgress { + _isRotating = true; + _isProgressing = true; + + [self displayLink].paused = false; +} + +- (void)setSucceed:(bool)fromRotation progress:(CGFloat)progress { + if (_isSucceed) + return; + + if (fromRotation) { + _isRotating = true; + _isProgressing = true; + _rotationValue = progress; + } + + _isSucceed = true; + + if (!_isRotating) + _isChecking = true; + else if (_rotationValue > 0.5f) + _delay = true; + + [self displayLink].paused = false; +} + +- (bool)isSucceed +{ + return _isSucceed; +} + +@end + + diff --git a/LegacyComponents/TGViewController.h b/LegacyComponents/TGViewController.h index e03b77f509..8234a9b5e2 100644 --- a/LegacyComponents/TGViewController.h +++ b/LegacyComponents/TGViewController.h @@ -108,7 +108,10 @@ typedef enum { @property (nonatomic, readonly) UIUserInterfaceSizeClass currentSizeClass; @property (nonatomic, copy) NSArray> *(^externalPreviewActionItems)(void); -@property (nonatomic, copy) void (^customRemoveFromParentViewController)(); +@property (nonatomic, copy) void (^customRemoveFromParentViewController)(void); +@property (nonatomic, copy) void (^customDismissSelf)(void); + +@property (nonatomic, readonly) id context; - (id)initWithContext:(id)context NS_DESIGNATED_INITIALIZER; @@ -162,6 +165,8 @@ typedef enum { - (void)layoutControllerForSize:(CGSize)size duration:(NSTimeInterval)duration; +- (void)presentWithContext:(UIViewController *(^)(id))generator; + @end @protocol TGDestructableViewController diff --git a/LegacyComponents/TGViewController.mm b/LegacyComponents/TGViewController.mm index 44393b843f..b386fd15ec 100644 --- a/LegacyComponents/TGViewController.mm +++ b/LegacyComponents/TGViewController.mm @@ -420,6 +420,15 @@ static id _defaultContext = nil; [_associatedPopoverController dismissPopoverAnimated:false]; } +- (id)context { + return _context; +} + +- (void)presentWithContext:(UIViewController *(^)(id))generator { + UIViewController *controller = generator(_context); + [self presentViewController:controller animated:true completion:nil]; +} + - (NSMutableArray *)associatedWindowStack { if (_associatedWindowStack == nil) @@ -1494,6 +1503,17 @@ static id _defaultContext = nil; [super removeFromParentViewController]; } +- (void)dismissViewControllerAnimated:(BOOL)flag completion:(void (^)())completion { + if (_customDismissSelf) { + _customDismissSelf(); + if (completion) { + completion(); + } + } else { + [super dismissViewControllerAnimated:flag completion:completion]; + } +} + @end @interface UINavigationController (DelegateAutomaticDismissKeyboard)