diff --git a/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java b/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java index 12a049b..5ee4225 100644 --- a/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java +++ b/android/src/main/java/cn/reactnative/modules/update/DownloadTask.java @@ -301,7 +301,7 @@ class DownloadTask extends AsyncTask { } } - private void doPatchFromApk(DownloadTaskParams param) throws IOException, JSONException { + private void doPatchFromApk(DownloadTaskParams param,int apkPatchType) throws IOException, JSONException { downloadFile(param); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.targetFile))); @@ -349,8 +349,12 @@ class DownloadTask extends AsyncTask { } if (fn.equals("index.bundlejs.patch")) { foundBundlePatch = true; - // do bsdiff patch - byte[] patched = bsdiffPatch(readOriginBundle(), readBytes(zis)); + + byte[] patched; + if (DownloadTaskParams.isHPatchType(apkPatchType)) // hpatch + patched = hdiffPatch(readOriginBundle(), readBytes(zis)); + else // do bsdiff patch + patched = bsdiffPatch(readOriginBundle(), readBytes(zis)); FileOutputStream fout = new FileOutputStream(new File(param.unzipDirectory, "index.bundlejs")); fout.write(patched); @@ -387,7 +391,7 @@ class DownloadTask extends AsyncTask { } - private void doPatchFromPpk(DownloadTaskParams param) throws IOException, JSONException { + private void doPatchFromPpk(DownloadTaskParams param,int ppkPatchType) throws IOException, JSONException { downloadFile(param); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(param.targetFile))); @@ -427,8 +431,11 @@ class DownloadTask extends AsyncTask { } if (fn.equals("index.bundlejs.patch")) { foundBundlePatch = true; - // do bsdiff patch - byte[] patched = bsdiffPatch(readFile(new File(param.originDirectory, "index.bundlejs")), readBytes(zis)); + byte[] patched; + if (DownloadTaskParams.isHPatchType(ppkPatchType)) // hpatch + patched = hdiffPatch(readFile(new File(param.originDirectory, "index.bundlejs")), readBytes(zis)); + else // do bsdiff patch + patched = bsdiffPatch(readFile(new File(param.originDirectory, "index.bundlejs")), readBytes(zis)); FileOutputStream fout = new FileOutputStream(new File(param.unzipDirectory, "index.bundlejs")); fout.write(patched); @@ -490,10 +497,12 @@ class DownloadTask extends AsyncTask { doFullPatch(params[0]); break; case DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK: - doPatchFromApk(params[0]); + case DownloadTaskParams.TASK_TYPE_HPATCH_FROM_APK: + doPatchFromApk(params[0],type); break; case DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK: - doPatchFromPpk(params[0]); + case DownloadTaskParams.TASK_TYPE_HPATCH_FROM_PPK: + doPatchFromPpk(params[0],type); break; case DownloadTaskParams.TASK_TYPE_CLEANUP: doCleanUp(params[0]); @@ -515,6 +524,8 @@ class DownloadTask extends AsyncTask { case DownloadTaskParams.TASK_TYPE_PATCH_FULL: case DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK: case DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK: + case DownloadTaskParams.TASK_TYPE_HPATCH_FROM_APK: + case DownloadTaskParams.TASK_TYPE_HPATCH_FROM_PPK: try { removeDirectory(params[0].unzipDirectory); } catch (IOException ioException) { diff --git a/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java b/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java index 248a392..fbddeb3 100644 --- a/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java +++ b/android/src/main/java/cn/reactnative/modules/update/DownloadTaskParams.java @@ -8,12 +8,19 @@ import java.io.File; * Created by tdzl2003 on 3/31/16. */ class DownloadTaskParams { - static final int TASK_TYPE_PATCH_FULL = 1; + static final int TASK_TYPE_CLEANUP = 0; //Keep hash & originHash + + static final int TASK_TYPE_PATCH_FULL = 1; static final int TASK_TYPE_PATCH_FROM_APK = 2; static final int TASK_TYPE_PATCH_FROM_PPK = 3; static final int TASK_TYPE_PLAIN_DOWNLOAD = 4; - static final int TASK_TYPE_CLEANUP = 0; //Keep hash & originHash + static final int TASK_TYPE_HPATCH_FROM_APK = 5; + static final int TASK_TYPE_HPATCH_FROM_PPK = 6; + + static boolean isHPatchType(int patchType){ + return (patchType==TASK_TYPE_HPATCH_FROM_APK)||(patchType==TASK_TYPE_HPATCH_FROM_PPK); + } int type; String url; diff --git a/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java b/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java index ab782fb..0b294f6 100644 --- a/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java +++ b/android/src/main/java/cn/reactnative/modules/update/UpdateContext.java @@ -90,13 +90,30 @@ public class UpdateContext { void onDownloadFailed(Throwable error); } + private String zipExtension(int patchType){ + switch (patchType) { + case TASK_TYPE_PATCH_FULL: + return ".ppk"; + case TASK_TYPE_PATCH_FROM_APK: + return ".apk.patch"; + case TASK_TYPE_PATCH_FROM_PPK: + return ".ppk.patch"; + case TASK_TYPE_HPATCH_FROM_APK: + return ".apk.hpatch"; + case TASK_TYPE_HPATCH_FROM_PPK: + return ".ppk.hpatch"; + default: + return "";//unknown type + } + } + public void downloadFullUpdate(String url, String hash, DownloadFileListener listener) { DownloadTaskParams params = new DownloadTaskParams(); params.type = DownloadTaskParams.TASK_TYPE_PATCH_FULL; params.url = url; params.hash = hash; params.listener = listener; - params.targetFile = new File(rootDir, hash + ".ppk"); + params.targetFile = new File(rootDir, hash + zipExtension(params.type)); params.unzipDirectory = new File(rootDir, hash); new DownloadTask(context).executeOnExecutor(this.executor, params); } @@ -112,25 +129,25 @@ public class UpdateContext { new DownloadTask(context).executeOnExecutor(this.executor, params); } - public void downloadPatchFromApk(String url, String hash, DownloadFileListener listener) { + public void downloadPatchFromApk(String url, String hash,int apkPatchType,DownloadFileListener listener) { DownloadTaskParams params = new DownloadTaskParams(); - params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK; + params.type = apkPatchType; params.url = url; params.hash = hash; params.listener = listener; - params.targetFile = new File(rootDir, hash + ".apk.patch"); + params.targetFile = new File(rootDir, hash + zipExtension(params.type)); params.unzipDirectory = new File(rootDir, hash); new DownloadTask(context).executeOnExecutor(this.executor, params); } - public void downloadPatchFromPpk(String url, String hash, String originHash, DownloadFileListener listener) { + public void downloadPatchFromPpk(String url,String hash,String originHash,int ppkPatchType,DownloadFileListener listener) { DownloadTaskParams params = new DownloadTaskParams(); - params.type = DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK; + params.type = ppkPatchType; params.url = url; params.hash = hash; params.originHash = originHash; params.listener = listener; - params.targetFile = new File(rootDir, originHash + "-" + hash + ".ppk.patch"); + params.targetFile = new File(rootDir, originHash + "-" + hash + zipExtension(params.type)); params.unzipDirectory = new File(rootDir, hash); params.originDirectory = new File(rootDir, originHash); new DownloadTask(context).executeOnExecutor(this.executor, params); diff --git a/android/src/main/java/cn/reactnative/modules/update/UpdateModule.java b/android/src/main/java/cn/reactnative/modules/update/UpdateModule.java index 046e606..cb79bb2 100644 --- a/android/src/main/java/cn/reactnative/modules/update/UpdateModule.java +++ b/android/src/main/java/cn/reactnative/modules/update/UpdateModule.java @@ -135,15 +135,36 @@ public class UpdateModule extends ReactContextBaseJavaModule { } } - - @ReactMethod - public void downloadPatchFromPackage(ReadableMap options, final Promise promise) { + private void _downloadPatchFromPackage(ReadableMap options, final Promise promise,int apkPatchType) { String url = options.getString("updateUrl"); String hash = options.getString("hash"); if (hash == null) { hash = options.getString("hashName"); } - updateContext.downloadPatchFromApk(url, hash, new UpdateContext.DownloadFileListener() { + updateContext.downloadPatchFromApk(url,hash,apkPatchType,new UpdateContext.DownloadFileListener() { + @Override + public void onDownloadCompleted(DownloadTaskParams params) { + promise.resolve(null); + } + + @Override + public void onDownloadFailed(Throwable error) { + promise.reject(error); + } + }); + } + + private void _downloadPatchFromPpk(ReadableMap options, final Promise promise,int ppkPatchType) { + String url = options.getString("updateUrl"); + String hash = options.getString("hash"); + if (hash == null) { + hash = options.getString("hashName"); + } + String originHash = options.getString("originHash"); + if (originHash == null) { + originHash = options.getString(("originHashName")); + } + updateContext.downloadPatchFromPpk(url,hash,originHash,ppkPatchType,new UpdateContext.DownloadFileListener() { @Override public void onDownloadCompleted(DownloadTaskParams params) { promise.resolve(null); @@ -157,27 +178,23 @@ public class UpdateModule extends ReactContextBaseJavaModule { } @ReactMethod - public void downloadPatchFromPpk(ReadableMap options, final Promise promise) { - String url = options.getString("updateUrl"); - String hash = options.getString("hash"); - if (hash == null) { - hash = options.getString("hashName"); - } - String originHash = options.getString("originHash"); - if (originHash == null) { - originHash = options.getString(("originHashName")); - } - updateContext.downloadPatchFromPpk(url, hash, originHash, new UpdateContext.DownloadFileListener() { - @Override - public void onDownloadCompleted(DownloadTaskParams params) { - promise.resolve(null); - } + public void downloadPatchFromPackage(ReadableMap options, final Promise promise) { + _downloadPatchFromPackage(options,promise,DownloadTaskParams.TASK_TYPE_PATCH_FROM_APK); + } + + @ReactMethod + public void downloadHPatchFromPackage(ReadableMap options, final Promise promise) { + _downloadPatchFromPackage(options,promise,DownloadTaskParams.TASK_TYPE_HPATCH_FROM_APK); + } - @Override - public void onDownloadFailed(Throwable error) { - promise.reject(error); - } - }); + @ReactMethod + public void downloadPatchFromPpk(ReadableMap options, final Promise promise) { + _downloadPatchFromPpk(options,promise,DownloadTaskParams.TASK_TYPE_PATCH_FROM_PPK); + } + + @ReactMethod + public void downloadHPatchFromPpk(ReadableMap options, final Promise promise) { + _downloadPatchFromPpk(options,promise,DownloadTaskParams.TASK_TYPE_HPATCH_FROM_PPK); } @ReactMethod diff --git a/ios/RCTPushy/BSDiff/BSDiff.h b/ios/RCTPushy/BSDiff/BSDiff.h index d334f3c..36a4e54 100644 --- a/ios/RCTPushy/BSDiff/BSDiff.h +++ b/ios/RCTPushy/BSDiff/BSDiff.h @@ -13,4 +13,9 @@ + (BOOL)bsdiffPatch:(NSString *)path origin:(NSString *)origin toDestination:(NSString *)destination; + ++ (BOOL)hdiffPatch:(NSString *)path + origin:(NSString *)origin + toDestination:(NSString *)destination; + @end diff --git a/ios/RCTPushy/BSDiff/BSDiff.m b/ios/RCTPushy/BSDiff/BSDiff.m index e63e63c..db26a92 100644 --- a/ios/RCTPushy/BSDiff/BSDiff.m +++ b/ios/RCTPushy/BSDiff/BSDiff.m @@ -8,7 +8,7 @@ #import "BSDiff.h" #include "bspatch.h" - +#include "../../../android/jni/hpatch.h" @implementation BSDiff @@ -34,4 +34,26 @@ return YES; } ++ (BOOL)hdiffPatch:(NSString *)patch + origin:(NSString *)origin + toDestination:(NSString *)destination +{ + if (![[NSFileManager defaultManager] fileExistsAtPath:patch]) { + return NO; + } + if (![[NSFileManager defaultManager] fileExistsAtPath:origin]) { + return NO; + } + + if ([[NSFileManager defaultManager] fileExistsAtPath:destination]) { + [[NSFileManager defaultManager] removeItemAtPath:destination error:nil]; + } + + int err = hpatch_by_file([origin UTF8String], [destination UTF8String], [patch UTF8String]); + if (err) { + return NO; + } + return YES; +} + @end diff --git a/ios/RCTPushy/RCTPushy.m b/ios/RCTPushy/RCTPushy.m index 77e9fd4..431fc44 100644 --- a/ios/RCTPushy/RCTPushy.m +++ b/ios/RCTPushy/RCTPushy.m @@ -39,6 +39,7 @@ static NSString * const BUNDLE_PATCH_NAME = @"index.bundlejs.patch"; // error def static NSString * const ERROR_OPTIONS = @"options error"; static NSString * const ERROR_BSDIFF = @"bsdiff error"; +static NSString * const ERROR_HDIFFPATCH = @"hdiffpatch error"; static NSString * const ERROR_FILE_OPERATION = @"file operation error"; // event def @@ -53,6 +54,9 @@ typedef NS_ENUM(NSInteger, PushyType) { PushyTypeFullDownload = 1, PushyTypePatchFromPackage = 2, PushyTypePatchFromPpk = 3, + //TASK_TYPE_PLAIN_DOWNLOAD=4? + PushyTypeHPatchFromPackage = 5, + PushyTypeHPatchFromPpk = 6, }; static BOOL ignoreRollback = false; @@ -253,6 +257,34 @@ RCT_EXPORT_METHOD(downloadPatchFromPpk:(NSDictionary *)options }]; } +RCT_EXPORT_METHOD(downloadHPatchFromPackage:(NSDictionary *)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [self doPushy:PushyTypeHPatchFromPackage options:options callback:^(NSError *error) { + if (error) { + reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error); + } + else { + resolve(nil); + } + }]; +} + +RCT_EXPORT_METHOD(downloadHPatchFromPpk:(NSDictionary *)options + resolver:(RCTPromiseResolveBlock)resolve + rejecter:(RCTPromiseRejectBlock)reject) +{ + [self doPushy:PushyTypeHPatchFromPpk options:options callback:^(NSError *error) { + if (error) { + reject([NSString stringWithFormat: @"%lu", (long)error.code], error.localizedDescription, error); + } + else { + resolve(nil); + } + }]; +} + RCT_EXPORT_METHOD(setNeedUpdate:(NSDictionary *)options) { NSString *hash = options[@"hash"]; @@ -349,7 +381,7 @@ RCT_EXPORT_METHOD(markSuccess) return; } NSString *originHash = [RCTConvert NSString:options[@"originHash"]]; - if (type == PushyTypePatchFromPpk && originHash <= 0) { + if (((type == PushyTypePatchFromPpk)||(type == PushyTypeHPatchFromPpk)) && originHash <= 0) { callback([self errorWithMessage:ERROR_OPTIONS]); return; } @@ -398,19 +430,27 @@ RCT_EXPORT_METHOD(markSuccess) else { switch (type) { case PushyTypePatchFromPackage: + case PushyTypeHPatchFromPackage: { NSString *sourceOrigin = [[NSBundle mainBundle] resourcePath]; NSString *bundleOrigin = [[RCTPushy binaryBundleURL] path]; - [self patch:hash fromBundle:bundleOrigin source:sourceOrigin callback:callback]; + if (type==PushyTypeHPatchFromPackage) + [self hpatch:hash fromBundle:bundleOrigin source:sourceOrigin callback:callback]; + else + [self patch:hash fromBundle:bundleOrigin source:sourceOrigin callback:callback]; } break; case PushyTypePatchFromPpk: + case PushyTypeHPatchFromPpk: { NSString *lastVersionDir = [dir stringByAppendingPathComponent:originHash]; NSString *sourceOrigin = lastVersionDir; NSString *bundleOrigin = [lastVersionDir stringByAppendingPathComponent:BUNDLE_FILE_NAME]; - [self patch:hash fromBundle:bundleOrigin source:sourceOrigin callback:callback]; + if (type==PushyTypeHPatchFromPpk) + [self hpatch:hash fromBundle:bundleOrigin source:sourceOrigin callback:callback]; + else + [self patch:hash fromBundle:bundleOrigin source:sourceOrigin callback:callback]; } break; default: @@ -424,14 +464,15 @@ RCT_EXPORT_METHOD(markSuccess) }]; } -- (void)patch:(NSString *)hash fromBundle:(NSString *)bundleOrigin source:(NSString *)sourceOrigin callback:(void (^)(NSError *error))callback +- (void)_dopatch:(NSString *)hash fromBundle:(NSString *)bundleOrigin source:(NSString *)sourceOrigin + callback:(void (^)(NSError *error))callback isHPatch:(BOOL)isHPatch { NSString *unzipDir = [[RCTPushy downloadDir] stringByAppendingPathComponent:hash]; NSString *sourcePatch = [unzipDir stringByAppendingPathComponent:SOURCE_PATCH_NAME]; NSString *bundlePatch = [unzipDir stringByAppendingPathComponent:BUNDLE_PATCH_NAME]; NSString *destination = [unzipDir stringByAppendingPathComponent:BUNDLE_FILE_NAME]; - [_fileManager bsdiffFileAtPath:bundlePatch fromOrigin:bundleOrigin toDestination:destination completionHandler:^(BOOL success) { + void (^)(BOOL success) completionHandler=^(BOOL success) { if (success) { NSData *data = [NSData dataWithContentsOfFile:sourcePatch]; NSError *error = nil; @@ -454,9 +495,25 @@ RCT_EXPORT_METHOD(markSuccess) }]; } else { - callback([self errorWithMessage:ERROR_BSDIFF]); + if (isHPatch) + callback([self errorWithMessage:ERROR_HDIFFPATCH]); + else + callback([self errorWithMessage:ERROR_BSDIFF]); } - }]; + } + if (isHPatch) + [_fileManager hdiffFileAtPath:bundlePatch fromOrigin:bundleOrigin toDestination:destination completionHandler:completionHandler]; + else + [_fileManager bsdiffFileAtPath:bundlePatch fromOrigin:bundleOrigin toDestination:destination completionHandler:completionHandler]; +} + +- (void)patch:(NSString *)hash fromBundle:(NSString *)bundleOrigin source:(NSString *)sourceOrigin callback:(void (^)(NSError *error))callback +{ + [self _dopatch:hash fromBundle:bundleOrigin source:sourceOrigin callback:callback isHPatch:false]; +} +- (void)hpatch:(NSString *)hash fromBundle:(NSString *)bundleOrigin source:(NSString *)sourceOrigin callback:(void (^)(NSError *error))callback +{ + [self _dopatch:hash fromBundle:bundleOrigin source:sourceOrigin callback:callback isHPatch:true]; } - (void)clearInvalidFiles @@ -488,6 +545,10 @@ RCT_EXPORT_METHOD(markSuccess) return @".ipa.patch"; case PushyTypePatchFromPpk: return @".ppk.patch"; + case PushyTypeHPatchFromPackage: + return @".ipa.hpatch"; + case PushyTypeHPatchFromPpk: + return @".ppk.hpatch"; default: break; } diff --git a/ios/RCTPushy/RCTPushyManager.h b/ios/RCTPushy/RCTPushyManager.h index 05b90d1..7be53be 100644 --- a/ios/RCTPushy/RCTPushyManager.h +++ b/ios/RCTPushy/RCTPushyManager.h @@ -23,6 +23,11 @@ toDestination:(NSString *)destination completionHandler:(void (^)(BOOL success))completionHandler; +- (void)hdiffFileAtPath:(NSString *)path + fromOrigin:(NSString *)origin + toDestination:(NSString *)destination + completionHandler:(void (^)(BOOL success))completionHandler; + - (void)copyFiles:(NSDictionary *)filesDic fromDir:(NSString *)fromDir toDir:(NSString *)toDir diff --git a/ios/RCTPushy/RCTPushyManager.m b/ios/RCTPushy/RCTPushyManager.m index 871b5ca..46f0182 100644 --- a/ios/RCTPushy/RCTPushyManager.m +++ b/ios/RCTPushy/RCTPushyManager.m @@ -88,6 +88,19 @@ }); } +- (void)hdiffFileAtPath:(NSString *)path + fromOrigin:(NSString *)origin + toDestination:(NSString *)destination + completionHandler:(void (^)(BOOL success))completionHandler +{ + dispatch_async(_opQueue, ^{ + BOOL success = [BSDiff hdiffPatch:path origin:origin toDestination:destination]; + if (completionHandler) { + completionHandler(success); + } + }); +} + - (void)copyFiles:(NSDictionary *)filesDic fromDir:(NSString *)fromDir toDir:(NSString *)toDir diff --git a/lib/index.d.ts b/lib/index.d.ts index c78bfa2..5555865 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -25,6 +25,8 @@ export interface UpdateAvailableResult { metaInfo: string; pdiffUrl: string; diffUrl?: string; + phdiffUrl?: string; + hdiffUrl?: string; } export type CheckResult = diff --git a/lib/index.js b/lib/index.js index 2f05597..26d341f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -167,6 +167,19 @@ export async function downloadUpdate(options, eventListeners) { updateUrl: options.pdiffUrl, hash: options.hash, }); + }else if (options.hdiffUrl) { + logger('downloading hdiff'); + await Pushy.downloadHPatchFromPpk({ + updateUrl: options.hdiffUrl, + hash: options.hash, + originHash: currentVersion, + }); + } else if (options.phdiffUrl) { + logger('downloading phdiff'); + await Pushy.downloadHPatchFromPackage({ + updateUrl: options.phdiffUrl, + hash: options.hash, + }); } progressHandler && progressHandler.remove(); return options.hash;