From fddfcb2af0114711340b99bb54cd54979a5b67f7 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Thu, 28 Jan 2016 20:16:58 -0800 Subject: [PATCH 1/7] android asset updates --- .../CodePushInvalidPackageException.java | 7 + .../codepush/react/CodePushPackage.java | 132 ++++++++++++++++-- .../codepush/react/CodePushUtils.java | 108 ++++++++++++++ 3 files changed, 235 insertions(+), 12 deletions(-) create mode 100644 android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPackageException.java diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPackageException.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPackageException.java new file mode 100644 index 0000000..72f124e --- /dev/null +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPackageException.java @@ -0,0 +1,7 @@ +package com.microsoft.codepush.react; + +public class CodePushInvalidPackageException extends RuntimeException { + public CodePushInvalidPackageException() { + super("Update is invalid - no files with extension .jsbundle or .bundle were found in the update package."); + } +} diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java index d91364a..3c9ebd7 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java @@ -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; } @@ -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,6 +163,8 @@ public class CodePushPackage { BufferedInputStream bin = null; FileOutputStream fos = null; BufferedOutputStream bout = null; + File downloadFile = null; + boolean isZip = false; try { downloadUrl = new URL(downloadUrlString); @@ -149,23 +174,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 +214,78 @@ public class CodePushPackage { throw new CodePushUnknownException("Error closing IO resources.", e); } } + + if (isZip) { + System.err.println("THIS IS A ZIP!"); + String unzippedFolderPath = getUnzippedFolderPath(); + CodePushUtils.unzipFile(downloadFile, unzippedFolderPath); + CodePushUtils.deleteFileSilently(downloadFile); + String diffManifestFilePath = CodePushUtils.appendPathComponent(unzippedFolderPath, + DIFF_MANIFEST_FILE_NAME); + File diffManifestFile = new File(unzippedFolderPath, DIFF_MANIFEST_FILE_NAME); + if (diffManifestFile.exists()) { + String currentPackageFolderPath = getCurrentPackageFolderPath(); + CodePushUtils.mergeEntriesInFolder(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); + CodePushUtils.deleteFileSilently(fileToDelete); + } + } + + CodePushUtils.mergeEntriesInFolder(unzippedFolderPath, newPackageFolderPath); + CodePushUtils.deleteFileAtPathSilently(unzippedFolderPath); + String relativeBundlePath = findMainBundleInFolder(newPackageFolderPath); + + if (relativeBundlePath == null) { + throw new CodePushInvalidPackageException(); + } 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 { + System.err.println("THIS IS NOT A ZIP!"); + // File is not a zip. + File updateBundleFile = new File(newPackageFolderPath, UPDATE_BUNDLE_FILE_NAME); + downloadFile.renameTo(updateBundleFile); + } + + String bundlePath = CodePushUtils.appendPathComponent(newPackageFolderPath, PACKAGE_FILE_NAME); + CodePushUtils.writeReadableMapToFile(updatePackage, bundlePath); + } + + public String findMainBundleInFolder(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 = findMainBundleInFolder(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; } public void installPackage(ReadableMap updatePackage) throws IOException { diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java index 295fc37..5aefef4 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java @@ -15,17 +15,23 @@ 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 final int WRITE_BUFFER_SIZE = 1024 * 8; public static String appendPathComponent(String basePath, String appendPathComponent) { return new File(basePath, appendPathComponent).getAbsolutePath(); @@ -265,6 +271,108 @@ public class CodePushUtils { } } + public static void unzipFile(File zipFile, String destination) throws IOException { + FileInputStream fis = null; + BufferedInputStream bis = null; + ZipInputStream zis = null; + try { + fis = new FileInputStream(zipFile); + bis = new BufferedInputStream(fis); + zis = new ZipInputStream(bis); + ZipEntry entry; + + File destinationFolder = new File(destination); + if (!destinationFolder.exists()) { + destinationFolder.mkdirs(); + } + + byte[] buffer = new byte[WRITE_BUFFER_SIZE]; + while ((entry = zis.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 = zis.read(buffer)) != -1) { + fout.write(buffer, 0, numBytesRead); + } + } finally { + fout.close(); + } + } + long time = entry.getTime(); + if (time > 0) { + file.setLastModified(time); + } + } + } finally { + try { + if (zis != null) zis.close(); + if (bis != null) bis.close(); + if (fis != null) fis.close(); + } catch (IOException e) { + throw new CodePushUnknownException("Error closing IO resources.", e); + } + } + } + + public static void mergeEntriesInFolder(String fromPath, String destinationPath) throws IOException { + File fromDir = new File(fromPath); + File destDir = new File(destinationPath); + if (!destDir.exists()) { + destDir.mkdir(); + } + + for (File fromFile : fromDir.listFiles()) { + if (fromFile.isDirectory()) { + mergeEntriesInFolder( + CodePushUtils.appendPathComponent(fromPath, fromFile.getName()), + CodePushUtils.appendPathComponent(destinationPath, fromFile.getName())); + } else { + File destFile = new File(destDir, fromFile.getName()); + FileInputStream fromFileStream = null; + BufferedInputStream fromBufferedStream = null; + FileOutputStream destStream = null; + byte[] buffer = new byte[WRITE_BUFFER_SIZE]; + try { + fromFileStream = new FileInputStream(fromFile); + 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 void deleteFileAtPathSilently(String path) { + deleteFileSilently(new File(path)); + } + + public static void deleteFileSilently(File file) { + if (!file.delete()) { + Log.e(CODE_PUSH_TAG, "Error deleting file " + file.getName()); + } + } + public static void log(String message) { Log.d(REACT_NATIVE_LOG_TAG, "[CodePush] " + message); } From 1185d11d5b9c7f57ccfadfe933a7f206a02903cf Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Thu, 28 Jan 2016 20:22:14 -0800 Subject: [PATCH 2/7] remove printlns --- .../main/java/com/microsoft/codepush/react/CodePushPackage.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java index 3c9ebd7..1b94483 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java @@ -216,7 +216,6 @@ public class CodePushPackage { } if (isZip) { - System.err.println("THIS IS A ZIP!"); String unzippedFolderPath = getUnzippedFolderPath(); CodePushUtils.unzipFile(downloadFile, unzippedFolderPath); CodePushUtils.deleteFileSilently(downloadFile); @@ -253,7 +252,6 @@ public class CodePushPackage { updatePackage = CodePushUtils.convertJsonObjectToWriteable(updatePackageJSON); } } else { - System.err.println("THIS IS NOT A ZIP!"); // File is not a zip. File updateBundleFile = new File(newPackageFolderPath, UPDATE_BUNDLE_FILE_NAME); downloadFile.renameTo(updateBundleFile); From 73b744c60ed4f9035ab0ef018df5ee2b5abc6d0b Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Thu, 28 Jan 2016 20:23:10 -0800 Subject: [PATCH 3/7] edit invalid package exception message --- .../codepush/react/CodePushInvalidPackageException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPackageException.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPackageException.java index 72f124e..1d6ad09 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPackageException.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPackageException.java @@ -2,6 +2,6 @@ package com.microsoft.codepush.react; public class CodePushInvalidPackageException extends RuntimeException { public CodePushInvalidPackageException() { - super("Update is invalid - no files with extension .jsbundle or .bundle were found in the update package."); + super("Update is invalid - no files with extension .bundle, .js or .jsbundle were found in the update package."); } } From d297fc8f43a73f49cc69687f4aabc870202be7d9 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Fri, 29 Jan 2016 12:17:54 -0800 Subject: [PATCH 4/7] android-asset-updates --- .../microsoft/codepush/react/CodePush.java | 77 ++-- .../codepush/react/CodePushPackage.java | 58 +-- .../codepush/react/CodePushUtils.java | 366 ++++++------------ .../microsoft/codepush/react/FileUtils.java | 181 +++++++++ 4 files changed, 359 insertions(+), 323 deletions(-) create mode 100644 android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java index 937806f..a247724 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java @@ -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 diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java index 1b94483..fd3648b 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java @@ -69,7 +69,7 @@ public class CodePushPackage { public WritableMap getCurrentPackageInfo() { String statusFilePath = getStatusFilePath(); - if (!CodePushUtils.fileAtPathExists(statusFilePath)) { + if (!FileUtils.fileAtPathExists(statusFilePath)) { return new WritableNativeMap(); } @@ -166,6 +166,7 @@ public class CodePushPackage { 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()); @@ -216,27 +217,34 @@ public class CodePushPackage { } if (isZip) { + // Unzip the downloaded file and then delete the zip String unzippedFolderPath = getUnzippedFolderPath(); - CodePushUtils.unzipFile(downloadFile, unzippedFolderPath); - CodePushUtils.deleteFileSilently(downloadFile); + 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); File diffManifestFile = new File(unzippedFolderPath, DIFF_MANIFEST_FILE_NAME); if (diffManifestFile.exists()) { String currentPackageFolderPath = getCurrentPackageFolderPath(); - CodePushUtils.mergeEntriesInFolder(currentPackageFolderPath, newPackageFolderPath); + 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); - CodePushUtils.deleteFileSilently(fileToDelete); + FileUtils.deleteFileSilently(fileToDelete); } } - CodePushUtils.mergeEntriesInFolder(unzippedFolderPath, newPackageFolderPath); - CodePushUtils.deleteFileAtPathSilently(unzippedFolderPath); - String relativeBundlePath = findMainBundleInFolder(newPackageFolderPath); + // Move merged update contents to a folder with the packageHash as its name + 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 = CodePushUtils.findJSBundleInUpdateContents(newPackageFolderPath); if (relativeBundlePath == null) { throw new CodePushInvalidPackageException(); @@ -252,46 +260,22 @@ public class CodePushPackage { updatePackage = CodePushUtils.convertJsonObjectToWriteable(updatePackageJSON); } } else { - // File is not a zip. + // 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 String findMainBundleInFolder(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 = findMainBundleInFolder(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; - } - public void installPackage(ReadableMap updatePackage) throws IOException { String packageHash = CodePushUtils.tryGetString(updatePackage, PACKAGE_HASH_KEY); 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)); @@ -302,7 +286,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); @@ -344,6 +328,6 @@ public class CodePushPackage { public void clearUpdates() { File statusFile = new File(getStatusFilePath()); statusFile.delete(); - CodePushUtils.deleteDirectoryAtPath(getCodePushPath()); + FileUtils.deleteDirectoryAtPath(getCodePushPath()); } } diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java index 5aefef4..750db71 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java @@ -31,89 +31,41 @@ public class CodePushUtils { public static final String CODE_PUSH_TAG = "CodePush"; public static final String REACT_NATIVE_LOG_TAG = "ReactNative"; - public static final int WRITE_BUFFER_SIZE = 1024 * 8; 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= 0) { + String fileExtension = fileName.substring(dotIndex + 1); + if (fileExtension.equals("bundle") || fileExtension.equals("js") || fileExtension.equals("jsbundle")) { + return fileName; + } + } + } + } + + return null; + } + + 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) { @@ -271,113 +237,9 @@ public class CodePushUtils { } } - public static void unzipFile(File zipFile, String destination) throws IOException { - FileInputStream fis = null; - BufferedInputStream bis = null; - ZipInputStream zis = null; - try { - fis = new FileInputStream(zipFile); - bis = new BufferedInputStream(fis); - zis = new ZipInputStream(bis); - ZipEntry entry; - - File destinationFolder = new File(destination); - if (!destinationFolder.exists()) { - destinationFolder.mkdirs(); - } - - byte[] buffer = new byte[WRITE_BUFFER_SIZE]; - while ((entry = zis.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 = zis.read(buffer)) != -1) { - fout.write(buffer, 0, numBytesRead); - } - } finally { - fout.close(); - } - } - long time = entry.getTime(); - if (time > 0) { - file.setLastModified(time); - } - } - } finally { - try { - if (zis != null) zis.close(); - if (bis != null) bis.close(); - if (fis != null) fis.close(); - } catch (IOException e) { - throw new CodePushUnknownException("Error closing IO resources.", e); - } - } - } - - public static void mergeEntriesInFolder(String fromPath, String destinationPath) throws IOException { - File fromDir = new File(fromPath); - File destDir = new File(destinationPath); - if (!destDir.exists()) { - destDir.mkdir(); - } - - for (File fromFile : fromDir.listFiles()) { - if (fromFile.isDirectory()) { - mergeEntriesInFolder( - CodePushUtils.appendPathComponent(fromPath, fromFile.getName()), - CodePushUtils.appendPathComponent(destinationPath, fromFile.getName())); - } else { - File destFile = new File(destDir, fromFile.getName()); - FileInputStream fromFileStream = null; - BufferedInputStream fromBufferedStream = null; - FileOutputStream destStream = null; - byte[] buffer = new byte[WRITE_BUFFER_SIZE]; - try { - fromFileStream = new FileInputStream(fromFile); - 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 void deleteFileAtPathSilently(String path) { - deleteFileSilently(new File(path)); - } - - public static void deleteFileSilently(File file) { - if (!file.delete()) { - Log.e(CODE_PUSH_TAG, "Error deleting file " + file.getName()); - } - } - - 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); } } diff --git a/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java b/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java new file mode 100644 index 0000000..5b68a1c --- /dev/null +++ b/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java @@ -0,0 +1,181 @@ +package com.microsoft.codepush.react; + +import android.util.Log; + +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 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 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 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 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 void deleteFileAtPathSilently(String path) { + deleteFileSilently(new File(path)); + } + + public static void deleteFileSilently(File file) { + if (!file.delete()) { + CodePushUtils.log("Error deleting file " + file.getName()); + } + } +} From 8ca94213005f704f2689267b933c922a973ee704 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Fri, 29 Jan 2016 12:19:49 -0800 Subject: [PATCH 5/7] CodePushInvalidUpdateException --- ...kageException.java => CodePushInvalidUpdateException.java} | 4 ++-- .../java/com/microsoft/codepush/react/CodePushPackage.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename android/app/src/main/java/com/microsoft/codepush/react/{CodePushInvalidPackageException.java => CodePushInvalidUpdateException.java} (58%) diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPackageException.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidUpdateException.java similarity index 58% rename from android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPackageException.java rename to android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidUpdateException.java index 1d6ad09..b678959 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPackageException.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidUpdateException.java @@ -1,7 +1,7 @@ package com.microsoft.codepush.react; -public class CodePushInvalidPackageException extends RuntimeException { - public CodePushInvalidPackageException() { +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."); } } diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java index fd3648b..a89f414 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java @@ -247,7 +247,7 @@ public class CodePushPackage { String relativeBundlePath = CodePushUtils.findJSBundleInUpdateContents(newPackageFolderPath); if (relativeBundlePath == null) { - throw new CodePushInvalidPackageException(); + throw new CodePushInvalidUpdateException(); } else { JSONObject updatePackageJSON = CodePushUtils.convertReadableToJsonObject(updatePackage); try { From 44d6b8da1b20b27454481e3a8c77f8fab1f72bc9 Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Fri, 29 Jan 2016 12:23:03 -0800 Subject: [PATCH 6/7] sort FileUtils methods --- .../microsoft/codepush/react/FileUtils.java | 164 +++++++++--------- 1 file changed, 82 insertions(+), 82 deletions(-) diff --git a/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java b/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java index 5b68a1c..16c5731 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java @@ -17,6 +17,81 @@ 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 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 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 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(); } } } From 0712451622c2e3c511ee021561500277c21bee3f Mon Sep 17 00:00:00 2001 From: Geoffrey Goh Date: Fri, 29 Jan 2016 14:34:42 -0800 Subject: [PATCH 7/7] outsource more methods to CodePushUpdateUtils --- CodePush.m | 52 ++++++++++--------- .../codepush/react/CodePushPackage.java | 16 ++---- .../codepush/react/CodePushUpdateUtils.java | 46 ++++++++++++++++ .../codepush/react/CodePushUtils.java | 25 --------- .../microsoft/codepush/react/FileUtils.java | 2 - 5 files changed, 77 insertions(+), 64 deletions(-) create mode 100644 android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java diff --git a/CodePush.m b/CodePush.m index 5e2370e..2c8c202 100644 --- a/CodePush.m +++ b/CodePush.m @@ -359,32 +359,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); + }]; + }); } /* diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java index a89f414..80645cb 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushPackage.java @@ -225,26 +225,17 @@ public class CodePushPackage { // Merge contents with current update based on the manifest String diffManifestFilePath = CodePushUtils.appendPathComponent(unzippedFolderPath, DIFF_MANIFEST_FILE_NAME); - File diffManifestFile = new File(unzippedFolderPath, DIFF_MANIFEST_FILE_NAME); - if (diffManifestFile.exists()) { + if (FileUtils.fileAtPathExists(diffManifestFilePath)) { String currentPackageFolderPath = getCurrentPackageFolderPath(); - 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); - } + CodePushUpdateUtils.copyNecessaryFilesFromCurrentPackage(diffManifestFilePath, currentPackageFolderPath, newPackageFolderPath); } - // Move merged update contents to a folder with the packageHash as its name 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 = CodePushUtils.findJSBundleInUpdateContents(newPackageFolderPath); + String relativeBundlePath = CodePushUpdateUtils.findJSBundleInUpdateContents(newPackageFolderPath); if (relativeBundlePath == null) { throw new CodePushInvalidUpdateException(); @@ -257,6 +248,7 @@ public class CodePushPackage { RELATIVE_BUNDLE_PATH_KEY + " to value " + relativeBundlePath + " in update package.", e); } + updatePackage = CodePushUtils.convertJsonObjectToWriteable(updatePackageJSON); } } else { diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java new file mode 100644 index 0000000..a8cd21c --- /dev/null +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java @@ -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; + } +} diff --git a/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java b/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java index 750db71..63d2709 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java @@ -183,31 +183,6 @@ public class CodePushUtils { return jsonObj; } - 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; - } - public static WritableMap getWritableMapFromFile(String filePath) throws IOException { String content = FileUtils.readFileToString(filePath); diff --git a/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java b/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java index 16c5731..113df51 100644 --- a/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java +++ b/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java @@ -1,7 +1,5 @@ package com.microsoft.codepush.react; -import android.util.Log; - import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File;