1
0
mirror of https://github.com/danog/Telegram.git synced 2024-12-02 09:27:55 +01:00
Telegram/Telegraph/TGAppDelegate.mm
2016-02-25 01:03:51 +01:00

3401 lines
146 KiB
Plaintext

#import "TGAppDelegate.h"
#import "Freedom.h"
#import "FreedomUIKit.h"
#import "TGTelegraph.h"
#import "TGTelegramNetworking.h"
#import <MTProtoKit/MTDatacenterAddress.h>
#import "TGDatabase.h"
#import "TGMessage+Telegraph.h"
#import "TGDateUtils.h"
#import "TGStringUtils.h"
#import "TGInterfaceManager.h"
#import "TGInterfaceAssets.h"
#import "TGSchema.h"
#import "TGImageManager.h"
#import "TGCache.h"
#import "TGRemoteImageView.h"
#import "TGImageUtils.h"
#import "TGViewController.h"
#import "TGTelegraphDialogListCompanion.h"
#import "TGNavigationBar.h"
#import "SGraphListNode.h"
#import "TGImageDownloadActor.h"
#import "TGHacks.h"
#import "TGTelegraphConversationMessageAssetsSource.h"
#import "TGReusableLabel.h"
#import "TGFont.h"
#import "TGNotificationWindow.h"
#import "TGMessageNotificationView.h"
#import <QuartzCore/QuartzCore.h>
#import <AudioToolbox/AudioToolbox.h>
#import <ImageIO/ImageIO.h>
#import "RMIntroViewController.h"
#import "TGLoginWelcomeController.h"
#import "TGLoginPhoneController.h"
#import "TGLoginCodeController.h"
#import "TGLoginProfileController.h"
#import "TGLoginInactiveUserController.h"
#import "TGApplication.h"
#import "TGPasscodeWindow.h"
#import "TGContentViewController.h"
#import "TGModernConversationController.h"
#import "TGGenericModernConversationCompanion.h"
#import "TGOverlayControllerWindow.h"
#import "TGModernGalleryController.h"
#import "TGSecretModernConversationCompanion.h"
#import "TGForwardTargetController.h"
#import "TGTimerTarget.h"
#import "TGAlertView.h"
#import "TGModernGalleryModel.h"
#import "TGConversationAddMessagesActor.h"
#import <pthread.h>
#import <objc/runtime.h>
#import <AVFoundation/AVFoundation.h>
#include <inttypes.h>
#import "TGProgressWindow.h"
#import "TGPasscodeSettingsController.h"
#import "TGPasscodeEntryController.h"
#import "TGDropboxHelper.h"
#import "TGStickersSignals.h"
#import <LocalAuthentication/LocalAuthentication.h>
#import "TGStickerPackPreviewWindow.h"
#import "TGBotSignals.h"
#import "TGBridgeServer.h"
#import "TGBridgeRemoteHandler.h"
#import "TGPeerIdAdapter.h"
#define TG_SYNCHRONIZED_DEFINE(lock) pthread_mutex_t TG_SYNCHRONIZED_##lock
#define TG_SYNCHRONIZED_INIT(lock) pthread_mutex_init(&TG_SYNCHRONIZED_##lock, NULL)
#define TG_SYNCHRONIZED_BEGIN(lock) pthread_mutex_lock(&TG_SYNCHRONIZED_##lock);
#define TG_SYNCHRONIZED_END(lock) pthread_mutex_unlock(&TG_SYNCHRONIZED_##lock);
#import <HockeySDK/HockeySDK.h>
#import "TGGroupManagementSignals.h"
#import "TGSendMessageSignals.h"
#import "TGChatMessageListSignal.h"
#import "TGAudioSessionManager.h"
#import "TGApplicationMainWindow.h"
#import "TGRootController.h"
#import "../../config.h"
#import <CoreSpotlight/CoreSpotlight.h>
#import <ContactsUI/ContactsUI.h>
#import "TGChatListSignals.h"
#import "TGKeyCommandController.h"
NSString *TGDeviceProximityStateChangedNotification = @"TGDeviceProximityStateChangedNotification";
CFAbsoluteTime applicationStartupTimestamp = 0;
CFAbsoluteTime mainLaunchTimestamp = 0;
TGAppDelegate *TGAppDelegateInstance = nil;
TGTelegraph *telegraph = nil;
@interface CMGestureManager : NSObject {
id _internal;
}
@property (copy) void(^gestureHandler)(int a, int b);
+ (BOOL)isGestureServiceAvailable;
+ (BOOL)isGestureServiceEnabled;
+ (void)setGestureServiceEnabled:(BOOL)arg1;
- (void)dealloc;
- (id /* block */)gestureHandler;
- (id)init;
- (id)initWithPriority:(int)arg1;
- (void)setGestureHandler:(void(^)(int a, int b))arg1;
@end
@interface TGAppDelegate () <BITHockeyManagerDelegate, BITUpdateManagerDelegate, BITCrashManagerDelegate, AVAudioPlayerDelegate>
{
bool _inBackground;
bool _enteringForeground;
NSTimer *_foregroundResumeTimer;
TGProgressWindow *_progressWindow;
bool _didBecomeInactive;
TGPasscodeWindow *_passcodeWindow;
//UIWindow *_blurredContentWindow;
SMetaDisposable *_deviceLockedRequestDisposable;
bool _didUpdateDeviceLocked;
TGUser *_currentInviteBot;
NSString *_currentInviteBotPayload;
SMetaDisposable *_lastConversationsDisposable;
}
@property (nonatomic) bool tokenAlreadyRequested;
@property (nonatomic, strong) id<TGDeviceTokenListener> deviceTokenListener;
@property (nonatomic) UIBackgroundTaskIdentifier backgroundTaskIdentifier;
@property (nonatomic, strong) NSTimer *backgroundTaskExpirationTimer;
@property (nonatomic, strong) NSMutableDictionary *loadedSoundSamples;
@property (nonatomic, strong) UIWebView *callingWebView;
@property (nonatomic, strong) AVAudioPlayer *currentAudioPlayer;
@property (nonatomic, strong) SMetaDisposable *currentAudioPlayerSession;
@end
@implementation TGAppDelegate
- (instancetype)init
{
self = [super init];
if (self != nil)
{
[TGBridgeServer instance];
}
return self;
}
- (TGNavigationController *)loginNavigationController
{
if (_loginNavigationController == nil)
{
UIViewController *rootController = nil;
bool useAnimated = true;
#if TARGET_IPHONE_SIMULATOR
//useAnimated = false;
#endif
if (useAnimated)
{
rootController = [[RMIntroViewController alloc] init];
}
else
rootController = [[TGLoginWelcomeController alloc] init];
_loginNavigationController = [TGNavigationController navigationControllerWithControllers:@[rootController] navigationBarClass:[TGTransparentNavigationBar class]];
_loginNavigationController.restrictLandscape = !TGIsPad();
_loginNavigationController.disableInteractiveKeyboardTransition = true;
//_loginNavigationController.modalTransitionStyle = UIModalTransitionStyleFlipHorizontal;
}
return _loginNavigationController;
}
static void overridenDrawRect(__unused id self, __unused SEL _cmd, __unused CGRect rect)
{
}
static unsigned int overrideIndexAbove(__unused id self, __unused SEL _cmd)
{
return [(TGNavigationBar *)self indexAboveBackdropBackground];
}
- (bool)enableLogging
{
NSNumber *logsEnabled = [[NSUserDefaults standardUserDefaults] objectForKey:@"__logsEnabled"];
#if (defined(DEBUG) || defined(INTERNAL_RELEASE)) && !defined(DISABLE_LOGGING)
if (logsEnabled == nil)
return true;
#endif
return [logsEnabled boolValue];
}
- (void)setEnableLogging:(bool)enableLogging
{
[[NSUserDefaults standardUserDefaults] setObject:@(enableLogging) forKey:@"__logsEnabled"];
TGLogSetEnabled(enableLogging);
}
- (UIResponder *)nextResponder
{
if (_keyCommandController != nil)
return _keyCommandController;
else
return [super nextResponder];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
TGLogSetEnabled([self enableLogging]);
TGLog(@"didFinishLaunchingWithOptions state: %@, %d", [UIApplication sharedApplication], [UIApplication sharedApplication].applicationState);
[TGAppDelegate movePathsToContainer];
NSString *documentsDirectory = [TGAppDelegate documentsPath];
[[NSURL fileURLWithPath:documentsDirectory] setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
[TGMessage registerMediaAttachmentParser:TGActionMediaAttachmentType parser:[[TGActionMediaAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGImageMediaAttachmentType parser:[[TGImageMediaAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGLocationMediaAttachmentType parser:[[TGLocationMediaAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGLocalMessageMetaMediaAttachmentType parser:[[TGLocalMessageMetaMediaAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGVideoMediaAttachmentType parser:[[TGVideoMediaAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGContactMediaAttachmentType parser:[[TGContactMediaAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGForwardedMessageMediaAttachmentType parser:[[TGForwardedMessageMediaAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGUnsupportedMediaAttachmentType parser:[[TGUnsupportedMediaAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGDocumentMediaAttachmentType parser:[[TGDocumentMediaAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGAudioMediaAttachmentType parser:[[TGAudioMediaAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGReplyMessageMediaAttachmentType parser:[[TGReplyMessageMediaAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGWebPageMediaAttachmentType parser:[[TGWebPageMediaAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGReplyMarkupAttachmentType parser:[[TGReplyMarkupAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGMessageEntitiesAttachmentType parser:[[TGMessageEntitiesAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGBotContextResultAttachmentType parser:[[TGBotContextResultAttachment alloc] init]];
[TGMessage registerMediaAttachmentParser:TGViaUserAttachmentType parser:[[TGViaUserAttachment alloc] init]];
TGLog(@"###### Early initialization ######");
[TGDatabase setPasswordRequiredBlock:^TGDatabasePasswordCheckResultBlock (void (^verifyBlock)(NSString *), bool simple)
{
TGDispatchOnMainThread(^
{
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
if (_passcodeWindow == nil)
{
CGRect passcodeFrame = [UIScreen mainScreen].bounds;
_passcodeWindow = [[TGPasscodeWindow alloc] initWithFrame:passcodeFrame];
TGPasscodeEntryController *controller = [[TGPasscodeEntryController alloc] initWithStyle:TGPasscodeEntryControllerStyleTranslucent mode:simple ? TGPasscodeEntryControllerModeVerifySimple : TGPasscodeEntryControllerModeVerifyComplex cancelEnabled:false allowTouchId:false completion:^(NSString *passcode)
{
verifyBlock(passcode);
}];
_passcodeWindow.windowLevel = 10000100.0f;
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[controller]];
navigationController.restrictLandscape = [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone;
_passcodeWindow.rootViewController = navigationController;
_passcodeWindow.hidden = false;
[controller prepareForAppear];
}
else
{
_passcodeWindow.hidden = false;
if (TGIsPad())
_passcodeWindow.frame = [UIScreen mainScreen].bounds;
else
_passcodeWindow.frame = (CGRect){CGPointZero, TGScreenSize()};
TGPasscodeEntryController *controller = (TGPasscodeEntryController *)(((TGNavigationController *)_passcodeWindow.rootViewController).topViewController);
controller.completion = ^(NSString *passcode)
{
verifyBlock(passcode);
};
controller.checkCurrentPasscode = nil;
[controller resetMode:simple ? TGPasscodeEntryControllerModeVerifySimple : TGPasscodeEntryControllerModeVerifyComplex];
[controller prepareForAppear];
}
[[TGBridgeServer instance] startRunning];
});
return ^(bool match)
{
TGDispatchOnMainThread(^
{
if (match)
{
[_passcodeWindow endEditing:true];
TGPasscodeEntryController *controller = (TGPasscodeEntryController *)(((TGNavigationController *)_passcodeWindow.rootViewController).topViewController);
[controller resetInvalidPasscodeAttempts];
[controller prepareForDisappear];
[UIView animateWithDuration:0.3 delay:0 options:[TGViewController preferredAnimationCurve] << 16 animations:^
{
_passcodeWindow.frame = CGRectOffset(_passcodeWindow.frame, 0.0f, _passcodeWindow.frame.size.height);
} completion:^(__unused BOOL finished)
{
_passcodeWindow.hidden = true;
}];
}
else
{
TGPasscodeEntryController *controller = (TGPasscodeEntryController *)(((TGNavigationController *)_passcodeWindow.rootViewController).topViewController);
[controller addInvalidPasscodeAttempt];
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
});
};
}];
__block TGProgressWindow *progressWindow = nil;
[TGDatabase setUpgradingBlock:^TGDatabaseUpgradeCompletedBlock ()
{
TGDispatchOnMainThread(^
{
progressWindow = [[TGProgressWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[progressWindow show:true];
});
return ^
{
TGDispatchOnMainThread(^
{
[progressWindow dismiss:true];
});
};
}];
[TGDatabase setLiveMessagesDispatchPath:@"/tg/conversations"];
[TGDatabase setLiveBroadcastMessagesDispatchPath:@"/tg/broadcastConversations"];
[TGDatabase setLiveUnreadCountDispatchPath:@"/tg/unreadCount"];
[[TGDatabase instance] markAllPendingMessagesAsFailed];
_deviceProximityListeners = [[TGHolderSet alloc] init];
_deviceProximityListeners.emptyStateChanged = ^(bool listenersExist)
{
if (listenersExist) {
[UIDevice currentDevice].proximityMonitoringEnabled = true;
bool deviceProximityState = [UIDevice currentDevice].proximityState;
if (deviceProximityState) {
_deviceProximityState = deviceProximityState;
[[NSNotificationCenter defaultCenter] postNotificationName:TGDeviceProximityStateChangedNotification object:nil];
}
} else if (!TGAppDelegateInstance->_deviceProximityState) {
[UIDevice currentDevice].proximityMonitoringEnabled = false;
_deviceProximityState = false;
}
};
[[NSNotificationCenter defaultCenter] addObserverForName:UIDeviceProximityStateDidChangeNotification object:[UIDevice currentDevice] queue:nil usingBlock:^(__unused NSNotification *notification)
{
_deviceProximityState = [UIDevice currentDevice].proximityState;
if (!_deviceProximityState && _deviceProximityListeners.isEmpty) {
[UIDevice currentDevice].proximityMonitoringEnabled = false;
}
[[NSNotificationCenter defaultCenter] postNotificationName:TGDeviceProximityStateChangedNotification object:nil];
}];
[FFNotificationCenter setShouldRotateBlock:^ bool()
{
bool restrictPasscodeWindow = false;
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone && _passcodeWindow != nil && !_passcodeWindow.hidden && [UIApplication sharedApplication].statusBarOrientation == UIInterfaceOrientationPortrait)
{
restrictPasscodeWindow = true;
}
return [_window.rootViewController shouldAutorotate] && !restrictPasscodeWindow;
}];
freedomInit();
freedomUIKitInit();
TGAppDelegateInstance = self;
if (iosMajorVersion() < 7)
{
UIGraphicsBeginImageContextWithOptions(CGSizeMake(1, 30), false, 0.0f);
UIImage *transparentImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
UIBarButtonItem *item = [UIBarButtonItem appearanceWhenContainedIn:[TGNavigationBar class], nil];
[item setBackgroundImage:transparentImage forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
UIImage *backImage = [UIImage imageNamed:@"NavigationBackButton.png"];
UIImage *backHighlightedImage = [UIImage imageNamed:@"NavigationBackButton_Highlighted.png"];
UIImage *backLandscapeImage = [UIImage imageNamed:@"NavigationBackButtonLandscape.png"];
UIImage *backLandscapeHighlightedImage = [UIImage imageNamed:@"NavigationBackButtonLandscape_Highlighted.png"];
[item setBackButtonBackgroundImage:[backImage stretchableImageWithLeftCapWidth:(int)(backImage.size.width) topCapHeight:0] forState:UIControlStateNormal barMetrics:UIBarMetricsDefault];
[item setBackButtonBackgroundImage:[backHighlightedImage stretchableImageWithLeftCapWidth:(int)(backHighlightedImage.size.width) topCapHeight:0] forState:UIControlStateHighlighted barMetrics:UIBarMetricsDefault];
[item setBackButtonBackgroundImage:[backLandscapeImage stretchableImageWithLeftCapWidth:(int)(backLandscapeImage.size.width) topCapHeight:0] forState:UIControlStateNormal barMetrics:UIBarMetricsLandscapePhone];
[item setBackButtonBackgroundImage:[backLandscapeHighlightedImage stretchableImageWithLeftCapWidth:(int)(backLandscapeHighlightedImage.size.width) topCapHeight:0] forState:UIControlStateHighlighted barMetrics:UIBarMetricsLandscapePhone];
[item setBackButtonTitlePositionAdjustment:UIOffsetMake(5, -1) forBarMetrics:UIBarMetricsDefault];
[item setBackButtonTitlePositionAdjustment:UIOffsetMake(5, -3) forBarMetrics:UIBarMetricsLandscapePhone];
[item setTitlePositionAdjustment:UIOffsetMake(0, 1) forBarMetrics:UIBarMetricsDefault];
[item setTitleTextAttributes:@{UITextAttributeTextColor: TGAccentColor(), UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGSystemFontOfSize(16.0f)} forState:UIControlStateNormal];
[item setTitleTextAttributes:@{UITextAttributeTextColor: [TGAccentColor() colorWithAlphaComponent:0.4f], UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGSystemFontOfSize(16.0f)} forState:UIControlStateHighlighted];
[[TGNavigationBar appearance] setTitleTextAttributes:@{UITextAttributeTextColor: [UIColor blackColor], UITextAttributeTextShadowColor: [UIColor clearColor], UITextAttributeFont: TGBoldSystemFontOfSize(17.0f)}];
[[TGNavigationBar appearance] setTitleVerticalPositionAdjustment:(TGIsRetina() ? 0.5f : 0.0f) forBarMetrics:UIBarMetricsDefault];
[[TGNavigationBar appearance] setTitleVerticalPositionAdjustment:-1.0f forBarMetrics:UIBarMetricsLandscapePhone];
}
else
{
//UIBarButtonItem *item = [UIBarButtonItem appearanceWhenContainedIn:[TGNavigationBar class], nil];
//[item setBackButtonTitlePositionAdjustment:UIOffsetMake([TGViewController useExperimentalRTL] ? -18.0f : 0.0f, -1) forBarMetrics:UIBarMetricsLandscapePhone];
//[[UIBarButtonItem appearance] setBackButtonTitlePositionAdjustment:UIOffsetMake([TGViewController useExperimentalRTL] ? -18.0f : 0.0f, 0.0f) forBarMetrics:UIBarMetricsDefault];
}
if (!TGIsPad())
[TGViewController disableAutorotation];
_window = [[TGApplicationMainWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[(TGApplication *)application forceSetStatusBarStyle:UIStatusBarStyleBlackTranslucent animated:false];
if (iosMajorVersion() < 7)
{
FreedomDecoration instanceDecorations[] = {
{ .name = 0x7927c35dU,
.imp = (IMP)&overridenDrawRect,
.newIdentifier = FreedomIdentifierEmpty,
.newEncoding = FreedomIdentifierEmpty
},
{ .name = 0xc6dda86U,
.imp = (IMP)&overrideIndexAbove,
.newIdentifier = FreedomIdentifierEmpty,
.newEncoding = FreedomIdentifierEmpty
}
};
freedomClassAutoDecorate(0xf457bfb2U, NULL, 0, instanceDecorations, sizeof(instanceDecorations) / sizeof(instanceDecorations[0]));
}
_loadedSoundSamples = [[NSMutableDictionary alloc] init];
_actionHandle = [[ASHandle alloc] initWithDelegate:self releaseOnMainThread:false];
[ASActor registerActorClass:[TGImageDownloadActor class]];
[TGInterfaceManager instance];
telegraph = [[TGTelegraph alloc] init];
[TGHacks hackSetAnimationDuration];
_rootController = [[TGRootController alloc] init];
self.window.rootViewController = _rootController;
self.window.backgroundColor = [UIColor blackColor];
[self.window makeKeyAndVisible];
if ([TGKeyCommandController keyCommandsSupported])
_keyCommandController = [[TGKeyCommandController alloc] initWithRootController:_rootController];
TGCache *sharedCache = [[TGCache alloc] init];
//sharedCache.imageMemoryLimit = 0;
//sharedCache.imageMemoryEvictionInterval = 0;
[TGRemoteImageView setSharedCache:sharedCache];
if (![TGDatabaseInstance() isEncryptionEnabled])
{
TGDispatchOnMainThread(^
{
[self displayUnlockWindowIfNeeded];
TGPasscodeEntryController *controller = (TGPasscodeEntryController *)(((TGNavigationController *)_passcodeWindow.rootViewController).topViewController);
[controller refreshTouchId];
});
}
[[TGBridgeServer instance] setPasscodeEnabled:[TGDatabaseInstance() isPasswordSet:NULL] passcodeEncrypted:[TGDatabaseInstance() isEncryptionEnabled]];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
{
[self loadSettings];
[TGDatabaseInstance() dispatchOnDatabaseThread:^
{
[TGDatabaseInstance() loadConversationListFromDate:INT32_MAX limit:32 excludeConversationIds:nil completion:^(NSArray *dialogList, bool loadedAllRegular)
{
bool dialogListLoaded = [TGDatabaseInstance() customProperty:@"dialogListLoaded"].length != 0;
NSMutableArray *filteredResult = [[NSMutableArray alloc] initWithArray:dialogList];
[filteredResult sortUsingComparator:^NSComparisonResult(TGConversation *lhs, TGConversation *rhs) {
if (lhs.date > rhs.date) {
return NSOrderedAscending;
} else if (lhs.date < rhs.date) {
return NSOrderedDescending;
} else {
if (lhs.conversationId < rhs.conversationId) {
return NSOrderedDescending;
} else {
return NSOrderedAscending;
}
}
}];
/*for (TGConversation *conversation in filteredResult) {
if (!conversation.isChannel && !conversation.isBroadcast && (!conversation.isChat || conversation.isEncrypted)) {
int32_t userId = 0;
if (conversation.isEncrypted)
{
if (conversation.chatParticipants.chatParticipantUids.count != 0)
userId = [conversation.chatParticipants.chatParticipantUids[0] intValue];
}
else
userId = (int)conversation.conversationId;
TGUser *user = [TGDatabaseInstance() loadUser:userId];
TGLog(@"%d: %@", conversation.date, user.displayName);
} else {
if (conversation.isChannel) {
TGLog(@"*%d: %@", conversation.date, conversation.chatTitle);
} else if (conversation.isBroadcast) {
TGLog(@"#%d: %@", conversation.date, conversation.chatTitle);
} else {
TGLog(@"%d: %@", conversation.date, conversation.chatTitle);
}
}
}*/
if (!dialogListLoaded || !loadedAllRegular) {
while (filteredResult.count != 0 && (((TGConversation *)[filteredResult lastObject]).isChannel || ((TGConversation *)[filteredResult lastObject]).isBroadcast)) {
[filteredResult removeLastObject];
}
/*TGLog(@"filtered=============");
for (TGConversation *conversation in filteredResult) {
if (!conversation.isChannel && !conversation.isBroadcast && (!conversation.isChat || conversation.isEncrypted)) {
int32_t userId = 0;
if (conversation.isEncrypted)
{
if (conversation.chatParticipants.chatParticipantUids.count != 0)
userId = [conversation.chatParticipants.chatParticipantUids[0] intValue];
}
else
userId = (int)conversation.conversationId;
TGUser *user = [TGDatabaseInstance() loadUser:userId];
TGLog(@"%d: %@", conversation.date, user.displayName);
} else {
if (conversation.isChannel) {
TGLog(@"*%d: %@", conversation.date, conversation.chatTitle);
} else if (conversation.isBroadcast) {
TGLog(@"#%d: %@", conversation.date, conversation.chatTitle);
} else {
TGLog(@"%d: %@", conversation.date, conversation.chatTitle);
}
}
}*/
}
TGLog(@"###### Dialog list loaded ######");
SGraphListNode *node = [[SGraphListNode alloc] init];
node.items = filteredResult;
[(id<ASWatcher>)_rootController.dialogListController.dialogListCompanion actorCompleted:ASStatusSuccess path:@"/tg/dialoglist/(0)" result:node];
TGLog(@"===== Dispatched dialog list");
[ActionStageInstance() dispatchOnStageQueue:^
{
[[TGTelegramNetworking instance] loadCredentials];
if (TGTelegraphInstance.clientUserId != 0)
{
[TGTelegraphInstance processAuthorizedWithUserId:TGTelegraphInstance.clientUserId clientIsActivated:TGTelegraphInstance.clientIsActivated];
if (!TGTelegraphInstance.clientIsActivated)
{
TGLog(@"===== User is not activated, presenting welcome screen");
[self presentLoginController:false showWelcomeScreen:true phoneNumber:nil phoneCode:nil phoneCodeHash:nil codeSentToTelegram:false profileFirstName:nil profileLastName:nil];
}
else if (launchOptions[UIApplicationLaunchOptionsURLKey] != nil)
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self handleOpenDocument:launchOptions[UIApplicationLaunchOptionsURLKey] animated:false];
});
}
else if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] != nil)
{
dispatch_async(dispatch_get_main_queue(), ^
{
id nFromId = [launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] objectForKey:@"from_id"];
id nChatId = [launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] objectForKey:@"chat_id"];
id nContactId = [launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] objectForKey:@"contact_id"];
int64_t peerId = 0;
if (nFromId != nil && [TGSchema canCreateIntFromObject:nFromId])
{
peerId = [TGSchema intFromObject:nFromId];
}
else if (nChatId != nil && [TGSchema canCreateIntFromObject:nChatId])
{
peerId = -[TGSchema intFromObject:nChatId];
}
else if (nContactId != nil && [TGSchema canCreateIntFromObject:nContactId])
{
peerId = [TGSchema intFromObject:nContactId];
}
if ([UIApplication sharedApplication].applicationState == UIApplicationStateInactive)
[self _replyActionForPeerId:peerId mid:0 openKeyboard:false responseInfo:nil completion:nil];
});
}
else if (launchOptions[UIApplicationLaunchOptionsLocalNotificationKey] != nil)
{
if ([UIApplication sharedApplication].applicationState == UIApplicationStateInactive)
{
dispatch_async(dispatch_get_main_queue(), ^
{
if ([launchOptions respondsToSelector:@selector(objectForKeyedSubscript:)] && [launchOptions[UIApplicationLaunchOptionsLocalNotificationKey] respondsToSelector:@selector(objectForKey:)] && [launchOptions[UIApplicationLaunchOptionsLocalNotificationKey][@"cid"] respondsToSelector:@selector(longLongValue)])
{
int64_t peerId = [[launchOptions[UIApplicationLaunchOptionsLocalNotificationKey] objectForKey:@"cid"] longLongValue];
[self _replyActionForPeerId:peerId mid:0 openKeyboard:false responseInfo:nil completion:nil];
}
});
}
}
TGDispatchOnMainThread(^
{
if (!TGIsPad())
{
[TGViewController enableAutorotation];
[TGViewController attemptAutorotation];
}
});
}
else
{
[TGTelegraphInstance processUnauthorized];
NSDictionary *blockStateDict = [self loadLoginState];
dispatch_async(dispatch_get_main_queue(), ^
{
NSDictionary *stateDict = blockStateDict;
int currentDate = ((int)CFAbsoluteTimeGetCurrent());
int stateDate = [stateDict[@"date"] intValue];
if (currentDate - stateDate > 60 * 60 * 23)
{
stateDict = nil;
[self resetLoginState];
}
[self presentLoginController:false showWelcomeScreen:false phoneNumber:stateDict[@"phoneNumber"] phoneCode:stateDict[@"phoneCode"] phoneCodeHash:stateDict[@"phoneCodeHash"] codeSentToTelegram:[stateDict[@"codeSentToTelegram"] boolValue] profileFirstName:stateDict[@"firstName"] profileLastName:stateDict[@"lastName"]];
if (!TGIsPad())
{
[TGViewController enableAutorotation];
[TGViewController attemptAutorotation];
}
});
[[TGDatabase instance] dropDatabase];
}
[[TGTelegramNetworking instance] start];
if (launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] != nil)
[self processPossibleConfigUpdateNotification:launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey]];
[[TGBridgeServer instance] startRunning];
}];
}];
} synchronous:false];
});
#ifndef EXTERNAL_INTERNAL_RELEASE
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^
{
NSString *appId = nil;
#ifdef SETUP_HOCKEYAPP_APP_ID
SETUP_HOCKEYAPP_APP_ID(appId)
#endif
if (appId != nil) {
TGLog(@"starting with %@", appId);
[[BITHockeyManager sharedHockeyManager] configureWithIdentifier:appId delegate:self];
[[BITHockeyManager sharedHockeyManager] startManager];
[[BITHockeyManager sharedHockeyManager].authenticator authenticateInstallation];
}
});
#endif
_foregroundResumeTimer = [TGTimerTarget scheduledMainThreadTimerWithTarget:self action:@selector(checkForegroundResume) interval:2.0 repeat:true];
TGDispatchAfter(1.0, dispatch_get_main_queue(), ^
{
@try
{
[UIView setAnimationsEnabled:true];
[CATransaction commit];
}
@catch (__unused NSException *exception)
{
}
});
[[TGBridgeServer instance] startServices];
return true;
}
- (void)checkForegroundResume
{
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
{
[[TGTelegramNetworking instance] resume];
}
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)__unused application
{
TGLog(@"******* Memory warning ******");
}
- (void)applicationWillResignActive:(UIApplication *)__unused application
{
[ActionStageInstance() dispatchOnStageQueue:^
{
int unreadCount = [TGDatabaseInstance() databaseState].unreadCount;
dispatch_async(dispatch_get_main_queue(), ^
{
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:unreadCount];
});
}];
[self onBecomeInactive];
}
- (void)displayUnlockWindowIfNeeded
{
NSNumber *nDeactivationDate = [[NSUserDefaults standardUserDefaults] objectForKey:@"Passcode_deactivationDate"];
bool displayByDeactivationTimeout = false;
if (nDeactivationDate != nil)
{
int32_t lockTimeout = [self automaticLockTimeout];
if (lockTimeout >= 0)
{
displayByDeactivationTimeout = [[NSDate date] timeIntervalSince1970] > ([nDeactivationDate doubleValue] + lockTimeout);
}
}
if ([self isManuallyLocked] || displayByDeactivationTimeout)
{
bool isStrong = false;
if ([TGDatabaseInstance() isPasswordSet:&isStrong])
{
if ([UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone)
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait];
TGPasscodeEntryControllerMode mode = (!isStrong) ? TGPasscodeEntryControllerModeVerifySimple : TGPasscodeEntryControllerModeVerifyComplex;
if (_passcodeWindow == nil)
{
CGRect passcodeFrame = [UIScreen mainScreen].bounds;
if (TGIsPad())
passcodeFrame = [UIScreen mainScreen].bounds;
else
passcodeFrame = (CGRect){CGPointZero, TGScreenSize()};
_passcodeWindow = [[TGPasscodeWindow alloc] initWithFrame:passcodeFrame];
TGPasscodeEntryController *controller = [[TGPasscodeEntryController alloc] initWithStyle:TGPasscodeEntryControllerStyleTranslucent mode:mode cancelEnabled:false allowTouchId:[TGPasscodeSettingsController enableTouchId] completion:^(NSString *passcode)
{
if ([TGDatabaseInstance() verifyPassword:passcode])
{
TGDispatchOnMainThread(^
{
[self setIsManuallyLocked:false];
[_passcodeWindow endEditing:true];
TGPasscodeEntryController *controller = (TGPasscodeEntryController *)(((TGNavigationController *)_passcodeWindow.rootViewController).topViewController);
[controller prepareForDisappear];
[UIView animateWithDuration:0.3 delay:0 options:[TGViewController preferredAnimationCurve] << 16 animations:^
{
_passcodeWindow.frame = CGRectOffset(_passcodeWindow.frame, 0.0f, _passcodeWindow.frame.size.height);
} completion:^(__unused BOOL finished)
{
_passcodeWindow.hidden = true;
}];
});
}
}];
controller.touchIdCompletion = ^
{
TGDispatchOnMainThread(^
{
[self setIsManuallyLocked:false];
[_passcodeWindow endEditing:true];
TGPasscodeEntryController *controller = (TGPasscodeEntryController *)(((TGNavigationController *)_passcodeWindow.rootViewController).topViewController);
[controller prepareForDisappear];
[UIView animateWithDuration:0.3 delay:0 options:[TGViewController preferredAnimationCurve] << 16 animations:^
{
_passcodeWindow.frame = CGRectOffset(_passcodeWindow.frame, 0.0f, _passcodeWindow.frame.size.height);
} completion:^(__unused BOOL finished)
{
_passcodeWindow.hidden = true;
}];
});
};
controller.checkCurrentPasscode = ^bool (NSString *passcode)
{
return [TGDatabaseInstance() verifyPassword:passcode];
};
_passcodeWindow.windowLevel = UIWindowLevelStatusBar - 0.0001f;
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[controller]];
navigationController.restrictLandscape = [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPhone;
_passcodeWindow.rootViewController = navigationController;
_passcodeWindow.hidden = false;
[controller prepareForAppear];
if (!TGIsPad())
{
navigationController.view.frame = (CGRect){CGPointZero, TGScreenSize()};;
controller.view.frame = (CGRect){CGPointZero, TGScreenSize()};;
}
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
[controller refreshTouchId];
}
else if (_passcodeWindow.hidden)
{
TGPasscodeEntryController *controller = (TGPasscodeEntryController *)(((TGNavigationController *)_passcodeWindow.rootViewController).topViewController);
controller.checkCurrentPasscode = ^(NSString *passcode)
{
return [TGDatabaseInstance() verifyPassword:passcode];
};
controller.completion = ^(NSString *passcode)
{
if ([TGDatabaseInstance() verifyPassword:passcode])
{
TGDispatchOnMainThread(^
{
[self setIsManuallyLocked:false];
[_passcodeWindow endEditing:true];
TGPasscodeEntryController *controller = (TGPasscodeEntryController *)(((TGNavigationController *)_passcodeWindow.rootViewController).topViewController);
[controller prepareForDisappear];
[UIView animateWithDuration:0.3 delay:0 options:[TGViewController preferredAnimationCurve] << 16 animations:^
{
_passcodeWindow.frame = CGRectOffset(_passcodeWindow.frame, 0.0f, _passcodeWindow.frame.size.height);
} completion:^(__unused BOOL finished)
{
_passcodeWindow.hidden = true;
}];
});
}
};
controller.touchIdCompletion = ^
{
TGDispatchOnMainThread(^
{
[self setIsManuallyLocked:false];
[_passcodeWindow endEditing:true];
TGPasscodeEntryController *controller = (TGPasscodeEntryController *)(((TGNavigationController *)_passcodeWindow.rootViewController).topViewController);
[controller prepareForDisappear];
[UIView animateWithDuration:0.3 delay:0 options:[TGViewController preferredAnimationCurve] << 16 animations:^
{
_passcodeWindow.frame = CGRectOffset(_passcodeWindow.frame, 0.0f, _passcodeWindow.frame.size.height);
} completion:^(__unused BOOL finished)
{
_passcodeWindow.hidden = true;
}];
});
};
[controller resetMode:mode];
if (TGIsPad())
_passcodeWindow.frame = [UIScreen mainScreen].bounds;
else
_passcodeWindow.frame = (CGRect){CGPointZero, TGScreenSize()};
_passcodeWindow.hidden = false;
[controller prepareForAppear];
controller.allowTouchId = [TGPasscodeSettingsController enableTouchId];
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive)
[controller refreshTouchId];
}
}
}
}
- (void)displayBlurredContentIfNeeded {
/*if (_blurredContentWindow == nil) {
_blurredContentWindow = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
_blurredContentWindow.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_blurredContentWindow.windowLevel = 1000000000000.0f;
_blurredContentWindow.backgroundColor = [UIColor whiteColor];
_blurredContentWindow.hidden = false;
}*/
}
- (void)hideBlurredContentIfNeeded {
/*if (_blurredContentWindow != nil) {
_blurredContentWindow.hidden = true;
_blurredContentWindow = nil;
}*/
}
- (void)applicationSignificantTimeChange:(UIApplication *)__unused application
{
TGLog(@"***** Significant time change");
[ActionStageInstance() dispatchOnStageQueue:^
{
[ActionStageInstance() dispatchResource:@"/system/significantTimeChange" resource:nil];
}];
[TGDatabaseInstance() processAndScheduleSelfDestruct];
[TGDatabaseInstance() processAndScheduleMediaCleanup];
[TGDatabaseInstance() processAndScheduleMute];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
#if defined(DEBUG) || defined(INTERNAL_RELEASE)
TGLogSynchronize();
#endif
_inBackground = true;
if (_backgroundTaskExpirationTimer != nil && [_backgroundTaskExpirationTimer isValid])
{
[_backgroundTaskExpirationTimer invalidate];
_backgroundTaskExpirationTimer = nil;
}
_backgroundTaskIdentifier = [application beginBackgroundTaskWithExpirationHandler:^
{
if (_backgroundTaskExpirationTimer != nil)
{
if ([_backgroundTaskExpirationTimer isValid])
[_backgroundTaskExpirationTimer invalidate];
_backgroundTaskExpirationTimer = nil;
}
UIBackgroundTaskIdentifier identifier = _backgroundTaskIdentifier;
_backgroundTaskIdentifier = UIBackgroundTaskInvalid;
[application endBackgroundTask:identifier];
}];
_enteredBackgroundTime = CFAbsoluteTimeGetCurrent();
NSTimeInterval maxBackgroundTime = 5.0;
if ([application backgroundTimeRemaining] >= 0.5 * 60.0 + 5)
maxBackgroundTime = [application backgroundTimeRemaining] - 0.5 * 60.0;
if (maxBackgroundTime < 60.0)
maxBackgroundTime = 60.0;
if (_disableBackgroundMode)
maxBackgroundTime = 1;
#ifdef DEBUG
maxBackgroundTime = 5;
#endif
TGLog(@"Background time remaining: %d m %d s", (int)(maxBackgroundTime / 60.0), ((int)maxBackgroundTime) % 60);
_backgroundTaskExpirationTimer = [NSTimer timerWithTimeInterval:MAX(maxBackgroundTime, 1.0) target:self selector:@selector(backgroundExpirationTimerEvent:) userInfo:nil repeats:false];
[[NSRunLoop mainRunLoop] addTimer:_backgroundTaskExpirationTimer forMode:NSRunLoopCommonModes];
[ActionStageInstance() dispatchOnStageQueue:^
{
[ActionStageInstance() removeWatcher:TGTelegraphInstance fromPath:@"/tg/service/updatepresence/(online)"];
[ActionStageInstance() removeWatcher:TGTelegraphInstance fromPath:@"/tg/service/updatepresence/(offline)"];
[ActionStageInstance() requestActor:@"/tg/service/updatepresence/(timeout)" options:nil watcher:TGTelegraphInstance];
}];
_didBecomeInactive = true;
if ([self isManuallyLocked]) {
[self displayUnlockWindowIfNeeded];
} else if ([self willBeLocked]) {
[self displayBlurredContentIfNeeded];
}
[self onBecomeInactive];
}
- (void)backgroundExpirationTimerEvent:(NSTimer *)__unused timer
{
[[TGTelegramNetworking instance] pause];
_backgroundTaskExpirationTimer = nil;
UIBackgroundTaskIdentifier identifier = _backgroundTaskIdentifier;
_backgroundTaskIdentifier = UIBackgroundTaskInvalid;
if (identifier == UIBackgroundTaskInvalid)
TGLog(@"***** Strange. *****");
double delayInSeconds = 5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^
{
[[UIApplication sharedApplication] endBackgroundTask:identifier];
});
}
- (bool)backgroundTaskOngoing
{
return (_backgroundTaskExpirationTimer != nil);
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
_enteringForeground = true;
dispatch_async(dispatch_get_main_queue(), ^
{
_inBackground = false;
_enteringForeground = false;
});
if (_backgroundTaskIdentifier != UIBackgroundTaskInvalid)
{
UIBackgroundTaskIdentifier identifier = _backgroundTaskIdentifier;
_backgroundTaskIdentifier = UIBackgroundTaskInvalid;
[application endBackgroundTask:identifier];
}
if (_backgroundTaskExpirationTimer != nil)
{
if ([_backgroundTaskExpirationTimer isValid])
[_backgroundTaskExpirationTimer invalidate];
_backgroundTaskExpirationTimer = nil;
}
if (_callingWebView != nil)
{
[_callingWebView stopLoading];
_callingWebView = nil;
}
[[TGTelegramNetworking instance] resume];
[ActionStageInstance() dispatchOnStageQueue:^
{
if ([ActionStageInstance() executingActorWithPath:@"/tg/service/updatepresence/(timeout)"] != nil)
[ActionStageInstance() removeWatcher:TGTelegraphInstance fromPath:@"/tg/service/updatepresence/(timeout)"];
else
[TGTelegraphInstance updatePresenceNow];
}];
}
- (void)applicationDidBecomeActive:(UIApplication *)__unused application
{
//[ActionStageInstance() requestActor:@"/tg/locationServicesState/(dispatch)" options:[[NSDictionary alloc] initWithObjectsAndKeys:[[NSNumber alloc] initWithBool:true], @"dispatch", nil] watcher:TGTelegraphInstance];
if (_didBecomeInactive)
{
_didBecomeInactive = false;
[self onBecomeActive];
if (_passcodeWindow != nil && [self isManuallyLocked])
{
[_window endEditing:true];
TGPasscodeEntryController *controller = (TGPasscodeEntryController *)(((TGNavigationController *)_passcodeWindow.rootViewController).topViewController);
[controller refreshTouchId];
}
}
[self hideBlurredContentIfNeeded];
}
- (void)applicationWillTerminate:(UIApplication *)__unused application
{
TGLogSynchronize();
}
- (void)resetLocalization
{
[TGDateUtils reset];
[_rootController localizationUpdated];
[[TGInterfaceManager instance] localizationUpdated];
[[TGBridgeServer instance] putNext:@(TGIsCustomLocalizationActive()) forKey:@"localization"];
}
- (void)performPhoneCall:(NSURL *)url
{
NSURL *realUrl = url;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
if ([url.scheme isEqualToString:@"tel"])
{
realUrl = [NSURL URLWithString:[[url absoluteString] stringByReplacingOccurrencesOfString:@"tel:" withString:@"facetime:"]];
}
}
_callingWebView = [[UIWebView alloc] init];
[_callingWebView loadRequest:[NSURLRequest requestWithURL:realUrl]];
}
- (void)presentLoginController:(bool)clearControllerStates showWelcomeScreen:(bool)showWelcomeScreen phoneNumber:(NSString *)phoneNumber phoneCode:(NSString *)phoneCode phoneCodeHash:(NSString *)phoneCodeHash codeSentToTelegram:(bool)codeSentToTelegram profileFirstName:(NSString *)profileFirstName profileLastName:(NSString *)profileLastName
{
if (![[NSThread currentThread] isMainThread])
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self presentLoginController:clearControllerStates showWelcomeScreen:showWelcomeScreen phoneNumber:phoneNumber phoneCode:phoneCode phoneCodeHash:phoneCodeHash codeSentToTelegram:codeSentToTelegram profileFirstName:profileFirstName profileLastName:profileLastName];
});
return;
}
else
{
TGNavigationController *loginNavigationController = [self loginNavigationController];
NSMutableArray *viewControllers = [[loginNavigationController viewControllers] mutableCopy];
if (showWelcomeScreen)
{
TGLoginInactiveUserController *inactiveUserController = [[TGLoginInactiveUserController alloc] init];
[viewControllers addObject:inactiveUserController];
}
else
{
if (phoneNumber.length != 0)
{
TGLoginPhoneController *phoneController = [[TGLoginPhoneController alloc] init];
[(TGLoginPhoneController *)phoneController setPhoneNumber:phoneNumber];
[viewControllers addObject:phoneController];
NSMutableString *cleanPhone = [[NSMutableString alloc] init];
for (int i = 0; i < (int)phoneNumber.length; i++)
{
unichar c = [phoneNumber characterAtIndex:i];
if (c >= '0' && c <= '9')
[cleanPhone appendString:[[NSString alloc] initWithCharacters:&c length:1]];
}
if (phoneCode.length != 0 && phoneCodeHash.length != 0)
{
TGLoginProfileController *profileController = [[TGLoginProfileController alloc] initWithShowKeyboard:true phoneNumber:cleanPhone phoneCodeHash:phoneCodeHash phoneCode:phoneCode];
[viewControllers addObject:profileController];
}
else if (phoneCodeHash.length != 0)
{
TGLoginCodeController *codeController = [[TGLoginCodeController alloc] initWithShowKeyboard:true phoneNumber:cleanPhone phoneCodeHash:phoneCodeHash phoneTimeout:60.0 messageSentToTelegram:codeSentToTelegram];
[viewControllers addObject:codeController];
}
}
}
[loginNavigationController setViewControllers:viewControllers animated:false];
if (TGAppDelegateInstance.rootController.presentedViewController != nil)
{
if (TGAppDelegateInstance.rootController.presentedViewController == loginNavigationController)
return;
[TGAppDelegateInstance.rootController dismissViewControllerAnimated:true completion:nil];
double delayInSeconds = 0.5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^
{
[TGAppDelegateInstance.rootController presentViewController:loginNavigationController animated:[[UIApplication sharedApplication] applicationState] == UIApplicationStateActive completion:nil];
});
}
else
[TGAppDelegateInstance.rootController presentViewController:loginNavigationController animated:[[UIApplication sharedApplication] applicationState] == UIApplicationStateActive completion:nil];
if (clearControllerStates)
{
double delayInSeconds = 0.5;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^
{
[_rootController.mainTabsController setSelectedIndex:1];
[_rootController.dialogListController.dialogListCompanion clearData];
[_rootController.contactsController clearData];
[TGAppDelegateInstance.rootController clearContentControllers];
[TGAppDelegateInstance resetControllerStack];
});
}
}
}
- (void)presentMainController
{
if (![[NSThread currentThread] isMainThread])
{
dispatch_async(dispatch_get_main_queue(), ^
{
[self presentMainController];
});
return;
}
self.loginNavigationController = nil;
UIViewController *presentedViewController = nil;
presentedViewController = TGAppDelegateInstance.rootController.presentedViewController;
if ([presentedViewController respondsToSelector:@selector(isBeingDismissed)])
{
if ([presentedViewController isBeingDismissed] || [presentedViewController isBeingPresented])
{
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];
TGDispatchAfter(0.1, dispatch_get_main_queue(), ^
{
[[UIApplication sharedApplication] endIgnoringInteractionEvents];
[self presentMainController];
});
}
else
{
[TGAppDelegateInstance.rootController dismissViewControllerAnimated:[[UIApplication sharedApplication] applicationState] == UIApplicationStateActive completion:nil];
}
}
else
{
[TGAppDelegateInstance.rootController dismissViewControllerAnimated:[[UIApplication sharedApplication] applicationState] == UIApplicationStateActive completion:nil];
}
}
- (void)presentContentController:(UIViewController *)controller
{
_contentWindow = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
_contentWindow.windowLevel = UIWindowLevelStatusBar - 0.1f;
_contentWindow.rootViewController = controller;
dispatch_async(dispatch_get_main_queue(), ^
{
[_contentWindow makeKeyAndVisible];
});
}
- (void)dismissContentController
{
if ([_contentWindow.rootViewController conformsToProtocol:@protocol(TGContentViewController)])
{
[(id<TGContentViewController>)_contentWindow.rootViewController contentControllerWillBeDismissed];
}
[_contentWindow.rootViewController viewWillDisappear:false];
[_contentWindow.rootViewController viewDidDisappear:false];
_contentWindow.rootViewController = nil;
if (_contentWindow.isKeyWindow)
[_contentWindow resignKeyWindow];
[_window makeKeyWindow];
_contentWindow = nil;
UIViewController *topViewController = TGAppDelegateInstance.rootController.viewControllers.lastObject;
if ([topViewController conformsToProtocol:@protocol(TGDestructableViewController)] && [topViewController respondsToSelector:@selector(contentControllerWillBeDismissed)]) {
[(id<TGDestructableViewController>)topViewController contentControllerWillBeDismissed];
}
dispatch_async(dispatch_get_main_queue(), ^
{
if (!_window.isKeyWindow)
[_window makeKeyWindow];
});
}
- (void)openURLNative:(NSURL *)url
{
[(TGApplication *)[UIApplication sharedApplication] openURL:url forceNative:true];
}
#pragma mark -
- (NSDictionary *)loadLoginState
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
NSString *documentsDirectory = [TGAppDelegate documentsPath];
NSData *stateData = [[NSData alloc] initWithContentsOfFile:[documentsDirectory stringByAppendingPathComponent:@"state.data"]];
if (stateData.length != 0)
{
NSInputStream *is = [[NSInputStream alloc] initWithData:stateData];
[is open];
uint8_t version = 0;
[is read:(uint8_t *)&version maxLength:1];
{
int date = [is readInt32];
if (date != 0)
dict[@"date"] = @(date);
}
{
NSString *phoneNumber = [is readString];
if (phoneNumber.length != 0)
dict[@"phoneNumber"] = phoneNumber;
}
{
NSString *phoneCode = [is readString];
if (phoneCode.length != 0)
dict[@"phoneCode"] = phoneCode;
}
{
NSString *phoneCodeHash = [is readString];
if (phoneCodeHash.length != 0)
dict[@"phoneCodeHash"] = phoneCodeHash;
}
{
NSString *firstName = [is readString];
if (firstName.length != 0)
dict[@"firstName"] = firstName;
}
{
NSString *lastName = [is readString];
if (lastName.length != 0)
dict[@"lastName"] = lastName;
}
{
NSData *photo = [is readBytes];
if (photo.length != 0)
dict[@"photo"] = photo;
}
if (version >= 1)
{
dict[@"codeSentToTelegram"] = @([is readInt32] != 0);
}
[is close];
}
return dict;
}
- (void)resetLoginState
{
NSString *documentsDirectory = [TGAppDelegate documentsPath];
[[NSFileManager defaultManager] removeItemAtPath:[documentsDirectory stringByAppendingPathComponent:@"state.data"] error:nil];
}
- (void)saveLoginStateWithDate:(int)date phoneNumber:(NSString *)phoneNumber phoneCode:(NSString *)phoneCode phoneCodeHash:(NSString *)phoneCodeHash codeSentToTelegram:(bool)codeSentToTelegram firstName:(NSString *)firstName lastName:(NSString *)lastName photo:(NSData *)photo
{
NSOutputStream *os = [[NSOutputStream alloc] initToMemory];
[os open];
uint8_t version = 1;
[os write:&version maxLength:1];
[os writeInt32:date];
[os writeString:phoneNumber];
[os writeString:phoneCode];
[os writeString:phoneCodeHash];
[os writeString:firstName];
[os writeString:lastName];
[os writeBytes:photo];
[os writeInt32:codeSentToTelegram ? 1 : 0];
[os close];
NSData *data = [os currentBytes];
NSString *documentsDirectory = [TGAppDelegate documentsPath];
[data writeToFile:[documentsDirectory stringByAppendingPathComponent:@"state.data"] atomically:true];
}
- (void)loadSettings
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
TGTelegraphInstance.clientUserId = [[userDefaults objectForKey:@"telegraphUserId"] intValue];
TGTelegraphInstance.clientIsActivated = [[userDefaults objectForKey:@"telegraphUserActivated"] boolValue];
TGLog(@"Activated = %d", TGTelegraphInstance.clientIsActivated ? 1 : 0);
id value = nil;
if ((value = [userDefaults objectForKey:@"soundEnabled"]) != nil)
_soundEnabled = [value boolValue];
else
_soundEnabled = true;
if ((value = [userDefaults objectForKey:@"outgoingSoundEnabled"]) != nil)
_outgoingSoundEnabled = [value boolValue];
else
_outgoingSoundEnabled = true;
if ((value = [userDefaults objectForKey:@"vibrationEnabled"]) != nil)
_vibrationEnabled = [value boolValue];
else
_vibrationEnabled = false;
if ((value = [userDefaults objectForKey:@"bannerEnabled"]) != nil)
_bannerEnabled = [value boolValue];
else
_bannerEnabled = true;
if ((value = [userDefaults objectForKey:@"locationTranslationEnabled"]) != nil)
_locationTranslationEnabled = [value boolValue];
else
_locationTranslationEnabled = false;
if ((value = [userDefaults objectForKey:@"exclusiveConversationControllers"]) != nil)
_exclusiveConversationControllers = [value boolValue];
else
_exclusiveConversationControllers = true;
if ((value = [userDefaults objectForKey:@"autosavePhotos"]) != nil)
_autosavePhotos = [value boolValue];
else
_autosavePhotos = false;
if ((value = [userDefaults objectForKey:@"customChatBackground"]) != nil)
_customChatBackground = [value boolValue];
else
{
_customChatBackground = false;
NSString *imageUrl = @"wallpaper-original-pattern-default";
NSString *thumbnailUrl = @"local://wallpaper-thumb-pattern-default";
NSString *filePath = [[NSBundle mainBundle] pathForResource:imageUrl ofType:@"jpg"];
int tintColor = 0x0c3259;
if (filePath != nil)
{
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSString *documentsDirectory = [TGAppDelegate documentsPath];
NSString *wallpapersPath = [documentsDirectory stringByAppendingPathComponent:@"wallpapers"];
[fileManager createDirectoryAtPath:wallpapersPath withIntermediateDirectories:true attributes:nil error:nil];
[fileManager copyItemAtPath:filePath toPath:[wallpapersPath stringByAppendingPathComponent:@"_custom.jpg"] error:nil];
[[thumbnailUrl dataUsingEncoding:NSUTF8StringEncoding] writeToFile:[wallpapersPath stringByAppendingPathComponent:@"_custom-meta"] atomically:false];
[(tintColor == -1 ? [NSData data] : [[NSData alloc] initWithBytes:&tintColor length:4]) writeToFile:[wallpapersPath stringByAppendingPathComponent:@"_custom_mono.dat"] atomically:false];
_customChatBackground = true;
}
}
if ((value = [userDefaults objectForKey:@"useDifferentBackend"]) != nil)
_useDifferentBackend = [value boolValue];
else
_useDifferentBackend = true;
if ((value = [userDefaults objectForKey:@"baseFontSize"]) != nil)
TGBaseFontSize = MAX(16, MIN(60, [value intValue]));
else
TGBaseFontSize = 16;
if ((value = [userDefaults objectForKey:@"autoDownloadPhotosInGroups"]) != nil)
_autoDownloadPhotosInGroups = [value boolValue];
else
_autoDownloadPhotosInGroups = true;
if ((value = [userDefaults objectForKey:@"autoDownloadPhotosInPrivateChats"]) != nil)
_autoDownloadPhotosInPrivateChats = [value boolValue];
else
_autoDownloadPhotosInPrivateChats = true;
if ((value = [userDefaults objectForKey:@"autoDownloadAudioInGroups"]) != nil)
_autoDownloadAudioInGroups = [value boolValue];
else
_autoDownloadAudioInGroups = true;
if ((value = [userDefaults objectForKey:@"autoDownloadAudioInPrivateChats"]) != nil)
_autoDownloadAudioInPrivateChats = [value boolValue];
else
_autoDownloadAudioInPrivateChats = true;
if ((value = [userDefaults objectForKey:@"autoPlayAudio"]) != nil)
_autoPlayAudio = [value boolValue];
else
_autoPlayAudio = false;
if ((value = [userDefaults objectForKey:@"autoPlayAnimations"]) != nil)
_autoPlayAnimations = [value boolValue];
else
_autoPlayAnimations = cpuCoreCount() > 1;
if ((value = [userDefaults objectForKey:@"alwaysShowStickersMode"]) != nil)
_alwaysShowStickersMode = [value intValue];
else
_alwaysShowStickersMode = false;
if ((value = [userDefaults objectForKey:@"allowSecretWebpages"]) != nil)
_allowSecretWebpages = [value intValue];
else
_allowSecretWebpages = false;
if ((value = [userDefaults objectForKey:@"allowSecretWebpagesInitialized"]) != nil)
_allowSecretWebpagesInitialized = [value intValue];
else
_allowSecretWebpagesInitialized = false;
if ((value = [userDefaults objectForKey:@"secretInlineBotsInitialized"]) != nil)
_secretInlineBotsInitialized = [value intValue];
else
_secretInlineBotsInitialized = false;
#if defined(DEBUG)
_allowSecretWebpages = false;
_allowSecretWebpagesInitialized = false;
_secretInlineBotsInitialized = false;
#endif
_locationTranslationEnabled = false;
}
- (void)saveSettings
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:[[NSNumber alloc] initWithInt:TGTelegraphInstance.clientUserId] forKey:@"telegraphUserId"];
[userDefaults setObject:[[NSNumber alloc] initWithBool:TGTelegraphInstance.clientIsActivated] forKey:@"telegraphUserActivated"];
[userDefaults setObject:[NSNumber numberWithBool:_soundEnabled] forKey:@"soundEnabled"];
[userDefaults setObject:[NSNumber numberWithBool:_outgoingSoundEnabled] forKey:@"outgoingSoundEnabled"];
[userDefaults setObject:[NSNumber numberWithBool:_vibrationEnabled] forKey:@"vibrationEnabled"];
[userDefaults setObject:[NSNumber numberWithBool:_bannerEnabled] forKey:@"bannerEnabled"];
[userDefaults setObject:[NSNumber numberWithBool:_locationTranslationEnabled] forKey:@"locationTranslationEnabled"];
[userDefaults setObject:[NSNumber numberWithBool:_exclusiveConversationControllers] forKey:@"exclusiveConversationControllers"];
[userDefaults setObject:[NSNumber numberWithBool:_autosavePhotos] forKey:@"autosavePhotos"];
[userDefaults setObject:[NSNumber numberWithBool:_customChatBackground] forKey:@"customChatBackground"];
[userDefaults setObject:[NSNumber numberWithBool:_useDifferentBackend] forKey:@"useDifferentBackend"];
[userDefaults setObject:[NSNumber numberWithInt:TGBaseFontSize] forKey:@"baseFontSize"];
[userDefaults setObject:[NSNumber numberWithBool:_autoDownloadPhotosInGroups] forKey:@"autoDownloadPhotosInGroups"];
[userDefaults setObject:[NSNumber numberWithBool:_autoDownloadPhotosInPrivateChats] forKey:@"autoDownloadPhotosInPrivateChats"];
[userDefaults setObject:[NSNumber numberWithBool:_autoDownloadAudioInGroups] forKey:@"autoDownloadAudioInGroups"];
[userDefaults setObject:[NSNumber numberWithBool:_autoDownloadAudioInPrivateChats] forKey:@"autoDownloadAudioInPrivateChats"];
[userDefaults setObject:[NSNumber numberWithBool:_autoPlayAudio] forKey:@"autoPlayAudio"];
[userDefaults setObject:[NSNumber numberWithBool:_autoPlayAnimations] forKey:@"autoPlayAnimations"];
[userDefaults setObject:[NSNumber numberWithInt:_alwaysShowStickersMode] forKey:@"alwaysShowStickersMode"];
[userDefaults setObject:[NSNumber numberWithInt:_allowSecretWebpages] forKey:@"allowSecretWebpages"];
[userDefaults setObject:[NSNumber numberWithInt:_allowSecretWebpagesInitialized] forKey:@"allowSecretWebpagesInitialized"];
[userDefaults setObject:[NSNumber numberWithInt:_secretInlineBotsInitialized] forKey:@"secretInlineBotsInitialized"];
[userDefaults synchronize];
}
#pragma mark -
- (NSArray *)modernAlertSoundTitles
{
static NSArray *soundArray = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:@"None"];
[array addObject:@"Default"];
[array addObject:@"Note"];
[array addObject:@"Aurora"];
[array addObject:@"Bamboo"];
[array addObject:@"Chord"];
[array addObject:@"Circles"];
[array addObject:@"Complete"];
[array addObject:@"Hello"];
[array addObject:@"Input"];
[array addObject:@"Keys"];
[array addObject:@"Popcorn"];
[array addObject:@"Pulse"];
[array addObject:@"Synth"];
soundArray = array;
});
return soundArray;
}
- (NSArray *)classicAlertSoundTitles
{
static NSArray *soundArray = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
NSMutableArray *array = [[NSMutableArray alloc] init];
[array addObject:@"Tri-tone"];
[array addObject:@"Tremolo"];
[array addObject:@"Alert"];
[array addObject:@"Bell"];
[array addObject:@"Calypso"];
[array addObject:@"Chime"];
[array addObject:@"Glass"];
[array addObject:@"Telegraph"];
soundArray = array;
});
return soundArray;
}
- (void)playSound:(NSString *)name vibrate:(bool)vibrate
{
dispatch_async(dispatch_get_main_queue(), ^
{
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive)
return;
if (name != nil && TGAppDelegateInstance.soundEnabled)
{
static NSMutableDictionary *soundPlayed = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
soundPlayed = [[NSMutableDictionary alloc] init];
});
double lastTimeSoundPlayed = [[soundPlayed objectForKey:name] doubleValue];
CFAbsoluteTime currentTime = CFAbsoluteTimeGetCurrent();
if (currentTime - lastTimeSoundPlayed < 0.25)
return;
[soundPlayed setObject:[[NSNumber alloc] initWithDouble:currentTime] forKey:name];
NSNumber *soundId = [_loadedSoundSamples objectForKey:name];
if (soundId == nil)
{
NSString *path = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] resourcePath], name];
NSURL *filePath = [NSURL fileURLWithPath:path isDirectory:NO];
SystemSoundID sound;
AudioServicesCreateSystemSoundID((__bridge CFURLRef)filePath, &sound);
soundId = [NSNumber numberWithUnsignedLong:sound];
[_loadedSoundSamples setObject:soundId forKey:name];
}
CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
AudioServicesPlaySystemSound((SystemSoundID)[soundId unsignedLongValue]);
TGLog(@"sound time: %f ms", (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0);
}
if (vibrate && TGAppDelegateInstance.vibrationEnabled)
{
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
}
});
}
- (void)playNotificationSound:(NSString *)name
{
dispatch_async(dispatch_get_main_queue(), ^
{
_currentAudioPlayer.delegate = nil;
_currentAudioPlayer = nil;
NSError *error = nil;
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[[NSBundle mainBundle] URLForResource:name withExtension: @"m4a"] error:&error];
if (error == nil)
{
if (_currentAudioPlayerSession == nil)
_currentAudioPlayerSession = [[SMetaDisposable alloc] init];
[_currentAudioPlayerSession setDisposable:[[TGAudioSessionManager instance] requestSessionWithType:TGAudioSessionTypePlayMusic interrupted:^
{
_currentAudioPlayer.delegate = nil;
[_currentAudioPlayer stop];
_currentAudioPlayer = nil;
}]];
_currentAudioPlayer = audioPlayer;
audioPlayer.delegate = self;
[audioPlayer play];
}
});
}
- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer *)player successfully:(BOOL)__unused flag
{
if (player == _currentAudioPlayer)
{
_currentAudioPlayer.delegate = nil;
_currentAudioPlayer = nil;
[_currentAudioPlayerSession setDisposable:nil];
}
}
- (void)audioPlayerDecodeErrorDidOccur:(AVAudioPlayer *)__unused player error:(NSError *)__unused error
{
if (player == _currentAudioPlayer)
{
_currentAudioPlayer.delegate = nil;
_currentAudioPlayer = nil;
}
}
#pragma mark -
- (void)requestDeviceToken:(id<TGDeviceTokenListener>)listener
{
if (_tokenAlreadyRequested)
{
[_deviceTokenListener deviceTokenRequestCompleted:nil];
return;
}
_deviceTokenListener = listener;
if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)])
{
UIMutableUserNotificationCategory *muteActionCategory = [[UIMutableUserNotificationCategory alloc] init];
[muteActionCategory setIdentifier:@"m"];
UIMutableUserNotificationCategory *replyActionCategory = [[UIMutableUserNotificationCategory alloc] init];
[replyActionCategory setIdentifier:@"r"];
UIMutableUserNotificationCategory *channelActionCategory = [[UIMutableUserNotificationCategory alloc] init];
[channelActionCategory setIdentifier:@"c"];
bool exclusiveQuickReplySupported = ((iosMajorVersion() == 9 && iosMinorVersion() >= 1) || iosMajorVersion() > 9);
{
UIMutableUserNotificationAction *replyAction = [[UIMutableUserNotificationAction alloc] init];
[replyAction setTitle:TGLocalized(@"Notification.Reply")];
[replyAction setIdentifier:@"reply"];
[replyAction setDestructive:false];
if (iosMajorVersion() >= 9)
{
[replyAction setAuthenticationRequired:false];
[replyAction setBehavior:UIUserNotificationActionBehaviorTextInput];
[replyAction setActivationMode:UIUserNotificationActivationModeBackground];
}
else
{
[replyAction setAuthenticationRequired:true];
[replyAction setActivationMode:UIUserNotificationActivationModeForeground];
}
UIMutableUserNotificationAction *muteAction = [[UIMutableUserNotificationAction alloc] init];
[muteAction setActivationMode:UIUserNotificationActivationModeBackground];
[muteAction setTitle:TGLocalized(@"Notification.Mute1h")];
[muteAction setIdentifier:@"mute"];
[muteAction setDestructive:true];
[muteAction setAuthenticationRequired:false];
if (exclusiveQuickReplySupported)
{
[muteActionCategory setActions:@[replyAction] forContext:UIUserNotificationActionContextDefault];
}
else
{
[muteActionCategory setActions:@[replyAction, muteAction] forContext:UIUserNotificationActionContextDefault];
}
}
{
UIMutableUserNotificationAction *replyAction = [[UIMutableUserNotificationAction alloc] init];
[replyAction setTitle:TGLocalized(@"Notification.Reply")];
[replyAction setIdentifier:@"reply"];
[replyAction setDestructive:false];
if (iosMajorVersion() >= 9)
{
[replyAction setAuthenticationRequired:false];
[replyAction setBehavior:UIUserNotificationActionBehaviorTextInput];
[replyAction setActivationMode:UIUserNotificationActivationModeBackground];
}
else
{
[replyAction setAuthenticationRequired:true];
[replyAction setActivationMode:UIUserNotificationActivationModeForeground];
}
UIMutableUserNotificationAction *muteAction = [[UIMutableUserNotificationAction alloc] init];
[muteAction setActivationMode:UIUserNotificationActivationModeBackground];
[muteAction setTitle:TGLocalized(@"Notification.Mute1hMin")];
[muteAction setIdentifier:@"mute"];
[muteAction setDestructive:true];
[muteAction setAuthenticationRequired:false];
if (exclusiveQuickReplySupported)
{
[muteActionCategory setActions:@[replyAction] forContext:UIUserNotificationActionContextMinimal];
}
else
{
[muteActionCategory setActions:@[replyAction, muteAction] forContext:UIUserNotificationActionContextMinimal];
}
}
{
UIMutableUserNotificationAction *replyAction = [[UIMutableUserNotificationAction alloc] init];
[replyAction setTitle:TGLocalized(@"Notification.Reply")];
[replyAction setIdentifier:@"reply"];
[replyAction setDestructive:false];
if (iosMajorVersion() >= 9)
{
[replyAction setAuthenticationRequired:false];
[replyAction setBehavior:UIUserNotificationActionBehaviorTextInput];
[replyAction setActivationMode:UIUserNotificationActivationModeBackground];
}
else
{
[replyAction setAuthenticationRequired:true];
[replyAction setActivationMode:UIUserNotificationActivationModeForeground];
}
UIMutableUserNotificationAction *likeAction = [[UIMutableUserNotificationAction alloc] init];
[likeAction setActivationMode:UIUserNotificationActivationModeBackground];
[likeAction setTitle:@"👍"];
[likeAction setIdentifier:@"like"];
[likeAction setDestructive:false];
[likeAction setAuthenticationRequired:false];
if (exclusiveQuickReplySupported)
{
[replyActionCategory setActions:@[replyAction] forContext:UIUserNotificationActionContextDefault];
}
else
{
[replyActionCategory setActions:@[replyAction, likeAction] forContext:UIUserNotificationActionContextDefault];
}
}
{
UIMutableUserNotificationAction *replyAction = [[UIMutableUserNotificationAction alloc] init];
[replyAction setTitle:TGLocalized(@"Notification.Reply")];
[replyAction setIdentifier:@"reply"];
[replyAction setDestructive:false];
if (iosMajorVersion() >= 9)
{
[replyAction setAuthenticationRequired:false];
[replyAction setBehavior:UIUserNotificationActionBehaviorTextInput];
[replyAction setActivationMode:UIUserNotificationActivationModeBackground];
}
else
{
[replyAction setAuthenticationRequired:true];
[replyAction setActivationMode:UIUserNotificationActivationModeForeground];
}
UIMutableUserNotificationAction *likeAction = [[UIMutableUserNotificationAction alloc] init];
[likeAction setActivationMode:UIUserNotificationActivationModeBackground];
[likeAction setTitle:@"👍"];
[likeAction setIdentifier:@"like"];
[likeAction setDestructive:false];
[likeAction setAuthenticationRequired:false];
if (exclusiveQuickReplySupported)
{
[replyActionCategory setActions:@[replyAction] forContext:UIUserNotificationActionContextMinimal];
}
else
{
[replyActionCategory setActions:@[replyAction, likeAction] forContext:UIUserNotificationActionContextMinimal];
}
}
{
UIMutableUserNotificationAction *muteAction = [[UIMutableUserNotificationAction alloc] init];
[muteAction setActivationMode:UIUserNotificationActivationModeBackground];
[muteAction setTitle:TGLocalized(@"Notification.Mute1h")];
[muteAction setIdentifier:@"mute"];
[muteAction setDestructive:true];
[muteAction setAuthenticationRequired:false];
[channelActionCategory setActions:@[muteAction] forContext:UIUserNotificationActionContextDefault];
}
{
UIMutableUserNotificationAction *muteAction = [[UIMutableUserNotificationAction alloc] init];
[muteAction setActivationMode:UIUserNotificationActivationModeBackground];
[muteAction setTitle:TGLocalized(@"Notification.Mute1hMin")];
[muteAction setIdentifier:@"mute"];
[muteAction setDestructive:true];
[muteAction setAuthenticationRequired:false];
[channelActionCategory setActions:@[muteAction] forContext:UIUserNotificationActionContextMinimal];
}
NSSet *categories = [NSSet setWithObjects:muteActionCategory, replyActionCategory, channelActionCategory, nil];
UIUserNotificationType types = (UIUserNotificationTypeAlert | UIUserNotificationTypeSound | UIUserNotificationTypeBadge);
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:categories];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
}
else
{
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}
}
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)__unused notificationSettings
{
[application registerForRemoteNotifications];
}
- (void)application:(UIApplication*)__unused application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
_tokenAlreadyRequested = true;
NSString *token = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]];
token = [token stringByReplacingOccurrencesOfString:@" " withString:@""];
TGLog(@"Device token: %@", token);
[_deviceTokenListener deviceTokenRequestCompleted:token];
_deviceTokenListener = nil;
}
- (void)application:(UIApplication*)__unused application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
{
_tokenAlreadyRequested = true;
TGLog(@"Failed register for remote notifications: %@", error);
[_deviceTokenListener deviceTokenRequestCompleted:nil];
_deviceTokenListener = nil;
}
- (void)application:(UIApplication *)__unused application didReceiveLocalNotification:(UILocalNotification *)notification
{
if (iosMajorVersion() >= 8 && [notification.category isEqualToString:@"wr"])
{
[TGBridgeRemoteHandler handleLocalNotification:notification.userInfo];
return;
}
if (!_inBackground || [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
return;
int64_t peerId = [[notification.userInfo objectForKey:@"cid"] longLongValue];
[self _replyActionForPeerId:peerId mid:0 openKeyboard:false responseInfo:nil completion:nil];
}
- (void)application:(UIApplication *)__unused application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
#ifdef DEBUG
TGLog(@"remoteNotification: %@", userInfo);
#endif
[self processPossibleConfigUpdateNotification:userInfo];
if (!_inBackground)
return;
[self processRemoteNotification:userInfo];
}
- (void)application:(UIApplication *)__unused application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[self processPossibleConfigUpdateNotification:userInfo];
if ([application applicationState] != UIApplicationStateActive)
{
if ([self isCurrentlyLocked] && (_passcodeWindow == nil || _passcodeWindow.hidden))
[self displayUnlockWindowIfNeeded];
[[TGTelegramNetworking instance] resume];
if (completionHandler != nil)
{
[[TGTelegramNetworking instance] wakeUpWithCompletion:^
{
TGDispatchOnMainThread(^
{
if (_inBackground)
{
[[TGTelegramNetworking instance] pause];
TGLog(@"paused network");
NSTimeInterval remainingTime = [[UIApplication sharedApplication] backgroundTimeRemaining];
if (remainingTime > 2.0) {
TGDispatchAfter(MIN(remainingTime, 5.0), dispatch_get_main_queue(), ^
{
TGLog(@"completed fetch");
completionHandler(UIBackgroundFetchResultNewData);
});
} else {
completionHandler(UIBackgroundFetchResultNewData);
}
}
});
}];
}
}
else if (completionHandler != nil)
completionHandler(UIBackgroundFetchResultNewData);
if (!_inBackground || !_enteringForeground)
return;
[self processRemoteNotification:userInfo];
}
- (void)processRemoteNotification:(NSDictionary *)userInfo
{
[self processRemoteNotification:userInfo removeView:nil];
}
- (void)processRemoteNotification:(NSDictionary *)userInfo removeView:(UIView *)removeView
{
if (TGTelegraphInstance.clientUserId == 0)
{
[removeView removeFromSuperview];
return;
}
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
return;
id nFromId = [userInfo objectForKey:@"from_id"];
id nChatId = [userInfo objectForKey:@"chat_id"];
id nContactId = [userInfo objectForKey:@"contact_id"];
id nChannelId = [userInfo objectForKey:@"channel_id"];
int64_t conversationId = 0;
if (nFromId != nil && [TGSchema canCreateIntFromObject:nFromId])
{
conversationId = [TGSchema intFromObject:nFromId];
}
else if (nChatId != nil && [TGSchema canCreateIntFromObject:nChatId])
{
conversationId = -[TGSchema intFromObject:nChatId];
}
else if (nContactId != nil && [TGSchema canCreateIntFromObject:nContactId])
{
conversationId = [TGSchema intFromObject:nContactId];
}
else if (nChannelId != nil && [TGSchema canCreateIntFromObject:nChannelId])
{
conversationId = TGPeerIdFromChannelId([TGSchema intFromObject:nChannelId]);
}
else
{
[removeView removeFromSuperview];
}
[self _replyActionForPeerId:conversationId mid:0 openKeyboard:false responseInfo:nil completion:nil];
}
- (void)processPossibleConfigUpdateNotification:(NSDictionary *)userInfo
{
if (userInfo[@"dc"] != nil && [userInfo[@"dc"] respondsToSelector:@selector(intValue)] && userInfo[@"addr"] != nil && [userInfo[@"addr"] respondsToSelector:@selector(rangeOfString:)])
{
int datacenterId = [userInfo[@"dc"] intValue];
NSString *addr = userInfo[@"addr"];
NSRange range = [addr rangeOfString:@":"];
if (range.location != NSNotFound)
{
NSString *ip = [addr substringWithRange:NSMakeRange(0, range.location)];
int port = [[addr substringWithRange:NSMakeRange(range.location + 1, addr.length - range.location - 1)] intValue];
TGLog(@"===== Updating dc%d: %@:%d", datacenterId, ip, port);
if (ip.length != 0)
{
[[TGTelegramNetworking instance] mergeDatacenterAddress:datacenterId address:[[MTDatacenterAddress alloc] initWithIp:ip port:(uint16_t)(port == 0 ? 443 : port) preferForMedia:false restrictToTcp:false]];
}
}
}
}
- (void)_generateUpdateNetworkConfigurationMessage
{
[ActionStageInstance() dispatchOnStageQueue:^
{
TGUser *selfUser = [TGDatabaseInstance() loadUser:TGTelegraphInstance.clientUserId];
if (selfUser != nil)
{
int uid = [TGTelegraphInstance createServiceUserIfNeeded];
TGMessage *message = [[TGMessage alloc] init];
message.mid = [[[TGDatabaseInstance() generateLocalMids:1] objectAtIndex:0] intValue];
message.fromUid = uid;
message.toUid = TGTelegraphInstance.clientUserId;
message.date = (int)[[TGTelegramNetworking instance] approximateRemoteTime];
message.unread = false;
message.outgoing = false;
message.cid = uid;
NSString *displayName = selfUser.firstName;
if (displayName.length == 0)
displayName = selfUser.lastName;
message.text = TGLocalized(@"Service.NetworkConfigurationUpdatedMessage");
static int messageActionId = 1000000;
[[[TGConversationAddMessagesActor alloc] initWithPath:[NSString stringWithFormat:@"/tg/addmessage/(%dact3)", messageActionId++]] execute:[NSDictionary dictionaryWithObjectsAndKeys:[[NSArray alloc] initWithObjects:message, nil], @"messages", nil]];
}
}];
}
- (NSUInteger)application:(UIApplication *)__unused application supportedInterfaceOrientationsForWindow:(UIWindow *)__unused window
{
return UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone ? UIInterfaceOrientationMaskAllButUpsideDown : UIInterfaceOrientationMaskAll;
}
#pragma mark - BITUpdateManagerDelegate
- (NSString *)customDeviceIdentifierForUpdateManager:(BITUpdateManager *)__unused updateManager
{
#if defined(DEBUG) || defined(INTERNAL_RELEASE)
TGLog(@"returning devide identifier");
if ([[UIDevice currentDevice] respondsToSelector:@selector(uniqueIdentifier)])
return [[UIDevice currentDevice] performSelector:@selector(uniqueIdentifier)];
#endif
return nil;
}
- (void)reloadSettingsController:(int)uid
{
TGAccountSettingsController *accountSettingsController = [[TGAccountSettingsController alloc] initWithUid:uid];
int index = -1;
for (id controller in _rootController.mainTabsController.viewControllers)
{
index++;
if ([controller isKindOfClass:[TGAccountSettingsController class]])
break;
}
if (index != -1)
{
NSMutableArray *viewControllers = [_rootController.mainTabsController.viewControllers mutableCopy];
[viewControllers replaceObjectAtIndex:index withObject:accountSettingsController];
[_rootController.mainTabsController setViewControllers:viewControllers];
_rootController.accountSettingsController = accountSettingsController;
}
}
- (BOOL)application:(UIApplication *)__unused application openURL:(NSURL *)url sourceApplication:(NSString *)__unused sourceApplication annotation:(id)__unused annotation
{
[self handleOpenDocument:url animated:false];
return true;
}
- (void)resetControllerStack
{
[TGAppDelegateInstance.rootController clearContentControllers];
}
- (void)handleOpenDocument:(NSURL *)url animated:(bool)__unused animated
{
if (TGTelegraphInstance.clientUserId != 0 && TGTelegraphInstance.clientIsActivated)
{
if ([url isFileURL])
{
[self resetControllerStack];
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:[url path] error:nil];
if (attributes != nil)
{
int fileSize = [[attributes objectForKey:NSFileSize] intValue];
TGForwardTargetController *forwardController = [[TGForwardTargetController alloc] initWithDocumentFile:url size:fileSize];
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[forwardController]];
[TGAppDelegateInstance.rootController dismissViewControllerAnimated:false completion:nil];
dispatch_async(dispatch_get_main_queue(), ^
{
[TGAppDelegateInstance.rootController presentViewController:navigationController animated:false completion:nil];
});
}
}
else if ([url.scheme isEqualToString:@"telegram"] || [url.scheme isEqualToString:@"tg"])
{
if ([url.resourceSpecifier hasPrefix:@"//share?"])
{
NSMutableArray *uploadFileArray = [[NSMutableArray alloc] init];
NSMutableArray *forwardMessageArray = [[NSMutableArray alloc] init];
NSMutableArray *sendMessageArray = [[NSMutableArray alloc] init];
NSString *groupName = [@"group." stringByAppendingString:[[NSBundle mainBundle] bundleIdentifier]];
NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupName];
if (groupURL != nil)
{
NSURL *inboxUrl = [groupURL URLByAppendingPathComponent:@"share-inbox" isDirectory:true];
[[NSFileManager defaultManager] createDirectoryAtURL:inboxUrl withIntermediateDirectories:true attributes:nil error:nil];
NSDictionary *dict = [TGStringUtils argumentDictionaryInUrlString:[url query]];
NSUInteger counter = 0;
while (true)
{
NSString *fileId = [[NSString alloc] initWithFormat:@"f%d", (int)counter];
if (dict[fileId] != nil)
{
NSURL *fileUrl = [inboxUrl URLByAppendingPathComponent:dict[fileId]];
NSString *fileType = @"raw";
NSString *rawFileType = dict[[[NSString alloc] initWithFormat:@"t%d", (int)counter]];
if ([rawFileType isEqualToString:@"i"])
fileType = @"image";
else if ([rawFileType isEqualToString:@"v"])
fileType = @"video";
NSString *fileName = dict[[[NSString alloc] initWithFormat:@"n%d", (int)counter]];
[uploadFileArray addObject:@{@"url": fileUrl, @"type": fileType, @"fileName": fileName == nil ? @"" : fileName}];
counter++;
}
else
{
NSString *internalMessageIdString = dict[[[NSString alloc] initWithFormat:@"m%d", (int)counter]];
if (internalMessageIdString != nil)
{
TGMessage *message = [TGDatabaseInstance() loadMessageWithMid:[internalMessageIdString intValue] peerId:0];
if (message == nil)
{
message = [TGDatabaseInstance() loadMediaMessageWithMid:[internalMessageIdString intValue]];
}
if (message != nil)
[forwardMessageArray addObject:message];
counter++;
}
else
{
NSString *urlString = dict[[[NSString alloc] initWithFormat:@"u%d", (int)counter]];
if (urlString != nil)
{
TGMessage *message = [[TGMessage alloc] init];
message.text = urlString;
[sendMessageArray addObject:message];
counter++;
}
else
break;
}
}
}
}
if (uploadFileArray.count != 0)
{
TGForwardTargetController *forwardController = [[TGForwardTargetController alloc] initWithDocumentFiles:uploadFileArray];
forwardController.controllerTitle = TGLocalized(@"Share.Title");
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[forwardController]];
[_rootController clearContentControllers];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
navigationController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
}
dispatch_async(dispatch_get_main_queue(), ^
{
[_rootController presentViewController:navigationController animated:false completion:nil];
});
}
else if (forwardMessageArray.count != 0 || sendMessageArray.count != 0)
{
TGForwardTargetController *forwardController = [[TGForwardTargetController alloc] initWithForwardMessages:forwardMessageArray sendMessages:sendMessageArray shareLink:nil showSecretChats:true];
forwardController.controllerTitle = TGLocalized(@"Share.Title");
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[forwardController]];
[_rootController clearContentControllers];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
navigationController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
}
dispatch_async(dispatch_get_main_queue(), ^
{
[_rootController presentViewController:navigationController animated:false completion:nil];
});
}
}
else if ([url.resourceSpecifier hasPrefix:@"//msg?"])
{
NSDictionary *dict = [TGStringUtils argumentDictionaryInUrlString:[url query]];
std::map<int, int> phoneIdToUid;
[TGDatabaseInstance() loadRemoteContactUidsContactIds:phoneIdToUid];
if ([dict[@"to"] respondsToSelector:@selector(characterAtIndex:)] && [(NSString *)dict[@"to"] length] != 0)
{
int32_t phoneId = phoneMatchHash(dict[@"to"]);
for (auto it : phoneIdToUid)
{
if (it.first == phoneId)
{
NSDictionary *actions = nil;
if ([dict[@"text"] respondsToSelector:@selector(characterAtIndex:)] && [(NSString *)dict[@"text"] length] != 0)
{
actions = @{@"text": dict[@"text"]};
}
[[TGInterfaceManager instance] navigateToConversationWithId:it.second conversation:nil performActions:actions animated:false];
break;
}
}
}
else if ([dict[@"text"] respondsToSelector:@selector(characterAtIndex:)] && [(NSString *)dict[@"text"] length] != 0)
{
TGMessage *message = [[TGMessage alloc] init];
message.text = dict[@"text"];
TGForwardTargetController *forwardController = [[TGForwardTargetController alloc] initWithForwardMessages:nil sendMessages:@[message] shareLink:nil showSecretChats:true];
[self resetControllerStack];
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[forwardController]];
[_rootController clearContentControllers];
[_rootController dismissViewControllerAnimated:false completion:nil];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
navigationController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
}
[_rootController presentViewController:navigationController animated:false completion:nil];
}
}
else if ([url.resourceSpecifier hasPrefix:@"//download-language?"])
{
NSDictionary *dict = [TGStringUtils argumentDictionaryInUrlString:[url query]];
if ([dict[@"url"] respondsToSelector:@selector(characterAtIndex:)])
{
[ActionStageInstance() requestActor:[[NSString alloc] initWithFormat:@"/tg/downloadLocalization/(%d)", murMurHash32(dict[@"url"])] options:@{@"url": dict[@"url"]} flags:0 watcher:TGTelegraphInstance];
}
}
else if ([url.resourceSpecifier hasPrefix:@"//resolve?"])
{
NSDictionary *dict = [TGStringUtils argumentDictionaryInUrlString:[url query]];
if ([dict[@"domain"] respondsToSelector:@selector(characterAtIndex:)])
{
[_rootController dismissViewControllerAnimated:false completion:nil];
NSMutableDictionary *arguments = [[NSMutableDictionary alloc] init];
if (dict[@"start"] != nil)
arguments[@"start"] = dict[@"start"];
if (dict[@"startgroup"] != nil)
arguments[@"startgroup"] = dict[@"startgroup"];
if (dict[@"post"] != nil) {
arguments[@"messageId"] = @([dict[@"post"] intValue]);
}
[ActionStageInstance() requestActor:[[NSString alloc] initWithFormat:@"/resolveDomain/(%@)", dict[@"domain"]] options:@{@"domain": dict[@"domain"], @"arguments": arguments} flags:0 watcher:TGTelegraphInstance];
}
}
else if ([url.resourceSpecifier hasPrefix:@"//join?"])
{
NSDictionary *dict = [TGStringUtils argumentDictionaryInUrlString:[url query]];
if ([dict[@"invite"] respondsToSelector:@selector(characterAtIndex:)])
{
[_rootController dismissViewControllerAnimated:false completion:nil];
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[progressWindow show:true];
[[[[TGGroupManagementSignals groupInvitationLinkInfo:dict[@"invite"]] deliverOn:[SQueue mainQueue]] onDispose:^
{
TGDispatchOnMainThread(^
{
[progressWindow dismiss:true];
});
}] startWithNext:^(TGGroupInvitationInfo *invitationInfo)
{
if (invitationInfo.alreadyAccepted && !invitationInfo.left)
{
if (invitationInfo.peerId != 0) {
[[TGInterfaceManager instance] navigateToConversationWithId:invitationInfo.peerId conversation:nil];
} else {
NSString *format = TGLocalized(@"GroupInfo.InvitationLinkAlreadyAccepted");
NSString *text = [[NSString alloc] initWithFormat:format, invitationInfo.title];
[[[TGAlertView alloc] initWithTitle:nil message:text cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
}
}
else
{
NSString *format = TGLocalized(@"GroupInfo.InvitationLinkAccept");
if (invitationInfo.isChannel && !invitationInfo.isChannelGroup) {
format = TGLocalized(@"GroupInfo.InvitationLinkAcceptChannel");
}
NSString *text = [[NSString alloc] initWithFormat:format, invitationInfo.title];
[[[TGAlertView alloc] initWithTitle:nil message:text cancelButtonTitle:TGLocalized(@"Common.Cancel") okButtonTitle:TGLocalized(@"Common.OK") completionBlock:^(bool okButtonPressed)
{
if (okButtonPressed)
{
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[progressWindow show:true];
[[[[TGGroupManagementSignals acceptGroupInvitationLink:dict[@"invite"]] deliverOn:[SQueue mainQueue]] onDispose:^
{
TGDispatchOnMainThread(^
{
[progressWindow dismiss:true];
});
}] startWithNext:^(TGConversation *conversation)
{
[[TGInterfaceManager instance] navigateToConversationWithId:conversation.conversationId conversation:conversation.isChannel ? conversation : nil];
} error:^(__unused id error)
{
NSString *text = TGLocalized(@"GroupInfo.InvitationLinkDoesNotExist");
if ([error respondsToSelector:@selector(characterAtIndex:)])
{
if ([error isEqualToString:@"USERS_TOO_MUCH"])
text = TGLocalized(@"GroupInfo.InvitationLinkGroupFull");
}
[[[TGAlertView alloc] initWithTitle:nil message:text cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
} completed:nil];
}
}] show];
}
} error:^(id error)
{
NSString *text = TGLocalized(@"GroupInfo.InvitationLinkDoesNotExist");
if ([error respondsToSelector:@selector(characterAtIndex:)])
{
if ([error isEqualToString:@"USER_ALREADY_PARTICIPANT"])
text = TGLocalized(@"GroupInfo.InvitationLinkAlreadyAccepted");
}
[[[TGAlertView alloc] initWithTitle:nil message:text cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
} completed:nil];
}
}
else if ([url.resourceSpecifier hasPrefix:@"//addstickers?"])
{
NSDictionary *dict = [TGStringUtils argumentDictionaryInUrlString:[url query]];
if ([dict[@"set"] respondsToSelector:@selector(characterAtIndex:)])
{
TGStickerPackShortnameReference *packReference = [[TGStickerPackShortnameReference alloc] initWithShortName:dict[@"set"]];
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] init];
[progressWindow show:true];
SSignal *stickerPackInfo = [TGStickersSignals stickerPackInfo:packReference];
SSignal *currentStickerPacks = [[TGStickersSignals stickerPacks] take:1];
SSignal *combinedSignal = [SSignal combineSignals:@[stickerPackInfo, currentStickerPacks]];
[[[combinedSignal deliverOn:[SQueue mainQueue]] onDispose:^
{
TGDispatchOnMainThread(^
{
[progressWindow dismiss:true];
});
}] startWithNext:^(NSArray *combined)
{
[self previewStickerPack:combined[0] currentStickerPacks:combined[1][@"packs"]];
} error:^(__unused id error)
{
} completed:nil];
}
}
else if ([url.resourceSpecifier hasPrefix:@"//msg_url?"]) {
NSDictionary *dict = [TGStringUtils argumentDictionaryInUrlString:[url query]];
if ([dict[@"url"] respondsToSelector:@selector(characterAtIndex:)]) {
NSMutableDictionary *linkInfo = [[NSMutableDictionary alloc] init];
linkInfo[@"url"] = dict[@"url"];
if ([dict[@"text"] respondsToSelector:@selector(characterAtIndex:)]) {
linkInfo[@"text"] = dict[@"text"];
}
TGForwardTargetController *forwardController = [[TGForwardTargetController alloc] initWithForwardMessages:nil sendMessages:nil shareLink:linkInfo showSecretChats:true];
forwardController.skipConfirmation = true;
[self resetControllerStack];
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[forwardController]];
[_rootController clearContentControllers];
[_rootController dismissViewControllerAnimated:false completion:nil];
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
navigationController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
}
[_rootController presentViewController:navigationController animated:false completion:nil];
}
}
}
else if ([url.scheme isEqualToString:[TGDropboxHelper dropboxURLScheme]])
{
[TGDropboxHelper handleOpenURL:url];
}
}
}
- (NSString *)stickerPackShortname:(TGStickerPack *)stickerPack
{
NSString *shortName = nil;
if ([stickerPack.packReference isKindOfClass:[TGStickerPackIdReference class]])
shortName = ((TGStickerPackIdReference *)stickerPack.packReference).shortName;
else if ([stickerPack.packReference isKindOfClass:[TGStickerPackShortnameReference class]])
shortName = ((TGStickerPackShortnameReference *)stickerPack.packReference).shortName;
return shortName;
}
- (void)previewStickerPack:(TGStickerPack *)stickerPack currentStickerPacks:(NSArray *)currentStickerPacks
{
TGStickerPackPreviewWindow *previewWindow = [[TGStickerPackPreviewWindow alloc] initWithParentController:_rootController.dialogListController stickerPack:stickerPack];
__weak TGStickerPackPreviewWindow *weakPreviewWindow = previewWindow;
bool alreadyInstalled = false;
for (TGStickerPack *currentStickerPack in currentStickerPacks)
{
if ([stickerPack.packReference isEqual:currentStickerPack.packReference])
{
alreadyInstalled = true;
break;
}
}
if (!alreadyInstalled && [self stickerPackShortname:stickerPack].length != 0)
{
NSString *text = [[NSString alloc] initWithFormat:TGLocalized([TGStringUtils integerValueFormat:@"StickerPack.AddStickerCount_" value:stickerPack.documents.count]), [TGStringUtils stringWithLocalizedNumber:(NSInteger)stickerPack.documents.count]];
[previewWindow.view setAction:^
{
__strong TGStickerPackPreviewWindow *strongPreviewWindow = weakPreviewWindow;
if (strongPreviewWindow != nil)
{
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] init];
[progressWindow show:true];
[[[[TGStickersSignals installStickerPack:stickerPack.packReference] deliverOn:[SQueue mainQueue]] onDispose:^
{
TGDispatchOnMainThread(^
{
[progressWindow dismissWithSuccess];
});
}] startWithNext:nil error:nil completed:^
{
__strong TGStickerPackPreviewWindow *strongPreviewWindow = weakPreviewWindow;
[strongPreviewWindow.view animateDismiss:^
{
__strong TGStickerPackPreviewWindow *strongPreviewWindow = weakPreviewWindow;
if (strongPreviewWindow != nil)
[strongPreviewWindow dismiss];
}];
}];
}
} title:text];
}
previewWindow.view.dismiss = ^
{
__strong TGStickerPackPreviewWindow *strongPreviewWindow = weakPreviewWindow;
if (strongPreviewWindow != nil)
[strongPreviewWindow dismiss];
};
previewWindow.hidden = false;
}
- (void)readyToApplyLocalizationFromFile:(NSString *)filePath warnings:(NSString *)warnings
{
[[[TGAlertView alloc] initWithTitle:nil message:warnings.length == 0 ? TGLocalized(@"Service.ApplyLocalization") : [[NSString alloc] initWithFormat:TGLocalized(@"Service.ApplyLocalizationWithWarnings"), warnings] cancelButtonTitle:TGLocalized(@"Common.Cancel") okButtonTitle:TGLocalized(@"Common.OK") completionBlock:^(bool okButtonPressed)
{
if (okButtonPressed)
{
TGSetLocalizationFromFile(filePath);
[TGAppDelegateInstance resetLocalization];
[self resetControllerStack];
}
}] show];
}
- (BOOL)application:(UIApplication *)__unused application willContinueUserActivityWithType:(NSString *)userActivityType
{
if ([userActivityType isEqualToString:@"org.telegram.conversation"]) {
if (_progressWindow != nil) {
[_progressWindow dismiss:true];
}
_progressWindow = [[TGProgressWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[_progressWindow show:true];
}
return true;
}
- (BOOL)application:(UIApplication *)__unused application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))__unused restorationHandler
{
[_progressWindow dismiss:true];
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
[application openURL:userActivity.webpageURL];
} else if ([userActivity.activityType isEqualToString:@"org.telegram.conversation"]) {
if ([userActivity.userInfo[@"user_id"] intValue] == TGTelegraphInstance.clientUserId)
{
int64_t peerId = 0;
if ([userActivity.userInfo[@"peer"][@"type"] isEqual:@"user"])
peerId = [userActivity.userInfo[@"peer"][@"id"] intValue];
else if ([userActivity.userInfo[@"peer"][@"type"] isEqual:@"group"])
peerId = -[userActivity.userInfo[@"peer"][@"id"] intValue];
if (peerId != 0)
{
[[TGInterfaceManager instance] navigateToConversationWithId:peerId conversation:nil performActions:userActivity.userInfo[@"text"] == nil ? nil : @{@"text": userActivity.userInfo[@"text"]} animated:false];
bool didSetText = false;
TGModernConversationController *controller = [[TGInterfaceManager instance] currentControllerWithPeerId:peerId];
if (controller != nil)
didSetText = TGStringCompare(userActivity.userInfo[@"text"], [controller inputText]);
if (didSetText)
{
[userActivity getContinuationStreamsWithCompletionHandler:^(__unused NSInputStream *inputStream, NSOutputStream *outputStream, NSError *error)
{
if (error == nil)
{
@try {
[outputStream open];
[outputStream close];
}
@catch (NSException *exception) {
}
@finally {
}
}
}];
}
}
}
} else if ([userActivity.activityType isEqual:CSSearchableItemActionType]) {
NSString *uniqueIdentifier = userActivity.userInfo[CSSearchableItemActivityIdentifier];
if (uniqueIdentifier != nil) {
int64_t peerId = [uniqueIdentifier longLongValue];
if (peerId != 0) {
if (peerId != 0)
{
[[TGInterfaceManager instance] navigateToConversationWithId:peerId conversation:nil performActions:nil animated:false];
}
}
}
}
return true;
}
- (void)application:(UIApplication *)__unused application didFailToContinueUserActivityWithType:(NSString *)__unused userActivityType error:(NSError *)__unused error
{
[_progressWindow dismiss:true];
}
- (void)application:(UIApplication *)__unused application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL))completionHandler
{
[_rootController clearContentControllers];
[_rootController.mainTabsController setSelectedIndex:1];
if ([shortcutItem.type isEqualToString:@"compose"])
{
[_rootController.dialogListController.dialogListCompanion composeMessageAndOpenSearch:false];
}
else if ([shortcutItem.type isEqualToString:@"search"])
{
[_rootController.dialogListController startSearch];
}
else if ([shortcutItem.type isEqualToString:@"conversation"])
{
NSDictionary *userInfo = shortcutItem.userInfo;
NSNumber *cidValue = userInfo[@"cid"];
if (cidValue != nil)
[[TGInterfaceManager instance] navigateToConversationWithId:cidValue.int64Value conversation:nil performActions:nil animated:false];
}
if (completionHandler != nil)
completionHandler(true);
}
- (void)application:(UIApplication *)__unused application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void (^)())completionHandler
{
[self application:application handleActionWithIdentifier:identifier forRemoteNotification:userInfo withResponseInfo:@{} completionHandler:completionHandler];
}
- (void)application:(UIApplication *)__unused application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(nonnull NSDictionary *)responseInfo completionHandler:(nonnull void (^)())completionHandler
{
id nFromId = [userInfo objectForKey:@"from_id"];
id nChatId = [userInfo objectForKey:@"chat_id"];
id nContactId = [userInfo objectForKey:@"contact_id"];
id nChannelId = [userInfo objectForKey:@"channel_id"];
id nMid = [userInfo objectForKey:@"msg_id"];
int64_t peerId = 0;
int32_t mid = 0;
if (nFromId != nil && [TGSchema canCreateIntFromObject:nFromId])
peerId = [TGSchema intFromObject:nFromId];
else if (nChatId != nil && [TGSchema canCreateIntFromObject:nChatId])
peerId = -[TGSchema intFromObject:nChatId];
else if (nContactId != nil && [TGSchema canCreateIntFromObject:nContactId])
peerId = [TGSchema intFromObject:nContactId];
else if (nChannelId != nil && [TGSchema canCreateIntFromObject:nChannelId])
peerId = TGPeerIdFromChannelId([TGSchema intFromObject:nChannelId]);
if (nMid != nil && [TGSchema canCreateIntFromObject:nMid])
mid = [TGSchema intFromObject:nMid];
if ([identifier isEqualToString:@"reply"])
[self _replyActionForPeerId:peerId mid:mid openKeyboard:true responseInfo:responseInfo completion:completionHandler];
else if ([identifier isEqualToString:@"like"])
[self _likeActionForPeerId:peerId completion:completionHandler];
else if ([identifier isEqualToString:@"mute"])
[self _muteActionForPeerId:peerId completion:completionHandler];
else if (completionHandler)
completionHandler();
}
- (void)application:(UIApplication *)__unused application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler
{
[self application:application handleActionWithIdentifier:identifier forLocalNotification:notification withResponseInfo:@{} completionHandler:completionHandler];
}
- (void)application:(UIApplication *)__unused application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(nonnull NSDictionary *)responseInfo completionHandler:(nonnull void (^)())completionHandler
{
int64_t peerId = [notification.userInfo[@"cid"] longLongValue];
int32_t mid = [notification.userInfo[@"mid"] int32Value];
if ([identifier isEqualToString:@"reply"])
[self _replyActionForPeerId:peerId mid:mid openKeyboard:true responseInfo:responseInfo completion:completionHandler];
else if ([identifier isEqualToString:@"like"])
[self _likeActionForPeerId:peerId completion:completionHandler];
else if ([identifier isEqualToString:@"mute"])
[self _muteActionForPeerId:peerId completion:completionHandler];
else if (completionHandler)
completionHandler();
}
- (void)_replyActionForPeerId:(int64_t)peerId mid:(int32_t)mid openKeyboard:(bool)openKeyboard responseInfo:(NSDictionary *)responseInfo completion:(void (^)())completion
{
if (iosMajorVersion() >= 9 && openKeyboard)
{
if (peerId == 0)
return;
int32_t replyToMid = 0;
if (TGPeerIdIsGroup(peerId) || TGPeerIdIsChannel(peerId))
replyToMid = mid;
void (^suspendBlock)(bool) = ^(bool succeed)
{
if (succeed)
{
TGDispatchOnMainThread(^
{
[self applicationDidEnterBackground:[UIApplication sharedApplication]];
if (completion)
completion();
});
}
else
{
[[TGTelegramNetworking instance] wakeUpWithCompletion:^
{
TGDispatchOnMainThread(^
{
if (_inBackground)
{
[[TGTelegramNetworking instance] pause];
if (completion)
completion();
}
});
}];
}
};
NSString *text = responseInfo[UIUserNotificationActionResponseTypedTextKey];
if ([text hasNonWhitespaceCharacters])
{
[[[[TGSendMessageSignals sendTextMessageWithPeerId:peerId text:text replyToMid:replyToMid] then:[[TGChatMessageListSignal readChatMessageListWithPeerId:peerId] delay:1.5 onQueue:[SQueue mainQueue]]] catch:^SSignal *(__unused id error)
{
suspendBlock(false);
return nil;
}] startWithNext:nil completed:^
{
suspendBlock(true);
}];
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive)
[[TGTelegramNetworking instance] resume];
}
else
{
suspendBlock(false);
}
}
else
{
TGLog(@"Reply action for %" PRId64 "", peerId);
if (peerId != 0 && [TGDatabaseInstance() loadConversationWithId:peerId] != nil)
{
[[TGInterfaceManager instance] navigateToConversationWithId:peerId conversation:nil performActions:nil atMessage:nil clearStack:true openKeyboard:openKeyboard && (_passcodeWindow == nil || _passcodeWindow.hidden) animated:false];
}
if (completion)
completion();
}
}
- (void)_likeActionForPeerId:(int64_t)peerId completion:(void (^)())completion
{
void (^suspendBlock)(bool) = ^(bool succeed)
{
if (succeed)
{
TGDispatchOnMainThread(^
{
[self applicationDidEnterBackground:[UIApplication sharedApplication]];
if (completion)
completion();
});
}
else
{
[[TGTelegramNetworking instance] wakeUpWithCompletion:^
{
TGDispatchOnMainThread(^
{
if (_inBackground)
{
[[TGTelegramNetworking instance] pause];
if (completion)
completion();
}
});
}];
}
};
[[[[TGSendMessageSignals sendTextMessageWithPeerId:peerId text:@"👍" replyToMid:0] then:[[TGChatMessageListSignal readChatMessageListWithPeerId:peerId] delay:1.5 onQueue:[SQueue mainQueue]]] catch:^SSignal *(__unused id error)
{
suspendBlock(false);
return nil;
}] startWithNext:nil error: nil completed:^
{
suspendBlock(true);
}];
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive)
[[TGTelegramNetworking instance] resume];
}
- (void)_muteActionForPeerId:(int64_t)peerId completion:(void (^)())completion
{
int muteUntil = 0;
[TGDatabaseInstance() loadPeerNotificationSettings:peerId soundId:NULL muteUntil:&muteUntil previewText:NULL messagesMuted:NULL notFound:NULL];
int muteTime = 1 * 60 * 60;
muteUntil = MAX(muteUntil, (int)[[TGTelegramNetworking instance] approximateRemoteTime] + muteTime);
static int actionId = 0;
void (^muteBlock)(int64_t, int32_t, NSNumber *) = ^(int64_t peerId, int32_t muteUntil, NSNumber *accessHash)
{
NSMutableDictionary *options = [NSMutableDictionary dictionaryWithDictionary:@{ @"peerId": @(peerId), @"muteUntil": @(muteUntil) }];
if (accessHash != nil)
options[@"accessHash"] = accessHash;
[ActionStageInstance() requestActor:[NSString stringWithFormat:@"/tg/changePeerSettings/(%" PRId64 ")/(muteAction%d)", peerId, actionId++] options:options watcher:TGTelegraphInstance];
};
if (TGPeerIdIsChannel(peerId))
{
[[[TGDatabaseInstance() existingChannel:peerId] take:1] startWithNext:^(TGConversation *channel)
{
muteBlock(peerId, muteUntil, @(channel.accessHash));
}];
}
else
{
muteBlock(peerId, muteUntil, nil);
}
TGDispatchAfter(9.0, dispatch_get_main_queue(), ^
{
if (completion)
completion();
});
if ([[UIApplication sharedApplication] applicationState] != UIApplicationStateActive)
{
[[TGTelegramNetworking instance] resume];
if (completion != nil)
{
[[TGTelegramNetworking instance] wakeUpWithCompletion:^
{
TGDispatchOnMainThread(^
{
if (_inBackground)
{
[[TGTelegramNetworking instance] pause];
if (completion != nil)
completion();
}
});
}];
}
}
}
- (bool)isManuallyLocked
{
return [[[NSUserDefaults standardUserDefaults] objectForKey:@"Passcode_lockManually"] boolValue];
}
- (int32_t)automaticLockTimeout
{
NSNumber *nLockTimeout = [[NSUserDefaults standardUserDefaults] objectForKey:@"Passcode_lockTimeout"];
if (nLockTimeout == nil)
return 1 * 60 * 60;
return (int32_t)[nLockTimeout intValue];
}
- (void)setAutomaticLockTimeout:(int32_t)automaticLockTimeout
{
[[NSUserDefaults standardUserDefaults] setObject:@(automaticLockTimeout) forKey:@"Passcode_lockTimeout"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
- (void)setIsManuallyLocked:(bool)isLocked
{
[[NSUserDefaults standardUserDefaults] setObject:@(isLocked) forKey:@"Passcode_lockManually"];
[[NSUserDefaults standardUserDefaults] synchronize];
[ActionStageInstance() dispatchResource:@"/databasePasswordChanged" resource:nil];
}
- (bool)isCurrentlyLocked
{
return [self isCurrentlyLocked:NULL];
}
- (bool)isCurrentlyLocked:(bool *)byTimeout
{
if ([TGDatabaseInstance() isPasswordSet:NULL])
{
NSNumber *nDeactivationDate = [[NSUserDefaults standardUserDefaults] objectForKey:@"Passcode_deactivationDate"];
bool displayByDeactivationTimeout = false;
if (nDeactivationDate != nil)
{
int32_t lockTimeout = [self automaticLockTimeout];
if (lockTimeout >= 0)
{
displayByDeactivationTimeout = [[NSDate date] timeIntervalSince1970] > ([nDeactivationDate doubleValue] + lockTimeout);
if (byTimeout)
*byTimeout = displayByDeactivationTimeout;
}
}
return [self isManuallyLocked] || displayByDeactivationTimeout;
}
return false;
}
- (bool)willBeLocked {
if ([TGDatabaseInstance() isPasswordSet:NULL]) {
int32_t lockTimeout = [self automaticLockTimeout];
if (lockTimeout >= 0) {
return true;
}
}
return false;
}
- (bool)isDisplayingPasscodeWindow
{
return _passcodeWindow != nil && !_passcodeWindow.hidden;
}
- (void)onBecomeInactive
{
if ([self isCurrentlyLocked])
{
[self setIsManuallyLocked:true];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"Passcode_deactivationDate"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
else
{
[[NSUserDefaults standardUserDefaults] setObject:@([[NSDate date] timeIntervalSince1970]) forKey:@"Passcode_deactivationDate"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
if ([TGDatabaseInstance() isPasswordSet:NULL])
{
if (TGTelegraphInstance.clientUserId != 0)
{
TLRPCaccount_updateDeviceLocked$account_updateDeviceLocked *updateDeviceLocked = [[TLRPCaccount_updateDeviceLocked$account_updateDeviceLocked alloc] init];
if ([self isManuallyLocked])
updateDeviceLocked.period = 0;
else
updateDeviceLocked.period = [self automaticLockTimeout];
if (_deviceLockedRequestDisposable == nil)
_deviceLockedRequestDisposable = [[SMetaDisposable alloc] init];
[_deviceLockedRequestDisposable setDisposable:[[[TGTelegramNetworking instance] requestSignal:updateDeviceLocked] startWithNext:^(__unused id next)
{
}]];
_didUpdateDeviceLocked = true;
}
}
}
- (void)onBecomeActive
{
if (_didUpdateDeviceLocked)
{
_didUpdateDeviceLocked = false;
[self resetRemoteDeviceLocked];
}
if ([self isCurrentlyLocked])
{
[self setIsManuallyLocked:true];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:@"Passcode_deactivationDate"];
}
[self displayUnlockWindowIfNeeded];
}
- (void)resetRemoteDeviceLocked
{
if (TGTelegraphInstance.clientUserId != 0)
{
TLRPCaccount_updateDeviceLocked$account_updateDeviceLocked *updateDeviceLocked = [[TLRPCaccount_updateDeviceLocked$account_updateDeviceLocked alloc] init];
updateDeviceLocked.period = -1;
if (_deviceLockedRequestDisposable == nil)
_deviceLockedRequestDisposable = [[SMetaDisposable alloc] init];
[_deviceLockedRequestDisposable setDisposable:[[[TGTelegramNetworking instance] requestSignal:updateDeviceLocked] startWithNext:^(__unused id next)
{
}]];
}
}
+ (void)movePathsToContainer
{
if (iosMajorVersion() >= 8)
{
NSString *groupName = [@"group." stringByAppendingString:[[NSBundle mainBundle] bundleIdentifier]];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *groupURL = [fileManager containerURLForSecurityApplicationGroupIdentifier:groupName];
if (groupURL != nil)
{
NSString *documentsPath = [[groupURL path] stringByAppendingPathComponent:@"Documents"];
[fileManager createDirectoryAtPath:documentsPath withIntermediateDirectories:true attributes:nil error:NULL];
NSString *defaultDocumentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true)[0];
NSArray *documentItems = [fileManager contentsOfDirectoryAtPath:defaultDocumentsPath error:nil];
int documentFileCount = 0;
for (NSString *fileName in documentItems)
{
documentFileCount++;
[fileManager moveItemAtPath:[defaultDocumentsPath stringByAppendingPathComponent:fileName] toPath:[documentsPath stringByAppendingPathComponent:fileName] error:nil];
}
TGLog(@"Moved %d document items to container", documentFileCount);
NSString *cachesPath = [[groupURL path] stringByAppendingPathComponent:@"Caches"];
[fileManager createDirectoryAtPath:cachesPath withIntermediateDirectories:true attributes:nil error:NULL];
NSString *defaultCachesPath = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, true)[0];
NSArray *cacheItems = [fileManager contentsOfDirectoryAtPath:defaultCachesPath error:nil];
int cacheFileCount = 0;
for (NSString *fileName in cacheItems)
{
cacheFileCount++;
[fileManager moveItemAtPath:[defaultCachesPath stringByAppendingPathComponent:fileName] toPath:[cachesPath stringByAppendingPathComponent:fileName] error:nil];
}
TGLog(@"Moved %d cache items to container", cacheFileCount);
}
}
}
+ (NSString *)documentsPath
{
static NSString *path = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
if (iosMajorVersion() >= 8)
{
NSString *groupName = [@"group." stringByAppendingString:[[NSBundle mainBundle] bundleIdentifier]];
NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupName];
if (groupURL != nil)
{
NSString *documentsPath = [[groupURL path] stringByAppendingPathComponent:@"Documents"];
[[NSFileManager defaultManager] createDirectoryAtPath:documentsPath withIntermediateDirectories:true attributes:nil error:NULL];
path = documentsPath;
}
else
path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true)[0];
}
else
path = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true)[0];
});
return path;
}
+ (NSString *)cachePath
{
static NSString *path = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^
{
if (iosMajorVersion() >= 8)
{
NSString *groupName = [@"group." stringByAppendingString:[[NSBundle mainBundle] bundleIdentifier]];
NSURL *groupURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:groupName];
if (groupURL != nil)
{
NSString *cachePath = [[groupURL path] stringByAppendingPathComponent:@"Caches"];
[[NSFileManager defaultManager] createDirectoryAtPath:cachePath withIntermediateDirectories:true attributes:nil error:NULL];
path = cachePath;
}
else
path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, true)[0];
}
else
path = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, true)[0];
});
return path;
}
- (void)inviteBotToGroup:(TGUser *)user payload:(NSString *)payload
{
TGForwardTargetController *controller = [[TGForwardTargetController alloc] initWithSelectGroup];
controller.watcherHandle = self.actionHandle;
TGNavigationController *navigationController = [TGNavigationController navigationControllerWithControllers:@[controller] navigationBarClass:[TGWhiteNavigationBar class]];
if ([_rootController.viewControllers.lastObject isKindOfClass:[TGModernConversationController class]]) {
[(TGModernConversationController *)_rootController.viewControllers.lastObject hideKeyboard];
}
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad)
{
[self resetControllerStack];
navigationController.presentationStyle = TGNavigationControllerPresentationStyleInFormSheet;
navigationController.modalPresentationStyle = UIModalPresentationFormSheet;
}
dispatch_async(dispatch_get_main_queue(), ^
{
[_rootController presentViewController:navigationController animated:true completion:nil];
});
_currentInviteBot = user;
_currentInviteBotPayload = payload;
}
- (void)setupShortcutItems
{
if (iosMajorVersion() < 9 || [UIDevice currentDevice].userInterfaceIdiom != UIUserInterfaceIdiomPhone)
return;
if (TGTelegraphInstance.clientUserId != 0 && TGTelegraphInstance.clientIsActivated)
{
UIApplicationShortcutItem *composeItem = [[UIApplicationShortcutItem alloc] initWithType:@"compose" localizedTitle:TGLocalized(@"Compose.NewMessage") localizedSubtitle:nil icon:[UIApplicationShortcutIcon iconWithType:UIApplicationShortcutIconTypeCompose] userInfo:nil];
NSArray *shortcuts = @[ composeItem ];
[UIApplication sharedApplication].shortcutItems = shortcuts;
if (_lastConversationsDisposable == nil)
{
_lastConversationsDisposable = [[SMetaDisposable alloc] init];
[_lastConversationsDisposable setDisposable:[[[[TGChatListSignals chatListWithLimit:8] map:^NSDictionary *(NSArray *chats)
{
NSMutableArray *filteredChats = [[NSMutableArray alloc] init];
NSMutableIndexSet *userIds = [[NSMutableIndexSet alloc] init];
for (TGConversation *conversation in chats)
{
if (!conversation.isChannel || conversation.isChannelGroup)
{
if (!conversation.isChat && !conversation.isChannelGroup)
[userIds addIndex:(int32_t)conversation.conversationId];
[filteredChats addObject:conversation];
}
}
NSMutableDictionary *users = [[NSMutableDictionary alloc] init];
[userIds enumerateIndexesUsingBlock:^(NSUInteger uid, __unused BOOL *stop)
{
TGUser *user = [[TGDatabase instance] loadUser:(int)uid];
if (user != nil)
users[@(uid)] = user;
}];
return @{ @"chats": filteredChats, @"users": users };
}] deliverOn:[SQueue mainQueue]] startWithNext:^(NSDictionary *next)
{
NSArray *chats = next[@"chats"];
NSDictionary *users = next[@"users"];
NSMutableArray *chatItems = [[NSMutableArray alloc] init];
for (TGConversation *chat in chats)
{
if (!chat.isChannel || chat.isChannelGroup)
[chatItems addObject:[TGAppDelegate shortcutItemForTGConversation:chat users:users]];
if (chatItems.count == 3)
break;
}
NSArray *finalShortcuts = [shortcuts arrayByAddingObjectsFromArray:chatItems];
[UIApplication sharedApplication].shortcutItems = finalShortcuts;
}]];
}
}
else
{
[UIApplication sharedApplication].shortcutItems = nil;
[_lastConversationsDisposable dispose];
_lastConversationsDisposable = nil;
}
}
+ (UIApplicationShortcutItem *)shortcutItemForTGConversation:(TGConversation *)conversation users:(NSDictionary *)users
{
NSString *title = @"";
CNMutableContact *contact = nil;
if (conversation.isChat || conversation.isChannelGroup)
{
title = [conversation chatTitle];
contact = [[CNMutableContact alloc] init];
contact.givenName = title;
}
else
{
TGUser *user = users[@(conversation.conversationId)];
title = user.displayName;
contact = [[CNMutableContact alloc] init];
contact.givenName = user.firstName;
contact.familyName = user.lastName;
}
UIApplicationShortcutIcon *icon = [UIApplicationShortcutIcon iconWithContact:contact];
UIApplicationShortcutItem *item = [[UIApplicationShortcutItem alloc] initWithType:@"conversation" localizedTitle:title localizedSubtitle:nil icon:icon userInfo:@{ @"cid": @(conversation.conversationId) }];
return item;
}
- (void)actionStageActionRequested:(NSString *)action options:(id)options
{
if ([action isEqualToString:@"willForwardMessages"])
{
int32_t uid = _currentInviteBot.uid;
NSString *payload = _currentInviteBotPayload;
_currentInviteBot = nil;
_currentInviteBotPayload = nil;
if (uid != 0 && payload.length != 0)
{
TGConversation *conversation = options[@"target"];
TGProgressWindow *progressWindow = [[TGProgressWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
[progressWindow show:true];
[[[[TGBotSignals botInviteUserId:uid toPeerId:conversation.conversationId accessHash:conversation.accessHash payload:payload] deliverOn:[SQueue mainQueue]] onDispose:^
{
[progressWindow dismiss:true];
}] startWithNext:^(__unused id next)
{
[[(UIViewController *)options[@"controller"] presentingViewController] dismissViewControllerAnimated:true completion:nil];
[[TGInterfaceManager instance] navigateToConversationWithId:conversation.conversationId conversation:nil];
} error:^(id error)
{
NSString *errorDescription = [[TGTelegramNetworking instance] extractNetworkErrorType:error];
NSString *alertText = TGLocalized(@"ConversationProfile.UnknownAddMemberError");
if ([errorDescription isEqualToString:@"USER_ALREADY_PARTICIPANT"])
alertText = TGLocalized(@"Target.InviteToGroupErrorAlreadyInvited");
[[[TGAlertView alloc] initWithTitle:nil message:alertText cancelButtonTitle:TGLocalized(@"Common.OK") okButtonTitle:nil completionBlock:nil] show];
} completed:nil];
}
}
}
@end
@interface UICollectionViewDisableForwardToUICollectionViewSentinel : NSObject @end @implementation UICollectionViewDisableForwardToUICollectionViewSentinel @end