Skip to content

Commit

Permalink
Fixed: Downloading starts very slowly on Android 14+.
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
MohitMaliDeveloper committed Dec 23, 2024
1 parent 0f16730 commit 2e63d70
Show file tree
Hide file tree
Showing 19 changed files with 262 additions and 118 deletions.
2 changes: 1 addition & 1 deletion buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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") {
Expand Down
2 changes: 1 addition & 1 deletion buildSrc/src/main/kotlin/Libs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

/**
Expand Down
8 changes: 4 additions & 4 deletions buildSrc/src/main/kotlin/Versions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,21 @@ 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"

const val androidx_test_core: String = "1.6.1"

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"

Expand Down Expand Up @@ -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"
}

/**
Expand Down
9 changes: 6 additions & 3 deletions buildSrc/src/main/kotlin/plugin/AllProjectConfigurer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -227,7 +231,6 @@ class AllProjectConfigurer {
implementation(Libs.roomRxjava2)
kapt(Libs.roomCompiler)
implementation(Libs.tracing)
implementation(Libs.fetch)
implementation(Libs.fetchOkhttp)
}
}
Expand Down
4 changes: 4 additions & 0 deletions core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
}
5 changes: 0 additions & 5 deletions core/src/main/java/org/kiwix/kiwixmobile/core/CoreApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -43,9 +42,6 @@ abstract class CoreApp : Application() {
lateinit var coreComponent: CoreComponent
}

@Inject
lateinit var downloadMonitor: DownloadMonitor

@Inject
lateinit var darkModeConfig: DarkModeConfig

Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@
package org.kiwix.kiwixmobile.core.downloader

interface DownloadMonitor {
fun init()
fun startMonitoringDownload()
fun stopListeningDownloads()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
}

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 init() {
// empty method to so class does not get reported unused
override fun stopListeningDownloads() {
fetch.removeListener(fetchListener)
updaterDisposable?.dispose()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -30,36 +30,27 @@ 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) {
if (isPause) {
fetch.resume(downloadId.toInt())
} else {
fetch.pause(downloadId.toInt())
}.also {
downloadManagerMonitor.startMonitoringDownloads()
}
}
}
Expand Down
Loading

0 comments on commit 2e63d70

Please sign in to comment.