mirror of
https://github.com/zhigang1992/react-native-code-push.git
synced 2026-06-10 23:59:42 +08:00
Merge pull request #168 from Microsoft/android-asset-updates
Android Asset Updates 🎉
This commit is contained in:
52
CodePush.m
52
CodePush.m
@@ -366,32 +366,34 @@ RCT_EXPORT_METHOD(downloadUpdate:(NSDictionary*)updatePackage
|
||||
resolver:(RCTPromiseResolveBlock)resolve
|
||||
rejecter:(RCTPromiseRejectBlock)reject)
|
||||
{
|
||||
[CodePushPackage downloadPackage:updatePackage
|
||||
// The download is progressing forward
|
||||
progressCallback:^(long long expectedContentLength, long long receivedContentLength) {
|
||||
// Notify the script-side about the progress
|
||||
[self.bridge.eventDispatcher
|
||||
sendDeviceEventWithName:@"CodePushDownloadProgress"
|
||||
body:@{
|
||||
@"totalBytes":[NSNumber numberWithLongLong:expectedContentLength],
|
||||
@"receivedBytes":[NSNumber numberWithLongLong:receivedContentLength]
|
||||
}];
|
||||
}
|
||||
// The download completed
|
||||
doneCallback:^{
|
||||
NSError *err;
|
||||
NSDictionary *newPackage = [CodePushPackage getPackage:updatePackage[PackageHashKey] error:&err];
|
||||
|
||||
if (err) {
|
||||
return reject(err);
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[CodePushPackage downloadPackage:updatePackage
|
||||
// The download is progressing forward
|
||||
progressCallback:^(long long expectedContentLength, long long receivedContentLength) {
|
||||
// Notify the script-side about the progress
|
||||
[self.bridge.eventDispatcher
|
||||
sendDeviceEventWithName:@"CodePushDownloadProgress"
|
||||
body:@{
|
||||
@"totalBytes":[NSNumber numberWithLongLong:expectedContentLength],
|
||||
@"receivedBytes":[NSNumber numberWithLongLong:receivedContentLength]
|
||||
}];
|
||||
}
|
||||
|
||||
resolve(newPackage);
|
||||
}
|
||||
// The download failed
|
||||
failCallback:^(NSError *err) {
|
||||
reject(err);
|
||||
}];
|
||||
// The download completed
|
||||
doneCallback:^{
|
||||
NSError *err;
|
||||
NSDictionary *newPackage = [CodePushPackage getPackage:updatePackage[PackageHashKey] error:&err];
|
||||
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
|
||||
resolve(newPackage);
|
||||
}
|
||||
// The download failed
|
||||
failCallback:^(NSError *err) {
|
||||
reject(err);
|
||||
}];
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -425,41 +425,50 @@ public class CodePush {
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
public void getNewStatusReport(Promise promise) {
|
||||
if (needToReportRollback) {
|
||||
needToReportRollback = false;
|
||||
JSONArray failedUpdates = getFailedUpdates();
|
||||
if (failedUpdates != null && failedUpdates.length() > 0) {
|
||||
try {
|
||||
JSONObject lastFailedPackageJSON = failedUpdates.getJSONObject(failedUpdates.length() - 1);
|
||||
WritableMap lastFailedPackage = CodePushUtils.convertJsonObjectToWriteable(lastFailedPackageJSON);
|
||||
WritableMap failedStatusReport = codePushTelemetryManager.getRollbackReport(lastFailedPackage);
|
||||
if (failedStatusReport != null) {
|
||||
promise.resolve(failedStatusReport);
|
||||
return;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
throw new CodePushUnknownException("Unable to read failed updates information stored in SharedPreferences.", e);
|
||||
}
|
||||
}
|
||||
} else if (didUpdate) {
|
||||
WritableMap currentPackage = codePushPackage.getCurrentPackage();
|
||||
if (currentPackage != null) {
|
||||
WritableMap newPackageStatusReport = codePushTelemetryManager.getUpdateReport(currentPackage);
|
||||
if (newPackageStatusReport != null) {
|
||||
promise.resolve(newPackageStatusReport);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else if (isRunningBinaryVersion) {
|
||||
WritableMap newAppVersionStatusReport = codePushTelemetryManager.getBinaryUpdateReport(appVersion);
|
||||
if (newAppVersionStatusReport != null) {
|
||||
promise.resolve(newAppVersionStatusReport);
|
||||
return;
|
||||
}
|
||||
}
|
||||
public void getNewStatusReport(final Promise promise) {
|
||||
|
||||
promise.resolve("");
|
||||
AsyncTask asyncTask = new AsyncTask() {
|
||||
@Override
|
||||
protected Void doInBackground(Object... params) {
|
||||
if (needToReportRollback) {
|
||||
needToReportRollback = false;
|
||||
JSONArray failedUpdates = getFailedUpdates();
|
||||
if (failedUpdates != null && failedUpdates.length() > 0) {
|
||||
try {
|
||||
JSONObject lastFailedPackageJSON = failedUpdates.getJSONObject(failedUpdates.length() - 1);
|
||||
WritableMap lastFailedPackage = CodePushUtils.convertJsonObjectToWriteable(lastFailedPackageJSON);
|
||||
WritableMap failedStatusReport = codePushTelemetryManager.getRollbackReport(lastFailedPackage);
|
||||
if (failedStatusReport != null) {
|
||||
promise.resolve(failedStatusReport);
|
||||
return null;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
throw new CodePushUnknownException("Unable to read failed updates information stored in SharedPreferences.", e);
|
||||
}
|
||||
}
|
||||
} else if (didUpdate) {
|
||||
WritableMap currentPackage = codePushPackage.getCurrentPackage();
|
||||
if (currentPackage != null) {
|
||||
WritableMap newPackageStatusReport = codePushTelemetryManager.getUpdateReport(currentPackage);
|
||||
if (newPackageStatusReport != null) {
|
||||
promise.resolve(newPackageStatusReport);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
} else if (isRunningBinaryVersion) {
|
||||
WritableMap newAppVersionStatusReport = codePushTelemetryManager.getBinaryUpdateReport(appVersion);
|
||||
if (newAppVersionStatusReport != null) {
|
||||
promise.resolve(newAppVersionStatusReport);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
promise.resolve("");
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
asyncTask.execute();
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.microsoft.codepush.react;
|
||||
|
||||
public class CodePushInvalidUpdateException extends RuntimeException {
|
||||
public CodePushInvalidUpdateException() {
|
||||
super("Update is invalid - no files with extension .bundle, .js or .jsbundle were found in the update package.");
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,14 @@ package com.microsoft.codepush.react;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
import com.facebook.react.bridge.WritableNativeMap;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
@@ -14,18 +18,23 @@ import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class CodePushPackage {
|
||||
|
||||
public final String CODE_PUSH_FOLDER_PREFIX = "CodePush";
|
||||
public final String STATUS_FILE = "codepush.json";
|
||||
public final String UPDATE_BUNDLE_FILE_NAME = "app.jsbundle";
|
||||
public final String CURRENT_PACKAGE_KEY = "currentPackage";
|
||||
public final String PREVIOUS_PACKAGE_KEY = "previousPackage";
|
||||
public final String DIFF_MANIFEST_FILE_NAME = "hotcodepush.json";
|
||||
public final int DOWNLOAD_BUFFER_SIZE = 1024 * 256;
|
||||
public final String DOWNLOAD_FILE_NAME = "download.zip";
|
||||
public final String DOWNLOAD_URL_KEY = "downloadUrl";
|
||||
public final String PACKAGE_FILE_NAME = "app.json";
|
||||
public final String PACKAGE_HASH_KEY = "packageHash";
|
||||
public final String DOWNLOAD_URL_KEY = "downloadUrl";
|
||||
public final int DOWNLOAD_BUFFER_SIZE = 1024 * 256;
|
||||
public final String PREVIOUS_PACKAGE_KEY = "previousPackage";
|
||||
public final String RELATIVE_BUNDLE_PATH_KEY = "bundlePath";
|
||||
public final String STATUS_FILE = "codepush.json";
|
||||
public final String UNZIPPED_FOLDER_NAME = "unzipped";
|
||||
public final String UPDATE_BUNDLE_FILE_NAME = "app.jsbundle";
|
||||
|
||||
private String documentsDirectory;
|
||||
|
||||
@@ -33,6 +42,14 @@ public class CodePushPackage {
|
||||
this.documentsDirectory = documentsDirectory;
|
||||
}
|
||||
|
||||
public String getDownloadFilePath() {
|
||||
return CodePushUtils.appendPathComponent(getCodePushPath(), DOWNLOAD_FILE_NAME);
|
||||
}
|
||||
|
||||
public String getUnzippedFolderPath() {
|
||||
return CodePushUtils.appendPathComponent(getCodePushPath(), UNZIPPED_FOLDER_NAME);
|
||||
}
|
||||
|
||||
public String getDocumentsDirectory() {
|
||||
return documentsDirectory;
|
||||
}
|
||||
@@ -52,7 +69,7 @@ public class CodePushPackage {
|
||||
|
||||
public WritableMap getCurrentPackageInfo() {
|
||||
String statusFilePath = getStatusFilePath();
|
||||
if (!CodePushUtils.fileAtPathExists(statusFilePath)) {
|
||||
if (!FileUtils.fileAtPathExists(statusFilePath)) {
|
||||
return new WritableNativeMap();
|
||||
}
|
||||
|
||||
@@ -87,7 +104,13 @@ public class CodePushPackage {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CodePushUtils.appendPathComponent(packageFolder, UPDATE_BUNDLE_FILE_NAME);
|
||||
WritableMap currentPackage = getCurrentPackage();
|
||||
String relativeBundlePath = CodePushUtils.tryGetString(currentPackage, RELATIVE_BUNDLE_PATH_KEY);
|
||||
if (relativeBundlePath == null) {
|
||||
return CodePushUtils.appendPathComponent(packageFolder, UPDATE_BUNDLE_FILE_NAME);
|
||||
} else {
|
||||
return CodePushUtils.appendPathComponent(packageFolder, relativeBundlePath);
|
||||
}
|
||||
}
|
||||
|
||||
public String getPackageFolderPath(String packageHash) {
|
||||
@@ -132,7 +155,7 @@ public class CodePushPackage {
|
||||
public void downloadPackage(Context applicationContext, ReadableMap updatePackage,
|
||||
DownloadProgressCallback progressCallback) throws IOException {
|
||||
|
||||
String packageFolderPath = getPackageFolderPath(CodePushUtils.tryGetString(updatePackage, PACKAGE_HASH_KEY));
|
||||
String newPackageFolderPath = getPackageFolderPath(CodePushUtils.tryGetString(updatePackage, PACKAGE_HASH_KEY));
|
||||
String downloadUrlString = CodePushUtils.tryGetString(updatePackage, DOWNLOAD_URL_KEY);
|
||||
|
||||
URL downloadUrl = null;
|
||||
@@ -140,7 +163,10 @@ public class CodePushPackage {
|
||||
BufferedInputStream bin = null;
|
||||
FileOutputStream fos = null;
|
||||
BufferedOutputStream bout = null;
|
||||
File downloadFile = null;
|
||||
boolean isZip = false;
|
||||
|
||||
// Download the file while checking if it is a zip and notifying client of progress.
|
||||
try {
|
||||
downloadUrl = new URL(downloadUrlString);
|
||||
connection = (HttpURLConnection) (downloadUrl.openConnection());
|
||||
@@ -149,23 +175,34 @@ public class CodePushPackage {
|
||||
long receivedBytes = 0;
|
||||
|
||||
bin = new BufferedInputStream(connection.getInputStream());
|
||||
File downloadFolder = new File(packageFolderPath);
|
||||
File downloadFolder = new File(getCodePushPath());
|
||||
downloadFolder.mkdirs();
|
||||
File downloadFile = new File(downloadFolder, UPDATE_BUNDLE_FILE_NAME);
|
||||
downloadFile = new File(downloadFolder, DOWNLOAD_FILE_NAME);
|
||||
fos = new FileOutputStream(downloadFile);
|
||||
bout = new BufferedOutputStream(fos, DOWNLOAD_BUFFER_SIZE);
|
||||
byte[] data = new byte[DOWNLOAD_BUFFER_SIZE];
|
||||
byte[] header = new byte[4];
|
||||
|
||||
int numBytesRead = 0;
|
||||
while ((numBytesRead = bin.read(data, 0, DOWNLOAD_BUFFER_SIZE)) >= 0) {
|
||||
if (receivedBytes < 4) {
|
||||
for (int i = 0; i < numBytesRead; i++) {
|
||||
int headerOffset = (int)(receivedBytes) + i;
|
||||
if (headerOffset >= 4) {
|
||||
break;
|
||||
}
|
||||
|
||||
header[headerOffset] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
receivedBytes += numBytesRead;
|
||||
bout.write(data, 0, numBytesRead);
|
||||
progressCallback.call(new DownloadProgress(totalBytes, receivedBytes));
|
||||
}
|
||||
|
||||
assert totalBytes == receivedBytes;
|
||||
|
||||
String bundlePath = CodePushUtils.appendPathComponent(packageFolderPath, PACKAGE_FILE_NAME);
|
||||
CodePushUtils.writeReadableMapToFile(updatePackage, bundlePath);
|
||||
isZip = ByteBuffer.wrap(header).getInt() == 0x504b0304;
|
||||
} catch (MalformedURLException e) {
|
||||
throw new CodePushMalformedDataException(downloadUrlString, e);
|
||||
} finally {
|
||||
@@ -178,6 +215,51 @@ public class CodePushPackage {
|
||||
throw new CodePushUnknownException("Error closing IO resources.", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (isZip) {
|
||||
// Unzip the downloaded file and then delete the zip
|
||||
String unzippedFolderPath = getUnzippedFolderPath();
|
||||
FileUtils.unzipFile(downloadFile, unzippedFolderPath);
|
||||
FileUtils.deleteFileSilently(downloadFile);
|
||||
|
||||
// Merge contents with current update based on the manifest
|
||||
String diffManifestFilePath = CodePushUtils.appendPathComponent(unzippedFolderPath,
|
||||
DIFF_MANIFEST_FILE_NAME);
|
||||
if (FileUtils.fileAtPathExists(diffManifestFilePath)) {
|
||||
String currentPackageFolderPath = getCurrentPackageFolderPath();
|
||||
CodePushUpdateUtils.copyNecessaryFilesFromCurrentPackage(diffManifestFilePath, currentPackageFolderPath, newPackageFolderPath);
|
||||
}
|
||||
|
||||
FileUtils.copyDirectoryContents(unzippedFolderPath, newPackageFolderPath);
|
||||
FileUtils.deleteFileAtPathSilently(unzippedFolderPath);
|
||||
|
||||
// For zip updates, we need to find the relative path to the jsBundle and save it in the
|
||||
// metadata so that we can find and run it easily the next time.
|
||||
String relativeBundlePath = CodePushUpdateUtils.findJSBundleInUpdateContents(newPackageFolderPath);
|
||||
|
||||
if (relativeBundlePath == null) {
|
||||
throw new CodePushInvalidUpdateException();
|
||||
} else {
|
||||
JSONObject updatePackageJSON = CodePushUtils.convertReadableToJsonObject(updatePackage);
|
||||
try {
|
||||
updatePackageJSON.put(RELATIVE_BUNDLE_PATH_KEY, relativeBundlePath);
|
||||
} catch (JSONException e) {
|
||||
throw new CodePushUnknownException("Unable to set key " +
|
||||
RELATIVE_BUNDLE_PATH_KEY + " to value " + relativeBundlePath +
|
||||
" in update package.", e);
|
||||
}
|
||||
|
||||
updatePackage = CodePushUtils.convertJsonObjectToWriteable(updatePackageJSON);
|
||||
}
|
||||
} else {
|
||||
// File is a jsBundle, move it to a folder with the packageHash as its name
|
||||
File updateBundleFile = new File(newPackageFolderPath, UPDATE_BUNDLE_FILE_NAME);
|
||||
downloadFile.renameTo(updateBundleFile);
|
||||
}
|
||||
|
||||
// Save metadata to the folder.
|
||||
String bundlePath = CodePushUtils.appendPathComponent(newPackageFolderPath, PACKAGE_FILE_NAME);
|
||||
CodePushUtils.writeReadableMapToFile(updatePackage, bundlePath);
|
||||
}
|
||||
|
||||
public void installPackage(ReadableMap updatePackage) throws IOException {
|
||||
@@ -185,7 +267,7 @@ public class CodePushPackage {
|
||||
WritableMap info = getCurrentPackageInfo();
|
||||
String previousPackageHash = getPreviousPackageHash();
|
||||
if (previousPackageHash != null && !previousPackageHash.equals(packageHash)) {
|
||||
CodePushUtils.deleteDirectoryAtPath(getPackageFolderPath(previousPackageHash));
|
||||
FileUtils.deleteDirectoryAtPath(getPackageFolderPath(previousPackageHash));
|
||||
}
|
||||
|
||||
info.putString(PREVIOUS_PACKAGE_KEY, CodePushUtils.tryGetString(info, CURRENT_PACKAGE_KEY));
|
||||
@@ -196,7 +278,7 @@ public class CodePushPackage {
|
||||
public void rollbackPackage() {
|
||||
WritableMap info = getCurrentPackageInfo();
|
||||
String currentPackageFolderPath = getCurrentPackageFolderPath();
|
||||
CodePushUtils.deleteDirectoryAtPath(currentPackageFolderPath);
|
||||
FileUtils.deleteDirectoryAtPath(currentPackageFolderPath);
|
||||
info.putString(CURRENT_PACKAGE_KEY, CodePushUtils.tryGetString(info, PREVIOUS_PACKAGE_KEY));
|
||||
info.putNull(PREVIOUS_PACKAGE_KEY);
|
||||
updateCurrentPackageInfo(info);
|
||||
@@ -238,6 +320,6 @@ public class CodePushPackage {
|
||||
public void clearUpdates() {
|
||||
File statusFile = new File(getStatusFilePath());
|
||||
statusFile.delete();
|
||||
CodePushUtils.deleteDirectoryAtPath(getCodePushPath());
|
||||
FileUtils.deleteDirectoryAtPath(getCodePushPath());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package com.microsoft.codepush.react;
|
||||
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.WritableMap;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class CodePushUpdateUtils {
|
||||
|
||||
public static void copyNecessaryFilesFromCurrentPackage(String diffManifestFilePath, String currentPackageFolderPath, String newPackageFolderPath) throws IOException{
|
||||
FileUtils.copyDirectoryContents(currentPackageFolderPath, newPackageFolderPath);
|
||||
WritableMap diffManifest = CodePushUtils.getWritableMapFromFile(diffManifestFilePath);
|
||||
ReadableArray deletedFiles = diffManifest.getArray("deletedFiles");
|
||||
for (int i = 0; i < deletedFiles.size(); i++) {
|
||||
String fileNameToDelete = deletedFiles.getString(i);
|
||||
File fileToDelete = new File(newPackageFolderPath, fileNameToDelete);
|
||||
FileUtils.deleteFileSilently(fileToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
public static String findJSBundleInUpdateContents(String folderPath) {
|
||||
File folder = new File(folderPath);
|
||||
File[] folderFiles = folder.listFiles();
|
||||
for (File file : folderFiles) {
|
||||
String fullFilePath = CodePushUtils.appendPathComponent(folderPath, file.getName());
|
||||
if (file.isDirectory()) {
|
||||
String mainBundlePathInSubFolder = findJSBundleInUpdateContents(fullFilePath);
|
||||
if (mainBundlePathInSubFolder != null) {
|
||||
return CodePushUtils.appendPathComponent(file.getName(), mainBundlePathInSubFolder);
|
||||
}
|
||||
} else {
|
||||
String fileName = file.getName();
|
||||
int dotIndex = fileName.lastIndexOf(".");
|
||||
if (dotIndex >= 0) {
|
||||
String fileExtension = fileName.substring(dotIndex + 1);
|
||||
if (fileExtension.equals("bundle") || fileExtension.equals("js") || fileExtension.equals("jsbundle")) {
|
||||
return fileName;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -15,99 +15,57 @@ import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Iterator;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class CodePushUtils {
|
||||
|
||||
public static final String CODE_PUSH_TAG = "CodePush";
|
||||
public static final String REACT_NATIVE_LOG_TAG = "ReactNative";
|
||||
|
||||
public static String appendPathComponent(String basePath, String appendPathComponent) {
|
||||
return new File(basePath, appendPathComponent).getAbsolutePath();
|
||||
}
|
||||
|
||||
public static boolean fileAtPathExists(String filePath) {
|
||||
return new File(filePath).exists();
|
||||
}
|
||||
|
||||
public static String readFileToString(String filePath) throws IOException {
|
||||
FileInputStream fin = null;
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
File fl = new File(filePath);
|
||||
fin = new FileInputStream(fl);
|
||||
reader = new BufferedReader(new InputStreamReader(fin));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
public static WritableArray convertJsonArrayToWriteable(JSONArray jsonArr) {
|
||||
WritableArray arr = Arguments.createArray();
|
||||
for (int i=0; i<jsonArr.length(); i++) {
|
||||
Object obj = null;
|
||||
try {
|
||||
obj = jsonArr.get(i);
|
||||
} catch (JSONException jsonException) {
|
||||
// Should not happen.
|
||||
throw new CodePushUnknownException(i + " should be within bounds of array " + jsonArr.toString(), jsonException);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
} finally {
|
||||
if (reader != null) reader.close();
|
||||
if (fin != null) fin.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeStringToFile(String content, String filePath) throws IOException {
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
out = new PrintWriter(filePath);
|
||||
out.print(content);
|
||||
} finally {
|
||||
if (out != null) out.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void deleteDirectoryAtPath(String directoryPath) {
|
||||
deleteDirectory(new File(directoryPath));
|
||||
}
|
||||
|
||||
public static void deleteDirectory(File directory) {
|
||||
if (directory.exists()) {
|
||||
File[] files = directory.listFiles();
|
||||
if (files != null) {
|
||||
for (int i=0; i<files.length; i++) {
|
||||
if(files[i].isDirectory()) {
|
||||
deleteDirectory(files[i]);
|
||||
}
|
||||
else {
|
||||
files[i].delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
directory.delete();
|
||||
}
|
||||
|
||||
public static boolean createFolderAtPath(String filePath) {
|
||||
File file = new File(filePath);
|
||||
return file.mkdir();
|
||||
}
|
||||
|
||||
public static WritableMap getWritableMapFromFile(String filePath) throws IOException {
|
||||
|
||||
String content = CodePushUtils.readFileToString(filePath);
|
||||
JSONObject json = null;
|
||||
try {
|
||||
json = new JSONObject(content);
|
||||
return convertJsonObjectToWriteable(json);
|
||||
} catch (JSONException jsonException) {
|
||||
throw new CodePushMalformedDataException(filePath, jsonException);
|
||||
if (obj instanceof JSONObject)
|
||||
arr.pushMap(convertJsonObjectToWriteable((JSONObject) obj));
|
||||
else if (obj instanceof JSONArray)
|
||||
arr.pushArray(convertJsonArrayToWriteable((JSONArray) obj));
|
||||
else if (obj instanceof String)
|
||||
arr.pushString((String) obj);
|
||||
else if (obj instanceof Double)
|
||||
arr.pushDouble((Double) obj);
|
||||
else if (obj instanceof Integer)
|
||||
arr.pushInt((Integer) obj);
|
||||
else if (obj instanceof Boolean)
|
||||
arr.pushBoolean((Boolean) obj);
|
||||
else if (obj == null)
|
||||
arr.pushNull();
|
||||
else
|
||||
throw new CodePushUnknownException("Unrecognized object: " + obj);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void writeReadableMapToFile(ReadableMap map, String filePath) throws IOException {
|
||||
JSONObject json = CodePushUtils.convertReadableToJsonObject(map);
|
||||
String jsonString = json.toString();
|
||||
CodePushUtils.writeStringToFile(jsonString, filePath);
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static WritableMap convertJsonObjectToWriteable(JSONObject jsonObj) {
|
||||
@@ -144,73 +102,9 @@ public class CodePushUtils {
|
||||
return map;
|
||||
}
|
||||
|
||||
public static WritableArray convertJsonArrayToWriteable(JSONArray jsonArr) {
|
||||
WritableArray arr = Arguments.createArray();
|
||||
for (int i=0; i<jsonArr.length(); i++) {
|
||||
Object obj = null;
|
||||
try {
|
||||
obj = jsonArr.get(i);
|
||||
} catch (JSONException jsonException) {
|
||||
// Should not happen.
|
||||
throw new CodePushUnknownException(i + " should be within bounds of array " + jsonArr.toString(), jsonException);
|
||||
}
|
||||
|
||||
if (obj instanceof JSONObject)
|
||||
arr.pushMap(convertJsonObjectToWriteable((JSONObject) obj));
|
||||
else if (obj instanceof JSONArray)
|
||||
arr.pushArray(convertJsonArrayToWriteable((JSONArray) obj));
|
||||
else if (obj instanceof String)
|
||||
arr.pushString((String) obj);
|
||||
else if (obj instanceof Double)
|
||||
arr.pushDouble((Double) obj);
|
||||
else if (obj instanceof Integer)
|
||||
arr.pushInt((Integer) obj);
|
||||
else if (obj instanceof Boolean)
|
||||
arr.pushBoolean((Boolean) obj);
|
||||
else if (obj == null)
|
||||
arr.pushNull();
|
||||
else
|
||||
throw new CodePushUnknownException("Unrecognized object: " + obj);
|
||||
}
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
public static JSONObject convertReadableToJsonObject(ReadableMap map) {
|
||||
JSONObject jsonObj = new JSONObject();
|
||||
ReadableMapKeySetIterator it = map.keySetIterator();
|
||||
while (it.hasNextKey()) {
|
||||
String key = it.nextKey();
|
||||
ReadableType type = map.getType(key);
|
||||
try {
|
||||
switch (type) {
|
||||
case Map:
|
||||
jsonObj.put(key, convertReadableToJsonObject(map.getMap(key)));
|
||||
break;
|
||||
case Array:
|
||||
jsonObj.put(key, convertReadableToJsonArray(map.getArray(key)));
|
||||
break;
|
||||
case String:
|
||||
jsonObj.put(key, map.getString(key));
|
||||
break;
|
||||
case Number:
|
||||
jsonObj.put(key, map.getDouble(key));
|
||||
break;
|
||||
case Boolean:
|
||||
jsonObj.put(key, map.getBoolean(key));
|
||||
break;
|
||||
case Null:
|
||||
jsonObj.put(key, null);
|
||||
break;
|
||||
default:
|
||||
throw new CodePushUnknownException("Unrecognized type: " + type + " of key: " + key);
|
||||
}
|
||||
} catch (JSONException jsonException) {
|
||||
throw new CodePushUnknownException("Error setting key: " + key + " in JSONObject", jsonException);
|
||||
}
|
||||
}
|
||||
|
||||
return jsonObj;
|
||||
public static WritableMap convertReadableMapToWritableMap(ReadableMap map) {
|
||||
JSONObject mapJSON = convertReadableToJsonObject(map);
|
||||
return convertJsonObjectToWriteable(mapJSON);
|
||||
}
|
||||
|
||||
public static JSONArray convertReadableToJsonArray(ReadableArray arr) {
|
||||
@@ -252,9 +146,62 @@ public class CodePushUtils {
|
||||
return jsonArr;
|
||||
}
|
||||
|
||||
public static WritableMap convertReadableMapToWritableMap(ReadableMap map) {
|
||||
JSONObject mapJSON = convertReadableToJsonObject(map);
|
||||
return convertJsonObjectToWriteable(mapJSON);
|
||||
public static JSONObject convertReadableToJsonObject(ReadableMap map) {
|
||||
JSONObject jsonObj = new JSONObject();
|
||||
ReadableMapKeySetIterator it = map.keySetIterator();
|
||||
while (it.hasNextKey()) {
|
||||
String key = it.nextKey();
|
||||
ReadableType type = map.getType(key);
|
||||
try {
|
||||
switch (type) {
|
||||
case Map:
|
||||
jsonObj.put(key, convertReadableToJsonObject(map.getMap(key)));
|
||||
break;
|
||||
case Array:
|
||||
jsonObj.put(key, convertReadableToJsonArray(map.getArray(key)));
|
||||
break;
|
||||
case String:
|
||||
jsonObj.put(key, map.getString(key));
|
||||
break;
|
||||
case Number:
|
||||
jsonObj.put(key, map.getDouble(key));
|
||||
break;
|
||||
case Boolean:
|
||||
jsonObj.put(key, map.getBoolean(key));
|
||||
break;
|
||||
case Null:
|
||||
jsonObj.put(key, null);
|
||||
break;
|
||||
default:
|
||||
throw new CodePushUnknownException("Unrecognized type: " + type + " of key: " + key);
|
||||
}
|
||||
} catch (JSONException jsonException) {
|
||||
throw new CodePushUnknownException("Error setting key: " + key + " in JSONObject", jsonException);
|
||||
}
|
||||
}
|
||||
|
||||
return jsonObj;
|
||||
}
|
||||
|
||||
public static WritableMap getWritableMapFromFile(String filePath) throws IOException {
|
||||
|
||||
String content = FileUtils.readFileToString(filePath);
|
||||
JSONObject json = null;
|
||||
try {
|
||||
json = new JSONObject(content);
|
||||
return convertJsonObjectToWriteable(json);
|
||||
} catch (JSONException jsonException) {
|
||||
throw new CodePushMalformedDataException(filePath, jsonException);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static void log(String message) {
|
||||
Log.d(REACT_NATIVE_LOG_TAG, "[CodePush] " + message);
|
||||
}
|
||||
|
||||
public static void logBundleUrl(String path) {
|
||||
log("Loading JS bundle from \"" + path + "\"");
|
||||
}
|
||||
|
||||
public static String tryGetString(ReadableMap map, String key) {
|
||||
@@ -265,11 +212,9 @@ public class CodePushUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static void log(String message) {
|
||||
Log.d(REACT_NATIVE_LOG_TAG, "[CodePush] " + message);
|
||||
}
|
||||
|
||||
public static void logBundleUrl(String path) {
|
||||
log("Loading JS bundle from \"" + path + "\"");
|
||||
public static void writeReadableMapToFile(ReadableMap map, String filePath) throws IOException {
|
||||
JSONObject json = CodePushUtils.convertReadableToJsonObject(map);
|
||||
String jsonString = json.toString();
|
||||
FileUtils.writeStringToFile(jsonString, filePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.microsoft.codepush.react;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
public static final int WRITE_BUFFER_SIZE = 1024 * 8;
|
||||
|
||||
public static void copyDirectoryContents(String sourceDirectoryPath, String destinationDirectoryPath) throws IOException {
|
||||
File sourceDir = new File(sourceDirectoryPath);
|
||||
File destDir = new File(destinationDirectoryPath);
|
||||
if (!destDir.exists()) {
|
||||
destDir.mkdir();
|
||||
}
|
||||
|
||||
for (File sourceFile : sourceDir.listFiles()) {
|
||||
if (sourceFile.isDirectory()) {
|
||||
copyDirectoryContents(
|
||||
CodePushUtils.appendPathComponent(sourceDirectoryPath, sourceFile.getName()),
|
||||
CodePushUtils.appendPathComponent(destinationDirectoryPath, sourceFile.getName()));
|
||||
} else {
|
||||
File destFile = new File(destDir, sourceFile.getName());
|
||||
FileInputStream fromFileStream = null;
|
||||
BufferedInputStream fromBufferedStream = null;
|
||||
FileOutputStream destStream = null;
|
||||
byte[] buffer = new byte[WRITE_BUFFER_SIZE];
|
||||
try {
|
||||
fromFileStream = new FileInputStream(sourceFile);
|
||||
fromBufferedStream = new BufferedInputStream(fromFileStream);
|
||||
destStream = new FileOutputStream(destFile);
|
||||
int bytesRead;
|
||||
while ((bytesRead = fromBufferedStream.read(buffer)) > 0) {
|
||||
destStream.write(buffer, 0, bytesRead);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
if (fromFileStream != null) fromFileStream.close();
|
||||
if (fromBufferedStream != null) fromBufferedStream.close();
|
||||
if (destStream != null) destStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new CodePushUnknownException("Error closing IO resources.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean createFolderAtPath(String filePath) {
|
||||
File file = new File(filePath);
|
||||
return file.mkdir();
|
||||
}
|
||||
|
||||
public static void deleteDirectory(File directory) {
|
||||
if (directory.exists()) {
|
||||
File[] files = directory.listFiles();
|
||||
if (files != null) {
|
||||
for (int i=0; i<files.length; i++) {
|
||||
if(files[i].isDirectory()) {
|
||||
deleteDirectory(files[i]);
|
||||
}
|
||||
else {
|
||||
files[i].delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
directory.delete();
|
||||
}
|
||||
|
||||
public static void deleteDirectoryAtPath(String directoryPath) {
|
||||
deleteDirectory(new File(directoryPath));
|
||||
}
|
||||
|
||||
public static void deleteFileAtPathSilently(String path) {
|
||||
deleteFileSilently(new File(path));
|
||||
}
|
||||
|
||||
public static void deleteFileSilently(File file) {
|
||||
if (!file.delete()) {
|
||||
CodePushUtils.log("Error deleting file " + file.getName());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean fileAtPathExists(String filePath) {
|
||||
return new File(filePath).exists();
|
||||
}
|
||||
|
||||
public static String readFileToString(String filePath) throws IOException {
|
||||
FileInputStream fin = null;
|
||||
BufferedReader reader = null;
|
||||
try {
|
||||
File fl = new File(filePath);
|
||||
fin = new FileInputStream(fl);
|
||||
reader = new BufferedReader(new InputStreamReader(fin));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line = null;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
} finally {
|
||||
if (reader != null) reader.close();
|
||||
if (fin != null) fin.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void unzipFile(File zipFile, String destination) throws IOException {
|
||||
FileInputStream fileStream = null;
|
||||
BufferedInputStream bufferedStream = null;
|
||||
ZipInputStream zipStream = null;
|
||||
try {
|
||||
fileStream = new FileInputStream(zipFile);
|
||||
bufferedStream = new BufferedInputStream(fileStream);
|
||||
zipStream = new ZipInputStream(bufferedStream);
|
||||
ZipEntry entry;
|
||||
|
||||
File destinationFolder = new File(destination);
|
||||
if (!destinationFolder.exists()) {
|
||||
destinationFolder.mkdirs();
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[WRITE_BUFFER_SIZE];
|
||||
while ((entry = zipStream.getNextEntry()) != null) {
|
||||
String fileName = entry.getName();
|
||||
File file = new File(destinationFolder, fileName);
|
||||
if (entry.isDirectory()) {
|
||||
file.mkdirs();
|
||||
} else {
|
||||
File parent = file.getParentFile();
|
||||
if (!parent.exists()) {
|
||||
parent.mkdirs();
|
||||
}
|
||||
|
||||
FileOutputStream fout = new FileOutputStream(file);
|
||||
try {
|
||||
int numBytesRead;
|
||||
while ((numBytesRead = zipStream.read(buffer)) != -1) {
|
||||
fout.write(buffer, 0, numBytesRead);
|
||||
}
|
||||
} finally {
|
||||
fout.close();
|
||||
}
|
||||
}
|
||||
long time = entry.getTime();
|
||||
if (time > 0) {
|
||||
file.setLastModified(time);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
if (zipStream != null) zipStream.close();
|
||||
if (bufferedStream != null) bufferedStream.close();
|
||||
if (fileStream != null) fileStream.close();
|
||||
} catch (IOException e) {
|
||||
throw new CodePushUnknownException("Error closing IO resources.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeStringToFile(String content, String filePath) throws IOException {
|
||||
PrintWriter out = null;
|
||||
try {
|
||||
out = new PrintWriter(filePath);
|
||||
out.print(content);
|
||||
} finally {
|
||||
if (out != null) out.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user