Fixed: The ZIM file was not opening when clicking on the download notification while the application was not running in the background.

This commit is contained in:
MohitMaliFtechiz 2024-12-24 16:36:19 +05:30
parent d5943856cd
commit a48f90422a
2 changed files with 113 additions and 40 deletions

View File

@ -18,30 +18,44 @@
package org.kiwix.kiwixmobile.core.downloader.downloadManager package org.kiwix.kiwixmobile.core.downloader.downloadManager
import android.annotation.SuppressLint
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service import android.app.Service
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build
import android.os.IBinder import android.os.IBinder
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import com.tonyodev.fetch2.Download import com.tonyodev.fetch2.Download
import com.tonyodev.fetch2.Error import com.tonyodev.fetch2.Error
import com.tonyodev.fetch2.Fetch import com.tonyodev.fetch2.Fetch
import com.tonyodev.fetch2.FetchListener import com.tonyodev.fetch2.FetchListener
import com.tonyodev.fetch2.R
import com.tonyodev.fetch2.Status import com.tonyodev.fetch2.Status
import com.tonyodev.fetch2.util.DEFAULT_NOTIFICATION_TIMEOUT_AFTER_RESET
import com.tonyodev.fetch2core.DownloadBlock import com.tonyodev.fetch2core.DownloadBlock
import io.reactivex.Observable
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.PublishSubject
import org.kiwix.kiwixmobile.core.CoreApp import org.kiwix.kiwixmobile.core.CoreApp
import org.kiwix.kiwixmobile.core.Intents
import org.kiwix.kiwixmobile.core.R.string
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
import org.kiwix.kiwixmobile.core.utils.files.Log import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import java.util.concurrent.TimeUnit import org.kiwix.kiwixmobile.core.utils.DOWNLOAD_NOTIFICATION_CHANNEL_ID
import javax.inject.Inject import javax.inject.Inject
class DownloadMonitorService : Service() { class DownloadMonitorService : Service() {
private val updater = PublishSubject.create<() -> Unit>() private val updater = PublishSubject.create<() -> Unit>()
private var updaterDisposable: Disposable? = null private var updaterDisposable: Disposable? = null
private var monitoringDisposable: Disposable? = null private var monitoringDisposable: Disposable? = null
private val lock = Any() private val notificationManager: NotificationManager by lazy {
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
}
private val downloadNotificationsBuilderMap = mutableMapOf<Int, NotificationCompat.Builder>()
@Inject @Inject
lateinit var fetch: Fetch lateinit var fetch: Fetch
@ -60,42 +74,10 @@ class DownloadMonitorService : Service() {
.inject(this) .inject(this)
super.onCreate() super.onCreate()
setupUpdater() setupUpdater()
startMonitoringDownloads()
fetch.addListener(fetchListener, true) fetch.addListener(fetchListener, true)
setForegroundNotification() setForegroundNotification()
} }
/**
* Periodically checks if there are active downloads.
* If no downloads are active, it stops the foreground service.
*/
private fun startMonitoringDownloads() {
// Check if monitoring is already active. If it is, do nothing.
if (monitoringDisposable?.isDisposed == false) return
monitoringDisposable = Observable.interval(ZERO.toLong(), FIVE.toLong(), TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(
{
try {
synchronized(lock) {
fetch.hasActiveDownloads(includeAddedDownloads = true) {
if (!it) {
stopForegroundServiceForDownloads()
}
}
}
} catch (ignore: Exception) {
Log.e(
"DOWNLOAD_MONITOR",
"Couldn't get the downloads update. Original exception = $ignore"
)
}
},
Throwable::printStackTrace
)
}
private fun setupUpdater() { private fun setupUpdater() {
updaterDisposable = updater.subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).subscribe( updaterDisposable = updater.subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).subscribe(
{ it.invoke() }, { it.invoke() },
@ -203,8 +185,19 @@ class DownloadMonitorService : Service() {
update(download) update(download)
} }
private fun update(download: Download, shouldSetForegroundNotification: Boolean = false) { private fun update(
updater.onNext { downloadRoomDao.update(download) }.also { download: Download,
shouldSetForegroundNotification: Boolean = false
) {
updater.onNext {
downloadRoomDao.update(download)
if (download.status == Status.COMPLETED) {
downloadRoomDao.getEntityForDownloadId(download.id.toLong())?.let {
showDownloadCompletedNotification(download)
// to move these downloads in NewBookDao.
downloadRoomDao.downloads().blockingFirst()
}
}
if (shouldSetForegroundNotification) { if (shouldSetForegroundNotification) {
setForegroundNotification() setForegroundNotification()
} }
@ -212,12 +205,91 @@ class DownloadMonitorService : Service() {
} }
private fun delete(download: Download) { private fun delete(download: Download) {
updater.onNext { downloadRoomDao.delete(download) }.also { updater.onNext {
downloadRoomDao.delete(download)
setForegroundNotification() setForegroundNotification()
} }
} }
} }
private fun showDownloadCompletedNotification(download: Download) {
downloadNotificationChannel()
val notificationBuilder = getNotificationBuilder(download.id)
val notificationTitle =
downloadRoomDao.getEntityForFileName(getDownloadNotificationTitle(download))?.title
?: download.file
notificationBuilder.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setSmallIcon(android.R.drawable.stat_sys_download_done)
.setContentTitle(notificationTitle)
.setContentText(getString(R.string.fetch_notification_download_complete))
.setOngoing(false)
.setGroup(download.id.toString())
.setGroupSummary(false)
.setProgress(ZERO, ZERO, false)
.setTimeoutAfter(DEFAULT_NOTIFICATION_TIMEOUT_AFTER_RESET)
.setContentIntent(getPendingIntentForDownloadedNotification(download))
.setAutoCancel(true)
notificationManager.notify(download.id, notificationBuilder.build())
}
private fun getPendingIntentForDownloadedNotification(download: Download): PendingIntent {
val internal = Intents.internal(CoreMainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
putExtra(DOWNLOAD_NOTIFICATION_TITLE, getDownloadNotificationTitle(download))
}
return PendingIntent.getActivity(
this,
download.id,
internal,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
}
private fun downloadNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (notificationManager.getNotificationChannel(DOWNLOAD_NOTIFICATION_CHANNEL_ID) == null) {
notificationManager.createNotificationChannel(createChannel())
}
}
}
@RequiresApi(Build.VERSION_CODES.O)
private fun createChannel() =
NotificationChannel(
DOWNLOAD_NOTIFICATION_CHANNEL_ID,
getString(string.download_notification_channel_name),
NotificationManager.IMPORTANCE_HIGH
).apply {
setSound(null, null)
enableVibration(false)
}
@SuppressLint("RestrictedApi")
private fun getNotificationBuilder(notificationId: Int): NotificationCompat.Builder {
synchronized(downloadNotificationsBuilderMap) {
val notificationBuilder = downloadNotificationsBuilderMap[notificationId]
?: NotificationCompat.Builder(this, DOWNLOAD_NOTIFICATION_CHANNEL_ID)
downloadNotificationsBuilderMap[notificationId] = notificationBuilder
notificationBuilder
.setGroup("$notificationId")
.setStyle(null)
.setProgress(ZERO, ZERO, false)
.setContentTitle(null)
.setContentText(null)
.setContentIntent(null)
.setGroupSummary(false)
.setTimeoutAfter(DEFAULT_NOTIFICATION_TIMEOUT_AFTER_RESET)
.setOngoing(false)
.setOnlyAlertOnce(true)
.setSmallIcon(android.R.drawable.stat_sys_download_done)
.mActions.clear()
return@getNotificationBuilder notificationBuilder
}
}
private fun getDownloadNotificationTitle(download: Download): String =
fetchDownloadNotificationManager.getDownloadNotificationTitle(download)
/** /**
* Stops the foreground service, disposes of resources, and removes the Fetch listener. * Stops the foreground service, disposes of resources, and removes the Fetch listener.
*/ */
@ -225,7 +297,7 @@ class DownloadMonitorService : Service() {
monitoringDisposable?.dispose() monitoringDisposable?.dispose()
updaterDisposable?.dispose() updaterDisposable?.dispose()
fetch.removeListener(fetchListener) fetch.removeListener(fetchListener)
stopForeground(STOP_FOREGROUND_REMOVE) stopForeground(STOP_FOREGROUND_DETACH)
stopSelf() stopSelf()
} }

View File

@ -39,6 +39,7 @@ const val EXTRA_IS_WIDGET_VOICE = "isWidgetVoice"
const val HOTSPOT_SERVICE_CHANNEL_ID = "hotspotService" const val HOTSPOT_SERVICE_CHANNEL_ID = "hotspotService"
const val OLD_PROVIDER_DOMAIN = "org.kiwix.zim.base" const val OLD_PROVIDER_DOMAIN = "org.kiwix.zim.base"
const val READ_ALOUD_SERVICE_CHANNEL_ID = "readAloudService" const val READ_ALOUD_SERVICE_CHANNEL_ID = "readAloudService"
const val DOWNLOAD_NOTIFICATION_CHANNEL_ID = "kiwixDownloadNotificationChannel"
// For Storage select dialog // For Storage select dialog
const val INTERNAL_SELECT_POSITION = 0 const val INTERNAL_SELECT_POSITION = 0