@hot-updater/react-native 1.0.0-rc.4 → 1.0.0-rc.40

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 (306) hide show
  1. package/android/build.gradle +13 -2
  2. package/android/proguard-rules.pro +8 -2
  3. package/android/src/bridge/BridgeReactInstance.kt +38 -0
  4. package/android/src/bridgeless/BridgeReactInstance.kt +24 -0
  5. package/android/src/main/java/com/hotupdater/ActivityStartGate.kt +94 -0
  6. package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
  7. package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +1064 -844
  8. package/android/src/main/java/com/hotupdater/BundleMetadata.kt +50 -11
  9. package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
  10. package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +287 -22
  11. package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +46 -30
  12. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +75 -192
  13. package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
  14. package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
  15. package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
  16. package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
  17. package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
  18. package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +127 -50
  19. package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
  20. package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
  21. package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
  22. package/android/src/newarch/HotUpdater.kt +7 -9
  23. package/android/src/newarch/HotUpdaterModule.kt +109 -84
  24. package/android/src/newarch/JavaScriptExceptionHooks.kt +48 -0
  25. package/android/src/newarch/ReactIntegrationManager.kt +6 -32
  26. package/android/src/oldarch/HotUpdater.kt +7 -9
  27. package/android/src/oldarch/HotUpdaterModule.kt +98 -74
  28. package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
  29. package/android/src/oldarch/JavaScriptExceptionHooks.kt +71 -0
  30. package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
  31. package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
  32. package/ios/HotUpdater/Internal/BundleFileStorageService.swift +1032 -1116
  33. package/ios/HotUpdater/Internal/BundleMetadata.swift +61 -54
  34. package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
  35. package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
  36. package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
  37. package/ios/HotUpdater/Internal/HotUpdater.mm +32 -67
  38. package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +229 -73
  39. package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
  40. package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
  41. package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
  42. package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +119 -71
  43. package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
  44. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
  45. package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
  46. package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
  47. package/lib/commonjs/appReady.js +102 -0
  48. package/lib/commonjs/appReady.js.map +1 -0
  49. package/lib/commonjs/checkForUpdate.js +120 -68
  50. package/lib/commonjs/checkForUpdate.js.map +1 -1
  51. package/lib/commonjs/clientPlugin.js +13 -0
  52. package/lib/commonjs/clientPlugin.js.map +1 -0
  53. package/lib/commonjs/error.js +4 -13
  54. package/lib/commonjs/error.js.map +1 -1
  55. package/lib/commonjs/fetchJSON.js +16 -27
  56. package/lib/commonjs/fetchJSON.js.map +1 -1
  57. package/lib/commonjs/fetchUpdateResponse.js +60 -0
  58. package/lib/commonjs/fetchUpdateResponse.js.map +1 -0
  59. package/lib/commonjs/httpClient.js +63 -94
  60. package/lib/commonjs/httpClient.js.map +1 -1
  61. package/lib/commonjs/index.js +71 -18
  62. package/lib/commonjs/index.js.map +1 -1
  63. package/lib/commonjs/native.js +74 -60
  64. package/lib/commonjs/native.js.map +1 -1
  65. package/lib/commonjs/pluginHost.js +45 -0
  66. package/lib/commonjs/pluginHost.js.map +1 -0
  67. package/lib/commonjs/releaseCatalogCache.js +36 -87
  68. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  69. package/lib/commonjs/sdkVersion.js +1 -1
  70. package/lib/commonjs/sdkVersion.js.map +1 -1
  71. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  72. package/lib/commonjs/store.js +3 -10
  73. package/lib/commonjs/store.js.map +1 -1
  74. package/lib/commonjs/updateError.js +145 -0
  75. package/lib/commonjs/updateError.js.map +1 -0
  76. package/lib/commonjs/wrap.js +43 -26
  77. package/lib/commonjs/wrap.js.map +1 -1
  78. package/lib/module/appReady.js +95 -0
  79. package/lib/module/appReady.js.map +1 -0
  80. package/lib/module/checkForUpdate.js +113 -63
  81. package/lib/module/checkForUpdate.js.map +1 -1
  82. package/lib/module/clientPlugin.js +4 -0
  83. package/lib/module/clientPlugin.js.map +1 -0
  84. package/lib/module/error.js +4 -13
  85. package/lib/module/error.js.map +1 -1
  86. package/lib/module/fetchJSON.js +14 -26
  87. package/lib/module/fetchJSON.js.map +1 -1
  88. package/lib/module/fetchUpdateResponse.js +55 -0
  89. package/lib/module/fetchUpdateResponse.js.map +1 -0
  90. package/lib/module/httpClient.js +62 -93
  91. package/lib/module/httpClient.js.map +1 -1
  92. package/lib/module/index.js +58 -20
  93. package/lib/module/index.js.map +1 -1
  94. package/lib/module/native.js +67 -53
  95. package/lib/module/native.js.map +1 -1
  96. package/lib/module/pluginHost.js +36 -0
  97. package/lib/module/pluginHost.js.map +1 -0
  98. package/lib/module/releaseCatalogCache.js +32 -82
  99. package/lib/module/releaseCatalogCache.js.map +1 -1
  100. package/lib/module/sdkVersion.js +1 -1
  101. package/lib/module/sdkVersion.js.map +1 -1
  102. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  103. package/lib/module/store.js +3 -10
  104. package/lib/module/store.js.map +1 -1
  105. package/lib/module/updateError.js +139 -0
  106. package/lib/module/updateError.js.map +1 -0
  107. package/lib/module/wrap.js +43 -26
  108. package/lib/module/wrap.js.map +1 -1
  109. package/lib/typescript/commonjs/appReady.d.ts +21 -0
  110. package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
  111. package/lib/typescript/commonjs/checkForUpdate.d.ts +16 -4
  112. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  113. package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
  114. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/error.d.ts +4 -13
  116. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/fetchJSON.d.ts +5 -5
  118. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  119. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +14 -0
  120. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -0
  121. package/lib/typescript/commonjs/httpClient.d.ts +4 -33
  122. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/index.d.ts +4 -8
  124. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/native.d.ts +29 -22
  126. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/pluginHost.d.ts +4 -0
  128. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
  129. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +8 -14
  130. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  132. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +35 -47
  134. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  135. package/lib/typescript/commonjs/store.d.ts +1 -3
  136. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/types.d.ts +1 -1
  138. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/updateError.d.ts +26 -0
  140. package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
  141. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  142. package/lib/typescript/commonjs/wrap.types.d.ts +6 -7
  143. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  144. package/lib/typescript/module/appReady.d.ts +21 -0
  145. package/lib/typescript/module/appReady.d.ts.map +1 -0
  146. package/lib/typescript/module/checkForUpdate.d.ts +16 -4
  147. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  148. package/lib/typescript/module/clientPlugin.d.ts +2 -0
  149. package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
  150. package/lib/typescript/module/error.d.ts +4 -13
  151. package/lib/typescript/module/error.d.ts.map +1 -1
  152. package/lib/typescript/module/fetchJSON.d.ts +5 -5
  153. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  154. package/lib/typescript/module/fetchUpdateResponse.d.ts +14 -0
  155. package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -0
  156. package/lib/typescript/module/httpClient.d.ts +4 -33
  157. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  158. package/lib/typescript/module/index.d.ts +4 -8
  159. package/lib/typescript/module/index.d.ts.map +1 -1
  160. package/lib/typescript/module/native.d.ts +29 -22
  161. package/lib/typescript/module/native.d.ts.map +1 -1
  162. package/lib/typescript/module/pluginHost.d.ts +4 -0
  163. package/lib/typescript/module/pluginHost.d.ts.map +1 -0
  164. package/lib/typescript/module/releaseCatalogCache.d.ts +8 -14
  165. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  166. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  167. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  168. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +35 -47
  169. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  170. package/lib/typescript/module/store.d.ts +1 -3
  171. package/lib/typescript/module/store.d.ts.map +1 -1
  172. package/lib/typescript/module/types.d.ts +1 -1
  173. package/lib/typescript/module/types.d.ts.map +1 -1
  174. package/lib/typescript/module/updateError.d.ts +26 -0
  175. package/lib/typescript/module/updateError.d.ts.map +1 -0
  176. package/lib/typescript/module/wrap.d.ts.map +1 -1
  177. package/lib/typescript/module/wrap.types.d.ts +6 -7
  178. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  179. package/package.json +7 -4
  180. package/src/appReady.ts +141 -0
  181. package/src/checkForUpdate.ts +145 -86
  182. package/src/clientPlugin.ts +18 -0
  183. package/src/error.ts +4 -14
  184. package/src/fetchJSON.ts +14 -30
  185. package/src/fetchUpdateResponse.ts +60 -0
  186. package/src/httpClient.ts +88 -159
  187. package/src/index.ts +90 -24
  188. package/src/native.ts +98 -102
  189. package/src/pluginHost.ts +47 -0
  190. package/src/releaseCatalogCache.ts +43 -172
  191. package/src/sdkVersion.ts +1 -1
  192. package/src/specs/NativeHotUpdater.ts +34 -48
  193. package/src/store.ts +2 -23
  194. package/src/types.ts +1 -1
  195. package/src/updateError.ts +167 -0
  196. package/src/wrap.tsx +57 -26
  197. package/src/wrap.types.ts +6 -7
  198. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  199. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  200. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  201. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  202. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  203. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
  204. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
  205. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
  206. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
  207. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
  208. package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
  209. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
  210. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  211. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  212. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  213. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  214. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  215. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  216. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  217. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  218. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
  219. package/lib/commonjs/catalogCacheNative.spec.js +0 -31
  220. package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
  221. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -616
  222. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  223. package/lib/commonjs/httpClient.spec.js +0 -102
  224. package/lib/commonjs/httpClient.spec.js.map +0 -1
  225. package/lib/commonjs/index.spec.js +0 -302
  226. package/lib/commonjs/index.spec.js.map +0 -1
  227. package/lib/commonjs/iosInterop.spec.js +0 -18
  228. package/lib/commonjs/iosInterop.spec.js.map +0 -1
  229. package/lib/commonjs/native.spec.js +0 -883
  230. package/lib/commonjs/native.spec.js.map +0 -1
  231. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
  232. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  233. package/lib/commonjs/notifyAppReadyInsights.js +0 -133
  234. package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
  235. package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
  236. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
  237. package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
  238. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
  239. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
  240. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
  241. package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
  242. package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
  243. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
  244. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  245. package/lib/commonjs/store.notification.spec.js +0 -76
  246. package/lib/commonjs/store.notification.spec.js.map +0 -1
  247. package/lib/commonjs/store.spec.js +0 -316
  248. package/lib/commonjs/store.spec.js.map +0 -1
  249. package/lib/commonjs/wrap.spec.js +0 -329
  250. package/lib/commonjs/wrap.spec.js.map +0 -1
  251. package/lib/module/catalogCacheNative.spec.js +0 -31
  252. package/lib/module/catalogCacheNative.spec.js.map +0 -1
  253. package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -615
  254. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  255. package/lib/module/httpClient.spec.js +0 -102
  256. package/lib/module/httpClient.spec.js.map +0 -1
  257. package/lib/module/index.spec.js +0 -301
  258. package/lib/module/index.spec.js.map +0 -1
  259. package/lib/module/iosInterop.spec.js +0 -18
  260. package/lib/module/iosInterop.spec.js.map +0 -1
  261. package/lib/module/native.spec.js +0 -882
  262. package/lib/module/native.spec.js.map +0 -1
  263. package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
  264. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  265. package/lib/module/notifyAppReadyInsights.js +0 -128
  266. package/lib/module/notifyAppReadyInsights.js.map +0 -1
  267. package/lib/module/notifyAppReadyInsights.spec.js +0 -245
  268. package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
  269. package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
  270. package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
  271. package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
  272. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
  273. package/lib/module/releaseCatalogCache.spec.js +0 -195
  274. package/lib/module/releaseCatalogCache.spec.js.map +0 -1
  275. package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
  276. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  277. package/lib/module/store.notification.spec.js +0 -77
  278. package/lib/module/store.notification.spec.js.map +0 -1
  279. package/lib/module/store.spec.js +0 -315
  280. package/lib/module/store.spec.js.map +0 -1
  281. package/lib/module/wrap.spec.js +0 -330
  282. package/lib/module/wrap.spec.js.map +0 -1
  283. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
  284. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
  285. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
  286. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  287. package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
  288. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
  289. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
  290. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  291. package/src/catalogCacheNative.spec.ts +0 -63
  292. package/src/checkForUpdate.releaseCatalog.spec.ts +0 -612
  293. package/src/httpClient.spec.ts +0 -132
  294. package/src/index.spec.ts +0 -337
  295. package/src/iosInterop.spec.ts +0 -30
  296. package/src/native.spec.ts +0 -945
  297. package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
  298. package/src/notifyAppReadyInsights.spec.ts +0 -243
  299. package/src/notifyAppReadyInsights.test-utils.ts +0 -31
  300. package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
  301. package/src/notifyAppReadyInsights.ts +0 -194
  302. package/src/releaseCatalogCache.spec.ts +0 -282
  303. package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
  304. package/src/store.notification.spec.ts +0 -92
  305. package/src/store.spec.ts +0 -372
  306. package/src/wrap.spec.ts +0 -406
@@ -20,8 +20,12 @@ def reactNativeArchitectures() {
20
20
  return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
21
21
  }
22
22
 
23
+ // The New Architecture is the default. React Native 0.82 and later run nothing
24
+ // else and set newArchEnabled to true on every library project, whatever the
25
+ // app's gradle.properties says, so only an older React Native app that sets
26
+ // newArchEnabled=false builds the old architecture.
23
27
  def isNewArchitectureEnabled() {
24
- return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
28
+ return !project.hasProperty("newArchEnabled") || project.property("newArchEnabled").toString().toBoolean()
25
29
  }
26
30
 
27
31
  apply plugin: "com.android.library"
@@ -115,7 +119,11 @@ android {
115
119
  "src/newarch",
116
120
  // Codegen specs
117
121
  "generated/java",
118
- "generated/jni"
122
+ "generated/jni",
123
+ // React Native 0.81 and older can run the New Architecture on the
124
+ // bridge. Later versions have none, so their builds never compile
125
+ // ReactNativeHost.
126
+ project.ext.reactNativeMinorVersion >= 82 ? "src/bridgeless" : "src/bridge"
119
127
  ]
120
128
  }
121
129
  }
@@ -146,6 +154,9 @@ dependencies {
146
154
  }
147
155
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
148
156
  implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1"
157
+ // ProcessLifecycleOwner, for an activity that started before Hot Updater was created.
158
+ // React Native's AppCompat already brings it at runtime.
159
+ implementation "androidx.lifecycle:lifecycle-process:2.6.1"
149
160
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"
150
161
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"
151
162
  implementation "org.apache.commons:commons-compress:1.27.1"
@@ -2,6 +2,7 @@
2
2
  # Invoked via reflection, when setting js bundle.
3
3
  -keepclassmembers class com.facebook.react.ReactInstanceManager {
4
4
  private final ** mBundleLoader;
5
+ private final ** mJSExceptionHandler;
5
6
  }
6
7
 
7
8
  # New Architecture
@@ -14,11 +15,16 @@
14
15
 
15
16
  -keepclassmembers class * implements com.facebook.react.runtime.ReactHostDelegate {
16
17
  ** jsBundleLoader;
18
+ ** exceptionHandler;
19
+ }
20
+
21
+ -keepclassmembers class com.facebook.react.bridge.CatalystInstanceImpl {
22
+ ** mJSExceptionHandler;
17
23
  }
18
24
 
19
25
  # Preserve the Brotli decoder and its embedded static dictionary
20
- # (DictionaryData). R8 otherwise strips the dictionary's static init, causing OTA
21
- # .tar.br extraction to fail at runtime with:
26
+ # (DictionaryData). R8 otherwise strips the dictionary's static init, causing
27
+ # per-file Brotli decompression to fail at runtime with:
22
28
  # java.io.IOException: Brotli stream decoding failed
23
29
  # Caused by: brotli dictionary is not set
24
30
  -keep class com.hotupdater.vendor.brotli.** { *; }
@@ -0,0 +1,38 @@
1
+ package com.hotupdater
2
+
3
+ import com.facebook.react.ReactApplication
4
+ import com.facebook.react.bridge.JSBundleLoader
5
+
6
+ /**
7
+ * React Native 0.81 and older can run the New Architecture on the bridge, which
8
+ * only ReactNativeHost reaches. Only builds for those versions compile this file.
9
+ */
10
+ internal object BridgeReactInstance {
11
+ fun setJSBundle(
12
+ application: ReactApplication,
13
+ bundleLoader: JSBundleLoader?,
14
+ ) {
15
+ @Suppress("DEPRECATION")
16
+ val instanceManager = application.reactNativeHost.reactInstanceManager
17
+ val bundleLoaderField = instanceManager::class.java.getDeclaredField("mBundleLoader")
18
+ bundleLoaderField.isAccessible = true
19
+ bundleLoaderField.set(instanceManager, bundleLoader)
20
+ }
21
+
22
+ fun reload(application: ReactApplication) {
23
+ @Suppress("DEPRECATION")
24
+ val reactNativeHost = application.reactNativeHost
25
+ try {
26
+ reactNativeHost.reactInstanceManager.recreateReactContextInBackground()
27
+ } catch (e: Exception) {
28
+ val currentActivity = reactNativeHost.reactInstanceManager.currentReactContext?.currentActivity
29
+ if (currentActivity == null) {
30
+ return
31
+ }
32
+
33
+ currentActivity.runOnUiThread {
34
+ currentActivity.recreate()
35
+ }
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,24 @@
1
+ package com.hotupdater
2
+
3
+ import android.util.Log
4
+ import com.facebook.react.ReactApplication
5
+ import com.facebook.react.bridge.JSBundleLoader
6
+
7
+ /**
8
+ * React Native 0.82 and later have no bridge, so an app without a ReactHost has
9
+ * no instance to update. Builds for those versions compile this file instead of
10
+ * the bridge one, and never reference ReactNativeHost.
11
+ */
12
+ @Suppress("UNUSED_PARAMETER")
13
+ internal object BridgeReactInstance {
14
+ fun setJSBundle(
15
+ application: ReactApplication,
16
+ bundleLoader: JSBundleLoader?,
17
+ ) {
18
+ Log.d("HotUpdater", "No ReactHost to set the JS bundle on")
19
+ }
20
+
21
+ fun reload(application: ReactApplication) {
22
+ Log.d("HotUpdater", "No ReactHost to reload")
23
+ }
24
+ }
@@ -0,0 +1,94 @@
1
+ package com.hotupdater
2
+
3
+ import android.app.Activity
4
+ import android.app.Application
5
+ import android.os.Bundle
6
+ import androidx.lifecycle.Lifecycle
7
+ import androidx.lifecycle.ProcessLifecycleOwner
8
+
9
+ /**
10
+ * Runs an action once this process has a started activity.
11
+ *
12
+ * A headless JS task (HeadlessJsTaskService, which background push libraries
13
+ * start) loads the bundle with no activity and never renders. Waiting for an
14
+ * activity keeps that launch from counting as one that ended before its first
15
+ * render.
16
+ *
17
+ * Android does not replay lifecycle callbacks to a late registration. An
18
+ * activity that started before this gate existed, as when a brownfield app adds
19
+ * React Native to an activity already on screen, is read from
20
+ * [processHasStartedActivity] instead.
21
+ */
22
+ internal class ActivityStartGate(
23
+ private val application: Application?,
24
+ private val processHasStartedActivity: () -> Boolean = ::hasStartedActivity,
25
+ ) : Application.ActivityLifecycleCallbacks {
26
+ private var startedActivities = 0
27
+ private var pendingAction: (() -> Unit)? = null
28
+
29
+ init {
30
+ application?.registerActivityLifecycleCallbacks(this)
31
+ }
32
+
33
+ /**
34
+ * Runs [action] now when an activity is started, otherwise at the next
35
+ * activity start. It replaces an action still waiting; null cancels it.
36
+ * Without an application to observe, it runs [action] now.
37
+ */
38
+ fun runWhenActivityStarted(action: (() -> Unit)?) {
39
+ val runNow =
40
+ synchronized(this) {
41
+ val waits =
42
+ application != null &&
43
+ startedActivities == 0 &&
44
+ !processHasStartedActivity()
45
+ pendingAction = action.takeIf { waits }
46
+ action.takeUnless { waits }
47
+ }
48
+ runNow?.invoke()
49
+ }
50
+
51
+ override fun onActivityStarted(activity: Activity) {
52
+ val action =
53
+ synchronized(this) {
54
+ startedActivities += 1
55
+ pendingAction.also { pendingAction = null }
56
+ }
57
+ action?.invoke()
58
+ }
59
+
60
+ override fun onActivityStopped(activity: Activity) {
61
+ synchronized(this) {
62
+ // An activity started before this gate existed was never counted.
63
+ if (startedActivities > 0) startedActivities -= 1
64
+ }
65
+ }
66
+
67
+ override fun onActivityCreated(
68
+ activity: Activity,
69
+ savedInstanceState: Bundle?,
70
+ ) {}
71
+
72
+ override fun onActivityResumed(activity: Activity) {}
73
+
74
+ override fun onActivityPaused(activity: Activity) {}
75
+
76
+ override fun onActivitySaveInstanceState(
77
+ activity: Activity,
78
+ outState: Bundle,
79
+ ) {}
80
+
81
+ override fun onActivityDestroyed(activity: Activity) {}
82
+ }
83
+
84
+ // ProcessLifecycleOwner observes activities from process start, through the
85
+ // androidx.startup initializer that lifecycle-process registers. It is STARTED
86
+ // while an activity of this process is started, and stays at INITIALIZED when
87
+ // an app removes that initializer, so the gate then waits for the next start.
88
+ private fun hasStartedActivity(): Boolean =
89
+ runCatching {
90
+ ProcessLifecycleOwner
91
+ .get()
92
+ .lifecycle.currentState
93
+ .isAtLeast(Lifecycle.State.STARTED)
94
+ }.getOrDefault(false)
@@ -0,0 +1,286 @@
1
+ package com.hotupdater
2
+
3
+ import android.content.Context
4
+ import android.os.Build
5
+ import android.util.TypedValue
6
+ import org.json.JSONObject
7
+ import java.io.File
8
+ import java.io.InputStream
9
+ import java.security.MessageDigest
10
+ import java.util.zip.ZipFile
11
+
12
+ interface BuiltInAssetResolver {
13
+ fun copyIfMatches(
14
+ assetPath: String,
15
+ expectedHash: String,
16
+ destination: File,
17
+ ): Boolean
18
+ }
19
+
20
+ internal class AndroidBuiltInAssetResolver(
21
+ private val context: Context,
22
+ private val cacheFile: File,
23
+ ) : BuiltInAssetResolver {
24
+ private data class CachedIndex(
25
+ val packageIdentity: String,
26
+ val locators: MutableMap<String, String>,
27
+ )
28
+
29
+ private val lock = Any()
30
+
31
+ override fun copyIfMatches(
32
+ assetPath: String,
33
+ expectedHash: String,
34
+ destination: File,
35
+ ): Boolean =
36
+ synchronized(lock) {
37
+ val normalizedPath = RelativePathResolver.normalizeRelativePath(assetPath) ?: return@synchronized false
38
+ val packageIdentity = packageIdentity() ?: return@synchronized false
39
+ val index = readIndex(packageIdentity)
40
+ val cachedLocator = index.locators[normalizedPath]
41
+ if (cachedLocator != null && copyLocatorIfMatches(cachedLocator, expectedHash, destination)) {
42
+ return@synchronized true
43
+ }
44
+
45
+ val locator = resolveLocator(normalizedPath) ?: return@synchronized false
46
+ if (!copyLocatorIfMatches(locator, expectedHash, destination)) return@synchronized false
47
+
48
+ index.locators[normalizedPath] = locator
49
+ writeIndex(index)
50
+ true
51
+ }
52
+
53
+ private fun resolveLocator(assetPath: String): String? {
54
+ if (assetPath == "index.android.bundle") {
55
+ return try {
56
+ context.assets.open(assetPath).close()
57
+ "asset:$assetPath"
58
+ } catch (_: Exception) {
59
+ null
60
+ }
61
+ }
62
+
63
+ val firstSlash = assetPath.indexOf('/')
64
+ if (firstSlash <= 0 || firstSlash == assetPath.lastIndex) return null
65
+ val directory = assetPath.substring(0, firstSlash)
66
+ val filename = assetPath.substring(firstSlash + 1)
67
+ val resourceType = directory.substringBefore('-')
68
+ if (resourceType != "drawable" && resourceType != "raw" && resourceType != "font") return null
69
+ val resourceName = filename.substringBeforeLast('.')
70
+ if (resourceName.isEmpty() || resourceName.any { !(it == '_' || it.isLowerCase() || it.isDigit()) }) {
71
+ return null
72
+ }
73
+
74
+ return try {
75
+ var resourceId = context.resources.getIdentifier(resourceName, resourceType, context.packageName)
76
+ // Metro emits fonts in raw/, while Re.Pack emits the same bytes in font/.
77
+ if (resourceId == 0 &&
78
+ (resourceType == "raw" || resourceType == "font") &&
79
+ filename.substringAfterLast('.') in setOf("ttf", "otf", "ttc")
80
+ ) {
81
+ val alternateType = if (resourceType == "raw") "font" else "raw"
82
+ resourceId = context.resources.getIdentifier(resourceName, alternateType, context.packageName)
83
+ }
84
+ if (resourceId == 0) return null
85
+ val value = TypedValue()
86
+ val density = densityFor(directory)
87
+ if (density == null) {
88
+ context.resources.getValue(resourceId, value, true)
89
+ } else {
90
+ context.resources.getValueForDensity(resourceId, density, value, true)
91
+ val actualDensity = if (value.density == TypedValue.DENSITY_DEFAULT) 160 else value.density
92
+ if (actualDensity != density) return null
93
+ }
94
+ val entry = value.string?.toString() ?: return null
95
+ installedApks().firstNotNullOfOrNull { apk ->
96
+ try {
97
+ ZipFile(apk).use { zip ->
98
+ if (zip.getEntry(entry) == null) null else "zip:${apk.absolutePath}!$entry"
99
+ }
100
+ } catch (_: Exception) {
101
+ null
102
+ }
103
+ }
104
+ } catch (_: Exception) {
105
+ null
106
+ }
107
+ }
108
+
109
+ private fun densityFor(directory: String): Int? =
110
+ when (directory.substringAfter('-', "")) {
111
+ "ldpi" -> 120
112
+ "mdpi" -> 160
113
+ "tvdpi" -> 213
114
+ "hdpi" -> 240
115
+ "xhdpi" -> 320
116
+ "xxhdpi" -> 480
117
+ "xxxhdpi" -> 640
118
+ "nodpi", "anydpi" -> TypedValue.DENSITY_NONE
119
+ else -> null
120
+ }
121
+
122
+ private fun copyLocatorIfMatches(
123
+ locator: String,
124
+ expectedHash: String,
125
+ destination: File,
126
+ ): Boolean {
127
+ val input = openLocator(locator) ?: return false
128
+ val tempFile = File(destination.parentFile, "${destination.name}.builtin.tmp")
129
+ return try {
130
+ destination.parentFile?.mkdirs()
131
+ val digest = MessageDigest.getInstance("SHA-256")
132
+ input.use { source ->
133
+ tempFile.outputStream().use { output ->
134
+ val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
135
+ while (true) {
136
+ val count = source.read(buffer)
137
+ if (count < 0) break
138
+ digest.update(buffer, 0, count)
139
+ output.write(buffer, 0, count)
140
+ }
141
+ }
142
+ }
143
+ val actualHash = digest.digest().joinToString("") { "%02x".format(it) }
144
+ if (!actualHash.equals(expectedHash, ignoreCase = true)) {
145
+ tempFile.delete()
146
+ false
147
+ } else {
148
+ if (destination.exists()) destination.delete()
149
+ tempFile.renameTo(destination) ||
150
+ run {
151
+ tempFile.copyTo(destination, overwrite = true)
152
+ tempFile.delete()
153
+ true
154
+ }
155
+ }
156
+ } catch (_: Exception) {
157
+ tempFile.delete()
158
+ false
159
+ }
160
+ }
161
+
162
+ private fun openLocator(locator: String): InputStream? {
163
+ return try {
164
+ when {
165
+ locator.startsWith("asset:") -> {
166
+ context.assets.open(locator.removePrefix("asset:"))
167
+ }
168
+
169
+ locator.startsWith("zip:") -> {
170
+ val separator = locator.indexOf('!', startIndex = 4)
171
+ if (separator < 0) return null
172
+ val apk = File(locator.substring(4, separator))
173
+ if (installedApks().none { it.absolutePath == apk.absolutePath }) return null
174
+ val zip = ZipFile(apk)
175
+ val entry =
176
+ zip.getEntry(locator.substring(separator + 1)) ?: run {
177
+ zip.close()
178
+ return null
179
+ }
180
+ val stream = zip.getInputStream(entry)
181
+ object : InputStream() {
182
+ override fun read(): Int = stream.read()
183
+
184
+ override fun read(
185
+ buffer: ByteArray,
186
+ offset: Int,
187
+ length: Int,
188
+ ): Int = stream.read(buffer, offset, length)
189
+
190
+ override fun close() {
191
+ stream.close()
192
+ zip.close()
193
+ }
194
+ }
195
+ }
196
+
197
+ else -> {
198
+ null
199
+ }
200
+ }
201
+ } catch (_: Exception) {
202
+ null
203
+ }
204
+ }
205
+
206
+ private fun installedApks(): List<File> {
207
+ val info = context.applicationInfo
208
+ return (listOf(info.sourceDir) + info.splitSourceDirs.orEmpty())
209
+ .filterNotNull()
210
+ .map(::File)
211
+ .filter(File::isFile)
212
+ }
213
+
214
+ private fun packageIdentity(): String? =
215
+ try {
216
+ val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
217
+ val versionCode =
218
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
219
+ packageInfo.longVersionCode
220
+ } else {
221
+ @Suppress("DEPRECATION")
222
+ packageInfo.versionCode.toLong()
223
+ }
224
+ buildString {
225
+ append(context.packageName)
226
+ append('|')
227
+ append(versionCode)
228
+ append('|')
229
+ append(packageInfo.lastUpdateTime)
230
+ installedApks().sortedBy(File::getAbsolutePath).forEach { apk ->
231
+ append('|')
232
+ append(apk.absolutePath)
233
+ append(':')
234
+ append(apk.length())
235
+ append(':')
236
+ append(apk.lastModified())
237
+ }
238
+ }
239
+ } catch (_: Exception) {
240
+ null
241
+ }
242
+
243
+ private fun readIndex(packageIdentity: String): CachedIndex {
244
+ try {
245
+ if (cacheFile.isFile) {
246
+ val json = JSONObject(cacheFile.readText())
247
+ if (json.optInt("schemaVersion") == SCHEMA_VERSION &&
248
+ json.optString("packageIdentity") == packageIdentity
249
+ ) {
250
+ val locatorsJson = json.optJSONObject("locators") ?: JSONObject()
251
+ val locators = mutableMapOf<String, String>()
252
+ locatorsJson.keys().forEach { path -> locators[path] = locatorsJson.getString(path) }
253
+ return CachedIndex(packageIdentity, locators)
254
+ }
255
+ }
256
+ } catch (_: Exception) {
257
+ // A corrupt cache is only a missed optimization.
258
+ }
259
+ return CachedIndex(packageIdentity, mutableMapOf())
260
+ }
261
+
262
+ private fun writeIndex(index: CachedIndex) {
263
+ try {
264
+ cacheFile.parentFile?.mkdirs()
265
+ val locatorsJson = JSONObject()
266
+ index.locators.toSortedMap().forEach { (path, locator) ->
267
+ locatorsJson.put(path, locator)
268
+ }
269
+ val json =
270
+ JSONObject()
271
+ .put("schemaVersion", SCHEMA_VERSION)
272
+ .put("packageIdentity", index.packageIdentity)
273
+ .put("locators", locatorsJson)
274
+ val tempFile = File(cacheFile.parentFile, "${cacheFile.name}.tmp")
275
+ tempFile.writeText("$json\n")
276
+ if (cacheFile.exists()) cacheFile.delete()
277
+ if (!tempFile.renameTo(cacheFile)) tempFile.delete()
278
+ } catch (_: Exception) {
279
+ // Cache persistence must never block a valid install.
280
+ }
281
+ }
282
+
283
+ companion object {
284
+ private const val SCHEMA_VERSION = 1
285
+ }
286
+ }