Skip to content

Commit

Permalink
Merge pull request #4118 from kiwix/Fixes#4113
Browse files Browse the repository at this point in the history
Fixed: Impossible to download full Wikipedia in English.
  • Loading branch information
kelson42 authored Dec 8, 2024
2 parents 116180e + ab2295b commit 154389f
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
)
}
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
toolbar?.let(activity::setupDrawerToggle)
toolbar?.let { activity.setupDrawerToggle(it, true) }
setFragmentContainerBottomMarginToSizeOfNavBar()
openPageInBookFromNavigationArguments()
}
Expand Down Expand Up @@ -167,7 +167,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
override fun hideTabSwitcher() {
actionBar?.let { actionBar ->
actionBar.setDisplayShowTitleEnabled(true)
toolbar?.let { activity?.setupDrawerToggle(it) }
toolbar?.let { activity?.setupDrawerToggle(it, true) }

setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,15 @@ class ZimManageViewModel @Inject constructor(
.callTimeout(CALL_TIMEOUT, SECONDS)
.addNetworkInterceptor(UserAgentInterceptor(USER_AGENT))
.build()
client.newCall(headRequest).execute().use { response ->
if (response.isSuccessful) {
return@getContentLengthOfLibraryXmlFile response.header("content-length")?.toLongOrNull()
?: DEFAULT_INT_VALUE.toLong()
try {
client.newCall(headRequest).execute().use { response ->
if (response.isSuccessful) {
return@getContentLengthOfLibraryXmlFile response.header("content-length")?.toLongOrNull()
?: DEFAULT_INT_VALUE.toLong()
}
}
} catch (ignore: Exception) {
// do nothing
}
return DEFAULT_INT_VALUE.toLong()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,57 +18,85 @@

package org.kiwix.kiwixmobile.core.downloader.downloadManager

import android.annotation.SuppressLint
import android.app.DownloadManager
import android.content.Context
import android.content.Intent
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
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.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.extensions.registerReceiver
import org.kiwix.kiwixmobile.core.zim_manager.ConnectivityBroadcastReceiver
import org.kiwix.kiwixmobile.core.extensions.isServiceRunning
import org.kiwix.kiwixmobile.core.utils.files.Log
import java.util.concurrent.TimeUnit
import javax.inject.Inject

class DownloadManagerMonitor @Inject constructor(
val downloadRoomDao: DownloadRoomDao,
private val context: Context,
private val connectivityBroadcastReceiver: ConnectivityBroadcastReceiver
private val context: Context
) : DownloadMonitor, DownloadManagerBroadcastReceiver.Callback {
private val lock = Any()
private var monitoringDisposable: Disposable? = null

init {
context.registerReceiver(connectivityBroadcastReceiver)
startServiceIfActiveDownloads()
trackNetworkState()
startMonitoringDownloads()
}

@SuppressLint("CheckResult")
private fun trackNetworkState() {
connectivityBroadcastReceiver.networkStates
.distinctUntilChanged()
@Suppress("MagicNumber")
fun startMonitoringDownloads() {
if (monitoringDisposable?.isDisposed == false) return
monitoringDisposable = Observable.interval(ZERO.toLong(), 5, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(
{
// Start the service when the network changes so that we can
// track the progress accurately.
startServiceIfActiveDownloads()
try {
synchronized(lock) {
// Observe downloads when the application is in the foreground.
// This is especially useful when downloads are resumed but the
// Download Manager takes some time to update the download status.
// In such cases, the foreground service may stop prematurely due to
// a lack of active downloads during this update delay.
if (downloadRoomDao.downloads().blockingFirst().isNotEmpty()) {
// Check if there are active downloads and the service is not running.
// If so, start the DownloadMonitorService to properly track download progress.
if (shouldStartService()) {
startService()
} else {
// Do nothing; it is for fixing the error when "if" is used as an expression.
}
} else {
monitoringDisposable?.dispose()
}
}
} catch (ignore: Exception) {
Log.e(
"DOWNLOAD_MONITOR",
"Couldn't get the downloads update. Original exception = $ignore"
)
}
},
Throwable::printStackTrace
)
}

private fun startServiceIfActiveDownloads() {
CoroutineScope(Dispatchers.IO).launch {
if (downloadRoomDao.downloads().blockingFirst().isNotEmpty()) {
startService()
}
/**
* Determines if the DownloadMonitorService should be started.
* Checks if there are active downloads and if the service is not already running.
*/
private fun shouldStartService(): Boolean =
getActiveDownloads().isNotEmpty() &&
!context.isServiceRunning(DownloadMonitorService::class.java)

private fun getActiveDownloads(): List<DownloadRoomEntity> =
downloadRoomDao.downloadRoomEntity().blockingFirst().filter {
it.status != Status.PAUSED && it.status != Status.CANCELLED
}
}

override fun downloadCompleteOrCancelled(intent: Intent) {
synchronized(lock) {
Expand All @@ -86,24 +114,23 @@ class DownloadManagerMonitor @Inject constructor(
}
}

fun startMonitoringDownloads() {
startService()
}

private fun startService() {
context.startService(Intent(context, DownloadMonitorService::class.java))
}

fun pauseDownload(downloadId: Long) {
context.startService(getDownloadMonitorIntent(ACTION_PAUSE, downloadId.toInt()))
startMonitoringDownloads()
}

fun resumeDownload(downloadId: Long) {
context.startService(getDownloadMonitorIntent(ACTION_RESUME, downloadId.toInt()))
startMonitoringDownloads()
}

fun cancelDownload(downloadId: Long) {
context.startService(getDownloadMonitorIntent(ACTION_CANCEL, downloadId.toInt()))
startMonitoringDownloads()
}

private fun getDownloadMonitorIntent(action: String, downloadId: Int): Intent =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ class DownloadMonitorService : Service() {
val status = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS))
val reason = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_REASON))
val bytesDownloaded =
cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
val totalBytes = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR))
val totalBytes = cursor.getLong(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES))
val progress = calculateProgress(bytesDownloaded, totalBytes)

val etaInMilliSeconds = calculateETA(downloadId, bytesDownloaded, totalBytes)
Expand Down Expand Up @@ -243,8 +243,8 @@ class DownloadMonitorService : Service() {
reason: Int,
progress: Int,
etaInMilliSeconds: Long,
bytesDownloaded: Int,
totalBytes: Int
bytesDownloaded: Long,
totalBytes: Long
) {
val error = mapDownloadError(reason)
updateDownloadStatus(
Expand All @@ -261,8 +261,8 @@ class DownloadMonitorService : Service() {
private fun handlePausedDownload(
downloadId: Long,
progress: Int,
bytesDownloaded: Int,
totalSizeOfDownload: Int,
bytesDownloaded: Long,
totalSizeOfDownload: Long,
reason: Int
) {
val pauseReason = mapDownloadPauseReason(reason)
Expand All @@ -288,8 +288,8 @@ class DownloadMonitorService : Service() {
downloadId: Long,
progress: Int,
etaInMilliSeconds: Long,
bytesDownloaded: Int,
totalSizeOfDownload: Int
bytesDownloaded: Long,
totalSizeOfDownload: Long
) {
updateDownloadStatus(
downloadId,
Expand Down Expand Up @@ -317,14 +317,14 @@ class DownloadMonitorService : Service() {
downloadInfoMap.remove(downloadId)
}

private fun calculateProgress(bytesDownloaded: Int, totalBytes: Int): Int =
private fun calculateProgress(bytesDownloaded: Long, totalBytes: Long): Int =
if (totalBytes > ZERO) {
(bytesDownloaded / totalBytes.toDouble()).times(HUNDERED).toInt()
} else {
ZERO
}

private fun calculateETA(downloadedFileId: Long, bytesDownloaded: Int, totalBytes: Int): Long {
private fun calculateETA(downloadedFileId: Long, bytesDownloaded: Long, totalBytes: Long): Long {
val currentTime = System.currentTimeMillis()
val downloadInfo = downloadInfoMap.getOrPut(downloadedFileId) {
DownloadInfo(startTime = currentTime, initialBytesDownloaded = bytesDownloaded)
Expand Down Expand Up @@ -376,8 +376,8 @@ class DownloadMonitorService : Service() {
error: Error,
progress: Int = DEFAULT_INT_VALUE,
etaInMilliSeconds: Long = DEFAULT_INT_VALUE.toLong(),
bytesDownloaded: Int = DEFAULT_INT_VALUE,
totalSizeOfDownload: Int = DEFAULT_INT_VALUE
bytesDownloaded: Long = DEFAULT_INT_VALUE.toLong(),
totalSizeOfDownload: Long = DEFAULT_INT_VALUE.toLong()
) {
synchronized(lock) {
updater.onNext {
Expand All @@ -392,11 +392,11 @@ class DownloadMonitorService : Service() {
this.progress = progress
}
this.etaInMilliSeconds = etaInMilliSeconds
if (bytesDownloaded != DEFAULT_INT_VALUE) {
this.bytesDownloaded = bytesDownloaded.toLong()
if (bytesDownloaded != DEFAULT_INT_VALUE.toLong()) {
this.bytesDownloaded = bytesDownloaded
}
if (totalSizeOfDownload != DEFAULT_INT_VALUE) {
this.totalSizeOfDownload = totalSizeOfDownload.toLong()
if (totalSizeOfDownload != DEFAULT_INT_VALUE.toLong()) {
this.totalSizeOfDownload = totalSizeOfDownload
}
}
downloadRoomDao.update(downloadModel)
Expand Down Expand Up @@ -614,7 +614,7 @@ class DownloadMonitorService : Service() {

data class DownloadInfo(
var startTime: Long,
var initialBytesDownloaded: Int
var initialBytesDownloaded: Long
)

interface AssignNewForegroundServiceNotification {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ object ActivityExtensions {
val Activity.cachedComponent: CoreActivityComponent
get() = coreMainActivity.cachedComponent

fun Activity.setupDrawerToggle(toolbar: Toolbar) =
coreMainActivity.setupDrawerToggle(toolbar)
fun Activity.setupDrawerToggle(toolbar: Toolbar, shouldEnableRightDrawer: Boolean = false) =
coreMainActivity.setupDrawerToggle(toolbar, shouldEnableRightDrawer)

fun Activity.navigate(fragmentId: Int) {
coreMainActivity.navigate(fragmentId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@

package org.kiwix.kiwixmobile.core.extensions

import android.app.ActivityManager
import android.app.Service
import android.content.Context
import android.content.Context.RECEIVER_NOT_EXPORTED
import android.content.Intent
Expand Down Expand Up @@ -113,3 +115,11 @@ fun Context.getBitmapFromDrawable(drawable: Drawable): Bitmap {

return bitmap
}

@Suppress("Deprecation")
fun Context.isServiceRunning(serviceClass: Class<out Service>): Boolean {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val services = activityManager.getRunningServices(Int.MAX_VALUE)

return services.any { it.service.className == serviceClass.name && it.foreground }
}
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
override fun onSupportNavigateUp(): Boolean =
navController.navigateUp() || super.onSupportNavigateUp()

open fun setupDrawerToggle(toolbar: Toolbar) {
open fun setupDrawerToggle(toolbar: Toolbar, shouldEnableRightDrawer: Boolean = false) {
// Set the initial contentDescription to the hamburger icon.
// This method is called from various locations after modifying the navigationIcon.
// For example, we previously changed this icon/contentDescription to the "+" button
Expand All @@ -241,8 +241,10 @@ abstract class CoreMainActivity : BaseActivity(), WebViewProvider {
it.syncState()
}
drawerContainerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED)
// Enable the right drawer
drawerContainerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END)
if (shouldEnableRightDrawer) {
// Enable the right drawer
drawerContainerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.END)
}
}

open fun disableDrawer() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,9 @@ abstract class CoreReaderFragment :
* to verify proper functionality.
*/
open fun setUpDrawerToggle(toolbar: Toolbar) {
toolbar.let((requireActivity() as CoreMainActivity)::setupDrawerToggle)
toolbar.let {
(requireActivity() as CoreMainActivity).setupDrawerToggle(it, true)
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ class CustomMainActivity : CoreMainActivity() {
}
}

override fun setupDrawerToggle(toolbar: Toolbar) {
super.setupDrawerToggle(toolbar)
override fun setupDrawerToggle(toolbar: Toolbar, shouldEnableRightDrawer: Boolean) {
super.setupDrawerToggle(toolbar, shouldEnableRightDrawer)
activityCustomMainBinding.drawerNavView.apply {
/**
* Hide the 'ZimHostFragment' option from the navigation menu
Expand Down

0 comments on commit 154389f

Please sign in to comment.