Fixed: Downloading starts very slowly on Android 14+.

* Upgraded Fetch to 3.4.1.
* Upgraded Kotlin kotlin-stdlib from JDK7 to JDK8.
* Upgraded Kotlin version to 2.0.0 to support the latest Fetch library.
* Upgraded Dagger to 2.53.1.
* Upgraded ObjectBox to 4.0.3.
* Notification now shows "Pause" and "Resume" buttons instead of "pause" and "resume".
* Fixed: Notification buttons were not working on Android 13 and above.
* Fixed: Downloading was not working in the background. Added a foreground service for Fetch so that downloading will continue in the background.
This commit is contained in:
MohitMaliFtechiz 2024-12-23 18:46:32 +05:30
parent 0f16730120
commit 2e63d70e6c
19 changed files with 262 additions and 118 deletions

View File

@ -12,7 +12,7 @@ repositories {
dependencies {
implementation("com.android.tools.build:gradle:8.1.3")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:2.0.0")
implementation("org.jacoco:org.jacoco.core:0.8.12")
implementation("org.jlleitschuh.gradle:ktlint-gradle:10.3.0")
implementation("com.google.apis:google-api-services-androidpublisher:v3-rev20230406-2.0.0") {

View File

@ -101,7 +101,7 @@ object Libs {
/**
* https://kotlinlang.org/
*/
const val kotlin_stdlib_jdk7: String = "org.jetbrains.kotlin:kotlin-stdlib-jdk7:" +
const val kotlin_stdlib_jdk8: String = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:" +
Versions.org_jetbrains_kotlin
/**

View File

@ -26,13 +26,13 @@ object Versions {
const val com_squareup_okhttp3: String = "4.12.0"
const val org_jetbrains_kotlin: String = "1.9.20"
const val org_jetbrains_kotlin: String = "2.0.0"
const val androidx_navigation: String = "2.5.3"
const val navigation_ui_ktx: String = "2.4.1"
const val com_google_dagger: String = "2.48.1"
const val com_google_dagger: String = "2.53.1"
const val androidx_test: String = "1.6.1"
@ -40,7 +40,7 @@ object Versions {
const val androidx_test_orchestrator: String = "1.5.0"
const val io_objectbox: String = "3.5.0"
const val io_objectbox: String = "4.0.3"
const val io_mockk: String = "1.13.13"
@ -110,7 +110,7 @@ object Versions {
const val keeper = "0.16.1"
const val fetch: String = "3.3.0"
const val fetch: String = "3.4.1"
}
/**

View File

@ -28,6 +28,7 @@ import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.dependencies
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jlleitschuh.gradle.ktlint.KtlintExtension
@ -75,7 +76,10 @@ class AllProjectConfigurer {
targetCompatibility = Config.javaVersion
}
target.tasks.withType(KotlinCompile::class.java) {
kotlinOptions.jvmTarget = "1.8"
compilerOptions {
jvmTarget.set(JvmTarget.JVM_1_8)
freeCompilerArgs.add("-Xjvm-default=all-compatibility")
}
}
buildFeatures.apply {
viewBinding = true
@ -191,7 +195,7 @@ class AllProjectConfigurer {
fun configureDependencies(target: Project) {
target.dependencies {
implementation(Libs.kotlin_stdlib_jdk7)
implementation(Libs.kotlin_stdlib_jdk8)
implementation(Libs.appcompat)
implementation(Libs.material)
implementation(Libs.constraintlayout)
@ -227,7 +231,6 @@ class AllProjectConfigurer {
implementation(Libs.roomRxjava2)
kapt(Libs.roomCompiler)
implementation(Libs.tracing)
implementation(Libs.fetch)
implementation(Libs.fetchOkhttp)
}
}

View File

@ -63,4 +63,8 @@ dependencies {
implementation(Libs.kotlinx_coroutines_android)
implementation(Libs.kotlinx_coroutines_rx3)
implementation(Libs.zxing)
api(Libs.fetch) {
// Todo: Will remove this when we add support for Android 15
exclude("androidx.core", "core-ktx")
}
}

View File

@ -28,7 +28,6 @@ import androidx.multidex.MultiDex
import com.jakewharton.threetenabp.AndroidThreeTen
import org.kiwix.kiwixmobile.core.di.components.CoreComponent
import org.kiwix.kiwixmobile.core.di.components.DaggerCoreComponent
import org.kiwix.kiwixmobile.core.downloader.DownloadMonitor
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.utils.files.FileLogger
import javax.inject.Inject
@ -43,9 +42,6 @@ abstract class CoreApp : Application() {
lateinit var coreComponent: CoreComponent
}
@Inject
lateinit var downloadMonitor: DownloadMonitor
@Inject
lateinit var darkModeConfig: DarkModeConfig
@ -84,7 +80,6 @@ abstract class CoreApp : Application() {
AndroidThreeTen.init(this)
coreComponent.inject(this)
serviceWorkerInitialiser.init(this)
downloadMonitor.init()
darkModeConfig.init()
fileLogger.writeLogFile(this)
configureStrictMode()

View File

@ -35,6 +35,7 @@ import javax.inject.Inject
class NewBookDao @Inject constructor(private val box: Box<BookOnDiskEntity>) {
@Suppress("NoOp")
fun books() = box.asFlowable()
.flatMap { books ->
io.reactivex.rxjava3.core.Flowable.fromIterable(books)

View File

@ -49,6 +49,7 @@ import org.kiwix.kiwixmobile.core.di.modules.JNIModule
import org.kiwix.kiwixmobile.core.di.modules.MutexModule
import org.kiwix.kiwixmobile.core.di.modules.NetworkModule
import org.kiwix.kiwixmobile.core.di.modules.SearchModule
import org.kiwix.kiwixmobile.core.downloader.DownloadMonitor
import org.kiwix.kiwixmobile.core.downloader.Downloader
import org.kiwix.kiwixmobile.core.error.ErrorActivity
import org.kiwix.kiwixmobile.core.main.KiwixWebView
@ -111,6 +112,7 @@ interface CoreComponent {
fun notificationManager(): NotificationManager
fun searchResultGenerator(): SearchResultGenerator
fun mutex(): Mutex
fun provideDownloadMonitor(): DownloadMonitor
fun inject(application: CoreApp)
fun inject(kiwixWebView: KiwixWebView)

View File

@ -32,7 +32,6 @@ import org.kiwix.kiwixmobile.core.data.remote.KiwixService
import org.kiwix.kiwixmobile.core.downloader.DownloadRequester
import org.kiwix.kiwixmobile.core.downloader.Downloader
import org.kiwix.kiwixmobile.core.downloader.DownloaderImpl
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadManagerMonitor
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadManagerRequester
import org.kiwix.kiwixmobile.core.downloader.downloadManager.FetchDownloadNotificationManager
import org.kiwix.kiwixmobile.core.utils.CONNECT_TIME_OUT
@ -56,10 +55,9 @@ object DownloaderModule {
@Singleton
fun providesDownloadRequester(
fetch: Fetch,
sharedPreferenceUtil: SharedPreferenceUtil,
downloadManagerMonitor: DownloadManagerMonitor
sharedPreferenceUtil: SharedPreferenceUtil
): DownloadRequester =
DownloadManagerRequester(fetch, sharedPreferenceUtil, downloadManagerMonitor)
DownloadManagerRequester(fetch, sharedPreferenceUtil)
@Provides
@Singleton

View File

@ -18,5 +18,6 @@
package org.kiwix.kiwixmobile.core.downloader
interface DownloadMonitor {
fun init()
fun startMonitoringDownload()
fun stopListeningDownloads()
}

View File

@ -20,10 +20,16 @@ package org.kiwix.kiwixmobile.core.downloader.downloadManager
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.util.Log
import com.tonyodev.fetch2.Download
import com.tonyodev.fetch2.Error
import com.tonyodev.fetch2.Fetch
import com.tonyodev.fetch2.FetchListener
import com.tonyodev.fetch2core.DownloadBlock
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.PublishSubject
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
import org.kiwix.kiwixmobile.core.downloader.DownloadMonitor
import org.kiwix.kiwixmobile.core.extensions.isServiceRunning
import javax.inject.Inject
const val ZERO = 0
@ -33,27 +39,103 @@ const val DEFAULT_INT_VALUE = -1
@SuppressLint("CheckResult")
class DownloadManagerMonitor @Inject constructor(
val context: Context
val fetch: Fetch,
val context: Context,
val downloadRoomDao: DownloadRoomDao
) : DownloadMonitor {
private val updater = PublishSubject.create<() -> Unit>()
private var updaterDisposable: Disposable? = null
init {
startMonitoringDownloads()
private fun setupUpdater() {
updaterDisposable = updater.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(
{ it.invoke() },
Throwable::printStackTrace
)
}
/**
* Starts monitoring the downloads by ensuring that the `DownloadMonitorService` is running.
* This service keeps the Fetch instance alive when the application is in the background
* or has been killed by the user or system, allowing downloads to continue in the background.
*/
fun startMonitoringDownloads() {
if (!context.isServiceRunning(DownloadMonitorService::class.java)) {
context.startService(Intent(context, DownloadMonitorService::class.java)).also {
Log.e("DOWNLOAD_MANAGER_MONITOR", "Starting DownloadMonitorService")
}
private val fetchListener = object : FetchListener {
override fun onAdded(download: Download) {
// Do nothing
}
override fun onCancelled(download: Download) {
delete(download)
}
override fun onCompleted(download: Download) {
update(download)
}
override fun onDeleted(download: Download) {
delete(download)
}
override fun onDownloadBlockUpdated(
download: Download,
downloadBlock: DownloadBlock,
totalBlocks: Int
) {
update(download)
}
override fun onError(download: Download, error: Error, throwable: Throwable?) {
update(download)
}
override fun onPaused(download: Download) {
update(download)
}
override fun onProgress(
download: Download,
etaInMilliSeconds: Long,
downloadedBytesPerSecond: Long
) {
update(download)
}
override fun onQueued(download: Download, waitingOnNetwork: Boolean) {
update(download)
}
override fun onRemoved(download: Download) {
delete(download)
}
override fun onResumed(download: Download) {
update(download)
}
override fun onStarted(
download: Download,
downloadBlocks: List<DownloadBlock>,
totalBlocks: Int
) {
update(download)
}
override fun onWaitingNetwork(download: Download) {
update(download)
}
}
override fun init() {
// empty method to so class does not get reported unused
private fun update(download: Download) {
updater.onNext { downloadRoomDao.update(download) }
}
private fun delete(download: Download) {
updater.onNext { downloadRoomDao.delete(download) }
}
override fun startMonitoringDownload() {
fetch.addListener(fetchListener, true)
setupUpdater()
}
override fun stopListeningDownloads() {
fetch.removeListener(fetchListener)
updaterDisposable?.dispose()
}
}

View File

@ -19,9 +19,9 @@
package org.kiwix.kiwixmobile.core.downloader.downloadManager
import com.tonyodev.fetch2.Fetch
import com.tonyodev.fetch2.Request
import com.tonyodev.fetch2.NetworkType.ALL
import com.tonyodev.fetch2.NetworkType.WIFI_ONLY
import com.tonyodev.fetch2.Request
import org.kiwix.kiwixmobile.core.downloader.DownloadRequester
import org.kiwix.kiwixmobile.core.downloader.model.DownloadRequest
import org.kiwix.kiwixmobile.core.utils.AUTO_RETRY_MAX_ATTEMPTS
@ -30,27 +30,20 @@ import javax.inject.Inject
class DownloadManagerRequester @Inject constructor(
private val fetch: Fetch,
private val sharedPreferenceUtil: SharedPreferenceUtil,
private val downloadManagerMonitor: DownloadManagerMonitor
private val sharedPreferenceUtil: SharedPreferenceUtil
) : DownloadRequester {
override fun enqueue(downloadRequest: DownloadRequest): Long {
val request = downloadRequest.toFetchRequest(sharedPreferenceUtil)
fetch.enqueue(request)
return request.id.toLong().also {
downloadManagerMonitor.startMonitoringDownloads()
}
return request.id.toLong()
}
override fun cancel(downloadId: Long) {
fetch.delete(downloadId.toInt()).also {
downloadManagerMonitor.startMonitoringDownloads()
}
fetch.delete(downloadId.toInt())
}
override fun retryDownload(downloadId: Long) {
fetch.retry(downloadId.toInt()).also {
downloadManagerMonitor.startMonitoringDownloads()
}
fetch.retry(downloadId.toInt())
}
override fun pauseResumeDownload(downloadId: Long, isPause: Boolean) {
@ -58,8 +51,6 @@ class DownloadManagerRequester @Inject constructor(
fetch.resume(downloadId.toInt())
} else {
fetch.pause(downloadId.toInt())
}.also {
downloadManagerMonitor.startMonitoringDownloads()
}
}
}

View File

@ -25,6 +25,7 @@ import com.tonyodev.fetch2.Download
import com.tonyodev.fetch2.Error
import com.tonyodev.fetch2.Fetch
import com.tonyodev.fetch2.FetchListener
import com.tonyodev.fetch2.Status
import com.tonyodev.fetch2core.DownloadBlock
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
@ -45,6 +46,9 @@ class DownloadMonitorService : Service() {
@Inject
lateinit var fetch: Fetch
@Inject
lateinit var fetchDownloadNotificationManager: FetchDownloadNotificationManager
@Inject
lateinit var downloadRoomDao: DownloadRoomDao
@ -55,11 +59,16 @@ class DownloadMonitorService : Service() {
.build()
.inject(this)
super.onCreate()
fetch.addListener(fetchListener, true)
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
@ -96,7 +105,38 @@ class DownloadMonitorService : Service() {
override fun onBind(intent: Intent?): IBinder? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int = START_NOT_STICKY
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent?.action == STOP_DOWNLOAD_SERVICE) {
stopForegroundServiceForDownloads()
}
return START_NOT_STICKY
}
/**
* Sets the foreground notification for the service.
* This notification is used to display the current download progress,
* and it is updated dynamically based on the state of the downloads.
*
* The method checks for any active downloads and, if found, updates the notification
* with the latest download progress. If there are no active downloads,
* the service is stopped and removed from the foreground.
*/
private fun setForegroundNotification() {
updater.onNext {
fetch.getDownloads { downloadList ->
downloadList.firstOrNull {
it.status == Status.NONE ||
it.status == Status.ADDED ||
it.status == Status.QUEUED ||
it.status == Status.DOWNLOADING
}?.let {
val notificationBuilder =
fetchDownloadNotificationManager.getNotificationBuilder(it.id, it.id)
startForeground(it.id, notificationBuilder.build())
} ?: kotlin.run(::stopForegroundServiceForDownloads)
}
}
}
private val fetchListener = object : FetchListener {
override fun onAdded(download: Download) {
@ -108,7 +148,7 @@ class DownloadMonitorService : Service() {
}
override fun onCompleted(download: Download) {
update(download)
update(download, true)
}
override fun onDeleted(download: Download) {
@ -124,11 +164,11 @@ class DownloadMonitorService : Service() {
}
override fun onError(download: Download, error: Error, throwable: Throwable?) {
update(download)
update(download, true)
}
override fun onPaused(download: Download) {
update(download)
update(download, true)
}
override fun onProgress(
@ -163,25 +203,33 @@ class DownloadMonitorService : Service() {
update(download)
}
private fun update(download: Download) {
updater.onNext { downloadRoomDao.update(download) }
private fun update(download: Download, shouldSetForegroundNotification: Boolean = false) {
updater.onNext { downloadRoomDao.update(download) }.also {
if (shouldSetForegroundNotification) {
setForegroundNotification()
}
}
}
private fun delete(download: Download) {
updater.onNext { downloadRoomDao.delete(download) }
updater.onNext { downloadRoomDao.delete(download) }.also {
setForegroundNotification()
}
}
}
/**
* Stops the foreground service, disposes of resources, and removes the Fetch listener.
*/
private fun stopForegroundServiceForDownloads() {
// foreGroundServiceInformation = true to DEFAULT_INT_VALUE
Log.e(
"DOWNLOAD_MANAGER_MONITOR",
"Stopping DownloadMonitorService as there is no more download available to track"
)
monitoringDisposable?.dispose()
updaterDisposable?.dispose()
fetch.removeListener(fetchListener)
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
}
companion object {
const val STOP_DOWNLOAD_SERVICE = "stop_download_service"
}
}

View File

@ -21,46 +21,52 @@ 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.PendingIntent.FLAG_IMMUTABLE
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
import android.app.PendingIntent.getActivity
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
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.DownloadNotification
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.R.drawable
import com.tonyodev.fetch2.R.string
import com.tonyodev.fetch2.util.DEFAULT_NOTIFICATION_TIMEOUT_AFTER_RESET
import org.kiwix.kiwixmobile.core.CoreApp
import org.kiwix.kiwixmobile.core.Intents
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import javax.inject.Inject
const val DOWNLOAD_NOTIFICATION_TITLE = "OPEN_ZIM_FILE"
class FetchDownloadNotificationManager(
private val context: Context,
class FetchDownloadNotificationManager @Inject constructor(
context: Context,
private val downloadRoomDao: DownloadRoomDao
) : DefaultFetchNotificationManager(context) {
override fun getFetchInstanceForNamespace(namespace: String): Fetch = Fetch.getDefaultInstance()
override fun registerBroadcastReceiver() {
val context = CoreApp.instance.applicationContext
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.registerReceiver(
broadcastReceiver,
IntentFilter(notificationManagerAction),
Context.RECEIVER_EXPORTED
)
} else {
context.registerReceiver(
broadcastReceiver,
IntentFilter(notificationManagerAction)
)
}
}
override fun createNotificationChannels(
context: Context,
notificationManager: NotificationManager
@ -90,7 +96,6 @@ class FetchDownloadNotificationManager(
downloadNotification: DownloadNotification,
context: Context
) {
// super method but with pause button removed
val smallIcon = if (downloadNotification.isDownloading) {
android.R.drawable.stat_sys_download
} else {
@ -124,7 +129,7 @@ class FetchDownloadNotificationManager(
getActionPendingIntent(downloadNotification, DownloadNotification.ActionType.DELETE)
).addAction(
drawable.fetch_notification_pause,
context.getString(R.string.tts_pause),
context.getString(R.string.notification_pause_button_text),
getActionPendingIntent(downloadNotification, DownloadNotification.ActionType.PAUSE)
)
@ -132,7 +137,7 @@ class FetchDownloadNotificationManager(
notificationBuilder.setTimeoutAfter(getNotificationTimeOutMillis())
.addAction(
drawable.fetch_notification_resume,
context.getString(R.string.tts_resume),
context.getString(R.string.notification_resume_button_text),
getActionPendingIntent(downloadNotification, DownloadNotification.ActionType.RESUME)
)
.addAction(
@ -149,35 +154,6 @@ class FetchDownloadNotificationManager(
notificationCustomisation(downloadNotification, notificationBuilder, context)
}
override fun getActionPendingIntent(
downloadNotification: DownloadNotification,
actionType: DownloadNotification.ActionType
): PendingIntent {
val intent = Intent(notificationManagerAction).apply {
putExtra(EXTRA_NAMESPACE, downloadNotification.namespace)
putExtra(EXTRA_DOWNLOAD_ID, downloadNotification.notificationId)
putExtra(EXTRA_NOTIFICATION_ID, downloadNotification.notificationId)
putExtra(EXTRA_GROUP_ACTION, false)
putExtra(EXTRA_NOTIFICATION_GROUP_ID, downloadNotification.groupId)
}
val action = when (actionType) {
DownloadNotification.ActionType.CANCEL -> ACTION_TYPE_CANCEL
DownloadNotification.ActionType.DELETE -> ACTION_TYPE_DELETE
DownloadNotification.ActionType.RESUME -> ACTION_TYPE_RESUME
DownloadNotification.ActionType.PAUSE -> ACTION_TYPE_PAUSE
DownloadNotification.ActionType.RETRY -> ACTION_TYPE_RETRY
else -> ACTION_TYPE_INVALID
}
intent.putExtra(EXTRA_ACTION_TYPE, action)
val flags = PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
return PendingIntent.getBroadcast(
context,
downloadNotification.notificationId + action,
intent,
flags
)
}
@SuppressLint("UnspecifiedImmutableFlag")
private fun notificationCustomisation(
downloadNotification: DownloadNotification,

View File

@ -52,9 +52,13 @@ import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions.Super.ShouldCa
import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToLibkiwixMigrator
import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToRoomMigrator
import org.kiwix.kiwixmobile.core.di.components.CoreActivityComponent
import org.kiwix.kiwixmobile.core.downloader.DownloadMonitor
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadMonitorService
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DownloadMonitorService.Companion.STOP_DOWNLOAD_SERVICE
import org.kiwix.kiwixmobile.core.error.ErrorActivity
import org.kiwix.kiwixmobile.core.extensions.browserIntent
import org.kiwix.kiwixmobile.core.extensions.getToolbarNavigationIcon
import org.kiwix.kiwixmobile.core.extensions.isServiceRunning
import org.kiwix.kiwixmobile.core.extensions.setToolTipWithContentDescription
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
@ -98,6 +102,9 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
@Inject lateinit var objectBoxToLibkiwixMigrator: ObjectBoxToLibkiwixMigrator
@Inject lateinit var objectBoxToRoomMigrator: ObjectBoxToRoomMigrator
@Inject
lateinit var downloadMonitor: DownloadMonitor
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.KiwixTheme)
super.onCreate(savedInstanceState)
@ -140,17 +147,51 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
override fun onStart() {
super.onStart()
downloadMonitor.startMonitoringDownload()
stopDownloadServiceIfRunning()
rateDialogHandler.checkForRateDialog(getIconResId())
navController.addOnDestinationChangedListener { _, destination, _ ->
configureActivityBasedOn(destination)
}
}
/**
* Stops the DownloadService if it is currently running,
* as the application is now in the foreground and can handle downloads directly.
*/
private fun stopDownloadServiceIfRunning() {
if (isServiceRunning(DownloadMonitorService::class.java)) {
startService(
Intent(
this,
DownloadMonitorService::class.java
).setAction(STOP_DOWNLOAD_SERVICE)
)
}
}
override fun onDestroy() {
onBackPressedCallBack.remove()
super.onDestroy()
}
override fun onStop() {
startMonitoringDownloads()
downloadMonitor.stopListeningDownloads()
super.onStop()
}
/**
* Starts monitoring the downloads by ensuring that the `DownloadMonitorService` is running.
* This service keeps the Fetch instance alive when the application is in the background
* or has been killed by the user or system, allowing downloads to continue in the background.
*/
private fun startMonitoringDownloads() {
if (!isServiceRunning(DownloadMonitorService::class.java)) {
startService(Intent(this, DownloadMonitorService::class.java))
}
}
open fun configureActivityBasedOn(destination: NavDestination) {
if (destination.id !in topLevelDestinations) {
handleDrawerOnNavigation()

View File

@ -1851,8 +1851,7 @@ abstract class CoreReaderFragment :
setIsCloseAllTabButtonClickable(true)
restoreDeletedTabs()
}
show()
}
}.show()
}
}

View File

@ -78,19 +78,19 @@ abstract class CorePrefsFragment :
@JvmField
@Inject
protected var sharedPreferenceUtil: SharedPreferenceUtil? = null
var sharedPreferenceUtil: SharedPreferenceUtil? = null
@JvmField
@Inject
protected var storageCalculator: StorageCalculator? = null
var storageCalculator: StorageCalculator? = null
@JvmField
@Inject
protected var darkModeConfig: DarkModeConfig? = null
var darkModeConfig: DarkModeConfig? = null
@JvmField
@Inject
protected var alertDialogShower: DialogShower? = null
var alertDialogShower: DialogShower? = null
@JvmField
@Inject

View File

@ -147,6 +147,8 @@
<string name="tts_pause">pause</string>
<string name="tts_resume">resume</string>
<string name="stop">stop</string>
<string name="notification_pause_button_text">Pause</string>
<string name="notification_resume_button_text">Resume</string>
<string name="internal_storage">Internal</string>
<string name="external_storage">External</string>
<string name="yes">Yes</string>

View File

@ -1,4 +1,5 @@
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx6096m
kotlin.code.style=1.7
kotlin.code.style=1.8
kapt.use.k2=true