mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-04 11:16:44 -04:00
Merge pull request #4210 from kiwix/Fixes#4207
Fixed: Download notification disappears when pausing a download.
This commit is contained in:
commit
c1a3b2bc40
@ -41,7 +41,8 @@ const val DEFAULT_INT_VALUE = -1
|
|||||||
class DownloadManagerMonitor @Inject constructor(
|
class DownloadManagerMonitor @Inject constructor(
|
||||||
val fetch: Fetch,
|
val fetch: Fetch,
|
||||||
val context: Context,
|
val context: Context,
|
||||||
val downloadRoomDao: DownloadRoomDao
|
val downloadRoomDao: DownloadRoomDao,
|
||||||
|
private val fetchDownloadNotificationManager: FetchDownloadNotificationManager
|
||||||
) : DownloadMonitor {
|
) : DownloadMonitor {
|
||||||
private val updater = PublishSubject.create<() -> Unit>()
|
private val updater = PublishSubject.create<() -> Unit>()
|
||||||
private var updaterDisposable: Disposable? = null
|
private var updaterDisposable: Disposable? = null
|
||||||
@ -122,7 +123,12 @@ class DownloadManagerMonitor @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun update(download: Download) {
|
private fun update(download: Download) {
|
||||||
updater.onNext { downloadRoomDao.update(download) }
|
updater.onNext {
|
||||||
|
downloadRoomDao.update(download)
|
||||||
|
if (download.isPaused()) {
|
||||||
|
fetchDownloadNotificationManager.showDownloadPauseNotification(fetch, download)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun delete(download: Download) {
|
private fun delete(download: Download) {
|
||||||
|
@ -116,12 +116,9 @@ class DownloadMonitorService : Service() {
|
|||||||
it.status == Status.NONE ||
|
it.status == Status.NONE ||
|
||||||
it.status == Status.ADDED ||
|
it.status == Status.ADDED ||
|
||||||
it.status == Status.QUEUED ||
|
it.status == Status.QUEUED ||
|
||||||
it.status == Status.DOWNLOADING
|
it.status == Status.DOWNLOADING ||
|
||||||
}?.let {
|
it.isPaused()
|
||||||
val notificationBuilder =
|
}?.let(::setForegroundNotificationForDownload) ?: kotlin.run {
|
||||||
fetchDownloadNotificationManager.getNotificationBuilder(it.id, it.id)
|
|
||||||
startForeground(it.id, notificationBuilder.build())
|
|
||||||
} ?: kotlin.run {
|
|
||||||
stopForegroundServiceForDownloads()
|
stopForegroundServiceForDownloads()
|
||||||
// Cancel the last ongoing notification after detaching it from
|
// Cancel the last ongoing notification after detaching it from
|
||||||
// the foreground service if no active downloads are found.
|
// the foreground service if no active downloads are found.
|
||||||
@ -131,6 +128,21 @@ class DownloadMonitorService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setForegroundNotificationForDownload(it: Download) {
|
||||||
|
val notificationBuilder =
|
||||||
|
fetchDownloadNotificationManager.getNotificationBuilder(it.id, it.id)
|
||||||
|
var foreGroundServiceNotification = notificationBuilder.build()
|
||||||
|
if (it.isPaused()) {
|
||||||
|
// Clear any pending actions on this notification builder.
|
||||||
|
notificationBuilder.clearActions()
|
||||||
|
// If a download is paused that means there is no notification for it, so we have to
|
||||||
|
// show our custom cancel notification.
|
||||||
|
foreGroundServiceNotification =
|
||||||
|
fetchDownloadNotificationManager.getCancelNotification(fetch, it, notificationBuilder)
|
||||||
|
}
|
||||||
|
startForeground(it.id, foreGroundServiceNotification)
|
||||||
|
}
|
||||||
|
|
||||||
private fun cancelNotificationForId(downloadId: Int) {
|
private fun cancelNotificationForId(downloadId: Int) {
|
||||||
notificationManager.cancel(downloadId)
|
notificationManager.cancel(downloadId)
|
||||||
}
|
}
|
||||||
@ -165,7 +177,7 @@ class DownloadMonitorService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onPaused(download: Download) {
|
override fun onPaused(download: Download) {
|
||||||
update(download, true)
|
update(download)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onProgress(
|
override fun onProgress(
|
||||||
@ -213,6 +225,13 @@ class DownloadMonitorService : Service() {
|
|||||||
downloadRoomDao.downloads().blockingFirst()
|
downloadRoomDao.downloads().blockingFirst()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If someone pause the Download then post a notification since fetch removes the
|
||||||
|
// notification for ongoing download when pause so we needs to show our custom notification.
|
||||||
|
if (download.isPaused()) {
|
||||||
|
fetchDownloadNotificationManager.showDownloadPauseNotification(fetch, download).also {
|
||||||
|
setForeGroundServiceNotificationIfNoActiveDownloads(fetch, download)
|
||||||
|
}
|
||||||
|
}
|
||||||
if (shouldSetForegroundNotification) {
|
if (shouldSetForegroundNotification) {
|
||||||
setForegroundNotification(download.id)
|
setForegroundNotification(download.id)
|
||||||
}
|
}
|
||||||
@ -227,6 +246,24 @@ class DownloadMonitorService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun setForeGroundServiceNotificationIfNoActiveDownloads(
|
||||||
|
fetch: Fetch,
|
||||||
|
download: Download
|
||||||
|
) {
|
||||||
|
updater.onNext {
|
||||||
|
// Check if there are any ongoing downloads.
|
||||||
|
// If the list is empty, it means no other downloads are running,
|
||||||
|
// so we need to promote this download to a foreground service.
|
||||||
|
fetch.getDownloadsWithStatus(
|
||||||
|
listOf(Status.NONE, Status.ADDED, Status.QUEUED, Status.DOWNLOADING)
|
||||||
|
) { activeDownloads ->
|
||||||
|
if (activeDownloads.isEmpty()) {
|
||||||
|
setForegroundNotificationForDownload(download)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
private fun showDownloadCompletedNotification(download: Download) {
|
private fun showDownloadCompletedNotification(download: Download) {
|
||||||
downloadNotificationChannel()
|
downloadNotificationChannel()
|
||||||
|
@ -19,8 +19,10 @@
|
|||||||
package org.kiwix.kiwixmobile.core.downloader.downloadManager
|
package org.kiwix.kiwixmobile.core.downloader.downloadManager
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
|
import android.app.Notification
|
||||||
import android.app.NotificationChannel
|
import android.app.NotificationChannel
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.app.PendingIntent.FLAG_IMMUTABLE
|
import android.app.PendingIntent.FLAG_IMMUTABLE
|
||||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
import android.app.PendingIntent.getActivity
|
import android.app.PendingIntent.getActivity
|
||||||
@ -30,12 +32,36 @@ import android.content.IntentFilter
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationCompat.Builder
|
||||||
|
import com.tonyodev.fetch2.ACTION_TYPE_CANCEL
|
||||||
|
import com.tonyodev.fetch2.ACTION_TYPE_DELETE
|
||||||
|
import com.tonyodev.fetch2.ACTION_TYPE_INVALID
|
||||||
|
import com.tonyodev.fetch2.ACTION_TYPE_PAUSE
|
||||||
|
import com.tonyodev.fetch2.ACTION_TYPE_RESUME
|
||||||
|
import com.tonyodev.fetch2.ACTION_TYPE_RETRY
|
||||||
import com.tonyodev.fetch2.DefaultFetchNotificationManager
|
import com.tonyodev.fetch2.DefaultFetchNotificationManager
|
||||||
|
import com.tonyodev.fetch2.Download
|
||||||
import com.tonyodev.fetch2.DownloadNotification
|
import com.tonyodev.fetch2.DownloadNotification
|
||||||
|
import com.tonyodev.fetch2.DownloadNotification.ActionType.CANCEL
|
||||||
|
import com.tonyodev.fetch2.DownloadNotification.ActionType.DELETE
|
||||||
|
import com.tonyodev.fetch2.DownloadNotification.ActionType.PAUSE
|
||||||
|
import com.tonyodev.fetch2.DownloadNotification.ActionType.RESUME
|
||||||
|
import com.tonyodev.fetch2.DownloadNotification.ActionType.RETRY
|
||||||
|
import com.tonyodev.fetch2.EXTRA_ACTION_TYPE
|
||||||
|
import com.tonyodev.fetch2.EXTRA_DOWNLOAD_ID
|
||||||
|
import com.tonyodev.fetch2.EXTRA_GROUP_ACTION
|
||||||
|
import com.tonyodev.fetch2.EXTRA_NAMESPACE
|
||||||
|
import com.tonyodev.fetch2.EXTRA_NOTIFICATION_GROUP_ID
|
||||||
|
import com.tonyodev.fetch2.EXTRA_NOTIFICATION_ID
|
||||||
import com.tonyodev.fetch2.Fetch
|
import com.tonyodev.fetch2.Fetch
|
||||||
import com.tonyodev.fetch2.R.drawable
|
import com.tonyodev.fetch2.R.drawable
|
||||||
import com.tonyodev.fetch2.R.string
|
import com.tonyodev.fetch2.R.string
|
||||||
|
import com.tonyodev.fetch2.Status
|
||||||
import com.tonyodev.fetch2.util.DEFAULT_NOTIFICATION_TIMEOUT_AFTER_RESET
|
import com.tonyodev.fetch2.util.DEFAULT_NOTIFICATION_TIMEOUT_AFTER_RESET
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.kiwix.kiwixmobile.core.CoreApp
|
import org.kiwix.kiwixmobile.core.CoreApp
|
||||||
import org.kiwix.kiwixmobile.core.Intents
|
import org.kiwix.kiwixmobile.core.Intents
|
||||||
import org.kiwix.kiwixmobile.core.R
|
import org.kiwix.kiwixmobile.core.R
|
||||||
@ -46,9 +72,13 @@ import javax.inject.Inject
|
|||||||
const val DOWNLOAD_NOTIFICATION_TITLE = "OPEN_ZIM_FILE"
|
const val DOWNLOAD_NOTIFICATION_TITLE = "OPEN_ZIM_FILE"
|
||||||
|
|
||||||
class FetchDownloadNotificationManager @Inject constructor(
|
class FetchDownloadNotificationManager @Inject constructor(
|
||||||
context: Context,
|
val context: Context,
|
||||||
private val downloadRoomDao: DownloadRoomDao
|
private val downloadRoomDao: DownloadRoomDao
|
||||||
) : DefaultFetchNotificationManager(context) {
|
) : DefaultFetchNotificationManager(context) {
|
||||||
|
private val downloadNotificationManager: NotificationManager by lazy {
|
||||||
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
}
|
||||||
|
|
||||||
override fun getFetchInstanceForNamespace(namespace: String): Fetch = Fetch.getDefaultInstance()
|
override fun getFetchInstanceForNamespace(namespace: String): Fetch = Fetch.getDefaultInstance()
|
||||||
|
|
||||||
override fun registerBroadcastReceiver() {
|
override fun registerBroadcastReceiver() {
|
||||||
@ -176,4 +206,78 @@ class FetchDownloadNotificationManager @Inject constructor(
|
|||||||
notificationBuilder.setAutoCancel(true)
|
notificationBuilder.setAutoCancel(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showDownloadPauseNotification(fetch: Fetch, download: Download) {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val notificationBuilder = getNotificationBuilder(download.id, download.id)
|
||||||
|
val cancelNotification = getCancelNotification(fetch, download, notificationBuilder)
|
||||||
|
downloadNotificationManager.notify(download.id, cancelNotification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getCancelNotification(
|
||||||
|
fetch: Fetch,
|
||||||
|
download: Download,
|
||||||
|
notificationBuilder: Builder
|
||||||
|
): Notification {
|
||||||
|
val downloadTitle = getDownloadNotificationTitle(download)
|
||||||
|
val notificationTitle =
|
||||||
|
runBlocking(Dispatchers.IO) {
|
||||||
|
downloadRoomDao.getEntityForFileName(downloadTitle)?.title
|
||||||
|
?: downloadTitle
|
||||||
|
}
|
||||||
|
return notificationBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT)
|
||||||
|
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||||
|
.setContentTitle(notificationTitle)
|
||||||
|
.setContentText(context.getString(string.fetch_notification_download_paused))
|
||||||
|
// Set the ongoing true so that could not cancel the pause notification.
|
||||||
|
// However, on Android 14 and above user can cancel the notification by swipe right so we
|
||||||
|
// can't control that see https://developer.android.com/about/versions/14/behavior-changes-all#non-dismissable-notifications
|
||||||
|
.setOngoing(true)
|
||||||
|
.setGroup(download.id.toString())
|
||||||
|
.setGroupSummary(false)
|
||||||
|
.setProgress(HUNDERED, download.progress, false)
|
||||||
|
.addAction(
|
||||||
|
drawable.fetch_notification_cancel,
|
||||||
|
context.getString(R.string.cancel),
|
||||||
|
getActionPendingIntent(fetch, download, DownloadNotification.ActionType.DELETE)
|
||||||
|
)
|
||||||
|
.addAction(
|
||||||
|
drawable.fetch_notification_resume,
|
||||||
|
context.getString(R.string.notification_resume_button_text),
|
||||||
|
getActionPendingIntent(fetch, download, DownloadNotification.ActionType.RESUME)
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getActionPendingIntent(
|
||||||
|
fetch: Fetch,
|
||||||
|
download: Download,
|
||||||
|
actionType: DownloadNotification.ActionType
|
||||||
|
): PendingIntent {
|
||||||
|
val intent = Intent(notificationManagerAction).apply {
|
||||||
|
putExtra(EXTRA_NAMESPACE, fetch.namespace)
|
||||||
|
putExtra(EXTRA_DOWNLOAD_ID, download.id)
|
||||||
|
putExtra(EXTRA_NOTIFICATION_ID, download.id)
|
||||||
|
putExtra(EXTRA_GROUP_ACTION, false)
|
||||||
|
putExtra(EXTRA_NOTIFICATION_GROUP_ID, download.id)
|
||||||
|
}
|
||||||
|
val action = when (actionType) {
|
||||||
|
CANCEL -> ACTION_TYPE_CANCEL
|
||||||
|
DELETE -> ACTION_TYPE_DELETE
|
||||||
|
RESUME -> ACTION_TYPE_RESUME
|
||||||
|
PAUSE -> ACTION_TYPE_PAUSE
|
||||||
|
RETRY -> ACTION_TYPE_RETRY
|
||||||
|
else -> ACTION_TYPE_INVALID
|
||||||
|
}
|
||||||
|
intent.putExtra(EXTRA_ACTION_TYPE, action)
|
||||||
|
return PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
download.id + action,
|
||||||
|
intent,
|
||||||
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun Download.isPaused() = status == Status.PAUSED
|
||||||
|
Loading…
x
Reference in New Issue
Block a user