From a48f90422a70a3f73cf9e32ec7465d02f42e6962 Mon Sep 17 00:00:00 2001 From: MohitMaliFtechiz Date: Tue, 24 Dec 2024 16:36:19 +0530 Subject: [PATCH] Fixed: The ZIM file was not opening when clicking on the download notification while the application was not running in the background. --- .../downloadManager/DownloadMonitorService.kt | 152 +++++++++++++----- .../kiwix/kiwixmobile/core/utils/Constants.kt | 1 + 2 files changed, 113 insertions(+), 40 deletions(-) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/downloadManager/DownloadMonitorService.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/downloadManager/DownloadMonitorService.kt index 2524aac65..b7749d3b0 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/downloadManager/DownloadMonitorService.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/downloadManager/DownloadMonitorService.kt @@ -18,30 +18,44 @@ 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.content.Context import android.content.Intent +import android.os.Build import android.os.IBinder +import androidx.annotation.RequiresApi +import androidx.core.app.NotificationCompat import com.tonyodev.fetch2.Download import com.tonyodev.fetch2.Error import com.tonyodev.fetch2.Fetch import com.tonyodev.fetch2.FetchListener +import com.tonyodev.fetch2.R import com.tonyodev.fetch2.Status +import com.tonyodev.fetch2.util.DEFAULT_NOTIFICATION_TIMEOUT_AFTER_RESET import com.tonyodev.fetch2core.DownloadBlock -import io.reactivex.Observable import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import io.reactivex.subjects.PublishSubject 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.utils.files.Log -import java.util.concurrent.TimeUnit +import org.kiwix.kiwixmobile.core.main.CoreMainActivity +import org.kiwix.kiwixmobile.core.utils.DOWNLOAD_NOTIFICATION_CHANNEL_ID import javax.inject.Inject class DownloadMonitorService : Service() { private val updater = PublishSubject.create<() -> Unit>() private var updaterDisposable: 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() @Inject lateinit var fetch: Fetch @@ -60,42 +74,10 @@ class DownloadMonitorService : Service() { .inject(this) super.onCreate() setupUpdater() - startMonitoringDownloads() fetch.addListener(fetchListener, true) 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() { updaterDisposable = updater.subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).subscribe( { it.invoke() }, @@ -203,8 +185,19 @@ class DownloadMonitorService : Service() { update(download) } - private fun update(download: Download, shouldSetForegroundNotification: Boolean = false) { - updater.onNext { downloadRoomDao.update(download) }.also { + private fun update( + 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) { setForegroundNotification() } @@ -212,12 +205,91 @@ class DownloadMonitorService : Service() { } private fun delete(download: Download) { - updater.onNext { downloadRoomDao.delete(download) }.also { + updater.onNext { + downloadRoomDao.delete(download) 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. */ @@ -225,7 +297,7 @@ class DownloadMonitorService : Service() { monitoringDisposable?.dispose() updaterDisposable?.dispose() fetch.removeListener(fetchListener) - stopForeground(STOP_FOREGROUND_REMOVE) + stopForeground(STOP_FOREGROUND_DETACH) stopSelf() } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/Constants.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/Constants.kt index 09a710ab2..b1b0361be 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/Constants.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/Constants.kt @@ -39,6 +39,7 @@ const val EXTRA_IS_WIDGET_VOICE = "isWidgetVoice" const val HOTSPOT_SERVICE_CHANNEL_ID = "hotspotService" const val OLD_PROVIDER_DOMAIN = "org.kiwix.zim.base" const val READ_ALOUD_SERVICE_CHANNEL_ID = "readAloudService" +const val DOWNLOAD_NOTIFICATION_CHANNEL_ID = "kiwixDownloadNotificationChannel" // For Storage select dialog const val INTERNAL_SELECT_POSITION = 0