From 429fcc8cab3ca877275d7deb1040fdff17a414c7 Mon Sep 17 00:00:00 2001 From: Dmitry Zakharov Date: Mon, 29 Jan 2018 11:07:37 -0800 Subject: [PATCH] Migrate DeviceInfoModule from RN Module to Native Function. Reviewed By: danzimm Differential Revision: D6750934 fbshipit-source-id: f453801737e41632c6b84ff894e7f0eb66b575dc --- Libraries/Utilities/Dimensions.js | 84 ++++++++++++++++----------- React/Modules/RCTDeviceInfo.m | 23 ++++---- React/React.xcodeproj/project.pbxproj | 14 +++++ React/UIUtils/RCTUIUtils.h | 37 ++++++++++++ React/UIUtils/RCTUIUtils.m | 52 +++++++++++++++++ 5 files changed, 165 insertions(+), 45 deletions(-) create mode 100644 React/UIUtils/RCTUIUtils.h create mode 100644 React/UIUtils/RCTUIUtils.m diff --git a/Libraries/Utilities/Dimensions.js b/Libraries/Utilities/Dimensions.js index cbb33d41e..a0fd7f308 100644 --- a/Libraries/Utilities/Dimensions.js +++ b/Libraries/Utilities/Dimensions.js @@ -11,7 +11,6 @@ */ 'use strict'; -var DeviceInfo = require('DeviceInfo'); var EventEmitter = require('EventEmitter'); var Platform = require('Platform'); var RCTDeviceEventEmitter = require('RCTDeviceEventEmitter'); @@ -21,22 +20,59 @@ var invariant = require('fbjs/lib/invariant'); var eventEmitter = new EventEmitter(); var dimensionsInitialized = false; var dimensions = {}; + +var dimensionsProvider: ?(() => {[key:string]: Object}); + class Dimensions { - /** - * This should only be called from native code by sending the - * didUpdateDimensions event. - * - * @param {object} dims Simple string-keyed object of dimensions to set - */ - static set(dims: {[key:string]: any}): void { + static setProvider(value: () => {[key:string]: Object}): void { + dimensionsProvider = value; + dimensionsInitialized = false; + dimensions = {}; + } + + static getDimensions(): {[key:string]: Object} { + if (dimensionsInitialized) { + return dimensions; + } + // We calculate the window dimensions in JS so that we don't encounter loss of // precision in transferring the dimensions (which could be non-integers) over // the bridge. + const dims = (dimensionsProvider || defaultDimProvider)(); + const result = Dimensions.updateDimensions(dims); + RCTDeviceEventEmitter.addListener('didUpdateDimensions', function(update) { + Dimensions.updateDimensions(update); + }); + return result; + } + + /** + * Initial dimensions are set before `runApplication` is called so they should + * be available before any other require's are run, but may be updated later. + * + * Note: Although dimensions are available immediately, they may change (e.g + * due to device rotation) so any rendering logic or styles that depend on + * these constants should try to call this function on every render, rather + * than caching the value (for example, using inline styles rather than + * setting a value in a `StyleSheet`). + * + * Example: `var {height, width} = Dimensions.get('window');` + * + * @param {string} dim Name of dimension as defined when calling `set`. + * @returns {Object?} Value for the dimension. + */ + static get(dim: string): Object { + const dims = Dimensions.getDimensions(); + invariant(dims[dim], 'No dimension set for key ' + dim); + return dims[dim]; + } + + static updateDimensions(dims: ?{[key:string]: Object}): {[key:string]: Object} { if (dims && dims.windowPhysicalPixels) { // parse/stringify => Clone hack dims = JSON.parse(JSON.stringify(dims)); - var windowPhysicalPixels = dims.windowPhysicalPixels; + const windowPhysicalPixels = dims.windowPhysicalPixels; dims.window = { width: windowPhysicalPixels.width / windowPhysicalPixels.scale, height: windowPhysicalPixels.height / windowPhysicalPixels.scale, @@ -45,7 +81,7 @@ class Dimensions { }; if (Platform.OS === 'android') { // Screen and window dimensions are different on android - var screenPhysicalPixels = dims.screenPhysicalPixels; + const screenPhysicalPixels = dims.screenPhysicalPixels; dims.screen = { width: screenPhysicalPixels.width / screenPhysicalPixels.scale, height: screenPhysicalPixels.height / screenPhysicalPixels.scale, @@ -72,26 +108,7 @@ class Dimensions { } else { dimensionsInitialized = true; } - } - - /** - * Initial dimensions are set before `runApplication` is called so they should - * be available before any other require's are run, but may be updated later. - * - * Note: Although dimensions are available immediately, they may change (e.g - * due to device rotation) so any rendering logic or styles that depend on - * these constants should try to call this function on every render, rather - * than caching the value (for example, using inline styles rather than - * setting a value in a `StyleSheet`). - * - * Example: `var {height, width} = Dimensions.get('window');` - * - * @param {string} dim Name of dimension as defined when calling `set`. - * @returns {Object?} Value for the dimension. - */ - static get(dim: string): Object { - invariant(dimensions[dim], 'No dimension set for key ' + dim); - return dimensions[dim]; + return dimensions; } /** @@ -128,9 +145,8 @@ class Dimensions { } } -Dimensions.set(DeviceInfo.Dimensions); -RCTDeviceEventEmitter.addListener('didUpdateDimensions', function(update) { - Dimensions.set(update); -}); +function defaultDimProvider(): {[key:string]: Object} { + return require('DeviceInfo').Dimensions; +} module.exports = Dimensions; diff --git a/React/Modules/RCTDeviceInfo.m b/React/Modules/RCTDeviceInfo.m index 1baab7551..6eb028d9b 100644 --- a/React/Modules/RCTDeviceInfo.m +++ b/React/Modules/RCTDeviceInfo.m @@ -12,6 +12,7 @@ #import "RCTAccessibilityManager.h" #import "RCTAssert.h" #import "RCTEventDispatcher.h" +#import "RCTUIUtils.h" #import "RCTUtils.h" @implementation RCTDeviceInfo { @@ -72,18 +73,18 @@ static NSDictionary *RCTExportedDimensions(RCTBridge *bridge) { RCTAssertMainQueue(); - // Don't use RCTScreenSize since it the interface orientation doesn't apply to it - CGRect screenSize = [[UIScreen mainScreen] bounds]; - NSDictionary *dims = @{ - @"width": @(screenSize.size.width), - @"height": @(screenSize.size.height), - @"scale": @(RCTScreenScale()), - @"fontScale": @(bridge.accessibilityManager.multiplier) - }; + RCTDimensions dimensions = RCTGetDimensions(bridge.accessibilityManager.multiplier); + typeof (dimensions.window) window = dimensions.window; // Window and Screen are considered equal for iOS. + NSDictionary *dims = @{ + @"width": @(window.width), + @"height": @(window.height), + @"scale": @(window.scale), + @"fontScale": @(window.fontScale) + }; return @{ - @"window": dims, - @"screen": dims - }; + @"window": dims, + @"screen": dims + }; } - (void)dealloc diff --git a/React/React.xcodeproj/project.pbxproj b/React/React.xcodeproj/project.pbxproj index f41e625e7..788ba96d2 100644 --- a/React/React.xcodeproj/project.pbxproj +++ b/React/React.xcodeproj/project.pbxproj @@ -1203,6 +1203,7 @@ EBF21BFE1FC499840052F4D5 /* InspectorInterfaces.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = EBF21BBA1FC498270052F4D5 /* InspectorInterfaces.h */; }; EBF21BFF1FC4998E0052F4D5 /* InspectorInterfaces.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EBF21BBB1FC498270052F4D5 /* InspectorInterfaces.cpp */; }; EBF21C001FC499A80052F4D5 /* InspectorInterfaces.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EBF21BBB1FC498270052F4D5 /* InspectorInterfaces.cpp */; }; + F1EFDA50201F661000EE6E4C /* RCTUIUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = F1EFDA4E201F660F00EE6E4C /* RCTUIUtils.m */; }; FEFAAC9E1FDB89B50057BBE0 /* RCTRedBoxExtraDataViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FEFAAC9C1FDB89B40057BBE0 /* RCTRedBoxExtraDataViewController.m */; }; FEFAAC9F1FDB89B50057BBE0 /* RCTRedBoxExtraDataViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = FEFAAC9D1FDB89B40057BBE0 /* RCTRedBoxExtraDataViewController.h */; }; /* End PBXBuildFile section */ @@ -2279,6 +2280,8 @@ EBF21BBB1FC498270052F4D5 /* InspectorInterfaces.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorInterfaces.cpp; sourceTree = ""; }; EBF21BDC1FC498900052F4D5 /* libjsinspector.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libjsinspector.a; sourceTree = BUILT_PRODUCTS_DIR; }; EBF21BFA1FC4989A0052F4D5 /* libjsinspector-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libjsinspector-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + F1EFDA4E201F660F00EE6E4C /* RCTUIUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTUIUtils.m; sourceTree = ""; }; + F1EFDA4F201F660F00EE6E4C /* RCTUIUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTUIUtils.h; sourceTree = ""; }; FEFAAC9C1FDB89B40057BBE0 /* RCTRedBoxExtraDataViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTRedBoxExtraDataViewController.m; sourceTree = ""; }; FEFAAC9D1FDB89B40057BBE0 /* RCTRedBoxExtraDataViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTRedBoxExtraDataViewController.h; sourceTree = ""; }; /* End PBXFileReference section */ @@ -2888,6 +2891,7 @@ 657734881EE8352500A0E9EA /* Inspector */, 13B07FE01A69315300A75B9A /* Modules */, 1450FF7F1BCFF28A00208362 /* Profiler */, + F1EFDA4D201F660F00EE6E4C /* UIUtils */, 13B07FF31A6947C200A75B9A /* Views */, ); name = React; @@ -3048,6 +3052,15 @@ path = jsinspector; sourceTree = ""; }; + F1EFDA4D201F660F00EE6E4C /* UIUtils */ = { + isa = PBXGroup; + children = ( + F1EFDA4F201F660F00EE6E4C /* RCTUIUtils.h */, + F1EFDA4E201F660F00EE6E4C /* RCTUIUtils.m */, + ); + path = UIUtils; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -4508,6 +4521,7 @@ 13AB90C11B6FA36700713B4F /* RCTComponentData.m in Sources */, 13B0801B1A69489C00A75B9A /* RCTNavigatorManager.m in Sources */, 916F9C2D1F743F57002E5920 /* RCTModalManager.m in Sources */, + F1EFDA50201F661000EE6E4C /* RCTUIUtils.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/React/UIUtils/RCTUIUtils.h b/React/UIUtils/RCTUIUtils.h new file mode 100644 index 000000000..5f6f0e022 --- /dev/null +++ b/React/UIUtils/RCTUIUtils.h @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +#ifdef __cplusplus +extern "C" { +#endif + +// Get window and screen dimensions +typedef struct { + struct { + CGFloat width, height, scale, fontScale; + } window, screen; +} RCTDimensions; +extern __attribute__((visibility("default"))) +RCTDimensions RCTGetDimensions(CGFloat fontScale); + +// Get font size multiplier for font base size (Large) by content size category +extern __attribute__((visibility("default"))) +CGFloat RCTGetMultiplierForContentSizeCategory(UIContentSizeCategory category); + +#ifdef __cplusplus +} +#endif + +NS_ASSUME_NONNULL_END diff --git a/React/UIUtils/RCTUIUtils.m b/React/UIUtils/RCTUIUtils.m new file mode 100644 index 000000000..ad9d44dd4 --- /dev/null +++ b/React/UIUtils/RCTUIUtils.m @@ -0,0 +1,52 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "RCTUIUtils.h" + +RCTDimensions RCTGetDimensions(CGFloat fontScale) +{ + UIScreen *mainScreen = UIScreen.mainScreen; + CGSize screenSize = mainScreen.bounds.size; + RCTDimensions result; + typeof (result.window) dims = { + .width = screenSize.width, + .height = screenSize.height, + .scale = mainScreen.scale, + .fontScale = fontScale + }; + result.window = dims; + result.screen = dims; + + return result; +} + +CGFloat RCTGetMultiplierForContentSizeCategory(UIContentSizeCategory category) +{ + static NSDictionary *multipliers = nil; + static dispatch_once_t token; + dispatch_once(&token, ^{ + multipliers = @{ + UIContentSizeCategoryExtraSmall: @0.823, + UIContentSizeCategorySmall: @0.882, + UIContentSizeCategoryMedium: @0.941, + UIContentSizeCategoryLarge: @1.0, + UIContentSizeCategoryExtraLarge: @1.118, + UIContentSizeCategoryExtraExtraLarge: @1.235, + UIContentSizeCategoryExtraExtraExtraLarge: @1.353, + UIContentSizeCategoryAccessibilityMedium: @1.786, + UIContentSizeCategoryAccessibilityLarge: @2.143, + UIContentSizeCategoryAccessibilityExtraLarge: @2.643, + UIContentSizeCategoryAccessibilityExtraExtraLarge: @3.143, + UIContentSizeCategoryAccessibilityExtraExtraExtraLarge: @3.571 + }; + }); + + double value = multipliers[category].doubleValue; + return value > 0.0 ? value : 1.0; +}