mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-03 18:56:44 -04:00
Merge pull request #4118 from kiwix/Fixes#4113
Fixed: Impossible to download full Wikipedia in English.
This commit is contained in:
commit
154389f459
@ -83,7 +83,7 @@ class KiwixReaderFragment : CoreReaderFragment() {
|
||||
)
|
||||
}
|
||||
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
toolbar?.let(activity::setupDrawerToggle)
|
||||
toolbar?.let { activity.setupDrawerToggle(it, true) }
|
||||
setFragmentContainerBottomMarginToSizeOfNavBar()
|
||||
openPageInBookFromNavigationArguments()
|
||||
}
|
||||
@ -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)
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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) {
|
||||
@ -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 =
|
||||
|
@ -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)
|
||||
@ -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(
|
||||
@ -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)
|
||||
@ -288,8 +288,8 @@ class DownloadMonitorService : Service() {
|
||||
downloadId: Long,
|
||||
progress: Int,
|
||||
etaInMilliSeconds: Long,
|
||||
bytesDownloaded: Int,
|
||||
totalSizeOfDownload: Int
|
||||
bytesDownloaded: Long,
|
||||
totalSizeOfDownload: Long
|
||||
) {
|
||||
updateDownloadStatus(
|
||||
downloadId,
|
||||
@ -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)
|
||||
@ -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 {
|
||||
@ -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)
|
||||
@ -614,7 +614,7 @@ class DownloadMonitorService : Service() {
|
||||
|
||||
data class DownloadInfo(
|
||||
var startTime: Long,
|
||||
var initialBytesDownloaded: Int
|
||||
var initialBytesDownloaded: Long
|
||||
)
|
||||
|
||||
interface AssignNewForegroundServiceNotification {
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
@ -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 }
|
||||
}
|
||||
|
@ -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
|
||||
@ -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() {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user