Rename
This commit is contained in:
1 parent
b86d90021c
commit
6c0757c91b
972 files changed
+6885
-7321
No files matched your search
@@ -0,0 +1,279 @@
|
||||
package io.legato.kazusa.service
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
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.Environment
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import io.legato.kazusa.R
|
||||
import io.legato.kazusa.base.BaseService
|
||||
import io.legato.kazusa.constant.AppConst
|
||||
import io.legato.kazusa.constant.AppLog
|
||||
import io.legato.kazusa.constant.IntentAction
|
||||
import io.legato.kazusa.constant.NotificationId
|
||||
import io.legato.kazusa.utils.IntentType
|
||||
import io.legato.kazusa.utils.openFileUri
|
||||
import io.legato.kazusa.utils.servicePendingIntent
|
||||
import io.legato.kazusa.utils.toastOnUi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import splitties.init.appCtx
|
||||
import splitties.systemservices.downloadManager
|
||||
import splitties.systemservices.notificationManager
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*/
|
||||
class DownloadService : BaseService() {
|
||||
private val groupKey = "${appCtx.packageName}.download"
|
||||
private val downloads = hashMapOf<Long, DownloadInfo>()
|
||||
private val completeDownloads = hashSetOf<Long>()
|
||||
private var upStateJob: Job? = null
|
||||
private val downloadReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
queryState()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
ContextCompat.registerReceiver(
|
||||
this,
|
||||
downloadReceiver,
|
||||
IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE),
|
||||
ContextCompat.RECEIVER_EXPORTED
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
unregisterReceiver(downloadReceiver)
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
when (intent?.action) {
|
||||
IntentAction.start -> startDownload(
|
||||
intent.getStringExtra("url"),
|
||||
intent.getStringExtra("fileName")
|
||||
)
|
||||
|
||||
IntentAction.play -> {
|
||||
val id = intent.getLongExtra("downloadId", 0)
|
||||
if (completeDownloads.contains(id)) {
|
||||
openDownload(id, downloads[id]?.fileName)
|
||||
} else {
|
||||
toastOnUi("未完成,下载的文件夹Download")
|
||||
}
|
||||
}
|
||||
|
||||
IntentAction.stop -> {
|
||||
val downloadId = intent.getLongExtra("downloadId", 0)
|
||||
removeDownload(downloadId)
|
||||
}
|
||||
}
|
||||
return super.onStartCommand(intent, flags, startId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始下载
|
||||
*/
|
||||
@Synchronized
|
||||
private fun startDownload(url: String?, fileName: String?) {
|
||||
if (url == null || fileName == null) {
|
||||
if (downloads.isEmpty()) {
|
||||
stopSelf()
|
||||
}
|
||||
return
|
||||
}
|
||||
if (downloads.values.any { it.url == url }) {
|
||||
toastOnUi("已在下载列表")
|
||||
return
|
||||
}
|
||||
kotlin.runCatching {
|
||||
// 指定下载地址
|
||||
val request = DownloadManager.Request(Uri.parse(url))
|
||||
// 设置通知
|
||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN)
|
||||
// 设置下载文件保存的路径和文件名
|
||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
|
||||
// 添加一个下载任务
|
||||
val downloadId = downloadManager.enqueue(request)
|
||||
downloads[downloadId] =
|
||||
DownloadInfo(url, fileName, NotificationId.Download + downloads.size)
|
||||
queryState()
|
||||
if (upStateJob == null) {
|
||||
checkDownloadState()
|
||||
}
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
val msg = when (it) {
|
||||
is SecurityException -> "下载出错,没有存储权限"
|
||||
else -> "下载出错,${it.localizedMessage}"
|
||||
}
|
||||
toastOnUi(msg)
|
||||
AppLog.put(msg, it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消下载
|
||||
*/
|
||||
@Synchronized
|
||||
private fun removeDownload(downloadId: Long) {
|
||||
if (!completeDownloads.contains(downloadId)) {
|
||||
downloadManager.remove(downloadId)
|
||||
}
|
||||
downloads.remove(downloadId)
|
||||
completeDownloads.remove(downloadId)
|
||||
notificationManager.cancel(downloadId.toInt())
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载成功
|
||||
*/
|
||||
@Synchronized
|
||||
private fun successDownload(downloadId: Long) {
|
||||
if (!completeDownloads.contains(downloadId)) {
|
||||
completeDownloads.add(downloadId)
|
||||
val fileName = downloads[downloadId]?.fileName
|
||||
openDownload(downloadId, fileName)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkDownloadState() {
|
||||
upStateJob?.cancel()
|
||||
upStateJob = lifecycleScope.launch {
|
||||
while (isActive) {
|
||||
queryState()
|
||||
delay(1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询下载进度
|
||||
*/
|
||||
@Synchronized
|
||||
private fun queryState() {
|
||||
if (downloads.isEmpty()) {
|
||||
stopSelf()
|
||||
return
|
||||
}
|
||||
val ids = downloads.keys
|
||||
val query = DownloadManager.Query()
|
||||
query.setFilterById(*ids.toLongArray())
|
||||
downloadManager.query(query).use { cursor ->
|
||||
if (cursor.moveToFirst()) {
|
||||
val idIndex = cursor.getColumnIndex(DownloadManager.COLUMN_ID)
|
||||
val progressIndex =
|
||||
cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)
|
||||
val fileSizeIndex = cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)
|
||||
val statusIndex = cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)
|
||||
do {
|
||||
val id = cursor.getLong(idIndex)
|
||||
val progress = cursor.getInt(progressIndex)
|
||||
val max = cursor.getInt(fileSizeIndex)
|
||||
val status = when (cursor.getInt(statusIndex)) {
|
||||
DownloadManager.STATUS_PAUSED -> getString(R.string.pause)
|
||||
DownloadManager.STATUS_PENDING -> getString(R.string.wait_download)
|
||||
DownloadManager.STATUS_RUNNING -> getString(R.string.downloading)
|
||||
DownloadManager.STATUS_SUCCESSFUL -> {
|
||||
successDownload(id)
|
||||
getString(R.string.download_success)
|
||||
}
|
||||
|
||||
DownloadManager.STATUS_FAILED -> getString(R.string.download_error)
|
||||
else -> getString(R.string.unknown_state)
|
||||
}
|
||||
downloads[id]?.let { downloadInfo ->
|
||||
upDownloadNotification(
|
||||
id,
|
||||
downloadInfo.notificationId,
|
||||
"${downloadInfo.fileName} $status",
|
||||
max,
|
||||
progress,
|
||||
downloadInfo.startTime
|
||||
)
|
||||
}
|
||||
} while (cursor.moveToNext())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开下载文件
|
||||
*/
|
||||
private fun openDownload(downloadId: Long, fileName: String?) {
|
||||
kotlin.runCatching {
|
||||
downloadManager.getUriForDownloadedFile(downloadId)?.let { uri ->
|
||||
val type = IntentType.from(fileName)
|
||||
openFileUri(uri, type)
|
||||
}
|
||||
}.onFailure {
|
||||
AppLog.put("打开下载文件${fileName}出错", it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun startForegroundNotification() {
|
||||
val notification = NotificationCompat.Builder(this, AppConst.channelIdDownload)
|
||||
.setSmallIcon(R.drawable.ic_download)
|
||||
.setSubText(getString(R.string.action_download))
|
||||
.setGroup(groupKey)
|
||||
.setGroupSummary(true)
|
||||
.setOngoing(true)
|
||||
.build()
|
||||
startForeground(NotificationId.DownloadService, notification)
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新通知
|
||||
*/
|
||||
private fun upDownloadNotification(
|
||||
downloadId: Long,
|
||||
notificationId: Int,
|
||||
content: String,
|
||||
max: Int,
|
||||
progress: Int,
|
||||
startTime: Long
|
||||
) {
|
||||
val notificationBuilder = NotificationCompat.Builder(this, AppConst.channelIdDownload)
|
||||
.setSmallIcon(R.drawable.ic_download)
|
||||
.setSubText(getString(R.string.action_download))
|
||||
.setContentTitle(content)
|
||||
.setOnlyAlertOnce(true)
|
||||
.setContentIntent(
|
||||
servicePendingIntent<DownloadService>(IntentAction.play, downloadId.toInt()) {
|
||||
putExtra("downloadId", downloadId)
|
||||
}
|
||||
)
|
||||
.setDeleteIntent(
|
||||
servicePendingIntent<DownloadService>(IntentAction.stop, downloadId.toInt()) {
|
||||
putExtra("downloadId", downloadId)
|
||||
}
|
||||
)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setGroup(groupKey)
|
||||
.setWhen(startTime)
|
||||
if (progress < max) {
|
||||
notificationBuilder.setProgress(max, progress, false)
|
||||
}
|
||||
notificationManager.notify(notificationId, notificationBuilder.build())
|
||||
}
|
||||
|
||||
private data class DownloadInfo(
|
||||
val url: String,
|
||||
val fileName: String,
|
||||
val notificationId: Int,
|
||||
val startTime: Long = System.currentTimeMillis()
|
||||
)
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user