package com.xuqm.sdk.update import android.content.Context import android.content.Intent import android.net.Uri import androidx.core.content.FileProvider import com.xuqm.sdk.XuqmSDK import com.xuqm.sdk.core.ServiceEndpointRegistry import com.xuqm.sdk.network.ApiClient import com.xuqm.sdk.update.api.UpdateApi import com.xuqm.sdk.update.model.RnUpdateInfo import com.xuqm.sdk.update.model.UpdateInfo import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.File import java.net.URL object UpdateSDK { private val api: UpdateApi get() = ApiClient.create(UpdateApi::class.java, ServiceEndpointRegistry.updateBaseUrl) private fun normalizeDownloadUrl(rawUrl: String?): String? { if (rawUrl.isNullOrBlank()) return rawUrl if (rawUrl.contains("/api/v1/updates/api/v1/rn/files/")) { return rawUrl.replace("/api/v1/updates/api/v1/rn/files/", "/api/v1/rn/files/") } return runCatching { val uri = Uri.parse(rawUrl) if (uri.path?.startsWith("/files/apk/") == true) { "${uri.scheme}://${uri.authority}/api/v1/updates${uri.path}" } else { rawUrl } }.getOrDefault(rawUrl) } suspend fun checkAppUpdate(context: Context): UpdateInfo? = withContext(Dispatchers.IO) { XuqmSDK.requireInit() val versionCode = context.packageManager .getPackageInfo(context.packageName, 0).longVersionCode.toInt() runCatching { api.checkUpdate(XuqmSDK.appId, "ANDROID", versionCode).data?.let { it.copy(downloadUrl = normalizeDownloadUrl(it.downloadUrl) ?: it.downloadUrl) } }.getOrNull() } suspend fun downloadAndInstall( context: Context, downloadUrl: String, onProgress: (Int) -> Unit = {}, ) = withContext(Dispatchers.IO) { val apkFile = File(context.getExternalFilesDir(null), "update.apk") val url = URL(downloadUrl) val connection = url.openConnection() connection.connect() val totalSize = connection.contentLengthLong connection.getInputStream().use { input -> apkFile.outputStream().use { output -> val buffer = ByteArray(8192) var downloaded = 0L var read: Int while (input.read(buffer).also { read = it } != -1) { output.write(buffer, 0, read) downloaded += read if (totalSize > 0) onProgress((downloaded * 100 / totalSize).toInt()) } } } withContext(Dispatchers.Main) { installApk(context, apkFile) } } private fun installApk(context: Context, apkFile: File) { val intent = Intent(Intent.ACTION_VIEW).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION val uri = FileProvider.getUriForFile(context, "${context.packageName}.fileprovider", apkFile) setDataAndType(uri, "application/vnd.android.package-archive") } context.startActivity(intent) } suspend fun checkRnUpdate(moduleId: String, currentVersion: String): RnUpdateInfo? = withContext(Dispatchers.IO) { XuqmSDK.requireInit() runCatching { api.checkRnUpdate(XuqmSDK.appId, moduleId, "ANDROID", currentVersion).data?.let { it.copy(downloadUrl = normalizeDownloadUrl(it.downloadUrl) ?: it.downloadUrl) } }.getOrNull() } }