@appzung/react-native-code-push 5.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/AlertAdapter.js +24 -0
  2. package/CONTRIBUTING.md +136 -0
  3. package/CodePush.js +666 -0
  4. package/CodePush.podspec +27 -0
  5. package/LICENSE.md +13 -0
  6. package/README.md +386 -0
  7. package/RestartManager.js +59 -0
  8. package/android/app/build.gradle +31 -0
  9. package/android/app/proguard-rules.pro +25 -0
  10. package/android/app/src/main/AndroidManifest.xml +10 -0
  11. package/android/app/src/main/java/com/microsoft/codepush/react/CodePush.java +395 -0
  12. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushBuilder.java +37 -0
  13. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushConstants.java +34 -0
  14. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushDialog.java +102 -0
  15. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushInstallMode.java +16 -0
  16. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidPublicKeyException.java +12 -0
  17. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushInvalidUpdateException.java +7 -0
  18. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushMalformedDataException.java +12 -0
  19. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNativeModule.java +641 -0
  20. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushNotInitializedException.java +12 -0
  21. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushTelemetryManager.java +175 -0
  22. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUnknownException.java +12 -0
  23. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateManager.java +369 -0
  24. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateState.java +15 -0
  25. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUpdateUtils.java +267 -0
  26. package/android/app/src/main/java/com/microsoft/codepush/react/CodePushUtils.java +236 -0
  27. package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgress.java +30 -0
  28. package/android/app/src/main/java/com/microsoft/codepush/react/DownloadProgressCallback.java +5 -0
  29. package/android/app/src/main/java/com/microsoft/codepush/react/FileUtils.java +190 -0
  30. package/android/app/src/main/java/com/microsoft/codepush/react/ReactInstanceHolder.java +17 -0
  31. package/android/app/src/main/java/com/microsoft/codepush/react/SettingsManager.java +173 -0
  32. package/android/build.gradle +20 -0
  33. package/android/codepush.gradle +113 -0
  34. package/android/gradle/wrapper/gradle-wrapper.jar +0 -0
  35. package/android/gradle/wrapper/gradle-wrapper.properties +5 -0
  36. package/android/gradle.properties +20 -0
  37. package/android/gradlew +164 -0
  38. package/android/gradlew.bat +90 -0
  39. package/android/settings.gradle +1 -0
  40. package/docs/api-android.md +65 -0
  41. package/docs/api-ios.md +31 -0
  42. package/docs/api-js.md +592 -0
  43. package/docs/multi-deployment-testing-android.md +107 -0
  44. package/docs/multi-deployment-testing-ios.md +53 -0
  45. package/docs/setup-android.md +406 -0
  46. package/docs/setup-ios.md +229 -0
  47. package/docs/setup-windows.md +65 -0
  48. package/gulpfile.js +302 -0
  49. package/ios/CodePush/Base64/Base64/MF_Base64Additions.h +34 -0
  50. package/ios/CodePush/Base64/Base64/MF_Base64Additions.m +252 -0
  51. package/ios/CodePush/Base64/README.md +47 -0
  52. package/ios/CodePush/CodePush.h +235 -0
  53. package/ios/CodePush/CodePush.m +1041 -0
  54. package/ios/CodePush/CodePushConfig.m +116 -0
  55. package/ios/CodePush/CodePushDownloadHandler.m +130 -0
  56. package/ios/CodePush/CodePushErrorUtils.m +20 -0
  57. package/ios/CodePush/CodePushPackage.m +598 -0
  58. package/ios/CodePush/CodePushTelemetryManager.m +175 -0
  59. package/ios/CodePush/CodePushUpdateUtils.m +376 -0
  60. package/ios/CodePush/CodePushUtils.m +9 -0
  61. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithm.h +69 -0
  62. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmFactory.h +16 -0
  63. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmFactory.m +51 -0
  64. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmNone.h +15 -0
  65. package/ios/CodePush/JWT/Core/Algorithms/Base/JWTAlgorithmNone.m +55 -0
  66. package/ios/CodePush/JWT/Core/Algorithms/ESFamily/JWTAlgorithmESBase.h +24 -0
  67. package/ios/CodePush/JWT/Core/Algorithms/ESFamily/JWTAlgorithmESBase.m +41 -0
  68. package/ios/CodePush/JWT/Core/Algorithms/HSFamily/JWTAlgorithmHSBase.h +28 -0
  69. package/ios/CodePush/JWT/Core/Algorithms/HSFamily/JWTAlgorithmHSBase.m +205 -0
  70. package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolder.h +103 -0
  71. package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolder.m +322 -0
  72. package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolderChain.h +37 -0
  73. package/ios/CodePush/JWT/Core/Algorithms/Holders/JWTAlgorithmDataHolderChain.m +145 -0
  74. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTAlgorithmRSBase.h +35 -0
  75. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTAlgorithmRSBase.m +551 -0
  76. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/JWTRSAlgorithm.h +23 -0
  77. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKey.h +43 -0
  78. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKey.m +230 -0
  79. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKeyExtractor.h +31 -0
  80. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoKeyExtractor.m +113 -0
  81. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoSecurity.h +38 -0
  82. package/ios/CodePush/JWT/Core/Algorithms/RSFamily/RSKeys/JWTCryptoSecurity.m +500 -0
  83. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaim.h +18 -0
  84. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaim.m +214 -0
  85. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSet.h +23 -0
  86. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSet.m +29 -0
  87. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetSerializer.h +19 -0
  88. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetSerializer.m +68 -0
  89. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetVerifier.h +18 -0
  90. package/ios/CodePush/JWT/Core/ClaimSet/JWTClaimsSetVerifier.m +72 -0
  91. package/ios/CodePush/JWT/Core/Coding/JWTCoding+ResultTypes.h +67 -0
  92. package/ios/CodePush/JWT/Core/Coding/JWTCoding+ResultTypes.m +111 -0
  93. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionOne.h +119 -0
  94. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionOne.m +307 -0
  95. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionThree.h +94 -0
  96. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionThree.m +619 -0
  97. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionTwo.h +164 -0
  98. package/ios/CodePush/JWT/Core/Coding/JWTCoding+VersionTwo.m +514 -0
  99. package/ios/CodePush/JWT/Core/Coding/JWTCoding.h +24 -0
  100. package/ios/CodePush/JWT/Core/Coding/JWTCoding.m +11 -0
  101. package/ios/CodePush/JWT/Core/FrameworkSupplement/JWT.h +52 -0
  102. package/ios/CodePush/JWT/Core/FrameworkSupplement/Map.modulemap +5 -0
  103. package/ios/CodePush/JWT/Core/Supplement/JWTBase64Coder.h +28 -0
  104. package/ios/CodePush/JWT/Core/Supplement/JWTBase64Coder.m +70 -0
  105. package/ios/CodePush/JWT/Core/Supplement/JWTDeprecations.h +22 -0
  106. package/ios/CodePush/JWT/Core/Supplement/JWTErrorDescription.h +34 -0
  107. package/ios/CodePush/JWT/Core/Supplement/JWTErrorDescription.m +73 -0
  108. package/ios/CodePush/JWT/LICENSE +19 -0
  109. package/ios/CodePush/JWT/README.md +489 -0
  110. package/ios/CodePush/RCTConvert+CodePushInstallMode.m +20 -0
  111. package/ios/CodePush/RCTConvert+CodePushUpdateState.m +20 -0
  112. package/ios/CodePush/SSZipArchive/Common.h +81 -0
  113. package/ios/CodePush/SSZipArchive/README.md +1 -0
  114. package/ios/CodePush/SSZipArchive/SSZipArchive.h +76 -0
  115. package/ios/CodePush/SSZipArchive/SSZipArchive.m +691 -0
  116. package/ios/CodePush/SSZipArchive/aes/aes.h +198 -0
  117. package/ios/CodePush/SSZipArchive/aes/aes_via_ace.h +541 -0
  118. package/ios/CodePush/SSZipArchive/aes/aescrypt.c +294 -0
  119. package/ios/CodePush/SSZipArchive/aes/aeskey.c +548 -0
  120. package/ios/CodePush/SSZipArchive/aes/aesopt.h +739 -0
  121. package/ios/CodePush/SSZipArchive/aes/aestab.c +391 -0
  122. package/ios/CodePush/SSZipArchive/aes/aestab.h +173 -0
  123. package/ios/CodePush/SSZipArchive/aes/brg_endian.h +126 -0
  124. package/ios/CodePush/SSZipArchive/aes/brg_types.h +219 -0
  125. package/ios/CodePush/SSZipArchive/aes/entropy.c +54 -0
  126. package/ios/CodePush/SSZipArchive/aes/entropy.h +16 -0
  127. package/ios/CodePush/SSZipArchive/aes/fileenc.c +144 -0
  128. package/ios/CodePush/SSZipArchive/aes/fileenc.h +121 -0
  129. package/ios/CodePush/SSZipArchive/aes/hmac.c +145 -0
  130. package/ios/CodePush/SSZipArchive/aes/hmac.h +103 -0
  131. package/ios/CodePush/SSZipArchive/aes/prng.c +155 -0
  132. package/ios/CodePush/SSZipArchive/aes/prng.h +82 -0
  133. package/ios/CodePush/SSZipArchive/aes/pwd2key.c +103 -0
  134. package/ios/CodePush/SSZipArchive/aes/pwd2key.h +57 -0
  135. package/ios/CodePush/SSZipArchive/aes/sha1.c +258 -0
  136. package/ios/CodePush/SSZipArchive/aes/sha1.h +73 -0
  137. package/ios/CodePush/SSZipArchive/minizip/crypt.h +130 -0
  138. package/ios/CodePush/SSZipArchive/minizip/ioapi.c +369 -0
  139. package/ios/CodePush/SSZipArchive/minizip/ioapi.h +175 -0
  140. package/ios/CodePush/SSZipArchive/minizip/mztools.c +284 -0
  141. package/ios/CodePush/SSZipArchive/minizip/mztools.h +31 -0
  142. package/ios/CodePush/SSZipArchive/minizip/unzip.c +1839 -0
  143. package/ios/CodePush/SSZipArchive/minizip/unzip.h +248 -0
  144. package/ios/CodePush/SSZipArchive/minizip/zip.c +1910 -0
  145. package/ios/CodePush/SSZipArchive/minizip/zip.h +202 -0
  146. package/ios/CodePush.xcodeproj/project.pbxproj +937 -0
  147. package/ios/CodePush.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
  148. package/ios/CodePush.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +8 -0
  149. package/logging.js +6 -0
  150. package/package-mixins.js +69 -0
  151. package/package.json +47 -0
  152. package/request-fetch-adapter.js +52 -0
  153. package/scripts/generateBundledResourcesHash.js +125 -0
  154. package/scripts/getFilesInFolder.js +19 -0
  155. package/scripts/postlink/android/postlink.js +87 -0
  156. package/scripts/postlink/ios/postlink.js +116 -0
  157. package/scripts/postlink/run.js +11 -0
  158. package/scripts/postunlink/android/postunlink.js +74 -0
  159. package/scripts/postunlink/ios/postunlink.js +87 -0
  160. package/scripts/postunlink/run.js +11 -0
  161. package/scripts/recordFilesBeforeBundleCommand.js +41 -0
  162. package/scripts/tools/linkToolsAndroid.js +57 -0
  163. package/scripts/tools/linkToolsIos.js +130 -0
  164. package/typings/react-native-code-push.d.ts +446 -0
  165. package/windows/CodePush/CodePush.csproj +128 -0
  166. package/windows/CodePush/CodePushUtils.cs +47 -0
  167. package/windows/CodePush/FileUtils.cs +40 -0
  168. package/windows/CodePush/Properties/AssemblyInfo.cs +29 -0
  169. package/windows/CodePush/Properties/CodePush.rd.xml +33 -0
  170. package/windows/CodePush/UpdateManager.cs +305 -0
  171. package/windows/CodePush/UpdateUtils.cs +46 -0
  172. package/windows/CodePush.Net46/Adapters/Http/HttpProgress.cs +28 -0
  173. package/windows/CodePush.Net46/Adapters/Storage/ApplicationDataContainer.cs +106 -0
  174. package/windows/CodePush.Net46/CodePush.Net46.csproj +103 -0
  175. package/windows/CodePush.Net46/CodePushUtils.cs +158 -0
  176. package/windows/CodePush.Net46/FileUtils.cs +55 -0
  177. package/windows/CodePush.Net46/Properties/AssemblyInfo.cs +36 -0
  178. package/windows/CodePush.Net46/UpdateManager.cs +330 -0
  179. package/windows/CodePush.Net46/UpdateUtils.cs +70 -0
  180. package/windows/CodePush.Net46/packages.config +5 -0
  181. package/windows/CodePush.Shared/CodePush.Shared.projitems +22 -0
  182. package/windows/CodePush.Shared/CodePush.Shared.shproj +13 -0
  183. package/windows/CodePush.Shared/CodePushConstants.cs +35 -0
  184. package/windows/CodePush.Shared/CodePushNativeModule.cs +329 -0
  185. package/windows/CodePush.Shared/CodePushReactPackage.cs +235 -0
  186. package/windows/CodePush.Shared/CodePushUtils.cs +70 -0
  187. package/windows/CodePush.Shared/InstallMode.cs +9 -0
  188. package/windows/CodePush.Shared/MinimumBackgroundListener.cs +44 -0
  189. package/windows/CodePush.Shared/SettingsManager.cs +148 -0
  190. package/windows/CodePush.Shared/TelemetryManager.cs +250 -0
  191. package/windows/CodePush.Shared/UpdateState.cs +9 -0
@@ -0,0 +1,641 @@
1
+ package com.microsoft.codepush.react;
2
+
3
+ import android.app.Activity;
4
+ import android.os.AsyncTask;
5
+ import android.os.Handler;
6
+ import android.os.Looper;
7
+ import android.provider.Settings;
8
+ import android.view.View;
9
+
10
+ import com.facebook.react.ReactApplication;
11
+ import com.facebook.react.ReactInstanceManager;
12
+ import com.facebook.react.ReactRootView;
13
+ import com.facebook.react.bridge.Arguments;
14
+ import com.facebook.react.bridge.JSBundleLoader;
15
+ import com.facebook.react.bridge.LifecycleEventListener;
16
+ import com.facebook.react.bridge.Promise;
17
+ import com.facebook.react.bridge.ReactApplicationContext;
18
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
19
+ import com.facebook.react.bridge.ReactMethod;
20
+ import com.facebook.react.bridge.ReadableMap;
21
+ import com.facebook.react.bridge.WritableMap;
22
+ import com.facebook.react.modules.core.ChoreographerCompat;
23
+ import com.facebook.react.modules.core.DeviceEventManagerModule;
24
+ import com.facebook.react.modules.core.ReactChoreographer;
25
+
26
+ import org.json.JSONArray;
27
+ import org.json.JSONException;
28
+ import org.json.JSONObject;
29
+
30
+ import java.io.IOException;
31
+ import java.lang.reflect.Field;
32
+ import java.util.Date;
33
+ import java.util.HashMap;
34
+ import java.util.List;
35
+ import java.util.Map;
36
+
37
+ public class CodePushNativeModule extends ReactContextBaseJavaModule {
38
+ private String mBinaryContentsHash = null;
39
+ private String mClientUniqueId = null;
40
+ private LifecycleEventListener mLifecycleEventListener = null;
41
+ private int mMinimumBackgroundDuration = 0;
42
+
43
+ private CodePush mCodePush;
44
+ private SettingsManager mSettingsManager;
45
+ private CodePushTelemetryManager mTelemetryManager;
46
+ private CodePushUpdateManager mUpdateManager;
47
+
48
+ public CodePushNativeModule(ReactApplicationContext reactContext, CodePush codePush, CodePushUpdateManager codePushUpdateManager, CodePushTelemetryManager codePushTelemetryManager, SettingsManager settingsManager) {
49
+ super(reactContext);
50
+
51
+ mCodePush = codePush;
52
+ mSettingsManager = settingsManager;
53
+ mTelemetryManager = codePushTelemetryManager;
54
+ mUpdateManager = codePushUpdateManager;
55
+
56
+ // Initialize module state while we have a reference to the current context.
57
+ mBinaryContentsHash = CodePushUpdateUtils.getHashForBinaryContents(reactContext, mCodePush.isDebugMode());
58
+ mClientUniqueId = Settings.Secure.getString(reactContext.getContentResolver(), Settings.Secure.ANDROID_ID);
59
+ }
60
+
61
+ @Override
62
+ public Map<String, Object> getConstants() {
63
+ final Map<String, Object> constants = new HashMap<>();
64
+
65
+ constants.put("codePushInstallModeImmediate", CodePushInstallMode.IMMEDIATE.getValue());
66
+ constants.put("codePushInstallModeOnNextRestart", CodePushInstallMode.ON_NEXT_RESTART.getValue());
67
+ constants.put("codePushInstallModeOnNextResume", CodePushInstallMode.ON_NEXT_RESUME.getValue());
68
+ constants.put("codePushInstallModeOnNextSuspend", CodePushInstallMode.ON_NEXT_SUSPEND.getValue());
69
+
70
+ constants.put("codePushUpdateStateRunning", CodePushUpdateState.RUNNING.getValue());
71
+ constants.put("codePushUpdateStatePending", CodePushUpdateState.PENDING.getValue());
72
+ constants.put("codePushUpdateStateLatest", CodePushUpdateState.LATEST.getValue());
73
+
74
+ return constants;
75
+ }
76
+
77
+ @Override
78
+ public String getName() {
79
+ return "CodePush";
80
+ }
81
+
82
+ private void loadBundleLegacy() {
83
+ final Activity currentActivity = getCurrentActivity();
84
+ if (currentActivity == null) {
85
+ // The currentActivity can be null if it is backgrounded / destroyed, so we simply
86
+ // no-op to prevent any null pointer exceptions.
87
+ return;
88
+ }
89
+ mCodePush.invalidateCurrentInstance();
90
+
91
+ currentActivity.runOnUiThread(new Runnable() {
92
+ @Override
93
+ public void run() {
94
+ currentActivity.recreate();
95
+ }
96
+ });
97
+ }
98
+
99
+ // Use reflection to find and set the appropriate fields on ReactInstanceManager. See #556 for a proposal for a less brittle way
100
+ // to approach this.
101
+ private void setJSBundle(ReactInstanceManager instanceManager, String latestJSBundleFile) throws IllegalAccessException {
102
+ try {
103
+ JSBundleLoader latestJSBundleLoader;
104
+ if (latestJSBundleFile.toLowerCase().startsWith("assets://")) {
105
+ latestJSBundleLoader = JSBundleLoader.createAssetLoader(getReactApplicationContext(), latestJSBundleFile, false);
106
+ } else {
107
+ latestJSBundleLoader = JSBundleLoader.createFileLoader(latestJSBundleFile);
108
+ }
109
+
110
+ Field bundleLoaderField = instanceManager.getClass().getDeclaredField("mBundleLoader");
111
+ bundleLoaderField.setAccessible(true);
112
+ bundleLoaderField.set(instanceManager, latestJSBundleLoader);
113
+ } catch (Exception e) {
114
+ CodePushUtils.log("Unable to set JSBundle - CodePush may not support this version of React Native");
115
+ throw new IllegalAccessException("Could not setJSBundle");
116
+ }
117
+ }
118
+
119
+ private void loadBundle() {
120
+ clearLifecycleEventListener();
121
+ try {
122
+ mCodePush.clearDebugCacheIfNeeded(resolveInstanceManager());
123
+ } catch(Exception e) {
124
+ // If we got error in out reflection we should clear debug cache anyway.
125
+ mCodePush.clearDebugCacheIfNeeded(null);
126
+ }
127
+
128
+ try {
129
+ // #1) Get the ReactInstanceManager instance, which is what includes the
130
+ // logic to reload the current React context.
131
+ final ReactInstanceManager instanceManager = resolveInstanceManager();
132
+ if (instanceManager == null) {
133
+ return;
134
+ }
135
+
136
+ String latestJSBundleFile = mCodePush.getJSBundleFileInternal(mCodePush.getAssetsBundleFileName());
137
+
138
+ // #2) Update the locally stored JS bundle file path
139
+ setJSBundle(instanceManager, latestJSBundleFile);
140
+
141
+ // #3) Get the context creation method and fire it on the UI thread (which RN enforces)
142
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
143
+ @Override
144
+ public void run() {
145
+ try {
146
+ // We don't need to resetReactRootViews anymore
147
+ // due the issue https://github.com/facebook/react-native/issues/14533
148
+ // has been fixed in RN 0.46.0
149
+ //resetReactRootViews(instanceManager);
150
+
151
+ instanceManager.recreateReactContextInBackground();
152
+ mCodePush.initializeUpdateAfterRestart();
153
+ } catch (Exception e) {
154
+ // The recreation method threw an unknown exception
155
+ // so just simply fallback to restarting the Activity (if it exists)
156
+ loadBundleLegacy();
157
+ }
158
+ }
159
+ });
160
+
161
+ } catch (Exception e) {
162
+ // Our reflection logic failed somewhere
163
+ // so fall back to restarting the Activity (if it exists)
164
+ CodePushUtils.log("Failed to load the bundle, falling back to restarting the Activity (if it exists). " + e.getMessage());
165
+ loadBundleLegacy();
166
+ }
167
+ }
168
+
169
+ // This workaround has been implemented in order to fix https://github.com/facebook/react-native/issues/14533
170
+ // resetReactRootViews allows to call recreateReactContextInBackground without any exceptions
171
+ // This fix also relates to https://github.com/Microsoft/react-native-code-push/issues/878
172
+ private void resetReactRootViews(ReactInstanceManager instanceManager) throws NoSuchFieldException, IllegalAccessException {
173
+ Field mAttachedRootViewsField = instanceManager.getClass().getDeclaredField("mAttachedRootViews");
174
+ mAttachedRootViewsField.setAccessible(true);
175
+ List<ReactRootView> mAttachedRootViews = (List<ReactRootView>)mAttachedRootViewsField.get(instanceManager);
176
+ for (ReactRootView reactRootView : mAttachedRootViews) {
177
+ reactRootView.removeAllViews();
178
+ reactRootView.setId(View.NO_ID);
179
+ }
180
+ mAttachedRootViewsField.set(instanceManager, mAttachedRootViews);
181
+ }
182
+
183
+ private void clearLifecycleEventListener() {
184
+ // Remove LifecycleEventListener to prevent infinite restart loop
185
+ if (mLifecycleEventListener != null) {
186
+ getReactApplicationContext().removeLifecycleEventListener(mLifecycleEventListener);
187
+ mLifecycleEventListener = null;
188
+ }
189
+ }
190
+
191
+ // Use reflection to find the ReactInstanceManager. See #556 for a proposal for a less brittle way to approach this.
192
+ private ReactInstanceManager resolveInstanceManager() throws NoSuchFieldException, IllegalAccessException {
193
+ ReactInstanceManager instanceManager = CodePush.getReactInstanceManager();
194
+ if (instanceManager != null) {
195
+ return instanceManager;
196
+ }
197
+
198
+ final Activity currentActivity = getCurrentActivity();
199
+ if (currentActivity == null) {
200
+ return null;
201
+ }
202
+
203
+ ReactApplication reactApplication = (ReactApplication) currentActivity.getApplication();
204
+ instanceManager = reactApplication.getReactNativeHost().getReactInstanceManager();
205
+
206
+ return instanceManager;
207
+ }
208
+
209
+ @ReactMethod
210
+ public void downloadUpdate(final ReadableMap updatePackage, final boolean notifyProgress, final Promise promise) {
211
+ AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
212
+ @Override
213
+ protected Void doInBackground(Void... params) {
214
+ try {
215
+ JSONObject mutableUpdatePackage = CodePushUtils.convertReadableToJsonObject(updatePackage);
216
+ CodePushUtils.setJSONValueForKey(mutableUpdatePackage, CodePushConstants.BINARY_MODIFIED_TIME_KEY, "" + mCodePush.getBinaryResourcesModifiedTime());
217
+ mUpdateManager.downloadPackage(mutableUpdatePackage, mCodePush.getAssetsBundleFileName(), new DownloadProgressCallback() {
218
+ private boolean hasScheduledNextFrame = false;
219
+ private DownloadProgress latestDownloadProgress = null;
220
+
221
+ @Override
222
+ public void call(DownloadProgress downloadProgress) {
223
+ if (!notifyProgress) {
224
+ return;
225
+ }
226
+
227
+ latestDownloadProgress = downloadProgress;
228
+ // If the download is completed, synchronously send the last event.
229
+ if (latestDownloadProgress.isCompleted()) {
230
+ dispatchDownloadProgressEvent();
231
+ return;
232
+ }
233
+
234
+ if (hasScheduledNextFrame) {
235
+ return;
236
+ }
237
+
238
+ hasScheduledNextFrame = true;
239
+ getReactApplicationContext().runOnUiQueueThread(new Runnable() {
240
+ @Override
241
+ public void run() {
242
+ ReactChoreographer.getInstance().postFrameCallback(ReactChoreographer.CallbackType.TIMERS_EVENTS, new ChoreographerCompat.FrameCallback() {
243
+ @Override
244
+ public void doFrame(long frameTimeNanos) {
245
+ if (!latestDownloadProgress.isCompleted()) {
246
+ dispatchDownloadProgressEvent();
247
+ }
248
+
249
+ hasScheduledNextFrame = false;
250
+ }
251
+ });
252
+ }
253
+ });
254
+ }
255
+
256
+ public void dispatchDownloadProgressEvent() {
257
+ getReactApplicationContext()
258
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
259
+ .emit(CodePushConstants.DOWNLOAD_PROGRESS_EVENT_NAME, latestDownloadProgress.createWritableMap());
260
+ }
261
+ }, mCodePush.getPublicKey());
262
+
263
+ JSONObject newPackage = mUpdateManager.getPackage(CodePushUtils.tryGetString(updatePackage, CodePushConstants.PACKAGE_HASH_KEY));
264
+ promise.resolve(CodePushUtils.convertJsonObjectToWritable(newPackage));
265
+ } catch (CodePushInvalidUpdateException e) {
266
+ CodePushUtils.log(e);
267
+ mSettingsManager.saveFailedUpdate(CodePushUtils.convertReadableToJsonObject(updatePackage));
268
+ promise.reject(e);
269
+ } catch (IOException | CodePushUnknownException e) {
270
+ CodePushUtils.log(e);
271
+ promise.reject(e);
272
+ }
273
+
274
+ return null;
275
+ }
276
+ };
277
+
278
+ asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
279
+ }
280
+
281
+ @ReactMethod
282
+ public void getConfiguration(Promise promise) {
283
+ try {
284
+ WritableMap configMap = Arguments.createMap();
285
+ configMap.putString("appVersion", mCodePush.getAppVersion());
286
+ configMap.putString("clientUniqueId", mClientUniqueId);
287
+ configMap.putString("deploymentKey", mCodePush.getDeploymentKey());
288
+ configMap.putString("serverUrl", mCodePush.getServerUrl());
289
+
290
+ // The binary hash may be null in debug builds
291
+ if (mBinaryContentsHash != null) {
292
+ configMap.putString(CodePushConstants.PACKAGE_HASH_KEY, mBinaryContentsHash);
293
+ }
294
+
295
+ promise.resolve(configMap);
296
+ } catch(CodePushUnknownException e) {
297
+ CodePushUtils.log(e);
298
+ promise.reject(e);
299
+ }
300
+ }
301
+
302
+ @ReactMethod
303
+ public void getUpdateMetadata(final int updateState, final Promise promise) {
304
+ AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
305
+ @Override
306
+ protected Void doInBackground(Void... params) {
307
+ try {
308
+ JSONObject currentPackage = mUpdateManager.getCurrentPackage();
309
+
310
+ if (currentPackage == null) {
311
+ promise.resolve(null);
312
+ return null;
313
+ }
314
+
315
+ Boolean currentUpdateIsPending = false;
316
+
317
+ if (currentPackage.has(CodePushConstants.PACKAGE_HASH_KEY)) {
318
+ String currentHash = currentPackage.optString(CodePushConstants.PACKAGE_HASH_KEY, null);
319
+ currentUpdateIsPending = mSettingsManager.isPendingUpdate(currentHash);
320
+ }
321
+
322
+ if (updateState == CodePushUpdateState.PENDING.getValue() && !currentUpdateIsPending) {
323
+ // The caller wanted a pending update
324
+ // but there isn't currently one.
325
+ promise.resolve(null);
326
+ } else if (updateState == CodePushUpdateState.RUNNING.getValue() && currentUpdateIsPending) {
327
+ // The caller wants the running update, but the current
328
+ // one is pending, so we need to grab the previous.
329
+ JSONObject previousPackage = mUpdateManager.getPreviousPackage();
330
+
331
+ if (previousPackage == null) {
332
+ promise.resolve(null);
333
+ return null;
334
+ }
335
+
336
+ promise.resolve(CodePushUtils.convertJsonObjectToWritable(previousPackage));
337
+ } else {
338
+ // The current package satisfies the request:
339
+ // 1) Caller wanted a pending, and there is a pending update
340
+ // 2) Caller wanted the running update, and there isn't a pending
341
+ // 3) Caller wants the latest update, regardless if it's pending or not
342
+ if (mCodePush.isRunningBinaryVersion()) {
343
+ // This only matters in Debug builds. Since we do not clear "outdated" updates,
344
+ // we need to indicate to the JS side that somehow we have a current update on
345
+ // disk that is not actually running.
346
+ CodePushUtils.setJSONValueForKey(currentPackage, "_isDebugOnly", true);
347
+ }
348
+
349
+ // Enable differentiating pending vs. non-pending updates
350
+ CodePushUtils.setJSONValueForKey(currentPackage, "isPending", currentUpdateIsPending);
351
+ promise.resolve(CodePushUtils.convertJsonObjectToWritable(currentPackage));
352
+ }
353
+ } catch (CodePushMalformedDataException e) {
354
+ // We need to recover the app in case 'codepush.json' is corrupted
355
+ CodePushUtils.log(e.getMessage());
356
+ clearUpdates();
357
+ promise.resolve(null);
358
+ } catch(CodePushUnknownException e) {
359
+ CodePushUtils.log(e);
360
+ promise.reject(e);
361
+ }
362
+
363
+ return null;
364
+ }
365
+ };
366
+
367
+ asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
368
+ }
369
+
370
+ @ReactMethod
371
+ public void getNewStatusReport(final Promise promise) {
372
+ AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
373
+ @Override
374
+ protected Void doInBackground(Void... params) {
375
+ try {
376
+ if (mCodePush.needToReportRollback()) {
377
+ mCodePush.setNeedToReportRollback(false);
378
+ JSONArray failedUpdates = mSettingsManager.getFailedUpdates();
379
+ if (failedUpdates != null && failedUpdates.length() > 0) {
380
+ try {
381
+ JSONObject lastFailedPackageJSON = failedUpdates.getJSONObject(failedUpdates.length() - 1);
382
+ WritableMap lastFailedPackage = CodePushUtils.convertJsonObjectToWritable(lastFailedPackageJSON);
383
+ WritableMap failedStatusReport = mTelemetryManager.getRollbackReport(lastFailedPackage);
384
+ if (failedStatusReport != null) {
385
+ promise.resolve(failedStatusReport);
386
+ return null;
387
+ }
388
+ } catch (JSONException e) {
389
+ throw new CodePushUnknownException("Unable to read failed updates information stored in SharedPreferences.", e);
390
+ }
391
+ }
392
+ } else if (mCodePush.didUpdate()) {
393
+ JSONObject currentPackage = mUpdateManager.getCurrentPackage();
394
+ if (currentPackage != null) {
395
+ WritableMap newPackageStatusReport = mTelemetryManager.getUpdateReport(CodePushUtils.convertJsonObjectToWritable(currentPackage));
396
+ if (newPackageStatusReport != null) {
397
+ promise.resolve(newPackageStatusReport);
398
+ return null;
399
+ }
400
+ }
401
+ } else if (mCodePush.isRunningBinaryVersion()) {
402
+ WritableMap newAppVersionStatusReport = mTelemetryManager.getBinaryUpdateReport(mCodePush.getAppVersion());
403
+ if (newAppVersionStatusReport != null) {
404
+ promise.resolve(newAppVersionStatusReport);
405
+ return null;
406
+ }
407
+ } else {
408
+ WritableMap retryStatusReport = mTelemetryManager.getRetryStatusReport();
409
+ if (retryStatusReport != null) {
410
+ promise.resolve(retryStatusReport);
411
+ return null;
412
+ }
413
+ }
414
+
415
+ promise.resolve("");
416
+ } catch(CodePushUnknownException e) {
417
+ CodePushUtils.log(e);
418
+ promise.reject(e);
419
+ }
420
+ return null;
421
+ }
422
+ };
423
+
424
+ asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
425
+ }
426
+
427
+ @ReactMethod
428
+ public void installUpdate(final ReadableMap updatePackage, final int installMode, final int minimumBackgroundDuration, final Promise promise) {
429
+ AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
430
+ @Override
431
+ protected Void doInBackground(Void... params) {
432
+ try {
433
+ mUpdateManager.installPackage(CodePushUtils.convertReadableToJsonObject(updatePackage), mSettingsManager.isPendingUpdate(null));
434
+
435
+ String pendingHash = CodePushUtils.tryGetString(updatePackage, CodePushConstants.PACKAGE_HASH_KEY);
436
+ if (pendingHash == null) {
437
+ throw new CodePushUnknownException("Update package to be installed has no hash.");
438
+ } else {
439
+ mSettingsManager.savePendingUpdate(pendingHash, /* isLoading */false);
440
+ }
441
+
442
+ if (installMode == CodePushInstallMode.ON_NEXT_RESUME.getValue() ||
443
+ // We also add the resume listener if the installMode is IMMEDIATE, because
444
+ // if the current activity is backgrounded, we want to reload the bundle when
445
+ // it comes back into the foreground.
446
+ installMode == CodePushInstallMode.IMMEDIATE.getValue() ||
447
+ installMode == CodePushInstallMode.ON_NEXT_SUSPEND.getValue()) {
448
+
449
+ // Store the minimum duration on the native module as an instance
450
+ // variable instead of relying on a closure below, so that any
451
+ // subsequent resume-based installs could override it.
452
+ CodePushNativeModule.this.mMinimumBackgroundDuration = minimumBackgroundDuration;
453
+
454
+ if (mLifecycleEventListener == null) {
455
+ // Ensure we do not add the listener twice.
456
+ mLifecycleEventListener = new LifecycleEventListener() {
457
+ private Date lastPausedDate = null;
458
+ private Handler appSuspendHandler = new Handler(Looper.getMainLooper());
459
+ private Runnable loadBundleRunnable = new Runnable() {
460
+ @Override
461
+ public void run() {
462
+ CodePushUtils.log("Loading bundle on suspend");
463
+ loadBundle();
464
+ }
465
+ };
466
+
467
+ @Override
468
+ public void onHostResume() {
469
+ appSuspendHandler.removeCallbacks(loadBundleRunnable);
470
+ // As of RN 36, the resume handler fires immediately if the app is in
471
+ // the foreground, so explicitly wait for it to be backgrounded first
472
+ if (lastPausedDate != null) {
473
+ long durationInBackground = (new Date().getTime() - lastPausedDate.getTime()) / 1000;
474
+ if (installMode == CodePushInstallMode.IMMEDIATE.getValue()
475
+ || durationInBackground >= CodePushNativeModule.this.mMinimumBackgroundDuration) {
476
+ CodePushUtils.log("Loading bundle on resume");
477
+ loadBundle();
478
+ }
479
+ }
480
+ }
481
+
482
+ @Override
483
+ public void onHostPause() {
484
+ // Save the current time so that when the app is later
485
+ // resumed, we can detect how long it was in the background.
486
+ lastPausedDate = new Date();
487
+
488
+ if (installMode == CodePushInstallMode.ON_NEXT_SUSPEND.getValue() && mSettingsManager.isPendingUpdate(null)) {
489
+ appSuspendHandler.postDelayed(loadBundleRunnable, minimumBackgroundDuration * 1000);
490
+ }
491
+ }
492
+
493
+ @Override
494
+ public void onHostDestroy() {
495
+ }
496
+ };
497
+
498
+ getReactApplicationContext().addLifecycleEventListener(mLifecycleEventListener);
499
+ }
500
+ }
501
+
502
+ promise.resolve("");
503
+ } catch(CodePushUnknownException e) {
504
+ CodePushUtils.log(e);
505
+ promise.reject(e);
506
+ }
507
+
508
+ return null;
509
+ }
510
+ };
511
+
512
+ asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
513
+ }
514
+
515
+ @ReactMethod
516
+ public void isFailedUpdate(String packageHash, Promise promise) {
517
+ try {
518
+ promise.resolve(mSettingsManager.isFailedHash(packageHash));
519
+ } catch (CodePushUnknownException e) {
520
+ CodePushUtils.log(e);
521
+ promise.reject(e);
522
+ }
523
+ }
524
+
525
+ @ReactMethod
526
+ public void getLatestRollbackInfo(Promise promise) {
527
+ try {
528
+ JSONObject latestRollbackInfo = mSettingsManager.getLatestRollbackInfo();
529
+ if (latestRollbackInfo != null) {
530
+ promise.resolve(CodePushUtils.convertJsonObjectToWritable(latestRollbackInfo));
531
+ } else {
532
+ promise.resolve(null);
533
+ }
534
+ } catch (CodePushUnknownException e) {
535
+ CodePushUtils.log(e);
536
+ promise.reject(e);
537
+ }
538
+ }
539
+
540
+ @ReactMethod
541
+ public void setLatestRollbackInfo(String packageHash, Promise promise) {
542
+ try {
543
+ mSettingsManager.setLatestRollbackInfo(packageHash);
544
+ promise.resolve(null);
545
+ } catch (CodePushUnknownException e) {
546
+ CodePushUtils.log(e);
547
+ promise.reject(e);
548
+ }
549
+ }
550
+
551
+ @ReactMethod
552
+ public void isFirstRun(String packageHash, Promise promise) {
553
+ try {
554
+ boolean isFirstRun = mCodePush.didUpdate()
555
+ && packageHash != null
556
+ && packageHash.length() > 0
557
+ && packageHash.equals(mUpdateManager.getCurrentPackageHash());
558
+ promise.resolve(isFirstRun);
559
+ } catch(CodePushUnknownException e) {
560
+ CodePushUtils.log(e);
561
+ promise.reject(e);
562
+ }
563
+ }
564
+
565
+ @ReactMethod
566
+ public void notifyApplicationReady(Promise promise) {
567
+ try {
568
+ mSettingsManager.removePendingUpdate();
569
+ promise.resolve("");
570
+ } catch(CodePushUnknownException e) {
571
+ CodePushUtils.log(e);
572
+ promise.reject(e);
573
+ }
574
+ }
575
+
576
+ @ReactMethod
577
+ public void recordStatusReported(ReadableMap statusReport) {
578
+ try {
579
+ mTelemetryManager.recordStatusReported(statusReport);
580
+ } catch(CodePushUnknownException e) {
581
+ CodePushUtils.log(e);
582
+ }
583
+ }
584
+
585
+ @ReactMethod
586
+ public void restartApp(boolean onlyIfUpdateIsPending, Promise promise) {
587
+ try {
588
+ // If this is an unconditional restart request, or there
589
+ // is current pending update, then reload the app.
590
+ if (!onlyIfUpdateIsPending || mSettingsManager.isPendingUpdate(null)) {
591
+ loadBundle();
592
+ promise.resolve(true);
593
+ return;
594
+ }
595
+
596
+ promise.resolve(false);
597
+ } catch(CodePushUnknownException e) {
598
+ CodePushUtils.log(e);
599
+ promise.reject(e);
600
+ }
601
+ }
602
+
603
+ @ReactMethod
604
+ public void saveStatusReportForRetry(ReadableMap statusReport) {
605
+ try {
606
+ mTelemetryManager.saveStatusReportForRetry(statusReport);
607
+ } catch(CodePushUnknownException e) {
608
+ CodePushUtils.log(e);
609
+ }
610
+ }
611
+
612
+ @ReactMethod
613
+ // Replaces the current bundle with the one downloaded from removeBundleUrl.
614
+ // It is only to be used during tests. No-ops if the test configuration flag is not set.
615
+ public void downloadAndReplaceCurrentBundle(String remoteBundleUrl) {
616
+ try {
617
+ if (mCodePush.isUsingTestConfiguration()) {
618
+ try {
619
+ mUpdateManager.downloadAndReplaceCurrentBundle(remoteBundleUrl, mCodePush.getAssetsBundleFileName());
620
+ } catch (IOException e) {
621
+ throw new CodePushUnknownException("Unable to replace current bundle", e);
622
+ }
623
+ }
624
+ } catch(CodePushUnknownException | CodePushMalformedDataException e) {
625
+ CodePushUtils.log(e);
626
+ }
627
+ }
628
+
629
+ /**
630
+ * This method clears CodePush's downloaded updates.
631
+ * It is needed to switch to a different deployment if the current deployment is more recent.
632
+ * Note: we don’t recommend to use this method in scenarios other than that (CodePush will call
633
+ * this method automatically when needed in other cases) as it could lead to unpredictable
634
+ * behavior.
635
+ */
636
+ @ReactMethod
637
+ public void clearUpdates() {
638
+ CodePushUtils.log("Clearing updates.");
639
+ mCodePush.clearUpdates();
640
+ }
641
+ }
@@ -0,0 +1,12 @@
1
+ package com.microsoft.codepush.react;
2
+
3
+ public final class CodePushNotInitializedException extends RuntimeException {
4
+
5
+ public CodePushNotInitializedException(String message, Throwable cause) {
6
+ super(message, cause);
7
+ }
8
+
9
+ public CodePushNotInitializedException(String message) {
10
+ super(message);
11
+ }
12
+ }