[iOS] Fixed crash in FBSDK

https://github.com/facebook/facebook-ios-sdk/issues/1161
This commit is contained in:
Alexander Boriskov 2020-01-31 14:29:48 +03:00 committed by Daria Volvenkova
parent f0332a58ce
commit df9a051ff5
37 changed files with 2505 additions and 1076 deletions

View file

@ -11,8 +11,8 @@ target 'MAPS.ME' do
pod 'AppsFlyerFramework', '4.9.0'
pod 'Pushwoosh', '5.16.0'
pod 'ActionSheetPicker-3.0', '2.3.0'
pod 'FBSDKCoreKit', '5.15'
pod 'FBSDKLoginKit', '5.15'
pod 'FBSDKCoreKit', '5.15.1'
pod 'FBSDKLoginKit', '5.15.1'
pod 'Flurry-iOS-SDK/FlurrySDK', '10.1'
pod 'mopub-ios-sdk', '5.10'
pod 'MoPub-FacebookAudienceNetwork-Adapters', '5.6.0.0'

View file

@ -3,15 +3,15 @@ PODS:
- AppsFlyerFramework (4.9.0)
- FBAudienceNetwork (5.6.0):
- FBSDKCoreKit/Basics (>= 5.6.0)
- FBSDKCoreKit (5.15.0):
- FBSDKCoreKit/Basics (= 5.15.0)
- FBSDKCoreKit/Core (= 5.15.0)
- FBSDKCoreKit/Basics (5.15.0)
- FBSDKCoreKit/Core (5.15.0):
- FBSDKCoreKit (5.15.1):
- FBSDKCoreKit/Basics (= 5.15.1)
- FBSDKCoreKit/Core (= 5.15.1)
- FBSDKCoreKit/Basics (5.15.1)
- FBSDKCoreKit/Core (5.15.1):
- FBSDKCoreKit/Basics
- FBSDKLoginKit (5.15.0):
- FBSDKLoginKit/Login (= 5.15.0)
- FBSDKLoginKit/Login (5.15.0):
- FBSDKLoginKit (5.15.1):
- FBSDKLoginKit/Login (= 5.15.1)
- FBSDKLoginKit/Login (5.15.1):
- FBSDKCoreKit (~> 5.0)
- Flurry-iOS-SDK/FlurrySDK (10.1.0)
- MoPub-FacebookAudienceNetwork-Adapters (5.6.0.0):
@ -40,8 +40,8 @@ PODS:
DEPENDENCIES:
- ActionSheetPicker-3.0 (= 2.3.0)
- AppsFlyerFramework (= 4.9.0)
- FBSDKCoreKit (= 5.15)
- FBSDKLoginKit (= 5.15)
- FBSDKCoreKit (= 5.15.1)
- FBSDKLoginKit (= 5.15.1)
- Flurry-iOS-SDK/FlurrySDK (= 10.1)
- MoPub-FacebookAudienceNetwork-Adapters (= 5.6.0.0)
- mopub-ios-sdk (= 5.10)
@ -63,13 +63,13 @@ SPEC CHECKSUMS:
ActionSheetPicker-3.0: eef157d75e151f255c5333d26656c7fbfe905a51
AppsFlyerFramework: f57e5d590ad3124d3e594a76032a181bc91ec6cd
FBAudienceNetwork: 1ea63543665445a3a5b4a059e8210a343b6ab3c1
FBSDKCoreKit: 083ba8c8e37939c0b4c84250654c9be0aef0c2f8
FBSDKLoginKit: bd40354a96e4718765713ef225bc368f10dc395d
FBSDKCoreKit: 1d5acf7c9d7a2f92bb1a242dc60cae5b7adb91df
FBSDKLoginKit: f1ea8026a58b52d30c9f2e6a58ca7d813619fb83
Flurry-iOS-SDK: be6bfad47f3b3c15a38b5e396935ef74512f1c38
MoPub-FacebookAudienceNetwork-Adapters: 3cec249235d12e3fec9a01bf559d608fa3ec05e1
mopub-ios-sdk: 3d65133b95b6498aa871a66818a11a5ba307e565
Pushwoosh: 6cba171e52f3f7b8ccd280f492a5831deac2f594
PODFILE CHECKSUM: 733b86434976d4702689274a81b45cdafc689454
PODFILE CHECKSUM: 506217dc5f5cdefa3e64c5a0b65266edb8ce347e
COCOAPODS: 1.8.3

View file

@ -27,7 +27,7 @@
#define FBSDK_MAX_CRASH_LOGS 5
#define FBSDK_CRASH_PATH_NAME @"instrument"
#ifndef FBSDK_VERSION_STRING
#define FBSDK_VERSION_STRING @"5.15.0"
#define FBSDK_VERSION_STRING @"5.15.1"
#endif
static NSUncaughtExceptionHandler *previousExceptionHandler = NULL;
@ -194,7 +194,13 @@ static void FBSDKExceptionHandler(NSException *exception)
for (NSDictionary<NSString *, id> *crashLog in crashLogs) {
NSArray<NSString *> *callstack = crashLog[kFBSDKCallstack];
NSDictionary<NSString *, id> *methodMapping = [self loadLibData:crashLog];
NSData *data = [self loadLibData:crashLog];
if (!data) {
continue;
}
NSDictionary<NSString *, id> *methodMapping = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:nil];
NSArray<NSString *> *symbolicatedCallstack = [FBSDKLibAnalyzer symbolicateCallstack:callstack methodMapping:methodMapping];
NSMutableDictionary<NSString *, id> *symbolicatedCrashLog = [NSMutableDictionary dictionaryWithDictionary:crashLog];
if (symbolicatedCallstack) {
@ -215,7 +221,13 @@ static void FBSDKExceptionHandler(NSException *exception)
NSMutableArray<NSDictionary<NSString *, id> *> *crashLogArray = [NSMutableArray array];
for (NSUInteger i = 0; i < MIN(fileNames.count, FBSDK_MAX_CRASH_LOGS); i++) {
NSDictionary<NSString *, id> *crashLog = [self loadCrashLog:fileNames[i]];
NSData *data = [self loadCrashLog:fileNames[i]];
if (!data) {
continue;
}
NSDictionary<NSString *, id>* crashLog = [NSJSONSerialization JSONObjectWithData:data
options:kNilOptions
error:nil];
if (crashLog) {
[crashLogArray addObject:crashLog];
}
@ -223,9 +235,9 @@ static void FBSDKExceptionHandler(NSException *exception)
return [crashLogArray copy];
}
+ (nullable NSDictionary<NSString *,id> *)loadCrashLog:(NSString *)fileName
+ (nullable NSData *)loadCrashLog:(NSString *)fileName
{
return [NSDictionary dictionaryWithContentsOfFile:[directoryPath stringByAppendingPathComponent:fileName]];
return [NSData dataWithContentsOfFile:[directoryPath stringByAppendingPathComponent:fileName] options:NSDataReadingMappedIfSafe error:nil];
}
+ (void)clearCrashReportFiles
@ -272,8 +284,10 @@ static void FBSDKExceptionHandler(NSException *exception)
[completeCrashLog setObject:[UIDevice currentDevice].systemVersion forKey:kFBSDKDeviceOSVersion];
[completeCrashLog writeToFile:[self getPathToCrashFile:currentTimestamp]
atomically:YES];
NSData *data = [NSJSONSerialization dataWithJSONObject:completeCrashLog options:0 error:nil];
[data writeToFile:[self getPathToCrashFile:currentTimestamp]
atomically:YES];
}
+ (void)generateMethodMapping:(id<FBSDKCrashObserving>)observer
@ -285,15 +299,16 @@ static void FBSDKExceptionHandler(NSException *exception)
NSDictionary<NSString *, NSString *> *methodMapping = [FBSDKLibAnalyzer getMethodsTable:observer.prefixes
frameworks:observer.frameworks];
if (methodMapping.count > 0){
[methodMapping writeToFile:[self getPathToLibDataFile:mappingTableIdentifier]
NSData *data = [NSJSONSerialization dataWithJSONObject:methodMapping options:0 error:nil];
[data writeToFile:[self getPathToLibDataFile:mappingTableIdentifier]
atomically:YES];
}
}
+ (NSDictionary<NSString *, id> *)loadLibData:(NSDictionary<NSString *, id> *)crashLog
+ (nullable NSData *)loadLibData:(NSDictionary<NSString *, id> *)crashLog
{
NSString *identifier = [crashLog objectForKey:kFBSDKMappingTableIdentifier];
return [NSDictionary dictionaryWithContentsOfFile:[self getPathToLibDataFile:identifier]];
return [NSData dataWithContentsOfFile:[self getPathToLibDataFile:identifier] options:NSDataReadingMappedIfSafe error:nil];
}
+ (NSString *)getPathToCrashFile:(NSString *)timestamp

View file

@ -40,7 +40,9 @@ static NSMutableDictionary<NSString *, NSString *> *_methodMapping;
[self addClass:object_getClass(class) isClassMethod:YES];
}
}
return [_methodMapping copy];
@synchronized (_methodMapping) {
return [_methodMapping copy];
}
}
#pragma mark - private methods
@ -116,7 +118,9 @@ static NSMutableDictionary<NSString *, NSString *> *_methodMapping;
NSStringFromSelector(selector)];
if (methodAddress && methodName) {
[_methodMapping setObject:methodName forKey:methodAddress];
@synchronized (_methodMapping) {
[_methodMapping setObject:methodName forKey:methodAddress];
}
}
}
}

View file

@ -162,6 +162,11 @@ NS_REFINED_FOR_SWIFT;
*/
@property (nonatomic, copy, readonly) NSString *userID;
/**
The graph domain where this access token is valid.
*/
@property (nonatomic, copy, readonly) NSString *graphDomain;
/**
Returns whether the access token is expired by checking its expirationDate property
*/
@ -206,6 +211,38 @@ NS_REFINED_FOR_SWIFT;
dataAccessExpirationDate:(nullable NSDate *)dataAccessExpirationDate
NS_DESIGNATED_INITIALIZER;
/**
Convenience initializer.
@param tokenString the opaque token string.
@param permissions the granted permissions. Note this is converted to NSSet and is only
an NSArray for the convenience of literal syntax.
@param declinedPermissions the declined permissions. Note this is converted to NSSet and is only
an NSArray for the convenience of literal syntax.
@param expiredPermissions the expired permissions. Note this is converted to NSSet and is only
an NSArray for the convenience of literal syntax.
@param appID the app ID.
@param userID the user ID.
@param expirationDate the optional expiration date (defaults to distantFuture).
@param refreshDate the optional date the token was last refreshed (defaults to today).
@param dataAccessExpirationDate the date which data access will expire for the given user
(defaults to distantFuture).
@param graphDomain the domain this access token can be used in.
This initializer should only be used for advanced apps that
manage tokens explicitly. Typical login flows only need to use `FBSDKLoginManager`
along with `+currentAccessToken`.
*/
- (instancetype)initWithTokenString:(NSString *)tokenString
permissions:(NSArray<NSString *> *)permissions
declinedPermissions:(NSArray<NSString *> *)declinedPermissions
expiredPermissions:(NSArray<NSString *> *)expiredPermissions
appID:(NSString *)appID
userID:(NSString *)userID
expirationDate:(nullable NSDate *)expirationDate
refreshDate:(nullable NSDate *)refreshDate
dataAccessExpirationDate:(nullable NSDate *)dataAccessExpirationDate
graphDomain:(nullable NSString *)graphDomain;
/**
Convenience getter to determine if a permission has been granted
@param permission The permission to check.

View file

@ -49,7 +49,7 @@ static FBSDKAccessToken *g_currentAccessToken;
#define FBSDK_ACCESSTOKEN_REFRESHDATE_KEY @"refreshDate"
#define FBSDK_ACCESSTOKEN_EXPIRATIONDATE_KEY @"expirationDate"
#define FBSDK_ACCESSTOKEN_DATA_EXPIRATIONDATE_KEY @"dataAccessExpirationDate"
#define FBSDK_ACCESSTOKEN_GRAPH_DOMAIN_KEY @"graphDomain"
@implementation FBSDKAccessToken
@ -77,6 +77,36 @@ static FBSDKAccessToken *g_currentAccessToken;
return self;
}
- (instancetype)initWithTokenString:(NSString *)tokenString
permissions:(NSArray<NSString *> *)permissions
declinedPermissions:(NSArray<NSString *> *)declinedPermissions
expiredPermissions:(NSArray<NSString *> *)expiredPermissions
appID:(NSString *)appID
userID:(NSString *)userID
expirationDate:(NSDate *)expirationDate
refreshDate:(NSDate *)refreshDate
dataAccessExpirationDate:(NSDate *)dataAccessExpirationDate
graphDomain:(NSString *)graphDomain
{
FBSDKAccessToken *accessToken =
[self
initWithTokenString:tokenString
permissions:permissions
declinedPermissions:declinedPermissions
expiredPermissions:expiredPermissions
appID:appID
userID:userID
expirationDate:expirationDate
refreshDate:refreshDate
dataAccessExpirationDate:dataAccessExpirationDate];
if (accessToken != nil) {
accessToken->_graphDomain = [graphDomain copy];
}
return accessToken;
}
- (BOOL)hasGranted:(NSString *)permission
{
return [self.permissions containsObject:permission];
@ -156,7 +186,8 @@ static FBSDKAccessToken *g_currentAccessToken;
self.userID.hash,
self.refreshDate.hash,
self.expirationDate.hash,
self.dataAccessExpirationDate.hash
self.dataAccessExpirationDate.hash,
self.graphDomain.hash
};
return [FBSDKMath hashWithIntegerArray:subhashes count:sizeof(subhashes) / sizeof(subhashes[0])];
}
@ -183,7 +214,8 @@ static FBSDKAccessToken *g_currentAccessToken;
[FBSDKInternalUtility object:self.userID isEqualToObject:token.userID] &&
[FBSDKInternalUtility object:self.refreshDate isEqualToObject:token.refreshDate] &&
[FBSDKInternalUtility object:self.expirationDate isEqualToObject:token.expirationDate] &&
[FBSDKInternalUtility object:self.dataAccessExpirationDate isEqualToObject:token.dataAccessExpirationDate] );
[FBSDKInternalUtility object:self.dataAccessExpirationDate isEqualToObject:token.dataAccessExpirationDate] &&
[FBSDKInternalUtility object:self.graphDomain isEqualToObject:token.graphDomain]);
}
#pragma mark - NSCopying
@ -212,16 +244,20 @@ static FBSDKAccessToken *g_currentAccessToken;
NSDate *refreshDate = [decoder decodeObjectOfClass:[NSDate class] forKey:FBSDK_ACCESSTOKEN_REFRESHDATE_KEY];
NSDate *expirationDate = [decoder decodeObjectOfClass:[NSDate class] forKey:FBSDK_ACCESSTOKEN_EXPIRATIONDATE_KEY];
NSDate *dataAccessExpirationDate = [decoder decodeObjectOfClass:[NSDate class] forKey:FBSDK_ACCESSTOKEN_DATA_EXPIRATIONDATE_KEY];
NSString *graphDomain = [decoder decodeObjectOfClass:[NSString class] forKey:FBSDK_ACCESSTOKEN_GRAPH_DOMAIN_KEY];
return [self initWithTokenString:tokenString
permissions:permissions.allObjects
declinedPermissions:declinedPermissions.allObjects
expiredPermissions:expiredPermissions.allObjects
appID:appID
userID:userID
expirationDate:expirationDate
refreshDate:refreshDate
dataAccessExpirationDate:dataAccessExpirationDate];
return
[self
initWithTokenString:tokenString
permissions:permissions.allObjects
declinedPermissions:declinedPermissions.allObjects
expiredPermissions:expiredPermissions.allObjects
appID:appID
userID:userID
expirationDate:expirationDate
refreshDate:refreshDate
dataAccessExpirationDate:dataAccessExpirationDate
graphDomain:graphDomain];
}
- (void)encodeWithCoder:(NSCoder *)encoder
@ -235,6 +271,7 @@ static FBSDKAccessToken *g_currentAccessToken;
[encoder encodeObject:self.expirationDate forKey:FBSDK_ACCESSTOKEN_EXPIRATIONDATE_KEY];
[encoder encodeObject:self.refreshDate forKey:FBSDK_ACCESSTOKEN_REFRESHDATE_KEY];
[encoder encodeObject:self.dataAccessExpirationDate forKey:FBSDK_ACCESSTOKEN_DATA_EXPIRATIONDATE_KEY];
[encoder encodeObject:self.graphDomain forKey:FBSDK_ACCESSTOKEN_GRAPH_DOMAIN_KEY];
}
@end

View file

@ -323,6 +323,11 @@ typedef NS_ERROR_ENUM(FBSDKErrorDomain, FBSDKCoreError)
Indicates an app switch to the browser (typically for a dialog) failed.
*/
FBSDKErrorBrowserUnavailable,
/**
Indicates that a bridge api interaction was interrupted.
*/
FBSDKErrorBridgeAPIInterruption,
} NS_SWIFT_NAME(CoreError);
/**

View file

@ -92,5 +92,5 @@
#endif
#define FBSDK_VERSION_STRING @"5.15.0"
#define FBSDK_VERSION_STRING @"5.15.1"
#define FBSDK_TARGET_PLATFORM_VERSION @"v5.0"

View file

@ -0,0 +1,39 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import "FBSDKButton.h"
NS_ASSUME_NONNULL_BEGIN
/*
An internal base class for device related flows.
This is an internal API that should not be used directly and is subject to change.
*/
NS_SWIFT_NAME(FBDeviceButton)
@interface FBSDKDeviceButton : FBSDKButton
@end
NS_ASSUME_NONNULL_END
#endif

View file

@ -0,0 +1,147 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import "FBSDKDeviceButton.h"
#import <UIKit/UIKit.h>
#import "FBSDKCoreKit+Internal.h"
static const CGFloat kFBLogoSize = 54.0;
static const CGFloat kFBLogoLeftMargin = 36.0;
static const CGFloat kRightMargin = 12.0;
static const CGFloat kPreferredPaddingBetweenLogoTitle = 44.0;
@implementation FBSDKDeviceButton
#pragma mark - Layout
- (void)didUpdateFocusInContext:(UIFocusUpdateContext *)context withAnimationCoordinator:(UIFocusAnimationCoordinator *)coordinator
{
[super didUpdateFocusInContext:context withAnimationCoordinator:coordinator];
if (self == context.nextFocusedView) {
[coordinator addCoordinatedAnimations:^{
self.transform = CGAffineTransformMakeScale(1.05, 1.05);
self.layer.shadowOpacity = 0.5;
} completion:NULL];
} else if (self == context.previouslyFocusedView) {
[coordinator addCoordinatedAnimations:^{
self.transform = CGAffineTransformMakeScale(1.0, 1.0);
self.layer.shadowOpacity = 0;
} completion:NULL];
}
}
- (CGRect)imageRectForContentRect:(CGRect)contentRect
{
CGFloat centerY = CGRectGetMidY(contentRect);
CGFloat y = centerY - (kFBLogoSize / 2.0);
return CGRectMake(kFBLogoLeftMargin, y, kFBLogoSize, kFBLogoSize);
}
- (CGRect)titleRectForContentRect:(CGRect)contentRect
{
if (self.hidden || CGRectIsEmpty(self.bounds)) {
return CGRectZero;
}
CGRect imageRect = [self imageRectForContentRect:contentRect];
CGFloat titleX = CGRectGetMaxX(imageRect);
CGRect rect = CGRectMake(titleX, 0, CGRectGetWidth(contentRect) - titleX - kRightMargin, CGRectGetHeight(contentRect));
if (!self.layer.needsLayout) {
CGSize titleSize = [FBSDKMath ceilForSize:[self.titleLabel.attributedText boundingRectWithSize:contentRect.size
options:(NSStringDrawingUsesDeviceMetrics |
NSStringDrawingUsesLineFragmentOrigin |
NSStringDrawingUsesFontLeading)
context:NULL].size];
CGFloat titlePadding = ( CGRectGetWidth(rect) - titleSize.width ) / 2;
if (titlePadding > titleX) {
// if there's room to re-center the text, do so.
rect = CGRectMake(kRightMargin, 0, CGRectGetWidth(contentRect) - kRightMargin - kRightMargin, CGRectGetHeight(contentRect));
}
}
return rect;
}
#pragma mark - FBSDKButton
- (UIFont *)defaultFont
{
return [UIFont systemFontOfSize:38];
}
- (CGSize)sizeThatFits:(CGSize)size attributedTitle:(NSAttributedString *)title
{
CGSize titleSize = [FBSDKMath ceilForSize:[title boundingRectWithSize:size
options:(NSStringDrawingUsesDeviceMetrics |
NSStringDrawingUsesLineFragmentOrigin |
NSStringDrawingUsesFontLeading)
context:NULL].size];
CGFloat logoAndTitleWidth = kFBLogoSize + kPreferredPaddingBetweenLogoTitle + titleSize.width + kPreferredPaddingBetweenLogoTitle;
CGFloat height = 108;
CGSize contentSize = CGSizeMake(kFBLogoLeftMargin + logoAndTitleWidth + kRightMargin,
height);
return contentSize;
}
- (CGSize)sizeThatFits:(CGSize)size title:(NSString *)title
{
return [self sizeThatFits:size attributedTitle:[self attributedTitleStringFromString:title]];
}
#pragma mark - Subclasses
- (NSAttributedString *)attributedTitleStringFromString:(NSString *)string
{
if (!string) {
return nil;
}
NSMutableParagraphStyle *style = [[NSMutableParagraphStyle alloc] init];
style.alignment = NSTextAlignmentCenter;
style.lineBreakMode = NSLineBreakByClipping;
NSMutableAttributedString *attributedString =
[[NSMutableAttributedString alloc] initWithString:string
attributes:@{
NSParagraphStyleAttributeName: style,
NSFontAttributeName: [self defaultFont],
NSForegroundColorAttributeName: [UIColor whiteColor]
}];
// Now find all the spaces and widen their kerning.
NSRange range = NSMakeRange(0, string.length);
while (range.location != NSNotFound) {
NSRange spaceRange = [string rangeOfString:@" " options:0 range:range];
if (spaceRange.location == NSNotFound) {
break;
}
[attributedString addAttribute:NSKernAttributeName
value:@2.7
range:spaceRange];
range = NSMakeRange(spaceRange.location + 1, string.length - spaceRange.location - 1);
}
return attributedString;
}
@end
#endif

View file

@ -0,0 +1,38 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/*
An internal base class for device related flows.
This is an internal API that should not be used directly and is subject to change.
*/
NS_SWIFT_NAME(FBDeviceViewControllerBase)
@interface FBSDKDeviceViewControllerBase : UIViewController
@end
NS_ASSUME_NONNULL_END
#endif

View file

@ -0,0 +1,135 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import "FBSDKDeviceViewControllerBase+Internal.h"
#import "FBSDKCoreKit+Internal.h"
#import "FBSDKSmartDeviceDialogView.h"
#import "FBSDKModalFormPresentationController.h"
static const NSTimeInterval kAnimationDurationTimeInterval = .5;
/*
Subclasses should generally:
- override viewDidDisappear to handle cancellations
- assign `deviceDialogView.confirmationCode` to set the code
*/
@implementation FBSDKDeviceViewControllerBase
- (instancetype)init
{
if ((self = [super init])) {
self.transitioningDelegate = self;
self.modalPresentationStyle = UIModalPresentationCustom;
}
return self;
}
- (void)loadView
{
CGRect frame = [UIScreen mainScreen].bounds;
BOOL smartLoginEnabled = ([FBSDKServerConfigurationManager cachedServerConfiguration].smartLoginOptions & FBSDKServerConfigurationSmartLoginOptionsEnabled);
FBSDKDeviceDialogView *deviceView =
(smartLoginEnabled ?
[[FBSDKSmartDeviceDialogView alloc] initWithFrame:frame] :
[[FBSDKDeviceDialogView alloc] initWithFrame:frame] );
deviceView.delegate = self;
self.view = deviceView;
}
- (FBSDKDeviceDialogView *)deviceDialogView
{
return (FBSDKDeviceDialogView *)self.view;
}
#pragma mark - UIViewControllerAnimatedTransitioning
// Extract this out to another class if we have other similar transitions.
- (NSTimeInterval)transitionDuration:(id<UIViewControllerContextTransitioning>)transitionContext
{
return kAnimationDurationTimeInterval;
}
- (void)animateTransition:(id<UIViewControllerContextTransitioning>)transitionContext
{
if ([self isBeingPresented]) {
UIView *presentedView = [transitionContext viewForKey:UITransitionContextToViewKey];
// animate the view to slide in from bottom
presentedView.center = CGPointMake(presentedView.center.x, presentedView.center.y + CGRectGetHeight(presentedView.bounds));
UIView *containerView = [transitionContext containerView];
[containerView addSubview:presentedView];
[UIView animateWithDuration:kAnimationDurationTimeInterval
delay:0
usingSpringWithDamping:1
initialSpringVelocity:0
options:UIViewAnimationOptionCurveEaseOut
animations:^{
presentedView.center = CGPointMake(presentedView.center.x, presentedView.center.y - CGRectGetHeight(presentedView.bounds));
} completion:^(BOOL finished) {
[transitionContext completeTransition:finished];
}];
} else {
UIView *presentedView = [transitionContext viewForKey:UITransitionContextFromViewKey];
// animate the view to slide out to the bottom
[UIView animateWithDuration:kAnimationDurationTimeInterval
delay:0
usingSpringWithDamping:1
initialSpringVelocity:0
options:UIViewAnimationOptionCurveEaseIn
animations:^{
presentedView.center = CGPointMake(presentedView.center.x, presentedView.center.y + CGRectGetHeight(presentedView.bounds));
} completion:^(BOOL finished) {
[transitionContext completeTransition:finished];
}];
}
}
#pragma mark - UIViewControllerTransitioningDelegate
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForDismissedController:(UIViewController *)dismissed
{
return self;
}
- (id<UIViewControllerAnimatedTransitioning>)animationControllerForPresentedController:(UIViewController *)presented presentingController:(UIViewController *)presenting sourceController:(UIViewController *)source
{
return self;
}
- (UIPresentationController *)presentationControllerForPresentedViewController:(UIViewController *)presented
presentingViewController:(UIViewController *)presenting
sourceViewController:(UIViewController *)source
{
return [[FBSDKModalFormPresentationController alloc] initWithPresentedViewController:presented
presentingViewController:presenting];
}
#pragma mark - FBSDKDeviceDialogViewDelegate
- (void)deviceDialogViewDidCancel:(FBSDKDeviceDialogView *)deviceDialogView
{
[self dismissViewControllerAnimated:YES completion:NULL];
}
@end
#endif

View file

@ -216,7 +216,8 @@ static NSMutableDictionary<NSString *, FBSDKTestUsersManager *> *gInstancesDicti
userID:userId
expirationDate:nil
refreshDate:nil
dataAccessExpirationDate:nil];
dataAccessExpirationDate:nil
graphDomain:nil];
}
- (NSArray *)userIdAndTokenOfExistingAccountWithPermissions:(NSSet *)permissions skip:(NSSet *)setToSkip {

View file

@ -78,7 +78,8 @@ static FBSDKAccessToken *_CreateExpiredAccessToken(FBSDKAccessToken *accessToken
userID:accessToken.userID
expirationDate:expirationDate
refreshDate:expirationDate
dataAccessExpirationDate:expirationDate];
dataAccessExpirationDate:expirationDate
graphDomain:accessToken.graphDomain];
}
#endif

View file

@ -103,6 +103,10 @@ typedef void (^FBSDKAuthenticationCompletionHandler)(NSURL *_Nullable callbackUR
annotation:(id)annotation
{
id<FBSDKURLOpening> pendingURLOpen = _pendingURLOpen;
BOOL canOpenURL = [pendingURLOpen canOpenURL:url
forApplication:application
sourceApplication:sourceApplication
annotation:annotation];
void (^completePendingOpenURLBlock)(void) = ^{
self->_pendingURLOpen = nil;
@ -123,15 +127,28 @@ typedef void (^FBSDKAuthenticationCompletionHandler)(NSURL *_Nullable callbackUR
if (_authenticationSession != nil) {
[_authenticationSession cancel];
_authenticationSession = nil;
// This check is needed in case another sdk / message / ad etc... tries to open the app
// during the login flow.
// This dismisses the authentication browser without triggering any login callbacks.
// Hence we need to explicitly call the authentication session's completion handler.
if (!canOpenURL) {
NSString *errorMessage = [[NSString alloc]
initWithFormat:@"Login attempt cancelled by alternate call to openURL from: %@",
url];
NSError *loginError = [[NSError alloc]
initWithDomain:FBSDKErrorDomain
code:FBSDKErrorBridgeAPIInterruption
userInfo:@{FBSDKErrorLocalizedDescriptionKey: errorMessage}];
_authenticationSessionCompletionHandler(url, loginError);
_authenticationSessionCompletionHandler = nil;
}
}
}
completePendingOpenURLBlock();
}
if ([pendingURLOpen canOpenURL:url
forApplication:application
sourceApplication:sourceApplication
annotation:annotation]) {
if (canOpenURL) {
return YES;
}

View file

@ -0,0 +1,39 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import <Foundation/Foundation.h>
#if SWIFT_PACKAGE
#import "FBSDKDeviceButton.h"
#else
#import <FBSDKCoreKit/FBSDKDeviceButton.h>
#endif
@interface FBSDKDeviceButton ()
- (NSAttributedString *)attributedTitleStringFromString:(NSString *)string;
- (CGSize)sizeThatFits:(CGSize)size title:(NSString *)title;
- (CGSize)sizeThatFits:(CGSize)size attributedTitle:(NSAttributedString *)title;
@end
#endif

View file

@ -0,0 +1,47 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import <UIKit/UIKit.h>
NS_SWIFT_NAME(DeviceDialogViewDelegate)
@protocol FBSDKDeviceDialogViewDelegate;
// internal class, APIs are subject to change.
NS_SWIFT_NAME(FBDeviceDialogView)
@interface FBSDKDeviceDialogView : UIView
@property (nonatomic, weak) id<FBSDKDeviceDialogViewDelegate> delegate;
@property (nonatomic, copy) NSString *confirmationCode;
// override point for subclasses.
- (void)buildView;
@end
NS_SWIFT_NAME(DeviceDialogViewDelegate)
@protocol FBSDKDeviceDialogViewDelegate <NSObject>
- (void)deviceDialogViewDidCancel:(FBSDKDeviceDialogView *)deviceDialogView;
@end
#endif

View file

@ -0,0 +1,227 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import "FBSDKDeviceDialogView.h"
#import "FBSDKCoreKit+Internal.h"
#import "FBSDKDeviceUtilities.h"
@implementation FBSDKDeviceDialogView
{
UIActivityIndicatorView *_spinner;
UILabel *_confirmationCodeLabel;
UIImageView *_qrImageView;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
[self buildView];
[self setNeedsUpdateConstraints];
}
return self;
}
#pragma mark - Properties
- (void)setConfirmationCode:(NSString *)confirmationCode
{
if (![_confirmationCode isEqualToString:confirmationCode]) {
if (confirmationCode == nil) {
_confirmationCodeLabel.text = @"";
_confirmationCodeLabel.hidden = YES;
_qrImageView.hidden = YES;
[_spinner startAnimating];
} else {
[_spinner stopAnimating];
_confirmationCodeLabel.text = confirmationCode;
_confirmationCodeLabel.hidden = NO;
_qrImageView.hidden = NO;
[_qrImageView setImage:[FBSDKDeviceUtilities buildQRCodeWithAuthorizationCode:confirmationCode]];
}
}
}
#pragma mark - Helpers
- (void)buildView
{
// This is a "static" view with just a cancel button so add all the constraints here
// rather than properly override `updateConstraints`.
const CGFloat kWidth = 1080;
const CGFloat kHeight = 820;
const CGFloat kVerticalSpaceBetweenHeaderViewAndInstructionLabel = 102;
const CGFloat kVerticalSpaceBetweenCancelButtonAndButtomAnchor = 117;
const CGFloat kDialogHeaderViewHeight = 309;
const CGFloat kLogoSize = 44;
const CGFloat kLogoMargin = 30;
const CGFloat kInstructionTextHorizontalMargin = 151;
const CGFloat kConfirmationCodeFontSize = 108;
const CGFloat kFontColorValue = 119.0/255.0;
const CGFloat kInstructionFontSize = 36;
const CGFloat kQRCodeMargin = 50;
const CGFloat kQRCodeSize = 200;
// build the container view.
UIView *dialogView = [[UIView alloc] init];
dialogView.layer.cornerRadius = 3;
dialogView.translatesAutoresizingMaskIntoConstraints = NO;
dialogView.clipsToBounds = YES;
dialogView.backgroundColor = [UIColor whiteColor];
[self addSubview:dialogView];
[NSLayoutConstraint constraintWithItem:dialogView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES;;
[NSLayoutConstraint constraintWithItem:dialogView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES;
[dialogView.widthAnchor constraintEqualToConstant:kWidth].active = YES;
[dialogView.heightAnchor constraintEqualToConstant:kHeight].active = YES;
// build the header container view (which will contain the logo and code).
UIView *dialogHeaderView = [[UIView alloc] init];
dialogHeaderView.translatesAutoresizingMaskIntoConstraints = NO;
dialogHeaderView.backgroundColor = [UIColor colorWithRed:226.0/255.0 green:231.0/255.0 blue:235.0/255.0 alpha:0.85];
[dialogView addSubview:dialogHeaderView];
[dialogHeaderView.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor].active = YES;
[dialogHeaderView.trailingAnchor constraintEqualToAnchor:dialogView.trailingAnchor].active = YES;
[dialogHeaderView.heightAnchor constraintEqualToConstant:kDialogHeaderViewHeight].active = YES;
[dialogHeaderView.topAnchor constraintEqualToAnchor:dialogView.topAnchor].active = YES;
// build the logo.
CGSize imageSize = CGSizeMake(kLogoSize, kLogoSize);
FBSDKLogo *logoHelper =[[FBSDKLogo alloc] initWithColor:[UIColor colorWithRed:66.0/255.0 green:103.0/255.0 blue:178.0/255.0 alpha:1]];
UIImage *image = [logoHelper imageWithSize:imageSize];
image = [image resizableImageWithCapInsets:UIEdgeInsetsZero resizingMode:UIImageResizingModeStretch];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.translatesAutoresizingMaskIntoConstraints = NO;
[dialogHeaderView addSubview:imageView];
[imageView.widthAnchor constraintEqualToConstant:kLogoSize].active = YES;
[imageView.heightAnchor constraintEqualToConstant:kLogoSize].active = YES;
[imageView.topAnchor constraintEqualToAnchor:dialogHeaderView.topAnchor constant:kLogoMargin].active = YES;
[imageView.leadingAnchor constraintEqualToAnchor:dialogHeaderView.leadingAnchor constant:kLogoMargin].active = YES;
// build the activity spinner
_spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
_spinner.translatesAutoresizingMaskIntoConstraints = NO;
[dialogHeaderView addSubview:_spinner];
[NSLayoutConstraint constraintWithItem:_spinner attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:dialogHeaderView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES;
[NSLayoutConstraint constraintWithItem:_spinner attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:dialogHeaderView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES;
[_spinner.widthAnchor constraintEqualToConstant:kConfirmationCodeFontSize].active = YES;
[_spinner.heightAnchor constraintEqualToConstant:kConfirmationCodeFontSize].active = YES;
[_spinner startAnimating];
// build the confirmation code (which replaces the spinner when the code is available).
_confirmationCodeLabel = [[UILabel alloc] init];
_confirmationCodeLabel.translatesAutoresizingMaskIntoConstraints = NO;
_confirmationCodeLabel.textColor = logoHelper.color;
_confirmationCodeLabel.font = [UIFont systemFontOfSize:kConfirmationCodeFontSize weight:UIFontWeightLight];
_confirmationCodeLabel.textAlignment = NSTextAlignmentCenter;
[_confirmationCodeLabel sizeToFit];
[dialogHeaderView addSubview:_confirmationCodeLabel];
[NSLayoutConstraint constraintWithItem:_confirmationCodeLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:dialogHeaderView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES;
[NSLayoutConstraint constraintWithItem:_confirmationCodeLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:dialogHeaderView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES;
_confirmationCodeLabel.hidden = YES;
// Build the QR code view
_qrImageView = [[UIImageView alloc] initWithImage:[FBSDKDeviceUtilities buildQRCodeWithAuthorizationCode:NULL]];
_qrImageView.translatesAutoresizingMaskIntoConstraints = NO;
[dialogView addSubview:_qrImageView];
[_qrImageView.topAnchor constraintEqualToAnchor:dialogHeaderView.bottomAnchor
constant:kQRCodeMargin].active = YES;
[_qrImageView.bottomAnchor constraintEqualToAnchor:_qrImageView.topAnchor
constant:kQRCodeSize].active = YES;
[_qrImageView.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor
constant:kQRCodeMargin].active = YES;
[_qrImageView.trailingAnchor constraintEqualToAnchor:_qrImageView.leadingAnchor
constant:kQRCodeSize].active = YES;
// build the instructions UILabel
UILabel *instructionLabel = [[UILabel alloc] init];
instructionLabel.translatesAutoresizingMaskIntoConstraints = NO;
NSString *localizedFormatString = NSLocalizedStringWithDefaultValue(@"DeviceLogin.LogInPrompt",
@"FacebookSDK",
[FBSDKInternalUtility bundleForStrings],
@"Visit %@ and enter your code.",
@"The format string for device login instructions");
NSString *const deviceLoginURLString = @"facebook.com/device";
NSString *instructionString = [NSString localizedStringWithFormat:localizedFormatString, deviceLoginURLString];
NSMutableParagraphStyle *instructionLabelParagraphStyle = [[NSMutableParagraphStyle alloc] init];
instructionLabelParagraphStyle.lineHeightMultiple = 1.1;
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:instructionString
attributes:@{ NSParagraphStyleAttributeName : instructionLabelParagraphStyle }];
NSRange range = [instructionString rangeOfString:deviceLoginURLString];
[attributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:kInstructionFontSize weight:UIFontWeightMedium] range:range];
instructionLabel.font = [UIFont systemFontOfSize:kInstructionFontSize weight:UIFontWeightLight];
instructionLabel.attributedText = attributedString;
instructionLabel.numberOfLines = 0;
instructionLabel.textAlignment = NSTextAlignmentCenter;
[instructionLabel sizeToFit];
instructionLabel.textColor = [UIColor colorWithWhite:kFontColorValue alpha:1.0];
[dialogView addSubview:instructionLabel];
[instructionLabel.topAnchor constraintEqualToAnchor:dialogHeaderView.bottomAnchor
constant:kVerticalSpaceBetweenHeaderViewAndInstructionLabel].active = YES;
[instructionLabel.leadingAnchor constraintEqualToAnchor:_qrImageView.trailingAnchor
constant:kQRCodeMargin].active = YES;
[dialogView.trailingAnchor constraintEqualToAnchor:instructionLabel.trailingAnchor
constant:kInstructionTextHorizontalMargin].active = YES;
// build the container view for the cancel button.
UIView *buttonContainerView = [[UIView alloc] init];
buttonContainerView.translatesAutoresizingMaskIntoConstraints = NO;
[dialogView addSubview:buttonContainerView];
[NSLayoutConstraint constraintWithItem:buttonContainerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:dialogView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES;
[buttonContainerView.heightAnchor constraintEqualToConstant:100].active = YES;
[buttonContainerView.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor
constant:400].active = YES;
[dialogView.trailingAnchor constraintEqualToAnchor:buttonContainerView.trailingAnchor
constant:400].active = YES;
[dialogView.bottomAnchor constraintEqualToAnchor:buttonContainerView.bottomAnchor
constant:kVerticalSpaceBetweenCancelButtonAndButtomAnchor].active = YES;
// build the cancel button.
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
button.layer.cornerRadius = 4.0;
button.translatesAutoresizingMaskIntoConstraints = NO;
[button setTitle:NSLocalizedStringWithDefaultValue(@"LoginButton.CancelLogout",
@"FacebookSDK",
[FBSDKInternalUtility bundleForStrings],
@"Cancel",
@"The label for the FBSDKLoginButton action sheet to cancel logging out")
forState:UIControlStateNormal];
button.titleLabel.font = instructionLabel.font;
[buttonContainerView addSubview:button];
[button.leadingAnchor constraintEqualToAnchor:buttonContainerView.leadingAnchor].active = YES;
[button.trailingAnchor constraintEqualToAnchor:buttonContainerView.trailingAnchor].active = YES;
[button.topAnchor constraintEqualToAnchor:buttonContainerView.topAnchor].active = YES;
[button.bottomAnchor constraintEqualToAnchor:buttonContainerView.bottomAnchor].active = YES;
[button setTitleColor:[UIColor colorWithWhite:kFontColorValue alpha:1] forState:UIControlStateNormal];
[button addTarget:self action:@selector(_cancelButtonTap:) forControlEvents:UIControlEventPrimaryActionTriggered];
}
- (void)_cancelButtonTap:(id)sender
{
[self.delegate deviceDialogViewDidCancel:self];
}
@end
#endif

View file

@ -0,0 +1,35 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import <UIKit/UIKit.h>
NS_SWIFT_NAME(DeviceUtilities)
@interface FBSDKDeviceUtilities : NSObject
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;
+ (UIImage *)buildQRCodeWithAuthorizationCode:(NSString *)authorizationCode;
@end
#endif

View file

@ -0,0 +1,52 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import "FBSDKDeviceUtilities.h"
@implementation FBSDKDeviceUtilities
+ (UIImage *)buildQRCodeWithAuthorizationCode:(NSString *)authorizationCode
{
NSString *authorizationUri = @"https://facebook.com/device";
if ([authorizationUri length] > 0) {
authorizationUri = [NSString stringWithFormat:@"https://facebook.com/device?user_code=%@&qr=1", authorizationCode];
}
NSData *qrCodeData = [authorizationUri dataUsingEncoding:NSISOLatin1StringEncoding];
CIFilter *qrCodeFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[qrCodeFilter setValue:qrCodeData forKey:@"inputMessage"];
[qrCodeFilter setValue:@"M" forKey:@"inputCorrectionLevel"];
CIImage *qrCodeImage = qrCodeFilter.outputImage;
CGRect qrImageSize = CGRectIntegral(qrCodeImage.extent);
CGSize qrOutputSize = CGSizeMake(200, 200);
CIImage *resizedImage =
[qrCodeImage imageByApplyingTransform: CGAffineTransformMakeScale(qrOutputSize.width / CGRectGetWidth(qrImageSize),
qrOutputSize.height / CGRectGetHeight(qrImageSize))];
return [UIImage imageWithCIImage:resizedImage];
}
@end
#endif

View file

@ -0,0 +1,53 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#if SWIFT_PACKAGE
#import "FBSDKDeviceViewControllerBase.h"
#else
#import <FBSDKCoreKit/FBSDKDeviceViewControllerBase.h>
#endif
#import "FBSDKCoreKit+Internal.h"
#import "FBSDKDeviceDialogView.h"
@class FBSDKDeviceDialogView;
NS_ASSUME_NONNULL_BEGIN
/*
An internal base class for device related flows.
This is an internal API that should not be used directly and is subject to change.
*/
@interface FBSDKDeviceViewControllerBase()<
UIViewControllerAnimatedTransitioning,
UIViewControllerTransitioningDelegate,
FBSDKDeviceDialogViewDelegate
>
@property (nonatomic, strong, readonly) FBSDKDeviceDialogView *deviceDialogView;
@end
NS_ASSUME_NONNULL_END
#endif

View file

@ -0,0 +1,37 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
// Custom UIPresentationController that is similar to
// UIModalPresentationFormSheet style (which is not available
// on tvOS).
NS_SWIFT_NAME(FBModalFormPresentationController)
@interface FBSDKModalFormPresentationController : UIPresentationController
@end
NS_ASSUME_NONNULL_END
#endif

View file

@ -0,0 +1,83 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import "FBSDKModalFormPresentationController.h"
@implementation FBSDKModalFormPresentationController {
UIView *_dimmedView;
}
- (UIView *)dimmedView
{
if (!_dimmedView) {
_dimmedView = [[UIView alloc] initWithFrame:self.containerView.bounds];
_dimmedView.backgroundColor = [UIColor colorWithWhite:0 alpha:.6];
}
return _dimmedView;
}
#pragma mark - UIPresentationController overrides
- (void)presentationTransitionWillBegin
{
[self.containerView addSubview:[self dimmedView]];
[self.containerView addSubview:[self presentedView]];
[self.presentingViewController.transitionCoordinator
animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
[self dimmedView].alpha = 1.0;
} completion:NULL];
}
- (void)presentationTransitionDidEnd:(BOOL)completed
{
if (!completed) {
[[self dimmedView] removeFromSuperview];
}
}
- (void)dismissalTransitionWillBegin
{
[self.presentingViewController.transitionCoordinator
animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
[self dimmedView].alpha = 0;
} completion:NULL];
}
- (void)dismissalTransitionDidEnd:(BOOL)completed
{
if (completed) {
[[self dimmedView] removeFromSuperview];
}
}
// technically not necessary for tvOS yet since there's no resizing.
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
[coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> _Nonnull context) {
[self dimmedView].frame = self.containerView.bounds;
} completion:NULL];
}
@end
#endif

View file

@ -0,0 +1,32 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import <UIKit/UIKit.h>
#import "FBSDKDeviceDialogView.h"
NS_SWIFT_NAME(FBSmartDeviceDialogView)
@interface FBSDKSmartDeviceDialogView : FBSDKDeviceDialogView
@end
#endif

View file

@ -0,0 +1,276 @@
// Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
//
// You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
// copy, modify, and distribute this software in source code or binary form for use
// in connection with the web services and APIs provided by Facebook.
//
// As with any software that integrates with the Facebook platform, your use of
// this software is subject to the Facebook Developer Principles and Policies
// [http://developers.facebook.com/policy/]. This copyright notice shall be
// included in all copies or substantial portions of the software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "TargetConditionals.h"
#if TARGET_OS_TV
#import "FBSDKSmartDeviceDialogView.h"
#import "FBSDKCoreKit+Internal.h"
#import "FBSDKDeviceUtilities.h"
@implementation FBSDKSmartDeviceDialogView
{
UIActivityIndicatorView *_spinner;
UILabel *_confirmationCodeLabel;
UIImageView *_qrImageView;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
[self _buildView];
}
return self;
}
#pragma mark - Overrides
- (void)setConfirmationCode:(NSString *)confirmationCode
{
if (![self.confirmationCode isEqualToString:confirmationCode]) {
if (confirmationCode == nil) {
_confirmationCodeLabel.text = @"";
_confirmationCodeLabel.hidden = YES;
_qrImageView.hidden = YES;
[_spinner startAnimating];
} else {
[_spinner stopAnimating];
_confirmationCodeLabel.text = confirmationCode;
_confirmationCodeLabel.hidden = NO;
_qrImageView.hidden = NO;
[_qrImageView setImage:[FBSDKDeviceUtilities buildQRCodeWithAuthorizationCode:confirmationCode]];
}
}
}
- (void)buildView
{
//intentionally blank.
}
#pragma mark - Helpers
- (void)_buildView
{
// This is a "static" view with just a cancel button so add all the constraints here
// rather than properly override `updateConstraints`.
const CGFloat kWidth = 1080;
const CGFloat kVerticalSpaceBetweenHeaderViewAndInstructionLabel = 50;
const CGFloat kDialogHeaderViewHeight = 250;
const CGFloat kLogoSize = 44;
const CGFloat kLogoMargin = 30;
const CGFloat kInstructionTextHorizontalMargin = 100;
const CGFloat kConfirmationCodeFontSize = 108;
const CGFloat kFontColorValue = 119.0/255.0;
const CGFloat kInstructionFontSize = 32;
const CGFloat kVerticalMarginOrLabel = 40;
const CGFloat kQRCodeSize = 200;
const CGFloat kQRCodeMargin = (kWidth - kQRCodeSize) / 2;
// build the container view.
UIView *dialogView = [[UIView alloc] init];
dialogView.layer.cornerRadius = 3;
dialogView.translatesAutoresizingMaskIntoConstraints = NO;
dialogView.clipsToBounds = YES;
dialogView.backgroundColor = [UIColor whiteColor];
[self addSubview:dialogView];
[NSLayoutConstraint constraintWithItem:dialogView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES;;
[NSLayoutConstraint constraintWithItem:dialogView attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES;
[dialogView.widthAnchor constraintEqualToConstant:kWidth].active = YES;
// build the header container view (which will contain the logo and code).
UIView *dialogHeaderView = [[UIView alloc] init];
dialogHeaderView.translatesAutoresizingMaskIntoConstraints = NO;
dialogHeaderView.backgroundColor = [UIColor colorWithRed:226.0/255.0 green:231.0/255.0 blue:235.0/255.0 alpha:0.85];
[dialogView addSubview:dialogHeaderView];
[dialogHeaderView.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor].active = YES;
[dialogHeaderView.trailingAnchor constraintEqualToAnchor:dialogView.trailingAnchor].active = YES;
[dialogHeaderView.heightAnchor constraintEqualToConstant:kDialogHeaderViewHeight].active = YES;
[dialogHeaderView.topAnchor constraintEqualToAnchor:dialogView.topAnchor].active = YES;
// build the logo.
CGSize imageSize = CGSizeMake(kLogoSize, kLogoSize);
FBSDKLogo *logoHelper =[[FBSDKLogo alloc] initWithColor:[UIColor colorWithRed:66.0/255.0 green:103.0/255.0 blue:178.0/255.0 alpha:1]];
UIImage *image = [logoHelper imageWithSize:imageSize];
image = [image resizableImageWithCapInsets:UIEdgeInsetsZero resizingMode:UIImageResizingModeStretch];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.translatesAutoresizingMaskIntoConstraints = NO;
[dialogHeaderView addSubview:imageView];
[imageView.widthAnchor constraintEqualToConstant:kLogoSize].active = YES;
[imageView.heightAnchor constraintEqualToConstant:kLogoSize].active = YES;
[imageView.topAnchor constraintEqualToAnchor:dialogHeaderView.topAnchor constant:kLogoMargin].active = YES;
[imageView.leadingAnchor constraintEqualToAnchor:dialogHeaderView.leadingAnchor constant:kLogoMargin].active = YES;
// build the activity spinner
_spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge];
_spinner.translatesAutoresizingMaskIntoConstraints = NO;
[dialogHeaderView addSubview:_spinner];
[NSLayoutConstraint constraintWithItem:_spinner attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:dialogHeaderView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES;
[NSLayoutConstraint constraintWithItem:_spinner attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:dialogHeaderView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES;
[_spinner.widthAnchor constraintEqualToConstant:kConfirmationCodeFontSize].active = YES;
[_spinner.heightAnchor constraintEqualToConstant:kConfirmationCodeFontSize].active = YES;
[_spinner startAnimating];
// build the confirmation code (which replaces the spinner when the code is available).
_confirmationCodeLabel = [[UILabel alloc] init];
_confirmationCodeLabel.translatesAutoresizingMaskIntoConstraints = NO;
_confirmationCodeLabel.textColor = logoHelper.color;
_confirmationCodeLabel.font = [UIFont systemFontOfSize:kConfirmationCodeFontSize weight:UIFontWeightLight];
_confirmationCodeLabel.textAlignment = NSTextAlignmentCenter;
[_confirmationCodeLabel sizeToFit];
[dialogHeaderView addSubview:_confirmationCodeLabel];
[NSLayoutConstraint constraintWithItem:_confirmationCodeLabel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:dialogHeaderView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES;
[NSLayoutConstraint constraintWithItem:_confirmationCodeLabel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:dialogHeaderView attribute:NSLayoutAttributeCenterY multiplier:1.0 constant:0].active = YES;
_confirmationCodeLabel.hidden = YES;
// build the smartlogin instructions
UILabel *smartInstructionLabel = [[UILabel alloc] init];
smartInstructionLabel.translatesAutoresizingMaskIntoConstraints = NO;
NSString *smartInstructionString = NSLocalizedStringWithDefaultValue(@"DeviceLogin.SmartLogInPrompt",
@"FacebookSDK",
[FBSDKInternalUtility bundleForStrings],
@"To connect your account, open the Facebook app on your mobile device and check for notifications.",
@"Instructions telling the user to open their Facebook app on a mobile device and check for a login notification.");
NSMutableParagraphStyle *instructionLabelParagraphStyle = [[NSMutableParagraphStyle alloc] init];
instructionLabelParagraphStyle.lineHeightMultiple = 1.3;
NSMutableAttributedString *attributedSmartString = [[NSMutableAttributedString alloc] initWithString:smartInstructionString
attributes:@{ NSParagraphStyleAttributeName : instructionLabelParagraphStyle }];
UIFont *instructionFont = [UIFont systemFontOfSize:kInstructionFontSize weight:UIFontWeightLight];
smartInstructionLabel.font = instructionFont;
smartInstructionLabel.attributedText = attributedSmartString;
smartInstructionLabel.numberOfLines = 0;
smartInstructionLabel.textAlignment = NSTextAlignmentCenter;
[smartInstructionLabel sizeToFit];
smartInstructionLabel.textColor = [UIColor colorWithWhite:kFontColorValue alpha:1.0];
[dialogView addSubview:smartInstructionLabel];
[smartInstructionLabel.topAnchor constraintEqualToAnchor:dialogHeaderView.bottomAnchor
constant:kVerticalSpaceBetweenHeaderViewAndInstructionLabel].active = YES;
[smartInstructionLabel.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor constant:kInstructionTextHorizontalMargin].active = YES;
[dialogView.trailingAnchor constraintEqualToAnchor:smartInstructionLabel.trailingAnchor constant:kInstructionTextHorizontalMargin].active = YES;
// build 'or' label
UILabel *orInstructionLabel = [[UILabel alloc] init];
orInstructionLabel.translatesAutoresizingMaskIntoConstraints = NO;
orInstructionLabel.font = [UIFont systemFontOfSize:kInstructionFontSize weight:UIFontWeightBold];
orInstructionLabel.text = NSLocalizedStringWithDefaultValue(@"DeviceLogin.SmartLogInOrLabel",
@"FacebookSDK",
[FBSDKInternalUtility bundleForStrings],
@"-- OR --",
@"The 'or' string for smart login instructions");;
orInstructionLabel.numberOfLines = 0;
orInstructionLabel.textAlignment = NSTextAlignmentCenter;
[orInstructionLabel sizeToFit];
orInstructionLabel.textColor = [UIColor colorWithWhite:kFontColorValue alpha:1.0];
[dialogView addSubview:orInstructionLabel];
[orInstructionLabel.topAnchor constraintEqualToAnchor:smartInstructionLabel.bottomAnchor constant:kVerticalMarginOrLabel].active = YES;
[orInstructionLabel.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor constant:kInstructionTextHorizontalMargin].active = YES;
[dialogView.trailingAnchor constraintEqualToAnchor:orInstructionLabel.trailingAnchor constant:kInstructionTextHorizontalMargin].active = YES;
// Build the QR code view
_qrImageView = [[UIImageView alloc] initWithImage:[FBSDKDeviceUtilities buildQRCodeWithAuthorizationCode:NULL]];
_qrImageView.translatesAutoresizingMaskIntoConstraints = NO;
[dialogView addSubview:_qrImageView];
[_qrImageView.topAnchor constraintEqualToAnchor:orInstructionLabel.bottomAnchor
constant:kVerticalMarginOrLabel].active = YES;
[_qrImageView.bottomAnchor constraintEqualToAnchor:_qrImageView.topAnchor
constant:kQRCodeSize].active = YES;
[_qrImageView.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor
constant:kQRCodeMargin].active = YES;
[dialogView.trailingAnchor constraintEqualToAnchor:_qrImageView.trailingAnchor
constant:kQRCodeMargin].active = YES;
// build the instructions UILabel
UILabel *instructionLabel = [[UILabel alloc] init];
instructionLabel.translatesAutoresizingMaskIntoConstraints = NO;
NSString *localizedFormatString = NSLocalizedStringWithDefaultValue(@"DeviceLogin.LogInPrompt",
@"FacebookSDK",
[FBSDKInternalUtility bundleForStrings],
@"Visit %@ and enter the code shown above.",
@"The format string for device login instructions");
NSString *const deviceLoginURLString = @"facebook.com/device";
NSString *instructionString = [NSString localizedStringWithFormat:localizedFormatString, deviceLoginURLString];
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString:instructionString
attributes:@{ NSParagraphStyleAttributeName : instructionLabelParagraphStyle }];
NSRange range = [instructionString rangeOfString:deviceLoginURLString];
[attributedString addAttribute:NSFontAttributeName value:[UIFont systemFontOfSize:kInstructionFontSize weight:UIFontWeightMedium] range:range];
instructionLabel.font = instructionFont;
instructionLabel.attributedText = attributedString;
instructionLabel.numberOfLines = 0;
instructionLabel.textAlignment = NSTextAlignmentCenter;
[instructionLabel sizeToFit];
instructionLabel.textColor = [UIColor colorWithWhite:kFontColorValue alpha:1.0];
[dialogView addSubview:instructionLabel];
[instructionLabel.topAnchor constraintEqualToAnchor:_qrImageView.bottomAnchor
constant:kVerticalMarginOrLabel].active = YES;
[instructionLabel.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor
constant:kInstructionTextHorizontalMargin].active = YES;
[dialogView.trailingAnchor constraintEqualToAnchor:instructionLabel.trailingAnchor
constant:kInstructionTextHorizontalMargin].active = YES;
// build the container view for the cancel button.
UIView *buttonContainerView = [[UIView alloc] init];
buttonContainerView.translatesAutoresizingMaskIntoConstraints = NO;
[dialogView addSubview:buttonContainerView];
[NSLayoutConstraint constraintWithItem:buttonContainerView attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:dialogView attribute:NSLayoutAttributeCenterX multiplier:1.0 constant:0].active = YES;
[buttonContainerView.heightAnchor constraintEqualToConstant:60].active = YES;
[buttonContainerView.leadingAnchor constraintEqualToAnchor:dialogView.leadingAnchor
constant:400].active = YES;
[dialogView.trailingAnchor constraintEqualToAnchor:buttonContainerView.trailingAnchor
constant:400].active = YES;
[buttonContainerView.topAnchor constraintEqualToAnchor:instructionLabel.bottomAnchor
constant:kVerticalMarginOrLabel].active = YES;
[dialogView.bottomAnchor constraintEqualToAnchor:buttonContainerView.bottomAnchor
constant:kVerticalMarginOrLabel].active = YES;
// build the cancel button.
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
button.layer.cornerRadius = 4.0;
button.translatesAutoresizingMaskIntoConstraints = NO;
[button setTitle:NSLocalizedStringWithDefaultValue(@"LoginButton.CancelLogout",
@"FacebookSDK",
[FBSDKInternalUtility bundleForStrings],
@"Cancel",
@"The label for the FBSDKLoginButton action sheet to cancel logging out")
forState:UIControlStateNormal];
button.titleLabel.font = instructionLabel.font;
[buttonContainerView addSubview:button];
[button.leadingAnchor constraintEqualToAnchor:buttonContainerView.leadingAnchor].active = YES;
[button.trailingAnchor constraintEqualToAnchor:buttonContainerView.trailingAnchor].active = YES;
[button.topAnchor constraintEqualToAnchor:buttonContainerView.topAnchor].active = YES;
[button.bottomAnchor constraintEqualToAnchor:buttonContainerView.bottomAnchor].active = YES;
[button setTitleColor:[UIColor colorWithWhite:kFontColorValue alpha:1] forState:UIControlStateNormal];
[button addTarget:self action:@selector(_cancelButtonTap:) forControlEvents:UIControlEventPrimaryActionTriggered];
}
- (void)_cancelButtonTap:(id)sender
{
[self.delegate deviceDialogViewDidCancel:self];
}
@end
#endif

View file

@ -143,7 +143,11 @@ typedef NS_ENUM(NSUInteger, FBSDKInternalUtilityVersionShift)
hostPrefix = [hostPrefix stringByAppendingString:@"."];
}
NSString *host = @"facebook.com";
NSString *host =
[[FBSDKAccessToken currentAccessToken].graphDomain isEqualToString:@"gaming"]
? @"fb.gg"
: @"facebook.com";
NSString *domainPart = [FBSDKSettings facebookDomainPart];
if (domainPart.length) {
host = [[NSString alloc] initWithFormat:@"%@.%@", domainPart, host];

View file

@ -76,7 +76,8 @@ static int const FBSDKTokenRefreshRetrySeconds = 60 * 60; // hour
userID:currentToken.userID
expirationDate:expirationDate
refreshDate:[NSDate date]
dataAccessExpirationDate:dataExpirationDate];
dataAccessExpirationDate:dataExpirationDate
graphDomain:currentToken.graphDomain];
if (expectedToken == currentToken) {
[FBSDKAccessToken setCurrentAccessToken:refreshedToken];
}

View file

@ -162,7 +162,8 @@ static NSMutableArray<FBSDKDeviceLoginManager *> *g_loginManagerInstances;
userID:userID
expirationDate:nil
refreshDate:nil
dataAccessExpirationDate:nil];
dataAccessExpirationDate:nil
graphDomain:nil];
FBSDKDeviceLoginManagerResult *result = [[FBSDKDeviceLoginManagerResult alloc] initWithToken:accessToken
isCancelled:NO];
completeWithResult(result);

View file

@ -230,7 +230,8 @@ typedef NS_ENUM(NSInteger, FBSDKLoginManagerState) {
userID:parameters.userID
expirationDate:parameters.expirationDate
refreshDate:[NSDate date]
dataAccessExpirationDate:parameters.dataAccessExpirationDate];
dataAccessExpirationDate:parameters.dataAccessExpirationDate
graphDomain:parameters.graphDomain];
result = [[FBSDKLoginManagerLoginResult alloc] initWithToken:token
isCancelled:NO
grantedPermissions:recentlyGrantedPermissions
@ -326,7 +327,7 @@ typedef NS_ENUM(NSInteger, FBSDKLoginManagerState) {
NSMutableDictionary *loginParams = [NSMutableDictionary dictionary];
loginParams[@"client_id"] = [FBSDKSettings appID];
loginParams[@"response_type"] = @"token_or_nonce,signed_request";
loginParams[@"response_type"] = @"token_or_nonce,signed_request,graph_domain";
loginParams[@"redirect_uri"] = @"fbconnect://success";
loginParams[@"display"] = @"touch";
loginParams[@"sdk"] = @"ios";

View file

@ -41,6 +41,8 @@
@property (nonatomic, copy) NSString *challenge;
@property (nonatomic, copy) NSString *graphDomain;
@end
#endif

View file

@ -58,6 +58,8 @@ NS_SWIFT_NAME(LoginCompletionParameters)
@property (nonatomic, copy, readonly) NSDate *dataAccessExpirationDate;
@property (nonatomic, copy, readonly) NSString *challenge;
@property (nonatomic, copy, readonly) NSString *graphDomain;
@end
NS_SWIFT_NAME(LoginCompleting)

View file

@ -198,6 +198,9 @@ static void FBSDKLoginRequestMeAndPermissions(FBSDKLoginCompletionParameters *pa
NSError *error = nil;
NSDictionary<id, id> *state = [FBSDKBasicUtility objectForJSONString:parameters[@"state"] error:&error];
_parameters.challenge = [FBSDKUtility URLDecode:state[@"challenge"]];
NSString *domain = parameters[@"graph_domain"];
_parameters.graphDomain = [domain copy];
}
- (void)setErrorWithDictionary:(NSDictionary *)parameters

View file

@ -3,15 +3,15 @@ PODS:
- AppsFlyerFramework (4.9.0)
- FBAudienceNetwork (5.6.0):
- FBSDKCoreKit/Basics (>= 5.6.0)
- FBSDKCoreKit (5.15.0):
- FBSDKCoreKit/Basics (= 5.15.0)
- FBSDKCoreKit/Core (= 5.15.0)
- FBSDKCoreKit/Basics (5.15.0)
- FBSDKCoreKit/Core (5.15.0):
- FBSDKCoreKit (5.15.1):
- FBSDKCoreKit/Basics (= 5.15.1)
- FBSDKCoreKit/Core (= 5.15.1)
- FBSDKCoreKit/Basics (5.15.1)
- FBSDKCoreKit/Core (5.15.1):
- FBSDKCoreKit/Basics
- FBSDKLoginKit (5.15.0):
- FBSDKLoginKit/Login (= 5.15.0)
- FBSDKLoginKit/Login (5.15.0):
- FBSDKLoginKit (5.15.1):
- FBSDKLoginKit/Login (= 5.15.1)
- FBSDKLoginKit/Login (5.15.1):
- FBSDKCoreKit (~> 5.0)
- Flurry-iOS-SDK/FlurrySDK (10.1.0)
- MoPub-FacebookAudienceNetwork-Adapters (5.6.0.0):
@ -40,8 +40,8 @@ PODS:
DEPENDENCIES:
- ActionSheetPicker-3.0 (= 2.3.0)
- AppsFlyerFramework (= 4.9.0)
- FBSDKCoreKit (= 5.15)
- FBSDKLoginKit (= 5.15)
- FBSDKCoreKit (= 5.15.1)
- FBSDKLoginKit (= 5.15.1)
- Flurry-iOS-SDK/FlurrySDK (= 10.1)
- MoPub-FacebookAudienceNetwork-Adapters (= 5.6.0.0)
- mopub-ios-sdk (= 5.10)
@ -63,13 +63,13 @@ SPEC CHECKSUMS:
ActionSheetPicker-3.0: eef157d75e151f255c5333d26656c7fbfe905a51
AppsFlyerFramework: f57e5d590ad3124d3e594a76032a181bc91ec6cd
FBAudienceNetwork: 1ea63543665445a3a5b4a059e8210a343b6ab3c1
FBSDKCoreKit: 083ba8c8e37939c0b4c84250654c9be0aef0c2f8
FBSDKLoginKit: bd40354a96e4718765713ef225bc368f10dc395d
FBSDKCoreKit: 1d5acf7c9d7a2f92bb1a242dc60cae5b7adb91df
FBSDKLoginKit: f1ea8026a58b52d30c9f2e6a58ca7d813619fb83
Flurry-iOS-SDK: be6bfad47f3b3c15a38b5e396935ef74512f1c38
MoPub-FacebookAudienceNetwork-Adapters: 3cec249235d12e3fec9a01bf559d608fa3ec05e1
mopub-ios-sdk: 3d65133b95b6498aa871a66818a11a5ba307e565
Pushwoosh: 6cba171e52f3f7b8ccd280f492a5831deac2f594
PODFILE CHECKSUM: 733b86434976d4702689274a81b45cdafc689454
PODFILE CHECKSUM: 506217dc5f5cdefa3e64c5a0b65266edb8ce347e
COCOAPODS: 1.8.3

File diff suppressed because it is too large Load diff

View file

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>5.15.0</string>
<string>5.15.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View file

@ -16,6 +16,8 @@
#import "FBSDKConstants.h"
#import "FBSDKCopying.h"
#import "FBSDKCoreKit.h"
#import "FBSDKDeviceButton.h"
#import "FBSDKDeviceViewControllerBase.h"
#import "FBSDKMeasurementEvent.h"
#import "FBSDKMutableCopying.h"
#import "FBSDKProfile.h"

View file

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>5.15.0</string>
<string>5.15.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>