This commit is contained in:
Li ZongYing
2024-05-31 11:10:18 +08:00
parent bcfca9e28c
commit cf8d7f00a8
17 changed files with 366 additions and 374 deletions
+2
View File
@@ -26,6 +26,7 @@ android {
cmake {
arguments "-DIS_SO_BUILD=${project.hasProperty('IS_SO_BUILD') ? project.IS_SO_BUILD : true}"
// abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
@@ -128,6 +129,7 @@ dependencies {
implementation 'com.github.bumptech.glide:glide:4.11.0'
implementation("androidx.work:work-runtime-ktx:2.9.0")
implementation 'androidx.core:core-ktx:1.11.0-beta02'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.leanback:leanback:1.2.0-alpha02'
+1
View File
@@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -1,26 +1,17 @@
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 androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.workDataOf
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(
@@ -32,17 +23,15 @@ class UpdateManager(
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) {
val code = release?.version_code
if (code != null) {
if (code.toLong() > versionCode) {
text = "最新版本:${release?.version_name}"
update = true
} else {
@@ -61,170 +50,24 @@ class UpdateManager(
dialog.show((context as FragmentActivity).supportFragmentManager, TAG)
}
private fun startDownload(release: ReleaseResponse) {
private fun startDownload(context: Context, release: ReleaseResponse) {
val versionName = release.version_name
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,
val downloadUrl =
"${ApiClient.DOWNLOAD_HOST}${release.version_name}/$apkName-${release.version_name}.apk"
Log.i(TAG, "downloadUrl: $downloadUrl")
val downloadRequest = OneTimeWorkRequestBuilder<UpdateWorker>()
.setInputData(
workDataOf(
"VERSION_NAME" to versionName,
"APK_FILENAME" to apkFileName,
"DOWNLOAD_URL" to downloadUrl
)
)
} else {
context.registerReceiver(
downloadReceiver,
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
)
}
.build()
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!")
}
}
WorkManager.getInstance(context).enqueue(downloadRequest)
}
companion object {
@@ -233,16 +76,9 @@ class UpdateManager(
override fun onConfirm() {
Log.i(TAG, "onConfirm $release")
release?.let { startDownload(it) }
release?.let { startDownload(context, it) }
}
override fun onCancel() {
}
fun destroy() {
if (downloadReceiver != null) {
context.unregisterReceiver(downloadReceiver)
Log.i(TAG, "destroy downloadReceiver")
}
}
}
@@ -0,0 +1,150 @@
package com.lizongying.mytv
import android.app.DownloadManager
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.core.content.FileProvider
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
class UpdateWorker(context: Context, params: WorkerParameters) : CoroutineWorker(context, params) {
private lateinit var notificationManager: NotificationManager
private lateinit var notificationBuilder: NotificationCompat.Builder
override suspend fun doWork(): Result {
val downloadUrl = inputData.getString("DOWNLOAD_URL") ?: return Result.failure()
val apkFileName = inputData.getString("APK_FILENAME") ?: return Result.failure()
val versionName = inputData.getString("VERSION_NAME") ?: return Result.failure()
showNotification(applicationContext)
return withContext(Dispatchers.IO) {
try {
downloadAndInstall(applicationContext, downloadUrl, apkFileName, versionName)
Result.success()
} catch (e: Exception) {
Result.failure()
}
}
}
private fun showNotification(context: Context) {
notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationBuilder = NotificationCompat.Builder(context, "download_channel").apply {
setSmallIcon(android.R.drawable.stat_sys_download)
setContentTitle("Downloading Update")
setContentText("Download in progress")
priority = NotificationCompat.PRIORITY_LOW
setOngoing(true)
setProgress(100, 0, false)
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
"download_channel",
"Download Updates",
NotificationManager.IMPORTANCE_LOW
)
notificationManager.createNotificationChannel(channel)
}
notificationManager.notify(1, notificationBuilder.build())
}
private fun downloadAndInstall(
context: Context,
downloadUrl: String,
apkFileName: String,
versionName: String
) {
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)?.mkdirs()
Log.i(TAG, "save dir ${Environment.DIRECTORY_DOWNLOADS}")
val request = DownloadManager.Request(Uri.parse(downloadUrl)).apply {
setTitle("${context.resources.getString(R.string.app_name)} $versionName Downloading")
setDescription("Downloading the latest version of the app")
setDestinationInExternalFilesDir(context, Environment.DIRECTORY_DOWNLOADS, apkFileName)
setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
// setAllowedOverRoaming(false)
setMimeType("application/vnd.android.package-archive")
}
val manager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
val downloadId = manager.enqueue(request)
var downloading = true
while (downloading) {
val query = DownloadManager.Query().setFilterById(downloadId)
val cursor = manager.query(query)
if (cursor.moveToFirst()) {
val bytesDownloaded =
cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
val bytesTotal =
cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
if (bytesTotal > 0) {
val progress = (bytesDownloaded * 100L / bytesTotal).toInt()
notificationBuilder.setProgress(100, progress, false)
notificationManager.notify(1, notificationBuilder.build())
}
when (cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS))) {
DownloadManager.STATUS_SUCCESSFUL -> downloading = false
DownloadManager.STATUS_FAILED -> {
downloading = false
throw Exception("Download failed")
}
}
}
cursor.close()
}
notificationBuilder.setContentText("Download complete")
.setProgress(0, 0, false)
.setOngoing(false)
notificationManager.notify(1, notificationBuilder.build())
val apkFile = File(
context.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS),
apkFileName
)
if (apkFile.exists()) {
val apkUri: Uri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
FileProvider.getUriForFile(context, context.packageName + ".fileprovider", apkFile)
.apply {
Intent.FLAG_GRANT_READ_URI_PERMISSION
}
} else {
Uri.parse("file://${apkFile.absolutePath}")
// Uri.fromFile(apkFile)
}
// 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 = "UpdateWorker"
}
}