diff --git a/Libraries/Image/__tests__/resolveAssetSource-test.js b/Libraries/Image/__tests__/resolveAssetSource-test.js index 81fdb2b08dc9d8..b406316a1a906a 100644 --- a/Libraries/Image/__tests__/resolveAssetSource-test.js +++ b/Libraries/Image/__tests__/resolveAssetSource-test.js @@ -10,7 +10,8 @@ jest .dontMock('AssetRegistry') - .dontMock('../resolveAssetSource'); + .dontMock('../resolveAssetSource') + .dontMock('../../../local-cli/bundle/assetPathUtils'); var AssetRegistry = require('AssetRegistry'); var Platform = require('Platform'); @@ -132,10 +133,10 @@ describe('resolveAssetSource', () => { }); }); - describe('bundle was loaded from file on Android', () => { + describe('bundle was loaded from assets on Android', () => { beforeEach(() => { NativeModules.SourceCode.scriptURL = - 'file:///Path/To/Simulator/main.bundle'; + 'assets://Path/To/Simulator/main.bundle'; Platform.OS = 'android'; }); @@ -159,6 +160,34 @@ describe('resolveAssetSource', () => { }); }); }); + + describe('bundle was loaded from file on Android', () => { + beforeEach(() => { + NativeModules.SourceCode.scriptURL = + 'file:///sdcard/Path/To/Simulator/main.bundle'; + Platform.OS = 'android'; + }); + + it('uses pre-packed image', () => { + expectResolvesAsset({ + __packager_asset: true, + fileSystemLocation: '/root/app/module/a', + httpServerLocation: '/assets/AwesomeModule/Subdir', + width: 100, + height: 200, + scales: [1], + hash: '5b6f00f', + name: '!@Logo#1_€', + type: 'png', + }, { + __packager_asset: true, + width: 100, + height: 200, + uri: 'file:///sdcard/Path/To/Simulator/drawable-mdpi/awesomemodule_subdir_logo1_.png', + scale: 1, + }); + }); + }); }); diff --git a/Libraries/Image/resolveAssetSource.js b/Libraries/Image/resolveAssetSource.js index 638b72ce06e40b..6527e91df096ea 100644 --- a/Libraries/Image/resolveAssetSource.js +++ b/Libraries/Image/resolveAssetSource.js @@ -25,6 +25,7 @@ var AssetRegistry = require('AssetRegistry'); var PixelRatio = require('PixelRatio'); var Platform = require('Platform'); var SourceCode = require('NativeModules').SourceCode; +var assetPathUtils = require('../../local-cli/bundle/assetPathUtils'); var _serverURL, _offlinePath; @@ -62,18 +63,18 @@ function getOfflinePath() { * Returns the path at which the asset can be found in the archive */ function getPathInArchive(asset) { + var offlinePath = getOfflinePath(); if (Platform.OS === 'android') { - var assetDir = getBasePath(asset); + if (offlinePath) { + // E.g. 'file:///sdcard/AwesomeModule/drawable-mdpi/icon.png' + return 'file://' + offlinePath + getAssetPathInDrawableFolder(asset); + } // E.g. 'assets_awesomemodule_icon' // The Android resource system picks the correct scale. - return (assetDir + '/' + asset.name) - .toLowerCase() - .replace(/\//g, '_') // Encode folder structure in file name - .replace(/([^a-z0-9_])/g, '') // Remove illegal chars - .replace(/^assets_/, ''); // Remove "assets_" prefix + return assetPathUtils.getAndroidResourceIdentifier(asset); } else { - // E.g. 'assets/AwesomeModule/icon@2x.png' - return getOfflinePath() + getScaledAssetPath(asset); + // E.g. '/assets/AwesomeModule/icon@2x.png' + return offlinePath + getScaledAssetPath(asset); } } @@ -86,29 +87,26 @@ function getPathOnDevserver(devServerUrl, asset) { '&hash=' + asset.hash; } -/** - * Returns a path like 'assets/AwesomeModule' - */ -function getBasePath(asset) { - // TODO(frantic): currently httpServerLocation is used both as - // path in http URL and path within IPA. Should we have zipArchiveLocation? - var path = asset.httpServerLocation; - if (path[0] === '/') { - path = path.substr(1); - } - return path; -} - /** * Returns a path like 'assets/AwesomeModule/icon@2x.png' */ function getScaledAssetPath(asset) { var scale = pickScale(asset.scales, PixelRatio.get()); var scaleSuffix = scale === 1 ? '' : '@' + scale + 'x'; - var assetDir = getBasePath(asset); + var assetDir = assetPathUtils.getBasePath(asset); return assetDir + '/' + asset.name + scaleSuffix + '.' + asset.type; } +/** + * Returns a path like 'drawable-mdpi/icon.png' + */ +function getAssetPathInDrawableFolder(asset) { + var scale = pickScale(asset.scales, PixelRatio.get()); + var drawbleFolder = assetPathUtils.getAndroidDrawableFolderName(asset, scale); + var fileName = assetPathUtils.getAndroidResourceIdentifier(asset); + return drawbleFolder + '/' + fileName + '.' + asset.type; +} + function pickScale(scales: Array, deviceScale: number): number { // Packager guarantees that `scales` array is sorted for (var i = 0; i < scales.length; i++) { diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/JSBundleLoader.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/JSBundleLoader.java index 90d97e17ef85cc..db9fb05df8eb7c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/JSBundleLoader.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/JSBundleLoader.java @@ -37,7 +37,7 @@ public void loadScript(ReactBridge bridge) { @Override public String getSourceUrl() { - return fileName; + return (fileName.startsWith("assets://") ? "" : "file://") + fileName; } }; } diff --git a/local-cli/bundle/__tests__/getAssetDestPathAndroid-test.js b/local-cli/bundle/__tests__/getAssetDestPathAndroid-test.js index 1613b5b3656ad1..3f58299549cc05 100644 --- a/local-cli/bundle/__tests__/getAssetDestPathAndroid-test.js +++ b/local-cli/bundle/__tests__/getAssetDestPathAndroid-test.js @@ -8,7 +8,9 @@ */ 'use strict'; -jest.dontMock('../getAssetDestPathAndroid'); +jest + .dontMock('../getAssetDestPathAndroid') + .dontMock('../assetPathUtils'); const getAssetDestPathAndroid = require('../getAssetDestPathAndroid'); diff --git a/local-cli/bundle/assetPathUtils.js b/local-cli/bundle/assetPathUtils.js new file mode 100644 index 00000000000000..cf14b2c62cf0dc --- /dev/null +++ b/local-cli/bundle/assetPathUtils.js @@ -0,0 +1,56 @@ +/** + * 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. + */ +'use strict'; + +function getAndroidAssetSuffix(scale) { + switch (scale) { + case 0.75: return 'ldpi'; + case 1: return 'mdpi'; + case 1.5: return 'hdpi'; + case 2: return 'xhdpi'; + case 3: return 'xxhdpi'; + case 4: return 'xxxhdpi'; + } +} + +function getAndroidDrawableFolderName(asset, scale) { + var suffix = getAndroidAssetSuffix(scale); + if (!suffix) { + throw new Error( + 'Don\'t know which android drawable suffix to use for asset: ' + + JSON.stringify(asset) + ); + } + const androidFolder = 'drawable-' + suffix; + return androidFolder; +} + +function getAndroidResourceIdentifier(asset) { + var folderPath = getBasePath(asset); + return (folderPath + '/' + asset.name) + .toLowerCase() + .replace(/\//g, '_') // Encode folder structure in file name + .replace(/([^a-z0-9_])/g, '') // Remove illegal chars + .replace(/^assets_/, ''); // Remove "assets_" prefix +} + +function getBasePath(asset) { + var basePath = asset.httpServerLocation; + if (basePath[0] === '/') { + basePath = basePath.substr(1); + } + return basePath; +} + +module.exports = { + getAndroidAssetSuffix: getAndroidAssetSuffix, + getAndroidDrawableFolderName: getAndroidDrawableFolderName, + getAndroidResourceIdentifier: getAndroidResourceIdentifier, + getBasePath: getBasePath +}; \ No newline at end of file diff --git a/local-cli/bundle/getAssetDestPathAndroid.js b/local-cli/bundle/getAssetDestPathAndroid.js index 3e039f54fd5ed7..f462ba5e81d179 100644 --- a/local-cli/bundle/getAssetDestPathAndroid.js +++ b/local-cli/bundle/getAssetDestPathAndroid.js @@ -9,34 +9,11 @@ 'use strict'; const path = require('path'); - -function getAndroidAssetSuffix(scale) { - switch (scale) { - case 0.75: return 'ldpi'; - case 1: return 'mdpi'; - case 1.5: return 'hdpi'; - case 2: return 'xhdpi'; - case 3: return 'xxhdpi'; - case 4: return 'xxxhdpi'; - } -} +const assetPathUtils = require('./assetPathUtils'); function getAssetDestPathAndroid(asset, scale) { - var suffix = getAndroidAssetSuffix(scale); - if (!suffix) { - throw new Error( - 'Don\'t know which android drawable suffix to use for asset: ' + - JSON.stringify(asset) - ); - } - const androidFolder = 'drawable-' + suffix; - // TODO: reuse this logic from https://fburl.com/151101135 - const fileName = (asset.httpServerLocation.substr(1) + '/' + asset.name) - .toLowerCase() - .replace(/\//g, '_') // Encode folder structure in file name - .replace(/([^a-z0-9_])/g, '') // Remove illegal chars - .replace(/^assets_/, ''); // Remove "assets_" prefix - + const androidFolder = assetPathUtils.getAndroidDrawableFolderName(asset, scale); + const fileName = assetPathUtils.getAndroidResourceIdentifier(asset); return path.join(androidFolder, fileName + '.' + asset.type); }