mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-04 03:06:41 -04:00
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:
parent
0f16730120
commit
2e63d70e6c
@ -12,7 +12,7 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.android.tools.build:gradle:8.1.3")
|
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.jacoco:org.jacoco.core:0.8.12")
|
||||||
implementation("org.jlleitschuh.gradle:ktlint-gradle:10.3.0")
|
implementation("org.jlleitschuh.gradle:ktlint-gradle:10.3.0")
|
||||||
implementation("com.google.apis:google-api-services-androidpublisher:v3-rev20230406-2.0.0") {
|
implementation("com.google.apis:google-api-services-androidpublisher:v3-rev20230406-2.0.0") {
|
||||||
|
@ -101,7 +101,7 @@ object Libs {
|
|||||||
/**
|
/**
|
||||||
* https://kotlinlang.org/
|
* 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
|
Versions.org_jetbrains_kotlin
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,13 +26,13 @@ object Versions {
|
|||||||
|
|
||||||
const val com_squareup_okhttp3: String = "4.12.0"
|
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 androidx_navigation: String = "2.5.3"
|
||||||
|
|
||||||
const val navigation_ui_ktx: String = "2.4.1"
|
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"
|
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 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"
|
const val io_mockk: String = "1.13.13"
|
||||||
|
|
||||||
@ -110,7 +110,7 @@ object Versions {
|
|||||||
|
|
||||||
const val keeper = "0.16.1"
|
const val keeper = "0.16.1"
|
||||||
|
|
||||||
const val fetch: String = "3.3.0"
|
const val fetch: String = "3.4.1"
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,6 +28,7 @@ import org.gradle.kotlin.dsl.apply
|
|||||||
import org.gradle.kotlin.dsl.dependencies
|
import org.gradle.kotlin.dsl.dependencies
|
||||||
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
|
import org.gradle.testing.jacoco.plugins.JacocoPluginExtension
|
||||||
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
|
import org.gradle.testing.jacoco.plugins.JacocoTaskExtension
|
||||||
|
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||||
import org.jlleitschuh.gradle.ktlint.KtlintExtension
|
import org.jlleitschuh.gradle.ktlint.KtlintExtension
|
||||||
|
|
||||||
@ -75,7 +76,10 @@ class AllProjectConfigurer {
|
|||||||
targetCompatibility = Config.javaVersion
|
targetCompatibility = Config.javaVersion
|
||||||
}
|
}
|
||||||
target.tasks.withType(KotlinCompile::class.java) {
|
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 {
|
buildFeatures.apply {
|
||||||
viewBinding = true
|
viewBinding = true
|
||||||
@ -191,7 +195,7 @@ class AllProjectConfigurer {
|
|||||||
|
|
||||||
fun configureDependencies(target: Project) {
|
fun configureDependencies(target: Project) {
|
||||||
target.dependencies {
|
target.dependencies {
|
||||||
implementation(Libs.kotlin_stdlib_jdk7)
|
implementation(Libs.kotlin_stdlib_jdk8)
|
||||||
implementation(Libs.appcompat)
|
implementation(Libs.appcompat)
|
||||||
implementation(Libs.material)
|
implementation(Libs.material)
|
||||||
implementation(Libs.constraintlayout)
|
implementation(Libs.constraintlayout)
|
||||||
@ -227,7 +231,6 @@ class AllProjectConfigurer {
|
|||||||
implementation(Libs.roomRxjava2)
|
implementation(Libs.roomRxjava2)
|
||||||
kapt(Libs.roomCompiler)
|
kapt(Libs.roomCompiler)
|
||||||
implementation(Libs.tracing)
|
implementation(Libs.tracing)
|
||||||
implementation(Libs.fetch)
|
|
||||||
implementation(Libs.fetchOkhttp)
|
implementation(Libs.fetchOkhttp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,4 +63,8 @@ dependencies {
|
|||||||
implementation(Libs.kotlinx_coroutines_android)
|
implementation(Libs.kotlinx_coroutines_android)
|
||||||
implementation(Libs.kotlinx_coroutines_rx3)
|
implementation(Libs.kotlinx_coroutines_rx3)
|
||||||
implementation(Libs.zxing)
|
implementation(Libs.zxing)
|
||||||
|
api(Libs.fetch) {
|
||||||
|
// Todo: Will remove this when we add support for Android 15
|
||||||
|
exclude("androidx.core", "core-ktx")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,6 @@ import androidx.multidex.MultiDex
|
|||||||
import com.jakewharton.threetenabp.AndroidThreeTen
|
import com.jakewharton.threetenabp.AndroidThreeTen
|
||||||
import org.kiwix.kiwixmobile.core.di.components.CoreComponent
|
import org.kiwix.kiwixmobile.core.di.components.CoreComponent
|
||||||
import org.kiwix.kiwixmobile.core.di.components.DaggerCoreComponent
|
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.main.CoreMainActivity
|
||||||
import org.kiwix.kiwixmobile.core.utils.files.FileLogger
|
import org.kiwix.kiwixmobile.core.utils.files.FileLogger
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -43,9 +42,6 @@ abstract class CoreApp : Application() {
|
|||||||
lateinit var coreComponent: CoreComponent
|
lateinit var coreComponent: CoreComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
|
||||||
lateinit var downloadMonitor: DownloadMonitor
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var darkModeConfig: DarkModeConfig
|
lateinit var darkModeConfig: DarkModeConfig
|
||||||
|
|
||||||
@ -84,7 +80,6 @@ abstract class CoreApp : Application() {
|
|||||||
AndroidThreeTen.init(this)
|
AndroidThreeTen.init(this)
|
||||||
coreComponent.inject(this)
|
coreComponent.inject(this)
|
||||||
serviceWorkerInitialiser.init(this)
|
serviceWorkerInitialiser.init(this)
|
||||||
downloadMonitor.init()
|
|
||||||
darkModeConfig.init()
|
darkModeConfig.init()
|
||||||
fileLogger.writeLogFile(this)
|
fileLogger.writeLogFile(this)
|
||||||
configureStrictMode()
|
configureStrictMode()
|
||||||
|
@ -35,6 +35,7 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
class NewBookDao @Inject constructor(private val box: Box<BookOnDiskEntity>) {
|
class NewBookDao @Inject constructor(private val box: Box<BookOnDiskEntity>) {
|
||||||
|
|
||||||
|
@Suppress("NoOp")
|
||||||
fun books() = box.asFlowable()
|
fun books() = box.asFlowable()
|
||||||
.flatMap { books ->
|
.flatMap { books ->
|
||||||
io.reactivex.rxjava3.core.Flowable.fromIterable(books)
|
io.reactivex.rxjava3.core.Flowable.fromIterable(books)
|
||||||
|
@ -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.MutexModule
|
||||||
import org.kiwix.kiwixmobile.core.di.modules.NetworkModule
|
import org.kiwix.kiwixmobile.core.di.modules.NetworkModule
|
||||||
import org.kiwix.kiwixmobile.core.di.modules.SearchModule
|
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.downloader.Downloader
|
||||||
import org.kiwix.kiwixmobile.core.error.ErrorActivity
|
import org.kiwix.kiwixmobile.core.error.ErrorActivity
|
||||||
import org.kiwix.kiwixmobile.core.main.KiwixWebView
|
import org.kiwix.kiwixmobile.core.main.KiwixWebView
|
||||||
@ -111,6 +112,7 @@ interface CoreComponent {
|
|||||||
fun notificationManager(): NotificationManager
|
fun notificationManager(): NotificationManager
|
||||||
fun searchResultGenerator(): SearchResultGenerator
|
fun searchResultGenerator(): SearchResultGenerator
|
||||||
fun mutex(): Mutex
|
fun mutex(): Mutex
|
||||||
|
fun provideDownloadMonitor(): DownloadMonitor
|
||||||
|
|
||||||
fun inject(application: CoreApp)
|
fun inject(application: CoreApp)
|
||||||
fun inject(kiwixWebView: KiwixWebView)
|
fun inject(kiwixWebView: KiwixWebView)
|
||||||
|
@ -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.DownloadRequester
|
||||||
import org.kiwix.kiwixmobile.core.downloader.Downloader
|
import org.kiwix.kiwixmobile.core.downloader.Downloader
|
||||||
import org.kiwix.kiwixmobile.core.downloader.DownloaderImpl
|
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.DownloadManagerRequester
|
||||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.FetchDownloadNotificationManager
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.FetchDownloadNotificationManager
|
||||||
import org.kiwix.kiwixmobile.core.utils.CONNECT_TIME_OUT
|
import org.kiwix.kiwixmobile.core.utils.CONNECT_TIME_OUT
|
||||||
@ -56,10 +55,9 @@ object DownloaderModule {
|
|||||||
@Singleton
|
@Singleton
|
||||||
fun providesDownloadRequester(
|
fun providesDownloadRequester(
|
||||||
fetch: Fetch,
|
fetch: Fetch,
|
||||||
sharedPreferenceUtil: SharedPreferenceUtil,
|
sharedPreferenceUtil: SharedPreferenceUtil
|
||||||
downloadManagerMonitor: DownloadManagerMonitor
|
|
||||||
): DownloadRequester =
|
): DownloadRequester =
|
||||||
DownloadManagerRequester(fetch, sharedPreferenceUtil, downloadManagerMonitor)
|
DownloadManagerRequester(fetch, sharedPreferenceUtil)
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
|
@ -18,5 +18,6 @@
|
|||||||
package org.kiwix.kiwixmobile.core.downloader
|
package org.kiwix.kiwixmobile.core.downloader
|
||||||
|
|
||||||
interface DownloadMonitor {
|
interface DownloadMonitor {
|
||||||
fun init()
|
fun startMonitoringDownload()
|
||||||
|
fun stopListeningDownloads()
|
||||||
}
|
}
|
||||||
|
@ -20,10 +20,16 @@ package org.kiwix.kiwixmobile.core.downloader.downloadManager
|
|||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import com.tonyodev.fetch2.Download
|
||||||
import android.util.Log
|
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.downloader.DownloadMonitor
|
||||||
import org.kiwix.kiwixmobile.core.extensions.isServiceRunning
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
const val ZERO = 0
|
const val ZERO = 0
|
||||||
@ -33,27 +39,103 @@ const val DEFAULT_INT_VALUE = -1
|
|||||||
|
|
||||||
@SuppressLint("CheckResult")
|
@SuppressLint("CheckResult")
|
||||||
class DownloadManagerMonitor @Inject constructor(
|
class DownloadManagerMonitor @Inject constructor(
|
||||||
val context: Context
|
val fetch: Fetch,
|
||||||
|
val context: Context,
|
||||||
|
val downloadRoomDao: DownloadRoomDao
|
||||||
) : DownloadMonitor {
|
) : DownloadMonitor {
|
||||||
|
private val updater = PublishSubject.create<() -> Unit>()
|
||||||
|
private var updaterDisposable: Disposable? = null
|
||||||
|
|
||||||
init {
|
private fun setupUpdater() {
|
||||||
startMonitoringDownloads()
|
updaterDisposable = updater.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
.subscribe(
|
||||||
|
{ it.invoke() },
|
||||||
|
Throwable::printStackTrace
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private val fetchListener = object : FetchListener {
|
||||||
* Starts monitoring the downloads by ensuring that the `DownloadMonitorService` is running.
|
override fun onAdded(download: Download) {
|
||||||
* This service keeps the Fetch instance alive when the application is in the background
|
// Do nothing
|
||||||
* or has been killed by the user or system, allowing downloads to continue in the background.
|
}
|
||||||
*/
|
|
||||||
fun startMonitoringDownloads() {
|
override fun onCancelled(download: Download) {
|
||||||
if (!context.isServiceRunning(DownloadMonitorService::class.java)) {
|
delete(download)
|
||||||
context.startService(Intent(context, DownloadMonitorService::class.java)).also {
|
}
|
||||||
Log.e("DOWNLOAD_MANAGER_MONITOR", "Starting DownloadMonitorService")
|
|
||||||
}
|
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() {
|
private fun update(download: Download) {
|
||||||
// empty method to so class does not get reported unused
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,9 @@
|
|||||||
package org.kiwix.kiwixmobile.core.downloader.downloadManager
|
package org.kiwix.kiwixmobile.core.downloader.downloadManager
|
||||||
|
|
||||||
import com.tonyodev.fetch2.Fetch
|
import com.tonyodev.fetch2.Fetch
|
||||||
import com.tonyodev.fetch2.Request
|
|
||||||
import com.tonyodev.fetch2.NetworkType.ALL
|
import com.tonyodev.fetch2.NetworkType.ALL
|
||||||
import com.tonyodev.fetch2.NetworkType.WIFI_ONLY
|
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.DownloadRequester
|
||||||
import org.kiwix.kiwixmobile.core.downloader.model.DownloadRequest
|
import org.kiwix.kiwixmobile.core.downloader.model.DownloadRequest
|
||||||
import org.kiwix.kiwixmobile.core.utils.AUTO_RETRY_MAX_ATTEMPTS
|
import org.kiwix.kiwixmobile.core.utils.AUTO_RETRY_MAX_ATTEMPTS
|
||||||
@ -30,27 +30,20 @@ import javax.inject.Inject
|
|||||||
|
|
||||||
class DownloadManagerRequester @Inject constructor(
|
class DownloadManagerRequester @Inject constructor(
|
||||||
private val fetch: Fetch,
|
private val fetch: Fetch,
|
||||||
private val sharedPreferenceUtil: SharedPreferenceUtil,
|
private val sharedPreferenceUtil: SharedPreferenceUtil
|
||||||
private val downloadManagerMonitor: DownloadManagerMonitor
|
|
||||||
) : DownloadRequester {
|
) : DownloadRequester {
|
||||||
override fun enqueue(downloadRequest: DownloadRequest): Long {
|
override fun enqueue(downloadRequest: DownloadRequest): Long {
|
||||||
val request = downloadRequest.toFetchRequest(sharedPreferenceUtil)
|
val request = downloadRequest.toFetchRequest(sharedPreferenceUtil)
|
||||||
fetch.enqueue(request)
|
fetch.enqueue(request)
|
||||||
return request.id.toLong().also {
|
return request.id.toLong()
|
||||||
downloadManagerMonitor.startMonitoringDownloads()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun cancel(downloadId: Long) {
|
override fun cancel(downloadId: Long) {
|
||||||
fetch.delete(downloadId.toInt()).also {
|
fetch.delete(downloadId.toInt())
|
||||||
downloadManagerMonitor.startMonitoringDownloads()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun retryDownload(downloadId: Long) {
|
override fun retryDownload(downloadId: Long) {
|
||||||
fetch.retry(downloadId.toInt()).also {
|
fetch.retry(downloadId.toInt())
|
||||||
downloadManagerMonitor.startMonitoringDownloads()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun pauseResumeDownload(downloadId: Long, isPause: Boolean) {
|
override fun pauseResumeDownload(downloadId: Long, isPause: Boolean) {
|
||||||
@ -58,8 +51,6 @@ class DownloadManagerRequester @Inject constructor(
|
|||||||
fetch.resume(downloadId.toInt())
|
fetch.resume(downloadId.toInt())
|
||||||
} else {
|
} else {
|
||||||
fetch.pause(downloadId.toInt())
|
fetch.pause(downloadId.toInt())
|
||||||
}.also {
|
|
||||||
downloadManagerMonitor.startMonitoringDownloads()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ 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.Status
|
||||||
import com.tonyodev.fetch2core.DownloadBlock
|
import com.tonyodev.fetch2core.DownloadBlock
|
||||||
import io.reactivex.Observable
|
import io.reactivex.Observable
|
||||||
import io.reactivex.disposables.Disposable
|
import io.reactivex.disposables.Disposable
|
||||||
@ -45,6 +46,9 @@ class DownloadMonitorService : Service() {
|
|||||||
@Inject
|
@Inject
|
||||||
lateinit var fetch: Fetch
|
lateinit var fetch: Fetch
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var fetchDownloadNotificationManager: FetchDownloadNotificationManager
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var downloadRoomDao: DownloadRoomDao
|
lateinit var downloadRoomDao: DownloadRoomDao
|
||||||
|
|
||||||
@ -55,11 +59,16 @@ class DownloadMonitorService : Service() {
|
|||||||
.build()
|
.build()
|
||||||
.inject(this)
|
.inject(this)
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
fetch.addListener(fetchListener, true)
|
|
||||||
setupUpdater()
|
setupUpdater()
|
||||||
startMonitoringDownloads()
|
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() {
|
private fun 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
|
||||||
@ -96,7 +105,38 @@ class DownloadMonitorService : Service() {
|
|||||||
|
|
||||||
override fun onBind(intent: Intent?): IBinder? = null
|
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 {
|
private val fetchListener = object : FetchListener {
|
||||||
override fun onAdded(download: Download) {
|
override fun onAdded(download: Download) {
|
||||||
@ -108,7 +148,7 @@ class DownloadMonitorService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCompleted(download: Download) {
|
override fun onCompleted(download: Download) {
|
||||||
update(download)
|
update(download, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDeleted(download: Download) {
|
override fun onDeleted(download: Download) {
|
||||||
@ -124,11 +164,11 @@ class DownloadMonitorService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onError(download: Download, error: Error, throwable: Throwable?) {
|
override fun onError(download: Download, error: Error, throwable: Throwable?) {
|
||||||
update(download)
|
update(download, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPaused(download: Download) {
|
override fun onPaused(download: Download) {
|
||||||
update(download)
|
update(download, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onProgress(
|
override fun onProgress(
|
||||||
@ -163,25 +203,33 @@ class DownloadMonitorService : Service() {
|
|||||||
update(download)
|
update(download)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun update(download: Download) {
|
private fun update(download: Download, shouldSetForegroundNotification: Boolean = false) {
|
||||||
updater.onNext { downloadRoomDao.update(download) }
|
updater.onNext { downloadRoomDao.update(download) }.also {
|
||||||
|
if (shouldSetForegroundNotification) {
|
||||||
|
setForegroundNotification()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun delete(download: Download) {
|
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() {
|
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()
|
monitoringDisposable?.dispose()
|
||||||
updaterDisposable?.dispose()
|
updaterDisposable?.dispose()
|
||||||
fetch.removeListener(fetchListener)
|
fetch.removeListener(fetchListener)
|
||||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||||
stopSelf()
|
stopSelf()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val STOP_DOWNLOAD_SERVICE = "stop_download_service"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,46 +21,52 @@ package org.kiwix.kiwixmobile.core.downloader.downloadManager
|
|||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
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
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
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 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.DownloadNotification
|
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.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.util.DEFAULT_NOTIFICATION_TIMEOUT_AFTER_RESET
|
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.Intents
|
||||||
import org.kiwix.kiwixmobile.core.R
|
import org.kiwix.kiwixmobile.core.R
|
||||||
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
|
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
|
||||||
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
const val DOWNLOAD_NOTIFICATION_TITLE = "OPEN_ZIM_FILE"
|
const val DOWNLOAD_NOTIFICATION_TITLE = "OPEN_ZIM_FILE"
|
||||||
|
|
||||||
class FetchDownloadNotificationManager(
|
class FetchDownloadNotificationManager @Inject constructor(
|
||||||
private val context: Context,
|
context: Context,
|
||||||
private val downloadRoomDao: DownloadRoomDao
|
private val downloadRoomDao: DownloadRoomDao
|
||||||
) : DefaultFetchNotificationManager(context) {
|
) : DefaultFetchNotificationManager(context) {
|
||||||
override fun getFetchInstanceForNamespace(namespace: String): Fetch = Fetch.getDefaultInstance()
|
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(
|
override fun createNotificationChannels(
|
||||||
context: Context,
|
context: Context,
|
||||||
notificationManager: NotificationManager
|
notificationManager: NotificationManager
|
||||||
@ -90,7 +96,6 @@ class FetchDownloadNotificationManager(
|
|||||||
downloadNotification: DownloadNotification,
|
downloadNotification: DownloadNotification,
|
||||||
context: Context
|
context: Context
|
||||||
) {
|
) {
|
||||||
// super method but with pause button removed
|
|
||||||
val smallIcon = if (downloadNotification.isDownloading) {
|
val smallIcon = if (downloadNotification.isDownloading) {
|
||||||
android.R.drawable.stat_sys_download
|
android.R.drawable.stat_sys_download
|
||||||
} else {
|
} else {
|
||||||
@ -124,7 +129,7 @@ class FetchDownloadNotificationManager(
|
|||||||
getActionPendingIntent(downloadNotification, DownloadNotification.ActionType.DELETE)
|
getActionPendingIntent(downloadNotification, DownloadNotification.ActionType.DELETE)
|
||||||
).addAction(
|
).addAction(
|
||||||
drawable.fetch_notification_pause,
|
drawable.fetch_notification_pause,
|
||||||
context.getString(R.string.tts_pause),
|
context.getString(R.string.notification_pause_button_text),
|
||||||
getActionPendingIntent(downloadNotification, DownloadNotification.ActionType.PAUSE)
|
getActionPendingIntent(downloadNotification, DownloadNotification.ActionType.PAUSE)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -132,7 +137,7 @@ class FetchDownloadNotificationManager(
|
|||||||
notificationBuilder.setTimeoutAfter(getNotificationTimeOutMillis())
|
notificationBuilder.setTimeoutAfter(getNotificationTimeOutMillis())
|
||||||
.addAction(
|
.addAction(
|
||||||
drawable.fetch_notification_resume,
|
drawable.fetch_notification_resume,
|
||||||
context.getString(R.string.tts_resume),
|
context.getString(R.string.notification_resume_button_text),
|
||||||
getActionPendingIntent(downloadNotification, DownloadNotification.ActionType.RESUME)
|
getActionPendingIntent(downloadNotification, DownloadNotification.ActionType.RESUME)
|
||||||
)
|
)
|
||||||
.addAction(
|
.addAction(
|
||||||
@ -149,35 +154,6 @@ class FetchDownloadNotificationManager(
|
|||||||
notificationCustomisation(downloadNotification, notificationBuilder, context)
|
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")
|
@SuppressLint("UnspecifiedImmutableFlag")
|
||||||
private fun notificationCustomisation(
|
private fun notificationCustomisation(
|
||||||
downloadNotification: DownloadNotification,
|
downloadNotification: DownloadNotification,
|
||||||
|
@ -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.ObjectBoxToLibkiwixMigrator
|
||||||
import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToRoomMigrator
|
import org.kiwix.kiwixmobile.core.data.remote.ObjectBoxToRoomMigrator
|
||||||
import org.kiwix.kiwixmobile.core.di.components.CoreActivityComponent
|
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.error.ErrorActivity
|
||||||
import org.kiwix.kiwixmobile.core.extensions.browserIntent
|
import org.kiwix.kiwixmobile.core.extensions.browserIntent
|
||||||
import org.kiwix.kiwixmobile.core.extensions.getToolbarNavigationIcon
|
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.extensions.setToolTipWithContentDescription
|
||||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
import org.kiwix.kiwixmobile.core.reader.ZimReaderContainer
|
||||||
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
|
import org.kiwix.kiwixmobile.core.reader.ZimReaderSource
|
||||||
@ -98,6 +102,9 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
|
|||||||
@Inject lateinit var objectBoxToLibkiwixMigrator: ObjectBoxToLibkiwixMigrator
|
@Inject lateinit var objectBoxToLibkiwixMigrator: ObjectBoxToLibkiwixMigrator
|
||||||
@Inject lateinit var objectBoxToRoomMigrator: ObjectBoxToRoomMigrator
|
@Inject lateinit var objectBoxToRoomMigrator: ObjectBoxToRoomMigrator
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
lateinit var downloadMonitor: DownloadMonitor
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
setTheme(R.style.KiwixTheme)
|
setTheme(R.style.KiwixTheme)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -140,17 +147,51 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
|
|||||||
|
|
||||||
override fun onStart() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
|
downloadMonitor.startMonitoringDownload()
|
||||||
|
stopDownloadServiceIfRunning()
|
||||||
rateDialogHandler.checkForRateDialog(getIconResId())
|
rateDialogHandler.checkForRateDialog(getIconResId())
|
||||||
navController.addOnDestinationChangedListener { _, destination, _ ->
|
navController.addOnDestinationChangedListener { _, destination, _ ->
|
||||||
configureActivityBasedOn(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() {
|
override fun onDestroy() {
|
||||||
onBackPressedCallBack.remove()
|
onBackPressedCallBack.remove()
|
||||||
super.onDestroy()
|
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) {
|
open fun configureActivityBasedOn(destination: NavDestination) {
|
||||||
if (destination.id !in topLevelDestinations) {
|
if (destination.id !in topLevelDestinations) {
|
||||||
handleDrawerOnNavigation()
|
handleDrawerOnNavigation()
|
||||||
|
@ -1851,8 +1851,7 @@ abstract class CoreReaderFragment :
|
|||||||
setIsCloseAllTabButtonClickable(true)
|
setIsCloseAllTabButtonClickable(true)
|
||||||
restoreDeletedTabs()
|
restoreDeletedTabs()
|
||||||
}
|
}
|
||||||
show()
|
}.show()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,19 +78,19 @@ abstract class CorePrefsFragment :
|
|||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
@Inject
|
@Inject
|
||||||
protected var sharedPreferenceUtil: SharedPreferenceUtil? = null
|
var sharedPreferenceUtil: SharedPreferenceUtil? = null
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
@Inject
|
@Inject
|
||||||
protected var storageCalculator: StorageCalculator? = null
|
var storageCalculator: StorageCalculator? = null
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
@Inject
|
@Inject
|
||||||
protected var darkModeConfig: DarkModeConfig? = null
|
var darkModeConfig: DarkModeConfig? = null
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
@Inject
|
@Inject
|
||||||
protected var alertDialogShower: DialogShower? = null
|
var alertDialogShower: DialogShower? = null
|
||||||
|
|
||||||
@JvmField
|
@JvmField
|
||||||
@Inject
|
@Inject
|
||||||
|
@ -147,6 +147,8 @@
|
|||||||
<string name="tts_pause">pause</string>
|
<string name="tts_pause">pause</string>
|
||||||
<string name="tts_resume">resume</string>
|
<string name="tts_resume">resume</string>
|
||||||
<string name="stop">stop</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="internal_storage">Internal</string>
|
||||||
<string name="external_storage">External</string>
|
<string name="external_storage">External</string>
|
||||||
<string name="yes">Yes</string>
|
<string name="yes">Yes</string>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
org.gradle.jvmargs=-Xmx6096m
|
org.gradle.jvmargs=-Xmx6096m
|
||||||
kotlin.code.style=1.7
|
kotlin.code.style=1.8
|
||||||
|
kapt.use.k2=true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user