package com.lizongying.mytv import android.app.DownloadManager import android.app.DownloadManager.Request import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter import android.database.Cursor import android.net.Uri import android.os.Build import android.os.Environment import android.os.Handler import android.os.Looper import android.util.Log import androidx.fragment.app.FragmentActivity import com.lizongying.mytv.api.ApiClient import com.lizongying.mytv.requests.ReleaseRequest import com.lizongying.mytv.requests.ReleaseResponse import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import java.io.File class UpdateManager( private var context: Context, private var versionCode: Long ) : ConfirmationFragment.ConfirmationListener { private var releaseRequest = ReleaseRequest() private var release: ReleaseResponse? = null private var downloadReceiver: DownloadReceiver? = null fun checkAndUpdate() { CoroutineScope(Dispatchers.Main).launch { var text = "版本获取失败" var update = false try { release = releaseRequest.getRelease() Log.i(TAG, "versionCode $versionCode ${release?.version_code}") if (release?.version_code != null) { if (release?.version_code!! > versionCode) { text = "最新版本:${release?.version_name}" update = true } else { text = "已是最新版本,不需要更新" } } } catch (e: Exception) { Log.e(TAG, "Error occurred: ${e.message}", e) } updateUI(text, update) } } private fun updateUI(text: String, update: Boolean) { val dialog = ConfirmationFragment(this@UpdateManager, text, update) dialog.show((context as FragmentActivity).supportFragmentManager, TAG) } private fun startDownload(release: ReleaseResponse) { val apkName = "my-tv" val apkFileName = "$apkName-${release.version_name}.apk" val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val request = Request(Uri.parse("${ApiClient.DOWNLOAD_HOST}${release.version_name}/$apkName-${release.version_name}.apk")) Log.i( TAG, "url ${Uri.parse("${ApiClient.DOWNLOAD_HOST}${release.version_name}/$apkName-${release.version_name}.apk")}" ) context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.mkdirs() Log.i(TAG, "save dir ${Environment.DIRECTORY_DOWNLOADS}") request.setDestinationInExternalFilesDir( context, Environment.DIRECTORY_DOWNLOADS, apkFileName ) request.setTitle("${context.resources.getString(R.string.app_name)} ${release.version_name}") request.setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED) request.setAllowedOverRoaming(false) request.setMimeType("application/vnd.android.package-archive") // 获取下载任务的引用 val downloadReference = downloadManager.enqueue(request) downloadReceiver = DownloadReceiver(context, apkFileName, downloadReference) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.registerReceiver( downloadReceiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE), Context.RECEIVER_NOT_EXPORTED, ) } else { context.registerReceiver( downloadReceiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE) ) } getDownloadProgress(context, downloadReference) { progress -> println("Download progress: $progress%") } } private fun getDownloadProgress( context: Context, downloadId: Long, progressListener: (Int) -> Unit ) { val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val handler = Handler(Looper.getMainLooper()) val intervalMillis: Long = 1000 handler.post(object : Runnable { override fun run() { Log.i(TAG, "search") val query = DownloadManager.Query().setFilterById(downloadId) val cursor: Cursor = downloadManager.query(query) cursor.use { if (it.moveToFirst()) { val bytesDownloadedIndex = it.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR) val bytesTotalIndex = it.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES) // 检查列名是否存在 if (bytesDownloadedIndex != -1 && bytesTotalIndex != -1) { val bytesDownloaded = it.getInt(bytesDownloadedIndex) val bytesTotal = it.getInt(bytesTotalIndex) if (bytesTotal != -1) { val progress = (bytesDownloaded * 100L / bytesTotal).toInt() progressListener(progress) if (progress == 100) { return } } } } } // handler.postDelayed(this, intervalMillis) } }) } private class DownloadReceiver( private val context: Context, private val apkFileName: String, private val downloadReference: Long ) : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val reference = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) Log.i(TAG, "reference $reference") if (reference == downloadReference) { val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val query = DownloadManager.Query().setFilterById(downloadReference) val cursor = downloadManager.query(query) if (cursor != null && cursor.moveToFirst()) { val statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS) if (statusIndex < 0) { Log.i(TAG, "Download failure") return } val status = cursor.getInt(statusIndex) val progressIndex = cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR) if (progressIndex < 0) { Log.i(TAG, "Download failure") return } val progress = cursor.getInt(progressIndex) val totalSizeIndex = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES) val totalSize = cursor.getInt(totalSizeIndex) cursor.close() when (status) { DownloadManager.STATUS_SUCCESSFUL -> { installNewVersion() } DownloadManager.STATUS_FAILED -> { // Handle download failure Log.i(TAG, "Download failure") } else -> { // Update UI with download progress val percentage = progress * 100 / totalSize Log.i(TAG, "Download progress: $percentage%") } } } } } private fun installNewVersion() { val apkFile = File( context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), apkFileName ) Log.i(TAG, "apkFile $apkFile") if (apkFile.exists()) { val apkUri = Uri.parse("file://$apkFile") Log.i(TAG, "apkUri $apkUri") val installIntent = Intent(Intent.ACTION_VIEW).apply { setDataAndType(apkUri, "application/vnd.android.package-archive") addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK) } context.startActivity(installIntent) } else { Log.e(TAG, "APK file does not exist!") } } } companion object { private const val TAG = "UpdateManager" } override fun onConfirm() { Log.i(TAG, "onConfirm $release") release?.let { startDownload(it) } } override fun onCancel() { } fun destroy() { if (downloadReceiver != null) { context.unregisterReceiver(downloadReceiver) Log.i(TAG, "destroy downloadReceiver") } } }