diff --git a/CodePush.js b/CodePush.js index 8d8244a..8a2f103 100644 --- a/CodePush.js +++ b/CodePush.js @@ -60,7 +60,7 @@ async function checkForUpdate(deploymentKey = null) { if (!update || update.updateAppVersion || (update.packageHash === localPackage.packageHash)) { return null; } else { - const remotePackage = { ...update, ...PackageMixins.remote }; + const remotePackage = { ...update, ...PackageMixins.remote(sdk.reportStatusDownload) }; remotePackage.failedInstall = await NativeCodePush.isFailedUpdate(remotePackage.packageHash); remotePackage.deploymentKey = deploymentKey || nativeConfig.deploymentKey; return remotePackage; @@ -102,8 +102,8 @@ function getPromisifiedSdk(requestFetchAdapter, config) { }); }); }; - - sdk.reportStatus = (package, status) => { + + sdk.reportStatusDeploy = (package, status) => { return new Promise((resolve, reject) => { module.exports.AcquisitionSdk.prototype.reportStatusDeploy.call(sdk, package, status, (err) => { if (err) { @@ -114,7 +114,19 @@ function getPromisifiedSdk(requestFetchAdapter, config) { }); }); }; - + + sdk.reportStatusDownload = (package, status) => { + return new Promise((resolve, reject) => { + module.exports.AcquisitionSdk.prototype.reportStatusDownload.call(sdk, package, (err) => { + if (err) { + reject(err); + } else { + resolve(); + } + }); + }); + }; + return sdk; } @@ -129,13 +141,12 @@ async function notifyApplicationReady() { if (statusReport) { const config = await getConfiguration(); if (statusReport.appVersion) { - config.appVersion = statusReport.appVersion; const sdk = getPromisifiedSdk(requestFetchAdapter, config); - sdk.reportStatus(); + sdk.reportStatusDeploy(); } else { config.deploymentKey = statusReport.package.deploymentKey; const sdk = getPromisifiedSdk(requestFetchAdapter, config); - sdk.reportStatus(statusReport.package, statusReport.status); + sdk.reportStatusDeploy(statusReport.package, statusReport.status); } } } diff --git a/CodePush.m b/CodePush.m index b886eea..7d42b0a 100644 --- a/CodePush.m +++ b/CodePush.m @@ -13,6 +13,7 @@ RCT_EXPORT_MODULE() +static BOOL didRollback = NO; static BOOL testConfigurationFlag = NO; // These constants represent valid deployment statuses @@ -189,6 +190,7 @@ static NSString *const PackageIsPendingKey = @"isPending"; NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; NSDictionary *pendingUpdate = [preferences objectForKey:PendingUpdateKey]; if (pendingUpdate) { + didRollback = NO; _isFirstRunAfterUpdate = YES; BOOL updateIsLoading = [pendingUpdate[PendingUpdateIsLoadingKey] boolValue]; if (updateIsLoading) { @@ -196,6 +198,7 @@ static NSString *const PackageIsPendingKey = @"isPending"; // Therefore, deduce that it is a broken update and rollback. NSLog(@"Update did not finish loading the last time, rolling back to a previous version."); [self rollbackPackage]; + didRollback = YES; } else { // Mark that we tried to initialize the new update, so that if it crashes, // we will know that we need to rollback when the app next starts. @@ -220,7 +223,7 @@ static NSString *const PackageIsPendingKey = @"isPending"; { NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey]; - if (failedUpdates == nil) { + if (failedUpdates == nil || packageHash == nil) { return NO; } else { for (NSDictionary *failedPackage in failedUpdates) @@ -535,6 +538,39 @@ RCT_EXPORT_METHOD(notifyApplicationReady:(RCTPromiseResolveBlock)resolve RCT_EXPORT_METHOD(getNewStatusReport:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { + if (didRollback) { + // Check if there was a rollback that was not yet reported + NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; + NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey]; + if (failedUpdates) { + NSDictionary* lastFailedPackage = [failedUpdates lastObject]; + if (lastFailedPackage) { + NSString* lastFailedPackageIdentifier = [self getPackageStatusReportIdentifier:lastFailedPackage]; + if (lastFailedPackageIdentifier && [self isDeploymentStatusNotYetReported:lastFailedPackageIdentifier]) { + [self recordDeploymentStatusReported:lastFailedPackageIdentifier + status:DeploymentFailed]; + resolve(@{ @"package": lastFailedPackage, @"status": DeploymentFailed }); + return; + } + } + } + } + + if (_isFirstRunAfterUpdate) { + // Check if the current CodePush package has been reported + NSError *error; + NSDictionary* currentPackage = [CodePushPackage getCurrentPackage:&error]; + if (currentPackage) { + NSString* currentPackageIdentifier = [self getPackageStatusReportIdentifier:currentPackage]; + if (currentPackageIdentifier && [self isDeploymentStatusNotYetReported:currentPackageIdentifier]) { + [self recordDeploymentStatusReported:currentPackageIdentifier + status:DeploymentSucceeded]; + resolve(@{ @"package": currentPackage, @"status": DeploymentSucceeded }); + return; + } + } + } + // Check if the current appVersion has been reported. NSString *appVersion = [[CodePushConfig current] appVersion]; if ([self isDeploymentStatusNotYetReported:appVersion]) { @@ -544,35 +580,6 @@ RCT_EXPORT_METHOD(getNewStatusReport:(RCTPromiseResolveBlock)resolve return; } - // Check if there was a rollback that was not yet reported - NSUserDefaults *preferences = [NSUserDefaults standardUserDefaults]; - NSMutableArray *failedUpdates = [preferences objectForKey:FailedUpdatesKey]; - if (failedUpdates) { - NSDictionary* lastFailedPackage = [failedUpdates lastObject]; - if (lastFailedPackage) { - NSString* lastFailedPackageIdentifier = [self getPackageStatusReportIdentifier:lastFailedPackage]; - if (lastFailedPackageIdentifier && [self isDeploymentStatusNotYetReported:lastFailedPackageIdentifier]) { - [self recordDeploymentStatusReported:lastFailedPackageIdentifier - status:DeploymentFailed]; - resolve(@{ @"package": lastFailedPackage, @"status": DeploymentFailed }); - return; - } - } - } - - // Check if the current CodePush package has been reported - NSError *error; - NSDictionary* currentPackage = [CodePushPackage getCurrentPackage:&error]; - if (currentPackage) { - NSString* currentPackageIdentifier = [self getPackageStatusReportIdentifier:currentPackage]; - if (currentPackageIdentifier && [self isDeploymentStatusNotYetReported:currentPackageIdentifier]) { - [self recordDeploymentStatusReported:currentPackageIdentifier - status:DeploymentSucceeded]; - resolve(@{ @"package": currentPackage, @"status": DeploymentSucceeded }); - return; - } - } - resolve([NSNull null]); return; } diff --git a/CodePushConfig.m b/CodePushConfig.m index 31884fc..341ba94 100644 --- a/CodePushConfig.m +++ b/CodePushConfig.m @@ -9,9 +9,9 @@ static CodePushConfig *_currentConfig; static NSString * const AppVersionConfigKey = @"appVersion"; static NSString * const BuildVdersionConfigKey = @"buildVersion"; +static NSString * const ClientUniqueIDConfigKey = @"clientUniqueId"; static NSString * const DeploymentKeyConfigKey = @"deploymentKey"; static NSString * const ServerURLConfigKey = @"serverUrl"; -static NSString * const ClientUniqueIDConfigKey = @"clientUniqueId"; + (instancetype)current { @@ -34,12 +34,7 @@ static NSString * const ClientUniqueIDConfigKey = @"clientUniqueId"; NSString *serverURL = [infoDictionary objectForKey:@"CodePushServerURL"]; NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults]; - NSString* clientUniqueId = [userDefaults stringForKey:ClientUniqueIDConfigKey]; - if (clientUniqueId == nil) { - clientUniqueId = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; - [userDefaults setObject:clientUniqueId forKey:ClientUniqueIDConfigKey]; - [userDefaults synchronize]; - } + NSString* clientUniqueId = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; if (!serverURL) { serverURL = @"https://codepush.azurewebsites.net/"; @@ -49,8 +44,8 @@ static NSString * const ClientUniqueIDConfigKey = @"clientUniqueId"; appVersion,AppVersionConfigKey, buildVersion,BuildVdersionConfigKey, serverURL,ServerURLConfigKey, - deploymentKey,DeploymentKeyConfigKey, clientUniqueId,ClientUniqueIDConfigKey, + deploymentKey,DeploymentKeyConfigKey, nil]; return self; diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/FirstUpdateTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/FirstUpdateTest.js index 21fbd0e..1b80c04 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/FirstUpdateTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/FirstUpdateTest.js @@ -24,7 +24,7 @@ let FirstUpdateTest = createTestCaseComponent( }, async () => { let update = await CodePush.checkForUpdate(); - assert.equal(JSON.stringify(update), JSON.stringify({ ...serverPackage, ...PackageMixins.remote, failedInstall: false }), "checkForUpdate did not return the update from the server"); + assert.equal(JSON.stringify(update), JSON.stringify({ ...serverPackage, ...PackageMixins.remote(), failedInstall: false }), "checkForUpdate did not return the update from the server"); } ); diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/NewUpdateTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/NewUpdateTest.js index c097c16..8f196c7 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/NewUpdateTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/NewUpdateTest.js @@ -23,7 +23,7 @@ let NewUpdateTest = createTestCaseComponent( }, async () => { let update = await CodePush.checkForUpdate(); - assert.equal(JSON.stringify(update), JSON.stringify({ ...serverPackage, ...PackageMixins.remote, failedInstall: false }), "checkForUpdate did not return the update from the server"); + assert.equal(JSON.stringify(update), JSON.stringify({ ...serverPackage, ...PackageMixins.remote(), failedInstall: false }), "checkForUpdate did not return the update from the server"); } ); diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/SwitchDeploymentKeyTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/SwitchDeploymentKeyTest.js index 7427d72..74628ba 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/SwitchDeploymentKeyTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/CheckForUpdateTests/testcases/SwitchDeploymentKeyTest.js @@ -25,7 +25,7 @@ let SwitchDeploymentKeyTest = createTestCaseComponent( }, async () => { let update = await CodePush.checkForUpdate(deploymentKey); - assert.equal(JSON.stringify(update), JSON.stringify({ ...serverPackage, ...PackageMixins.remote, failedInstall: false, deploymentKey }), "checkForUpdate did not return the update from the server"); + assert.equal(JSON.stringify(update), JSON.stringify({ ...serverPackage, ...PackageMixins.remote(), failedInstall: false, deploymentKey }), "checkForUpdate did not return the update from the server"); } ); diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/testcases/DownloadProgressTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/testcases/DownloadProgressTest.js index ee7bd98..ac419e3 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/testcases/DownloadProgressTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/DownloadProgressTests/testcases/DownloadProgressTest.js @@ -28,7 +28,7 @@ let DownloadProgressTest = createTestCaseComponent( "should successfully download all the bytes contained in the test packages", () => { testPackages.forEach((aPackage, index) => { - testPackages[index] = Object.assign(aPackage, PackageMixins.remote); + testPackages[index] = Object.assign(aPackage, PackageMixins.remote()); }); return Promise.resolve(); }, diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/resources/RollbackTestBundleV1.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/resources/RollbackTestBundleV1.js index 565ab06..2cc5faa 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/resources/RollbackTestBundleV1.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/resources/RollbackTestBundleV1.js @@ -27,7 +27,7 @@ let RollbackTest = React.createClass({ await NativeCodePush.downloadAndReplaceCurrentBundle("http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/RollbackTestBundleV1Pass.includeRequire.runModule.bundle?platform=ios&dev=true"); } - remotePackage = Object.assign(remotePackage, PackageMixins.remote); + remotePackage = Object.assign(remotePackage, PackageMixins.remote()); let localPackage = await remotePackage.download(); return await localPackage.install(NativeCodePush.codePushInstallModeImmediate); diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/InstallModeImmediateTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/InstallModeImmediateTest.js index 7c74f3d..bc74375 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/InstallModeImmediateTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/InstallModeImmediateTest.js @@ -21,7 +21,7 @@ let InstallModeImmediateTest = createTestCaseComponent( remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/PassInstallModeImmediateTest.includeRequire.runModule.bundle?platform=ios&dev=true" } - remotePackage = Object.assign(remotePackage, PackageMixins.remote); + remotePackage = Object.assign(remotePackage, PackageMixins.remote()); }, async () => { let localPackage = await remotePackage.download(); diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/InstallModeOnNextRestartTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/InstallModeOnNextRestartTest.js index 9381951..1106bc1 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/InstallModeOnNextRestartTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/InstallModeOnNextRestartTest.js @@ -22,7 +22,7 @@ let InstallModeOnNextRestartTest = createTestCaseComponent( remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/PassInstallModeOnNextRestartTest.includeRequire.runModule.bundle?platform=ios&dev=true" } - remotePackage = Object.assign(remotePackage, PackageMixins.remote); + remotePackage = Object.assign(remotePackage, PackageMixins.remote()); }, async () => { let localPackage = await remotePackage.download(); diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/InstallModeOnNextResumeTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/InstallModeOnNextResumeTest.js index ed695e7..48e88a8 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/InstallModeOnNextResumeTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/InstallModeOnNextResumeTest.js @@ -21,7 +21,7 @@ let InstallModeOnNextResumeTest = createTestCaseComponent( remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/PassInstallModeOnNextResumeTest.includeRequire.runModule.bundle?platform=ios&dev=true" } - remotePackage = Object.assign(remotePackage, PackageMixins.remote); + remotePackage = Object.assign(remotePackage, PackageMixins.remote()); }, async () => { let localPackage = await remotePackage.download() diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/IsFailedUpdateTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/IsFailedUpdateTest.js index 5669de1..2fb11b9 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/IsFailedUpdateTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/IsFailedUpdateTest.js @@ -21,7 +21,7 @@ let IsFailedUpdateTest = createTestCaseComponent( remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/IsFailedUpdateTestBundleV1.includeRequire.runModule.bundle?platform=ios&dev=true" } - remotePackage = Object.assign(remotePackage, PackageMixins.remote); + remotePackage = Object.assign(remotePackage, PackageMixins.remote()); }, async () => { let localPackage = await remotePackage.download(); diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/IsFirstRunTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/IsFirstRunTest.js index f8c28cd..02644b3 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/IsFirstRunTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/IsFirstRunTest.js @@ -20,7 +20,7 @@ let IsFirstRunTest = createTestCaseComponent( remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/CheckIsFirstRunAndPassTest.includeRequire.runModule.bundle?platform=ios&dev=true" } - remotePackage = Object.assign(remotePackage, PackageMixins.remote); + remotePackage = Object.assign(remotePackage, PackageMixins.remote()); }, async () => { let localPackage = await remotePackage.download(); diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/IsPendingTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/IsPendingTest.js index 3e61f71..355a633 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/IsPendingTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/IsPendingTest.js @@ -21,7 +21,7 @@ let IsPendingTest = createTestCaseComponent( remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/CheckIsFirstRunAndPassTest.includeRequire.runModule.bundle?platform=ios&dev=true" } - remotePackage = Object.assign(remotePackage, PackageMixins.remote); + remotePackage = Object.assign(remotePackage, PackageMixins.remote()); }, async () => { let localPackage = await remotePackage.download(); diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/NotifyApplicationReadyTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/NotifyApplicationReadyTest.js index d77e125..15b7477 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/NotifyApplicationReadyTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/NotifyApplicationReadyTest.js @@ -21,7 +21,7 @@ let NotifyApplicationReadyTest = createTestCaseComponent( remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/NotifyApplicationReadyAndRestart.includeRequire.runModule.bundle?platform=ios&dev=true" } - remotePackage = Object.assign(remotePackage, PackageMixins.remote); + remotePackage = Object.assign(remotePackage, PackageMixins.remote()); }, async () => { let localPackage = await remotePackage.download() diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/RollbackTest.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/RollbackTest.js index d002965..bf6e433 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/RollbackTest.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/InstallUpdateTests/testcases/RollbackTest.js @@ -21,7 +21,7 @@ let RollbackTest = createTestCaseComponent( remotePackage.downloadUrl = "http://localhost:8081/CodePushDemoAppTests/InstallUpdateTests/resources/RollbackTestBundleV1.includeRequire.runModule.bundle?platform=ios&dev=true" } - remotePackage = Object.assign(remotePackage, PackageMixins.remote); + remotePackage = Object.assign(remotePackage, PackageMixins.remote()); }, async () => { let localPackage = await remotePackage.download() diff --git a/Examples/CodePushDemoApp/CodePushDemoAppTests/utils/mockAcquisitionSdk.js b/Examples/CodePushDemoApp/CodePushDemoAppTests/utils/mockAcquisitionSdk.js index aebc467..ffff99b 100644 --- a/Examples/CodePushDemoApp/CodePushDemoAppTests/utils/mockAcquisitionSdk.js +++ b/Examples/CodePushDemoApp/CodePushDemoAppTests/utils/mockAcquisitionSdk.js @@ -13,7 +13,12 @@ function createMockAcquisitionSdk(serverPackage, localPackage, expectedDeploymen callback(/*err:*/ null, serverPackage); }; - AcquisitionManager.prototype.reportStatus = (status, message, callback) => { + AcquisitionManager.prototype.reportStatusDeploy = (package, status, callback) => { + // No-op and return success. + callback(null, null); + }; + + AcquisitionManager.prototype.reportStatusDownload = (package, callback) => { // No-op and return success. callback(null, null); }; 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 ae74487..1b45069 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 @@ -41,6 +41,8 @@ import java.util.zip.ZipFile; public class CodePush { private static boolean testConfigurationFlag = false; + private static boolean didRollback = false; + private boolean didUpdate = false; private String assetsBundleFileName; @@ -233,6 +235,7 @@ public class CodePush { JSONObject pendingUpdate = getPendingUpdate(); if (pendingUpdate != null) { didUpdate = true; + didRollback = false; try { boolean updateIsLoading = pendingUpdate.getBoolean(PENDING_UPDATE_IS_LOADING_KEY); if (updateIsLoading) { @@ -240,6 +243,7 @@ public class CodePush { // Therefore, deduce that it is a broken update and rollback. CodePushUtils.log("Update did not finish loading the last time, rolling back to a previous version."); rollbackPackage(); + didRollback = true; } else { // Clear the React dev bundle cache so that new updates can be loaded. if (this.isDebugMode) { @@ -275,16 +279,18 @@ public class CodePush { private boolean isFailedHash(String packageHash) { JSONArray failedUpdates = getFailedUpdates(); - for (int i = 0; i < failedUpdates.length(); i++) { - JSONObject failedPackage = null; - try { - failedPackage = failedUpdates.getJSONObject(i); - String failedPackageHash = failedPackage.getString(PACKAGE_HASH_KEY); - if (packageHash.equals(failedPackageHash)) { - return true; + if (packageHash != null) { + for (int i = 0; i < failedUpdates.length(); i++) { + JSONObject failedPackage = null; + try { + failedPackage = failedUpdates.getJSONObject(i); + String failedPackageHash = failedPackage.getString(PACKAGE_HASH_KEY); + if (packageHash.equals(failedPackageHash)) { + return true; + } + } catch (JSONException e) { + throw new CodePushUnknownException("Unable to read failedUpdates data stored in SharedPreferences.", e); } - } catch (JSONException e) { - throw new CodePushUnknownException("Unable to read failedUpdates data stored in SharedPreferences.", e); } } @@ -484,6 +490,49 @@ public class CodePush { @ReactMethod public void getNewStatusReport(Promise promise) { + // Check if there was a rollback that was not yet reported + if (didRollback) { + JSONArray failedUpdates = getFailedUpdates(); + if (failedUpdates != null && failedUpdates.length() > 0) { + try { + JSONObject lastFailedPackageJSON = failedUpdates.getJSONObject(failedUpdates.length() - 1); + WritableMap lastFailedPackage = CodePushUtils.convertJsonObjectToWriteable(lastFailedPackageJSON); + String lastFailedPackageIdentifier = getPackageStatusReportIdentifier(lastFailedPackage); + if (lastFailedPackage != null && isDeploymentStatusNotYetReported(lastFailedPackageIdentifier)) { + recordDeploymentStatusReported(lastFailedPackageIdentifier, DEPLOYMENT_FAILED_STATUS); + WritableNativeMap reportMap = new WritableNativeMap(); + reportMap.putMap("package", lastFailedPackage); + reportMap.putString("status", DEPLOYMENT_FAILED_STATUS); + promise.resolve(reportMap); + return; + } + } catch (JSONException e) { + throw new CodePushUnknownException("Unable to read failed updates information stored in SharedPreferences.", e); + } + } + } + + // Check if the current CodePush package has been reported + if (didUpdate) { + try { + WritableMap currentPackage = codePushPackage.getCurrentPackage(); + if (currentPackage != null) { + String currentPackageIdentifier = getPackageStatusReportIdentifier(currentPackage); + if (currentPackageIdentifier != null && isDeploymentStatusNotYetReported(currentPackageIdentifier)) { + recordDeploymentStatusReported(currentPackageIdentifier, DEPLOYMENT_SUCCEEDED_STATUS); + WritableNativeMap reportMap = new WritableNativeMap(); + reportMap.putMap("package", currentPackage); + reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS); + promise.resolve(reportMap); + return; + } + } + } catch (IOException e) { + // If didUpdate is true, there should be a current package, so this should not happen. + throw new CodePushUnknownException("Error getting current package after an update.", e); + } + } + // Check if the current appVersion has been reported. if (isDeploymentStatusNotYetReported(appVersion)) { recordDeploymentStatusReported(appVersion, DEPLOYMENT_SUCCEEDED_STATUS); @@ -493,45 +542,6 @@ public class CodePush { return; } - // Check if there was a rollback that was not yet reported - JSONArray failedUpdates = getFailedUpdates(); - if (failedUpdates != null && failedUpdates.length() > 0) { - try { - JSONObject lastFailedPackageJSON = failedUpdates.getJSONObject(failedUpdates.length() - 1); - WritableMap lastFailedPackage = CodePushUtils.convertJsonObjectToWriteable(lastFailedPackageJSON); - String lastFailedPackageIdentifier = getPackageStatusReportIdentifier(lastFailedPackage); - if (lastFailedPackage != null && isDeploymentStatusNotYetReported(lastFailedPackageIdentifier)) { - recordDeploymentStatusReported(lastFailedPackageIdentifier, DEPLOYMENT_FAILED_STATUS); - WritableNativeMap reportMap = new WritableNativeMap(); - reportMap.putMap("package", lastFailedPackage); - reportMap.putString("status", DEPLOYMENT_FAILED_STATUS); - promise.resolve(reportMap); - return; - } - } catch (JSONException e) { - throw new CodePushUnknownException("Unable to read failed updates information stored in SharedPreferences.", e); - } - } - - // Check if the current CodePush package has been reported - try { - WritableMap currentPackage = codePushPackage.getCurrentPackage(); - if (currentPackage != null) { - String currentPackageIdentifier = getPackageStatusReportIdentifier(currentPackage); - if (currentPackageIdentifier != null && isDeploymentStatusNotYetReported(currentPackageIdentifier)) { - recordDeploymentStatusReported(currentPackageIdentifier, DEPLOYMENT_SUCCEEDED_STATUS); - WritableNativeMap reportMap = new WritableNativeMap(); - reportMap.putMap("package", currentPackage); - reportMap.putString("status", DEPLOYMENT_SUCCEEDED_STATUS); - promise.resolve(reportMap); - return; - } - } - } catch (IOException e) { - // No current package, resolve no report. - promise.resolve(""); - } - promise.resolve(""); } diff --git a/package-mixins.js b/package-mixins.js index 6b2f75e..6078bee 100644 --- a/package-mixins.js +++ b/package-mixins.js @@ -1,35 +1,39 @@ +import { AcquisitionManager as Sdk } from "code-push/script/acquisition-sdk"; import { DeviceEventEmitter } from "react-native"; // This function is used to augment remote and local // package objects with additional functionality/properties // beyond what is included in the metadata sent by the server. module.exports = (NativeCodePush) => { - const remote = { - async download(downloadProgressCallback) { - if (!this.downloadUrl) { - throw new Error("Cannot download an update without a download url"); - } + const remote = (reportStatusDownload) => { + return { + async download(downloadProgressCallback) { + if (!this.downloadUrl) { + throw new Error("Cannot download an update without a download url"); + } - let downloadProgressSubscription; - if (downloadProgressCallback) { - // Use event subscription to obtain download progress. - downloadProgressSubscription = DeviceEventEmitter.addListener( - "CodePushDownloadProgress", - downloadProgressCallback - ); - } + let downloadProgressSubscription; + if (downloadProgressCallback) { + // Use event subscription to obtain download progress. + downloadProgressSubscription = DeviceEventEmitter.addListener( + "CodePushDownloadProgress", + downloadProgressCallback + ); + } - // Use the downloaded package info. Native code will save the package info - // so that the client knows what the current package version is. - try { - const downloadedPackage = await NativeCodePush.downloadUpdate(this); - return { ...downloadedPackage, ...local }; - } finally { - downloadProgressSubscription && downloadProgressSubscription.remove(); - } - }, + // Use the downloaded package info. Native code will save the package info + // so that the client knows what the current package version is. + try { + const downloadedPackage = await NativeCodePush.downloadUpdate(this); + reportStatusDownload && reportStatusDownload(this); + return { ...downloadedPackage, ...local }; + } finally { + downloadProgressSubscription && downloadProgressSubscription.remove(); + } + }, - isPending: false // A remote package could never be in a pending state + isPending: false // A remote package could never be in a pending state + }; }; const local = {