mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-12 00:48:29 -04:00
Fixed: the download notification controls was not working.
* Refactored/cleanup code. * Added foreground permission in core module so that it can be used in both modules.
This commit is contained in:
parent
1f62caebbe
commit
da9528b8ff
@ -8,9 +8,6 @@
|
|||||||
tools:ignore="CoarseFineLocation" />
|
tools:ignore="CoarseFineLocation" />
|
||||||
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
|
||||||
<uses-permission android:name="${permission}" />
|
<uses-permission android:name="${permission}" />
|
||||||
<!-- Device with versions >= Pie need this permission -->
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
|
||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.NEARBY_WIFI_DEVICES"
|
android:name="android.permission.NEARBY_WIFI_DEVICES"
|
||||||
android:usesPermissionFlags="neverForLocation"
|
android:usesPermissionFlags="neverForLocation"
|
||||||
|
@ -23,9 +23,7 @@ import android.app.Service
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.Provides
|
import dagger.Provides
|
||||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationActionsBroadcastReceiver
|
|
||||||
import org.kiwix.kiwixmobile.core.qr.GenerateQR
|
import org.kiwix.kiwixmobile.core.qr.GenerateQR
|
||||||
import org.kiwix.kiwixmobile.core.read_aloud.ReadAloudNotificationManger
|
|
||||||
import org.kiwix.kiwixmobile.di.ServiceScope
|
import org.kiwix.kiwixmobile.di.ServiceScope
|
||||||
import org.kiwix.kiwixmobile.webserver.KiwixServer
|
import org.kiwix.kiwixmobile.webserver.KiwixServer
|
||||||
import org.kiwix.kiwixmobile.webserver.WebServerHelper
|
import org.kiwix.kiwixmobile.webserver.WebServerHelper
|
||||||
@ -35,12 +33,6 @@ import org.kiwix.kiwixmobile.webserver.wifi_hotspot.IpAddressCallbacks
|
|||||||
|
|
||||||
@Module
|
@Module
|
||||||
class ServiceModule {
|
class ServiceModule {
|
||||||
@Provides
|
|
||||||
@ServiceScope
|
|
||||||
fun providesReadAloudNotificationManager(
|
|
||||||
notificationManager: NotificationManager,
|
|
||||||
context: Context
|
|
||||||
): ReadAloudNotificationManger = ReadAloudNotificationManger(notificationManager, context)
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@ServiceScope
|
@ServiceScope
|
||||||
@ -76,17 +68,4 @@ class ServiceModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@ServiceScope
|
@ServiceScope
|
||||||
fun providesGenerateQr(): GenerateQR = GenerateQR()
|
fun providesGenerateQr(): GenerateQR = GenerateQR()
|
||||||
|
|
||||||
@Provides
|
|
||||||
@ServiceScope
|
|
||||||
fun providesDownloadNotificationActionsBroadcastReceiverCallback(service: Service):
|
|
||||||
DownloadNotificationActionsBroadcastReceiver.Callback =
|
|
||||||
service as DownloadNotificationActionsBroadcastReceiver.Callback
|
|
||||||
|
|
||||||
@Provides
|
|
||||||
@ServiceScope
|
|
||||||
fun providesDownloadNotificationActionsBroadcastReceiver(
|
|
||||||
callBack: DownloadNotificationActionsBroadcastReceiver.Callback
|
|
||||||
): DownloadNotificationActionsBroadcastReceiver =
|
|
||||||
DownloadNotificationActionsBroadcastReceiver(callBack)
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,9 @@
|
|||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
|
android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
|
||||||
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
|
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
|
||||||
|
<!-- Device with versions >= Pie need this permission -->
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
|
||||||
<queries>
|
<queries>
|
||||||
<intent>
|
<intent>
|
||||||
<action android:name="android.intent.action.TTS_SERVICE" />
|
<action android:name="android.intent.action.TTS_SERVICE" />
|
||||||
|
@ -35,8 +35,8 @@ const val CONNECTION_TIMEOUT = 10L
|
|||||||
// increase the read and call timeout since the content is 19MB large so it takes
|
// increase the read and call timeout since the content is 19MB large so it takes
|
||||||
// more time to read on slow internet connection, and due to less read timeout
|
// more time to read on slow internet connection, and due to less read timeout
|
||||||
// the request is canceled.
|
// the request is canceled.
|
||||||
const val READ_TIMEOUT = 180L
|
const val READ_TIMEOUT = 300L
|
||||||
const val CALL_TIMEOUT = 180L
|
const val CALL_TIMEOUT = 300L
|
||||||
const val USER_AGENT = "kiwix-android-version:${BuildConfig.VERSION_CODE}"
|
const val USER_AGENT = "kiwix-android-version:${BuildConfig.VERSION_CODE}"
|
||||||
const val KIWIX_DOWNLOAD_URL = "https://mirror.download.kiwix.org/"
|
const val KIWIX_DOWNLOAD_URL = "https://mirror.download.kiwix.org/"
|
||||||
|
|
||||||
|
@ -19,94 +19,82 @@
|
|||||||
package org.kiwix.kiwixmobile.core.downloader.downloadManager
|
package org.kiwix.kiwixmobile.core.downloader.downloadManager
|
||||||
|
|
||||||
import android.app.DownloadManager
|
import android.app.DownloadManager
|
||||||
import android.content.ComponentName
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.ServiceConnection
|
|
||||||
import android.os.IBinder
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
|
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
|
||||||
|
import org.kiwix.kiwixmobile.core.dao.entities.DownloadRoomEntity
|
||||||
import org.kiwix.kiwixmobile.core.downloader.DownloadMonitor
|
import org.kiwix.kiwixmobile.core.downloader.DownloadMonitor
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.ACTION_CANCEL
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.ACTION_PAUSE
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.ACTION_QUERY_DOWNLOAD_STATUS
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.ACTION_RESUME
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
class DownloadManagerMonitor @Inject constructor(
|
class DownloadManagerMonitor @Inject constructor(
|
||||||
val downloadRoomDao: DownloadRoomDao,
|
val downloadRoomDao: DownloadRoomDao,
|
||||||
private val context: Context
|
private val context: Context
|
||||||
) : DownloadMonitor, DownloadManagerBroadcastReceiver.Callback, DownloadMonitorServiceCallback {
|
) : DownloadMonitor, DownloadManagerBroadcastReceiver.Callback {
|
||||||
private val lock = Any()
|
private val lock = Any()
|
||||||
private var downloadMonitorService: DownloadMonitorService? = null
|
|
||||||
private val serviceConnection = object : ServiceConnection {
|
|
||||||
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
|
|
||||||
downloadMonitorService =
|
|
||||||
(binder as? DownloadMonitorService.DownloadMonitorBinder)?.downloadMonitorService?.get()
|
|
||||||
downloadMonitorService?.registerCallback(this@DownloadManagerMonitor)
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
if (downloadRoomDao.downloads().blockingFirst().isNotEmpty()) {
|
|
||||||
startService()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onServiceDisconnected(name: ComponentName?) {
|
|
||||||
downloadMonitorService = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
bindService()
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
if (getActiveDownloads().isNotEmpty()) {
|
||||||
|
startService()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun bindService() {
|
private suspend fun getActiveDownloads(): List<DownloadRoomEntity> =
|
||||||
val serviceIntent = Intent(context, DownloadMonitorService::class.java)
|
downloadRoomDao.downloadRoomEntity().blockingFirst().filter {
|
||||||
context.bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE)
|
it.status != Status.PAUSED && it.status != Status.CANCELLED
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun downloadCompleteOrCancelled(intent: Intent) {
|
override fun downloadCompleteOrCancelled(intent: Intent) {
|
||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
intent.extras?.let {
|
intent.extras?.let {
|
||||||
val downloadId = it.getLong(DownloadManager.EXTRA_DOWNLOAD_ID, -1L)
|
val downloadId = it.getLong(DownloadManager.EXTRA_DOWNLOAD_ID, -1L)
|
||||||
if (downloadId != -1L) {
|
if (downloadId != -1L) {
|
||||||
downloadMonitorService?.queryDownloadStatus(downloadId)
|
context.startService(
|
||||||
|
getDownloadMonitorIntent(
|
||||||
|
ACTION_QUERY_DOWNLOAD_STATUS,
|
||||||
|
downloadId.toInt()
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun startMonitoringDownloads() {
|
fun startMonitoringDownloads() {
|
||||||
bindService()
|
|
||||||
startService()
|
startService()
|
||||||
downloadMonitorService?.startMonitoringDownloads()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startService() {
|
private fun startService() {
|
||||||
ContextCompat.startForegroundService(
|
context.startService(Intent(context, DownloadMonitorService::class.java))
|
||||||
context,
|
|
||||||
Intent(context, DownloadMonitorService::class.java)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun pauseDownload(downloadId: Long) {
|
fun pauseDownload(downloadId: Long) {
|
||||||
downloadMonitorService?.pauseDownload(downloadId)
|
context.startService(getDownloadMonitorIntent(ACTION_PAUSE, downloadId.toInt()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resumeDownload(downloadId: Long) {
|
fun resumeDownload(downloadId: Long) {
|
||||||
downloadMonitorService?.resumeDownload(downloadId)
|
context.startService(getDownloadMonitorIntent(ACTION_RESUME, downloadId.toInt()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelDownload(downloadId: Long) {
|
fun cancelDownload(downloadId: Long) {
|
||||||
downloadMonitorService?.cancelDownload(downloadId)
|
context.startService(getDownloadMonitorIntent(ACTION_CANCEL, downloadId.toInt()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getDownloadMonitorIntent(action: String, downloadId: Int): Intent =
|
||||||
|
Intent(context, DownloadMonitorService::class.java).apply {
|
||||||
|
putExtra(DownloadNotificationManager.NOTIFICATION_ACTION, action)
|
||||||
|
putExtra(DownloadNotificationManager.EXTRA_DOWNLOAD_ID, downloadId)
|
||||||
|
}
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
// empty method to so class does not get reported unused
|
// empty method to so class does not get reported unused
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onServiceDestroyed() {
|
|
||||||
downloadMonitorService?.registerCallback(null)
|
|
||||||
context.unbindService(serviceConnection)
|
|
||||||
downloadMonitorService = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -41,12 +41,7 @@ class DownloadManagerRequester @Inject constructor(
|
|||||||
private val downloadManagerMonitor: DownloadManagerMonitor
|
private val downloadManagerMonitor: DownloadManagerMonitor
|
||||||
) : DownloadRequester {
|
) : DownloadRequester {
|
||||||
override fun enqueue(downloadRequest: DownloadRequest): Long =
|
override fun enqueue(downloadRequest: DownloadRequest): Long =
|
||||||
downloadManager.enqueue(downloadRequest.toDownloadManagerRequest(sharedPreferenceUtil)).also {
|
downloadManager.enqueue(downloadRequest.toDownloadManagerRequest(sharedPreferenceUtil))
|
||||||
Log.e(
|
|
||||||
"DOWNLOADING_STEP",
|
|
||||||
"enqueue: ${downloadRequest.toDownloadManagerRequest(sharedPreferenceUtil)}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDownloadAdded() {
|
override fun onDownloadAdded() {
|
||||||
// Start monitoring downloads after enqueuing a new download request.
|
// Start monitoring downloads after enqueuing a new download request.
|
||||||
|
@ -26,7 +26,6 @@ import android.content.ContentValues
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Binder
|
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
@ -35,10 +34,13 @@ import io.reactivex.subjects.PublishSubject
|
|||||||
import org.kiwix.kiwixmobile.core.CoreApp
|
import org.kiwix.kiwixmobile.core.CoreApp
|
||||||
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
|
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
|
||||||
import org.kiwix.kiwixmobile.core.dao.entities.DownloadRoomEntity
|
import org.kiwix.kiwixmobile.core.dao.entities.DownloadRoomEntity
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.ACTION_CANCEL
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.ACTION_PAUSE
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.ACTION_QUERY_DOWNLOAD_STATUS
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.ACTION_RESUME
|
||||||
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
|
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
|
||||||
import org.kiwix.kiwixmobile.core.downloader.model.DownloadState
|
import org.kiwix.kiwixmobile.core.downloader.model.DownloadState
|
||||||
import org.kiwix.kiwixmobile.core.utils.files.Log
|
import org.kiwix.kiwixmobile.core.utils.files.Log
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -59,7 +61,7 @@ const val STATUS_PAUSED_BY_APP = 193
|
|||||||
const val COLUMN_CONTROL = "control"
|
const val COLUMN_CONTROL = "control"
|
||||||
val downloadBaseUri: Uri = Uri.parse("content://downloads/my_downloads")
|
val downloadBaseUri: Uri = Uri.parse("content://downloads/my_downloads")
|
||||||
|
|
||||||
class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastReceiver.Callback {
|
class DownloadMonitorService : Service() {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var downloadManager: DownloadManager
|
lateinit var downloadManager: DownloadManager
|
||||||
@ -73,19 +75,9 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
private var monitoringDisposable: Disposable? = null
|
private var monitoringDisposable: Disposable? = null
|
||||||
private val downloadInfoMap = mutableMapOf<Long, DownloadInfo>()
|
private val downloadInfoMap = mutableMapOf<Long, DownloadInfo>()
|
||||||
private val updater = PublishSubject.create<() -> Unit>()
|
private val updater = PublishSubject.create<() -> Unit>()
|
||||||
private val downloadMonitorBinder: IBinder = DownloadMonitorBinder(this)
|
private var foreGroundServiceInformation: Pair<Boolean, Int> = true to DEFAULT_INT_VALUE
|
||||||
private var downloadMonitorServiceCallback: DownloadMonitorServiceCallback? = null
|
|
||||||
private var isForeGroundServiceNotification: Boolean = true
|
|
||||||
|
|
||||||
// @set:Inject
|
override fun onBind(intent: Intent?): IBinder? = null
|
||||||
// var downloadNotificationBroadcastReceiver: DownloadNotificationActionsBroadcastReceiver? = null
|
|
||||||
|
|
||||||
class DownloadMonitorBinder(downloadMonitorService: DownloadMonitorService) : Binder() {
|
|
||||||
val downloadMonitorService: WeakReference<DownloadMonitorService> =
|
|
||||||
WeakReference<DownloadMonitorService>(downloadMonitorService)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder = downloadMonitorBinder
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
CoreApp.coreComponent
|
CoreApp.coreComponent
|
||||||
@ -96,13 +88,22 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
super.onCreate()
|
super.onCreate()
|
||||||
setupUpdater()
|
setupUpdater()
|
||||||
startMonitoringDownloads()
|
startMonitoringDownloads()
|
||||||
// downloadNotificationBroadcastReceiver?.let(this::registerReceiver)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int = START_NOT_STICKY
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
val downloadId =
|
||||||
fun registerCallback(downloadMonitorServiceCallback: DownloadMonitorServiceCallback?) {
|
intent?.getIntExtra(DownloadNotificationManager.EXTRA_DOWNLOAD_ID, DEFAULT_INT_VALUE)
|
||||||
this.downloadMonitorServiceCallback = downloadMonitorServiceCallback
|
?: DEFAULT_INT_VALUE
|
||||||
|
val notificationAction = intent?.getStringExtra(DownloadNotificationManager.NOTIFICATION_ACTION)
|
||||||
|
if (downloadId != DEFAULT_INT_VALUE) {
|
||||||
|
when (notificationAction) {
|
||||||
|
ACTION_PAUSE -> pauseDownload(downloadId.toLong())
|
||||||
|
ACTION_RESUME -> resumeDownload(downloadId.toLong())
|
||||||
|
ACTION_CANCEL -> cancelDownload(downloadId.toLong())
|
||||||
|
ACTION_QUERY_DOWNLOAD_STATUS -> queryDownloadStatus(downloadId.toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return START_NOT_STICKY
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("CheckResult")
|
@Suppress("CheckResult")
|
||||||
@ -122,8 +123,7 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
* when there are no ongoing downloads to avoid unnecessary resource usage.
|
* when there are no ongoing downloads to avoid unnecessary resource usage.
|
||||||
*/
|
*/
|
||||||
@Suppress("MagicNumber")
|
@Suppress("MagicNumber")
|
||||||
fun startMonitoringDownloads() {
|
private fun startMonitoringDownloads() {
|
||||||
Log.e("DOWNLOADING_STEP", "startMonitoringDownloads:")
|
|
||||||
// Check if monitoring is already active. If it is, do nothing.
|
// Check if monitoring is already active. If it is, do nothing.
|
||||||
if (monitoringDisposable?.isDisposed == false) return
|
if (monitoringDisposable?.isDisposed == false) return
|
||||||
monitoringDisposable = Observable.interval(ZERO.toLong(), 5, TimeUnit.SECONDS)
|
monitoringDisposable = Observable.interval(ZERO.toLong(), 5, TimeUnit.SECONDS)
|
||||||
@ -142,7 +142,7 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ignore: Exception) {
|
} catch (ignore: Exception) {
|
||||||
Log.i(
|
Log.e(
|
||||||
"DOWNLOAD_MONITOR",
|
"DOWNLOAD_MONITOR",
|
||||||
"Couldn't get the downloads update. Original exception = $ignore"
|
"Couldn't get the downloads update. Original exception = $ignore"
|
||||||
)
|
)
|
||||||
@ -155,7 +155,6 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
@SuppressLint("Range")
|
@SuppressLint("Range")
|
||||||
private fun checkDownloads() {
|
private fun checkDownloads() {
|
||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
Log.e("DOWNLOADING_STEP", "checkDownloads: lock")
|
|
||||||
val query = DownloadManager.Query().setFilterByStatus(
|
val query = DownloadManager.Query().setFilterByStatus(
|
||||||
DownloadManager.STATUS_RUNNING or
|
DownloadManager.STATUS_RUNNING or
|
||||||
DownloadManager.STATUS_PAUSED or
|
DownloadManager.STATUS_PAUSED or
|
||||||
@ -163,7 +162,6 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
DownloadManager.STATUS_SUCCESSFUL
|
DownloadManager.STATUS_SUCCESSFUL
|
||||||
)
|
)
|
||||||
downloadManager.query(query).use { cursor ->
|
downloadManager.query(query).use { cursor ->
|
||||||
Log.e("DOWNLOADING_STEP", "checkDownloads:")
|
|
||||||
if (cursor.moveToFirst()) {
|
if (cursor.moveToFirst()) {
|
||||||
do {
|
do {
|
||||||
val downloadId = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID))
|
val downloadId = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_ID))
|
||||||
@ -385,10 +383,8 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
) {
|
) {
|
||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
updater.onNext {
|
updater.onNext {
|
||||||
Log.e("DOWNLOADING_STEP", "updateDownloadStatus:")
|
|
||||||
downloadRoomDao.getEntityForDownloadId(downloadId)?.let { downloadEntity ->
|
downloadRoomDao.getEntityForDownloadId(downloadId)?.let { downloadEntity ->
|
||||||
if (shouldUpdateDownloadStatus(downloadEntity)) {
|
if (shouldUpdateDownloadStatus(downloadEntity)) {
|
||||||
Log.e("DOWNLOADING_STEP", "shouldUpdateDownloadStatus:")
|
|
||||||
val downloadModel = DownloadModel(downloadEntity).apply {
|
val downloadModel = DownloadModel(downloadEntity).apply {
|
||||||
if (shouldUpdateDownloadStatus(status, error, downloadEntity)) {
|
if (shouldUpdateDownloadStatus(status, error, downloadEntity)) {
|
||||||
state = status
|
state = status
|
||||||
@ -406,7 +402,6 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
downloadRoomDao.update(downloadModel)
|
downloadRoomDao.update(downloadModel)
|
||||||
Log.e("DOWNLOADING_STEP", "updateNotification: $downloadModel")
|
|
||||||
updateNotification(downloadModel, downloadEntity.title, downloadEntity.description)
|
updateNotification(downloadModel, downloadEntity.title, downloadEntity.description)
|
||||||
return@let
|
return@let
|
||||||
}
|
}
|
||||||
@ -472,14 +467,63 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
|
|
||||||
private fun cancelNotification(downloadId: Long) {
|
private fun cancelNotification(downloadId: Long) {
|
||||||
downloadNotificationManager.cancelNotification(downloadId.toInt())
|
downloadNotificationManager.cancelNotification(downloadId.toInt())
|
||||||
|
assignNewForegroundNotification()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun assignNewForegroundNotification() {
|
||||||
|
val activeDownloads = getActiveDownloads()
|
||||||
|
if (activeDownloads.isNotEmpty()) {
|
||||||
|
// Promote the first active download to foreground
|
||||||
|
val downloadRoomEntity = activeDownloads.first()
|
||||||
|
foreGroundServiceInformation =
|
||||||
|
foreGroundServiceInformation.first to downloadRoomEntity.downloadId.toInt()
|
||||||
|
val downloadNotificationModel =
|
||||||
|
getDownloadNotificationModel(
|
||||||
|
DownloadModel(downloadRoomEntity),
|
||||||
|
downloadRoomEntity.title,
|
||||||
|
downloadRoomEntity.description
|
||||||
|
)
|
||||||
|
val notification = downloadNotificationManager.createNotification(downloadNotificationModel)
|
||||||
|
startForeground(foreGroundServiceInformation.second, notification)
|
||||||
|
} else {
|
||||||
|
// Stop the service if no active downloads remain
|
||||||
|
stopMonitoringDownloads()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getActiveDownloads(): List<DownloadRoomEntity> =
|
||||||
|
downloadRoomDao.downloadRoomEntity().blockingFirst().filter {
|
||||||
|
it.status != Status.PAUSED && it.status != Status.CANCELLED
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateNotification(
|
private fun updateNotification(
|
||||||
downloadModel: DownloadModel,
|
downloadModel: DownloadModel,
|
||||||
title: String,
|
title: String,
|
||||||
description: String?
|
description: String?
|
||||||
) {
|
) {
|
||||||
val downloadNotificationModel = DownloadNotificationModel(
|
val downloadNotificationModel = getDownloadNotificationModel(downloadModel, title, description)
|
||||||
|
val notification = downloadNotificationManager.createNotification(downloadNotificationModel)
|
||||||
|
if (foreGroundServiceInformation.first) {
|
||||||
|
startForeground(downloadModel.downloadId.toInt(), notification)
|
||||||
|
foreGroundServiceInformation = false to downloadModel.downloadId.toInt()
|
||||||
|
} else {
|
||||||
|
downloadNotificationManager.updateNotification(
|
||||||
|
downloadNotificationModel,
|
||||||
|
object : AssignNewForegroundServiceNotification {
|
||||||
|
override fun assignNewForegroundServiceNotification(downloadId: Long) {
|
||||||
|
cancelNotification(downloadId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDownloadNotificationModel(
|
||||||
|
downloadModel: DownloadModel,
|
||||||
|
title: String,
|
||||||
|
description: String?
|
||||||
|
): DownloadNotificationModel =
|
||||||
|
DownloadNotificationModel(
|
||||||
downloadId = downloadModel.downloadId.toInt(),
|
downloadId = downloadModel.downloadId.toInt(),
|
||||||
status = downloadModel.state,
|
status = downloadModel.state,
|
||||||
progress = downloadModel.progress,
|
progress = downloadModel.progress,
|
||||||
@ -493,28 +537,8 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
downloadModel.book.url
|
downloadModel.book.url
|
||||||
).toReadableState(this).toString()
|
).toReadableState(this).toString()
|
||||||
)
|
)
|
||||||
val notification = downloadNotificationManager.createNotification(downloadNotificationModel)
|
|
||||||
if (isForeGroundServiceNotification) {
|
|
||||||
startForeground(downloadModel.downloadId.toInt(), notification)
|
|
||||||
isForeGroundServiceNotification = false
|
|
||||||
} else {
|
|
||||||
downloadNotificationManager.updateNotification(downloadNotificationModel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun pauseDownloads(downloadId: Long) {
|
private fun pauseDownload(downloadId: Long) {
|
||||||
pauseDownload(downloadId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun resumeDownloads(downloadId: Long) {
|
|
||||||
resumeDownload(downloadId)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun cancelDownloads(downloadId: Long) {
|
|
||||||
cancelNotification(downloadId)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun pauseDownload(downloadId: Long) {
|
|
||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
updater.onNext {
|
updater.onNext {
|
||||||
if (pauseResumeDownloadInDownloadManagerContentResolver(
|
if (pauseResumeDownloadInDownloadManagerContentResolver(
|
||||||
@ -529,7 +553,7 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resumeDownload(downloadId: Long) {
|
private fun resumeDownload(downloadId: Long) {
|
||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
updater.onNext {
|
updater.onNext {
|
||||||
if (pauseResumeDownloadInDownloadManagerContentResolver(
|
if (pauseResumeDownloadInDownloadManagerContentResolver(
|
||||||
@ -544,7 +568,7 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelDownload(downloadId: Long) {
|
private fun cancelDownload(downloadId: Long) {
|
||||||
synchronized(lock) {
|
synchronized(lock) {
|
||||||
downloadManager.remove(downloadId)
|
downloadManager.remove(downloadId)
|
||||||
handleCancelledDownload(downloadId)
|
handleCancelledDownload(downloadId)
|
||||||
@ -577,27 +601,23 @@ class DownloadMonitorService : Service(), DownloadNotificationActionsBroadcastRe
|
|||||||
downloadRoomEntity.status != Status.COMPLETED
|
downloadRoomEntity.status != Status.COMPLETED
|
||||||
|
|
||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
// downloadNotificationBroadcastReceiver?.let(::unregisterReceiver)
|
|
||||||
monitoringDisposable?.dispose()
|
monitoringDisposable?.dispose()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun stopMonitoringDownloads() {
|
private fun stopMonitoringDownloads() {
|
||||||
Log.e("DOWNLOADING_STEP", "stopMonitoringDownloads: ")
|
foreGroundServiceInformation = true to DEFAULT_INT_VALUE
|
||||||
isForeGroundServiceNotification = true
|
|
||||||
monitoringDisposable?.dispose()
|
monitoringDisposable?.dispose()
|
||||||
downloadMonitorServiceCallback?.onServiceDestroyed()
|
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val CHANNEL_ID = "download_monitor_channel"
|
|
||||||
private const val ONGOING_NOTIFICATION_ID = 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class DownloadInfo(
|
data class DownloadInfo(
|
||||||
var startTime: Long,
|
var startTime: Long,
|
||||||
var initialBytesDownloaded: Int
|
var initialBytesDownloaded: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
interface AssignNewForegroundServiceNotification {
|
||||||
|
fun assignNewForegroundServiceNotification(downloadId: Long)
|
||||||
|
}
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
/*
|
|
||||||
* Kiwix Android
|
|
||||||
* Copyright (c) 2024 Kiwix <android.kiwix.org>
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.kiwix.kiwixmobile.core.downloader.downloadManager
|
|
||||||
|
|
||||||
interface DownloadMonitorServiceCallback {
|
|
||||||
fun onServiceDestroyed()
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* Kiwix Android
|
|
||||||
* Copyright (c) 2024 Kiwix <android.kiwix.org>
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.kiwix.kiwixmobile.core.downloader.downloadManager
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import org.kiwix.kiwixmobile.core.base.BaseBroadcastReceiver
|
|
||||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.ACTION_CANCEL
|
|
||||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.ACTION_PAUSE
|
|
||||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.ACTION_RESUME
|
|
||||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.EXTRA_DOWNLOAD_ID
|
|
||||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadNotificationManager.Companion.NOTIFICATION_ACTION
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
const val DOWNLOAD_NOTIFICATION_ACTION = "org.kiwix.kiwixmobile.download_notification_action"
|
|
||||||
|
|
||||||
class DownloadNotificationActionsBroadcastReceiver @Inject constructor(
|
|
||||||
private val callback: Callback
|
|
||||||
) :
|
|
||||||
BaseBroadcastReceiver() {
|
|
||||||
|
|
||||||
override val action: String = DOWNLOAD_NOTIFICATION_ACTION
|
|
||||||
override fun onIntentWithActionReceived(context: Context, intent: Intent) {
|
|
||||||
val downloadId = intent.getIntExtra(EXTRA_DOWNLOAD_ID, -1)
|
|
||||||
val notificationAction = intent.getStringExtra(NOTIFICATION_ACTION)
|
|
||||||
if (downloadId != -1) {
|
|
||||||
when (notificationAction) {
|
|
||||||
ACTION_PAUSE -> callback.pauseDownloads(downloadId.toLong())
|
|
||||||
ACTION_RESUME -> callback.resumeDownloads(downloadId.toLong())
|
|
||||||
ACTION_CANCEL -> callback.cancelDownloads(downloadId.toLong())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Callback {
|
|
||||||
fun pauseDownloads(downloadId: Long)
|
|
||||||
fun resumeDownloads(downloadId: Long)
|
|
||||||
fun cancelDownloads(downloadId: Long)
|
|
||||||
}
|
|
||||||
}
|
|
@ -54,7 +54,8 @@ class DownloadNotificationManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun updateNotification(
|
fun updateNotification(
|
||||||
downloadNotificationModel: DownloadNotificationModel
|
downloadNotificationModel: DownloadNotificationModel,
|
||||||
|
assignNewForegroundServiceNotification: AssignNewForegroundServiceNotification
|
||||||
) {
|
) {
|
||||||
synchronized(downloadNotificationsBuilderMap) {
|
synchronized(downloadNotificationsBuilderMap) {
|
||||||
if (shouldUpdateNotification(downloadNotificationModel)) {
|
if (shouldUpdateNotification(downloadNotificationModel)) {
|
||||||
@ -64,7 +65,9 @@ class DownloadNotificationManager @Inject constructor(
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// the download is cancelled/paused so remove the notification.
|
// the download is cancelled/paused so remove the notification.
|
||||||
cancelNotification(downloadNotificationModel.downloadId)
|
assignNewForegroundServiceNotification.assignNewForegroundServiceNotification(
|
||||||
|
downloadNotificationModel.downloadId.toLong()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -191,16 +194,16 @@ class DownloadNotificationManager @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getActionPendingIntent(action: String, downloadId: Int): PendingIntent {
|
private fun getActionPendingIntent(action: String, downloadId: Int): PendingIntent {
|
||||||
val intent =
|
val pendingIntent =
|
||||||
Intent(DOWNLOAD_NOTIFICATION_ACTION).apply {
|
Intent(context, DownloadMonitorService::class.java).apply {
|
||||||
putExtra(NOTIFICATION_ACTION, action)
|
putExtra(NOTIFICATION_ACTION, action)
|
||||||
putExtra(EXTRA_DOWNLOAD_ID, downloadId)
|
putExtra(EXTRA_DOWNLOAD_ID, downloadId)
|
||||||
}
|
}
|
||||||
val requestCode = downloadId + action.hashCode()
|
val requestCode = downloadId + action.hashCode()
|
||||||
return PendingIntent.getBroadcast(
|
return PendingIntent.getService(
|
||||||
context,
|
context,
|
||||||
requestCode,
|
requestCode,
|
||||||
intent,
|
pendingIntent,
|
||||||
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -250,6 +253,7 @@ class DownloadNotificationManager @Inject constructor(
|
|||||||
const val ACTION_PAUSE = "action_pause"
|
const val ACTION_PAUSE = "action_pause"
|
||||||
const val ACTION_RESUME = "action_resume"
|
const val ACTION_RESUME = "action_resume"
|
||||||
const val ACTION_CANCEL = "action_cancel"
|
const val ACTION_CANCEL = "action_cancel"
|
||||||
|
const val ACTION_QUERY_DOWNLOAD_STATUS = "action_query_download_status"
|
||||||
const val EXTRA_DOWNLOAD_ID = "extra_download_id"
|
const val EXTRA_DOWNLOAD_ID = "extra_download_id"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user