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

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 (317) 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 +96 -0
  48. package/lib/commonjs/appReady.js.map +1 -0
  49. package/lib/commonjs/checkForUpdate.js +121 -69
  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 +5 -14
  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 +64 -95
  60. package/lib/commonjs/httpClient.js.map +1 -1
  61. package/lib/commonjs/index.js +355 -324
  62. package/lib/commonjs/index.js.map +1 -1
  63. package/lib/commonjs/init.types.js +6 -0
  64. package/lib/commonjs/init.types.js.map +1 -0
  65. package/lib/commonjs/native.js +77 -63
  66. package/lib/commonjs/native.js.map +1 -1
  67. package/lib/commonjs/pluginHost.js +41 -0
  68. package/lib/commonjs/pluginHost.js.map +1 -0
  69. package/lib/commonjs/releaseCatalogCache.js +36 -87
  70. package/lib/commonjs/releaseCatalogCache.js.map +1 -1
  71. package/lib/commonjs/sdkVersion.js +1 -1
  72. package/lib/commonjs/sdkVersion.js.map +1 -1
  73. package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
  74. package/lib/commonjs/store.js +3 -10
  75. package/lib/commonjs/store.js.map +1 -1
  76. package/lib/commonjs/updateError.js +145 -0
  77. package/lib/commonjs/updateError.js.map +1 -0
  78. package/lib/commonjs/wrap.js +46 -38
  79. package/lib/commonjs/wrap.js.map +1 -1
  80. package/lib/module/appReady.js +90 -0
  81. package/lib/module/appReady.js.map +1 -0
  82. package/lib/module/checkForUpdate.js +114 -64
  83. package/lib/module/checkForUpdate.js.map +1 -1
  84. package/lib/module/clientPlugin.js +4 -0
  85. package/lib/module/clientPlugin.js.map +1 -0
  86. package/lib/module/error.js +5 -14
  87. package/lib/module/error.js.map +1 -1
  88. package/lib/module/fetchJSON.js +14 -26
  89. package/lib/module/fetchJSON.js.map +1 -1
  90. package/lib/module/fetchUpdateResponse.js +55 -0
  91. package/lib/module/fetchUpdateResponse.js.map +1 -0
  92. package/lib/module/httpClient.js +63 -94
  93. package/lib/module/httpClient.js.map +1 -1
  94. package/lib/module/index.js +335 -327
  95. package/lib/module/index.js.map +1 -1
  96. package/lib/module/init.types.js +4 -0
  97. package/lib/module/init.types.js.map +1 -0
  98. package/lib/module/native.js +70 -56
  99. package/lib/module/native.js.map +1 -1
  100. package/lib/module/pluginHost.js +36 -0
  101. package/lib/module/pluginHost.js.map +1 -0
  102. package/lib/module/releaseCatalogCache.js +32 -82
  103. package/lib/module/releaseCatalogCache.js.map +1 -1
  104. package/lib/module/sdkVersion.js +1 -1
  105. package/lib/module/sdkVersion.js.map +1 -1
  106. package/lib/module/specs/NativeHotUpdater.js.map +1 -1
  107. package/lib/module/store.js +3 -10
  108. package/lib/module/store.js.map +1 -1
  109. package/lib/module/updateError.js +139 -0
  110. package/lib/module/updateError.js.map +1 -0
  111. package/lib/module/wrap.js +47 -38
  112. package/lib/module/wrap.js.map +1 -1
  113. package/lib/typescript/commonjs/appReady.d.ts +30 -0
  114. package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
  115. package/lib/typescript/commonjs/checkForUpdate.d.ts +20 -6
  116. package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
  117. package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
  118. package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
  119. package/lib/typescript/commonjs/error.d.ts +5 -14
  120. package/lib/typescript/commonjs/error.d.ts.map +1 -1
  121. package/lib/typescript/commonjs/fetchJSON.d.ts +5 -5
  122. package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +14 -0
  124. package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -0
  125. package/lib/typescript/commonjs/httpClient.d.ts +5 -34
  126. package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/index.d.ts +124 -116
  128. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/init.types.d.ts +21 -0
  130. package/lib/typescript/commonjs/init.types.d.ts.map +1 -0
  131. package/lib/typescript/commonjs/native.d.ts +32 -25
  132. package/lib/typescript/commonjs/native.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/pluginHost.d.ts +8 -0
  134. package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
  135. package/lib/typescript/commonjs/releaseCatalogCache.d.ts +8 -14
  136. package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
  138. package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +35 -47
  140. package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/store.d.ts +1 -3
  142. package/lib/typescript/commonjs/store.d.ts.map +1 -1
  143. package/lib/typescript/commonjs/types.d.ts +1 -1
  144. package/lib/typescript/commonjs/types.d.ts.map +1 -1
  145. package/lib/typescript/commonjs/updateError.d.ts +26 -0
  146. package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
  147. package/lib/typescript/commonjs/wrap.d.ts +2 -3
  148. package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
  149. package/lib/typescript/commonjs/wrap.types.d.ts +13 -41
  150. package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
  151. package/lib/typescript/module/appReady.d.ts +30 -0
  152. package/lib/typescript/module/appReady.d.ts.map +1 -0
  153. package/lib/typescript/module/checkForUpdate.d.ts +20 -6
  154. package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
  155. package/lib/typescript/module/clientPlugin.d.ts +2 -0
  156. package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
  157. package/lib/typescript/module/error.d.ts +5 -14
  158. package/lib/typescript/module/error.d.ts.map +1 -1
  159. package/lib/typescript/module/fetchJSON.d.ts +5 -5
  160. package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
  161. package/lib/typescript/module/fetchUpdateResponse.d.ts +14 -0
  162. package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -0
  163. package/lib/typescript/module/httpClient.d.ts +5 -34
  164. package/lib/typescript/module/httpClient.d.ts.map +1 -1
  165. package/lib/typescript/module/index.d.ts +124 -116
  166. package/lib/typescript/module/index.d.ts.map +1 -1
  167. package/lib/typescript/module/init.types.d.ts +21 -0
  168. package/lib/typescript/module/init.types.d.ts.map +1 -0
  169. package/lib/typescript/module/native.d.ts +32 -25
  170. package/lib/typescript/module/native.d.ts.map +1 -1
  171. package/lib/typescript/module/pluginHost.d.ts +8 -0
  172. package/lib/typescript/module/pluginHost.d.ts.map +1 -0
  173. package/lib/typescript/module/releaseCatalogCache.d.ts +8 -14
  174. package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
  175. package/lib/typescript/module/sdkVersion.d.ts +1 -1
  176. package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
  177. package/lib/typescript/module/specs/NativeHotUpdater.d.ts +35 -47
  178. package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
  179. package/lib/typescript/module/store.d.ts +1 -3
  180. package/lib/typescript/module/store.d.ts.map +1 -1
  181. package/lib/typescript/module/types.d.ts +1 -1
  182. package/lib/typescript/module/types.d.ts.map +1 -1
  183. package/lib/typescript/module/updateError.d.ts +26 -0
  184. package/lib/typescript/module/updateError.d.ts.map +1 -0
  185. package/lib/typescript/module/wrap.d.ts +2 -3
  186. package/lib/typescript/module/wrap.d.ts.map +1 -1
  187. package/lib/typescript/module/wrap.types.d.ts +13 -41
  188. package/lib/typescript/module/wrap.types.d.ts.map +1 -1
  189. package/package.json +8 -4
  190. package/src/appReady.ts +147 -0
  191. package/src/checkForUpdate.ts +156 -89
  192. package/src/clientPlugin.ts +21 -0
  193. package/src/error.ts +5 -15
  194. package/src/fetchJSON.ts +14 -30
  195. package/src/fetchUpdateResponse.ts +60 -0
  196. package/src/httpClient.ts +89 -160
  197. package/src/index.ts +413 -425
  198. package/src/init.types.ts +24 -0
  199. package/src/native.ts +101 -105
  200. package/src/pluginHost.ts +52 -0
  201. package/src/releaseCatalogCache.ts +43 -172
  202. package/src/sdkVersion.ts +1 -1
  203. package/src/specs/NativeHotUpdater.ts +34 -48
  204. package/src/store.ts +2 -23
  205. package/src/types.ts +1 -1
  206. package/src/updateError.ts +167 -0
  207. package/src/wrap.tsx +60 -43
  208. package/src/wrap.types.ts +18 -46
  209. package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
  210. package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
  211. package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
  212. package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
  213. package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
  214. package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
  215. package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
  216. package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
  217. package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
  218. package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
  219. package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
  220. package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
  221. package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
  222. package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
  223. package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
  224. package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
  225. package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
  226. package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
  227. package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
  228. package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
  229. package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
  230. package/lib/commonjs/catalogCacheNative.spec.js +0 -31
  231. package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
  232. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -616
  233. package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  234. package/lib/commonjs/httpClient.spec.js +0 -102
  235. package/lib/commonjs/httpClient.spec.js.map +0 -1
  236. package/lib/commonjs/index.spec.js +0 -302
  237. package/lib/commonjs/index.spec.js.map +0 -1
  238. package/lib/commonjs/iosInterop.spec.js +0 -18
  239. package/lib/commonjs/iosInterop.spec.js.map +0 -1
  240. package/lib/commonjs/native.spec.js +0 -883
  241. package/lib/commonjs/native.spec.js.map +0 -1
  242. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
  243. package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  244. package/lib/commonjs/notifyAppReadyInsights.js +0 -133
  245. package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
  246. package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
  247. package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
  248. package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
  249. package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
  250. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
  251. package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
  252. package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
  253. package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
  254. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
  255. package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  256. package/lib/commonjs/store.notification.spec.js +0 -76
  257. package/lib/commonjs/store.notification.spec.js.map +0 -1
  258. package/lib/commonjs/store.spec.js +0 -316
  259. package/lib/commonjs/store.spec.js.map +0 -1
  260. package/lib/commonjs/wrap.spec.js +0 -329
  261. package/lib/commonjs/wrap.spec.js.map +0 -1
  262. package/lib/module/catalogCacheNative.spec.js +0 -31
  263. package/lib/module/catalogCacheNative.spec.js.map +0 -1
  264. package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -615
  265. package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
  266. package/lib/module/httpClient.spec.js +0 -102
  267. package/lib/module/httpClient.spec.js.map +0 -1
  268. package/lib/module/index.spec.js +0 -301
  269. package/lib/module/index.spec.js.map +0 -1
  270. package/lib/module/iosInterop.spec.js +0 -18
  271. package/lib/module/iosInterop.spec.js.map +0 -1
  272. package/lib/module/native.spec.js +0 -882
  273. package/lib/module/native.spec.js.map +0 -1
  274. package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
  275. package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
  276. package/lib/module/notifyAppReadyInsights.js +0 -128
  277. package/lib/module/notifyAppReadyInsights.js.map +0 -1
  278. package/lib/module/notifyAppReadyInsights.spec.js +0 -245
  279. package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
  280. package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
  281. package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
  282. package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
  283. package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
  284. package/lib/module/releaseCatalogCache.spec.js +0 -195
  285. package/lib/module/releaseCatalogCache.spec.js.map +0 -1
  286. package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
  287. package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
  288. package/lib/module/store.notification.spec.js +0 -77
  289. package/lib/module/store.notification.spec.js.map +0 -1
  290. package/lib/module/store.spec.js +0 -315
  291. package/lib/module/store.spec.js.map +0 -1
  292. package/lib/module/wrap.spec.js +0 -330
  293. package/lib/module/wrap.spec.js.map +0 -1
  294. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
  295. package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
  296. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
  297. package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  298. package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
  299. package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
  300. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
  301. package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
  302. package/src/catalogCacheNative.spec.ts +0 -63
  303. package/src/checkForUpdate.releaseCatalog.spec.ts +0 -612
  304. package/src/httpClient.spec.ts +0 -132
  305. package/src/index.spec.ts +0 -337
  306. package/src/iosInterop.spec.ts +0 -30
  307. package/src/native.spec.ts +0 -945
  308. package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
  309. package/src/notifyAppReadyInsights.spec.ts +0 -243
  310. package/src/notifyAppReadyInsights.test-utils.ts +0 -31
  311. package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
  312. package/src/notifyAppReadyInsights.ts +0 -194
  313. package/src/releaseCatalogCache.spec.ts +0 -282
  314. package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
  315. package/src/store.notification.spec.ts +0 -92
  316. package/src/store.spec.ts +0 -372
  317. 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
+ }