@hot-updater/react-native 1.0.0-rc.1 → 1.0.0-rc.15
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/proguard-rules.pro +17 -2
- package/android/src/main/java/com/hotupdater/BuiltInAssetResolver.kt +286 -0
- package/android/src/main/java/com/hotupdater/BundleFileStorageService.kt +651 -637
- package/android/src/main/java/com/hotupdater/BundleMetadata.kt +10 -0
- package/android/src/main/java/com/hotupdater/HotUpdaterException.kt +0 -13
- package/android/src/main/java/com/hotupdater/HotUpdaterImpl.kt +10 -14
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryManager.kt +37 -54
- package/android/src/main/java/com/hotupdater/HotUpdaterRecoveryState.kt +64 -0
- package/android/src/main/java/com/hotupdater/OkHttpDownloadService.kt +19 -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 +40 -20
- package/android/src/oldarch/HotUpdater.kt +7 -9
- package/android/src/oldarch/HotUpdaterModule.kt +40 -20
- package/android/src/test/java/com/hotupdater/BundleFileStorageServiceTest.kt +1126 -74
- package/android/src/test/java/com/hotupdater/HotUpdaterImplTest.kt +4 -5
- package/android/src/test/java/com/hotupdater/HotUpdaterRecoveryStateTest.kt +97 -0
- package/android/src/test/java/com/hotupdater/PendingCrashRecoveryTest.kt +44 -0
- package/android/src/test/java/com/hotupdater/TarBrArchiveExtractorTest.kt +206 -0
- package/ios/HotUpdater/Internal/BrotliFileDecompressor.swift +167 -0
- package/ios/HotUpdater/Internal/BuiltInAssetResolver.swift +196 -0
- package/ios/HotUpdater/Internal/BundleFileStorageService.swift +773 -1016
- package/ios/HotUpdater/Internal/BundleMetadata.swift +5 -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 +5 -27
- package/ios/HotUpdater/Internal/HotUpdaterImpl.swift +149 -51
- package/ios/HotUpdater/Internal/NotificationExtension.swift +0 -2
- package/ios/HotUpdater/Internal/TarArchiveExtractor.swift +182 -311
- package/ios/HotUpdater/Internal/URLSessionDownloadService.swift +13 -72
- 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/checkForUpdate.js +32 -23
- package/lib/commonjs/checkForUpdate.js.map +1 -1
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js +97 -28
- package/lib/commonjs/checkForUpdate.releaseCatalog.spec.js.map +1 -1
- package/lib/commonjs/error.js +4 -13
- package/lib/commonjs/error.js.map +1 -1
- package/lib/commonjs/fetchJSON.js +9 -2
- package/lib/commonjs/fetchJSON.js.map +1 -1
- package/lib/commonjs/httpClient.js +46 -29
- package/lib/commonjs/httpClient.js.map +1 -1
- package/lib/commonjs/httpClient.spec.js +57 -5
- package/lib/commonjs/httpClient.spec.js.map +1 -1
- package/lib/commonjs/index.js +31 -14
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.spec.js +56 -11
- package/lib/commonjs/index.spec.js.map +1 -1
- package/lib/commonjs/native.js +15 -14
- package/lib/commonjs/native.js.map +1 -1
- package/lib/commonjs/native.spec.js +128 -22
- package/lib/commonjs/native.spec.js.map +1 -1
- package/lib/commonjs/{notifyAppReadyAnalytics.boundary.spec.js → notifyAppReadyInsights.boundary.spec.js} +28 -21
- package/lib/commonjs/notifyAppReadyInsights.boundary.spec.js.map +1 -0
- package/lib/commonjs/notifyAppReadyInsights.js +188 -0
- package/lib/commonjs/notifyAppReadyInsights.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +161 -29
- package/lib/commonjs/notifyAppReadyInsights.spec.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.test-utils.js → notifyAppReadyInsights.test-utils.js} +3 -3
- package/lib/commonjs/notifyAppReadyInsights.test-utils.js.map +1 -0
- package/lib/commonjs/{notifyAppReadyAnalytics.transport.spec.js → notifyAppReadyInsights.transport.spec.js} +14 -7
- package/lib/commonjs/notifyAppReadyInsights.transport.spec.js.map +1 -0
- 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/store.notification.spec.js +12 -4
- package/lib/commonjs/store.notification.spec.js.map +1 -1
- package/lib/commonjs/store.spec.js +2 -136
- package/lib/commonjs/store.spec.js.map +1 -1
- package/lib/commonjs/wrap.js +11 -12
- package/lib/commonjs/wrap.js.map +1 -1
- package/lib/commonjs/wrap.spec.js +97 -25
- package/lib/commonjs/wrap.spec.js.map +1 -1
- package/lib/module/checkForUpdate.js +32 -23
- package/lib/module/checkForUpdate.js.map +1 -1
- package/lib/module/checkForUpdate.releaseCatalog.spec.js +97 -28
- package/lib/module/checkForUpdate.releaseCatalog.spec.js.map +1 -1
- package/lib/module/error.js +4 -13
- package/lib/module/error.js.map +1 -1
- package/lib/module/fetchJSON.js +7 -1
- package/lib/module/fetchJSON.js.map +1 -1
- package/lib/module/httpClient.js +48 -31
- package/lib/module/httpClient.js.map +1 -1
- package/lib/module/httpClient.spec.js +57 -5
- package/lib/module/httpClient.spec.js.map +1 -1
- package/lib/module/index.js +32 -15
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.spec.js +56 -11
- package/lib/module/index.spec.js.map +1 -1
- package/lib/module/native.js +13 -12
- package/lib/module/native.js.map +1 -1
- package/lib/module/native.spec.js +128 -22
- package/lib/module/native.spec.js.map +1 -1
- package/lib/module/{notifyAppReadyAnalytics.boundary.spec.js → notifyAppReadyInsights.boundary.spec.js} +23 -16
- package/lib/module/notifyAppReadyInsights.boundary.spec.js.map +1 -0
- package/lib/module/notifyAppReadyInsights.js +181 -0
- package/lib/module/notifyAppReadyInsights.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.spec.js → notifyAppReadyInsights.spec.js} +153 -21
- package/lib/module/notifyAppReadyInsights.spec.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.test-utils.js → notifyAppReadyInsights.test-utils.js} +3 -3
- package/lib/module/notifyAppReadyInsights.test-utils.js.map +1 -0
- package/lib/module/{notifyAppReadyAnalytics.transport.spec.js → notifyAppReadyInsights.transport.spec.js} +11 -4
- package/lib/module/notifyAppReadyInsights.transport.spec.js.map +1 -0
- 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/store.notification.spec.js +12 -4
- package/lib/module/store.notification.spec.js.map +1 -1
- package/lib/module/store.spec.js +2 -136
- package/lib/module/store.spec.js.map +1 -1
- package/lib/module/wrap.js +11 -12
- package/lib/module/wrap.js.map +1 -1
- package/lib/module/wrap.spec.js +99 -25
- package/lib/module/wrap.spec.js.map +1 -1
- package/lib/typescript/commonjs/checkForUpdate.d.ts +2 -3
- package/lib/typescript/commonjs/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/commonjs/error.d.ts +4 -13
- package/lib/typescript/commonjs/error.d.ts.map +1 -1
- package/lib/typescript/commonjs/fetchJSON.d.ts +4 -0
- package/lib/typescript/commonjs/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/commonjs/httpClient.d.ts +6 -6
- package/lib/typescript/commonjs/httpClient.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +4 -7
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/native.d.ts +5 -9
- package/lib/typescript/commonjs/native.d.ts.map +1 -1
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts +25 -0
- package/lib/typescript/commonjs/notifyAppReadyInsights.d.ts.map +1 -0
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts +8 -0
- package/lib/typescript/commonjs/notifyAppReadyInsights.test-utils.d.ts.map +1 -0
- 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 +8 -35
- 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/wrap.types.d.ts +5 -8
- package/lib/typescript/commonjs/wrap.types.d.ts.map +1 -1
- package/lib/typescript/module/checkForUpdate.d.ts +2 -3
- package/lib/typescript/module/checkForUpdate.d.ts.map +1 -1
- package/lib/typescript/module/error.d.ts +4 -13
- package/lib/typescript/module/error.d.ts.map +1 -1
- package/lib/typescript/module/fetchJSON.d.ts +4 -0
- package/lib/typescript/module/fetchJSON.d.ts.map +1 -1
- package/lib/typescript/module/httpClient.d.ts +6 -6
- package/lib/typescript/module/httpClient.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +4 -7
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/native.d.ts +5 -9
- package/lib/typescript/module/native.d.ts.map +1 -1
- package/lib/typescript/module/notifyAppReadyInsights.d.ts +25 -0
- package/lib/typescript/module/notifyAppReadyInsights.d.ts.map +1 -0
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts +8 -0
- package/lib/typescript/module/notifyAppReadyInsights.test-utils.d.ts.map +1 -0
- 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 +8 -35
- 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/wrap.types.d.ts +5 -8
- package/lib/typescript/module/wrap.types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/checkForUpdate.releaseCatalog.spec.ts +137 -61
- package/src/checkForUpdate.ts +35 -29
- package/src/error.ts +4 -14
- package/src/fetchJSON.ts +12 -1
- package/src/httpClient.spec.ts +70 -5
- package/src/httpClient.ts +78 -64
- package/src/index.spec.ts +59 -11
- package/src/index.ts +41 -17
- package/src/native.spec.ts +133 -22
- package/src/native.ts +21 -32
- package/src/{notifyAppReadyAnalytics.boundary.spec.ts → notifyAppReadyInsights.boundary.spec.ts} +25 -17
- package/src/{notifyAppReadyAnalytics.spec.ts → notifyAppReadyInsights.spec.ts} +141 -22
- package/src/{notifyAppReadyAnalytics.test-utils.ts → notifyAppReadyInsights.test-utils.ts} +4 -4
- package/src/{notifyAppReadyAnalytics.transport.spec.ts → notifyAppReadyInsights.transport.spec.ts} +13 -5
- package/src/notifyAppReadyInsights.ts +293 -0
- package/src/sdkVersion.ts +1 -1
- package/src/specs/NativeHotUpdater.ts +8 -36
- package/src/store.notification.spec.ts +13 -3
- package/src/store.spec.ts +0 -157
- package/src/store.ts +2 -23
- package/src/wrap.spec.ts +109 -31
- package/src/wrap.tsx +11 -11
- package/src/wrap.types.ts +5 -8
- 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/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/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.js +0 -133
- package/lib/commonjs/notifyAppReadyAnalytics.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.spec.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.test-utils.js.map +0 -1
- package/lib/commonjs/notifyAppReadyAnalytics.transport.spec.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.boundary.spec.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.js +0 -128
- package/lib/module/notifyAppReadyAnalytics.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.spec.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.test-utils.js.map +0 -1
- package/lib/module/notifyAppReadyAnalytics.transport.spec.js.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts +0 -13
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.d.ts.map +0 -1
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts +0 -8
- package/lib/typescript/commonjs/notifyAppReadyAnalytics.test-utils.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts +0 -13
- package/lib/typescript/module/notifyAppReadyAnalytics.d.ts.map +0 -1
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts +0 -8
- package/lib/typescript/module/notifyAppReadyAnalytics.test-utils.d.ts.map +0 -1
- package/src/notifyAppReadyAnalytics.ts +0 -194
|
@@ -2,9 +2,19 @@
|
|
|
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
|
|
9
|
+
# Crash recovery discovers these accessors through reflection.
|
|
10
|
+
-keepclassmembers class * implements com.facebook.react.ReactApplication {
|
|
11
|
+
public *** getReactHost();
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
-keepclassmembers class * implements com.facebook.react.ReactHost {
|
|
15
|
+
public *** getCurrentReactContext();
|
|
16
|
+
}
|
|
17
|
+
|
|
8
18
|
# Keep fields accessed via reflection in ReactHost
|
|
9
19
|
# Support both Java (mReactHostDelegate) and Kotlin (reactHostDelegate) field names
|
|
10
20
|
-keepclassmembers class com.facebook.react.runtime.ReactHostImpl {
|
|
@@ -14,11 +24,16 @@
|
|
|
14
24
|
|
|
15
25
|
-keepclassmembers class * implements com.facebook.react.runtime.ReactHostDelegate {
|
|
16
26
|
** jsBundleLoader;
|
|
27
|
+
** exceptionHandler;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
-keepclassmembers class com.facebook.react.bridge.CatalystInstanceImpl {
|
|
31
|
+
** mJSExceptionHandler;
|
|
17
32
|
}
|
|
18
33
|
|
|
19
34
|
# Preserve the Brotli decoder and its embedded static dictionary
|
|
20
|
-
# (DictionaryData). R8 otherwise strips the dictionary's static init, causing
|
|
21
|
-
#
|
|
35
|
+
# (DictionaryData). R8 otherwise strips the dictionary's static init, causing
|
|
36
|
+
# per-file Brotli decompression to fail at runtime with:
|
|
22
37
|
# java.io.IOException: Brotli stream decoding failed
|
|
23
38
|
# Caused by: brotli dictionary is not set
|
|
24
39
|
-keep class com.hotupdater.vendor.brotli.** { *; }
|
|
@@ -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
|
+
}
|