Unverified Commit f7a01b28 authored by Viktor Dlouhy's avatar Viktor Dlouhy Committed by GitHub

Merge branch 'master' into master

parents eaca0b12 aa056392
## Release Notes ## Release Notes
### Upcoming
- Make play-services optional (https://github.com/rebeccahughes/react-native-device-info/pull/226)
- Critical fix on WIFI STATE (https://github.com/rebeccahughes/react-native-device-info/pull/249)
- Added `getTotalMemory` and `getMaxMemory` (https://github.com/rebeccahughes/react-native-device-info/pull/289)
### 0.12.0
- Get real WebView UserAgent on Android (https://github.com/rebeccahughes/react-native-device-info/pull/207)
- Add DeviceUID.h to public headers (https://github.com/rebeccahughes/react-native-device-info/pull/217)
- Add `getPhoneNumber` (https://github.com/rebeccahughes/react-native-device-info/pull/174)
- Fix typescript definitions (https://github.com/rebeccahughes/react-native-device-info/pull/221)
- Add `getFirstInstallTime` and `getLastInstallTime` (https://github.com/rebeccahughes/react-native-device-info/pull/222)
- Added version check and permission to work with Android API >= 16 (https://github.com/rebeccahughes/react-native-device-info/pull/225)
- Added device detection even when in an iOS emulator (https://github.com/rebeccahughes/react-native-device-info/pull/224)
- Add support for new iPhone, iPad, and Apple TV models (https://github.com/rebeccahughes/react-native-device-info/pull/230)
- Add android only `getAPILevel` method (https://github.com/rebeccahughes/react-native-device-info/pull/232)
- Add Android support for serial number, IP, and MAC address (https://github.com/rebeccahughes/react-native-device-info/pull/150)
- Add tvOS support (https://github.com/rebeccahughes/react-native-device-info/pull/235)
- Add flow types
- Fix getCurrentActivity() null crash in Android (https://github.com/rebeccahughes/react-native-device-info/pull/247)
[Diff](https://github.com/rebeccahughes/react-native-device-info/compare/1aafc6f0b20d7cd6f0939ea5370e9899e4914c93...master)
### 0.11.0
- Add support for RN > 0.47
- Update typescript definitions
[Diff](https://github.com/rebeccahughes/react-native-device-info/compare/5b869cdd5e16b65cbe4e85a565aa331bd7546b89...1aafc6f0b20d7cd6f0939ea5370e9899e4914c93)
### 0.10.2
- Add typescript definitions
[Diff](https://github.com/rebeccahughes/react-native-device-info/compare/f3967862711892615e7f51d49d0034ee134f3e3d...5b869cdd5e16b65cbe4e85a565aa331bd7546b89)
### 0.10.1
- Add `isPinOrFingerprintSet` method
- Add support for RN > 0.40
[Diff](https://github.com/rebeccahughes/react-native-device-info/compare/c843144ea872a79f4d53a53b32f72511fbfc8d8b...f3967862711892615e7f51d49d0034ee134f3e3d)
### 0.10.0
- Semver fix
[Diff](https://github.com/rebeccahughes/react-native-device-info/compare/e8bfe5ea8d5f5414f2f97f35a5d02b611cbe39e3...c843144ea872a79f4d53a53b32f72511fbfc8d8b)
### 0.9.8
[Diff](https://github.com/rebeccahughes/react-native-device-info/compare/668996c64e23f477fc8156cdc43a49198b4fdd20...e8bfe5ea8d5f5414f2f97f35a5d02b611cbe39e3)
### 0.9.7 ### 0.9.7
Several bugfixes and detecting if device is a tablet Several bugfixes and detecting if device is a tablet
### 0.9.3 ### 0.9.3
adds support for Brand information e.g. apple, htc, etc adds support for Brand information e.g. apple, htc, etc
......
This diff is collapsed.
...@@ -11,6 +11,10 @@ ...@@ -11,6 +11,10 @@
BF770A3D1F6A3EEE007E5F09 /* DeviceUID.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 76E65CA21D4CA143009B7AF1 /* DeviceUID.h */; }; BF770A3D1F6A3EEE007E5F09 /* DeviceUID.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 76E65CA21D4CA143009B7AF1 /* DeviceUID.h */; };
DA5891DC1BA9A9FC002B4DB2 /* RNDeviceInfo.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DA5891DB1BA9A9FC002B4DB2 /* RNDeviceInfo.h */; }; DA5891DC1BA9A9FC002B4DB2 /* RNDeviceInfo.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DA5891DB1BA9A9FC002B4DB2 /* RNDeviceInfo.h */; };
DA5891DE1BA9A9FC002B4DB2 /* RNDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5891DD1BA9A9FC002B4DB2 /* RNDeviceInfo.m */; }; DA5891DE1BA9A9FC002B4DB2 /* RNDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5891DD1BA9A9FC002B4DB2 /* RNDeviceInfo.m */; };
E72EC1491F7ABC0C0001BC90 /* DeviceUID.m in Sources */ = {isa = PBXBuildFile; fileRef = 76E65CA31D4CA143009B7AF1 /* DeviceUID.m */; };
E72EC14A1F7ABC0E0001BC90 /* RNDeviceInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = DA5891DD1BA9A9FC002B4DB2 /* RNDeviceInfo.m */; };
E72EC14B1F7ABC1A0001BC90 /* DeviceUID.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 76E65CA21D4CA143009B7AF1 /* DeviceUID.h */; };
E72EC14C1F7ABC1D0001BC90 /* RNDeviceInfo.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DA5891DB1BA9A9FC002B4DB2 /* RNDeviceInfo.h */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */ /* Begin PBXCopyFilesBuildPhase section */
...@@ -25,6 +29,17 @@ ...@@ -25,6 +29,17 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
E72EC13E1F7ABB5A0001BC90 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
E72EC14B1F7ABC1A0001BC90 /* DeviceUID.h in CopyFiles */,
E72EC14C1F7ABC1D0001BC90 /* RNDeviceInfo.h in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
...@@ -33,6 +48,7 @@ ...@@ -33,6 +48,7 @@
DA5891D81BA9A9FC002B4DB2 /* libRNDeviceInfo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNDeviceInfo.a; sourceTree = BUILT_PRODUCTS_DIR; }; DA5891D81BA9A9FC002B4DB2 /* libRNDeviceInfo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNDeviceInfo.a; sourceTree = BUILT_PRODUCTS_DIR; };
DA5891DB1BA9A9FC002B4DB2 /* RNDeviceInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNDeviceInfo.h; sourceTree = "<group>"; }; DA5891DB1BA9A9FC002B4DB2 /* RNDeviceInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNDeviceInfo.h; sourceTree = "<group>"; };
DA5891DD1BA9A9FC002B4DB2 /* RNDeviceInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNDeviceInfo.m; sourceTree = "<group>"; }; DA5891DD1BA9A9FC002B4DB2 /* RNDeviceInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNDeviceInfo.m; sourceTree = "<group>"; };
E72EC1401F7ABB5A0001BC90 /* libRNDeviceInfo-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libRNDeviceInfo-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
...@@ -43,6 +59,13 @@ ...@@ -43,6 +59,13 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
E72EC13D1F7ABB5A0001BC90 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
...@@ -58,6 +81,7 @@ ...@@ -58,6 +81,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
DA5891D81BA9A9FC002B4DB2 /* libRNDeviceInfo.a */, DA5891D81BA9A9FC002B4DB2 /* libRNDeviceInfo.a */,
E72EC1401F7ABB5A0001BC90 /* libRNDeviceInfo-tvOS.a */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -93,6 +117,23 @@ ...@@ -93,6 +117,23 @@
productReference = DA5891D81BA9A9FC002B4DB2 /* libRNDeviceInfo.a */; productReference = DA5891D81BA9A9FC002B4DB2 /* libRNDeviceInfo.a */;
productType = "com.apple.product-type.library.static"; productType = "com.apple.product-type.library.static";
}; };
E72EC13F1F7ABB5A0001BC90 /* RNDeviceInfo-tvOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = E72EC1481F7ABB5A0001BC90 /* Build configuration list for PBXNativeTarget "RNDeviceInfo-tvOS" */;
buildPhases = (
E72EC13C1F7ABB5A0001BC90 /* Sources */,
E72EC13D1F7ABB5A0001BC90 /* Frameworks */,
E72EC13E1F7ABB5A0001BC90 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = "RNDeviceInfo-tvOS";
productName = "RNDeviceInfo-tvOS";
productReference = E72EC1401F7ABB5A0001BC90 /* libRNDeviceInfo-tvOS.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
/* Begin PBXProject section */ /* Begin PBXProject section */
...@@ -105,6 +146,10 @@ ...@@ -105,6 +146,10 @@
DA5891D71BA9A9FC002B4DB2 = { DA5891D71BA9A9FC002B4DB2 = {
CreatedOnToolsVersion = 7.0; CreatedOnToolsVersion = 7.0;
}; };
E72EC13F1F7ABB5A0001BC90 = {
CreatedOnToolsVersion = 9.0;
ProvisioningStyle = Automatic;
};
}; };
}; };
buildConfigurationList = DA5891D31BA9A9FC002B4DB2 /* Build configuration list for PBXProject "RNDeviceInfo" */; buildConfigurationList = DA5891D31BA9A9FC002B4DB2 /* Build configuration list for PBXProject "RNDeviceInfo" */;
...@@ -120,6 +165,7 @@ ...@@ -120,6 +165,7 @@
projectRoot = ""; projectRoot = "";
targets = ( targets = (
DA5891D71BA9A9FC002B4DB2 /* RNDeviceInfo */, DA5891D71BA9A9FC002B4DB2 /* RNDeviceInfo */,
E72EC13F1F7ABB5A0001BC90 /* RNDeviceInfo-tvOS */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
...@@ -134,6 +180,15 @@ ...@@ -134,6 +180,15 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
E72EC13C1F7ABB5A0001BC90 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E72EC1491F7ABC0C0001BC90 /* DeviceUID.m in Sources */,
E72EC14A1F7ABC0E0001BC90 /* RNDeviceInfo.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */ /* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
...@@ -251,6 +306,60 @@ ...@@ -251,6 +306,60 @@
}; };
name = Release; name = Release;
}; };
E72EC1461F7ABB5A0001BC90 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
GCC_C_LANGUAGE_STANDARD = gnu11;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 10.0;
};
name = Debug;
};
E72EC1471F7ABB5A0001BC90 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
GCC_C_LANGUAGE_STANDARD = gnu11;
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = appletvos;
SKIP_INSTALL = YES;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 10.0;
};
name = Release;
};
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
...@@ -272,6 +381,15 @@ ...@@ -272,6 +381,15 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
E72EC1481F7ABB5A0001BC90 /* Build configuration list for PBXNativeTarget "RNDeviceInfo-tvOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
E72EC1461F7ABB5A0001BC90 /* Debug */,
E72EC1471F7ABB5A0001BC90 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */ /* End XCConfigurationList section */
}; };
rootObject = DA5891D01BA9A9FC002B4DB2 /* Project object */; rootObject = DA5891D01BA9A9FC002B4DB2 /* Project object */;
......
...@@ -8,32 +8,45 @@ ...@@ -8,32 +8,45 @@
#import "RNDeviceInfo.h" #import "RNDeviceInfo.h"
#import "DeviceUID.h" #import "DeviceUID.h"
#if !(TARGET_OS_TV)
#import <LocalAuthentication/LocalAuthentication.h> #import <LocalAuthentication/LocalAuthentication.h>
#endif
@interface RNDeviceInfo() @interface RNDeviceInfo()
@property (nonatomic) bool isEmulator;
@end @end
@import CoreTelephony;
@implementation RNDeviceInfo @implementation RNDeviceInfo
{
} @synthesize isEmulator;
RCT_EXPORT_MODULE() RCT_EXPORT_MODULE(RNDeviceInfo)
- (dispatch_queue_t)methodQueue + (BOOL)requiresMainQueueSetup
{ {
return dispatch_get_main_queue(); return YES;
} }
- (NSString*) deviceId - (NSString*) deviceId
{ {
struct utsname systemInfo; struct utsname systemInfo;
uname(&systemInfo); uname(&systemInfo);
return [NSString stringWithCString:systemInfo.machine NSString* deviceId = [NSString stringWithCString:systemInfo.machine
encoding:NSUTF8StringEncoding]; 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 - (NSString*) deviceName
...@@ -42,9 +55,7 @@ RCT_EXPORT_MODULE() ...@@ -42,9 +55,7 @@ RCT_EXPORT_MODULE()
if (!deviceNamesByCode) { if (!deviceNamesByCode) {
deviceNamesByCode = @{@"i386" :@"Simulator", deviceNamesByCode = @{@"iPod1,1" :@"iPod Touch", // (Original)
@"x86_64" :@"Simulator",
@"iPod1,1" :@"iPod Touch", // (Original)
@"iPod2,1" :@"iPod Touch", // (Second Generation) @"iPod2,1" :@"iPod Touch", // (Second Generation)
@"iPod3,1" :@"iPod Touch", // (Third Generation) @"iPod3,1" :@"iPod Touch", // (Third Generation)
@"iPod4,1" :@"iPod Touch", // (Fourth Generation) @"iPod4,1" :@"iPod Touch", // (Fourth Generation)
...@@ -86,6 +97,12 @@ RCT_EXPORT_MODULE() ...@@ -86,6 +97,12 @@ RCT_EXPORT_MODULE()
@"iPhone9,3" :@"iPhone 7", // (model A1778 | Global) @"iPhone9,3" :@"iPhone 7", // (model A1778 | Global)
@"iPhone9,2" :@"iPhone 7 Plus", // (model A1661 | CDMA) @"iPhone9,2" :@"iPhone 7 Plus", // (model A1661 | CDMA)
@"iPhone9,4" :@"iPhone 7 Plus", // (model A1784 | Global) @"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,1" :@"iPad Air", // 5th Generation iPad (iPad Air) - Wifi
@"iPad4,2" :@"iPad Air", // 5th Generation iPad (iPad Air) - Cellular @"iPad4,2" :@"iPad Air", // 5th Generation iPad (iPad Air) - Cellular
@"iPad4,3" :@"iPad Air", // 5th Generation iPad (iPad Air) @"iPad4,3" :@"iPad Air", // 5th Generation iPad (iPad Air)
...@@ -103,10 +120,15 @@ RCT_EXPORT_MODULE() ...@@ -103,10 +120,15 @@ RCT_EXPORT_MODULE()
@"iPad6,4" :@"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,7" :@"iPad Pro 12.9-inch",// iPad Pro 12.9-inch
@"iPad6,8" :@"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 12.5-inch - Wifi
@"iPad7,4" :@"iPad Pro 10.5-inch",// iPad Pro 12.5-inch - Cellular
@"AppleTV2,1":@"Apple TV", // Apple TV (2nd Generation) @"AppleTV2,1":@"Apple TV", // Apple TV (2nd Generation)
@"AppleTV3,1":@"Apple TV", // Apple TV (3rd Generation) @"AppleTV3,1":@"Apple TV", // Apple TV (3rd Generation)
@"AppleTV3,2":@"Apple TV", // Apple TV (3rd Generation - Rev A) @"AppleTV3,2":@"Apple TV", // Apple TV (3rd Generation - Rev A)
@"AppleTV5,3":@"Apple TV", // Apple TV (4th Generation) @"AppleTV5,3":@"Apple TV", // Apple TV (4th Generation)
@"AppleTV6,2":@"Apple TV 4K", // Apple TV 4K
}; };
} }
...@@ -124,15 +146,29 @@ RCT_EXPORT_MODULE() ...@@ -124,15 +146,29 @@ RCT_EXPORT_MODULE()
else if([self.deviceId rangeOfString:@"iPhone"].location != NSNotFound){ else if([self.deviceId rangeOfString:@"iPhone"].location != NSNotFound){
deviceName = @"iPhone"; deviceName = @"iPhone";
} }
else if([self.deviceId rangeOfString:@"AppleTV"].location != NSNotFound){
deviceName = @"Apple TV";
}
} }
return deviceName; return deviceName;
} }
- (NSString *) carrier
{
CTTelephonyNetworkInfo *netinfo = [[CTTelephonyNetworkInfo alloc] init];
CTCarrier *carrier = [netinfo subscriberCellularProvider];
return carrier.carrierName;
}
- (NSString*) userAgent - (NSString*) userAgent
{ {
#if TARGET_OS_TV
return @"not available";
#else
UIWebView* webView = [[UIWebView alloc] initWithFrame:CGRectZero]; UIWebView* webView = [[UIWebView alloc] initWithFrame:CGRectZero];
return [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; return [webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"];
#endif
} }
- (NSString*) deviceLocale - (NSString*) deviceLocale
...@@ -153,14 +189,19 @@ RCT_EXPORT_MODULE() ...@@ -153,14 +189,19 @@ RCT_EXPORT_MODULE()
return currentTimeZone.name; return currentTimeZone.name;
} }
- (bool) isEmulator - (bool) isTablet
{ {
return [self.deviceName isEqual: @"Simulator"]; return [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad;
} }
- (bool) isTablet - (bool) is24Hour
{ {
return [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad; 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 *)constantsToExport - (NSDictionary *)constantsToExport
...@@ -172,6 +213,7 @@ RCT_EXPORT_MODULE() ...@@ -172,6 +213,7 @@ RCT_EXPORT_MODULE()
return @{ return @{
@"systemName": currentDevice.systemName, @"systemName": currentDevice.systemName,
@"systemVersion": currentDevice.systemVersion, @"systemVersion": currentDevice.systemVersion,
@"apiLevel": @"not available",
@"model": self.deviceName, @"model": self.deviceName,
@"brand": @"Apple", @"brand": @"Apple",
@"deviceId": self.deviceId, @"deviceId": self.deviceId,
...@@ -184,17 +226,24 @@ RCT_EXPORT_MODULE() ...@@ -184,17 +226,24 @@ RCT_EXPORT_MODULE()
@"appVersion": [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] ?: [NSNull null], @"appVersion": [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"] ?: [NSNull null],
@"buildNumber": [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"], @"buildNumber": [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleVersion"],
@"systemManufacturer": @"Apple", @"systemManufacturer": @"Apple",
@"carrier": self.carrier ?: [NSNull null],
@"userAgent": self.userAgent, @"userAgent": self.userAgent,
@"timezone": self.timezone, @"timezone": self.timezone,
@"isEmulator": @(self.isEmulator), @"isEmulator": @(self.isEmulator),
@"isTablet": @(self.isTablet), @"isTablet": @(self.isTablet),
@"is24Hour": @(self.is24Hour),
@"totalMemory": @(self.totalMemory)
}; };
} }
RCT_EXPORT_METHOD(isPinOrFingerprintSet:(RCTResponseSenderBlock)callback) RCT_EXPORT_METHOD(isPinOrFingerprintSet:(RCTResponseSenderBlock)callback)
{ {
#if TARGET_OS_TV
BOOL isPinOrFingerprintSet = false;
#else
LAContext *context = [[LAContext alloc] init]; LAContext *context = [[LAContext alloc] init];
BOOL isPinOrFingerprintSet = ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:nil]); BOOL isPinOrFingerprintSet = ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthentication error:nil]);
#endif
callback(@[[NSNumber numberWithBool:isPinOrFingerprintSet]]); callback(@[[NSNumber numberWithBool:isPinOrFingerprintSet]]);
} }
......
package com.learnium.RNDeviceInfo; package com.learnium.RNDeviceInfo;
import android.Manifest;
import android.app.KeyguardManager; import android.app.KeyguardManager;
import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothAdapter;
import android.content.Context; import android.content.Context;
...@@ -7,22 +8,26 @@ import android.content.pm.PackageInfo; ...@@ -7,22 +8,26 @@ import android.content.pm.PackageInfo;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiInfo;
import android.os.Build; import android.os.Build;
import android.provider.Settings.Secure; import android.provider.Settings.Secure;
import android.webkit.WebSettings; import android.webkit.WebSettings;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import android.text.format.Formatter;
import com.google.android.gms.iid.InstanceID; import android.app.ActivityManager;
import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod; import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
import java.lang.Runtime;
import javax.annotation.Nullable; import javax.annotation.Nullable;
...@@ -30,8 +35,11 @@ public class RNDeviceModule extends ReactContextBaseJavaModule { ...@@ -30,8 +35,11 @@ public class RNDeviceModule extends ReactContextBaseJavaModule {
ReactApplicationContext reactContext; ReactApplicationContext reactContext;
WifiInfo wifiInfo;
public RNDeviceModule(ReactApplicationContext reactContext) { public RNDeviceModule(ReactApplicationContext reactContext) {
super(reactContext); super(reactContext);
this.reactContext = reactContext; this.reactContext = reactContext;
} }
...@@ -40,6 +48,14 @@ public class RNDeviceModule extends ReactContextBaseJavaModule { ...@@ -40,6 +48,14 @@ public class RNDeviceModule extends ReactContextBaseJavaModule {
return "RNDeviceInfo"; return "RNDeviceInfo";
} }
private WifiInfo getWifiInfo() {
if ( this.wifiInfo == null ) {
WifiManager manager = (WifiManager) reactContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
this.wifiInfo = manager.getConnectionInfo();
}
return this.wifiInfo;
}
private String getCurrentLanguage() { private String getCurrentLanguage() {
Locale current = getReactApplicationContext().getResources().getConfiguration().locale; Locale current = getReactApplicationContext().getResources().getConfiguration().locale;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
...@@ -76,17 +92,38 @@ public class RNDeviceModule extends ReactContextBaseJavaModule { ...@@ -76,17 +92,38 @@ public class RNDeviceModule extends ReactContextBaseJavaModule {
return layout == Configuration.SCREENLAYOUT_SIZE_LARGE || layout == Configuration.SCREENLAYOUT_SIZE_XLARGE; return layout == Configuration.SCREENLAYOUT_SIZE_LARGE || layout == Configuration.SCREENLAYOUT_SIZE_XLARGE;
} }
private Boolean is24Hour() {
return android.text.format.DateFormat.is24HourFormat(this.reactContext.getApplicationContext());
}
@ReactMethod @ReactMethod
public void isPinOrFingerprintSet(Callback callback) { public void isPinOrFingerprintSet(Callback callback) {
KeyguardManager keyguardManager = (KeyguardManager) this.reactContext.getSystemService(Context.KEYGUARD_SERVICE); //api 16+ KeyguardManager keyguardManager = (KeyguardManager) this.reactContext.getApplicationContext().getSystemService(Context.KEYGUARD_SERVICE); //api 16+
callback.invoke(keyguardManager.isKeyguardSecure()); callback.invoke(keyguardManager.isKeyguardSecure());
} }
@ReactMethod
public void getIpAddress(Promise p) {
String ipAddress = Formatter.formatIpAddress(getWifiInfo().getIpAddress());
p.resolve(ipAddress);
}
@ReactMethod
public void getMacAddress(Promise p) {
String macAddress = getWifiInfo().getMacAddress();
p.resolve(macAddress);
}
@ReactMethod
public String getCarrier() {
TelephonyManager telMgr = (TelephonyManager) this.reactContext.getSystemService(Context.TELEPHONY_SERVICE);
return telMgr.getNetworkOperatorName();
}
@Override @Override
public @Nullable Map<String, Object> getConstants() { public @Nullable Map<String, Object> getConstants() {
HashMap<String, Object> constants = new HashMap<String, Object>(); HashMap<String, Object> constants = new HashMap<String, Object>();
TelephonyManager telMgr = (TelephonyManager) this.reactContext.getSystemService(Context.TELEPHONY_SERVICE);
PackageManager packageManager = this.reactContext.getPackageManager(); PackageManager packageManager = this.reactContext.getPackageManager();
String packageName = this.reactContext.getPackageName(); String packageName = this.reactContext.getPackageName();
String applicationName = this.reactContext.getApplicationInfo().loadLabel(this.reactContext.getPackageManager()).toString(); String applicationName = this.reactContext.getApplicationInfo().loadLabel(this.reactContext.getPackageManager()).toString();
...@@ -119,23 +156,53 @@ public class RNDeviceModule extends ReactContextBaseJavaModule { ...@@ -119,23 +156,53 @@ public class RNDeviceModule extends ReactContextBaseJavaModule {
e.printStackTrace(); e.printStackTrace();
} }
constants.put("instanceId", InstanceID.getInstance(this.reactContext).getId()); try {
if (Class.forName("com.google.android.gms.iid.InstanceID") != null) {
constants.put("instanceId", com.google.android.gms.iid.InstanceID.getInstance(this.reactContext).getId());
}
} catch (ClassNotFoundException e) {
constants.put("instanceId", "N/A: Add com.google.android.gms:play-services-gcm to your project.");
}
constants.put("serialNumber", Build.SERIAL);
constants.put("deviceName", deviceName); constants.put("deviceName", deviceName);
constants.put("systemName", "Android"); constants.put("systemName", "Android");
constants.put("systemVersion", Build.VERSION.RELEASE); constants.put("systemVersion", Build.VERSION.RELEASE);
constants.put("model", Build.MODEL); constants.put("model", Build.MODEL);
constants.put("brand", Build.BRAND); constants.put("brand", Build.BRAND);
constants.put("deviceId", Build.BOARD); constants.put("deviceId", Build.BOARD);
constants.put("apiLevel", Build.VERSION.SDK_INT);
constants.put("deviceLocale", this.getCurrentLanguage()); constants.put("deviceLocale", this.getCurrentLanguage());
constants.put("deviceCountry", this.getCurrentCountry()); constants.put("deviceCountry", this.getCurrentCountry());
constants.put("uniqueId", Secure.getString(this.reactContext.getContentResolver(), Secure.ANDROID_ID)); constants.put("uniqueId", Secure.getString(this.reactContext.getContentResolver(), Secure.ANDROID_ID));
constants.put("systemManufacturer", Build.MANUFACTURER); constants.put("systemManufacturer", Build.MANUFACTURER);
constants.put("bundleId", packageName); constants.put("bundleId", packageName);
constants.put("userAgent", WebSettings.getDefaultUserAgent(this.reactContext)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
try {
constants.put("userAgent", WebSettings.getDefaultUserAgent(this.reactContext));
} catch (RuntimeException e) {
constants.put("userAgent", System.getProperty("http.agent"));
}
}
constants.put("timezone", TimeZone.getDefault().getID()); constants.put("timezone", TimeZone.getDefault().getID());
constants.put("isEmulator", this.isEmulator()); constants.put("isEmulator", this.isEmulator());
constants.put("isTablet", this.isTablet()); constants.put("isTablet", this.isTablet());
constants.put("phoneNumber", telMgr.getLine1Number()); constants.put("is24Hour", this.is24Hour());
if (getCurrentActivity() != null &&
(getCurrentActivity().checkCallingOrSelfPermission(Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED ||
getCurrentActivity().checkCallingOrSelfPermission(Manifest.permission.READ_SMS) == PackageManager.PERMISSION_GRANTED ||
getCurrentActivity().checkCallingOrSelfPermission("android.permission.READ_PHONE_NUMBERS") == PackageManager.PERMISSION_GRANTED)) {
TelephonyManager telMgr = (TelephonyManager) this.reactContext.getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
constants.put("phoneNumber", telMgr.getLine1Number());
}
constants.put("carrier", this.getCarrier());
Runtime rt = Runtime.getRuntime();
constants.put("maxMemory", rt.maxMemory());
ActivityManager actMgr = (ActivityManager) this.reactContext.getSystemService(Context.ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
actMgr.getMemoryInfo(memInfo);
constants.put("totalMemory", memInfo.totalMem);
return constants; return constants;
} }
} }
...@@ -21,7 +21,15 @@ export function getTimezone(): string; ...@@ -21,7 +21,15 @@ export function getTimezone(): string;
export function getInstanceID(): string; export function getInstanceID(): string;
export function isEmulator(): boolean; export function isEmulator(): boolean;
export function isTablet(): boolean; export function isTablet(): boolean;
export function is24Hour(): boolean;
export function isPinOrFingerprintSet(): (cb: (isPinOrFingerprintSet: boolean) => void) => void; export function isPinOrFingerprintSet(): (cb: (isPinOrFingerprintSet: boolean) => void) => void;
export function getFirstInstallTime(): number; export function getFirstInstallTime(): number;
export function getLastUpdateTime(): number; export function getLastUpdateTime(): number;
export function getSerialNumber(): string;
export function getIPAddress(): Promise<string>;
export function getMACAddress(): Promise<string>;
export function getPhoneNumber(): string;
export function getAPILevel(): number;
export function getCarrier(): string;
export function getTotalMemory(): number;
export function getMaxMemory(): number;
...@@ -5,30 +5,42 @@ ...@@ -5,30 +5,42 @@
var RNDeviceInfo = require('react-native').NativeModules.RNDeviceInfo; var RNDeviceInfo = require('react-native').NativeModules.RNDeviceInfo;
module.exports = { module.exports = {
getUniqueID: function () { getUniqueID: function() {
return RNDeviceInfo.uniqueId; return RNDeviceInfo.uniqueId;
}, },
getInstanceID: function() { getInstanceID: function() {
return RNDeviceInfo.instanceId; return RNDeviceInfo.instanceId;
}, },
getDeviceId: function () { getSerialNumber: function() {
return RNDeviceInfo.serialNumber;
},
getIPAddress: function() {
return RNDeviceInfo.getIpAddress();
},
getMACAddress: function() {
return RNDeviceInfo.getMacAddress();
},
getDeviceId: function() {
return RNDeviceInfo.deviceId; return RNDeviceInfo.deviceId;
}, },
getManufacturer: function () { getManufacturer: function() {
return RNDeviceInfo.systemManufacturer; return RNDeviceInfo.systemManufacturer;
}, },
getModel: function () { getModel: function() {
return RNDeviceInfo.model; return RNDeviceInfo.model;
}, },
getBrand: function () { getBrand: function() {
return RNDeviceInfo.brand; return RNDeviceInfo.brand;
}, },
getSystemName: function () { getSystemName: function() {
return RNDeviceInfo.systemName; return RNDeviceInfo.systemName;
}, },
getSystemVersion: function () { getSystemVersion: function() {
return RNDeviceInfo.systemVersion; return RNDeviceInfo.systemVersion;
}, },
getAPILevel: function() {
return RNDeviceInfo.apiLevel;
},
getBundleId: function() { getBundleId: function() {
return RNDeviceInfo.bundleId; return RNDeviceInfo.bundleId;
}, },
...@@ -42,7 +54,7 @@ module.exports = { ...@@ -42,7 +54,7 @@ module.exports = {
return RNDeviceInfo.appVersion; return RNDeviceInfo.appVersion;
}, },
getReadableVersion: function() { getReadableVersion: function() {
return RNDeviceInfo.appVersion + "." + RNDeviceInfo.buildNumber; return RNDeviceInfo.appVersion + '.' + RNDeviceInfo.buildNumber;
}, },
getDeviceName: function() { getDeviceName: function() {
return RNDeviceInfo.deviceName; return RNDeviceInfo.deviceName;
...@@ -65,14 +77,28 @@ module.exports = { ...@@ -65,14 +77,28 @@ module.exports = {
isTablet: function() { isTablet: function() {
return RNDeviceInfo.isTablet; return RNDeviceInfo.isTablet;
}, },
is24Hour: function() {
return RNDeviceInfo.is24Hour;
},
isPinOrFingerprintSet: function () { isPinOrFingerprintSet: function () {
return RNDeviceInfo.isPinOrFingerprintSet; return RNDeviceInfo.isPinOrFingerprintSet;
}, },
getFirstInstallTime: function () { getFirstInstallTime: function() {
return RNDeviceInfo.firstInstallTime; return RNDeviceInfo.firstInstallTime;
}, },
getLastUpdateTime: function () { getLastUpdateTime: function() {
return RNDeviceInfo.lastUpdateTime; return RNDeviceInfo.lastUpdateTime;
}, },
getPhoneNumber: _ => RNDeviceInfo.phoneNumber getPhoneNumber: function() {
return RNDeviceInfo.phoneNumber;
},
getCarrier: function() {
return RNDeviceInfo.carrier;
},
getTotalMemory: function() {
return RNDeviceInfo.totalMemory;
},
getMaxMemory: function() {
return RNDeviceInfo.maxMemory;
},
}; };
// @flow
declare module.exports: {
getUniqueID: () => string,
getManufacturer: () => string,
getBrand: () => string,
getModel: () => string,
getDeviceId: () => string,
getSystemName: () => string,
getSystemVersion: () => string,
getBundleId: () => string,
getBuildNumber: () => string,
getVersion: () => string,
getReadableVersion: () => string,
getDeviceName: () => string,
getUserAgent: () => string,
getDeviceLocale: () => string,
getDeviceCountry: () => string,
getTimezone: () => string,
isEmulator: () => boolean,
isTablet: () => boolean,
is24Hour: () => boolean,
isPinOrFingerprintSet: () => (cb: (isPinOrFingerprintSet: boolean) => void) => void,
getAPILevel: () => number,
getInstanceID: () => string,
getPhoneNumber: () => ?string,
getFirstInstallTime: () => number,
getLastUpdateTime: () => number,
getSerialNumber: () => string,
getIPAddress: () => Promise<string>,
getMACAddress: () => Promise<string>,
getCarrier: () => string,
getTotalMemory: () => number,
getMaxMemory: () => number,
}
{ {
"name": "react-native-device-info", "name": "react-native-device-info",
"version": "0.11.0", "version": "0.13.0",
"description": "Get device information using react-native", "description": "Get device information using react-native",
"main": "deviceinfo.js", "main": "deviceinfo.js",
"typings": "./deviceinfo.d.ts", "typings": "./deviceinfo.d.ts",
...@@ -8,6 +8,9 @@ ...@@ -8,6 +8,9 @@
"type": "git", "type": "git",
"url": "https://github.com/rebeccahughes/react-native-device-info" "url": "https://github.com/rebeccahughes/react-native-device-info"
}, },
"scripts": {
"shipit": "np"
},
"keywords": [ "keywords": [
"react-component", "react-component",
"react-native", "react-native",
...@@ -19,10 +22,15 @@ ...@@ -19,10 +22,15 @@
"cocoapod" "cocoapod"
], ],
"author": "Rebecca Hughes <rebecca@learnium.net> (https://github.com/rebeccahughes)", "author": "Rebecca Hughes <rebecca@learnium.net> (https://github.com/rebeccahughes)",
"contributors": [{ "contributors": [
"name": "Gant Laborde", {
"email": "gant@infinite.red", "name": "Gant Laborde",
"url": "https://github.com/gantman" "email": "gant@infinite.red",
}], "url": "https://github.com/gantman"
"license": "MIT" }
],
"license": "MIT",
"devDependencies": {
"np": "^2.16.0"
}
} }
...@@ -7,7 +7,6 @@ using System.Text.RegularExpressions; ...@@ -7,7 +7,6 @@ using System.Text.RegularExpressions;
using Windows.ApplicationModel; using Windows.ApplicationModel;
using Windows.UI.Xaml; using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Controls;
using WinRTXamlToolkit.Controls;
namespace RNDeviceInfo namespace RNDeviceInfo
{ {
...@@ -27,7 +26,7 @@ namespace RNDeviceInfo ...@@ -27,7 +26,7 @@ namespace RNDeviceInfo
} }
private bool IsEmulator(string model) private bool IsEmulator(string model)
{ {
Regex rgx = new Regex("(?i:virtual)"); Regex rgx = new Regex("(?i:virtual)");
return rgx.IsMatch(model); return rgx.IsMatch(model);
} }
...@@ -38,6 +37,11 @@ namespace RNDeviceInfo ...@@ -38,6 +37,11 @@ namespace RNDeviceInfo
return !rgx.IsMatch(os); return !rgx.IsMatch(os);
} }
private bool is24Hour()
{
return DateTimeFormatInfo.CurrentInfo.ShortTimePattern.Contains("H");
}
public override IReadOnlyDictionary<string, object> Constants public override IReadOnlyDictionary<string, object> Constants
{ {
...@@ -84,7 +88,7 @@ namespace RNDeviceInfo ...@@ -84,7 +88,7 @@ namespace RNDeviceInfo
model = deviceInfo.SystemProductName; model = deviceInfo.SystemProductName;
hardwareVersion = deviceInfo.SystemHardwareVersion; hardwareVersion = deviceInfo.SystemHardwareVersion;
os = deviceInfo.OperatingSystem; os = deviceInfo.OperatingSystem;
string deviceFamilyVersion = Windows.System.Profile.AnalyticsInfo.VersionInfo.DeviceFamilyVersion; string deviceFamilyVersion = Windows.System.Profile.AnalyticsInfo.VersionInfo.DeviceFamilyVersion;
ulong version2 = ulong.Parse(deviceFamilyVersion); ulong version2 = ulong.Parse(deviceFamilyVersion);
...@@ -100,6 +104,7 @@ namespace RNDeviceInfo ...@@ -100,6 +104,7 @@ namespace RNDeviceInfo
constants["deviceName"] = deviceName; constants["deviceName"] = deviceName;
constants["systemName"] = "Windows"; constants["systemName"] = "Windows";
constants["systemVersion"] = osVersion; constants["systemVersion"] = osVersion;
constants["apiLevel"] = "not available";
constants["model"] = model; constants["model"] = model;
constants["brand"] = model; constants["brand"] = model;
constants["deviceId"] = hardwareVersion; constants["deviceId"] = hardwareVersion;
...@@ -113,6 +118,8 @@ namespace RNDeviceInfo ...@@ -113,6 +118,8 @@ namespace RNDeviceInfo
constants["timezone"] = TimeZoneInfo.Local.Id; constants["timezone"] = TimeZoneInfo.Local.Id;
constants["isEmulator"] = IsEmulator(model); constants["isEmulator"] = IsEmulator(model);
constants["isTablet"] = IsTablet(os); constants["isTablet"] = IsTablet(os);
constants["carrier"] = "not available";
constants["is24Hour"] = is24Hour();
return constants; return constants;
} }
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment