Merge pull request #2003 from kiwix/macgills/feature/2002-pre-allocation

#2002 Stop pre allocation of downloads and instead check currently downloading books to calculate available space
This commit is contained in:
Seán Mac Gillicuddy 2020-04-03 15:34:04 +01:00 committed by GitHub
commit 45b14119c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 99 additions and 37 deletions

View File

@ -7,12 +7,12 @@
<ID>EmptyFunctionBlock:ZimHostActivity.kt$ZimHostActivity.&lt;no name provided&gt;${}</ID>
<ID>ForbiddenComment:KiwixMainActivity.kt$KiwixMainActivity$// TODO: Show to user</ID>
<ID>LongParameterList:ZimManageViewModel.kt$ZimManageViewModel$( booksOnFileSystem: List&lt;BookOnDisk&gt;, activeDownloads: List&lt;DownloadModel&gt;, allLanguages: List&lt;Language&gt;, libraryNetworkEntity: LibraryNetworkEntity, filter: String, fileSystemState: FileSystemState )</ID>
<ID>MagicNumber:LibraryFragment.kt$LibraryFragment$1024f</ID>
<ID>MagicNumber:LibraryListItem.kt$LibraryListItem.LibraryDownloadItem$1000L</ID>
<ID>MagicNumber:ShareFiles.kt$ShareFiles$24</ID>
<ID>MagicNumber:ZimManageViewModel.kt$ZimManageViewModel$5</ID>
<ID>MagicNumber:ZimManageViewModel.kt$ZimManageViewModel$500</ID>
<ID>MagicNumber:ZimManageViewModel.kt$ZimManageViewModel$60</ID>
<ID>PackageNaming:AvailableSpaceCalculator.kt$package org.kiwix.kiwixmobile.zim_manager.library_view</ID>
<ID>PackageNaming:ConnectivityBroadcastReceiver.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>PackageNaming:DefaultLanguageProvider.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>PackageNaming:DeleteFiles.kt$package org.kiwix.kiwixmobile.zim_manager.fileselect_view.effects</ID>
@ -42,7 +42,6 @@
<ID>PackageNaming:ZimManageViewModel.kt$package org.kiwix.kiwixmobile.zim_manager</ID>
<ID>ReturnCount:Fat32Checker.kt$Fat32Checker$private fun canCreate4GbFile(storage: String): Boolean</ID>
<ID>ReturnCount:LanguageActivity.kt$LanguageActivity$override fun onOptionsItemSelected(item: MenuItem): Boolean</ID>
<ID>ReturnCount:LibraryFragment.kt$LibraryFragment$private fun onBookItemClick(item: BookItem)</ID>
<ID>TooGenericExceptionCaught:FileWritingFileSystemChecker.kt$FileWritingFileSystemChecker$e: Exception</ID>
<ID>TooGenericExceptionCaught:KiwixMainActivity.kt$KiwixMainActivity$e: Exception</ID>
<ID>TooGenericExceptionThrown:ActivityExtensions.kt$throw RuntimeException( """ applicationContext is ${applicationContext::class.java.simpleName} application is ${application::class.java.simpleName} """.trimIndent() )</ID>

View File

@ -23,7 +23,6 @@ import android.os.Environment
import org.kiwix.kiwixmobile.core.settings.CorePrefsFragment
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.PREF_LANG
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil.PREF_STORAGE
import java.io.File
class KiwixPrefsFragment : CorePrefsFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
@ -37,7 +36,6 @@ class KiwixPrefsFragment : CorePrefsFragment() {
} else {
findPreference(PREF_STORAGE).title = sharedPreferenceUtil.getPrefStorageTitle("External")
}
findPreference(PREF_STORAGE).summary =
storageCalculator.calculateAvailableSpace(File(sharedPreferenceUtil.prefStorage))
findPreference(PREF_STORAGE).summary = storageCalculator.calculateAvailableSpace()
}
}

View File

@ -0,0 +1,53 @@
/*
* Kiwix Android
* Copyright (c) 2020 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.zim_manager.library_view
import eu.mhutti1.utils.storage.Bytes
import eu.mhutti1.utils.storage.Kb
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.kiwix.kiwixmobile.core.dao.FetchDownloadDao
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.core.settings.StorageCalculator
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem
import javax.inject.Inject
class AvailableSpaceCalculator @Inject constructor(
private val downloadDao: FetchDownloadDao,
private val storageCalculator: StorageCalculator
) {
fun hasAvailableSpaceFor(
bookItem: LibraryListItem.BookItem,
successAction: (LibraryListItem.BookItem) -> Unit,
failureAction: (String) -> Unit
) {
downloadDao.allDownloads()
.map { it.map(DownloadModel::bytesRemaining).sum() }
.map { bytesToBeDownloaded -> storageCalculator.availableBytes() - bytesToBeDownloaded }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { trueAvailableBytes ->
if (bookItem.book.size.toLong() * Kb < trueAvailableBytes) {
successAction.invoke(bookItem)
} else {
failureAction.invoke(Bytes(trueAvailableBytes).humanReadable)
}
}
}
}

View File

@ -42,7 +42,6 @@ import org.kiwix.kiwixmobile.core.downloader.Downloader
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.viewModel
import org.kiwix.kiwixmobile.core.extensions.snack
import org.kiwix.kiwixmobile.core.settings.StorageCalculator
import org.kiwix.kiwixmobile.core.utils.BookUtils
import org.kiwix.kiwixmobile.core.utils.DialogShower
import org.kiwix.kiwixmobile.core.utils.KiwixDialog.YesNoDialog.StopDownload
@ -60,7 +59,6 @@ import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryDelegate.Di
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryDelegate.DownloadDelegate
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem
import org.kiwix.kiwixmobile.zim_manager.library_view.adapter.LibraryListItem.BookItem
import java.io.File
import javax.inject.Inject
class LibraryFragment : BaseFragment() {
@ -71,7 +69,7 @@ class LibraryFragment : BaseFragment() {
@Inject lateinit var dialogShower: DialogShower
@Inject lateinit var viewModelFactory: ViewModelProvider.Factory
@Inject lateinit var bookUtils: BookUtils
@Inject lateinit var storageCalculator: StorageCalculator
@Inject lateinit var availableSpaceCalculator: AvailableSpaceCalculator
private val zimManageViewModel by lazy {
activity!!.viewModel<ZimManageViewModel>(viewModelFactory)
@ -87,9 +85,6 @@ class LibraryFragment : BaseFragment() {
)
}
private val spaceAvailable: Long
get() = storageCalculator.availableBytes(File(sharedPreferenceUtil.prefStorage))
private val noWifiWithWifiOnlyPreferenceSet
get() = sharedPreferenceUtil.prefWifiOnly && !NetworkUtils.isWiFi(context!!)
@ -192,16 +187,6 @@ class LibraryFragment : BaseFragment() {
private fun onBookItemClick(item: BookItem) {
when {
notEnoughSpaceAvailable(item) -> {
libraryList.snack(
getString(R.string.download_no_space) +
"\n" + getString(R.string.space_available) + " " +
storageCalculator.calculateAvailableSpace(File(sharedPreferenceUtil.prefStorage)),
R.string.download_change_storage,
::showStorageSelectDialog
)
return
}
isNotConnected -> {
noInternetSnackbar()
return
@ -213,13 +198,20 @@ class LibraryFragment : BaseFragment() {
})
return
}
else -> downloadFile(item.book)
else -> availableSpaceCalculator.hasAvailableSpaceFor(item,
{ downloadFile(item.book) },
{
libraryList.snack(
getString(R.string.download_no_space) +
"\n" + getString(R.string.space_available) + " " +
it,
R.string.download_change_storage,
::showStorageSelectDialog
)
})
}
}
private fun notEnoughSpaceAvailable(item: BookItem) =
spaceAvailable < item.book.size.toLong() * 1024f
private fun showStorageSelectDialog() = StorageSelectDialog()
.apply {
onSelectAction = ::storeDeviceInPreferences

View File

@ -23,6 +23,7 @@ import io.objectbox.Box
import io.objectbox.kotlin.equal
import io.objectbox.kotlin.query
import io.reactivex.Flowable
import io.reactivex.Single
import org.kiwix.kiwixmobile.core.dao.entities.FetchDownloadEntity
import org.kiwix.kiwixmobile.core.dao.entities.FetchDownloadEntity_
import org.kiwix.kiwixmobile.core.downloader.DownloadRequester
@ -43,6 +44,8 @@ class FetchDownloadDao @Inject constructor(
.doOnNext(::moveCompletedToBooksOnDiskDao)
.map { it.map(::DownloadModel) }
fun allDownloads() = Single.fromCallable { box.all.map(::DownloadModel) }
private fun moveCompletedToBooksOnDiskDao(downloadEntities: List<FetchDownloadEntity>) {
downloadEntities.filter { it.status == COMPLETED }.takeIf { it.isNotEmpty() }?.let {
box.store.callInTx {

View File

@ -73,13 +73,19 @@ object DownloaderModule {
enableLogging(BuildConfig.DEBUG)
enableRetryOnNetworkGain(true)
setHttpDownloader(okHttpDownloader)
preAllocateFileOnCreation(false)
setNotificationManager(fetchNotificationManager)
}.build().also(Impl::setDefaultInstanceConfiguration)
@JvmStatic
@Provides
@Singleton
fun provideOkHttpDownloader() = OkHttpDownloader(OkHttpClient.Builder().build())
fun provideOkHttpDownloader() = OkHttpDownloader(
OkHttpClient.Builder()
.followRedirects(true)
.followSslRedirects(true)
.build()
)
@JvmStatic
@Provides

View File

@ -35,6 +35,7 @@ data class DownloadModel(
val progress: Int,
val book: Book
) {
val bytesRemaining: Long by lazy { totalSizeOfDownload - bytesDownloaded }
val fileNameFromUrl: String by lazy { StorageUtils.getFileNameFromUrl(book.url) }
constructor(downloadEntity: FetchDownloadEntity) : this(

View File

@ -19,18 +19,21 @@
package org.kiwix.kiwixmobile.core.settings
import eu.mhutti1.utils.storage.Bytes
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
import java.io.File
import javax.inject.Inject
class StorageCalculator @Inject constructor() {
class StorageCalculator @Inject constructor(
private val sharedPreferenceUtil: SharedPreferenceUtil
) {
fun calculateAvailableSpace(file: File): String =
fun calculateAvailableSpace(file: File = File(sharedPreferenceUtil.prefStorage)): String =
Bytes(availableBytes(file)).humanReadable
fun calculateTotalSpace(file: File): String =
fun calculateTotalSpace(file: File = File(sharedPreferenceUtil.prefStorage)): String =
Bytes(totalBytes(file)).humanReadable
fun availableBytes(file: File) =
fun availableBytes(file: File = File(sharedPreferenceUtil.prefStorage)) =
if (file.exists()) file.freeSpace
else 0L

View File

@ -110,17 +110,24 @@ public class SharedPreferenceUtil {
}
public String getPrefStorage() {
String storage = sharedPreferences.getString(PREF_STORAGE, null);
final String storage = sharedPreferences.getString(PREF_STORAGE, null);
if (storage == null) {
final File externalFilesDir =
ContextCompat.getExternalFilesDirs(CoreApp.getInstance(), null)[0];
storage = externalFilesDir != null ? externalFilesDir.getPath()
: CoreApp.getInstance().getFilesDir().getPath(); // workaround for emulators
putPrefStorage(storage);
final String defaultStorage = defaultStorage();
putPrefStorage(defaultStorage);
return defaultStorage;
} else if (!new File(storage).exists()) {
return defaultStorage();
}
return storage;
}
private String defaultStorage() {
final File externalFilesDir =
ContextCompat.getExternalFilesDirs(CoreApp.getInstance(), null)[0];
return externalFilesDir != null ? externalFilesDir.getPath()
: CoreApp.getInstance().getFilesDir().getPath(); // workaround for emulators
}
public String getPrefStorageTitle(String defaultTitle) {
return sharedPreferences.getString(PREF_STORAGE_TITLE, defaultTitle);
}

View File

@ -26,7 +26,7 @@ import java.io.File
internal class StorageCalculatorTest {
private val storageCalculator = StorageCalculator()
private val storageCalculator = StorageCalculator(mockk())
private val file: File = mockk()
@Test