1
0
Code Issues Pull Requests Packages Projects Releases Wiki Activity GitHub Gitee
This commit is contained in:
tdzl2003 2016-04-04 23:02:28 +08:00
parent b9ea739a44
commit 063c14ab45
10 changed files with 394 additions and 346 deletions

@ -9,6 +9,7 @@ import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response; import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseBody; import com.squareup.okhttp.ResponseBody;
import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONTokener; import org.json.JSONTokener;
@ -212,45 +213,37 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
copyFilesWithBlacklist("", from, to, blackList); copyFilesWithBlacklist("", from, to, blackList);
} }
private void doDownload(DownloadTaskParams param) { private void doDownload(DownloadTaskParams param) throws IOException {
try { downloadFile(param.url, param.zipFilePath);
downloadFile(param.url, param.zipFilePath);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.zipFilePath))); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.zipFilePath)));
ZipEntry ze; ZipEntry ze;
String filename; String filename;
removeDirectory(param.unzipDirectory); removeDirectory(param.unzipDirectory);
param.unzipDirectory.mkdirs(); param.unzipDirectory.mkdirs();
while ((ze = zis.getNextEntry()) != null) while ((ze = zis.getNextEntry()) != null)
{ {
String fn = ze.getName(); String fn = ze.getName();
File fmd = new File(param.unzipDirectory, fn); File fmd = new File(param.unzipDirectory, fn);
if (UpdateContext.DEBUG) {
Log.d("RNUpdate", "Unzipping " + fn);
}
if (ze.isDirectory()) {
fmd.mkdirs();
continue;
}
unzipToFile(zis, fmd);
}
zis.close();
if (UpdateContext.DEBUG) { if (UpdateContext.DEBUG) {
Log.d("RNUpdate", "Unzip finished"); Log.d("RNUpdate", "Unzipping " + fn);
} }
} catch (Throwable e) { if (ze.isDirectory()) {
if (UpdateContext.DEBUG) { fmd.mkdirs();
e.printStackTrace(); continue;
} }
param.listener.onDownloadFailed(e);
unzipToFile(zis, fmd);
}
zis.close();
if (UpdateContext.DEBUG) {
Log.d("RNUpdate", "Unzip finished");
} }
} }
@ -268,162 +261,161 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, Void, Void> {
} }
} }
private void doPatchFromApk(DownloadTaskParams param) { private void doPatchFromApk(DownloadTaskParams param) throws IOException, JSONException {
try { downloadFile(param.url, param.zipFilePath);
downloadFile(param.url, param.zipFilePath);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.zipFilePath))); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.zipFilePath)));
ZipEntry ze; ZipEntry ze;
int count; int count;
String filename; String filename;
removeDirectory(param.unzipDirectory); removeDirectory(param.unzipDirectory);
param.unzipDirectory.mkdirs(); param.unzipDirectory.mkdirs();
while ((ze = zis.getNextEntry()) != null) while ((ze = zis.getNextEntry()) != null)
{ {
String fn = ze.getName(); String fn = ze.getName();
if (fn.equals("__diff.json")) { if (fn.equals("__diff.json")) {
// copy files from assets // copy files from assets
byte[] bytes = readBytes(zis); byte[] bytes = readBytes(zis);
String json = new String(bytes, "UTF-8"); String json = new String(bytes, "UTF-8");
JSONObject obj = (JSONObject)new JSONTokener(json).nextValue(); JSONObject obj = (JSONObject)new JSONTokener(json).nextValue();
JSONObject copies = obj.getJSONObject("copies"); JSONObject copies = obj.getJSONObject("copies");
Iterator<?> keys = copies.keys(); Iterator<?> keys = copies.keys();
while( keys.hasNext() ) { while( keys.hasNext() ) {
String to = (String)keys.next(); String to = (String)keys.next();
String from = copies.getString(to); String from = copies.getString(to);
if (from.isEmpty()) { if (from.isEmpty()) {
from = to; from = to;
}
copyFromResource(from, new File(param.unzipDirectory, to));
} }
continue; copyFromResource(from, new File(param.unzipDirectory, to));
} }
if (fn.equals("index.bundlejs.patch")) { continue;
// do bsdiff patch
byte[] patched = bsdiffPatch(readOriginBundle(), readBytes(zis));
FileOutputStream fout = new FileOutputStream(new File(param.unzipDirectory, "index.bundlejs"));
fout.write(patched);
fout.close();
continue;
}
File fmd = new File(param.unzipDirectory, fn);
if (UpdateContext.DEBUG) {
Log.d("RNUpdate", "Unzipping " + fn);
}
if (ze.isDirectory()) {
fmd.mkdirs();
continue;
}
unzipToFile(zis, fmd);
} }
if (fn.equals("index.bundlejs.patch")) {
// do bsdiff patch
byte[] patched = bsdiffPatch(readOriginBundle(), readBytes(zis));
zis.close(); FileOutputStream fout = new FileOutputStream(new File(param.unzipDirectory, "index.bundlejs"));
fout.write(patched);
fout.close();
continue;
}
File fmd = new File(param.unzipDirectory, fn);
if (UpdateContext.DEBUG) { if (UpdateContext.DEBUG) {
Log.d("RNUpdate", "Unzip finished"); Log.d("RNUpdate", "Unzipping " + fn);
} }
} catch (Throwable e) { if (ze.isDirectory()) {
if (UpdateContext.DEBUG) { fmd.mkdirs();
e.printStackTrace(); continue;
} }
param.listener.onDownloadFailed(e);
unzipToFile(zis, fmd);
} }
zis.close();
if (UpdateContext.DEBUG) {
Log.d("RNUpdate", "Unzip finished");
}
} }
private void doPatchFromPpk(DownloadTaskParams param) { private void doPatchFromPpk(DownloadTaskParams param) throws IOException, JSONException {
try { downloadFile(param.url, param.zipFilePath);
downloadFile(param.url, param.zipFilePath);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.zipFilePath))); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.zipFilePath)));
ZipEntry ze; ZipEntry ze;
int count; int count;
String filename; String filename;
removeDirectory(param.unzipDirectory); removeDirectory(param.unzipDirectory);
param.unzipDirectory.mkdirs(); param.unzipDirectory.mkdirs();
while ((ze = zis.getNextEntry()) != null) while ((ze = zis.getNextEntry()) != null)
{ {
String fn = ze.getName(); String fn = ze.getName();
if (fn.equals("__diff.json")) { if (fn.equals("__diff.json")) {
// copy files from assets // copy files from assets
byte[] bytes = readBytes(zis); byte[] bytes = readBytes(zis);
String json = new String(bytes, "UTF-8"); String json = new String(bytes, "UTF-8");
JSONObject obj = (JSONObject)new JSONTokener(json).nextValue(); JSONObject obj = (JSONObject)new JSONTokener(json).nextValue();
JSONObject copies = obj.getJSONObject("copies"); JSONObject copies = obj.getJSONObject("copies");
Iterator<?> keys = copies.keys(); Iterator<?> keys = copies.keys();
while( keys.hasNext() ) { while( keys.hasNext() ) {
String to = (String)keys.next(); String to = (String)keys.next();
String from = copies.getString(to); String from = copies.getString(to);
if (from.isEmpty()) { if (from.isEmpty()) {
from = to; from = to;
}
copyFile(new File(param.originDirectory, from), new File(param.unzipDirectory, to));
} }
JSONObject blackList = obj.getJSONObject("deletes"); copyFile(new File(param.originDirectory, from), new File(param.unzipDirectory, to));
copyFilesWithBlacklist(param.originDirectory, param.unzipDirectory, blackList);
continue;
} }
if (fn.equals("index.bundlejs.patch")) { JSONObject blackList = obj.getJSONObject("deletes");
// do bsdiff patch copyFilesWithBlacklist(param.originDirectory, param.unzipDirectory, blackList);
byte[] patched = bsdiffPatch(readFile(new File(param.originDirectory, "index.bundlejs")), readBytes(zis)); continue;
FileOutputStream fout = new FileOutputStream(new File(param.unzipDirectory, "index.bundlejs"));
fout.write(patched);
fout.close();
continue;
}
File fmd = new File(param.unzipDirectory, fn);
if (UpdateContext.DEBUG) {
Log.d("RNUpdate", "Unzipping " + fn);
}
if (ze.isDirectory()) {
fmd.mkdirs();
continue;
}
unzipToFile(zis, fmd);
} }
if (fn.equals("index.bundlejs.patch")) {
// do bsdiff patch
byte[] patched = bsdiffPatch(readFile(new File(param.originDirectory, "index.bundlejs")), readBytes(zis));
zis.close(); FileOutputStream fout = new FileOutputStream(new File(param.unzipDirectory, "index.bundlejs"));
fout.write(patched);
fout.close();
continue;
}
File fmd = new File(param.unzipDirectory, fn);
if (UpdateContext.DEBUG) { if (UpdateContext.DEBUG) {
Log.d("RNUpdate", "Unzip finished"); Log.d("RNUpdate", "Unzipping " + fn);
} }
} catch (Throwable e) { if (ze.isDirectory()) {
if (UpdateContext.DEBUG) { fmd.mkdirs();
e.printStackTrace(); continue;
} }
param.listener.onDownloadFailed(e);
unzipToFile(zis, fmd);
} }
zis.close();
if (UpdateContext.DEBUG) {
Log.d("RNUpdate", "Unzip finished");
}
}
private void doCleanUp(DownloadTaskParams param) {
} }
@Override @Override
protected Void doInBackground(DownloadTaskParams... params) { protected Void doInBackground(DownloadTaskParams... params) {
switch (params[0].type) { try {
case DownloadTaskParams.TASK_TYPE_FULL_DOWNLOAD: switch (params[0].type) {
doDownload(params[0]); case DownloadTaskParams.TASK_TYPE_FULL_DOWNLOAD:
break; doDownload(params[0]);
case DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK: break;
doPatchFromApk(params[0]); case DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK:
break; doPatchFromApk(params[0]);
case DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK: break;
doPatchFromPpk(params[0]); case DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK:
break; doPatchFromPpk(params[0]);
break;
case DownloadTaskParams.TASK_TYPE_CLEARUP:
doCleanUp(params[0]);
break;
}
params[0].listener.onDownloadCompleted();
} catch (Throwable e) {
if (UpdateContext.DEBUG) {
e.printStackTrace();
}
params[0].listener.onDownloadFailed(e);
} }
return null; return null;
} }

@ -12,6 +12,8 @@ class DownloadTaskParams {
static final int TASK_TYPE_PATCH_FROM_APK = 2; static final int TASK_TYPE_PATCH_FROM_APK = 2;
static final int TASK_TYPE_PATCH_FROM_PPK = 3; static final int TASK_TYPE_PATCH_FROM_PPK = 3;
static final int TASK_TYPE_CLEARUP = 0; //Keep hash & originHash
int type; int type;
String url; String url;
String hash; String hash;

@ -1,8 +1,12 @@
package cn.reactnative.modules.update; package cn.reactnative.modules.update;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import java.io.File; import java.io.File;
import java.net.URL;
/** /**
* Created by tdzl2003 on 3/31/16. * Created by tdzl2003 on 3/31/16.
@ -13,13 +17,6 @@ public class UpdateContext {
public static boolean DEBUG = false; public static boolean DEBUG = false;
private static UpdateContext currentInstance = null;
static UpdateContext instance() {
return currentInstance;
}
public UpdateContext(Context context) { public UpdateContext(Context context) {
this.context = context; this.context = context;
@ -28,12 +25,26 @@ public class UpdateContext {
if (!rootDir.exists()) { if (!rootDir.exists()) {
rootDir.mkdir(); rootDir.mkdir();
} }
this.sp = context.getSharedPreferences("update", Context.MODE_PRIVATE);
} }
public String getRootDir() { public String getRootDir() {
return rootDir.toString(); return rootDir.toString();
} }
public String getPackageVersion() {
PackageManager pm = context.getPackageManager();
PackageInfo pi = null;
try {
pi = pm.getPackageInfo(context.getPackageName(), 0);
return pi.versionName;
} catch( PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
public interface DownloadFileListener { public interface DownloadFileListener {
void onDownloadCompleted(); void onDownloadCompleted();
void onDownloadFailed(Throwable error); void onDownloadFailed(Throwable error);
@ -73,4 +84,102 @@ public class UpdateContext {
params.originDirectory = new File(rootDir, originHashName); params.originDirectory = new File(rootDir, originHashName);
new DownloadTask(context).execute(params); new DownloadTask(context).execute(params);
} }
private SharedPreferences sp;
public void switchVersion(String hashName) {
if (!new File(rootDir, hashName).exists()) {
throw new Error("Hash name not found, must download first.");
}
String lastVersion = getCurrentVersion();
SharedPreferences.Editor editor = sp.edit();
editor.putString("currentVersion", hashName);
if (lastVersion != null) {
editor.putString("lastVersion", hashName);
}
editor.putBoolean("firstTime", true);
editor.putBoolean("shouldRollback", false);
editor.putBoolean("rolledBack", false);
editor.apply();
}
public String getCurrentVersion() {
return sp.getString("currentVersion", null);
}
public boolean isFirstTime() {
return sp.getBoolean("firstTime", false);
}
public boolean isRolledBack() {
return sp.getBoolean("rolledBack", false);
}
public void clearFirstTimeMark() {
SharedPreferences.Editor editor = sp.edit();
editor.putBoolean("firstTime", false);
editor.putBoolean("shouldRollback", false);
editor.apply();
this.clearUp();
}
public static String getBundleUrl(Context context) {
return new UpdateContext(context.getApplicationContext()).getBundleUrl();
}
public static String getBundleUrl(Context context, String defaultAssetsUrl) {
return new UpdateContext(context.getApplicationContext()).getBundleUrl(defaultAssetsUrl);
}
public String getBundleUrl() {
return getBundleUrl("assets://index.android.bundle");
}
public String getBundleUrl(String defaultAssetsUrl) {
// Test should rollback.
if (sp.getBoolean("shouldRollback", false)) {
this.rollBack();
}
String currentVersion = getCurrentVersion();
if (currentVersion == null) {
return defaultAssetsUrl;
}
if (sp.getBoolean("firstTime", false)) {
SharedPreferences.Editor editor = sp.edit();
editor.putBoolean("shouldRollback", true);
editor.apply();
}
return new File(rootDir, currentVersion+"/index.bundlejs").toURI().toString();
}
private void rollBack() {
String lastVersion = sp.getString("lastVersion", null);
if (lastVersion == null) {
throw new Error("This should never happen");
}
SharedPreferences.Editor editor = sp.edit();
editor.putString("currentVersion", lastVersion);
editor.putBoolean("shouldRollback", false);
editor.putBoolean("firstTime", false);
editor.putBoolean("rolledBack", true);
editor.apply();
}
private void clearUp() {
DownloadTaskParams params = new DownloadTaskParams();
params.type = DownloadTaskParams.TASK_TYPE_CLEARUP;
params.hash = sp.getString("currentVersion", null);
params.originHash = sp.getString("lastVersion", null);;
params.listener = new DownloadFileListener() {
@Override
public void onDownloadCompleted() {
}
@Override
public void onDownloadFailed(Throwable error) {
}
};
new DownloadTask(context).execute(params);
}
} }

@ -1,10 +1,14 @@
package cn.reactnative.modules.update; package cn.reactnative.modules.update;
import android.app.Activity;
import android.content.Intent;
import com.facebook.react.bridge.Promise; import com.facebook.react.bridge.Promise;
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.ReadableMap; import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.UiThreadUtil;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -15,15 +19,23 @@ import java.util.Map;
public class UpdateModule extends ReactContextBaseJavaModule{ public class UpdateModule extends ReactContextBaseJavaModule{
UpdateContext updateContext; UpdateContext updateContext;
public UpdateModule(ReactApplicationContext reactContext) { public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
super(reactContext); super(reactContext);
this.updateContext = new UpdateContext(reactContext.getApplicationContext()); this.updateContext = updateContext;
}
public UpdateModule(ReactApplicationContext reactContext) {
this(reactContext, new UpdateContext(reactContext.getApplicationContext()));
} }
@Override @Override
public Map<String, Object> getConstants() { public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>(); final Map<String, Object> constants = new HashMap<>();
constants.put("downloadRootDir", updateContext.getRootDir()); constants.put("downloadRootDir", updateContext.getRootDir());
constants.put("packageVersion", updateContext.getPackageVersion());
constants.put("currentVersion", updateContext.getCurrentVersion());
constants.put("firstTime", updateContext.isFirstTime());
constants.put("rolledBack", updateContext.isRolledBack());
return constants; return constants;
} }
@ -50,7 +62,7 @@ public class UpdateModule extends ReactContextBaseJavaModule{
} }
@ReactMethod @ReactMethod
public void downloadPatchFromApk(ReadableMap options, final Promise promise){ public void downloadPatchFromPackage(ReadableMap options, final Promise promise){
String url = options.getString("updateUrl"); String url = options.getString("updateUrl");
String hash = options.getString("hashName"); String hash = options.getString("hashName");
updateContext.downloadPatchFromApk(url, hash, new UpdateContext.DownloadFileListener() { updateContext.downloadPatchFromApk(url, hash, new UpdateContext.DownloadFileListener() {
@ -83,4 +95,32 @@ public class UpdateModule extends ReactContextBaseJavaModule{
} }
}); });
} }
@ReactMethod
public void reloadUpdate(ReadableMap options) {
final String hash = options.getString("hashName");
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.switchVersion(hash);
Activity activity = getCurrentActivity();
Intent intent = activity.getIntent();
activity.finish();
activity.startActivity(intent);
}
});
}
@ReactMethod
public void setNeedUpdate(ReadableMap options) {
final String hash = options.getString("hashName");
UiThreadUtil.runOnUiThread(new Runnable() {
@Override
public void run() {
updateContext.switchVersion(hash);
}
});
}
} }

@ -3,30 +3,4 @@
* @providesModule react-native-update * @providesModule react-native-update
*/ */
//if (__DEV__){ require('./lib/index');
// if (global.__fbBatchedBridge) {
// require('fbjs/lib/warning')('Should require pushy before react-native to do hook stuff!');
// }
//}
//
//require('./lib/hooks');
const HotUpdate = require('react-native').NativeModules.HotUpdate;
const NativeAppEventEmitter = require('react-native').NativeAppEventEmitter;
const downloadRootDir = HotUpdate.downloadRootDir;
export function downloadFile(options) {
HotUpdate.downloadUpdate(options, r=>{
//console.log(r);
})
}
export function reloadUpdate(options) {
HotUpdate.reloadUpdate(options);
}
export function setNeedUpdate(options) {
HotUpdate.setNeedUpdate(options);
}

@ -1,8 +0,0 @@
/**
* Created by tdzl2003 on 2/6/16.
*/
// Hook resolvedAssetSource
__d('resolveAssetSource', function(_1, _2, module, _3) {
module.exports = require('./resolveAssetSource');
});

84
lib/index.js Normal file

@ -0,0 +1,84 @@
/**
* Created by tdzl2003 on 4/4/16.
*/
const HotUpdate = require('react-native').NativeModules.HotUpdate;
let host = 'http://update.reactnative.cn/api';
export const downloadRootDir = HotUpdate.downloadRootDir;
export const packageVersion = HotUpdate.packageVersion;
export const currentVersion = HotUpdate.currentVersion;
export const isFirstTime = HotUpdate.isFirstTime;
export const isRolledBack = HotUpdate.isRolledBack;
/*
Return json:
Package was expired:
{
expired: true,
downloadUrl: 'http://appstore/downloadUrl',
}
Package is up to date:
{
upToDate: true,
}
There is available update:
{
update: true,
name: '1.0.3-rc',
hash: 'hash',
description: '添加聊天功能\n修复商城页面BUG',
metaInfo: '{"silent":true}',
updateUrl: 'http://update-packages.reactnative.cn/hash',
pdiffUrl: 'http://update-packages.reactnative.cn/hash',
diffUrl: 'http://update-packages.reactnative.cn/hash',
}
*/
export async function checkUpdate(APPKEY) {
const resp = await fetch(`${host}/checkUpdate/${APPKEY}`, {
method: 'POST',
body: JSON.stringify({
packageVersion: packageVersion,
hash: currentVersion,
}),
});
if (resp.status !== 200) {
throw new Error(resp.message);
}
return await resp.json();
}
export async function downloadUpdate(options) {
if (!options.update) {
return;
}
if (options.diff) {
await HotUpdate.downloadPatchFromPpk({
updateUrl: options.diffUrl,
hashName: options.hash,
originHash: currentVersion,
});
} else if (options.diff) {
await HotUpdate.downloadPatchFromPackage({
updateUrl: options.pdiffUrl,
hashName: options.hash,
});
} else {
await HotUpdate.downloadPatchFromPackage({
updateUrl: options.updateUrl,
hashName: options.hash,
});
}
}
export async function switchVersion({hash}) {
HotUpdate.reloadUpdate({hashName:hash});
}
export async function switchVersionLater({hash}) {
HotUpdate.setNeedUpdate({hashName:hash});
}

@ -1,144 +0,0 @@
/**
* 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.
*
* @providesModule resolveAssetSource
* @flow
*
* Resolves an asset into a `source` for `Image`.
*
* Hooked by @tdzl2003 for react-native-pushy
*/
'use strict';
var AssetRegistry = require('react-native/Libraries/Image/AssetRegistry');
var PixelRatio = require('react-native/Libraries/Utilities/PixelRatio');
var Platform = require('react-native/Libraries/Utilities/Platform');
var NativeModules = require('react-native/Libraries/BatchedBridge/BatchedBridgedModules/NativeModules.js');
var SourceCode = NativeModules.SourceCode;
var _serverURL, _offlinePath;
function getDevServerURL() {
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;
}
}
return _serverURL;
}
function getOfflinePath() {
if (_offlinePath === undefined) {
var scriptURL = SourceCode.scriptURL;
var match = scriptURL && scriptURL.match(/^file:\/\/(\/.*\/)/);
if (match) {
_offlinePath = match[1];
} else {
_offlinePath = '';
}
}
return _offlinePath;
}
/**
* 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
.replace(/^assets_/, ''); // Remove "assets_" prefix
} else {
// E.g. 'assets/AwesomeModule/icon@2x.png'
return getOfflinePath() + 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) + '?platform=' + Platform.OS +
'&hash=' + asset.hash;
}
/**
* Returns a path like 'assets/AwesomeModule'
*/
function getBasePath(asset) {
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++) {
if (scales[i] >= deviceScale) {
return scales[i];
}
}
// If nothing matches, device scale is larger than any available
// scales, so we return the biggest one. Unless the array is empty,
// in which case we default to 1
return scales[scales.length - 1] || 1;
}
function resolveAssetSource(source){
if (typeof source === 'object') {
return source;
}
var asset = AssetRegistry.getAssetByID(source);
if (asset) {
return assetToImageSource(asset);
}
return null;
}
function assetToImageSource(asset){
var devServerURL = getDevServerURL();
return {
__packager_asset: true,
width: asset.width,
height: asset.height,
uri: devServerURL ? getPathOnDevserver(devServerURL, asset) : getPathInArchive(asset),
scale: pickScale(asset.scales, PixelRatio.get()),
};
}
module.exports = resolveAssetSource;
module.exports.pickScale = pickScale;

@ -3,7 +3,7 @@
*/ */
const fetch = require('isomorphic-fetch'); const fetch = require('isomorphic-fetch');
let host = process.env.PUSHY_REGISTRY || 'http://update.reactnative.cn'; let host = process.env.PUSHY_REGISTRY || 'http://update.reactnative.cn/api';
const fs = require('fs-promise'); const fs = require('fs-promise');
import * as fsOrigin from 'fs'; import * as fsOrigin from 'fs';
import request from 'request'; import request from 'request';
@ -18,7 +18,7 @@ exports.loadSession = async function() {
exports.replaceSession(JSON.parse(await fs.readFile('.update', 'utf8'))); exports.replaceSession(JSON.parse(await fs.readFile('.update', 'utf8')));
savedSession = session; savedSession = session;
} catch (e) { } catch (e) {
console.error('Failed to parse file `.pushy`. Try to remove it manually.'); console.error('Failed to parse file `.update`. Try to remove it manually.');
throw e; throw e;
} }
} }
@ -94,7 +94,7 @@ async function uploadFile(fn) {
let realUrl = url; let realUrl = url;
if (!/^https?\:\/\//.test(url)) { if (!/^https?\:\/\//.test(url)) {
url = host + url; realUrl = host + url;
} }
const fileSize = (await fs.stat(fn)).size; const fileSize = (await fs.stat(fn)).size;
@ -111,8 +111,7 @@ async function uploadFile(fn) {
formData.file.on('data', function(data) { formData.file.on('data', function(data) {
bar.tick(data.length); bar.tick(data.length);
}) })
request.post({ request.post(realUrl, {
realUrl,
formData, formData,
}, (err, resp, body) => { }, (err, resp, body) => {
if (err) { if (err) {

@ -61,7 +61,7 @@ export const commands = {
uploadApk: async function({args}) { uploadApk: async function({args}) {
const fn = args[0]; const fn = args[0];
if (!fn) { if (!fn) {
throw new Error('Usage: pushy uploadApk <ipaFile>'); throw new Error('Usage: pushy uploadApk <apkFile>');
} }
const name = await getApkVersion(fn); const name = await getApkVersion(fn);
const {appId} = await getSelectedApp('android'); const {appId} = await getSelectedApp('android');