ilabs-flir 1.0.4 → 1.0.6
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/Flir/build.gradle.kts +14 -27
- package/android/Flir/libs/androidsdk-release.aar +0 -0
- package/android/Flir/libs/thermalsdk-release.aar +0 -0
- package/android/Flir/src/main/AndroidManifest.xml +14 -0
- package/android/Flir/src/main/java/flir/android/FlirDownloadManager.kt +23 -44
- package/android/Flir/src/main/java/flir/android/FlirManager.kt +312 -313
- package/android/Flir/src/main/java/flir/android/FlirModule.kt +183 -0
- package/android/Flir/src/main/java/flir/android/FlirSDKLoader.kt +35 -194
- package/android/Flir/src/main/java/flir/android/FlirSdkManager.java +409 -1339
- package/package.json +1 -1
- package/src/index.d.ts +60 -1
- package/android/Flir/libs/flir-stubs.jar +0 -0
- package/android/Flir/src/main/java/com/flir/thermalsdk/ErrorCode.java +0 -13
- package/android/Flir/src/main/java/com/flir/thermalsdk/ErrorCodeException.java +0 -14
- package/android/Flir/src/main/java/com/flir/thermalsdk/ThermalSdkAndroid.java +0 -16
- package/android/Flir/src/main/java/com/flir/thermalsdk/androidsdk/image/BitmapAndroid.java +0 -20
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/ImageBuffer.java +0 -11
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/JavaImageBuffer.java +0 -35
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/Palette.java +0 -15
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/PaletteManager.java +0 -16
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/Point.java +0 -11
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/ThermalImage.java +0 -23
- package/android/Flir/src/main/java/com/flir/thermalsdk/image/ThermalValue.java +0 -9
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/Camera.java +0 -26
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/CameraType.java +0 -8
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/CommunicationInterface.java +0 -16
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/ConnectParameters.java +0 -16
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/Identity.java +0 -23
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/IpSettings.java +0 -9
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/RemoteControl.java +0 -16
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/connectivity/ConnectionStatusListener.java +0 -7
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/discovery/DiscoveryEventListener.java +0 -14
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/discovery/DiscoveryFactory.java +0 -33
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/remote/OnReceived.java +0 -5
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/remote/OnRemoteError.java +0 -7
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/streaming/Stream.java +0 -8
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/streaming/ThermalStreamer.java +0 -28
- package/android/Flir/src/main/java/com/flir/thermalsdk/live/streaming/VisualStreamer.java +0 -18
|
@@ -1,12 +1,35 @@
|
|
|
1
1
|
package flir.android
|
|
2
2
|
|
|
3
|
+
import android.util.Log
|
|
3
4
|
import com.facebook.react.bridge.Promise
|
|
4
5
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
6
|
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
6
7
|
import com.facebook.react.bridge.ReactMethod
|
|
8
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
7
9
|
|
|
8
10
|
class FlirModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
|
11
|
+
|
|
12
|
+
companion object {
|
|
13
|
+
private const val TAG = "FlirModule"
|
|
14
|
+
}
|
|
15
|
+
|
|
9
16
|
override fun getName(): String = "FlirModule"
|
|
17
|
+
|
|
18
|
+
// Required for RN event emitter support
|
|
19
|
+
private var listenerCount = 0
|
|
20
|
+
|
|
21
|
+
@ReactMethod
|
|
22
|
+
fun addListener(eventName: String) {
|
|
23
|
+
listenerCount++
|
|
24
|
+
Log.d(TAG, "addListener: $eventName (count: $listenerCount)")
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@ReactMethod
|
|
28
|
+
fun removeListeners(count: Int) {
|
|
29
|
+
listenerCount -= count
|
|
30
|
+
if (listenerCount < 0) listenerCount = 0
|
|
31
|
+
Log.d(TAG, "removeListeners: $count (remaining: $listenerCount)")
|
|
32
|
+
}
|
|
10
33
|
|
|
11
34
|
// Simple placeholder conversion: converts an ARGB color to a pseudo-temperature value.
|
|
12
35
|
// Replace with SDK call when integrating thermalsdk APIs.
|
|
@@ -71,4 +94,164 @@ class FlirModule(private val reactContext: ReactApplicationContext) : ReactConte
|
|
|
71
94
|
promise.reject("ERR_FLIR_DEVICE_INFO", e)
|
|
72
95
|
}
|
|
73
96
|
}
|
|
97
|
+
|
|
98
|
+
@ReactMethod
|
|
99
|
+
fun isSDKDownloaded(promise: Promise) {
|
|
100
|
+
try {
|
|
101
|
+
val available = FlirSDKLoader.isSDKAvailable(reactContext)
|
|
102
|
+
promise.resolve(available)
|
|
103
|
+
} catch (e: Exception) {
|
|
104
|
+
promise.reject("ERR_FLIR_SDK_CHECK", e)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@ReactMethod
|
|
109
|
+
fun getSDKStatus(promise: Promise) {
|
|
110
|
+
try {
|
|
111
|
+
val available = FlirSDKLoader.isSDKAvailable(reactContext)
|
|
112
|
+
val arch = FlirSDKLoader.getDeviceArch()
|
|
113
|
+
val dexPath = FlirSDKLoader.getDexPath(reactContext)
|
|
114
|
+
val nativeLibDir = FlirSDKLoader.getNativeLibDir(reactContext)
|
|
115
|
+
|
|
116
|
+
val result = com.facebook.react.bridge.Arguments.createMap()
|
|
117
|
+
result.putBoolean("available", available)
|
|
118
|
+
result.putString("arch", arch)
|
|
119
|
+
result.putString("dexPath", dexPath?.absolutePath ?: "not downloaded")
|
|
120
|
+
result.putString("nativeLibPath", nativeLibDir?.absolutePath ?: "not downloaded")
|
|
121
|
+
result.putBoolean("dexExists", dexPath?.exists() == true)
|
|
122
|
+
result.putBoolean("nativeLibsExist", nativeLibDir?.exists() == true)
|
|
123
|
+
|
|
124
|
+
promise.resolve(result)
|
|
125
|
+
} catch (e: Exception) {
|
|
126
|
+
promise.reject("ERR_FLIR_SDK_STATUS", e)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
@ReactMethod
|
|
131
|
+
fun getDiscoveredDevices(promise: Promise) {
|
|
132
|
+
try {
|
|
133
|
+
val devices = FlirManager.getDiscoveredDevices()
|
|
134
|
+
val result = com.facebook.react.bridge.Arguments.createArray()
|
|
135
|
+
|
|
136
|
+
devices.forEach { identity ->
|
|
137
|
+
val deviceMap = com.facebook.react.bridge.Arguments.createMap()
|
|
138
|
+
deviceMap.putString("id", identity.deviceId)
|
|
139
|
+
deviceMap.putString("name", identity.deviceId)
|
|
140
|
+
deviceMap.putString("communicationType", identity.communicationInterface.name)
|
|
141
|
+
deviceMap.putBoolean("isEmulator", identity.communicationInterface.name == "EMULATOR")
|
|
142
|
+
result.pushMap(deviceMap)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
promise.resolve(result)
|
|
146
|
+
} catch (e: Exception) {
|
|
147
|
+
promise.reject("ERR_FLIR_DEVICES", e)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@ReactMethod
|
|
152
|
+
fun startEmulator(emulatorType: String, promise: Promise) {
|
|
153
|
+
try {
|
|
154
|
+
// Ensure SDK is initialized with context before starting discovery
|
|
155
|
+
FlirManager.init(reactContext)
|
|
156
|
+
// With simplified API, just start discovery - emulators are discovered like any device
|
|
157
|
+
FlirManager.startDiscovery(true)
|
|
158
|
+
promise.resolve(true)
|
|
159
|
+
} catch (e: Exception) {
|
|
160
|
+
promise.reject("ERR_FLIR_EMULATOR", e)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
@ReactMethod
|
|
165
|
+
fun connectToDevice(deviceId: String, promise: Promise) {
|
|
166
|
+
try {
|
|
167
|
+
// Ensure SDK is initialized with context before connecting
|
|
168
|
+
FlirManager.init(reactContext)
|
|
169
|
+
FlirManager.connectToDevice(deviceId)
|
|
170
|
+
promise.resolve(true)
|
|
171
|
+
} catch (e: Exception) {
|
|
172
|
+
promise.reject("ERR_FLIR_CONNECT", e)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
@ReactMethod
|
|
177
|
+
fun startDiscovery(promise: Promise) {
|
|
178
|
+
try {
|
|
179
|
+
// Ensure SDK is initialized with context before starting discovery
|
|
180
|
+
FlirManager.init(reactContext)
|
|
181
|
+
FlirManager.startDiscovery(true)
|
|
182
|
+
promise.resolve(true)
|
|
183
|
+
} catch (e: Exception) {
|
|
184
|
+
promise.reject("ERR_FLIR_DISCOVERY", e)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
@ReactMethod
|
|
189
|
+
fun stopDiscovery(promise: Promise) {
|
|
190
|
+
try {
|
|
191
|
+
FlirManager.stopDiscovery()
|
|
192
|
+
promise.resolve(true)
|
|
193
|
+
} catch (e: Exception) {
|
|
194
|
+
promise.reject("ERR_FLIR_STOP_DISCOVERY", e)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@ReactMethod
|
|
199
|
+
fun stopFlir(promise: Promise) {
|
|
200
|
+
try {
|
|
201
|
+
FlirManager.stop()
|
|
202
|
+
promise.resolve(true)
|
|
203
|
+
} catch (e: Exception) {
|
|
204
|
+
promise.reject("ERR_FLIR_STOP", e)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
@ReactMethod
|
|
209
|
+
fun initializeSDK(promise: Promise) {
|
|
210
|
+
try {
|
|
211
|
+
FlirManager.init(reactContext)
|
|
212
|
+
|
|
213
|
+
val result = com.facebook.react.bridge.Arguments.createMap()
|
|
214
|
+
result.putBoolean("initialized", true)
|
|
215
|
+
result.putString("message", "SDK initialized successfully")
|
|
216
|
+
promise.resolve(result)
|
|
217
|
+
} catch (e: Exception) {
|
|
218
|
+
val result = com.facebook.react.bridge.Arguments.createMap()
|
|
219
|
+
result.putBoolean("initialized", false)
|
|
220
|
+
result.putString("error", e.message ?: "Unknown error")
|
|
221
|
+
result.putString("errorType", e.javaClass.simpleName)
|
|
222
|
+
promise.resolve(result)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
@ReactMethod
|
|
227
|
+
fun getDebugInfo(promise: Promise) {
|
|
228
|
+
try {
|
|
229
|
+
val result = com.facebook.react.bridge.Arguments.createMap()
|
|
230
|
+
|
|
231
|
+
// SDK availability
|
|
232
|
+
result.putBoolean("sdkAvailable", FlirSDKLoader.isSDKAvailable(reactContext))
|
|
233
|
+
result.putString("arch", FlirSDKLoader.getDeviceArch())
|
|
234
|
+
|
|
235
|
+
// Check if FLIR SDK classes are loadable
|
|
236
|
+
val classesLoaded = try {
|
|
237
|
+
Class.forName("com.flir.thermalsdk.androidsdk.ThermalSdkAndroid")
|
|
238
|
+
Class.forName("com.flir.thermalsdk.live.discovery.DiscoveryFactory")
|
|
239
|
+
true
|
|
240
|
+
} catch (e: ClassNotFoundException) {
|
|
241
|
+
false
|
|
242
|
+
}
|
|
243
|
+
result.putBoolean("sdkClassesLoaded", classesLoaded)
|
|
244
|
+
|
|
245
|
+
// Discovery state
|
|
246
|
+
val devices = FlirManager.getDiscoveredDevices()
|
|
247
|
+
result.putInt("discoveredDeviceCount", devices.size)
|
|
248
|
+
result.putBoolean("isConnected", FlirManager.isConnected())
|
|
249
|
+
result.putBoolean("isStreaming", FlirManager.isStreaming())
|
|
250
|
+
result.putString("connectedDevice", FlirManager.getConnectedDeviceInfo())
|
|
251
|
+
|
|
252
|
+
promise.resolve(result)
|
|
253
|
+
} catch (e: Exception) {
|
|
254
|
+
promise.reject("ERR_DEBUG_INFO", e)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
74
257
|
}
|
|
@@ -3,67 +3,18 @@ package flir.android
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.os.Build
|
|
5
5
|
import android.util.Log
|
|
6
|
-
import kotlinx.coroutines.*
|
|
7
|
-
import org.json.JSONObject
|
|
8
|
-
import java.io.File
|
|
9
|
-
import java.io.FileOutputStream
|
|
10
|
-
import java.net.HttpURLConnection
|
|
11
|
-
import java.net.URL
|
|
12
|
-
import java.util.zip.ZipInputStream
|
|
13
6
|
|
|
14
7
|
/**
|
|
15
|
-
* FlirSDKLoader -
|
|
8
|
+
* FlirSDKLoader - SDK availability checker
|
|
16
9
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* URLs are read from sdk-manifest.json in assets folder
|
|
10
|
+
* Since the SDK is now bundled via AAR files, this class simply reports
|
|
11
|
+
* that the SDK is always available. The AAR files include native .so libraries
|
|
12
|
+
* for all supported architectures, which Android handles automatically.
|
|
22
13
|
*/
|
|
23
14
|
object FlirSDKLoader {
|
|
24
15
|
|
|
25
16
|
private const val TAG = "FlirSDKLoader"
|
|
26
17
|
|
|
27
|
-
// Cached manifest data
|
|
28
|
-
private var cachedManifest: SDKManifest? = null
|
|
29
|
-
|
|
30
|
-
// Manifest data classes
|
|
31
|
-
data class ArchPackage(val downloadUrl: String, val sizeBytes: Long)
|
|
32
|
-
data class SDKManifest(val version: String, val packages: Map<String, ArchPackage>)
|
|
33
|
-
|
|
34
|
-
private fun getSDKDirectory(context: Context) = File(context.filesDir, "FlirSDK")
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Load manifest from assets
|
|
38
|
-
*/
|
|
39
|
-
private fun loadManifest(context: Context): SDKManifest? {
|
|
40
|
-
if (cachedManifest != null) return cachedManifest
|
|
41
|
-
|
|
42
|
-
return try {
|
|
43
|
-
val json = context.assets.open("sdk-manifest.json").bufferedReader().readText()
|
|
44
|
-
val root = JSONObject(json)
|
|
45
|
-
val android = root.getJSONObject("android")
|
|
46
|
-
val packagesJson = android.getJSONObject("packages")
|
|
47
|
-
|
|
48
|
-
val packages = mutableMapOf<String, ArchPackage>()
|
|
49
|
-
packagesJson.keys().forEach { arch ->
|
|
50
|
-
val pkg = packagesJson.getJSONObject(arch)
|
|
51
|
-
packages[arch] = ArchPackage(
|
|
52
|
-
downloadUrl = pkg.getString("downloadUrl"),
|
|
53
|
-
sizeBytes = pkg.getLong("sizeBytes")
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
SDKManifest(
|
|
58
|
-
version = root.getString("version"),
|
|
59
|
-
packages = packages
|
|
60
|
-
).also { cachedManifest = it }
|
|
61
|
-
} catch (e: Exception) {
|
|
62
|
-
Log.e(TAG, "Failed to load manifest: ${e.message}")
|
|
63
|
-
null
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
18
|
/**
|
|
68
19
|
* Get the primary ABI for this device
|
|
69
20
|
*/
|
|
@@ -71,7 +22,7 @@ object FlirSDKLoader {
|
|
|
71
22
|
val supportedAbis = Build.SUPPORTED_ABIS
|
|
72
23
|
Log.d(TAG, "Device supported ABIs: ${supportedAbis.joinToString()}")
|
|
73
24
|
|
|
74
|
-
val knownArchs = setOf("arm64-v8a", "armeabi-v7a", "x86_64")
|
|
25
|
+
val knownArchs = setOf("arm64-v8a", "armeabi-v7a", "x86_64", "x86")
|
|
75
26
|
for (abi in supportedAbis) {
|
|
76
27
|
if (abi in knownArchs) {
|
|
77
28
|
Log.d(TAG, "Selected ABI: $abi")
|
|
@@ -82,162 +33,52 @@ object FlirSDKLoader {
|
|
|
82
33
|
}
|
|
83
34
|
|
|
84
35
|
/**
|
|
85
|
-
* Check if SDK is
|
|
36
|
+
* Check if SDK is available - always true since bundled in AAR
|
|
86
37
|
*/
|
|
87
38
|
fun isSDKAvailable(context: Context): Boolean {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
val dexFile = File(sdkDir, "classes.dex")
|
|
92
|
-
val soDir = File(sdkDir, "jni/$arch")
|
|
93
|
-
|
|
94
|
-
val hasDex = dexFile.exists() && dexFile.length() > 0
|
|
95
|
-
val hasSo = soDir.exists() && soDir.listFiles()?.isNotEmpty() == true
|
|
96
|
-
|
|
97
|
-
Log.d(TAG, "SDK available: dex=$hasDex, so=$hasSo")
|
|
98
|
-
return hasDex && hasSo
|
|
39
|
+
Log.d(TAG, "SDK is bundled in AAR - always available")
|
|
40
|
+
return true
|
|
99
41
|
}
|
|
100
42
|
|
|
101
43
|
/**
|
|
102
|
-
*
|
|
44
|
+
* No download needed - SDK is bundled
|
|
103
45
|
*/
|
|
104
|
-
fun
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
*/
|
|
112
|
-
fun getNativeLibDir(context: Context): File? {
|
|
113
|
-
val arch = getDeviceArch()
|
|
114
|
-
val libDir = File(getSDKDirectory(context), "jni/$arch")
|
|
115
|
-
return if (libDir.exists()) libDir else null
|
|
46
|
+
suspend fun downloadSDK(
|
|
47
|
+
context: Context,
|
|
48
|
+
onProgress: ((progress: Int, bytesDownloaded: Long, totalBytes: Long) -> Unit)? = null
|
|
49
|
+
): Boolean {
|
|
50
|
+
Log.d(TAG, "SDK is bundled - no download needed")
|
|
51
|
+
onProgress?.invoke(100, 0, 0)
|
|
52
|
+
return true
|
|
116
53
|
}
|
|
117
54
|
|
|
118
55
|
/**
|
|
119
|
-
* Get
|
|
56
|
+
* Get SDK status
|
|
120
57
|
*/
|
|
121
|
-
fun
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
58
|
+
fun getSDKStatus(context: Context): Map<String, Any> {
|
|
59
|
+
return mapOf(
|
|
60
|
+
"available" to true,
|
|
61
|
+
"bundled" to true,
|
|
62
|
+
"arch" to getDeviceArch(),
|
|
63
|
+
"version" to "4.16.0" // SDK version from AAR
|
|
64
|
+
)
|
|
125
65
|
}
|
|
126
66
|
|
|
127
67
|
/**
|
|
128
|
-
*
|
|
68
|
+
* Get DEX path - not applicable when bundled via AAR
|
|
69
|
+
* Returns null since SDK is bundled in AAR, no separate DEX needed
|
|
129
70
|
*/
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
): Result<Unit> = withContext(Dispatchers.IO) {
|
|
134
|
-
try {
|
|
135
|
-
val arch = getDeviceArch()
|
|
136
|
-
val manifest = loadManifest(context)
|
|
137
|
-
?: return@withContext Result.failure(Exception("Failed to load manifest"))
|
|
138
|
-
|
|
139
|
-
val archPackage = manifest.packages[arch]
|
|
140
|
-
?: return@withContext Result.failure(Exception("No package for architecture: $arch"))
|
|
141
|
-
|
|
142
|
-
val downloadUrl = archPackage.downloadUrl
|
|
143
|
-
Log.i(TAG, "Downloading SDK for $arch from $downloadUrl")
|
|
144
|
-
|
|
145
|
-
val sdkDir = getSDKDirectory(context).apply { mkdirs() }
|
|
146
|
-
val zipFile = File(context.cacheDir, "flir-sdk-$arch.zip")
|
|
147
|
-
|
|
148
|
-
// Download with redirect handling (GitHub uses 302 redirects)
|
|
149
|
-
var connection = URL(downloadUrl).openConnection() as HttpURLConnection
|
|
150
|
-
connection.instanceFollowRedirects = true
|
|
151
|
-
connection.connectTimeout = 30000
|
|
152
|
-
connection.readTimeout = 60000
|
|
153
|
-
|
|
154
|
-
// Handle redirects manually for cross-protocol redirects
|
|
155
|
-
var redirectCount = 0
|
|
156
|
-
while (connection.responseCode in 301..302 && redirectCount < 5) {
|
|
157
|
-
val redirectUrl = connection.getHeaderField("Location")
|
|
158
|
-
Log.d(TAG, "Redirect to: $redirectUrl")
|
|
159
|
-
connection.disconnect()
|
|
160
|
-
connection = URL(redirectUrl).openConnection() as HttpURLConnection
|
|
161
|
-
connection.instanceFollowRedirects = true
|
|
162
|
-
redirectCount++
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
if (connection.responseCode != HttpURLConnection.HTTP_OK) {
|
|
166
|
-
return@withContext Result.failure(Exception("HTTP error ${connection.responseCode}"))
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
val totalSize = connection.contentLengthLong.let {
|
|
170
|
-
if (it > 0) it else archPackage.sizeBytes
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
connection.inputStream.use { input ->
|
|
174
|
-
FileOutputStream(zipFile).use { output ->
|
|
175
|
-
val buffer = ByteArray(8192)
|
|
176
|
-
var totalRead = 0L
|
|
177
|
-
var bytesRead: Int
|
|
178
|
-
|
|
179
|
-
while (input.read(buffer).also { bytesRead = it } != -1) {
|
|
180
|
-
output.write(buffer, 0, bytesRead)
|
|
181
|
-
totalRead += bytesRead
|
|
182
|
-
withContext(Dispatchers.Main) {
|
|
183
|
-
onProgress(totalRead, totalSize)
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
connection.disconnect()
|
|
189
|
-
|
|
190
|
-
Log.i(TAG, "Download complete: ${zipFile.length()} bytes")
|
|
191
|
-
|
|
192
|
-
// Extract
|
|
193
|
-
unzip(zipFile, sdkDir)
|
|
194
|
-
zipFile.delete()
|
|
195
|
-
|
|
196
|
-
// Verify
|
|
197
|
-
val dexFile = File(sdkDir, "classes.dex")
|
|
198
|
-
val soDir = File(sdkDir, "jni/$arch")
|
|
199
|
-
|
|
200
|
-
if (!dexFile.exists()) {
|
|
201
|
-
return@withContext Result.failure(Exception("DEX file not extracted"))
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
dexFile.setReadOnly()
|
|
205
|
-
Log.i(TAG, "SDK installed: dex=${dexFile.length()} bytes, libs=${soDir.listFiles()?.size ?: 0} files")
|
|
206
|
-
|
|
207
|
-
Result.success(Unit)
|
|
208
|
-
} catch (e: Exception) {
|
|
209
|
-
Log.e(TAG, "Download failed", e)
|
|
210
|
-
Result.failure(e)
|
|
211
|
-
}
|
|
71
|
+
fun getDexPath(context: Context): java.io.File? {
|
|
72
|
+
// SDK is bundled in AAR - no separate DEX file
|
|
73
|
+
return null
|
|
212
74
|
}
|
|
213
75
|
|
|
214
76
|
/**
|
|
215
|
-
*
|
|
77
|
+
* Get native library directory - not applicable when bundled via AAR
|
|
78
|
+
* Returns null since native libs are included in AAR and handled by Android
|
|
216
79
|
*/
|
|
217
|
-
fun
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
private fun unzip(source: File, destination: File) {
|
|
222
|
-
Log.d(TAG, "Extracting ${source.name} to ${destination.absolutePath}")
|
|
223
|
-
|
|
224
|
-
ZipInputStream(source.inputStream()).use { zip ->
|
|
225
|
-
var entry = zip.nextEntry
|
|
226
|
-
while (entry != null) {
|
|
227
|
-
val file = File(destination, entry.name)
|
|
228
|
-
Log.d(TAG, " Extracting: ${entry.name}")
|
|
229
|
-
|
|
230
|
-
if (entry.isDirectory) {
|
|
231
|
-
file.mkdirs()
|
|
232
|
-
} else {
|
|
233
|
-
file.parentFile?.mkdirs()
|
|
234
|
-
file.outputStream().use { output ->
|
|
235
|
-
zip.copyTo(output)
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
zip.closeEntry()
|
|
239
|
-
entry = zip.nextEntry
|
|
240
|
-
}
|
|
241
|
-
}
|
|
80
|
+
fun getNativeLibDir(context: Context): java.io.File? {
|
|
81
|
+
// SDK native libs are bundled in AAR and extracted automatically by Android
|
|
82
|
+
return null
|
|
242
83
|
}
|
|
243
|
-
}
|
|
84
|
+
}
|