@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.
- package/android/build.gradle +13 -2
- package/android/proguard-rules.pro +8 -2
- package/android/src/bridge/BridgeReactInstance.kt +38 -0
- package/android/src/bridgeless/BridgeReactInstance.kt +24 -0
- package/android/src/main/java/com/hotupdater/ActivityStartGate.kt +94 -0
- package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
- package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +1064 -844
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +50 -11
- package/android/src/main/java/com/hotupdater/FileManagerService.kt +45 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +287 -22
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +46 -30
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +75 -192
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
- package/android/src/main/java/com/hotupdater/InstallIdentityService.kt +37 -0
- package/android/src/main/java/com/hotupdater/KeyValueStorageService.kt +86 -0
- package/android/src/main/java/com/hotupdater/NetworkState.kt +31 -0
- package/android/src/main/java/com/hotupdater/NoBackupStorage.kt +17 -0
- package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +127 -50
- package/android/src/main/java/com/hotupdater/ReactNativeValueConverters.kt +34 -0
- package/android/src/main/java/com/hotupdater/TarArchiveInputStream.kt +214 -375
- package/android/src/main/java/com/hotupdater/TarBrArchiveExtractor.kt +119 -0
- package/android/src/newarch/HotUpdater.kt +7 -9
- package/android/src/newarch/HotUpdaterModule.kt +109 -84
- package/android/src/newarch/JavaScriptExceptionHooks.kt +48 -0
- package/android/src/newarch/ReactIntegrationManager.kt +6 -32
- package/android/src/oldarch/HotUpdater.kt +7 -9
- package/android/src/oldarch/HotUpdaterModule.kt +98 -74
- package/android/src/oldarch/HotUpdaterSpec.kt +4 -6
- package/android/src/oldarch/JavaScriptExceptionHooks.kt +71 -0
- package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
- package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +1032 -1116
- package/ios/HotUpdater/Internal/BundleMetadata.swift +61 -54
- package/ios/HotUpdater/Internal/FileManagerService.swift +48 -0
- package/ios/HotUpdater/Internal/FileUtilities.swift +64 -0
- package/ios/HotUpdater/Internal/HotUpdater-Bridging-Header.h +0 -1
- package/ios/HotUpdater/Internal/HotUpdater.mm +32 -67
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +229 -73
- package/ios/HotUpdater/Internal/KeyValueStorageService.swift +71 -0
- package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
- package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +119 -71
- package/ios/HotUpdater/Internal/UpdateFailure.swift +390 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecovery.h +16 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecovery.mm +83 -0
- package/ios/HotUpdater/Recovery/HotUpdaterRecoveryHooks.mm +49 -0
- package/lib/commonjs/appReady.js +96 -0
- package/lib/commonjs/appReady.js.map +1 -0
- package/lib/commonjs/checkForUpdate.js +121 -69
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/clientPlugin.js +13 -0
- package/lib/commonjs/clientPlugin.js.map +1 -0
- package/lib/commonjs/error.js +5 -14
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/fetchJSON.js +16 -27
- package/lib/commonjs/fetchJSON.js.map +1 -1
- package/lib/commonjs/fetchUpdateResponse.js +60 -0
- package/lib/commonjs/fetchUpdateResponse.js.map +1 -0
- package/lib/commonjs/httpClient.js +64 -95
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/index.js +355 -324
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/init.types.js +6 -0
- package/lib/commonjs/init.types.js.map +1 -0
- package/lib/commonjs/native.js +77 -63
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/pluginHost.js +41 -0
- package/lib/commonjs/pluginHost.js.map +1 -0
- package/lib/commonjs/releaseCatalogCache.js +36 -87
- package/lib/commonjs/releaseCatalogCache.js.map +1 -1
- package/lib/commonjs/sdkVersion.js +1 -1
- package/lib/commonjs/sdkVersion.js.map +1 -1
- package/lib/commonjs/specs/NativeHotUpdater.js.map +1 -1
- package/lib/commonjs/store.js +3 -10
- package/lib/commonjs/store.js.map +1 -1
- package/lib/commonjs/updateError.js +145 -0
- package/lib/commonjs/updateError.js.map +1 -0
- package/lib/commonjs/wrap.js +46 -38
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/module/appReady.js +90 -0
- package/lib/module/appReady.js.map +1 -0
- package/lib/module/checkForUpdate.js +114 -64
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/clientPlugin.js +4 -0
- package/lib/module/clientPlugin.js.map +1 -0
- package/lib/module/error.js +5 -14
- package/lib/module/error.js.map +1 -1
- package/lib/module/fetchJSON.js +14 -26
- package/lib/module/fetchJSON.js.map +1 -1
- package/lib/module/fetchUpdateResponse.js +55 -0
- package/lib/module/fetchUpdateResponse.js.map +1 -0
- package/lib/module/httpClient.js +63 -94
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/index.js +335 -327
- package/lib/module/index.js.map +1 -1
- package/lib/module/init.types.js +4 -0
- package/lib/module/init.types.js.map +1 -0
- package/lib/module/native.js +70 -56
- package/lib/module/native.js.map +1 -1
- package/lib/module/pluginHost.js +36 -0
- package/lib/module/pluginHost.js.map +1 -0
- package/lib/module/releaseCatalogCache.js +32 -82
- package/lib/module/releaseCatalogCache.js.map +1 -1
- package/lib/module/sdkVersion.js +1 -1
- package/lib/module/sdkVersion.js.map +1 -1
- package/lib/module/specs/NativeHotUpdater.js.map +1 -1
- package/lib/module/store.js +3 -10
- package/lib/module/store.js.map +1 -1
- package/lib/module/updateError.js +139 -0
- package/lib/module/updateError.js.map +1 -0
- package/lib/module/wrap.js +47 -38
- package/lib/module/wrap.js.map +1 -1
- package/lib/typescript/commonjs/appReady.d.ts +30 -0
- package/lib/typescript/commonjs/appReady.d.ts.map +1 -0
- package/lib/typescript/commonjs/checkForUpdate.d.ts +20 -6
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/clientPlugin.d.ts +2 -0
- package/lib/typescript/commonjs/clientPlugin.d.ts.map +1 -0
- package/lib/typescript/commonjs/error.d.ts +5 -14
- package/lib/typescript/commonjs/error.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchJSON.d.ts +5 -5
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchUpdateResponse.d.ts +14 -0
- package/lib/typescript/commonjs/fetchUpdateResponse.d.ts.map +1 -0
- package/lib/typescript/commonjs/httpClient.d.ts +5 -34
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +124 -116
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/init.types.d.ts +21 -0
- package/lib/typescript/commonjs/init.types.d.ts.map +1 -0
- package/lib/typescript/commonjs/native.d.ts +32 -25
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/pluginHost.d.ts +8 -0
- package/lib/typescript/commonjs/pluginHost.d.ts.map +1 -0
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts +8 -14
- package/lib/typescript/commonjs/releaseCatalogCache.d.ts.map +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts +1 -1
- package/lib/typescript/commonjs/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts +35 -47
- package/lib/typescript/commonjs/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/commonjs/store.d.ts +1 -3
- package/lib/typescript/commonjs/store.d.ts.map +1 -1
- package/lib/typescript/commonjs/types.d.ts +1 -1
- package/lib/typescript/commonjs/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/updateError.d.ts +26 -0
- package/lib/typescript/commonjs/updateError.d.ts.map +1 -0
- package/lib/typescript/commonjs/wrap.d.ts +2 -3
- package/lib/typescript/commonjs/wrap.d.ts.map +1 -1
- package/lib/typescript/commonjs/wrap.types.d.ts +13 -41
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
- package/lib/typescript/module/appReady.d.ts +30 -0
- package/lib/typescript/module/appReady.d.ts.map +1 -0
- package/lib/typescript/module/checkForUpdate.d.ts +20 -6
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/clientPlugin.d.ts +2 -0
- package/lib/typescript/module/clientPlugin.d.ts.map +1 -0
- package/lib/typescript/module/error.d.ts +5 -14
- package/lib/typescript/module/error.d.ts.map +1 -1
- package/lib/typescript/module/fetchJSON.d.ts +5 -5
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/module/fetchUpdateResponse.d.ts +14 -0
- package/lib/typescript/module/fetchUpdateResponse.d.ts.map +1 -0
- package/lib/typescript/module/httpClient.d.ts +5 -34
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +124 -116
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/init.types.d.ts +21 -0
- package/lib/typescript/module/init.types.d.ts.map +1 -0
- package/lib/typescript/module/native.d.ts +32 -25
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/pluginHost.d.ts +8 -0
- package/lib/typescript/module/pluginHost.d.ts.map +1 -0
- package/lib/typescript/module/releaseCatalogCache.d.ts +8 -14
- package/lib/typescript/module/releaseCatalogCache.d.ts.map +1 -1
- package/lib/typescript/module/sdkVersion.d.ts +1 -1
- package/lib/typescript/module/sdkVersion.d.ts.map +1 -1
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts +35 -47
- package/lib/typescript/module/specs/NativeHotUpdater.d.ts.map +1 -1
- package/lib/typescript/module/store.d.ts +1 -3
- package/lib/typescript/module/store.d.ts.map +1 -1
- package/lib/typescript/module/types.d.ts +1 -1
- package/lib/typescript/module/types.d.ts.map +1 -1
- package/lib/typescript/module/updateError.d.ts +26 -0
- package/lib/typescript/module/updateError.d.ts.map +1 -0
- package/lib/typescript/module/wrap.d.ts +2 -3
- package/lib/typescript/module/wrap.d.ts.map +1 -1
- package/lib/typescript/module/wrap.types.d.ts +13 -41
- package/lib/typescript/module/wrap.types.d.ts.map +1 -1
- package/package.json +8 -4
- package/src/appReady.ts +147 -0
- package/src/checkForUpdate.ts +156 -89
- package/src/clientPlugin.ts +21 -0
- package/src/error.ts +5 -15
- package/src/fetchJSON.ts +14 -30
- package/src/fetchUpdateResponse.ts +60 -0
- package/src/httpClient.ts +89 -160
- package/src/index.ts +413 -425
- package/src/init.types.ts +24 -0
- package/src/native.ts +101 -105
- package/src/pluginHost.ts +52 -0
- package/src/releaseCatalogCache.ts +43 -172
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +34 -48
- package/src/store.ts +2 -23
- package/src/types.ts +1 -1
- package/src/updateError.ts +167 -0
- package/src/wrap.tsx +60 -43
- package/src/wrap.types.ts +18 -46
- package/android/src/main/java/com/hotupdater/DecompressService.kt +0 -89
- package/android/src/main/java/com/hotupdater/DecompressionStrategy.kt +0 -26
- package/android/src/main/java/com/hotupdater/TarBrDecompressionStrategy.kt +0 -117
- package/android/src/main/java/com/hotupdater/TarGzDecompressionStrategy.kt +0 -117
- package/android/src/main/java/com/hotupdater/ZipDecompressionStrategy.kt +0 -192
- package/android/src/test/java/com/hotupdater/BsdiffPatchTest.kt +0 -46
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +0 -799
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +0 -273
- package/android/src/test/java/com/hotupdater/NativeConfigUtilsTest.kt +0 -20
- package/android/src/test/java/com/hotupdater/OkHttpDownloadServiceTest.kt +0 -111
- package/android/src/test/java/com/hotupdater/RelativePathResolverTest.kt +0 -41
- package/android/src/test/java/com/hotupdater/ReleaseCatalogCacheServiceTest.kt +0 -118
- package/android/src/test/java/com/hotupdater/TarArchiveInputStreamTest.kt +0 -68
- package/ios/HotUpdater/Internal/ArchiveExtractionUtilities.swift +0 -178
- package/ios/HotUpdater/Internal/DecompressService.swift +0 -112
- package/ios/HotUpdater/Internal/DecompressionStrategy.swift +0 -22
- package/ios/HotUpdater/Internal/StreamingTarArchiveExtractor.swift +0 -370
- package/ios/HotUpdater/Internal/TarBrDecompressionStrategy.swift +0 -39
- package/ios/HotUpdater/Internal/TarGzDecompressionStrategy.swift +0 -59
- package/ios/HotUpdater/Internal/ZipArchiveExtractor.swift +0 -482
- package/ios/HotUpdater/Internal/ZipDecompressionStrategy.swift +0 -56
- package/lib/commonjs/catalogCacheNative.spec.js +0 -31
- package/lib/commonjs/catalogCacheNative.spec.js.map +0 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +0 -616
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +0 -1
- package/lib/commonjs/httpClient.spec.js +0 -102
- package/lib/commonjs/httpClient.spec.js.map +0 -1
- package/lib/commonjs/index.spec.js +0 -302
- package/lib/commonjs/index.spec.js.map +0 -1
- package/lib/commonjs/iosInterop.spec.js +0 -18
- package/lib/commonjs/iosInterop.spec.js.map +0 -1
- package/lib/commonjs/native.spec.js +0 -883
- package/lib/commonjs/native.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js +0 -218
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.js +0 -133
- package/lib/commonjs/notifyAppReadyInsights.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.spec.js +0 -246
- package/lib/commonjs/notifyAppReadyInsights.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js +0 -24
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +0 -1
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js +0 -117
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +0 -1
- package/lib/commonjs/releaseCatalogCache.spec.js +0 -195
- package/lib/commonjs/releaseCatalogCache.spec.js.map +0 -1
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js +0 -64
- package/lib/commonjs/releaseCatalogCacheNativeContract.spec.js.map +0 -1
- package/lib/commonjs/store.notification.spec.js +0 -76
- package/lib/commonjs/store.notification.spec.js.map +0 -1
- package/lib/commonjs/store.spec.js +0 -316
- package/lib/commonjs/store.spec.js.map +0 -1
- package/lib/commonjs/wrap.spec.js +0 -329
- package/lib/commonjs/wrap.spec.js.map +0 -1
- package/lib/module/catalogCacheNative.spec.js +0 -31
- package/lib/module/catalogCacheNative.spec.js.map +0 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +0 -615
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +0 -1
- package/lib/module/httpClient.spec.js +0 -102
- package/lib/module/httpClient.spec.js.map +0 -1
- package/lib/module/index.spec.js +0 -301
- package/lib/module/index.spec.js.map +0 -1
- package/lib/module/iosInterop.spec.js +0 -18
- package/lib/module/iosInterop.spec.js.map +0 -1
- package/lib/module/native.spec.js +0 -882
- package/lib/module/native.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.boundary.spec.js +0 -217
- package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.js +0 -128
- package/lib/module/notifyAppReadyInsights.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.spec.js +0 -245
- package/lib/module/notifyAppReadyInsights.spec.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.test-utils.js +0 -18
- package/lib/module/notifyAppReadyInsights.test-utils.js.map +0 -1
- package/lib/module/notifyAppReadyInsights.transport.spec.js +0 -116
- package/lib/module/notifyAppReadyInsights.transport.spec.js.map +0 -1
- package/lib/module/releaseCatalogCache.spec.js +0 -195
- package/lib/module/releaseCatalogCache.spec.js.map +0 -1
- package/lib/module/releaseCatalogCacheNativeContract.spec.js +0 -64
- package/lib/module/releaseCatalogCacheNativeContract.spec.js.map +0 -1
- package/lib/module/store.notification.spec.js +0 -77
- package/lib/module/store.notification.spec.js.map +0 -1
- package/lib/module/store.spec.js +0 -315
- package/lib/module/store.spec.js.map +0 -1
- package/lib/module/wrap.spec.js +0 -330
- package/lib/module/wrap.spec.js.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +0 -13
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +0 -8
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyInsights.d.ts +0 -13
- package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +0 -8
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +0 -1
- package/src/catalogCacheNative.spec.ts +0 -63
- package/src/checkForUpdate.releaseCatalog.spec.ts +0 -612
- package/src/httpClient.spec.ts +0 -132
- package/src/index.spec.ts +0 -337
- package/src/iosInterop.spec.ts +0 -30
- package/src/native.spec.ts +0 -945
- package/src/notifyAppReadyInsights.boundary.spec.ts +0 -234
- package/src/notifyAppReadyInsights.spec.ts +0 -243
- package/src/notifyAppReadyInsights.test-utils.ts +0 -31
- package/src/notifyAppReadyInsights.transport.spec.ts +0 -130
- package/src/notifyAppReadyInsights.ts +0 -194
- package/src/releaseCatalogCache.spec.ts +0 -282
- package/src/releaseCatalogCacheNativeContract.spec.ts +0 -80
- package/src/store.notification.spec.ts +0 -92
- package/src/store.spec.ts +0 -372
- package/src/wrap.spec.ts +0 -406
package/android/build.gradle
CHANGED
|
@@ -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
|
|
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
|
|
21
|
-
#
|
|
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
|
+
}
|