diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ca5b1e976..648bb7323 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,6 +18,7 @@ + servicePendingIntent(context: Context, action: String): PendingIntent? { - return PendingIntent.getService( - context, - 0, - Intent(context, T::class.java).apply { this.action = action }, - PendingIntent.FLAG_UPDATE_CURRENT - ) + inline fun servicePendingIntent( + context: Context, + action: String, + bundle: Bundle? = null + ): PendingIntent? { + val intent = Intent(context, T::class.java) + intent.action = action + bundle?.let { + intent.putExtras(bundle) + } + return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) } - inline fun activityPendingIntent(context: Context, action: String): PendingIntent? { - return PendingIntent.getActivity( - context, - 0, - Intent(context, T::class.java).apply { this.action = action }, - PendingIntent.FLAG_UPDATE_CURRENT - ) + inline fun activityPendingIntent( + context: Context, + action: String, + bundle: Bundle? = null + ): PendingIntent? { + val intent = Intent(context, T::class.java) + intent.action = action + bundle?.let { + intent.putExtras(bundle) + } + return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) } } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/service/DownloadService.kt b/app/src/main/java/io/legado/app/service/DownloadService.kt index 7a77bb7a5..5b7522869 100644 --- a/app/src/main/java/io/legado/app/service/DownloadService.kt +++ b/app/src/main/java/io/legado/app/service/DownloadService.kt @@ -1,46 +1,172 @@ package io.legado.app.service +import android.app.DownloadManager +import android.content.BroadcastReceiver +import android.content.Context import android.content.Intent +import android.content.IntentFilter +import android.os.Handler import androidx.core.app.NotificationCompat +import androidx.core.os.bundleOf import io.legado.app.R import io.legado.app.base.BaseService import io.legado.app.constant.AppConst import io.legado.app.constant.IntentAction import io.legado.app.help.IntentHelp +import org.jetbrains.anko.downloadManager +import org.jetbrains.anko.notificationManager + class DownloadService : BaseService() { - private val notificationBuilder by lazy { - val builder = NotificationCompat.Builder(this, AppConst.channelIdDownload) - .setSmallIcon(R.drawable.ic_download) - .setOngoing(true) - .setContentTitle(getString(R.string.action_download)) - builder.addAction( - R.drawable.ic_stop_black_24dp, - getString(R.string.cancel), - IntentHelp.servicePendingIntent(this, IntentAction.stop) - ) - builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + private val downloads = hashMapOf() + private val completeDownloads = hashSetOf() + private val handler = Handler() + private val runnable = Runnable { + checkDownloadState() + } + + private val downloadReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + queryState() + } } override fun onCreate() { super.onCreate() - updateNotification("准备下载") + registerReceiver(downloadReceiver, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) + } + + override fun onTaskRemoved(rootIntent: Intent?) { + super.onTaskRemoved(rootIntent) + stopSelf() + } + + override fun onDestroy() { + super.onDestroy() + unregisterReceiver(downloadReceiver) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { when (intent?.action) { - + IntentAction.start -> startDownload( + intent.getLongExtra("downloadId", 0), + intent.getStringExtra("fileName") ?: "未知文件" + ) + IntentAction.play -> { + val id = intent.getLongExtra("downloadId", 0) + if (downloads[id]?.endsWith(".apk") == true) { + installApk(id) + } + } + IntentAction.stop -> { + val downloadId = intent.getLongExtra("downloadId", 0) + if (downloadId > 0) { + downloadManager.remove(downloadId) + notificationManager.cancel(downloadId.toInt()) + } else { + stopSelf() + } + } } return super.onStartCommand(intent, flags, startId) } + private fun startDownload(downloadId: Long, fileName: String) { + if (downloadId > 0) { + downloads[downloadId] = fileName + queryState() + checkDownloadState() + } + } + + private fun checkDownloadState() { + handler.removeCallbacks(runnable) + queryState() + handler.postDelayed(runnable, 1000) + } + + //查询下载进度 + private fun queryState() { + val ids = downloads.keys + val query = DownloadManager.Query() + query.setFilterById(*ids.toLongArray()) + val cursor = downloadManager.query(query) + if (!cursor.moveToFirst()) return + val id = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID)) + val progress: Int = + cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR)) + val max: Int = + cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES)) + val status = when (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))) { + DownloadManager.STATUS_PAUSED -> "暂停" + DownloadManager.STATUS_PENDING -> "待下载" + DownloadManager.STATUS_RUNNING -> "下载中" + DownloadManager.STATUS_SUCCESSFUL -> { + if (!completeDownloads.contains(id)) { + completeDownloads.add(id) + if (downloads[id]?.endsWith(".apk") == true) { + installApk(id) + } + } + "下载完成" + } + DownloadManager.STATUS_FAILED -> "下载失败" + else -> "未知状态" + } + updateNotification(id, "${downloads[id]} $status", max, progress) + if (!cursor.isClosed) { + cursor.close() + } + } + + private fun installApk(downloadId: Long) { + val downloadFileUri = downloadManager.getUriForDownloadedFile(downloadId) + downloadFileUri?.let { + val install = Intent(Intent.ACTION_VIEW) + install.setDataAndType(downloadFileUri, "application/vnd.android.package-archive") + install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + startActivity(install) + } + } + /** * 更新通知 */ - private fun updateNotification(content: String) { + private fun updateNotification(downloadId: Long, content: String, max: Int, progress: Int) { + val notificationBuilder = NotificationCompat.Builder(this, AppConst.channelIdDownload) + .setSmallIcon(R.drawable.ic_download) + .setOngoing(true) + .setContentTitle(getString(R.string.action_download)) + notificationBuilder.setContentIntent( + IntentHelp.servicePendingIntent( + this, + IntentAction.play, + bundleOf("downloadId" to downloadId) + ) + ) + notificationBuilder.addAction( + R.drawable.ic_stop_black_24dp, + getString(R.string.cancel), + IntentHelp.servicePendingIntent( + this, + IntentAction.stop, + bundleOf("downloadId" to downloadId) + ) + ) + notificationBuilder.setDeleteIntent( + IntentHelp.servicePendingIntent( + this, + IntentAction.stop, + bundleOf("downloadId" to downloadId) + ) + ) + notificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) notificationBuilder.setContentText(content) + notificationBuilder.setProgress(max, progress, false) + notificationBuilder.setAutoCancel(true) val notification = notificationBuilder.build() - startForeground(AppConst.notificationIdDownload, notification) + startForeground(downloadId.toInt(), notification) } + } \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/service/help/Download.kt b/app/src/main/java/io/legado/app/service/help/Download.kt new file mode 100644 index 000000000..cddbf59b6 --- /dev/null +++ b/app/src/main/java/io/legado/app/service/help/Download.kt @@ -0,0 +1,26 @@ +package io.legado.app.service.help + +import android.content.Context +import android.content.Intent +import io.legado.app.constant.IntentAction +import io.legado.app.service.DownloadService + +object Download { + + fun start(context: Context, downloadId: Long, fileName: String) { + Intent(context, DownloadService::class.java).let { + it.action = IntentAction.start + it.putExtra("downloadId", downloadId) + it.putExtra("fileName", fileName) + context.startService(it) + } + } + + fun stop(context: Context) { + Intent(context, DownloadService::class.java).let { + it.action = IntentAction.stop + context.startService(it) + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt b/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt index 841a5b9a9..b8d71b515 100644 --- a/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt +++ b/app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt @@ -324,7 +324,9 @@ class BgTextConfigDialog : BaseDialogFragment(), FileChooserDialog.CallBack { val fontName = FileUtils.getName(config.textFont) val fontPath = FileUtils.getPath(requireContext().externalFilesDir, "font", fontName) - FileUtils.getFile(configDir, fontName).copyTo(File(fontPath)) + if (!FileUtils.exist(fontPath)) { + FileUtils.getFile(configDir, fontName).copyTo(File(fontPath)) + } config.textFont = fontPath } if (config.bgType() == 2) { diff --git a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt index e2834e456..c66cad69f 100644 --- a/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt +++ b/app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt @@ -15,12 +15,14 @@ import io.legado.app.R import io.legado.app.base.VMBaseActivity import io.legado.app.lib.theme.DrawableUtils import io.legado.app.lib.theme.primaryTextColor +import io.legado.app.service.help.Download import io.legado.app.ui.filechooser.FileChooserDialog import io.legado.app.ui.filechooser.FilePicker import io.legado.app.utils.* import kotlinx.android.synthetic.main.activity_rss_read.* import kotlinx.coroutines.launch import org.apache.commons.text.StringEscapeUtils +import org.jetbrains.anko.downloadManager import org.jetbrains.anko.share import org.jsoup.Jsoup @@ -160,13 +162,12 @@ class ReadRssActivity : VMBaseActivity(R.layout.activity_rss_r request.setAllowedOverRoaming(true) // 允许下载的网路类型 request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI) - request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) + request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN) // 设置下载文件保存的路径和文件名 request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName) - val downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager // 添加一个下载任务 val downloadId = downloadManager.enqueue(request) - print(downloadId) + Download.start(this, downloadId, fileName) } } }