1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-03 09:57:46 +01:00
Telegram/legacy/TelegraphKit/TGNavigationController.m
2015-10-01 19:19:52 +03:00

671 lines
22 KiB
Objective-C

#import "TGNavigationController.h"
#import "Freedom.h"
#import "TGNavigationBar.h"
#import "TGViewController.h"
#import "TGToolbarButton.h"
#import "TGHacks.h"
#import "TGRTLScreenEdgePanGestureRecognizer.h"
#import "TGImageUtils.h"
#import <QuartzCore/QuartzCore.h>
#import <objc/runtime.h>
#import <objc/message.h>
#import "TGViewController.h"
#import "TGMainTabsController.h"
#import "TGTelegraph.h"
@interface TGNavigationPercentTransition : UIPercentDrivenInteractiveTransition
@end
@interface TGNavigationController () <UINavigationControllerDelegate>
{
UITapGestureRecognizer *_dimmingTapRecognizer;
CGSize _preferredContentSize;
id<SDisposable> _playerStatusDisposable;
CGFloat _currentAdditionalNavigationBarHeight;
}
@property (nonatomic) bool wasShowingNavigationBar;
@property (nonatomic, strong) TGAutorotationLock *autorotationLock;
@end
@implementation TGNavigationController
+ (TGNavigationController *)navigationControllerWithRootController:(UIViewController *)controller
{
return [self navigationControllerWithControllers:[NSArray arrayWithObject:controller]];
}
+ (TGNavigationController *)navigationControllerWithControllers:(NSArray *)controllers
{
return [self navigationControllerWithControllers:controllers navigationBarClass:[TGNavigationBar class]];
}
+ (TGNavigationController *)navigationControllerWithControllers:(NSArray *)controllers navigationBarClass:(Class)navigationBarClass
{
if (iosMajorVersion() >= 7)
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
if ([TGViewController useExperimentalRTL])
{
InjectInstanceMethodFromAnotherClass([TGNavigationController class], [TGNavigationController class], @selector(replacedKeyboardDirection:arg2:), NSSelectorFromString(TGEncodeText(@"`lfzcpbseEjsfdujpoGpsUsbotjujpo;psefsjohJo;", -1)));
}
});
}
TGNavigationController *navigationController = [[TGNavigationController alloc] initWithNavigationBarClass:navigationBarClass toolbarClass:[UIToolbar class]];
bool first = true;
for (id controller in controllers) {
if ([controller isKindOfClass:[TGViewController class]]) {
[(TGViewController *)controller setIsFirstInStack:first];
}
first = false;
}
[navigationController setViewControllers:controllers];
((TGNavigationBar *)navigationController.navigationBar).navigationController = navigationController;
return navigationController;
}
- (instancetype)initWithNavigationBarClass:(Class)navigationBarClass toolbarClass:(Class)toolbarClass
{
self = [super initWithNavigationBarClass:navigationBarClass toolbarClass:toolbarClass];
if (self != nil)
{
}
return self;
}
- (void)dealloc
{
[_playerStatusDisposable dispose];
self.delegate = nil;
[_dimmingTapRecognizer.view removeGestureRecognizer:_dimmingTapRecognizer];
}
- (void)loadView
{
[super loadView];
/*if ([TGViewController useExperimentalRTL])
((UIView *)self.view.subviews[0]).transform = CGAffineTransformMakeScale(-1.0f, 1.0f);
if ([TGViewController useExperimentalRTL])
{
SEL selector = NSSelectorFromString(TGEncodeText(@"`tdsffoFehfQboHftuvsfSfdphoj{fs", -1));
if ([self respondsToSelector:selector])
{
UIScreenEdgePanGestureRecognizer *screenPanRecognizer = objc_msgSend(self, selector);
if (screenPanRecognizer != nil)
{
screenPanRecognizer.edges = UIRectEdgeRight;
object_setClass(screenPanRecognizer, [TGRTLScreenEdgePanGestureRecognizer class]);
}
}
}*/
}
- (void)setDisplayPlayer:(bool)displayPlayer
{
_displayPlayer = displayPlayer;
if (_displayPlayer && [self.navigationBar isKindOfClass:[TGNavigationBar class]])
{
__weak TGNavigationController *weakSelf = self;
[_playerStatusDisposable dispose];
_playerStatusDisposable = [[[[TGTelegraphInstance musicPlayer] playingStatus] deliverOn:[SQueue mainQueue]] startWithNext:^(TGMusicPlayerStatus *status)
{
__strong TGNavigationController *strongSelf = weakSelf;
if (strongSelf != nil)
{
if (status != nil && strongSelf->_currentAdditionalNavigationBarHeight < FLT_EPSILON)
{
strongSelf->_minimizePlayer = false;
[(TGNavigationBar *)self.navigationBar setMinimizedMusicPlayer:_minimizePlayer];
}
CGFloat currentAdditionalNavigationBarHeight = status == nil ? 0.0f : (strongSelf->_minimizePlayer ? 2.0f : 36.0f);
if (ABS(strongSelf->_currentAdditionalNavigationBarHeight - currentAdditionalNavigationBarHeight) > FLT_EPSILON)
{
strongSelf->_currentAdditionalNavigationBarHeight = currentAdditionalNavigationBarHeight;
[((TGNavigationBar *)strongSelf.navigationBar) showMusicPlayerView:status != nil animation:^
{
[strongSelf updatePlayerOnControllers];
}];
}
}
}];
}
else
{
[_playerStatusDisposable dispose];
}
}
- (void)setMinimizePlayer:(bool)minimizePlayer
{
if (_minimizePlayer != minimizePlayer)
{
_minimizePlayer = minimizePlayer;
if (_currentAdditionalNavigationBarHeight > FLT_EPSILON)
_currentAdditionalNavigationBarHeight = _minimizePlayer ? 2.0f : 36.0f;
[(TGNavigationBar *)self.navigationBar setMinimizedMusicPlayer:_minimizePlayer];
[UIView animateWithDuration:0.25 animations:^
{
[self updatePlayerOnControllers];
}];
}
}
static UIView *findDimmingView(UIView *view)
{
static NSString *encodedString = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
encodedString = TGEncodeText(@"VJEjnnjohWjfx", -1);
});
if ([NSStringFromClass(view.class) isEqualToString:encodedString])
return view;
for (UIView *subview in view.subviews)
{
UIView *result = findDimmingView(subview);
if (result != nil)
return result;
}
return nil;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (self.modalPresentationStyle == UIModalPresentationFormSheet)
{
UIView *dimmingView = findDimmingView(self.view.window);
bool tapSetup = false;
if (_dimmingTapRecognizer != nil)
{
for (UIGestureRecognizer *recognizer in dimmingView.gestureRecognizers)
{
if (recognizer == _dimmingTapRecognizer)
{
tapSetup = true;
break;
}
}
}
if (!tapSetup)
{
_dimmingTapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dimmingViewTapped:)];
[dimmingView addGestureRecognizer:_dimmingTapRecognizer];
}
}
}
- (void)dimmingViewTapped:(UITapGestureRecognizer *)recognizer
{
if (recognizer.state == UIGestureRecognizerStateRecognized)
{
[self.presentingViewController dismissViewControllerAnimated:true completion:nil];
}
}
- (void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
CGSize screenSize = TGScreenSize();
static Class containerClass = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
containerClass = freedomClass(0xf045e5dfU);
});
for (UIView *view in self.view.subviews)
{
if ([view isKindOfClass:containerClass])
{
CGRect frame = view.frame;
if (ABS(frame.size.width - screenSize.width) < FLT_EPSILON)
{
if (ABS(frame.size.height - screenSize.height + 20) < FLT_EPSILON)
{
frame.origin.y = frame.size.height - screenSize.height;
frame.size.height = screenSize.height;
}
else if (frame.size.height > screenSize.height + FLT_EPSILON)
{
frame.origin.y = 0;
frame.size.height = screenSize.height;
}
}
else if (ABS(frame.size.width - screenSize.height) < FLT_EPSILON)
{
if (frame.size.height > screenSize.width + FLT_EPSILON)
{
frame.origin.y = 0;
frame.size.height = screenSize.width;
}
}
if (ABS(frame.size.height) < FLT_EPSILON)
{
frame.size.height = screenSize.height;
}
if (!CGRectEqualToRect(view.frame, frame))
view.frame = frame;
break;
}
}
}
- (void)viewDidLoad
{
self.delegate = self;
[super viewDidLoad];
}
- (void)updateControllerLayout:(bool)__unused animated
{
}
- (void)setupNavigationBarForController:(UIViewController *)viewController animated:(bool)animated
{
UIBarStyle barStyle = UIBarStyleDefault;
bool navigationBarShouldBeHidden = false;
UIStatusBarStyle statusBarStyle = UIStatusBarStyleBlackOpaque;
bool statusBarShouldBeHidden = false;
if ([viewController conformsToProtocol:@protocol(TGViewControllerNavigationBarAppearance)])
{
id<TGViewControllerNavigationBarAppearance> appearance = (id<TGViewControllerNavigationBarAppearance>)viewController;
barStyle = [appearance requiredNavigationBarStyle];
navigationBarShouldBeHidden = [appearance navigationBarShouldBeHidden];
if ([appearance respondsToSelector:@selector(preferredStatusBarStyle)])
statusBarStyle = [appearance preferredStatusBarStyle];
if ([appearance respondsToSelector:@selector(statusBarShouldBeHidden)])
statusBarShouldBeHidden = [appearance statusBarShouldBeHidden];
}
if (navigationBarShouldBeHidden != self.navigationBarHidden)
{
[self setNavigationBarHidden:navigationBarShouldBeHidden animated:animated];
}
if ([[UIApplication sharedApplication] isStatusBarHidden] != statusBarShouldBeHidden)
[[UIApplication sharedApplication] setStatusBarHidden:statusBarShouldBeHidden withAnimation:animated ? UIStatusBarAnimationFade : UIStatusBarAnimationNone];
if ([[UIApplication sharedApplication] statusBarStyle] != statusBarStyle)
[[UIApplication sharedApplication] setStatusBarStyle:statusBarStyle animated:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (_restrictLandscape)
return interfaceOrientation == UIInterfaceOrientationPortrait;
if (self.topViewController != nil)
return [self.topViewController shouldAutorotateToInterfaceOrientation:interfaceOrientation];
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (BOOL)shouldAutorotate
{
if (_restrictLandscape)
return false;
if (self.topViewController != nil)
{
if ([self.topViewController respondsToSelector:@selector(shouldAutorotate)])
{
if (![self.topViewController shouldAutorotate])
return false;
}
}
bool result = [super shouldAutorotate];
if (!result)
return false;
if ([self respondsToSelector:@selector(interactivePopGestureRecognizer)])
{
UIGestureRecognizerState state = self.interactivePopGestureRecognizer.state;
if (state == UIGestureRecognizerStateBegan || state == UIGestureRecognizerStateChanged)
return false;
}
return true;
}
- (void)acquireRotationLock
{
if (_autorotationLock == nil)
_autorotationLock = [[TGAutorotationLock alloc] init];
}
- (void)releaseRotationLock
{
_autorotationLock = nil;
}
- (NSUInteger)supportedInterfaceOrientations
{
if (_restrictLandscape)
return UIInterfaceOrientationMaskPortrait;
return UIInterfaceOrientationMaskAllButUpsideDown;
}
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willAnimateRotationToInterfaceOrientation:toInterfaceOrientation duration:duration];
}
- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated
{
if (!hidden)
self.navigationBar.alpha = 1.0f;
[(TGNavigationBar *)self.navigationBar setHiddenState:hidden animated:animated];
[super setNavigationBarHidden:hidden animated:animated];
}
- (void)setupPlayerOnControllers:(NSArray *)controllers
{
if (_displayPlayer && [[self navigationBar] isKindOfClass:[TGNavigationBar class]])
{
for (id maybeController in controllers)
{
if ([maybeController isKindOfClass:[TGViewController class]])
{
TGViewController *controller = maybeController;
[controller setAdditionalNavigationBarHeight:_currentAdditionalNavigationBarHeight];
}
else if ([maybeController isKindOfClass:[TGMainTabsController class]])
{
[self setupPlayerOnControllers:((TGMainTabsController *)maybeController).viewControllers];
}
}
}
}
- (void)updatePlayerOnControllers
{
if (_displayPlayer && [[self navigationBar] isKindOfClass:[TGNavigationBar class]])
{
for (id maybeController in [self viewControllers])
{
if ([maybeController isKindOfClass:[TGViewController class]])
{
[((TGViewController *)maybeController) setAdditionalNavigationBarHeight:_currentAdditionalNavigationBarHeight];
}
else if ([maybeController isKindOfClass:[TGMainTabsController class]])
{
for (id controller in ((TGMainTabsController *)maybeController).viewControllers)
{
if ([controller isKindOfClass:[TGViewController class]])
{
[((TGViewController *)controller) setAdditionalNavigationBarHeight:_currentAdditionalNavigationBarHeight];
}
}
}
}
}
}
- (void)pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
if (self.viewControllers.count == 0) {
if ([viewController isKindOfClass:[TGViewController class]]) {
[(TGViewController *)viewController setIsFirstInStack:true];
} else {
[(TGViewController *)viewController setIsFirstInStack:false];
}
}
_isInControllerTransition = true;
if (viewController != nil)
[self setupPlayerOnControllers:@[viewController]];
[super pushViewController:viewController animated:animated];
_isInControllerTransition = false;
}
- (void)setViewControllers:(NSArray *)viewControllers animated:(BOOL)animated
{
bool first = true;
for (id controller in viewControllers) {
if ([controller isKindOfClass:[TGViewController class]]) {
[(TGViewController *)controller setIsFirstInStack:first];
}
first = false;
}
_isInControllerTransition = true;
[self setupPlayerOnControllers:viewControllers];
[super setViewControllers:viewControllers animated:animated];
_isInControllerTransition = false;
}
- (UIViewController *)popViewControllerAnimated:(BOOL)animated
{
if (animated)
{
static ptrdiff_t controllerOffset = -1;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
controllerOffset = freedomIvarOffset([UINavigationController class], 0xb281e8fU);
});
if (controllerOffset != -1)
{
__unsafe_unretained NSObject **controller = (__unsafe_unretained NSObject **)(void *)(((uint8_t *)(__bridge void *)self) + controllerOffset);
if (*controller != nil)
{
static Class decoratedClass = Nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
decoratedClass = freedomMakeClass([*controller class], [TGNavigationPercentTransition class]);
});
if (decoratedClass != Nil && ![*controller isKindOfClass:decoratedClass])
object_setClass(*controller, decoratedClass);
}
}
}
_isInPopTransition = true;
UIViewController *result = [super popViewControllerAnimated:animated];
_isInPopTransition = false;
return result;
}
- (NSArray *)popToRootViewControllerAnimated:(BOOL)animated
{
for (NSUInteger i = self.viewControllers.count - 1; i >= 1; i--)
{
UIViewController *viewController = self.viewControllers[i];
if (viewController.presentedViewController != nil)
[viewController dismissViewControllerAnimated:false completion:nil];
}
return [super popToRootViewControllerAnimated:animated];
}
TGNavigationController *findNavigationControllerInWindow(UIWindow *window)
{
if ([window.rootViewController isKindOfClass:[TGNavigationController class]])
return (TGNavigationController *)window.rootViewController;
return nil;
}
TGNavigationController *findNavigationController()
{
NSArray *windows = [UIApplication sharedApplication].windows;
for (int i = (int)windows.count - 1; i >= 0; i--)
{
TGNavigationController *result = findNavigationControllerInWindow(windows[i]);
if (result != nil)
return result;
}
return nil;
}
- (CGFloat)myNominalTransitionAnimationDuration
{
return 0.2f;
}
- (int)replacedKeyboardDirection:(int)arg1 arg2:(BOOL)arg2
{
static SEL selector = NULL;
static int (*impl)(id, SEL, int, BOOL) = NULL;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
selector = NSSelectorFromString(TGEncodeText(@"`lfzcpbseEjsfdujpoGpsUsbotjujpo;psefsjohJo;", -1));
Method method = class_getInstanceMethod([UINavigationController class], selector);
impl = (int (*)(id, SEL, int, BOOL))method_getImplementation(method);
});
int result = 1;
if (impl != NULL)
result = impl(self, selector, arg1, arg2);
if ([TGViewController useExperimentalRTL])
{
if (result == 1)
result = 2;
else if (result == 2)
result = 1;
}
return result;
}
- (void)setPreferredContentSize:(CGSize)preferredContentSize
{
_preferredContentSize = preferredContentSize;
}
- (CGSize)preferredContentSize
{
return _preferredContentSize;
}
@end
@implementation TGNavigationPercentTransition
- (void)updateInteractiveTransition:(CGFloat)percentComplete
{
TGNavigationController *navigationController = findNavigationController();
if (navigationController != nil)
{
if (!navigationController.disableInteractiveKeyboardTransition && [TGHacks applicationKeyboardWindow] != nil && ![TGHacks applicationKeyboardWindow].hidden)
{
CGSize screenSize = [TGViewController screenSizeForInterfaceOrientation:navigationController.interfaceOrientation];
CGFloat keyboardOffset = MAX(0.0f, percentComplete * screenSize.width);
if ([TGViewController useExperimentalRTL])
keyboardOffset = -keyboardOffset;
UIView *keyboardView = [TGHacks applicationKeyboardView];
CGRect keyboardViewFrame = keyboardView.frame;
keyboardViewFrame.origin.x = keyboardOffset;
keyboardView.frame = keyboardViewFrame;
}
}
[super updateInteractiveTransition:percentComplete];
}
- (void)finishInteractiveTransition
{
CGFloat value = self.percentComplete;
UIView *keyboardView = [TGHacks applicationKeyboardView];
CGRect keyboardViewFrame = keyboardView.frame;
[super finishInteractiveTransition];
TGNavigationController *navigationController = findNavigationController();
if (navigationController != nil)
{
if (!navigationController.disableInteractiveKeyboardTransition)
{
keyboardView.frame = keyboardViewFrame;
CGSize screenSize = [TGViewController screenSizeForInterfaceOrientation:navigationController.interfaceOrientation];
CGFloat keyboardOffset = 1.0f * screenSize.width;
if ([TGViewController useExperimentalRTL])
keyboardOffset = -keyboardOffset;
keyboardViewFrame.origin.x = keyboardOffset;
NSTimeInterval duration = (1.0 - value) * [navigationController myNominalTransitionAnimationDuration];
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^
{
keyboardView.frame = keyboardViewFrame;
} completion:nil];
}
}
}
- (void)cancelInteractiveTransition
{
CGFloat value = self.percentComplete;
TGNavigationController *navigationController = findNavigationController();
if (navigationController != nil)
{
if (!navigationController.disableInteractiveKeyboardTransition && [TGHacks applicationKeyboardWindow] != nil && ![TGHacks applicationKeyboardWindow].hidden)
{
UIView *keyboardView = [TGHacks applicationKeyboardView];
CGRect keyboardViewFrame = keyboardView.frame;
keyboardViewFrame.origin.x = 0.0f;
NSTimeInterval duration = value * [navigationController myNominalTransitionAnimationDuration];
[UIView animateWithDuration:duration delay:0 options:UIViewAnimationOptionCurveLinear animations:^
{
keyboardView.frame = keyboardViewFrame;
} completion:nil];
}
}
[super cancelInteractiveTransition];
}
@end