@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/src/wrap.tsx
CHANGED
|
@@ -1,30 +1,21 @@
|
|
|
1
1
|
import React, { useEffect, useState } from "react";
|
|
2
2
|
|
|
3
|
-
import { checkForUpdate } from "./checkForUpdate";
|
|
4
3
|
import { useEventCallback } from "./hooks/useEventCallback";
|
|
5
4
|
import { getUpdateId, reload } from "./native";
|
|
6
|
-
import { handleNotifyAppReady } from "./notifyAppReadyInsights";
|
|
7
5
|
import { useHotUpdaterStore } from "./store";
|
|
8
6
|
import type {
|
|
9
|
-
|
|
7
|
+
HotUpdaterFallbackComponentProps,
|
|
10
8
|
InternalWrapOptions,
|
|
11
9
|
UpdateStatus,
|
|
12
10
|
} from "./wrap.types";
|
|
13
11
|
|
|
14
12
|
export type {
|
|
15
|
-
AutoUpdateOptions,
|
|
16
13
|
HotUpdaterFallbackComponentProps,
|
|
17
|
-
|
|
18
|
-
HotUpdaterOptions,
|
|
19
|
-
InternalInitOptions,
|
|
14
|
+
HotUpdaterWrapOptions,
|
|
20
15
|
InternalWrapOptions,
|
|
21
16
|
RunUpdateProcessResponse,
|
|
22
17
|
} from "./wrap.types";
|
|
23
18
|
|
|
24
|
-
export function init(options: InternalInitOptions): void {
|
|
25
|
-
void handleNotifyAppReady(options);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
19
|
export function wrap(
|
|
29
20
|
options: InternalWrapOptions,
|
|
30
21
|
): <P extends object>(
|
|
@@ -32,11 +23,46 @@ export function wrap(
|
|
|
32
23
|
) => React.ComponentType<P> {
|
|
33
24
|
const { reloadOnForceUpdate = true, ...restOptions } = options;
|
|
34
25
|
|
|
26
|
+
// Progress is subscribed only where it is rendered or reported, so progress
|
|
27
|
+
// events don't re-render the wrapped app.
|
|
28
|
+
const ProgressReporter = ({
|
|
29
|
+
onProgress,
|
|
30
|
+
}: {
|
|
31
|
+
onProgress: (progress: number) => void;
|
|
32
|
+
}) => {
|
|
33
|
+
const progress = useHotUpdaterStore((state) => state.progress);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
onProgress(progress);
|
|
37
|
+
}, [progress]);
|
|
38
|
+
|
|
39
|
+
return null;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const FallbackWithProgress = ({
|
|
43
|
+
Fallback,
|
|
44
|
+
status,
|
|
45
|
+
message,
|
|
46
|
+
}: {
|
|
47
|
+
Fallback: React.FC<HotUpdaterFallbackComponentProps>;
|
|
48
|
+
status: HotUpdaterFallbackComponentProps["status"];
|
|
49
|
+
message: string | null;
|
|
50
|
+
}) => {
|
|
51
|
+
const progressState = useHotUpdaterStore((state) => state);
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Fallback
|
|
55
|
+
artifactType={progressState.artifactType}
|
|
56
|
+
details={progressState.details}
|
|
57
|
+
progress={progressState.progress}
|
|
58
|
+
status={status}
|
|
59
|
+
message={message}
|
|
60
|
+
/>
|
|
61
|
+
);
|
|
62
|
+
};
|
|
63
|
+
|
|
35
64
|
return <P extends object>(WrappedComponent: React.ComponentType<P>) => {
|
|
36
65
|
const HotUpdaterHOC: React.FC<P> = (props: P) => {
|
|
37
|
-
const progressState = useHotUpdaterStore((state) => state);
|
|
38
|
-
const progress = progressState.progress;
|
|
39
|
-
|
|
40
66
|
const [message, setMessage] = useState<string | null>(null);
|
|
41
67
|
const [updateStatus, setUpdateStatus] =
|
|
42
68
|
useState<UpdateStatus>("CHECK_FOR_UPDATE");
|
|
@@ -45,15 +71,12 @@ export function wrap(
|
|
|
45
71
|
try {
|
|
46
72
|
setUpdateStatus("CHECK_FOR_UPDATE");
|
|
47
73
|
|
|
48
|
-
const updateInfo = await checkForUpdate({
|
|
49
|
-
insights: restOptions.insights,
|
|
50
|
-
client: restOptions.client,
|
|
74
|
+
const updateInfo = await restOptions.checkForUpdate({
|
|
51
75
|
updateStrategy: restOptions.updateStrategy,
|
|
52
|
-
requestHeaders: restOptions.requestHeaders,
|
|
53
|
-
requestTimeout: restOptions.requestTimeout,
|
|
54
|
-
onError: restOptions.onError,
|
|
55
76
|
});
|
|
56
77
|
|
|
78
|
+
// A forced update reloads only after init has read this launch.
|
|
79
|
+
await restOptions.appReady();
|
|
57
80
|
setMessage(updateInfo?.message ?? null);
|
|
58
81
|
|
|
59
82
|
if (!updateInfo) {
|
|
@@ -69,7 +92,7 @@ export function wrap(
|
|
|
69
92
|
|
|
70
93
|
if (updateInfo.shouldForceUpdate === false) {
|
|
71
94
|
void updateInfo.updateBundle().catch((error: unknown) => {
|
|
72
|
-
restOptions.onError
|
|
95
|
+
restOptions.onError(error);
|
|
73
96
|
});
|
|
74
97
|
|
|
75
98
|
restOptions.onUpdateProcessCompleted?.({
|
|
@@ -106,44 +129,38 @@ export function wrap(
|
|
|
106
129
|
} catch (error) {
|
|
107
130
|
const normalizedError =
|
|
108
131
|
error instanceof Error ? error : new Error(String(error));
|
|
109
|
-
restOptions.onError
|
|
132
|
+
restOptions.onError(normalizedError);
|
|
110
133
|
setUpdateStatus("UPDATE_PROCESS_COMPLETED");
|
|
111
134
|
}
|
|
112
135
|
});
|
|
113
136
|
|
|
114
|
-
useEffect(() => {
|
|
115
|
-
restOptions.onProgress?.(progress);
|
|
116
|
-
}, [progress]);
|
|
117
|
-
|
|
118
|
-
// Read the native launch report after the first render commit.
|
|
119
|
-
useEffect(() => {
|
|
120
|
-
void handleNotifyAppReady(restOptions);
|
|
121
|
-
}, []);
|
|
122
|
-
|
|
123
137
|
// Start update check
|
|
124
138
|
useEffect(() => {
|
|
125
139
|
initHotUpdater();
|
|
126
140
|
}, []);
|
|
127
141
|
|
|
128
|
-
|
|
142
|
+
const content =
|
|
129
143
|
restOptions.fallbackComponent &&
|
|
130
|
-
updateStatus !== "UPDATE_PROCESS_COMPLETED"
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return (
|
|
134
|
-
<Fallback
|
|
135
|
-
artifactType={progressState.artifactType}
|
|
136
|
-
details={progressState.details}
|
|
137
|
-
downloadedBytes={progressState.downloadedBytes}
|
|
138
|
-
progress={progress}
|
|
144
|
+
updateStatus !== "UPDATE_PROCESS_COMPLETED" ? (
|
|
145
|
+
<FallbackWithProgress
|
|
146
|
+
Fallback={restOptions.fallbackComponent}
|
|
139
147
|
status={updateStatus}
|
|
140
148
|
message={message}
|
|
141
|
-
totalBytes={progressState.totalBytes}
|
|
142
149
|
/>
|
|
150
|
+
) : (
|
|
151
|
+
<WrappedComponent {...props} />
|
|
143
152
|
);
|
|
153
|
+
|
|
154
|
+
if (!restOptions.onProgress) {
|
|
155
|
+
return content;
|
|
144
156
|
}
|
|
145
157
|
|
|
146
|
-
return
|
|
158
|
+
return (
|
|
159
|
+
<>
|
|
160
|
+
{content}
|
|
161
|
+
<ProgressReporter onProgress={restOptions.onProgress} />
|
|
162
|
+
</>
|
|
163
|
+
);
|
|
147
164
|
};
|
|
148
165
|
|
|
149
166
|
return HotUpdaterHOC as React.ComponentType<P>;
|
package/src/wrap.types.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type React from "react";
|
|
2
2
|
|
|
3
|
-
import type {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import type {
|
|
4
|
+
CheckForUpdateOptions,
|
|
5
|
+
CheckForUpdateResult,
|
|
6
|
+
} from "./checkForUpdate";
|
|
6
7
|
import type { HotUpdaterState } from "./store";
|
|
7
|
-
import type { HotUpdaterBaseURL } from "./types";
|
|
8
8
|
|
|
9
9
|
export interface RunUpdateProcessResponse {
|
|
10
10
|
status: "ROLLBACK" | "UPDATE" | "UP_TO_DATE";
|
|
@@ -21,57 +21,29 @@ export type UpdateStatus =
|
|
|
21
21
|
export type HotUpdaterFallbackComponentProps = {
|
|
22
22
|
status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
|
|
23
23
|
progress: number;
|
|
24
|
-
downloadedBytes: HotUpdaterState["downloadedBytes"];
|
|
25
|
-
totalBytes: HotUpdaterState["totalBytes"];
|
|
26
24
|
message: string | null;
|
|
27
25
|
artifactType: HotUpdaterState["artifactType"];
|
|
28
26
|
details: HotUpdaterState["details"];
|
|
29
27
|
};
|
|
30
28
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
*/
|
|
37
|
-
insights?: boolean;
|
|
38
|
-
/** Base URL of a server exposing the Hot Updater v1 client HTTP protocol. */
|
|
39
|
-
baseURL: HotUpdaterBaseURL;
|
|
40
|
-
requestHeaders?: Record<string, string>;
|
|
41
|
-
requestTimeout?: number;
|
|
42
|
-
onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
|
|
43
|
-
onError?: (error: HotUpdaterError | Error | unknown) => void;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export type AutoUpdateOptions = CommonHotUpdaterOptions & {
|
|
29
|
+
/**
|
|
30
|
+
* The update flow `hotUpdater.wrap` runs when the wrapped root mounts. The
|
|
31
|
+
* server, request settings, and plugins come from `HotUpdater.init`.
|
|
32
|
+
*/
|
|
33
|
+
export interface HotUpdaterWrapOptions {
|
|
47
34
|
updateStrategy: "fingerprint" | "appVersion";
|
|
48
35
|
fallbackComponent?: React.FC<HotUpdaterFallbackComponentProps>;
|
|
49
36
|
onProgress?: (progress: number) => void;
|
|
50
37
|
reloadOnForceUpdate?: boolean;
|
|
51
38
|
onUpdateProcessCompleted?: (response: RunUpdateProcessResponse) => void;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export type HotUpdaterInitOptions = CommonHotUpdaterOptions;
|
|
55
|
-
|
|
56
|
-
export type HotUpdaterOptions = AutoUpdateOptions;
|
|
39
|
+
}
|
|
57
40
|
|
|
58
|
-
type
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
41
|
+
export type InternalWrapOptions = HotUpdaterWrapOptions & {
|
|
42
|
+
/** The instance's check, over the configuration of `HotUpdater.init`. */
|
|
43
|
+
checkForUpdate: (
|
|
44
|
+
options: Pick<CheckForUpdateOptions, "updateStrategy">,
|
|
45
|
+
) => Promise<CheckForUpdateResult | null>;
|
|
46
|
+
/** Settles once `HotUpdater.init` has read this launch. */
|
|
47
|
+
appReady: () => Promise<unknown>;
|
|
48
|
+
onError: (error: unknown) => void;
|
|
65
49
|
};
|
|
66
|
-
|
|
67
|
-
type InternalAutoUpdateOptions = InternalCommonOptions & {
|
|
68
|
-
updateStrategy: "fingerprint" | "appVersion";
|
|
69
|
-
fallbackComponent?: React.FC<HotUpdaterFallbackComponentProps>;
|
|
70
|
-
onProgress?: (progress: number) => void;
|
|
71
|
-
reloadOnForceUpdate?: boolean;
|
|
72
|
-
onUpdateProcessCompleted?: (response: RunUpdateProcessResponse) => void;
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
export type InternalInitOptions = InternalCommonOptions;
|
|
76
|
-
|
|
77
|
-
export type InternalWrapOptions = InternalAutoUpdateOptions;
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
package com.hotupdater
|
|
2
|
-
|
|
3
|
-
import android.util.Log
|
|
4
|
-
import java.io.File
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Unified decompression service that uses Strategy pattern to handle multiple compression formats.
|
|
8
|
-
* Automatically detects format by trying each strategy's validation and delegates to appropriate decompression strategy.
|
|
9
|
-
*/
|
|
10
|
-
class DecompressService {
|
|
11
|
-
companion object {
|
|
12
|
-
private const val TAG = "DecompressService"
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Strategies with reliable file signatures that can be validated cheaply.
|
|
16
|
-
// TAR.BR is attempted only as the final fallback because Brotli has no reliable magic bytes.
|
|
17
|
-
private val signatureStrategies =
|
|
18
|
-
listOf(
|
|
19
|
-
ZipDecompressionStrategy(),
|
|
20
|
-
TarGzDecompressionStrategy(),
|
|
21
|
-
)
|
|
22
|
-
private val tarBrStrategy = TarBrDecompressionStrategy()
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Extracts a compressed file to the destination directory.
|
|
26
|
-
* Automatically detects compression format by trying each strategy's validation.
|
|
27
|
-
* @param filePath Path to the compressed file
|
|
28
|
-
* @param destinationPath Path to the destination directory
|
|
29
|
-
* @param progressCallback Callback for progress updates (0.0 - 1.0)
|
|
30
|
-
* @return true if extraction was successful, false otherwise
|
|
31
|
-
*/
|
|
32
|
-
fun extractZipFile(
|
|
33
|
-
filePath: String,
|
|
34
|
-
destinationPath: String,
|
|
35
|
-
progressCallback: (Double) -> Unit,
|
|
36
|
-
): Boolean {
|
|
37
|
-
// Collect file information for better error messages
|
|
38
|
-
val file = File(filePath)
|
|
39
|
-
val fileName = file.name
|
|
40
|
-
val fileSize = if (file.exists()) file.length() else 0L
|
|
41
|
-
|
|
42
|
-
// Try each signature-based strategy first.
|
|
43
|
-
for (strategy in signatureStrategies) {
|
|
44
|
-
if (strategy.isValid(filePath)) {
|
|
45
|
-
Log.d(TAG, "Using strategy for $fileName")
|
|
46
|
-
return strategy.decompress(filePath, destinationPath, progressCallback)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
Log.d(TAG, "No ZIP/TAR.GZ signature matched for $fileName, trying TAR.BR fallback")
|
|
51
|
-
|
|
52
|
-
if (tarBrStrategy.decompress(filePath, destinationPath, progressCallback)) {
|
|
53
|
-
Log.d(TAG, "Using TAR.BR fallback for $fileName")
|
|
54
|
-
return true
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
val errorMessage = createInvalidArchiveMessage(fileName, fileSize)
|
|
58
|
-
Log.e(TAG, errorMessage)
|
|
59
|
-
return false
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Validates if a file matches one of the signature-based archive formats.
|
|
64
|
-
* @param filePath Path to the file to validate
|
|
65
|
-
* @return true if the file is a valid compressed archive
|
|
66
|
-
*/
|
|
67
|
-
fun isValidZipFile(filePath: String): Boolean {
|
|
68
|
-
for (strategy in signatureStrategies) {
|
|
69
|
-
if (strategy.isValid(filePath)) {
|
|
70
|
-
return true
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
Log.d(TAG, "No ZIP/TAR.GZ signature matched for file: $filePath. TAR.BR is handled during extraction fallback.")
|
|
74
|
-
return false
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
private fun createInvalidArchiveMessage(
|
|
78
|
-
fileName: String,
|
|
79
|
-
fileSize: Long,
|
|
80
|
-
): String =
|
|
81
|
-
"""
|
|
82
|
-
The downloaded bundle file is not a valid compressed archive: $fileName ($fileSize bytes)
|
|
83
|
-
|
|
84
|
-
Supported formats:
|
|
85
|
-
- ZIP archives (.zip)
|
|
86
|
-
- GZIP compressed TAR archives (.tar.gz)
|
|
87
|
-
- Brotli compressed TAR archives (.tar.br)
|
|
88
|
-
""".trimIndent()
|
|
89
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
package com.hotupdater
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Interface for decompression strategies
|
|
5
|
-
*/
|
|
6
|
-
interface DecompressionStrategy {
|
|
7
|
-
/**
|
|
8
|
-
* Validates if a file can be decompressed by this strategy
|
|
9
|
-
* @param filePath Path to the file to validate
|
|
10
|
-
* @return true if the file is valid for this strategy
|
|
11
|
-
*/
|
|
12
|
-
fun isValid(filePath: String): Boolean
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Decompresses a file to the destination directory
|
|
16
|
-
* @param filePath Path to the compressed file
|
|
17
|
-
* @param destinationPath Path to the destination directory
|
|
18
|
-
* @param progressCallback Callback for progress updates (0.0 - 1.0)
|
|
19
|
-
* @return true if decompression was successful, false otherwise
|
|
20
|
-
*/
|
|
21
|
-
fun decompress(
|
|
22
|
-
filePath: String,
|
|
23
|
-
destinationPath: String,
|
|
24
|
-
progressCallback: (Double) -> Unit,
|
|
25
|
-
): Boolean
|
|
26
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
package com.hotupdater
|
|
2
|
-
|
|
3
|
-
import android.util.Log
|
|
4
|
-
import com.hotupdater.vendor.brotli.dec.BrotliInputStream
|
|
5
|
-
import java.io.BufferedInputStream
|
|
6
|
-
import java.io.File
|
|
7
|
-
import java.io.FileInputStream
|
|
8
|
-
import java.io.FileOutputStream
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Strategy for handling TAR+Brotli compressed files
|
|
12
|
-
* Uses native Brotli decoder and custom TAR parser
|
|
13
|
-
*/
|
|
14
|
-
class TarBrDecompressionStrategy : DecompressionStrategy {
|
|
15
|
-
companion object {
|
|
16
|
-
private const val TAG = "TarBrStrategy"
|
|
17
|
-
private const val MIN_FILE_SIZE = 10L
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
override fun isValid(filePath: String): Boolean {
|
|
21
|
-
val file = File(filePath)
|
|
22
|
-
|
|
23
|
-
if (!file.exists() || file.length() < MIN_FILE_SIZE) {
|
|
24
|
-
Log.d(TAG, "Invalid file: doesn't exist or too small (${file.length()} bytes)")
|
|
25
|
-
return false
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
return try {
|
|
29
|
-
FileInputStream(file).use { fileInputStream ->
|
|
30
|
-
BufferedInputStream(fileInputStream).use { bufferedInputStream ->
|
|
31
|
-
BrotliInputStream(bufferedInputStream).use { brotliInputStream ->
|
|
32
|
-
TarArchiveInputStream(brotliInputStream).use { tarInputStream ->
|
|
33
|
-
val firstEntry = tarInputStream.getNextEntry()
|
|
34
|
-
val isValid = firstEntry != null
|
|
35
|
-
|
|
36
|
-
if (!isValid) {
|
|
37
|
-
Log.d(TAG, "Invalid file: tar archive has no entries")
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
isValid
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
} catch (e: Exception) {
|
|
46
|
-
Log.d(TAG, "Invalid file: Brotli/TAR validation failed - ${e.message}")
|
|
47
|
-
false
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
override fun decompress(
|
|
52
|
-
filePath: String,
|
|
53
|
-
destinationPath: String,
|
|
54
|
-
progressCallback: (Double) -> Unit,
|
|
55
|
-
): Boolean =
|
|
56
|
-
try {
|
|
57
|
-
val destinationDir = File(destinationPath)
|
|
58
|
-
if (!destinationDir.exists()) {
|
|
59
|
-
destinationDir.mkdirs()
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
val sourceFile = File(filePath)
|
|
63
|
-
val totalSize = sourceFile.length()
|
|
64
|
-
var processedBytes = 0L
|
|
65
|
-
|
|
66
|
-
Log.d(TAG, "Extracting tar.br file: $filePath")
|
|
67
|
-
|
|
68
|
-
FileInputStream(filePath).use { fileInputStream ->
|
|
69
|
-
BufferedInputStream(fileInputStream).use { bufferedInputStream ->
|
|
70
|
-
BrotliInputStream(bufferedInputStream).use { brotliInputStream ->
|
|
71
|
-
TarArchiveInputStream(brotliInputStream).use { tarInputStream ->
|
|
72
|
-
var entry = tarInputStream.getNextEntry()
|
|
73
|
-
|
|
74
|
-
while (entry != null) {
|
|
75
|
-
val file = RelativePathResolver.resolveInside(destinationDir, entry.name)
|
|
76
|
-
|
|
77
|
-
if (file == null) {
|
|
78
|
-
Log.w(TAG, "Skipping potentially malicious tar entry: ${entry.name}")
|
|
79
|
-
entry = tarInputStream.getNextEntry()
|
|
80
|
-
continue
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (entry.isDirectory) {
|
|
84
|
-
file.mkdirs()
|
|
85
|
-
} else {
|
|
86
|
-
file.parentFile?.mkdirs()
|
|
87
|
-
|
|
88
|
-
FileOutputStream(file).use { output ->
|
|
89
|
-
val buffer = ByteArray(8 * 1024)
|
|
90
|
-
var bytesRead: Int
|
|
91
|
-
|
|
92
|
-
while (tarInputStream.read(buffer).also { bytesRead = it } != -1) {
|
|
93
|
-
output.write(buffer, 0, bytesRead)
|
|
94
|
-
processedBytes += bytesRead
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
val progress = processedBytes.toDouble() / (totalSize * 2.0)
|
|
100
|
-
progressCallback.invoke(progress.coerceIn(0.0, 1.0))
|
|
101
|
-
|
|
102
|
-
entry = tarInputStream.getNextEntry()
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
Log.d(TAG, "Successfully extracted tar.br file")
|
|
110
|
-
progressCallback.invoke(1.0)
|
|
111
|
-
true
|
|
112
|
-
} catch (e: Exception) {
|
|
113
|
-
Log.d(TAG, "Failed to extract tar.br file: ${e.message}")
|
|
114
|
-
e.printStackTrace()
|
|
115
|
-
false
|
|
116
|
-
}
|
|
117
|
-
}
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
package com.hotupdater
|
|
2
|
-
|
|
3
|
-
import android.util.Log
|
|
4
|
-
import java.io.BufferedInputStream
|
|
5
|
-
import java.io.File
|
|
6
|
-
import java.io.FileInputStream
|
|
7
|
-
import java.io.FileOutputStream
|
|
8
|
-
import java.util.zip.GZIPInputStream
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Strategy for handling TAR+GZIP compressed files
|
|
12
|
-
* Uses native GZIP decoder and custom TAR parser
|
|
13
|
-
*/
|
|
14
|
-
class TarGzDecompressionStrategy : DecompressionStrategy {
|
|
15
|
-
companion object {
|
|
16
|
-
private const val TAG = "TarGzStrategy"
|
|
17
|
-
private const val MIN_FILE_SIZE = 10L
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
override fun isValid(filePath: String): Boolean {
|
|
21
|
-
val file = File(filePath)
|
|
22
|
-
|
|
23
|
-
if (!file.exists() || file.length() < MIN_FILE_SIZE) {
|
|
24
|
-
Log.d(TAG, "Invalid file: doesn't exist or too small (${file.length()} bytes)")
|
|
25
|
-
return false
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
try {
|
|
29
|
-
FileInputStream(file).use { fis ->
|
|
30
|
-
val header = ByteArray(2)
|
|
31
|
-
if (fis.read(header) != 2) {
|
|
32
|
-
Log.d(TAG, "Invalid file: cannot read header")
|
|
33
|
-
return false
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// Check GZIP magic bytes (0x1F 0x8B)
|
|
37
|
-
val isGzip = header[0] == 0x1F.toByte() && header[1] == 0x8B.toByte()
|
|
38
|
-
if (!isGzip) {
|
|
39
|
-
Log.d(
|
|
40
|
-
TAG,
|
|
41
|
-
"Invalid file: wrong magic bytes (expected 0x1F 0x8B, got 0x${header[0].toString(16)} 0x${header[1].toString(16)})",
|
|
42
|
-
)
|
|
43
|
-
}
|
|
44
|
-
return isGzip
|
|
45
|
-
}
|
|
46
|
-
} catch (e: Exception) {
|
|
47
|
-
Log.d(TAG, "Invalid file: error reading header: ${e.message}")
|
|
48
|
-
return false
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
override fun decompress(
|
|
53
|
-
filePath: String,
|
|
54
|
-
destinationPath: String,
|
|
55
|
-
progressCallback: (Double) -> Unit,
|
|
56
|
-
): Boolean =
|
|
57
|
-
try {
|
|
58
|
-
val destinationDir = File(destinationPath)
|
|
59
|
-
if (!destinationDir.exists()) {
|
|
60
|
-
destinationDir.mkdirs()
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
val sourceFile = File(filePath)
|
|
64
|
-
val totalSize = sourceFile.length()
|
|
65
|
-
var processedBytes = 0L
|
|
66
|
-
|
|
67
|
-
Log.d(TAG, "Extracting tar.gz file: $filePath")
|
|
68
|
-
|
|
69
|
-
FileInputStream(filePath).use { fileInputStream ->
|
|
70
|
-
BufferedInputStream(fileInputStream).use { bufferedInputStream ->
|
|
71
|
-
GZIPInputStream(bufferedInputStream).use { gzipInputStream ->
|
|
72
|
-
TarArchiveInputStream(gzipInputStream).use { tarInputStream ->
|
|
73
|
-
var entry = tarInputStream.getNextEntry()
|
|
74
|
-
|
|
75
|
-
while (entry != null) {
|
|
76
|
-
val file = RelativePathResolver.resolveInside(destinationDir, entry.name)
|
|
77
|
-
|
|
78
|
-
if (file == null) {
|
|
79
|
-
Log.w(TAG, "Skipping potentially malicious tar entry: ${entry.name}")
|
|
80
|
-
entry = tarInputStream.getNextEntry()
|
|
81
|
-
continue
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (entry.isDirectory) {
|
|
85
|
-
file.mkdirs()
|
|
86
|
-
} else {
|
|
87
|
-
file.parentFile?.mkdirs()
|
|
88
|
-
|
|
89
|
-
FileOutputStream(file).use { output ->
|
|
90
|
-
val buffer = ByteArray(8 * 1024)
|
|
91
|
-
var bytesRead: Int
|
|
92
|
-
|
|
93
|
-
while (tarInputStream.read(buffer).also { bytesRead = it } != -1) {
|
|
94
|
-
output.write(buffer, 0, bytesRead)
|
|
95
|
-
processedBytes += bytesRead
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
val progress = processedBytes.toDouble() / (totalSize * 2.0)
|
|
101
|
-
progressCallback.invoke(progress.coerceIn(0.0, 1.0))
|
|
102
|
-
|
|
103
|
-
entry = tarInputStream.getNextEntry()
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
Log.d(TAG, "Successfully extracted tar.gz file")
|
|
111
|
-
progressCallback.invoke(1.0)
|
|
112
|
-
true
|
|
113
|
-
} catch (e: Exception) {
|
|
114
|
-
Log.e(TAG, "Error extracting tar.gz file: ${e.message}", e)
|
|
115
|
-
false
|
|
116
|
-
}
|
|
117
|
-
}
|