[ReactNative] Resolve assets on Android

This commit is contained in:
Martin Konicek 2015-05-12 09:50:41 -07:00 committed by Christopher Chedeau
parent 369e30f685
commit 92fc92835f
2 changed files with 119 additions and 22 deletions

View File

@ -12,9 +12,10 @@ jest
.dontMock('AssetRegistry')
.dontMock('../resolveAssetSource');
var resolveAssetSource;
var SourceCode;
var AssetRegistry;
var Platform;
var SourceCode;
var resolveAssetSource;
function expectResolvesAsset(input, expectedSource) {
var assetId = AssetRegistry.registerAsset(input);
@ -24,8 +25,9 @@ function expectResolvesAsset(input, expectedSource) {
describe('resolveAssetSource', () => {
beforeEach(() => {
jest.resetModuleRegistry();
SourceCode = require('NativeModules').SourceCode;
AssetRegistry = require('AssetRegistry');
Platform = require('Platform');
SourceCode = require('NativeModules').SourceCode;
resolveAssetSource = require('../resolveAssetSource');
});
@ -59,7 +61,7 @@ describe('resolveAssetSource', () => {
expect(resolveAssetSource('nonsense')).toBe(null);
});
describe('bundle was loaded from network', () => {
describe('bundle was loaded from network (DEV)', () => {
beforeEach(() => {
SourceCode.scriptURL = 'http://10.0.0.1:8081/main.bundle';
});
@ -104,9 +106,21 @@ describe('resolveAssetSource', () => {
});
describe('bundle was loaded from file', () => {
describe('bundle was loaded from file (PROD) on iOS', () => {
var originalDevMode;
var originalPlatform;
beforeEach(() => {
SourceCode.scriptURL = 'file:///Path/To/Simulator/main.bundle';
originalDevMode = __DEV__;
originalPlatform = Platform.OS;
__DEV__ = false;
Platform.OS = 'ios';
});
afterEach(() => {
__DEV__ = originalDevMode;
Platform.OS = originalPlatform;
});
it('uses pre-packed image', () => {
@ -129,6 +143,43 @@ describe('resolveAssetSource', () => {
});
});
describe('bundle was loaded from file (PROD) on Android', () => {
var originalDevMode;
var originalPlatform;
beforeEach(() => {
SourceCode.scriptURL = 'file:///Path/To/Simulator/main.bundle';
originalDevMode = __DEV__;
originalPlatform = Platform.OS;
__DEV__ = false;
Platform.OS = 'android';
});
afterEach(() => {
__DEV__ = originalDevMode;
Platform.OS = originalPlatform;
});
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_€', // Invalid chars shouldn't get passed to native
type: 'png',
}, {
isStatic: true,
width: 100,
height: 200,
uri: 'assets_awesomemodule_subdir_logo1_',
});
});
});
});
describe('resolveAssetSource.pickScale', () => {

View File

@ -7,22 +7,31 @@
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule resolveAssetSource
*
* Resolves an asset into a `source` for `Image`.
*/
'use strict';
var AssetRegistry = require('AssetRegistry');
var PixelRatio = require('PixelRatio');
var Platform = require('Platform');
var SourceCode = require('NativeModules').SourceCode;
var _serverURL;
function getServerURL() {
function getDevServerURL() {
if (!__DEV__) {
// In prod we want assets to be loaded from the archive
return null;
}
if (_serverURL === undefined) {
var scriptURL = SourceCode.scriptURL;
var match = scriptURL && scriptURL.match(/^https?:\/\/.*?\//);
if (match) {
// Bundle was loaded from network
_serverURL = match[0];
} else {
// Bundle was loaded from file
_serverURL = null;
}
}
@ -30,6 +39,55 @@ function getServerURL() {
return _serverURL;
}
/**
* Returns the path at which the asset can be found in the archive
*/
function getPathInArchive(asset) {
if (Platform.OS === 'android') {
var assetDir = getBasePath(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
} else {
// E.g. 'assets/AwesomeModule/icon@2x.png'
return getScaledAssetPath(asset);
}
}
/**
* Returns an absolute URL which can be used to fetch the asset
* from the devserver
*/
function getPathOnDevserver(devServerUrl, asset) {
return devServerUrl + getScaledAssetPath(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);
return assetDir + '/' + asset.name + scaleSuffix + '.' + asset.type;
}
function pickScale(scales, deviceScale) {
// Packager guarantees that `scales` array is sorted
for (var i = 0; i < scales.length; i++) {
@ -58,31 +116,19 @@ function resolveAssetSource(source) {
}
function assetToImageSource(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);
}
var scale = pickScale(asset.scales, PixelRatio.get());
var scaleSuffix = scale === 1 ? '' : '@' + scale + 'x';
var fileName = asset.name + scaleSuffix + '.' + asset.type;
var serverURL = getServerURL();
if (serverURL) {
var devServerURL = getDevServerURL();
if (devServerURL) {
return {
width: asset.width,
height: asset.height,
uri: serverURL + path + '/' + fileName +
'?hash=' + asset.hash,
uri: getPathOnDevserver(devServerURL, asset),
isStatic: false,
};
} else {
return {
width: asset.width,
height: asset.height,
uri: path + '/' + fileName,
uri: getPathInArchive(asset),
isStatic: true,
};
}