修复bug, 添加订阅下载完自动安装

pull/375/head
gedoor 4 years ago
parent cd8615075c
commit e55227a2a9
  1. 1
      app/src/main/AndroidManifest.xml
  2. 37
      app/src/main/java/io/legado/app/help/IntentHelp.kt
  3. 156
      app/src/main/java/io/legado/app/service/DownloadService.kt
  4. 26
      app/src/main/java/io/legado/app/service/help/Download.kt
  5. 4
      app/src/main/java/io/legado/app/ui/book/read/config/BgTextConfigDialog.kt
  6. 7
      app/src/main/java/io/legado/app/ui/rss/read/ReadRssActivity.kt

@ -18,6 +18,7 @@
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" /> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
<application <application
android:name=".App" android:name=".App"

@ -3,6 +3,7 @@ package io.legado.app.help
import android.app.PendingIntent import android.app.PendingIntent
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Bundle
import io.legado.app.R import io.legado.app.R
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
@ -32,21 +33,29 @@ object IntentHelp {
} }
} }
inline fun <reified T> servicePendingIntent(context: Context, action: String): PendingIntent? { inline fun <reified T> servicePendingIntent(
return PendingIntent.getService( context: Context,
context, action: String,
0, bundle: Bundle? = null
Intent(context, T::class.java).apply { this.action = action }, ): PendingIntent? {
PendingIntent.FLAG_UPDATE_CURRENT 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 <reified T> activityPendingIntent(context: Context, action: String): PendingIntent? { inline fun <reified T> activityPendingIntent(
return PendingIntent.getActivity( context: Context,
context, action: String,
0, bundle: Bundle? = null
Intent(context, T::class.java).apply { this.action = action }, ): PendingIntent? {
PendingIntent.FLAG_UPDATE_CURRENT 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)
} }
} }

@ -1,46 +1,172 @@
package io.legado.app.service package io.legado.app.service
import android.app.DownloadManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.os.Handler
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.os.bundleOf
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseService import io.legado.app.base.BaseService
import io.legado.app.constant.AppConst import io.legado.app.constant.AppConst
import io.legado.app.constant.IntentAction import io.legado.app.constant.IntentAction
import io.legado.app.help.IntentHelp import io.legado.app.help.IntentHelp
import org.jetbrains.anko.downloadManager
import org.jetbrains.anko.notificationManager
class DownloadService : BaseService() { class DownloadService : BaseService() {
private val notificationBuilder by lazy { private val downloads = hashMapOf<Long, String>()
val builder = NotificationCompat.Builder(this, AppConst.channelIdDownload) private val completeDownloads = hashSetOf<Long>()
.setSmallIcon(R.drawable.ic_download) private val handler = Handler()
.setOngoing(true) private val runnable = Runnable {
.setContentTitle(getString(R.string.action_download)) checkDownloadState()
builder.addAction( }
R.drawable.ic_stop_black_24dp,
getString(R.string.cancel), private val downloadReceiver = object : BroadcastReceiver() {
IntentHelp.servicePendingIntent<DownloadService>(this, IntentAction.stop) override fun onReceive(context: Context, intent: Intent) {
) queryState()
builder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC) }
} }
override fun onCreate() { override fun onCreate() {
super.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 { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) { 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) 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<DownloadService>(
this,
IntentAction.play,
bundleOf("downloadId" to downloadId)
)
)
notificationBuilder.addAction(
R.drawable.ic_stop_black_24dp,
getString(R.string.cancel),
IntentHelp.servicePendingIntent<DownloadService>(
this,
IntentAction.stop,
bundleOf("downloadId" to downloadId)
)
)
notificationBuilder.setDeleteIntent(
IntentHelp.servicePendingIntent<DownloadService>(
this,
IntentAction.stop,
bundleOf("downloadId" to downloadId)
)
)
notificationBuilder.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
notificationBuilder.setContentText(content) notificationBuilder.setContentText(content)
notificationBuilder.setProgress(max, progress, false)
notificationBuilder.setAutoCancel(true)
val notification = notificationBuilder.build() val notification = notificationBuilder.build()
startForeground(AppConst.notificationIdDownload, notification) startForeground(downloadId.toInt(), notification)
} }
} }

@ -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)
}
}
}

@ -324,7 +324,9 @@ class BgTextConfigDialog : BaseDialogFragment(), FileChooserDialog.CallBack {
val fontName = FileUtils.getName(config.textFont) val fontName = FileUtils.getName(config.textFont)
val fontPath = val fontPath =
FileUtils.getPath(requireContext().externalFilesDir, "font", fontName) 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 config.textFont = fontPath
} }
if (config.bgType() == 2) { if (config.bgType() == 2) {

@ -15,12 +15,14 @@ import io.legado.app.R
import io.legado.app.base.VMBaseActivity import io.legado.app.base.VMBaseActivity
import io.legado.app.lib.theme.DrawableUtils import io.legado.app.lib.theme.DrawableUtils
import io.legado.app.lib.theme.primaryTextColor 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.FileChooserDialog
import io.legado.app.ui.filechooser.FilePicker import io.legado.app.ui.filechooser.FilePicker
import io.legado.app.utils.* import io.legado.app.utils.*
import kotlinx.android.synthetic.main.activity_rss_read.* import kotlinx.android.synthetic.main.activity_rss_read.*
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.apache.commons.text.StringEscapeUtils import org.apache.commons.text.StringEscapeUtils
import org.jetbrains.anko.downloadManager
import org.jetbrains.anko.share import org.jetbrains.anko.share
import org.jsoup.Jsoup import org.jsoup.Jsoup
@ -160,13 +162,12 @@ class ReadRssActivity : VMBaseActivity<ReadRssViewModel>(R.layout.activity_rss_r
request.setAllowedOverRoaming(true) request.setAllowedOverRoaming(true)
// 允许下载的网路类型 // 允许下载的网路类型
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI) request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN)
// 设置下载文件保存的路径和文件名 // 设置下载文件保存的路径和文件名
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName) request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
val downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
// 添加一个下载任务 // 添加一个下载任务
val downloadId = downloadManager.enqueue(request) val downloadId = downloadManager.enqueue(request)
print(downloadId) Download.start(this, downloadId, fileName)
} }
} }
} }

Loading…
Cancel
Save