|
@@ -0,0 +1,542 @@
|
|
|
+package com.sikey.interconnect.component.network.http
|
|
|
+/*
|
|
|
+
|
|
|
+import android.app.AlertDialog
|
|
|
+import android.app.DownloadManager
|
|
|
+import android.content.BroadcastReceiver
|
|
|
+import android.content.Context
|
|
|
+import android.content.Intent
|
|
|
+import android.content.IntentFilter
|
|
|
+import android.net.Uri
|
|
|
+import android.os.Build
|
|
|
+import android.os.Environment
|
|
|
+import android.provider.Settings
|
|
|
+import androidx.activity.ComponentActivity
|
|
|
+import androidx.core.content.FileProvider
|
|
|
+import com.sikey.interconnect.component.log.Logger
|
|
|
+import kotlinx.coroutines.CoroutineScope
|
|
|
+import kotlinx.coroutines.Dispatchers
|
|
|
+import kotlinx.coroutines.launch
|
|
|
+import kotlinx.coroutines.withContext
|
|
|
+import java.io.File
|
|
|
+
|
|
|
+object UpdateManager {
|
|
|
+ private val TAG = UpdateManager::class.java.simpleName
|
|
|
+ private var downloadId: Long = -1L
|
|
|
+ private lateinit var downloadManager: DownloadManager
|
|
|
+ private var isReceiverRegistered = false
|
|
|
+
|
|
|
+ // 初始化 DownloadManager 和广播接收器
|
|
|
+ fun initialize(context: Context) {
|
|
|
+ downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
|
|
+ if (!isReceiverRegistered) {
|
|
|
+ registerDownloadReceiver(context)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查更新
|
|
|
+*/
|
|
|
+/* fun checkForUpdate(context: Context, onUpdateAvailable: (UpdateInfo) -> Unit) {
|
|
|
+ CoroutineScope(Dispatchers.IO).launch {
|
|
|
+ try {
|
|
|
+ val response = RetrofitClient.apiService.getUpdateInfo()
|
|
|
+ if (response.isSuccessful) {
|
|
|
+ val updateInfo = response.body()
|
|
|
+ updateInfo?.let {
|
|
|
+ val currentVersion = getCurrentVersionCode(context)
|
|
|
+ if (it.versionCode > currentVersion) {
|
|
|
+ withContext(Dispatchers.Main) {
|
|
|
+ onUpdateAvailable(it)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (e: Exception) {
|
|
|
+ withContext(Dispatchers.Main) {
|
|
|
+ showToast(context, "检查更新失败: ${e.message}")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }*//*
|
|
|
+
|
|
|
+
|
|
|
+ // 显示更新对话框
|
|
|
+*/
|
|
|
+/* fun showUpdateDialog(context: Context, updateInfo: UpdateInfo) {
|
|
|
+ val builder = AlertDialog.Builder(context)
|
|
|
+ builder.setTitle("发现新版本 ${updateInfo.versionName}")
|
|
|
+ builder.setMessage(updateInfo.updateLog)
|
|
|
+ builder.setPositiveButton("立即更新") { _, _ ->
|
|
|
+ downloadAndInstall(context, updateInfo.downloadUrl)
|
|
|
+ }
|
|
|
+ if (!updateInfo.forceUpdate) {
|
|
|
+ builder.setNegativeButton("稍后") { dialog, _ -> dialog.dismiss() }
|
|
|
+ }
|
|
|
+ val dialog = builder.create()
|
|
|
+ dialog.setCancelable(!updateInfo.forceUpdate)
|
|
|
+ dialog.show()
|
|
|
+ }*//*
|
|
|
+
|
|
|
+
|
|
|
+ // 获取当前版本号
|
|
|
+ private fun getCurrentVersionCode(context: Context): Int {
|
|
|
+ return try {
|
|
|
+ context.packageManager.getPackageInfo(context.packageName, 0).versionCode
|
|
|
+ } catch (e: Exception) {
|
|
|
+ 0
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 下载 APK
|
|
|
+ fun downloadAndInstall(context: Context, downloadUrl: String) {
|
|
|
+ Logger.d(TAG, "downloadAndInstall")
|
|
|
+ val request = DownloadManager.Request(Uri.parse(downloadUrl)).apply {
|
|
|
+ Logger.d(TAG, "downloadAndInstall 2")
|
|
|
+ setTitle("正在下载更新")
|
|
|
+ setDescription("下载 ${context.packageName} 新版本")
|
|
|
+ setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
+ setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, "update.apk")
|
|
|
+ } else {
|
|
|
+ setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "update.apk")
|
|
|
+ }
|
|
|
+ setMimeType("application/vnd.android.package-archive")
|
|
|
+ setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
|
|
|
+ Logger.d(TAG, "downloadAndInstall 3")
|
|
|
+ }
|
|
|
+ downloadId = downloadManager.enqueue(request)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 安装 APK
|
|
|
+ fun installApk(context: Context, apkFile: File) {
|
|
|
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
|
+ if (!context.packageManager.canRequestPackageInstalls()) {
|
|
|
+ val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES)
|
|
|
+ .setData(Uri.parse("package:${context.packageName}"))
|
|
|
+ if (context is ComponentActivity) {
|
|
|
+ context.startActivity(intent)
|
|
|
+ }
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
|
+ FileProvider.getUriForFile(context, "com.sikey.interconnect.download", apkFile)
|
|
|
+ } else {
|
|
|
+ Uri.fromFile(apkFile)
|
|
|
+ }
|
|
|
+
|
|
|
+ val intent = Intent(Intent.ACTION_VIEW)
|
|
|
+ .setDataAndType(uri, "application/vnd.android.package-archive")
|
|
|
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
|
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
|
+ context.startActivity(intent)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 注册下载完成广播
|
|
|
+ private fun registerDownloadReceiver(context: Context) {
|
|
|
+ val filter = IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
|
|
|
+ context.registerReceiver(object : BroadcastReceiver() {
|
|
|
+ override fun onReceive(context: Context, intent: Intent) {
|
|
|
+ val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
|
|
|
+ if (id == downloadId) {
|
|
|
+ val query = DownloadManager.Query().setFilterById(id)
|
|
|
+ downloadManager.query(query).use { cursor ->
|
|
|
+ if (cursor.moveToFirst()) {
|
|
|
+ val status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS))
|
|
|
+ if (status == DownloadManager.STATUS_SUCCESSFUL) {
|
|
|
+ val uriString = cursor.getString(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_LOCAL_URI))
|
|
|
+ val fileUri = Uri.parse(uriString)
|
|
|
+ val file = File(fileUri.path ?: return)
|
|
|
+ installApk(context, file)
|
|
|
+ } else {
|
|
|
+ showToast(context, "下载失败")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }, filter, Context.RECEIVER_NOT_EXPORTED)
|
|
|
+ isReceiverRegistered = true
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun showToast(context: Context, message: String) {
|
|
|
+ android.widget.Toast.makeText(context, message, android.widget.Toast.LENGTH_SHORT).show()
|
|
|
+ }
|
|
|
+}*/
|
|
|
+
|
|
|
+import android.Manifest
|
|
|
+import android.app.DownloadManager
|
|
|
+import android.content.BroadcastReceiver
|
|
|
+import android.content.Context
|
|
|
+import android.content.Intent
|
|
|
+import android.content.IntentFilter
|
|
|
+import android.content.pm.PackageManager
|
|
|
+import android.net.Uri
|
|
|
+import android.os.Build
|
|
|
+import android.os.Environment
|
|
|
+import androidx.core.app.ActivityCompat
|
|
|
+import androidx.core.app.NotificationCompat
|
|
|
+import androidx.core.app.NotificationManagerCompat
|
|
|
+import androidx.core.content.FileProvider
|
|
|
+import androidx.lifecycle.lifecycleScope
|
|
|
+import com.sikey.interconnect.R
|
|
|
+import com.sikey.interconnect.component.log.Logger
|
|
|
+import com.sikey.interconnect.component.network.http.model.UpdateAppRepo
|
|
|
+import com.sikey.interconnect.constant.UrlConstants.SK_TOOL_URL
|
|
|
+import com.sikey.interconnect.ui.avtivity.base.BaseNoActionBarActivity
|
|
|
+import com.sikey.interconnect.utils.ToastUtils
|
|
|
+import com.sikey.interconnect.utils.VersionUtils
|
|
|
+import kotlinx.coroutines.launch
|
|
|
+import retrofit2.Call
|
|
|
+import retrofit2.Callback
|
|
|
+import retrofit2.Response
|
|
|
+import retrofit2.Retrofit
|
|
|
+import retrofit2.converter.gson.GsonConverterFactory
|
|
|
+import java.io.File
|
|
|
+import java.io.FileInputStream
|
|
|
+import java.math.BigInteger
|
|
|
+import java.security.MessageDigest
|
|
|
+
|
|
|
+class UpdateManager {
|
|
|
+ private var mContext: Context? = null
|
|
|
+ private val notificationId = 1001
|
|
|
+ private var downloadInfo = DownloadInfo(-1L, "", "", 0)
|
|
|
+ private lateinit var downloadManager: DownloadManager
|
|
|
+ private var isReceiverRegistered = false
|
|
|
+ private var updateAppService: UpdateAppService? = null
|
|
|
+
|
|
|
+ fun initialize(context: Context) {
|
|
|
+ mContext = context
|
|
|
+ downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
|
|
+ if (!isReceiverRegistered) {
|
|
|
+ Logger.d(TAG, "======== registerDownloadReceiver")
|
|
|
+ registerDownloadReceiver(context)
|
|
|
+ }
|
|
|
+ val retrofit = Retrofit.Builder()
|
|
|
+ .baseUrl(SK_TOOL_URL)
|
|
|
+ .addConverterFactory(GsonConverterFactory.create())
|
|
|
+ .build()
|
|
|
+ updateAppService = retrofit.create(UpdateAppService::class.java)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Check for new version
|
|
|
+/* suspend fun checkForUpdate(currentVersion: String, updateUrl: String): UpdateInfo? {
|
|
|
+ return try {
|
|
|
+ withContext(Dispatchers.IO) {
|
|
|
+ val latestVersionInfo = URL(updateUrl).readText()
|
|
|
+ // Assuming updateUrl returns JSON with version and apkUrl
|
|
|
+ // Parse JSON to get version and download URL (simplified example)
|
|
|
+ val latestVersion = parseVersionFromJson(latestVersionInfo)
|
|
|
+ val apkUrl = parseApkUrlFromJson(latestVersionInfo)
|
|
|
+
|
|
|
+ if (isNewerVersion(currentVersion, latestVersion)) {
|
|
|
+ UpdateInfo(latestVersion, apkUrl)
|
|
|
+ } else {
|
|
|
+ null
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (e: Exception) {
|
|
|
+ null
|
|
|
+ }
|
|
|
+ }*/
|
|
|
+
|
|
|
+ // Start APK download and show notification
|
|
|
+ fun startDownload(updateInfo: UpdateInfo) {
|
|
|
+ val request = DownloadManager.Request(Uri.parse(updateInfo.apkUrl)).apply {
|
|
|
+ setTitle("${mContext?.packageName}")
|
|
|
+ setDescription("Downloading new version...")
|
|
|
+ //if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
|
|
+ setDestinationInExternalFilesDir(mContext, Environment.DIRECTORY_DOWNLOADS, DOWNLOADED_APK_FILENAME)
|
|
|
+/* } else {
|
|
|
+ setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "app-update.apk")
|
|
|
+ }*/
|
|
|
+ setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
|
|
|
+ }
|
|
|
+
|
|
|
+ downloadInfo.downloadId = downloadManager.enqueue(request)
|
|
|
+ downloadInfo.apkUrl = updateInfo.apkUrl
|
|
|
+ downloadInfo.md5 = updateInfo.md5
|
|
|
+ showDownloadNotification(0)
|
|
|
+ }
|
|
|
+
|
|
|
+ fun handleManualUpdate(updateInfo: UpdateInfo) {
|
|
|
+ val file = File(mContext?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), DOWNLOADED_APK_FILENAME)
|
|
|
+ var md5 = ""
|
|
|
+ if (file.exists()) {
|
|
|
+ md5 = calculateMD5(file)
|
|
|
+ Logger.d(TAG, "======== handleManualUpdate updateInfo.md5:${updateInfo.md5}, md5:$md5")
|
|
|
+ }
|
|
|
+ if (file.exists() && updateInfo.md5.equals(md5, ignoreCase = true)) {
|
|
|
+ Logger.d(TAG, "====== app-update.apk already exists")
|
|
|
+ showInstallDialog()
|
|
|
+ } else {
|
|
|
+ Logger.d(TAG, "====== no app-update.apk, start downloading")
|
|
|
+ //showInstallDialog()
|
|
|
+ startDownload(updateInfo)
|
|
|
+ ToastUtils.showLongToast(R.string.update_app_start_downloading)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Notification with progress bar
|
|
|
+ private fun showDownloadNotification(progress: Int) {
|
|
|
+ val builder = mContext?.let {
|
|
|
+ NotificationCompat.Builder(it, "update_channel").apply {
|
|
|
+ setSmallIcon(android.R.drawable.stat_sys_download)
|
|
|
+ setContentTitle(it.packageName)
|
|
|
+ setContentText("Downloading...")
|
|
|
+ setPriority(NotificationCompat.PRIORITY_LOW)
|
|
|
+ setProgress(100, progress, false)
|
|
|
+ setOngoing(true)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mContext?.let {
|
|
|
+ ActivityCompat.checkSelfPermission(
|
|
|
+ it,
|
|
|
+ Manifest.permission.POST_NOTIFICATIONS
|
|
|
+ )
|
|
|
+ } != PackageManager.PERMISSION_GRANTED
|
|
|
+ ) {
|
|
|
+ // TODO: Consider calling
|
|
|
+ // ActivityCompat#requestPermissions
|
|
|
+ // here to request the missing permissions, and then overriding
|
|
|
+ // public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
|
+ // int[] grantResults)
|
|
|
+ // to handle the case where the user grants the permission. See the documentation
|
|
|
+ // for ActivityCompat#requestPermissions for more details.
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (builder != null) {
|
|
|
+ NotificationManagerCompat.from(mContext!!).notify(notificationId, builder.build())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Register receiver to monitor download progress and completion
|
|
|
+ private fun registerDownloadReceiver(context: Context) {
|
|
|
+ val receiver = object : BroadcastReceiver() {
|
|
|
+ override fun onReceive(context: Context, intent: Intent) {
|
|
|
+ val id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1)
|
|
|
+ if (id != downloadInfo.downloadId) return
|
|
|
+
|
|
|
+ val query = DownloadManager.Query().setFilterById(downloadInfo.downloadId)
|
|
|
+ downloadManager.query(query).use { cursor ->
|
|
|
+ if (cursor.moveToFirst()) {
|
|
|
+ val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
|
|
|
+ when (status) {
|
|
|
+ DownloadManager.STATUS_SUCCESSFUL -> {
|
|
|
+ Logger.d(TAG, "======== DownloadManager.STATUS_SUCCESSFUL")
|
|
|
+ NotificationManagerCompat.from(context).cancel(notificationId)
|
|
|
+ val apkFile = File(mContext?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), DOWNLOADED_APK_FILENAME)
|
|
|
+ val md5 = calculateMD5(apkFile)
|
|
|
+ Logger.d(TAG, "======== DownloadManager downloadInfo.md5:${downloadInfo.md5}, md5:$md5")
|
|
|
+ if (apkFile.exists() && downloadInfo.md5.equals(md5, ignoreCase = true)) {
|
|
|
+ Logger.d(TAG, "======== DownloadManager showInstallDialog")
|
|
|
+ showInstallDialog()
|
|
|
+ } else {
|
|
|
+ if (apkFile.exists()) {
|
|
|
+ apkFile.delete()
|
|
|
+ }
|
|
|
+ if (downloadInfo.retry < 2) {
|
|
|
+ Logger.d(TAG, "======== DownloadManager downloadInfo.retry:${downloadInfo.retry}")
|
|
|
+ downloadInfo.retry ++
|
|
|
+ startDownload(UpdateInfo("", "", downloadInfo.apkUrl, downloadInfo.md5))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ DownloadManager.STATUS_RUNNING -> {
|
|
|
+ Logger.d(TAG, "======== DownloadManager.STATUS_RUNNING")
|
|
|
+ val bytesDownloaded = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
|
|
|
+ val bytesTotal = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
|
|
|
+ if (bytesTotal > 0) {
|
|
|
+ val progress = (bytesDownloaded * 100 / bytesTotal)
|
|
|
+ showDownloadNotification(progress)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ DownloadManager.STATUS_FAILED -> {
|
|
|
+ Logger.d(TAG, "======== DownloadManager.STATUS_FAILED")
|
|
|
+ NotificationManagerCompat.from(context).cancel(notificationId)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ context.registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
|
|
|
+ Context.RECEIVER_NOT_EXPORTED)
|
|
|
+ context.registerReceiver(receiver, IntentFilter(DownloadManager.ACTION_NOTIFICATION_CLICKED),
|
|
|
+ Context.RECEIVER_NOT_EXPORTED)
|
|
|
+ isReceiverRegistered = true
|
|
|
+ }
|
|
|
+
|
|
|
+ // Show install dialog
|
|
|
+/* private fun showInstallDialog() {
|
|
|
+ val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "app-update.apk")
|
|
|
+ val uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
|
+ mContext?.let { FileProvider.getUriForFile(it, "com.sikey.interconnect.download", file) }
|
|
|
+ } else {
|
|
|
+ Uri.fromFile(file)
|
|
|
+ }
|
|
|
+
|
|
|
+ val intent = Intent(Intent.ACTION_VIEW).apply {
|
|
|
+ setDataAndType(uri, "application/vnd.android.package-archive")
|
|
|
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
|
|
+ }
|
|
|
+
|
|
|
+ val pendingIntent = PendingIntent.getActivity(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE)
|
|
|
+ val builder = mContext?.let {
|
|
|
+ NotificationCompat.Builder(it, "update_channel").apply {
|
|
|
+ setSmallIcon(android.R.drawable.stat_sys_download_done)
|
|
|
+ setContentTitle("Update Downloaded")
|
|
|
+ setContentText("Tap to install the new version")
|
|
|
+ setPriority(NotificationCompat.PRIORITY_HIGH)
|
|
|
+ setContentIntent(pendingIntent)
|
|
|
+ setAutoCancel(true)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (mContext?.let {
|
|
|
+ ActivityCompat.checkSelfPermission(
|
|
|
+ it,
|
|
|
+ Manifest.permission.POST_NOTIFICATIONS
|
|
|
+ )
|
|
|
+ } != PackageManager.PERMISSION_GRANTED
|
|
|
+ ) {
|
|
|
+ // TODO: Consider calling
|
|
|
+ // ActivityCompat#requestPermissions
|
|
|
+ // here to request the missing permissions, and then overriding
|
|
|
+ // public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
|
|
+ // int[] grantResults)
|
|
|
+ // to handle the case where the user grants the permission. See the documentation
|
|
|
+ // for ActivityCompat#requestPermissions for more details.
|
|
|
+ return
|
|
|
+ }
|
|
|
+ mContext?.let {
|
|
|
+ if (builder != null) {
|
|
|
+ NotificationManagerCompat.from(it).notify(notificationId, builder.build())
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } */
|
|
|
+
|
|
|
+ fun showInstallDialog() {
|
|
|
+ Logger.d(TAG, "installApk")
|
|
|
+ try {
|
|
|
+ val apkFile = File(mContext?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "app-update.apk")
|
|
|
+ if (!apkFile.exists()) {
|
|
|
+ Logger.e(TAG, "File not exist")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ val apkUri: Uri? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
|
|
+ // Android 7.0+ 使用 FileProvider
|
|
|
+ mContext?.let {
|
|
|
+ FileProvider.getUriForFile(
|
|
|
+ it,
|
|
|
+ "com.sikey.interconnect.download",
|
|
|
+ apkFile
|
|
|
+ )
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ Uri.fromFile(apkFile)
|
|
|
+ }
|
|
|
+
|
|
|
+ val intent = Intent(Intent.ACTION_VIEW).apply {
|
|
|
+ setDataAndType(apkUri, "application/vnd.android.package-archive")
|
|
|
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
|
+ addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
|
+ }
|
|
|
+ mContext?.startActivity(intent)
|
|
|
+ } catch (e: Exception) {
|
|
|
+ e.printStackTrace()
|
|
|
+ Logger.e(TAG, "exception $e")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Helper functions (implement these based on your needs)
|
|
|
+ private fun parseVersionFromJson(json: String): String {
|
|
|
+ // Parse JSON to extract version (e.g., "1.2.3")
|
|
|
+ return "1.2.3" // Placeholder
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun parseApkUrlFromJson(json: String): String {
|
|
|
+ // Parse JSON to extract APK URL
|
|
|
+ return "https://example.com/app-update.apk" // Placeholder
|
|
|
+ }
|
|
|
+
|
|
|
+ private fun isNewerVersion(currentVersion: String, latestVersion: String): Boolean {
|
|
|
+ // Compare versions (e.g., "1.2.3" vs "1.2.4")
|
|
|
+ val currentParts = currentVersion.split(".").map { it.toInt() }
|
|
|
+ val latestParts = latestVersion.split(".").map { it.toInt() }
|
|
|
+ for (i in currentParts.indices) {
|
|
|
+ if (latestParts[i] > currentParts[i]) return true
|
|
|
+ if (latestParts[i] < currentParts[i]) return false
|
|
|
+ }
|
|
|
+ return false
|
|
|
+ }
|
|
|
+
|
|
|
+ fun calculateMD5(file: File): String {
|
|
|
+ val md = MessageDigest.getInstance("MD5")
|
|
|
+
|
|
|
+ FileInputStream(file).use { fis ->
|
|
|
+ val buffer = ByteArray(8192)
|
|
|
+ var bytesRead: Int
|
|
|
+
|
|
|
+ while (fis.read(buffer).also { bytesRead = it } != -1) {
|
|
|
+ md.update(buffer, 0, bytesRead)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ val md5Bytes = md.digest()
|
|
|
+ val bigInt = BigInteger(1, md5Bytes)
|
|
|
+ return bigInt.toString(16).padStart(32, '0')
|
|
|
+ }
|
|
|
+
|
|
|
+ fun checkForUpdate(context: Context, onShowDialog: (updateInfo: UpdateInfo) -> Unit) {
|
|
|
+ (context as BaseNoActionBarActivity).lifecycleScope.launch {
|
|
|
+ val call = updateAppService?.queryAppUpgrade(
|
|
|
+ mutableMapOf(
|
|
|
+ "PackageName" to context.packageName
|
|
|
+ )
|
|
|
+ )
|
|
|
+ call?.enqueue(object : Callback<UpdateAppRepo> {
|
|
|
+ override fun onResponse(call: Call<UpdateAppRepo>, response: Response<UpdateAppRepo>) {
|
|
|
+ if (response.isSuccessful) {
|
|
|
+ val responseBody = response.body()
|
|
|
+ Logger.d(TAG, "====== Success: $responseBody")
|
|
|
+ if (responseBody!!.code == "200") {
|
|
|
+ val versionCode = responseBody.data.versionCode
|
|
|
+ val versionName = responseBody.data.versionName
|
|
|
+ val url = responseBody.data.downloadUrl
|
|
|
+ val md5 = responseBody.data.md5
|
|
|
+ Logger.d(TAG, "====== checkForUpdate onResponse, versionCode:$versionCode, name:$versionName, md5:$md5")
|
|
|
+ onShowDialog(UpdateInfo(versionCode, versionName, url, md5))
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ val errorBody = response.errorBody()?.string()
|
|
|
+ Logger.e(TAG, "Error: $errorBody")
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onFailure(call: Call<UpdateAppRepo>, t: Throwable) {
|
|
|
+ Logger.e(TAG, "Network error: ${t.message}")
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private object UpdateManagerHolder {
|
|
|
+ val instance = UpdateManager()
|
|
|
+ }
|
|
|
+
|
|
|
+ companion object {
|
|
|
+ fun getInstance(): UpdateManager {
|
|
|
+ return UpdateManagerHolder.instance
|
|
|
+ }
|
|
|
+ private const val TAG = "UpdateManager"
|
|
|
+ private const val DOWNLOADED_APK_FILENAME = "app-update.apk"
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+data class UpdateInfo(val versionCode: String, val version: String, val apkUrl: String, val md5: String)
|
|
|
+data class DownloadInfo(var downloadId: Long, var apkUrl: String, var md5: String, var retry: Int)
|