Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 32 additions & 3 deletions Libraries/Image/__tests__/resolveAssetSource-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@

jest
.dontMock('AssetRegistry')
.dontMock('../resolveAssetSource');
.dontMock('../resolveAssetSource')
.dontMock('../../../local-cli/bundle/assetPathUtils');

var AssetRegistry = require('AssetRegistry');
var Platform = require('Platform');
Expand Down Expand Up @@ -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';
});

Expand All @@ -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,
});
});
});

});

Expand Down
42 changes: 20 additions & 22 deletions Libraries/Image/resolveAssetSource.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still not sure about this dependency.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like this (assuming it works). Before this change we had two places that compute image file name given set of its properties (platform, scale, etc.). I would recommend extracting this change in a separate PR for easier review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since both resolveAssetSource.js and getAssetDestPath.js is going to depend on this refactor, extracting this change is going to be hard and confusing because then one PR would rely on the other PR getting merged.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's one of the reasons I hate Github's review flow (Phabricator's is so much better)


var _serverURL, _offlinePath;

Expand Down Expand Up @@ -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);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can you structure this block more like the iOS side encapsulating the offlinepath and scaledassetpath notions. Bonus points for unifying the code.

The android res handling is is going to need to be different, you can have a separate getResourcePath or something.

// E.g. 'assets_awesomemodule_icon'
// The Android resource system picks the correct scale.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

quotes: Strings must use singlequote.

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);
}
}

Expand All @@ -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<number>, deviceScale: number): number {
// Packager guarantees that `scales` array is sorted
for (var i = 0; i < scales.length; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public void loadScript(ReactBridge bridge) {

@Override
public String getSourceUrl() {
return fileName;
return (fileName.startsWith("assets://") ? "" : "file://") + fileName;
}
};
}
Expand Down
4 changes: 3 additions & 1 deletion local-cli/bundle/__tests__/getAssetDestPathAndroid-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
*/
'use strict';

jest.dontMock('../getAssetDestPathAndroid');
jest
.dontMock('../getAssetDestPathAndroid')
.dontMock('../assetPathUtils');

const getAssetDestPathAndroid = require('../getAssetDestPathAndroid');

Expand Down
56 changes: 56 additions & 0 deletions local-cli/bundle/assetPathUtils.js
Original file line number Diff line number Diff line change
@@ -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
};
29 changes: 3 additions & 26 deletions local-cli/bundle/getAssetDestPathAndroid.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down