Skip to content

Commit

Permalink
Merge pull request #3935 from kiwix/Fixes#3141
Browse files Browse the repository at this point in the history
Replacing the fetch library with Android's DownloadManager.
  • Loading branch information
kelson42 authored Aug 12, 2024
2 parents eff8321 + d34dd37 commit 78e6349
Show file tree
Hide file tree
Showing 106 changed files with 1,674 additions and 1,038 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,6 @@ are interested in our custom apps, they have their own repo
classes by default.
- [JUnit5](https://github.com/junit-team/junit5/) - The next generation of JUnit
- [AssertJ](https://github.com/joel-costigliola/assertj-core) - Fluent assertions for test code
- [Fetch](https://github.com/tonyofrancis/Fetch) - A customizable file download manager library for
Android
- [ZXing](https://github.com/zxing/zxing) - Barcode scanning library for Java, Android

## Contributing
Expand Down
2 changes: 1 addition & 1 deletion app/detekt_baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ID>EmptyFunctionBlock:None.kt$None${ }</ID>
<ID>EmptyFunctionBlock:SimplePageChangeListener.kt$SimplePageChangeListener${ }</ID>
<ID>LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( booksOnFileSystem: List&lt;BookOnDisk>, activeDownloads: List&lt;DownloadModel>, allLanguages: List&lt;Language>, libraryNetworkEntity: LibraryNetworkEntity, filter: String, fileSystemState: FileSystemState )</ID>
<ID>LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( private val downloadDao: FetchDownloadDao, private val bookDao: NewBookDao, private val languageDao: NewLanguagesDao, private val storageObserver: StorageObserver, private val kiwixService: KiwixService, private val context: Application, private val connectivityBroadcastReceiver: ConnectivityBroadcastReceiver, private val bookUtils: BookUtils, private val fat32Checker: Fat32Checker, private val defaultLanguageProvider: DefaultLanguageProvider, private val dataSource: DataSource, private val connectivityManager: ConnectivityManager, private val sharedPreferenceUtil: SharedPreferenceUtil )</ID>
<ID>LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( private val downloadDao: DownloadRoomDao, private val bookDao: NewBookDao, private val languageDao: NewLanguagesDao, private val storageObserver: StorageObserver, private val kiwixService: KiwixService, private val context: Application, private val connectivityBroadcastReceiver: ConnectivityBroadcastReceiver, private val bookUtils: BookUtils, private val fat32Checker: Fat32Checker, private val defaultLanguageProvider: DefaultLanguageProvider, private val dataSource: DataSource, private val connectivityManager: ConnectivityManager, private val sharedPreferenceUtil: SharedPreferenceUtil )</ID>
<ID>MagicNumber:LibraryListItem.kt$LibraryListItem.LibraryDownloadItem$1000L</ID>
<ID>MagicNumber:PeerGroupHandshake.kt$PeerGroupHandshake$15000</ID>
<ID>MagicNumber:ShareFiles.kt$ShareFiles$24</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,14 @@ class DownloadRobot : BaseRobot() {
}

fun assertDownloadPaused() {
pauseForBetterTestPerformance()
onView(withText(org.kiwix.kiwixmobile.core.R.string.paused_state)).check(matches(isDisplayed()))
testFlakyView({
pauseForBetterTestPerformance()
onView(
withText(
org.kiwix.kiwixmobile.core.R.string.paused_state
)
).check(matches(isDisplayed()))
})
}

fun resumeDownload() {
Expand Down
5 changes: 0 additions & 5 deletions app/src/main/assets/credits.html
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,6 @@ <h3>Apache</h3>
<br/>
Copyright 2013 Evan Tatarka
</li>
<li>
<b>Fetch</b>
<br/>
Copyright (C) 2017 Tonyo Francis
</li>
<li>
<b>ObjectBox</b>
<br/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package org.kiwix.kiwixmobile.localFileTransfer

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Context.RECEIVER_NOT_EXPORTED
import android.content.IntentFilter
import android.net.Uri
import android.net.wifi.WpsInfo
Expand All @@ -32,10 +33,10 @@ import android.net.wifi.p2p.WifiP2pManager.Channel
import android.net.wifi.p2p.WifiP2pManager.ChannelListener
import android.net.wifi.p2p.WifiP2pManager.ConnectionInfoListener
import android.net.wifi.p2p.WifiP2pManager.PeerListListener
import android.os.Build
import android.os.Build.VERSION
import android.os.Build.VERSION_CODES
import android.os.Looper.getMainLooper
import org.kiwix.kiwixmobile.core.utils.files.Log
import android.widget.Toast
import androidx.lifecycle.LifecycleCoroutineScope
import kotlinx.coroutines.launch
Expand All @@ -44,6 +45,7 @@ import org.kiwix.kiwixmobile.core.extensions.toast
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.FileTransferConfirmation
import org.kiwix.kiwixmobile.core.utils.files.Log
import org.kiwix.kiwixmobile.localFileTransfer.FileItem.FileStatus
import org.kiwix.kiwixmobile.localFileTransfer.KiwixWifiP2pBroadcastReceiver.P2pEventListener
import java.io.IOException
Expand Down Expand Up @@ -105,7 +107,11 @@ class WifiDirectManager @Inject constructor(
addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)
addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION)
}
context.registerReceiver(receiver, intentFilter)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
context.registerReceiver(receiver, intentFilter, RECEIVER_NOT_EXPORTED)
} else {
context.registerReceiver(receiver, intentFilter)
}
}

private fun unregisterWifiDirectBroadcastReceiver() = context.unregisterReceiver(receiver)
Expand Down
19 changes: 6 additions & 13 deletions app/src/main/java/org/kiwix/kiwixmobile/main/KiwixMainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,8 @@ import org.kiwix.kiwixmobile.BuildConfig
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.downloader.fetch.DOWNLOAD_NOTIFICATION_TITLE
import org.kiwix.kiwixmobile.core.downloader.downloadManager.DOWNLOAD_NOTIFICATION_TITLE
import org.kiwix.kiwixmobile.core.main.CoreMainActivity
import org.kiwix.kiwixmobile.core.utils.EXTERNAL_SELECT_POSITION
import org.kiwix.kiwixmobile.core.utils.INTERNAL_SELECT_POSITION
import org.kiwix.kiwixmobile.core.utils.LanguageUtils.Companion.handleLocaleChange
import org.kiwix.kiwixmobile.databinding.ActivityKiwixMainBinding
import org.kiwix.kiwixmobile.kiwixActivityComponent
Expand Down Expand Up @@ -122,16 +120,11 @@ class KiwixMainActivity : CoreMainActivity() {

private fun migrateInternalToPublicAppDirectory() {
if (!sharedPreferenceUtil.prefIsAppDirectoryMigrated) {
val writableStoragePaths = StorageDeviceUtils.getWritableStorage(this)
val targetStoragePath = when (sharedPreferenceUtil.storagePosition) {
INTERNAL_SELECT_POSITION ->
sharedPreferenceUtil.getPublicDirectoryPath(writableStoragePaths[0].name)

EXTERNAL_SELECT_POSITION -> writableStoragePaths.getOrNull(1)?.name
else -> null
}
targetStoragePath?.let {
sharedPreferenceUtil.putPrefStorage(it)
val storagePath = StorageDeviceUtils.getWritableStorage(this)
.getOrNull(sharedPreferenceUtil.storagePosition)
?.name
storagePath?.let {
sharedPreferenceUtil.putPrefStorage(sharedPreferenceUtil.getPublicDirectoryPath(it))
sharedPreferenceUtil.putPrefAppDirectoryMigrated(true)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import android.Manifest
import android.Manifest.permission.POST_NOTIFICATIONS
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
import android.annotation.SuppressLint
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.net.ConnectivityManager
Expand All @@ -36,7 +35,6 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar
Expand All @@ -50,7 +48,6 @@ import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.tonyodev.fetch2.Status
import eu.mhutti1.utils.storage.StorageDevice
import eu.mhutti1.utils.storage.StorageSelectDialog
import org.kiwix.kiwixmobile.R
Expand All @@ -59,6 +56,7 @@ import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.BaseFragment
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
import org.kiwix.kiwixmobile.core.downloader.Downloader
import org.kiwix.kiwixmobile.core.downloader.downloadManager.Status
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.hasNotificationPermission
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.isManageExternalStoragePermissionGranted
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.navigate
Expand All @@ -85,9 +83,7 @@ import org.kiwix.kiwixmobile.core.utils.SimpleTextListener
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
import org.kiwix.kiwixmobile.core.utils.dialog.DialogShower
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.SelectFolder
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.YesNoDialog.WifiOnly
import org.kiwix.kiwixmobile.core.utils.files.FileUtils.getPathFromUri
import org.kiwix.kiwixmobile.databinding.FragmentDestinationDownloadBinding
import org.kiwix.kiwixmobile.zimManager.NetworkState
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel
Expand Down Expand Up @@ -136,7 +132,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
context?.let { context ->
downloader.pauseResumeDownload(
it.downloadId,
it.downloadState.toReadableState(context) == getString(R.string.paused_state)
it.downloadState.toReadableState(context).contains(getString(R.string.paused_state))
)
}
}
Expand Down Expand Up @@ -380,62 +376,16 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
private fun storeDeviceInPreferences(
storageDevice: StorageDevice
) {
if (storageDevice.isInternal) {
sharedPreferenceUtil.putPrefStorage(
sharedPreferenceUtil.getPublicDirectoryPath(storageDevice.name)
)
sharedPreferenceUtil.putStoragePosition(INTERNAL_SELECT_POSITION)
clickOnBookItem()
} else {
if (sharedPreferenceUtil.isPlayStoreBuild) {
setExternalStoragePath(storageDevice)
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R &&
Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU
) {
val view = LayoutInflater.from(activity).inflate(R.layout.select_folder_dialog, null)
dialogShower.show(SelectFolder { view }, ::selectFolder)
} else {
setExternalStoragePath(storageDevice)
}
}
}
}

private fun setExternalStoragePath(storageDevice: StorageDevice) {
sharedPreferenceUtil.putPrefStorage(storageDevice.name)
sharedPreferenceUtil.putStoragePosition(EXTERNAL_SELECT_POSITION)
clickOnBookItem()
}

private fun selectFolder() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(
Intent.FLAG_GRANT_READ_URI_PERMISSION
or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
or Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
or Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
sharedPreferenceUtil.putPrefStorage(
sharedPreferenceUtil.getPublicDirectoryPath(storageDevice.name)
)
selectFolderLauncher.launch(intent)
sharedPreferenceUtil.putStoragePosition(
if (storageDevice.isInternal) INTERNAL_SELECT_POSITION
else EXTERNAL_SELECT_POSITION
)
clickOnBookItem()
}

private val selectFolderLauncher =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == RESULT_OK) {
result.data?.let { intent ->
getPathFromUri(requireActivity(), intent)?.let(sharedPreferenceUtil::putPrefStorage)
sharedPreferenceUtil.putStoragePosition(EXTERNAL_SELECT_POSITION)
clickOnBookItem()
} ?: run {
activity.toast(
resources
.getString(R.string.system_unable_to_grant_permission_message),
Toast.LENGTH_SHORT
)
}
}
}

private fun requestNotificationPermission() {
if (!shouldShowRationale(POST_NOTIFICATIONS)) {
requireActivity().requestNotificationPermission()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.StorageObserver
import org.kiwix.kiwixmobile.core.base.SideEffect
import org.kiwix.kiwixmobile.core.compat.CompatHelper.Companion.isWifi
import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
import org.kiwix.kiwixmobile.core.data.DataSource
Expand Down Expand Up @@ -88,7 +88,7 @@ const val MAX_PROGRESS = 100
private const val TAG_RX_JAVA_DEFAULT_ERROR_HANDLER = "RxJavaDefaultErrorHandler"

class ZimManageViewModel @Inject constructor(
private val downloadDao: FetchDownloadDao,
private val downloadDao: DownloadRoomDao,
private val bookDao: NewBookDao,
private val languageDao: NewLanguagesDao,
private val storageObserver: StorageObserver,
Expand Down Expand Up @@ -334,10 +334,12 @@ class ZimManageViewModel @Inject constructor(
fromLocalesWithNetworkMatchesSetActiveBy(
networkLanguageCounts(booksFromNetwork), defaultLanguage()
)

booksFromNetwork.isNotEmpty() && allLanguages.isNotEmpty() ->
fromLocalesWithNetworkMatchesSetActiveBy(
networkLanguageCounts(booksFromNetwork), allLanguages
)

else -> throw RuntimeException("Impossible state")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ import eu.mhutti1.utils.storage.Kb
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao
import org.kiwix.kiwixmobile.core.dao.DownloadRoomDao
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
import org.kiwix.kiwixmobile.core.settings.StorageCalculator
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem
import javax.inject.Inject

class AvailableSpaceCalculator @Inject constructor(
private val downloadDao: FetchDownloadDao,
private val downloadRoomDao: DownloadRoomDao,
private val storageCalculator: StorageCalculator
) {
private var availableSpaceCalculatorDisposable: Disposable? = null
Expand All @@ -40,7 +40,7 @@ class AvailableSpaceCalculator @Inject constructor(
successAction: (LibraryListItem.BookItem) -> Unit,
failureAction: (String) -> Unit
) {
availableSpaceCalculatorDisposable = downloadDao.allDownloads()
availableSpaceCalculatorDisposable = downloadRoomDao.allDownloads()
.map { it.map(DownloadModel::bytesRemaining).sum() }
.map { bytesToBeDownloaded -> storageCalculator.availableBytes() - bytesToBeDownloaded }
.subscribeOn(Schedulers.io())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
package org.kiwix.kiwixmobile.zimManager.libraryView.adapter

import androidx.annotation.StringRes
import com.tonyodev.fetch2.Status
import org.kiwix.kiwixmobile.core.downloader.downloadManager.Status
import org.kiwix.kiwixmobile.core.downloader.model.Base64String
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.core.downloader.model.DownloadState
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@
package org.kiwix.kiwixmobile.zimManager.libraryView.adapter

import android.view.View
import com.tonyodev.fetch2.Status
import org.kiwix.kiwixmobile.R
import org.kiwix.kiwixmobile.core.base.adapter.BaseViewHolder
import org.kiwix.kiwixmobile.core.downloader.downloadManager.Error
import org.kiwix.kiwixmobile.core.downloader.downloadManager.Status
import org.kiwix.kiwixmobile.core.downloader.model.Base64String
import org.kiwix.kiwixmobile.core.downloader.model.DownloadState
import org.kiwix.kiwixmobile.core.extensions.setBitmap
import org.kiwix.kiwixmobile.core.extensions.setImageDrawableCompat
import org.kiwix.kiwixmobile.core.extensions.setTextAndVisibility
Expand Down Expand Up @@ -97,6 +99,7 @@ sealed class LibraryViewHolder<in T : LibraryListItem>(containerView: View) :
) :
LibraryViewHolder<LibraryDownloadItem>(itemDownloadBinding.root) {

@Suppress("MagicNumber")
override fun bind(item: LibraryDownloadItem) {
itemDownloadBinding.libraryDownloadFavicon.setBitmap(item.favIcon)
itemDownloadBinding.libraryDownloadTitle.text = item.title
Expand All @@ -118,18 +121,44 @@ sealed class LibraryViewHolder<in T : LibraryListItem>(containerView: View) :
itemDownloadBinding.downloadState.text =
item.downloadState.toReadableState(containerView.context).also {
val pauseResumeIconId =
if (it == itemDownloadBinding.root.context.getString(R.string.paused_state)) {
if (it.contains(itemDownloadBinding.root.context.getString(R.string.paused_state))) {
R.drawable.ic_play_24dp
} else {
R.drawable.ic_pause_24dp
}
itemDownloadBinding.pauseResume.setImageDrawableCompat(pauseResumeIconId)
itemDownloadBinding.pauseResume.apply {
setImageDrawableCompat(pauseResumeIconId)
if (shouldEnablePauseResumeButton(item.downloadState)) {
isEnabled = true
alpha = 1f
} else {
isEnabled = false
alpha = 0.5f
}
}
}
if (item.currentDownloadState == Status.FAILED) {
clickAction.invoke(item)
}
itemDownloadBinding.eta.text = item.readableEta
}

private fun shouldEnablePauseResumeButton(
downloadState: DownloadState
): Boolean =
when (downloadState) {
is DownloadState.Failed -> false
is DownloadState.Paused -> shouldEnablePauseResumeButtonForPauseReason(downloadState.reason)
else -> true
}

/**
* Disable the pause button when the DownloadManager is waiting for
* Wi-Fi or network connection. This prevents the user from trying
* to resume the download, as it will not work without a connection.
*/
private fun shouldEnablePauseResumeButtonForPauseReason(reason: Error?): Boolean =
reason !in listOf(Error.QUEUED_FOR_WIFI, Error.WAITING_FOR_NETWORK)
}

class LibraryDividerViewHolder(private val libraryDividerBinding: LibraryDividerBinding) :
Expand Down
Loading

0 comments on commit 78e6349

Please sign in to comment.