//
// RNDeviceInfo.m
// Learnium
//
// Created by Rebecca Hughes on 03/08/2015.
// Copyright © 2015 Learnium Limited. All rights reserved.
//
#import "RNDeviceInfo.h"
#import "DeviceUID.h"
#if !(TARGET_OS_TV)
#import <LocalAuthentication/LocalAuthentication.h>
#endif
@interface RNDeviceInfo()
@property (nonatomic) bool isEmulator;
@end
#if !(TARGET_OS_TV)
@import CoreTelephony;
#endif
@implementation RNDeviceInfo
@synthesize isEmulator;
RCT_EXPORT_MODULE(RNDeviceInfo)
+ (BOOL)requiresMainQueueSetup
{
return YES;
}
- (NSString*) deviceId
{
struct utsname systemInfo;
uname(&systemInfo);
NSString* deviceId = [NSString stringWithCString:systemInfo.machine
encoding:NSUTF8StringEncoding];
if ([deviceId isEqualToString:@"i386"] || [deviceId isEqualToString:@"x86_64"] ) {
deviceId = [NSString stringWithFormat:@"%s", getenv("SIMULATOR_MODEL_IDENTIFIER")];
self.isEmulator = YES;
} else {
self.isEmulator = NO;
}
return deviceId;
}
- (NSString*) deviceName
{
static NSDictionary* deviceNamesByCode = nil;
if (!deviceNamesByCode) {
deviceNamesByCode = @{@"iPod1,1" :@"iPod Touch", // (Original)
@"iPod2,1" :@"iPod Touch", // (Second Generation)
@"iPod3,1" :@"iPod Touch", // (Third Generation)
@"iPod4,1" :@"iPod Touch", // (Fourth Generation)
@"iPod5,1" :@"iPod Touch", // (Fifth Generation)
@"iPod7,1" :@"iPod Touch", // (Sixth Generation)
@"iPhone1,1" :@"iPhone", // (Original)
@"iPhone1,2" :@"iPhone 3G", // (3G)
@"iPhone2,1" :@"iPhone 3GS", // (3GS)
@"iPad1,1" :@"iPad", // (Original)
@"iPad2,1" :@"iPad 2", //
@"iPad2,2" :@"iPad 2", //
@"iPad2,3" :@"iPad 2", //
@"iPad2,4" :@"iPad 2", //
@"iPad3,1" :@"iPad", // (3rd Generation)
@"iPad3,2" :@"iPad", // (3rd Generation)
@"iPad3,3" :@"iPad", // (3rd Generation)
@"iPhone3,1" :@"iPhone 4", // (GSM)
@"iPhone3,2" :@"iPhone 4", // iPhone 4
@"iPhone3,3" :@"iPhone 4", // (CDMA/Verizon/Sprint)
@"iPhone4,1" :@"iPhone 4S", //
@"iPhone5,1" :@"iPhone 5", // (model A1428, AT&T/Canada)
@"iPhone5,2" :@"iPhone 5", // (model A1429, everything else)
@"iPad3,4" :@"iPad", // (4th Generation)
@"iPad3,5" :@"iPad", // (4th Generation)
@"iPad3,6" :@"iPad", // (4th Generation)
@"iPad2,5" :@"iPad Mini", // (Original)
@"iPad2,6" :@"iPad Mini", // (Original)
@"iPad2,7" :@"iPad Mini", // (Original)
@"iPhone5,3" :@"iPhone 5c", // (model A1456, A1532 | GSM)
@"iPhone5,4" :@"iPhone 5c", // (model A1507, A1516, A1526 (China), A1529 | Global)
@"iPhone6,1" :@"iPhone 5s", // (model A1433, A1533 | GSM)
@"iPhone6,2" :@"iPhone 5s", // (model A1457, A1518, A1528 (China), A1530 | Global)
@"iPhone7,1" :@"iPhone 6 Plus", //
@"iPhone7,2" :@"iPhone 6", //
@"iPhone8,1" :@"iPhone 6s", //
@"iPhone8,2" :@"iPhone 6s Plus", //
@"iPhone8,4" :@"iPhone SE", //
@"iPhone9,1" :@"iPhone 7", // (model A1660 | CDMA)
@"iPhone9,3" :@"iPhone 7", // (model A1778 | Global)
@"iPhone9,2" :@"iPhone 7 Plus", // (model A1661 | CDMA)
@"iPhone9,4" :@"iPhone 7 Plus", // (model A1784 | Global)
@"iPhone10,3":@"iPhone X", // (model A1865, A1902)
@"iPhone10,6":@"iPhone X", // (model A1901)
@"iPhone10,1":@"iPhone 8", // (model A1863, A1906, A1907)
@"iPhone10,4":@"iPhone 8", // (model A1905)
@"iPhone10,2":@"iPhone 8 Plus", // (model A1864, A1898, A1899)
@"iPhone10,5":@"iPhone 8 Plus", // (model A1897)
@"iPad4,1" :@"iPad Air", // 5th Generation iPad (iPad Air) - Wifi
@"iPad4,2" :@"iPad Air", // 5th Generation iPad (iPad Air) - Cellular
@"iPad4,3" :@"iPad Air", // 5th Generation iPad (iPad Air)
@"iPad4,4" :@"iPad Mini 2", // (2nd Generation iPad Mini - Wifi)
@"iPad4,5" :@"iPad Mini 2", // (2nd Generation iPad Mini - Cellular)
@"iPad4,6" :@"iPad Mini 2", // (2nd Generation iPad Mini)
@"iPad4,7" :@"iPad Mini 3", // (3rd Generation iPad Mini)
@"iPad4,8" :@"iPad Mini 3", // (3rd Generation iPad Mini)
@"iPad4,9" :@"iPad Mini 3", // (3rd Generation iPad Mini)
@"iPad5,1" :@"iPad Mini 4", // (4th Generation iPad Mini)
@"iPad5,2" :@"iPad Mini 4", // (4th Generation iPad Mini)
@"iPad5,3" :@"iPad Air 2", // 6th Generation iPad (iPad Air 2)
@"iPad5,4" :@"iPad Air 2", // 6th Generation iPad (iPad Air 2)
@"iPad6,3" :@"iPad Pro 9.7-inch",// iPad Pro 9.7-inch
@"iPad6,4" :@"iPad Pro 9.7-inch",// iPad Pro 9.7-inch
@"iPad6,7" :@"iPad Pro 12.9-inch",// iPad Pro 12.9-inch
@"iPad6,8" :@"iPad Pro 12.9-inch",// iPad Pro 12.9-inch
@"iPad7,1" :@"iPad Pro 12.9-inch",// 2nd Generation iPad Pro 12.5-inch - Wifi
@"iPad7,2" :@"iPad Pro 12.9-inch",// 2nd Generation iPad Pro 12.5-inch - Cellular
@"iPad7,3" :@"iPad Pro 10.5-inch",// iPad Pro 10.5-inch - Wifi
@"iPad7,4" :@"iPad Pro 10.5-inch",// iPad Pro 10.5-inch - Cellular
@"AppleTV2,1":@"Apple TV", // Apple TV (2nd Generation)
@"AppleTV3,1":@"Apple TV", // Apple TV (3rd Generation)
@"AppleTV3,2":@"Apple TV", // Apple TV (3rd Generation - Rev A)
@"AppleTV5,3":@"Apple TV", // Apple TV (4th Generation)
@"AppleTV6,2":@"Apple TV 4K", // Apple TV 4K
};
}
NSString* deviceName = [deviceNamesByCode objectForKey:self.deviceId];
if (!deviceName) {
// Not found on database. At least guess main device type from string contents:
if ([self.deviceId rangeOfString:@"iPod"].location != NSNotFound) {
deviceName = @"iPod Touch";
}
else if([self.deviceId rangeOfString:@"iPad"].location != NSNotFound) {
deviceName = @"iPad";
}
else if([self.deviceId rangeOfString:@"iPhone"].location != NSNotFound){
deviceName = @"iPhone";
}
else if([self.deviceId rangeOfString:@"AppleTV"].location != NSNotFound){
deviceName = @"Apple TV";
}
}
return deviceName;
}
- (NSString *) carrier
{
#if (TARGET_OS_TV)
return nil;
#else
CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];
CTCarrier *carrier = [netinfo subscriberCellularProvider];
return carrier.carrierName;
#endif
}
- (NSString*) userAgent
{
#if TARGET_OS_TV
return @"not available";
#else
UIWebView* webView = [[UIWebView alloc] initWithFrame:CGRectZero];
return [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
#endif
}
- (NSString*) deviceLocale
{
NSString *language = [[NSLocale preferredLanguages] objectAtIndex:0];
return language;
}
- (NSString*) deviceCountry
{
NSString *country = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode];
return country;
}
- (NSString*) timezone
{
NSTimeZone *currentTimeZone = [NSTimeZone localTimeZone];
return currentTimeZone.name;
}
- (bool) isTablet
{
return [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
}
// Font scales based on font sizes from https://developer.apple.com/ios/human-interface-guidelines/visual-design/typography/
- (NSNumber*) fontScale
{
float fontScale = 1.0;
NSString *contentSize = [UIApplication sharedApplication].preferredContentSizeCategory;
if ([contentSize isEqual: @"UICTContentSizeCategoryXS"]) fontScale = 0.82;
else if ([contentSize isEqual: @"UICTContentSizeCategoryS"]) fontScale = 0.88;
else if ([contentSize isEqual: @"UICTContentSizeCategoryM"]) fontScale = 0.95;
else if ([contentSize isEqual: @"UICTContentSizeCategoryL"]) fontScale = 1.0;
else if ([contentSize isEqual: @"UICTContentSizeCategoryXL"]) fontScale = 1.12;
else if ([contentSize isEqual: @"UICTContentSizeCategoryXXL"]) fontScale = 1.23;
else if ([contentSize isEqual: @"UICTContentSizeCategoryXXXL"]) fontScale = 1.35;
else if ([contentSize isEqual: @"UICTContentSizeCategoryAccessibilityM"]) fontScale = 1.64;
else if ([contentSize isEqual: @"UICTContentSizeCategoryAccessibilityL"]) fontScale = 1.95;
else if ([contentSize isEqual: @"UICTContentSizeCategoryAccessibilityXL"]) fontScale = 2.35;
else if ([contentSize isEqual: @"UICTContentSizeCategoryAccessibilityXXL"]) fontScale = 2.76;
else if ([contentSize isEqual: @"UICTContentSizeCategoryAccessibilityXXXL"]) fontScale = 3.12;
return [NSNumber numberWithFloat: fontScale];
}
- (bool) is24Hour
{
NSString *format = [NSDateFormatter dateFormatFromTemplate:@"j" options:0 locale:[NSLocale currentLocale]];
return ([format rangeOfString:@"a"].location == NSNotFound);
}
- (unsigned long long) totalMemory {
return [NSProcessInfo processInfo].physicalMemory;
}
- (NSDictionary *) getStorageDictionary {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[NSFileManager defaultManager] attributesOfFileSystemForPath:[paths lastObject] error: nil];
}
- (uint64_t) totalDiskCapacity {
uint64_t totalSpace = 0;
NSDictionary *storage = [self getStorageDictionary];
if (storage) {
NSNumber *fileSystemSizeInBytes = [storage objectForKey: NSFileSystemSize];
totalSpace = [fileSystemSizeInBytes unsignedLongLongValue];
}
return totalSpace;
}
- (uint64_t) freeDiskStorage {
uint64_t freeSpace = 0;
NSDictionary *storage = [self getStorageDictionary];
if (storage) {
NSNumber *freeFileSystemSizeInBytes = [storage objectForKey: NSFileSystemFreeSize];
freeSpace = [freeFileSystemSizeInBytes unsignedLongLongValue];
}
return freeSpace;
}
- (NSDictionary *)constantsToExport
{
UIDevice *currentDevice = [UIDevice currentDevice];
NSString *uniqueId = [DeviceUID uid];
return @{
@"systemName": currentDevice.systemName,
@"systemVersion": currentDevice.systemVersion,
@"apiLevel": @"not available",
@"model": self.deviceName ?: [NSNull null],
@"brand": @"Apple",
@"deviceId": self.deviceId ?: [NSNull null],
@"deviceName": currentDevice.name,
@"deviceLocale": self.deviceLocale ?: [NSNull null],
@"deviceCountry": self.deviceCountry ?: [NSNull null],
@"uniqueId": uniqueId,
@"appName": [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleDisplayName"] ?: [NSNull null],
@"bundleId": [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"] ?: [NSNull null],
@"appVersion": [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] ?: [NSNull null],
@"buildNumber": [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"] ?: [NSNull null],
@"systemManufacturer": @"Apple",
@"carrier": self.carrier ?: [NSNull null],
@"userAgent": self.userAgent ?: [NSNull null],
@"timezone": self.timezone ?: [NSNull null],
@"isEmulator": @(self.isEmulator),
@"isTablet": @(self.isTablet),
@"is24Hour": @(self.is24Hour),
@"fontScale": self.fontScale,
@"totalMemory": @(self.totalMemory),
@"totalDiskCapacity": @(self.totalDiskCapacity),
@"freeDiskStorage": @(self.freeDiskStorage),
};
}
RCT_EXPORT_METHOD(isPinOrFingerprintSet:(RCTResponseSenderBlock)callback)
{
#if TARGET_OS_TV
BOOL isPinOrFingerprintSet = false;
#else
LAContext *context = [[LAContext alloc] init];
BOOL isPinOrFingerprintSet = ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:nil]);
#endif
callback(@[[NSNumber numberWithBool:isPinOrFingerprintSet]]);
}
RCT_EXPORT_METHOD(getBatteryLevel:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
#if TARGET_OS_TV
float batteryLevel = 1.0;
#else
float batteryLevel = [UIDevice currentDevice].batteryLevel;
#endif
resolve(@(batteryLevel));
}
@end